behavior.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: behavior.php 8114 2009-03-17 21:10:28Z renan.saddam $ */
00003 /**
00004  * Model behaviors base class.
00005  *
00006  * Adds methods and automagic functionality to Cake Models.
00007  *
00008  * PHP versions 4 and 5
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
00011  * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
00012  *
00013  * Licensed under The MIT License
00014  * Redistributions of files must retain the above copyright notice.
00015  *
00016  * @filesource
00017  * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
00018  * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00019  * @package       cake
00020  * @subpackage    cake.cake.libs.model
00021  * @since         CakePHP(tm) v 1.2.0.0
00022  * @version       $Revision: 8114 $
00023  * @modifiedby    $LastChangedBy: renan.saddam $
00024  * @lastmodified  $Date: 2009-03-17 17:10:28 -0400 (Tue, 17 Mar 2009) $
00025  * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
00026  */
00027 /**
00028  * Model behavior base class.
00029  *
00030  * Defines the Behavior interface, and contains common model interaction functionality.
00031  *
00032  * @package       cake
00033  * @subpackage    cake.cake.libs.model
00034  */
00035 class ModelBehavior extends Object {
00036 /**
00037  * Contains configuration settings for use with individual model objects.  This
00038  * is used because if multiple models use this Behavior, each will use the same
00039  * object instance.  Individual model settings should be stored as an
00040  * associative array, keyed off of the model name.
00041  *
00042  * @var array
00043  * @access public
00044  * @see Model::$alias
00045  */
00046     var $settings = array();
00047 /**
00048  * Allows the mapping of preg-compatible regular expressions to public or
00049  * private methods in this class, where the array key is a /-delimited regular
00050  * expression, and the value is a class method.  Similar to the functionality of
00051  * the findBy* / findAllBy* magic methods.
00052  *
00053  * @var array
00054  * @access public
00055  */
00056     var $mapMethods = array();
00057 /**
00058  * Setup this behavior with the specified configuration settings.
00059  *
00060  * @param object $model Model using this behavior
00061  * @param array $config Configuration settings for $model
00062  * @access public
00063  */
00064     function setup(&$model, $config = array()) { }
00065 /**
00066  * Clean up any initialization this behavior has done on a model.  Called when a behavior is dynamically
00067  * detached from a model using Model::detach().
00068  *
00069  * @param object $model Model using this behavior
00070  * @access public
00071  * @see BehaviorCollection::detach()
00072  */
00073     function cleanup(&$model) {
00074         if (isset($this->settings[$model->alias])) {
00075             unset($this->settings[$model->alias]);
00076         }
00077     }
00078 /**
00079  * Before find callback
00080  *
00081  * @param object $model Model using this behavior
00082  * @param array $queryData Data used to execute this query, i.e. conditions, order, etc.
00083  * @return boolean True if the operation should continue, false if it should abort
00084  * @access public
00085  */
00086     function beforeFind(&$model, $query) { }
00087 /**
00088  * After find callback. Can be used to modify any results returned by find and findAll.
00089  *
00090  * @param object $model Model using this behavior
00091  * @param mixed $results The results of the find operation
00092  * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association)
00093  * @return mixed Result of the find operation
00094  * @access public
00095  */
00096     function afterFind(&$model, $results, $primary) { }
00097 /**
00098  * Before validate callback
00099  *
00100  * @param object $model Model using this behavior
00101  * @return boolean True if validate operation should continue, false to abort
00102  * @access public
00103  */
00104     function beforeValidate(&$model) { }
00105 /**
00106  * Before save callback
00107  *
00108  * @param object $model Model using this behavior
00109  * @return boolean True if the operation should continue, false if it should abort
00110  * @access public
00111  */
00112     function beforeSave(&$model) { }
00113 /**
00114  * After save callback
00115  *
00116  * @param object $model Model using this behavior
00117  * @param boolean $created True if this save created a new record
00118  * @access public
00119  */
00120     function afterSave(&$model, $created) { }
00121 /**
00122  * Before delete callback
00123  *
00124  * @param object $model Model using this behavior
00125  * @param boolean $cascade If true records that depend on this record will also be deleted
00126  * @return boolean True if the operation should continue, false if it should abort
00127  * @access public
00128  */
00129     function beforeDelete(&$model, $cascade = true) { }
00130 /**
00131  * After delete callback
00132  *
00133  * @param object $model Model using this behavior
00134  * @access public
00135  */
00136     function afterDelete(&$model) { }
00137 /**
00138  * DataSource error callback
00139  *
00140  * @param object $model Model using this behavior
00141  * @param string $error Error generated in DataSource
00142  * @access public
00143  */
00144     function onError(&$model, $error) { }
00145 /**
00146  * Overrides Object::dispatchMethod to account for PHP4's broken reference support
00147  *
00148  * @see Object::dispatchMethod
00149  * @access public
00150  * @return mixed
00151  */
00152     function dispatchMethod(&$model, $method, $params = array()) {
00153         if (empty($params)) {
00154             return $this->{$method}($model);
00155         }
00156         $params = array_values($params);
00157 
00158         switch (count($params)) {
00159             case 1:
00160                 return $this->{$method}($model, $params[0]);
00161             case 2:
00162                 return $this->{$method}($model, $params[0], $params[1]);
00163             case 3:
00164                 return $this->{$method}($model, $params[0], $params[1], $params[2]);
00165             case 4:
00166                 return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3]);
00167             case 5:
00168                 return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3], $params[4]);
00169             default:
00170                 array_unshift($params, $model);
00171                 return call_user_func_array(array(&$this, $method), $params);
00172             break;
00173         }
00174     }
00175 /**
00176  * If $model's whitelist property is non-empty, $field will be added to it.
00177  * Note: this method should *only* be used in beforeValidate or beforeSave to ensure
00178  * that it only modifies the whitelist for the current save operation.  Also make sure
00179  * you explicitly set the value of the field which you are allowing.
00180  *
00181  * @param object $model Model using this behavior
00182  * @param string $field Field to be added to $model's whitelist
00183  * @access protected
00184  * @return void
00185  */
00186     function _addToWhitelist(&$model, $field) {
00187         if (is_array($field)) {
00188             foreach ($field as $f) {
00189                 $this->_addToWhitelist($model, $f);
00190             }
00191             return;
00192         }
00193         if (!empty($model->whitelist) && !in_array($field, $model->whitelist)) {
00194             $model->whitelist[] = $field;
00195         }
00196     }
00197 }
00198 
00199 /**
00200  * Model behavior collection class.
00201  *
00202  * Defines the Behavior interface, and contains common model interaction functionality.
00203  *
00204  * @package       cake
00205  * @subpackage    cake.cake.libs.model
00206  */
00207 class BehaviorCollection extends Object {
00208 /**
00209  * Stores a reference to the attached name
00210  *
00211  * @var string
00212  * @access public
00213  */
00214     var $modelName = null;
00215 /**
00216  * Lists the currently-attached behavior objects
00217  *
00218  * @var array
00219  * @access private
00220  */
00221     var $_attached = array();
00222 /**
00223  * Lists the currently-attached behavior objects which are disabled
00224  *
00225  * @var array
00226  * @access private
00227  */
00228     var $_disabled = array();
00229 /**
00230  * Keeps a list of all methods of attached behaviors
00231  *
00232  * @var array
00233  */
00234     var $__methods = array();
00235 /**
00236  * Keeps a list of all methods which have been mapped with regular expressions
00237  *
00238  * @var array
00239  */
00240     var $__mappedMethods = array();
00241 /**
00242  * Attaches a model object and loads a list of behaviors
00243  *
00244  * @access public
00245  * @return void
00246  */
00247     function init($modelName, $behaviors = array()) {
00248         $this->modelName = $modelName;
00249 
00250         if (!empty($behaviors)) {
00251             foreach (Set::normalize($behaviors) as $behavior => $config) {
00252                 $this->attach($behavior, $config);
00253             }
00254         }
00255     }
00256 /**
00257  * Attaches a behavior to a model
00258  *
00259  * @param string $behavior CamelCased name of the behavior to load
00260  * @param array $config Behavior configuration parameters
00261  * @return boolean True on success, false on failure
00262  * @access public
00263  */
00264     function attach($behavior, $config = array()) {
00265         $name = $behavior;
00266         if (strpos($behavior, '.')) {
00267             list($plugin, $name) = explode('.', $behavior, 2);
00268         }
00269         $class = $name . 'Behavior';
00270 
00271         if (!App::import('Behavior', $behavior)) {
00272             return false;
00273         }
00274 
00275         if (!isset($this->{$name})) {
00276             if (ClassRegistry::isKeySet($class)) {
00277                 if (PHP5) {
00278                     $this->{$name} = ClassRegistry::getObject($class);
00279                 } else {
00280                     $this->{$name} =& ClassRegistry::getObject($class);
00281                 }
00282             } else {
00283                 if (PHP5) {
00284                     $this->{$name} = new $class;
00285                 } else {
00286                     $this->{$name} =& new $class;
00287                 }
00288                 ClassRegistry::addObject($class, $this->{$name});
00289             }
00290         } elseif (isset($this->{$name}->settings) && isset($this->{$name}->settings[$this->modelName])) {
00291             if ($config !== null && $config !== false) {
00292                 $config = array_merge($this->{$name}->settings[$this->modelName], $config);
00293             } else {
00294                 $config = array();
00295             }
00296         }
00297         if (empty($config)) {
00298             $config = array();
00299         }
00300         $this->{$name}->setup(ClassRegistry::getObject($this->modelName), $config);
00301 
00302         foreach ($this->{$name}->mapMethods as $method => $alias) {
00303             $this->__mappedMethods[$method] = array($alias, $name);
00304         }
00305         $methods = get_class_methods($this->{$name});
00306         $parentMethods = array_flip(get_class_methods('ModelBehavior'));
00307         $callbacks = array(
00308             'setup', 'cleanup', 'beforeFind', 'afterFind', 'beforeSave', 'afterSave',
00309             'beforeDelete', 'afterDelete', 'afterError'
00310         );
00311 
00312         foreach ($methods as $m) {
00313             if (!isset($parentMethods[$m])) {
00314                 $methodAllowed = (
00315                     $m[0] != '_' && !array_key_exists($m, $this->__methods) &&
00316                     !in_array($m, $callbacks)
00317                 );
00318                 if ($methodAllowed) {
00319                     $this->__methods[$m] = array($m, $name);
00320                 }
00321             }
00322         }
00323 
00324         if (!in_array($name, $this->_attached)) {
00325             $this->_attached[] = $name;
00326         }
00327         if (in_array($name, $this->_disabled) && !(isset($config['enabled']) && $config['enabled'] === false)) {
00328             $this->enable($name);
00329         } elseif (isset($config['enabled']) && $config['enabled'] === false) {
00330             $this->disable($name);
00331         }
00332         return true;
00333     }
00334 /**
00335  * Detaches a behavior from a model
00336  *
00337  * @param string $name CamelCased name of the behavior to unload
00338  * @return void
00339  * @access public
00340  */
00341     function detach($name) {
00342         if (isset($this->{$name})) {
00343             $this->{$name}->cleanup(ClassRegistry::getObject($this->modelName));
00344             unset($this->{$name});
00345         }
00346         foreach ($this->__methods as $m => $callback) {
00347             if (is_array($callback) && $callback[1] == $name) {
00348                 unset($this->__methods[$m]);
00349             }
00350         }
00351         $this->_attached = array_values(array_diff($this->_attached, (array)$name));
00352     }
00353 /**
00354  * Enables callbacks on a behavior or array of behaviors
00355  *
00356  * @param mixed $name CamelCased name of the behavior(s) to enable (string or array)
00357  * @return void
00358  * @access public
00359  */
00360     function enable($name) {
00361         $this->_disabled = array_diff($this->_disabled, (array)$name);
00362     }
00363 /**
00364  * Disables callbacks on a behavior or array of behaviors.  Public behavior methods are still
00365  * callable as normal.
00366  *
00367  * @param mixed $name CamelCased name of the behavior(s) to disable (string or array)
00368  * @return void
00369  * @access public
00370  */
00371     function disable($name) {
00372         foreach ((array)$name as $behavior) {
00373             if (in_array($behavior, $this->_attached) && !in_array($behavior, $this->_disabled)) {
00374                 $this->_disabled[] = $behavior;
00375             }
00376         }
00377     }
00378 /**
00379  * Gets the list of currently-enabled behaviors, or, the current status of a single behavior
00380  *
00381  * @param string $name Optional.  The name of the behavior to check the status of.  If omitted,
00382  *   returns an array of currently-enabled behaviors
00383  * @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
00384  *   Otherwise, returns an array of all enabled behaviors.
00385  * @access public
00386  */
00387     function enabled($name = null) {
00388         if (!empty($name)) {
00389             return (in_array($name, $this->_attached) && !in_array($name, $this->_disabled));
00390         }
00391         return array_diff($this->_attached, $this->_disabled);
00392     }
00393 /**
00394  * Dispatches a behavior method
00395  *
00396  * @return array All methods for all behaviors attached to this object
00397  * @access public
00398  */
00399     function dispatchMethod(&$model, $method, $params = array(), $strict = false) {
00400         $methods = array_keys($this->__methods);
00401         foreach ($methods as $key => $value) {
00402             $methods[$key] = strtolower($value);
00403         }
00404         $method = strtolower($method);
00405         $check = array_flip($methods);
00406         $found = isset($check[$method]);
00407         $call = null;
00408 
00409         if ($strict && !$found) {
00410             trigger_error("BehaviorCollection::dispatchMethod() - Method {$method} not found in any attached behavior", E_USER_WARNING);
00411             return null;
00412         } elseif ($found) {
00413             $methods = array_combine($methods, array_values($this->__methods));
00414             $call = $methods[$method];
00415         } else {
00416             $count = count($this->__mappedMethods);
00417             $mapped = array_keys($this->__mappedMethods);
00418 
00419             for ($i = 0; $i < $count; $i++) {
00420                 if (preg_match($mapped[$i] . 'i', $method)) {
00421                     $call = $this->__mappedMethods[$mapped[$i]];
00422                     array_unshift($params, $method);
00423                     break;
00424                 }
00425             }
00426         }
00427 
00428         if (!empty($call)) {
00429             return $this->{$call[1]}->dispatchMethod($model, $call[0], $params);
00430         }
00431         return array('unhandled');
00432     }
00433 /**
00434  * Dispatches a behavior callback on all attached behavior objects
00435  *
00436  * @param model $model
00437  * @param string $callback
00438  * @param array $params
00439  * @param array $options
00440  * @return mixed
00441  * @access public
00442  */
00443     function trigger(&$model, $callback, $params = array(), $options = array()) {
00444         if (empty($this->_attached)) {
00445             return true;
00446         }
00447         $_params = $params;
00448         $options = array_merge(array('break' => false, 'breakOn' => array(null, false), 'modParams' => false), $options);
00449         $count = count($this->_attached);
00450 
00451         for ($i = 0; $i < $count; $i++) {
00452             $name = $this->_attached[$i];
00453             if (in_array($name, $this->_disabled)) {
00454                 continue;
00455             }
00456             $result = $this->{$name}->dispatchMethod($model, $callback, $params);
00457 
00458             if ($options['break'] && ($result === $options['breakOn'] || (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true)))) {
00459                 return $result;
00460             } elseif ($options['modParams'] && is_array($result)) {
00461                 $params[0] = $result;
00462             }
00463         }
00464         if ($options['modParams'] && isset($params[0])) {
00465             return $params[0];
00466         }
00467         return true;
00468     }
00469 /**
00470  * Gets the method list for attached behaviors, i.e. all public, non-callback methods
00471  *
00472  * @return array All public methods for all behaviors attached to this collection
00473  * @access public
00474  */
00475     function methods() {
00476         return $this->__methods;
00477     }
00478 /**
00479  * Gets the list of attached behaviors, or, whether the given behavior is attached
00480  *
00481  * @param string $name Optional.  The name of the behavior to check the status of.  If omitted,
00482  *   returns an array of currently-attached behaviors
00483  * @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
00484  *    Otherwise, returns an array of all attached behaviors.
00485  * @access public
00486  */
00487     function attached($name = null) {
00488         if (!empty($name)) {
00489             return (in_array($name, $this->_attached));
00490         }
00491         return $this->_attached;
00492     }
00493 }
00494 ?>

Generated on Sun Nov 22 00:30:53 2009 for CakePHP 1.2.x.x (v1.2.4.8284) by doxygen 1.4.7