diff cake/libs/model/model_behavior.php @ 0:261e66bd5a0c

hg init
author Shoshi TAMAKI <shoshi@cr.ie.u-ryukyu.ac.jp>
date Sun, 24 Jul 2011 21:08:31 +0900
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cake/libs/model/model_behavior.php	Sun Jul 24 21:08:31 2011 +0900
@@ -0,0 +1,533 @@
+<?php
+/**
+ * Model behaviors base class.
+ *
+ * Adds methods and automagic functionality to Cake Models.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 1.2.0.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 mixed False if the operation should abort. An array will replace the value of $query.
+ * @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 An array value will replace the value of $results - any other value will be ignored.
+ * @access public
+ */
+	function afterFind(&$model, $results, $primary) { }
+
+/**
+ * Before validate callback
+ *
+ * @param object $model Model using this behavior
+ * @return mixed False if the operation should abort. Any other result will continue.
+ * @access public
+ */
+	function beforeValidate(&$model) { }
+
+/**
+ * Before save callback
+ *
+ * @param object $model Model using this behavior
+ * @return mixed False if the operation should abort. Any other result will continue.
+ * @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 mixed False if the operation should abort. Any other result will continue.
+ * @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:
+				$params = array_merge(array(&$model), $params);
+				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 string
+ * @access public
+ */
+	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()) {
+		list($plugin, $name) = pluginSplit($behavior);
+		$class = $name . 'Behavior';
+
+		if (!App::import('Behavior', $behavior)) {
+			$this->cakeError('missingBehaviorFile', array(array(
+				'behavior' => $behavior,
+				'file' => Inflector::underscore($behavior) . '.php',
+				'code' => 500,
+				'base' => '/'
+			)));
+			return false;
+		}
+		if (!class_exists($class)) {
+			$this->cakeError('missingBehaviorClass', array(array(
+				'behavior' => $class,
+				'file' => Inflector::underscore($class) . '.php',
+				'code' => 500,
+				'base' => '/'
+			)));
+			return false;
+		}
+
+		if (!isset($this->{$name})) {
+			if (ClassRegistry::isKeySet($class)) {
+				if (PHP5) {
+					$this->{$name} = ClassRegistry::getObject($class);
+				} else {
+					$this->{$name} =& ClassRegistry::getObject($class);
+				}
+			} else {
+				if (PHP5) {
+					$this->{$name} = new $class;
+				} else {
+					$this->{$name} =& new $class;
+				}
+				ClassRegistry::addObject($class, $this->{$name});
+				if (!empty($plugin)) {
+					ClassRegistry::addObject($plugin.'.'.$class, $this->{$name});
+				}
+			}
+		} elseif (isset($this->{$name}->settings) && isset($this->{$name}->settings[$this->modelName])) {
+			if ($config !== null && $config !== false) {
+				$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', 'cleanup', 'beforeFind', 'afterFind', 'beforeSave', 'afterSave',
+			'beforeDelete', 'afterDelete', 'afterError'
+		);
+
+		foreach ($methods as $m) {
+			if (!isset($parentMethods[$m])) {
+				$methodAllowed = (
+					$m[0] != '_' && !array_key_exists($m, $this->__methods) &&
+					!in_array($m, $callbacks)
+				);
+				if ($methodAllowed) {
+					$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) {
+		list($plugin, $name) = pluginSplit($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(sprintf(__("BehaviorCollection::dispatchMethod() - Method %s not found in any attached behavior", true), $method), 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;
+		}
+		$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;
+	}
+}