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 1.2.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Controller;
16:
17: use Cake\Core\InstanceConfigTrait;
18: use Cake\Event\EventListenerInterface;
19: use Cake\Log\LogTrait;
20:
21: /**
22: * Base class for an individual Component. Components provide reusable bits of
23: * controller logic that can be composed into a controller. Components also
24: * provide request life-cycle callbacks for injecting logic at specific points.
25: *
26: * ### Initialize hook
27: *
28: * Like Controller and Table, this class has an initialize() hook that you can use
29: * to add custom 'constructor' logic. It is important to remember that each request
30: * (and sub-request) will only make one instance of any given component.
31: *
32: * ### Life cycle callbacks
33: *
34: * Components can provide several callbacks that are fired at various stages of the request
35: * cycle. The available callbacks are:
36: *
37: * - `beforeFilter(Event $event)`
38: * Called before the controller's beforeFilter method by default.
39: * - `startup(Event $event)`
40: * Called after the controller's beforeFilter method, and before the
41: * controller action is called.
42: * - `beforeRender(Event $event)`
43: * Called before the Controller beforeRender, and before the view class is loaded.
44: * - `shutdown(Event $event)`
45: * Called after the action is complete and the view has been rendered but
46: * before Controller::afterFilter().
47: * - `beforeRedirect(Event $event $url, Response $response)`
48: * Called before a redirect is done. Allows you to change the URL that will
49: * be redirected to by returning a Response instance with new URL set using
50: * Response::location(). Redirection can be prevented by stopping the event
51: * propagation.
52: *
53: * While the controller is not an explicit argument for the callback methods it
54: * is the subject of each event and can be fetched using Event::getSubject().
55: *
56: * @link https://book.cakephp.org/3.0/en/controllers/components.html
57: * @see \Cake\Controller\Controller::$components
58: */
59: class Component implements EventListenerInterface
60: {
61: use InstanceConfigTrait;
62: use LogTrait;
63:
64: /**
65: * Request object
66: *
67: * @var \Cake\Http\ServerRequest
68: * @deprecated 3.4.0 Storing references to the request is deprecated. Use Component::getController()
69: * or callback $event->getSubject() to access the controller & request instead.
70: */
71: public $request;
72:
73: /**
74: * Response object
75: *
76: * @var \Cake\Http\Response
77: * @deprecated 3.4.0 Storing references to the response is deprecated. Use Component::getController()
78: * or callback $event->getSubject() to access the controller & response instead.
79: */
80: public $response;
81:
82: /**
83: * Component registry class used to lazy load components.
84: *
85: * @var \Cake\Controller\ComponentRegistry
86: */
87: protected $_registry;
88:
89: /**
90: * Other Components this component uses.
91: *
92: * @var array
93: */
94: public $components = [];
95:
96: /**
97: * Default config
98: *
99: * These are merged with user-provided config when the component is used.
100: *
101: * @var array
102: */
103: protected $_defaultConfig = [];
104:
105: /**
106: * A component lookup table used to lazy load component objects.
107: *
108: * @var array
109: */
110: protected $_componentMap = [];
111:
112: /**
113: * Constructor
114: *
115: * @param \Cake\Controller\ComponentRegistry $registry A ComponentRegistry this component can use to lazy load its components
116: * @param array $config Array of configuration settings.
117: */
118: public function __construct(ComponentRegistry $registry, array $config = [])
119: {
120: $this->_registry = $registry;
121: $controller = $registry->getController();
122: if ($controller) {
123: $this->request =& $controller->request;
124: $this->response =& $controller->response;
125: }
126:
127: $this->setConfig($config);
128:
129: if ($this->components) {
130: $this->_componentMap = $registry->normalizeArray($this->components);
131: }
132: $this->initialize($config);
133: }
134:
135: /**
136: * Get the controller this component is bound to.
137: *
138: * @return \Cake\Controller\Controller The bound controller.
139: */
140: public function getController()
141: {
142: return $this->_registry->getController();
143: }
144:
145: /**
146: * Constructor hook method.
147: *
148: * Implement this method to avoid having to overwrite
149: * the constructor and call parent.
150: *
151: * @param array $config The configuration settings provided to this component.
152: * @return void
153: */
154: public function initialize(array $config)
155: {
156: }
157:
158: /**
159: * Magic method for lazy loading $components.
160: *
161: * @param string $name Name of component to get.
162: * @return mixed A Component object or null.
163: */
164: public function __get($name)
165: {
166: if (isset($this->_componentMap[$name]) && !isset($this->{$name})) {
167: $config = (array)$this->_componentMap[$name]['config'] + ['enabled' => false];
168: $this->{$name} = $this->_registry->load($this->_componentMap[$name]['class'], $config);
169: }
170: if (!isset($this->{$name})) {
171: return null;
172: }
173:
174: return $this->{$name};
175: }
176:
177: /**
178: * Get the Controller callbacks this Component is interested in.
179: *
180: * Uses Conventions to map controller events to standard component
181: * callback method names. By defining one of the callback methods a
182: * component is assumed to be interested in the related event.
183: *
184: * Override this method if you need to add non-conventional event listeners.
185: * Or if you want components to listen to non-standard events.
186: *
187: * @return array
188: */
189: public function implementedEvents()
190: {
191: $eventMap = [
192: 'Controller.initialize' => 'beforeFilter',
193: 'Controller.startup' => 'startup',
194: 'Controller.beforeRender' => 'beforeRender',
195: 'Controller.beforeRedirect' => 'beforeRedirect',
196: 'Controller.shutdown' => 'shutdown',
197: ];
198: $events = [];
199: foreach ($eventMap as $event => $method) {
200: if (method_exists($this, $method)) {
201: $events[$event] = $method;
202: }
203: }
204:
205: return $events;
206: }
207:
208: /**
209: * Returns an array that can be used to describe the internal state of this
210: * object.
211: *
212: * @return array
213: */
214: public function __debugInfo()
215: {
216: return [
217: 'components' => $this->components,
218: 'implementedEvents' => $this->implementedEvents(),
219: '_config' => $this->getConfig(),
220: ];
221: }
222: }
223: