PageRenderTime 29ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/cakephp/cakephp/src/ORM/Behavior.php

https://gitlab.com/vannh/portal_training
PHP | 399 lines | 155 code | 26 blank | 218 comment | 19 complexity | 7ddedac413d35433f4c10c2013068cbc MD5 | raw file
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://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. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\ORM;
  16. use Cake\Core\Exception\Exception;
  17. use Cake\Core\InstanceConfigTrait;
  18. use Cake\Event\EventListenerInterface;
  19. /**
  20. * Base class for behaviors.
  21. *
  22. * Behaviors allow you to simulate mixins, and create
  23. * reusable blocks of application logic, that can be reused across
  24. * several models. Behaviors also provide a way to hook into model
  25. * callbacks and augment their behavior.
  26. *
  27. * ### Mixin methods
  28. *
  29. * Behaviors can provide mixin like features by declaring public
  30. * methods. These methods will be accessible on the tables the
  31. * behavior has been added to.
  32. *
  33. * ```
  34. * function doSomething($arg1, $arg2) {
  35. * // do something
  36. * }
  37. * ```
  38. *
  39. * Would be called like `$table->doSomething($arg1, $arg2);`.
  40. *
  41. * ### Callback methods
  42. *
  43. * Behaviors can listen to any events fired on a Table. By default
  44. * CakePHP provides a number of lifecycle events your behaviors can
  45. * listen to:
  46. *
  47. * - `beforeFind(Event $event, Query $query, ArrayObject $options, boolean $primary)`
  48. * Fired before each find operation. By stopping the event and supplying a
  49. * return value you can bypass the find operation entirely. Any changes done
  50. * to the $query instance will be retained for the rest of the find. The
  51. * $primary parameter indicates whether or not this is the root query,
  52. * or an associated query.
  53. *
  54. * - `buildValidator(Event $event, Validator $validator, string $name)`
  55. * Fired when the validator object identified by $name is being built. You can use this
  56. * callback to add validation rules or add validation providers.
  57. *
  58. * - `buildRules(Event $event, RulesChecker $rules)`
  59. * Fired when the rules checking object for the table is being built. You can use this
  60. * callback to add more rules to the set.
  61. *
  62. * - `beforeRules(Event $event, Entity $entity, ArrayObject $options, $operation)`
  63. * Fired before an entity is validated using by a rules checker. By stopping this event,
  64. * you can return the final value of the rules checking operation.
  65. *
  66. * - `afterRules(Event $event, Entity $entity, ArrayObject $options, bool $result, $operation)`
  67. * Fired after the rules have been checked on the entity. By stopping this event,
  68. * you can return the final value of the rules checking operation.
  69. *
  70. * - `beforeSave(Event $event, Entity $entity, ArrayObject $options)`
  71. * Fired before each entity is saved. Stopping this event will abort the save
  72. * operation. When the event is stopped the result of the event will be returned.
  73. *
  74. * - `afterSave(Event $event, Entity $entity, ArrayObject $options)`
  75. * Fired after an entity is saved.
  76. *
  77. * - `beforeDelete(Event $event, Entity $entity, ArrayObject $options)`
  78. * Fired before an entity is deleted. By stopping this event you will abort
  79. * the delete operation.
  80. *
  81. * - `afterDelete(Event $event, Entity $entity, ArrayObject $options)`
  82. * Fired after an entity has been deleted.
  83. *
  84. * In addition to the core events, behaviors can respond to any
  85. * event fired from your Table classes including custom application
  86. * specific ones.
  87. *
  88. * You can set the priority of a behaviors callbacks by using the
  89. * `priority` setting when attaching a behavior. This will set the
  90. * priority for all the callbacks a behavior provides.
  91. *
  92. * ### Finder methods
  93. *
  94. * Behaviors can provide finder methods that hook into a Table's
  95. * find() method. Custom finders are a great way to provide preset
  96. * queries that relate to your behavior. For example a SluggableBehavior
  97. * could provide a find('slugged') finder. Behavior finders
  98. * are implemented the same as other finders. Any method
  99. * starting with `find` will be setup as a finder. Your finder
  100. * methods should expect the following arguments:
  101. *
  102. * ```
  103. * findSlugged(Query $query, array $options)
  104. * ```
  105. *
  106. * @see \Cake\ORM\Table::addBehavior()
  107. * @see \Cake\Event\EventManager
  108. */
  109. class Behavior implements EventListenerInterface
  110. {
  111. use InstanceConfigTrait;
  112. /**
  113. * Table instance.
  114. *
  115. * @var \Cake\ORM\Table
  116. */
  117. protected $_table;
  118. /**
  119. * Reflection method cache for behaviors.
  120. *
  121. * Stores the reflected method + finder methods per class.
  122. * This prevents reflecting the same class multiple times in a single process.
  123. *
  124. * @var array
  125. */
  126. protected static $_reflectionCache = [];
  127. /**
  128. * Default configuration
  129. *
  130. * These are merged with user-provided configuration when the behavior is used.
  131. *
  132. * @var array
  133. */
  134. protected $_defaultConfig = [];
  135. /**
  136. * Constructor
  137. *
  138. * Merges config with the default and store in the config property
  139. *
  140. * @param \Cake\ORM\Table $table The table this behavior is attached to.
  141. * @param array $config The config for this behavior.
  142. */
  143. public function __construct(Table $table, array $config = [])
  144. {
  145. $config = $this->_resolveMethodAliases(
  146. 'implementedFinders',
  147. $this->_defaultConfig,
  148. $config
  149. );
  150. $config = $this->_resolveMethodAliases(
  151. 'implementedMethods',
  152. $this->_defaultConfig,
  153. $config
  154. );
  155. $this->_table = $table;
  156. $this->config($config);
  157. $this->initialize($config);
  158. }
  159. /**
  160. * Constructor hook method.
  161. *
  162. * Implement this method to avoid having to overwrite
  163. * the constructor and call parent.
  164. *
  165. * @param array $config The configuration settings provided to this behavior.
  166. * @return void
  167. */
  168. public function initialize(array $config)
  169. {
  170. }
  171. /**
  172. * Removes aliased methods that would otherwise be duplicated by userland configuration.
  173. *
  174. * @param string $key The key to filter.
  175. * @param array $defaults The default method mappings.
  176. * @param array $config The customized method mappings.
  177. * @return array A de-duped list of config data.
  178. */
  179. protected function _resolveMethodAliases($key, $defaults, $config)
  180. {
  181. if (!isset($defaults[$key], $config[$key])) {
  182. return $config;
  183. }
  184. if (isset($config[$key]) && $config[$key] === []) {
  185. $this->config($key, [], false);
  186. unset($config[$key]);
  187. return $config;
  188. }
  189. $indexed = array_flip($defaults[$key]);
  190. $indexedCustom = array_flip($config[$key]);
  191. foreach ($indexed as $method => $alias) {
  192. if (!isset($indexedCustom[$method])) {
  193. $indexedCustom[$method] = $alias;
  194. }
  195. }
  196. $this->config($key, array_flip($indexedCustom), false);
  197. unset($config[$key]);
  198. return $config;
  199. }
  200. /**
  201. * verifyConfig
  202. *
  203. * Checks that implemented keys contain values pointing at callable.
  204. *
  205. * @return void
  206. * @throws \Cake\Core\Exception\Exception if config are invalid
  207. */
  208. public function verifyConfig()
  209. {
  210. $keys = ['implementedFinders', 'implementedMethods'];
  211. foreach ($keys as $key) {
  212. if (!isset($this->_config[$key])) {
  213. continue;
  214. }
  215. foreach ($this->_config[$key] as $method) {
  216. if (!is_callable([$this, $method])) {
  217. throw new Exception(sprintf('The method %s is not callable on class %s', $method, get_class($this)));
  218. }
  219. }
  220. }
  221. }
  222. /**
  223. * Gets the Model callbacks this behavior is interested in.
  224. *
  225. * By defining one of the callback methods a behavior is assumed
  226. * to be interested in the related event.
  227. *
  228. * Override this method if you need to add non-conventional event listeners.
  229. * Or if you want your behavior to listen to non-standard events.
  230. *
  231. * @return array
  232. */
  233. public function implementedEvents()
  234. {
  235. $eventMap = [
  236. 'Model.beforeMarshal' => 'beforeMarshal',
  237. 'Model.beforeFind' => 'beforeFind',
  238. 'Model.beforeSave' => 'beforeSave',
  239. 'Model.afterSave' => 'afterSave',
  240. 'Model.beforeDelete' => 'beforeDelete',
  241. 'Model.afterDelete' => 'afterDelete',
  242. 'Model.buildValidator' => 'buildValidator',
  243. 'Model.buildRules' => 'buildRules',
  244. 'Model.beforeRules' => 'beforeRules',
  245. 'Model.afterRules' => 'afterRules',
  246. ];
  247. $config = $this->config();
  248. $priority = isset($config['priority']) ? $config['priority'] : null;
  249. $events = [];
  250. foreach ($eventMap as $event => $method) {
  251. if (!method_exists($this, $method)) {
  252. continue;
  253. }
  254. if ($priority === null) {
  255. $events[$event] = $method;
  256. } else {
  257. $events[$event] = [
  258. 'callable' => $method,
  259. 'priority' => $priority
  260. ];
  261. }
  262. }
  263. return $events;
  264. }
  265. /**
  266. * implementedFinders
  267. *
  268. * Provides an alias->methodname map of which finders a behavior implements. Example:
  269. *
  270. * ```
  271. * [
  272. * 'this' => 'findThis',
  273. * 'alias' => 'findMethodName'
  274. * ]
  275. * ```
  276. *
  277. * With the above example, a call to `$Table->find('this')` will call `$Behavior->findThis()`
  278. * and a call to `$Table->find('alias')` will call `$Behavior->findMethodName()`
  279. *
  280. * It is recommended, though not required, to define implementedFinders in the config property
  281. * of child classes such that it is not necessary to use reflections to derive the available
  282. * method list. See core behaviors for examples
  283. *
  284. * @return array
  285. */
  286. public function implementedFinders()
  287. {
  288. $methods = $this->config('implementedFinders');
  289. if (isset($methods)) {
  290. return $methods;
  291. }
  292. return $this->_reflectionCache()['finders'];
  293. }
  294. /**
  295. * implementedMethods
  296. *
  297. * Provides an alias->methodname map of which methods a behavior implements. Example:
  298. *
  299. * ```
  300. * [
  301. * 'method' => 'method',
  302. * 'aliasedmethod' => 'somethingElse'
  303. * ]
  304. * ```
  305. *
  306. * With the above example, a call to `$Table->method()` will call `$Behavior->method()`
  307. * and a call to `$Table->aliasedmethod()` will call `$Behavior->somethingElse()`
  308. *
  309. * It is recommended, though not required, to define implementedFinders in the config property
  310. * of child classes such that it is not necessary to use reflections to derive the available
  311. * method list. See core behaviors for examples
  312. *
  313. * @return array
  314. */
  315. public function implementedMethods()
  316. {
  317. $methods = $this->config('implementedMethods');
  318. if (isset($methods)) {
  319. return $methods;
  320. }
  321. return $this->_reflectionCache()['methods'];
  322. }
  323. /**
  324. * Gets the methods implemented by this behavior
  325. *
  326. * Uses the implementedEvents() method to exclude callback methods.
  327. * Methods starting with `_` will be ignored, as will methods
  328. * declared on Cake\ORM\Behavior
  329. *
  330. * @return array
  331. */
  332. protected function _reflectionCache()
  333. {
  334. $class = get_class($this);
  335. if (isset(self::$_reflectionCache[$class])) {
  336. return self::$_reflectionCache[$class];
  337. }
  338. $events = $this->implementedEvents();
  339. $eventMethods = [];
  340. foreach ($events as $e => $binding) {
  341. if (is_array($binding) && isset($binding['callable'])) {
  342. $binding = $binding['callable'];
  343. }
  344. $eventMethods[$binding] = true;
  345. }
  346. $baseClass = 'Cake\ORM\Behavior';
  347. if (isset(self::$_reflectionCache[$baseClass])) {
  348. $baseMethods = self::$_reflectionCache[$baseClass];
  349. } else {
  350. $baseMethods = get_class_methods($baseClass);
  351. self::$_reflectionCache[$baseClass] = $baseMethods;
  352. }
  353. $return = [
  354. 'finders' => [],
  355. 'methods' => []
  356. ];
  357. $reflection = new \ReflectionClass($class);
  358. foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
  359. $methodName = $method->getName();
  360. if (in_array($methodName, $baseMethods) ||
  361. isset($eventMethods[$methodName])
  362. ) {
  363. continue;
  364. }
  365. if (substr($methodName, 0, 4) === 'find') {
  366. $return['finders'][lcfirst(substr($methodName, 4))] = $methodName;
  367. } else {
  368. $return['methods'][$methodName] = $methodName;
  369. }
  370. }
  371. return self::$_reflectionCache[$class] = $return;
  372. }
  373. }