Subversion-Projekte lars-tiefland.cakephp

Revision

Blame | Letzte Änderung | Log anzeigen | RSS feed

<?php
/* SVN FILE: $Id: behavior.php 8004 2009-01-16 20:15:21Z gwoo $ */
/**
 * Model behaviors base class.
 *
 * Adds methods and automagic functionality to Cake Models.
 *
 * PHP versions 4 and 5
 *
 * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
 * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @filesource
 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
 * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
 * @package       cake
 * @subpackage    cake.cake.libs.model
 * @since         CakePHP(tm) v 1.2.0.0
 * @version       $Revision: 8004 $
 * @modifiedby    $LastChangedBy: gwoo $
 * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
 */
/**
 * Model behavior base class.
 *
 * Defines the Behavior interface, and contains common model interaction functionality.
 *
 * @package       cake
 * @subpackage    cake.cake.libs.model
 */
class ModelBehavior extends Object {
/**
 * Contains configuration settings for use with individual model objects.  This
 * is used because if multiple models use this Behavior, each will use the same
 * object instance.  Individual model settings should be stored as an
 * associative array, keyed off of the model name.
 *
 * @var array
 * @access public
 * @see Model::$alias
 */
        var $settings = array();
/**
 * Allows the mapping of preg-compatible regular expressions to public or
 * private methods in this class, where the array key is a /-delimited regular
 * expression, and the value is a class method.  Similar to the functionality of
 * the findBy* / findAllBy* magic methods.
 *
 * @var array
 * @access public
 */
        var $mapMethods = array();
/**
 * Setup this behavior with the specified configuration settings.
 *
 * @param object $model Model using this behavior
 * @param array $config Configuration settings for $model
 * @access public
 */
        function setup(&$model, $config = array()) { }
/**
 * Clean up any initialization this behavior has done on a model.  Called when a behavior is dynamically
 * detached from a model using Model::detach().
 *
 * @param object $model Model using this behavior
 * @access public
 * @see BehaviorCollection::detach()
 */
        function cleanup(&$model) {
                if (isset($this->settings[$model->alias])) {
                        unset($this->settings[$model->alias]);
                }
        }
/**
 * Before find callback
 *
 * @param object $model Model using this behavior
 * @param array $queryData Data used to execute this query, i.e. conditions, order, etc.
 * @return boolean True if the operation should continue, false if it should abort
 * @access public
 */
        function beforeFind(&$model, $query) { }
/**
 * After find callback. Can be used to modify any results returned by find and findAll.
 *
 * @param object $model Model using this behavior
 * @param mixed $results The results of the find operation
 * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association)
 * @return mixed Result of the find operation
 * @access public
 */
        function afterFind(&$model, $results, $primary) { }
/**
 * Before validate callback
 *
 * @param object $model Model using this behavior
 * @return boolean True if validate operation should continue, false to abort
 * @access public
 */
        function beforeValidate(&$model) { }
/**
 * Before save callback
 *
 * @param object $model Model using this behavior
 * @return boolean True if the operation should continue, false if it should abort
 * @access public
 */
        function beforeSave(&$model) { }
/**
 * After save callback
 *
 * @param object $model Model using this behavior
 * @param boolean $created True if this save created a new record
 * @access public
 */
        function afterSave(&$model, $created) { }
/**
 * Before delete callback
 *
 * @param object $model Model using this behavior
 * @param boolean $cascade If true records that depend on this record will also be deleted
 * @return boolean True if the operation should continue, false if it should abort
 * @access public
 */
        function beforeDelete(&$model, $cascade = true) { }
/**
 * After delete callback
 *
 * @param object $model Model using this behavior
 * @access public
 */
        function afterDelete(&$model) { }
/**
 * DataSource error callback
 *
 * @param object $model Model using this behavior
 * @param string $error Error generated in DataSource
 * @access public
 */
        function onError(&$model, $error) { }
/**
 * Overrides Object::dispatchMethod to account for PHP4's broken reference support
 *
 * @see Object::dispatchMethod
 * @access public
 * @return mixed
 */
        function dispatchMethod(&$model, $method, $params = array()) {
                if (empty($params)) {
                        return $this->{$method}($model);
                }
                $params = array_values($params);

                switch (count($params)) {
                        case 1:
                                return $this->{$method}($model, $params[0]);
                        case 2:
                                return $this->{$method}($model, $params[0], $params[1]);
                        case 3:
                                return $this->{$method}($model, $params[0], $params[1], $params[2]);
                        case 4:
                                return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3]);
                        case 5:
                                return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3], $params[4]);
                        default:
                                array_unshift($params, $model);
                                return call_user_func_array(array(&$this, $method), $params);
                        break;
                }
        }
