Component.php 6.4 KB

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