Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Moving BehaviorCollection into its own file.
Changing how behaviors are stored in the collection object. Makes it work the same as HelperCollection.
- Loading branch information
Showing
3 changed files
with
280 additions
and
255 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,279 @@ | ||
<?php | ||
/** | ||
* BehaviorCollection | ||
* | ||
* Provides managment and interface for interacting with collections of behaviors. | ||
* | ||
* PHP 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) | ||
*/ | ||
App::import('Core', 'ObjectCollection'); | ||
|
||
/** | ||
* Model behavior collection class. | ||
* | ||
* Defines the Behavior interface, and contains common model interaction functionality. | ||
* | ||
* @package cake | ||
* @subpackage cake.cake.libs.model | ||
*/ | ||
class BehaviorCollection extends ObjectCollection { | ||
|
||
/** | ||
* Stores a reference to the attached name | ||
* | ||
* @var string | ||
* @access public | ||
*/ | ||
public $modelName = null; | ||
|
||
/** | ||
* Keeps a list of all methods of attached behaviors | ||
* | ||
* @var array | ||
*/ | ||
private $__methods = array(); | ||
|
||
/** | ||
* Keeps a list of all methods which have been mapped with regular expressions | ||
* | ||
* @var array | ||
*/ | ||
private $__mappedMethods = array(); | ||
|
||
/** | ||
* Attaches a model object and loads a list of behaviors | ||
* | ||
* @todo Make this method a constructor instead.. | ||
* @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); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Backwards compatible alias for load() | ||
* | ||
* @return void | ||
* @deprecated Replaced with load() | ||
*/ | ||
public function attach($behavior, $config = array()) { | ||
return $this->load($behavior, $config); | ||
} | ||
|
||
/** | ||
* Loads a behavior into the collection. | ||
* | ||
* @param string $behavior CamelCased name of the behavior to load | ||
* @param array $config Behavior configuration parameters | ||
* @param boolean $enable Whether or not this helper should be enabled by default | ||
* @return boolean True on success, false on failure | ||
* @throws MissingBehaviorFileException or MissingBehaviorClassException when a behavior could not be found. | ||
*/ | ||
public function load($behavior, $config = array(), $enable = true) { | ||
list($plugin, $name) = pluginSplit($behavior); | ||
$class = $name . 'Behavior'; | ||
|
||
if (!App::import('Behavior', $behavior)) { | ||
throw new MissingBehaviorFileException(Inflector::underscore($behavior) . '.php'); | ||
} | ||
if (!class_exists($class)) { | ||
throw new MissingBehaviorClassException(Inflector::underscore($class)); | ||
} | ||
|
||
if (!isset($this->{$name})) { | ||
if (ClassRegistry::isKeySet($class)) { | ||
$this->_loaded[$name] = ClassRegistry::getObject($class); | ||
} else { | ||
$this->_loaded[$name] = new $class(); | ||
ClassRegistry::addObject($class, $this->_loaded[$name]); | ||
if (!empty($plugin)) { | ||
ClassRegistry::addObject($plugin . '.' . $class, $this->_loaded[$name]); | ||
} | ||
} | ||
} elseif (isset($this->_loaded[$name]->settings) && isset($this->_loaded[$name]->settings[$this->modelName])) { | ||
if ($config !== null && $config !== false) { | ||
$config = array_merge($this->_loaded[$name]->settings[$this->modelName], $config); | ||
} else { | ||
$config = array(); | ||
} | ||
} | ||
if (empty($config)) { | ||
$config = array(); | ||
} | ||
$this->_loaded[$name]->setup(ClassRegistry::getObject($this->modelName), $config); | ||
|
||
foreach ($this->_loaded[$name]->mapMethods as $method => $alias) { | ||
$this->__mappedMethods[$method] = array($alias, $name); | ||
} | ||
$methods = get_class_methods($this->_loaded[$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 | ||
*/ | ||
public function unload($name) { | ||
list($plugin, $name) = pluginSplit($name); | ||
if (isset($this->_loaded[$name])) { | ||
$this->_loaded[$name]->cleanup(ClassRegistry::getObject($this->modelName)); | ||
unset($this->_loaded[$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)); | ||
} | ||
|
||
/** | ||
* Backwards compatible alias for unload() | ||
* | ||
* @param string $name Name of behavior | ||
* @return void | ||
* @deprecated Use unload instead. | ||
*/ | ||
public function detach($name) { | ||
return $this->unload($name); | ||
} | ||
|
||
/** | ||
* Dispatches a behavior method | ||
* | ||
* @return array All methods for all behaviors attached to this object | ||
*/ | ||
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"), $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->_loaded[$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 | ||
*/ | ||
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->_loaded[$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 | ||
*/ | ||
public function methods() { | ||
return $this->__methods; | ||
} | ||
|
||
} | ||
|
||
/** | ||
* Runtime Exceptions for behaviors | ||
*/ | ||
class MissingBehaviorFileException extends RuntimeException { } | ||
class MissingBehaviorClassException extends RuntimeException { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.