Skip to content

Commit

Permalink
Moving BehaviorCollection into its own file.
Browse files Browse the repository at this point in the history
Changing how behaviors are stored in the collection object.  Makes it work the same as HelperCollection.
  • Loading branch information
markstory committed Aug 11, 2010
1 parent fcbfb55 commit b225085
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 255 deletions.
279 changes: 279 additions & 0 deletions cake/libs/model/behavior_collection.php
@@ -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 { }
1 change: 1 addition & 0 deletions cake/libs/model/model.php
Expand Up @@ -24,6 +24,7 @@
* Included libs
*/
App::import('Core', array('ClassRegistry', 'Validation', 'Set', 'String'));
App::import('Model', 'BehaviorCollection', false);
App::import('Model', 'ModelBehavior', false);
App::import('Model', 'ConnectionManager', false);

Expand Down

0 comments on commit b225085

Please sign in to comment.