Seditio Source
Root |
./othercms/croogo-4.0.7/vendor/cakephp/cakephp/src/ORM/Behavior.php
<?php
/**
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 * @link          https://cakephp.org CakePHP(tm) Project
 * @since         3.0.0
 * @license       https://opensource.org/licenses/mit-license.php MIT License
 */
namespace Cake\ORM;

use
Cake\Core\Exception\Exception;
use
Cake\Core\InstanceConfigTrait;
use
Cake\Event\EventListenerInterface;
use
ReflectionClass;
use
ReflectionMethod;

/**
 * Base class for behaviors.
 *
 * Behaviors allow you to simulate mixins, and create
 * reusable blocks of application logic, that can be reused across
 * several models. Behaviors also provide a way to hook into model
 * callbacks and augment their behavior.
 *
 * ### Mixin methods
 *
 * Behaviors can provide mixin like features by declaring public
 * methods. These methods will be accessible on the tables the
 * behavior has been added to.
 *
 * ```
 * function doSomething($arg1, $arg2) {
 *   // do something
 * }
 * ```
 *
 * Would be called like `$table->doSomething($arg1, $arg2);`.
 *
 * ### Callback methods
 *
 * Behaviors can listen to any events fired on a Table. By default
 * CakePHP provides a number of lifecycle events your behaviors can
 * listen to:
 *
 * - `beforeFind(Event $event, Query $query, ArrayObject $options, boolean $primary)`
 *   Fired before each find operation. By stopping the event and supplying a
 *   return value you can bypass the find operation entirely. Any changes done
 *   to the $query instance will be retained for the rest of the find. The
 *   $primary parameter indicates whether or not this is the root query,
 *   or an associated query.
 *
 * - `buildValidator(Event $event, Validator $validator, string $name)`
 *   Fired when the validator object identified by $name is being built. You can use this
 *   callback to add validation rules or add validation providers.
 *
 * - `buildRules(Event $event, RulesChecker $rules)`
 *   Fired when the rules checking object for the table is being built. You can use this
 *   callback to add more rules to the set.
 *
 * - `beforeRules(Event $event, EntityInterface $entity, ArrayObject $options, $operation)`
 *   Fired before an entity is validated using by a rules checker. By stopping this event,
 *   you can return the final value of the rules checking operation.
 *
 * - `afterRules(Event $event, EntityInterface $entity, ArrayObject $options, bool $result, $operation)`
 *   Fired after the rules have been checked on the entity. By stopping this event,
 *   you can return the final value of the rules checking operation.
 *
 * - `beforeSave(Event $event, EntityInterface $entity, ArrayObject $options)`
 *   Fired before each entity is saved. Stopping this event will abort the save
 *   operation. When the event is stopped the result of the event will be returned.
 *
 * - `afterSave(Event $event, EntityInterface $entity, ArrayObject $options)`
 *   Fired after an entity is saved.
 *
 * - `beforeDelete(Event $event, EntityInterface $entity, ArrayObject $options)`
 *   Fired before an entity is deleted. By stopping this event you will abort
 *   the delete operation.
 *
 * - `afterDelete(Event $event, EntityInterface $entity, ArrayObject $options)`
 *   Fired after an entity has been deleted.
 *
 * In addition to the core events, behaviors can respond to any
 * event fired from your Table classes including custom application
 * specific ones.
 *
 * You can set the priority of a behaviors callbacks by using the
 * `priority` setting when attaching a behavior. This will set the
 * priority for all the callbacks a behavior provides.
 *
 * ### Finder methods
 *
 * Behaviors can provide finder methods that hook into a Table's
 * find() method. Custom finders are a great way to provide preset
 * queries that relate to your behavior. For example a SluggableBehavior
 * could provide a find('slugged') finder. Behavior finders
 * are implemented the same as other finders. Any method
 * starting with `find` will be setup as a finder. Your finder
 * methods should expect the following arguments:
 *
 * ```
 * findSlugged(Query $query, array $options)
 * ```
 *
 * @see \Cake\ORM\Table::addBehavior()
 * @see \Cake\Event\EventManager
 */
class Behavior implements EventListenerInterface
{
    use
InstanceConfigTrait;

   
/**
     * Table instance.
     *
     * @var \Cake\ORM\Table
     */
   
protected $_table;

   
/**
     * Reflection method cache for behaviors.
     *
     * Stores the reflected method + finder methods per class.
     * This prevents reflecting the same class multiple times in a single process.
     *
     * @var array
     */
   
protected static $_reflectionCache = [];

   
/**
     * Default configuration
     *
     * These are merged with user-provided configuration when the behavior is used.
     *
     * @var array
     */
   
protected $_defaultConfig = [];

   
/**
     * Constructor
     *
     * Merges config with the default and store in the config property
     *
     * @param \Cake\ORM\Table $table The table this behavior is attached to.
     * @param array $config The config for this behavior.
     */
   
public function __construct(Table $table, array $config = [])
    {
       
$config = $this->_resolveMethodAliases(
           
'implementedFinders',
           
$this->_defaultConfig,
           
$config
       
);
       
$config = $this->_resolveMethodAliases(
           
'implementedMethods',
           
$this->_defaultConfig,
           
$config
       
);
       
$this->_table = $table;
       
$this->setConfig($config);
       
$this->initialize($config);
    }

   
/**
     * Constructor hook method.
     *
     * Implement this method to avoid having to overwrite
     * the constructor and call parent.
     *
     * @param array $config The configuration settings provided to this behavior.
     * @return void
     */
   
public function initialize(array $config)
    {
    }

   
/**
     * Get the table instance this behavior is bound to.
     *
     * @return \Cake\ORM\Table The bound table instance.
     */
   
public function getTable()
    {
        return
$this->_table;
    }

   
/**
     * Removes aliased methods that would otherwise be duplicated by userland configuration.
     *
     * @param string $key The key to filter.
     * @param array $defaults The default method mappings.
     * @param array $config The customized method mappings.
     * @return array A de-duped list of config data.
     */
   
protected function _resolveMethodAliases($key, $defaults, $config)
    {
        if (!isset(
$defaults[$key], $config[$key])) {
            return
$config;
        }
        if (isset(
$config[$key]) && $config[$key] === []) {
           
$this->setConfig($key, [], false);
            unset(
$config[$key]);

            return
$config;
        }

       
$indexed = array_flip($defaults[$key]);
       
$indexedCustom = array_flip($config[$key]);
        foreach (
$indexed as $method => $alias) {
            if (!isset(
$indexedCustom[$method])) {
               
$indexedCustom[$method] = $alias;
            }
        }
       
$this->setConfig($key, array_flip($indexedCustom), false);
        unset(
$config[$key]);

        return
$config;
    }

   
/**
     * verifyConfig
     *
     * Checks that implemented keys contain values pointing at callable.
     *
     * @return void
     * @throws \Cake\Core\Exception\Exception if config are invalid
     */
   
public function verifyConfig()
    {
       
$keys = ['implementedFinders', 'implementedMethods'];
        foreach (
$keys as $key) {
            if (!isset(
$this->_config[$key])) {
                continue;
            }

            foreach (
$this->_config[$key] as $method) {
                if (!
is_callable([$this, $method])) {
                    throw new
Exception(sprintf('The method %s is not callable on class %s', $method, get_class($this)));
                }
            }
        }
    }

   
/**
     * Gets the Model callbacks this behavior is interested in.
     *
     * By defining one of the callback methods a behavior is assumed
     * to be interested in the related event.
     *
     * Override this method if you need to add non-conventional event listeners.
     * Or if you want your behavior to listen to non-standard events.
     *
     * @return array
     */
   
public function implementedEvents()
    {
       
$eventMap = [
           
'Model.beforeMarshal' => 'beforeMarshal',
           
'Model.beforeFind' => 'beforeFind',
           
'Model.beforeSave' => 'beforeSave',
           
'Model.afterSave' => 'afterSave',
           
'Model.afterSaveCommit' => 'afterSaveCommit',
           
'Model.beforeDelete' => 'beforeDelete',
           
'Model.afterDelete' => 'afterDelete',
           
'Model.afterDeleteCommit' => 'afterDeleteCommit',
           
'Model.buildValidator' => 'buildValidator',
           
'Model.buildRules' => 'buildRules',
           
'Model.beforeRules' => 'beforeRules',
           
'Model.afterRules' => 'afterRules',
        ];
       
$config = $this->getConfig();
       
$priority = isset($config['priority']) ? $config['priority'] : null;
       
$events = [];

        foreach (
$eventMap as $event => $method) {
            if (!
method_exists($this, $method)) {
                continue;
            }
            if (
$priority === null) {
               
$events[$event] = $method;
            } else {
               
$events[$event] = [
                   
'callable' => $method,
                   
'priority' => $priority,
                ];
            }
        }

        return
$events;
    }

   
/**
     * implementedFinders
     *
     * Provides an alias->methodname map of which finders a behavior implements. Example:
     *
     * ```
     *  [
     *    'this' => 'findThis',
     *    'alias' => 'findMethodName'
     *  ]
     * ```
     *
     * With the above example, a call to `$Table->find('this')` will call `$Behavior->findThis()`
     * and a call to `$Table->find('alias')` will call `$Behavior->findMethodName()`
     *
     * It is recommended, though not required, to define implementedFinders in the config property
     * of child classes such that it is not necessary to use reflections to derive the available
     * method list. See core behaviors for examples
     *
     * @return array
     * @throws \ReflectionException
     */
   
public function implementedFinders()
    {
       
$methods = $this->getConfig('implementedFinders');
        if (isset(
$methods)) {
            return
$methods;
        }

        return
$this->_reflectionCache()['finders'];
    }

   
/**
     * implementedMethods
     *
     * Provides an alias->methodname map of which methods a behavior implements. Example:
     *
     * ```
     *  [
     *    'method' => 'method',
     *    'aliasedmethod' => 'somethingElse'
     *  ]
     * ```
     *
     * With the above example, a call to `$Table->method()` will call `$Behavior->method()`
     * and a call to `$Table->aliasedmethod()` will call `$Behavior->somethingElse()`
     *
     * It is recommended, though not required, to define implementedFinders in the config property
     * of child classes such that it is not necessary to use reflections to derive the available
     * method list. See core behaviors for examples
     *
     * @return array
     * @throws \ReflectionException
     */
   
public function implementedMethods()
    {
       
$methods = $this->getConfig('implementedMethods');
        if (isset(
$methods)) {
            return
$methods;
        }

        return
$this->_reflectionCache()['methods'];
    }

   
/**
     * Gets the methods implemented by this behavior
     *
     * Uses the implementedEvents() method to exclude callback methods.
     * Methods starting with `_` will be ignored, as will methods
     * declared on Cake\ORM\Behavior
     *
     * @return array
     * @throws \ReflectionException
     */
   
protected function _reflectionCache()
    {
       
$class = get_class($this);
        if (isset(
self::$_reflectionCache[$class])) {
            return
self::$_reflectionCache[$class];
        }

       
$events = $this->implementedEvents();
       
$eventMethods = [];
        foreach (
$events as $e => $binding) {
            if (
is_array($binding) && isset($binding['callable'])) {
               
/** @var string $callable */
               
$callable = $binding['callable'];
               
$binding = $callable;
            }
           
$eventMethods[$binding] = true;
        }

       
$baseClass = self::class;
        if (isset(
self::$_reflectionCache[$baseClass])) {
           
$baseMethods = self::$_reflectionCache[$baseClass];
        } else {
           
$baseMethods = get_class_methods($baseClass);
           
self::$_reflectionCache[$baseClass] = $baseMethods;
        }

       
$return = [
           
'finders' => [],
           
'methods' => [],
        ];

       
$reflection = new ReflectionClass($class);

        foreach (
$reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
           
$methodName = $method->getName();
            if (
               
in_array($methodName, $baseMethods, true) ||
                isset(
$eventMethods[$methodName])
            ) {
                continue;
            }

            if (
substr($methodName, 0, 4) === 'find') {
               
$return['finders'][lcfirst(substr($methodName, 4))] = $methodName;
            } else {
               
$return['methods'][$methodName] = $methodName;
            }
        }

        return
self::$_reflectionCache[$class] = $return;
    }
}