/**
 * If $model's whitelist property is non-empty, $field will be added to it.
 * Note: this method should *only* be used in beforeValidate or beforeSave to ensure
 * that it only modifies the whitelist for the current save operation.  Also make sure
 * you explicitly set the value of the field which you are allowing.
 *
 * @param object $model Model using this behavior
 * @param string $field Field to be added to $model's whitelist
 * @access protected
 * @return void
 */
        function _addToWhitelist(&$model, $field) {
                if (is_array($field)) {
                        foreach ($field as $f) {
                                $this->_addToWhitelist($model, $f);
                        }
                        return;
                }
                if (!empty($model->whitelist) && !in_array($field, $model->whitelist)) {
                        $model->whitelist[] = $field;
                }
        }
}

/**
 * Model behavior collection class.
 *
 * Defines the Behavior interface, and contains common model interaction functionality.
 *
 * @package       cake
 * @subpackage    cake.cake.libs.model
 */
class BehaviorCollection extends Object {

/**
 * Stores a reference to the attached name
 *
 * @var object
 */
        var $modelName = null;
/**
 * Lists the currently-attached behavior objects
 *
 * @var array
 * @access private
 */
        var $_attached = array();
/**
 * Lists the currently-attached behavior objects which are disabled
 *
 * @var array
 * @access private
 */
        var $_disabled = array();
/**
 * Keeps a list of all methods of attached behaviors
 *
 * @var array
 */
        var $__methods = array();
/**
 * Keeps a list of all methods which have been mapped with regular expressions
 *
 * @var array
 */
        var $__mappedMethods = array();
/**
 * Attaches a model object and loads a list of behaviors
 *
 * @access public
 * @return void
 */
        function init($modelName, $behaviors = array()) {
                $this->modelName = $modelName;

                if (!empty($behaviors)) {
                        foreach (Set::normalize($behaviors) as $behavior => $config) {
                                $this->attach($behavior, $config);
                        }
                }
        }
/**
 * Attaches a behavior to a model
 *
 * @param string $behavior CamelCased name of the behavior to load
 * @param array $config Behavior configuration parameters
 * @return boolean True on success, false on failure
 * @access public
 */
        function attach($behavior, $config = array()) {
                $name = $behavior;
                if (strpos($behavior, '.')) {
                        list($plugin, $name) = explode('.', $behavior, 2);
                }
                $class = $name . 'Behavior';

                if (!App::import('Behavior', $behavior)) {
                        return false;
                }

                if (!isset($this->{$name})) {
                        if (PHP5) {
                                $this->{$name} = new $class;
                        } else {
                                $this->{$name} =& new $class;
                        }
                } elseif (isset($this->{$name}->settings) && isset($this->{$name}->settings[$this->modelName])) {
                        if (!empty($config)) {
                                $config = array_merge($this->{$name}->settings[$this->modelName], $config);
                        } else {
                                $config = array();
                        }
                }
                if (empty($config)) {
                        $config = array();
                }
                $this->{$name}->setup(ClassRegistry::getObject($this->modelName), $config);

                foreach ($this->{$name}->mapMethods as $method => $alias) {
                        $this->__mappedMethods[$method] = array($alias, $name);
                }
                $methods = get_class_methods($this->{$name});
                $parentMethods = array_flip(get_class_methods('ModelBehavior'));
                $callbacks = array('setup' => true, 'cleanup' => true, 'beforeFind' => true, 'afterFind' => true, 'beforeSave' => true, 'afterSave' => true, 'beforeDelete' => true, 'afterDelete' => true, 'afterError' => true);

                foreach ($methods as $m) {
                        if (!isset($parentMethods[$m])) {
                                if ($m[0] != '_' && !array_key_exists($m, $this->__methods) && !isset($callbacks[$m])) {
                                        $this->__methods[$m] = array($m, $name);
                                }
                        }
                }

                if (!in_array($name, $this->_attached)) {
                        $this->_attached[] = $name;
                }
                if (in_array($name, $this->_disabled) && !(isset($config['enabled']) && $config['enabled'] === false)) {
                        $this->enable($name);
                } elseif (isset($config['enabled']) && $config['enabled'] === false) {
                        $this->disable($name);
                }
                return true;
        }
/**
 * Detaches a behavior from a model
 *
 * @param string $name CamelCased name of the behavior to unload
 * @return void
 * @access public
 */
        function detach($name) {
                if (isset($this->{$name})) {
                        $this->{$name}->cleanup(ClassRegistry::getObject($this->modelName));
                        unset($this->{$name});
                }
                foreach ($this->__methods as $m => $callback) {
                        if (is_array($callback) && $callback[1] == $name) {
                                unset($this->__methods[$m]);
                        }
                }
                $this->_attached = array_values(array_diff($this->_attached, (array)$name));
        }
/**
 * Enables callbacks on a behavior or array of behaviors
 *
 * @param mixed $name CamelCased name of the behavior(s) to enable (string or array)
 * @return void
 * @access public
 */
        function enable($name) {
                $this->_disabled = array_diff($this->_disabled, (array)$name);
        }
/**
 * Disables callbacks on a behavior or array of behaviors.  Public behavior methods are still
 * callable as normal.
 *
 * @param mixed $name CamelCased name of the behavior(s) to disable (string or array)
 * @return void
 * @access public
 */
        function disable($name) {
                foreach ((array)$name as $behavior) {
                        if (in_array($behavior, $this->_attached) && !in_array($behavior, $this->_disabled)) {
                                $this->_disabled[] = $behavior;
                        }
                }
        }
/**
 * Gets the list of currently-enabled behaviors, or, the current status of a single behavior
 *
 * @param string $name Optional.  The name of the behavior to check the status of.  If omitted,
 *                                              returns an array of currently-enabled behaviors
 * @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
 *               Otherwise, returns an array of all enabled behaviors.
 * @access public
 */
        function enabled($name = null) {
                if (!empty($name)) {
                        return (in_array($name, $this->_attached) && !in_array($name, $this->_disabled));
                }
                return array_diff($this->_attached, $this->_disabled);
        }
/**
 * Dispatches a behavior method
 *
 * @return array All methods for all behaviors attached to this object
 * @access public
 */
        function dispatchMethod(&$model, $method, $params = array(), $strict = false) {
                $methods = array_keys($this->__methods);
                foreach ($methods as $key => $value) {
                        $methods[$key] = strtolower($value);
                }
                $method = strtolower($method);
                $check = array_flip($methods);
                $found = isset($check[$method]);
                $call = null;

                if ($strict && !$found) {
                        trigger_error("BehaviorCollection::dispatchMethod() - Method {$method} not found in any attached behavior", E_USER_WARNING);
                        return null;
                } elseif ($found) {
                        $methods = array_combine($methods, array_values($this->__methods));
                        $call = $methods[$method];
                } else {
                        $count = count($this->__mappedMethods);
                        $mapped = array_keys($this->__mappedMethods);

                        for ($i = 0; $i < $count; $i++) {
                                if (preg_match($mapped[$i] . 'i', $method)) {
                                        $call = $this->__mappedMethods[$mapped[$i]];
                                        array_unshift($params, $method);
                                        break;
                                }
                        }
                }

                if (!empty($call)) {
                        return $this->{$call[1]}->dispatchMethod($model, $call[0], $params);
                }
                return array('unhandled');
        }
/**
 * Dispatches a behavior callback on all attached behavior objects
 *
 * @param model $model
 * @param string $callback
 * @param array $params
 * @param array $options
 * @return mixed
 * @access public
 */
        function trigger(&$model, $callback, $params = array(), $options = array()) {
                if (empty($this->_attached)) {
                        return true;
                }
                $_params = $params;
                $options = array_merge(array('break' => false, 'breakOn' => array(null, false), 'modParams' => false), $options);
                $count = count($this->_attached);

                for ($i = 0; $i < $count; $i++) {
                        $name = $this->_attached[$i];
                        if (in_array($name, $this->_disabled)) {
                                continue;
                        }
                        $result = $this->{$name}->dispatchMethod($model, $callback, $params);

                        if ($options['break'] && ($result === $options['breakOn'] || (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true)))) {
                                return $result;
                        } elseif ($options['modParams'] && is_array($result)) {
                                $params[0] = $result;
                        }
                }
                if ($options['modParams'] && isset($params[0])) {
                        return $params[0];
                }
                return true;
        }
/**
 * Gets the method list for attached behaviors, i.e. all public, non-callback methods
 *
 * @return array All public methods for all behaviors attached to this collection
 * @access public
 */
        function methods() {
                return $this->__methods;
        }
/**
 * Gets the list of attached behaviors, or, whether the given behavior is attached
 *
 * @param string $name Optional.  The name of the behavior to check the status of.  If omitted,
 *                                              returns an array of currently-attached behaviors
 * @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
 *               Otherwise, returns an array of all attached behaviors.
 * @access public
 */
        function attached($name = null) {
                if (!empty($name)) {
                        return (in_array($name, $this->_attached));
                }
                return $this->_attached;
        }
}
?>