1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 3.3.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Http;
16:
17: use Cake\Controller\Controller;
18: use Cake\Event\EventDispatcherTrait;
19: use Cake\Event\EventListenerInterface;
20: use Cake\Routing\Router;
21: use LogicException;
22:
23: /**
24: * This class provides compatibility with dispatcher filters
25: * and interacting with the controller layers.
26: *
27: * Long term this should just be the controller dispatcher, but
28: * for now it will do a bit more than that.
29: */
30: class ActionDispatcher
31: {
32: use EventDispatcherTrait;
33:
34: /**
35: * Attached routing filters
36: *
37: * @var \Cake\Event\EventListenerInterface[]
38: */
39: protected $filters = [];
40:
41: /**
42: * Controller factory instance.
43: *
44: * @var \Cake\Http\ControllerFactory
45: */
46: protected $factory;
47:
48: /**
49: * Constructor
50: *
51: * @param \Cake\Http\ControllerFactory|null $factory A controller factory instance.
52: * @param \Cake\Event\EventManager|null $eventManager An event manager if you want to inject one.
53: * @param \Cake\Event\EventListenerInterface[] $filters The list of filters to include.
54: */
55: public function __construct($factory = null, $eventManager = null, array $filters = [])
56: {
57: if ($eventManager) {
58: $this->setEventManager($eventManager);
59: }
60: foreach ($filters as $filter) {
61: $this->addFilter($filter);
62: }
63: $this->factory = $factory ?: new ControllerFactory();
64: }
65:
66: /**
67: * Dispatches a Request & Response
68: *
69: * @param \Cake\Http\ServerRequest $request The request to dispatch.
70: * @param \Cake\Http\Response $response The response to dispatch.
71: * @return \Cake\Http\Response A modified/replaced response.
72: * @throws \ReflectionException
73: */
74: public function dispatch(ServerRequest $request, Response $response)
75: {
76: if (Router::getRequest(true) !== $request) {
77: Router::pushRequest($request);
78: }
79: $beforeEvent = $this->dispatchEvent('Dispatcher.beforeDispatch', compact('request', 'response'));
80:
81: $request = $beforeEvent->getData('request');
82: if ($beforeEvent->getResult() instanceof Response) {
83: return $beforeEvent->getResult();
84: }
85:
86: // Use the controller built by an beforeDispatch
87: // event handler if there is one.
88: if ($beforeEvent->getData('controller') instanceof Controller) {
89: $controller = $beforeEvent->getData('controller');
90: } else {
91: $controller = $this->factory->create($request, $response);
92: }
93:
94: $response = $this->_invoke($controller);
95: if ($request->getParam('return')) {
96: return $response;
97: }
98:
99: $afterEvent = $this->dispatchEvent('Dispatcher.afterDispatch', compact('request', 'response'));
100:
101: return $afterEvent->getData('response');
102: }
103:
104: /**
105: * Invoke a controller's action and wrapping methods.
106: *
107: * @param \Cake\Controller\Controller $controller The controller to invoke.
108: * @return \Cake\Http\Response The response
109: * @throws \LogicException If the controller action returns a non-response value.
110: */
111: protected function _invoke(Controller $controller)
112: {
113: $this->dispatchEvent('Dispatcher.invokeController', ['controller' => $controller]);
114:
115: $result = $controller->startupProcess();
116: if ($result instanceof Response) {
117: return $result;
118: }
119:
120: $response = $controller->invokeAction();
121: if ($response !== null && !($response instanceof Response)) {
122: throw new LogicException('Controller actions can only return Cake\Http\Response or null.');
123: }
124:
125: if (!$response && $controller->isAutoRenderEnabled()) {
126: $controller->render();
127: }
128:
129: $result = $controller->shutdownProcess();
130: if ($result instanceof Response) {
131: return $result;
132: }
133: if (!$response) {
134: $response = $controller->getResponse();
135: }
136:
137: return $response;
138: }
139:
140: /**
141: * Add a filter to this dispatcher.
142: *
143: * The added filter will be attached to the event manager used
144: * by this dispatcher.
145: *
146: * @param \Cake\Event\EventListenerInterface $filter The filter to connect. Can be
147: * any EventListenerInterface. Typically an instance of \Cake\Routing\DispatcherFilter.
148: * @return void
149: * @deprecated This is only available for backwards compatibility with DispatchFilters
150: */
151: public function addFilter(EventListenerInterface $filter)
152: {
153: deprecationWarning(
154: 'ActionDispatcher::addFilter() is deprecated. ' .
155: 'This is only available for backwards compatibility with DispatchFilters'
156: );
157:
158: $this->filters[] = $filter;
159: $this->getEventManager()->on($filter);
160: }
161:
162: /**
163: * Get the connected filters.
164: *
165: * @return array
166: */
167: public function getFilters()
168: {
169: return $this->filters;
170: }
171: }
172: