1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20:
21: App::uses('ObjectCollection', 'Utility');
22: App::uses('CakeEventListener', 'Event');
23:
24: 25: 26: 27: 28: 29: 30:
31: class BehaviorCollection extends ObjectCollection implements CakeEventListener {
32:
33: 34: 35: 36: 37:
38: public $modelName = null;
39:
40: 41: 42: 43: 44:
45: protected $_methods = array();
46:
47: 48: 49: 50: 51:
52: protected $_mappedMethods = array();
53:
54: 55: 56: 57: 58: 59: 60:
61: public function init($modelName, $behaviors = array()) {
62: $this->modelName = $modelName;
63:
64: if (!empty($behaviors)) {
65: foreach (BehaviorCollection::normalizeObjectArray($behaviors) as $config) {
66: $this->load($config['class'], $config['settings']);
67: }
68: }
69: }
70:
71: 72: 73: 74: 75: 76: 77: 78:
79: public function attach($behavior, $config = array()) {
80: return $this->load($behavior, $config);
81: }
82:
83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102:
103: public function load($behavior, $config = array()) {
104: if (isset($config['className'])) {
105: $alias = $behavior;
106: $behavior = $config['className'];
107: }
108: $configDisabled = isset($config['enabled']) && $config['enabled'] === false;
109: $priority = isset($config['priority']) ? $config['priority'] : $this->defaultPriority;
110: unset($config['enabled'], $config['className'], $config['priority']);
111:
112: list($plugin, $name) = pluginSplit($behavior, true);
113: if (!isset($alias)) {
114: $alias = $name;
115: }
116:
117: $class = $name . 'Behavior';
118:
119: App::uses($class, $plugin . 'Model/Behavior');
120: if (!class_exists($class)) {
121: throw new MissingBehaviorException(array(
122: 'class' => $class,
123: 'plugin' => substr($plugin, 0, -1)
124: ));
125: }
126:
127: if (!isset($this->{$alias})) {
128: if (ClassRegistry::isKeySet($class)) {
129: $this->_loaded[$alias] = ClassRegistry::getObject($class);
130: } else {
131: $this->_loaded[$alias] = new $class();
132: ClassRegistry::addObject($class, $this->_loaded[$alias]);
133: }
134: } elseif (isset($this->_loaded[$alias]->settings) && isset($this->_loaded[$alias]->settings[$this->modelName])) {
135: if ($config !== null && $config !== false) {
136: $config = array_merge($this->_loaded[$alias]->settings[$this->modelName], $config);
137: } else {
138: $config = array();
139: }
140: }
141: if (empty($config)) {
142: $config = array();
143: }
144: $this->_loaded[$alias]->settings['priority'] = $priority;
145: $this->_loaded[$alias]->setup(ClassRegistry::getObject($this->modelName), $config);
146:
147: foreach ($this->_loaded[$alias]->mapMethods as $method => $methodAlias) {
148: $this->_mappedMethods[$method] = array($alias, $methodAlias);
149: }
150: $methods = get_class_methods($this->_loaded[$alias]);
151: $parentMethods = array_flip(get_class_methods('ModelBehavior'));
152: $callbacks = array(
153: 'setup', 'cleanup', 'beforeFind', 'afterFind', 'beforeSave', 'afterSave',
154: 'beforeDelete', 'afterDelete', 'onError'
155: );
156:
157: foreach ($methods as $m) {
158: if (!isset($parentMethods[$m])) {
159: $methodAllowed = (
160: $m[0] !== '_' && !array_key_exists($m, $this->_methods) &&
161: !in_array($m, $callbacks)
162: );
163: if ($methodAllowed) {
164: $this->_methods[$m] = array($alias, $m);
165: }
166: }
167: }
168:
169: if ($configDisabled) {
170: $this->disable($alias);
171: } elseif (!$this->enabled($alias)) {
172: $this->enable($alias);
173: } else {
174: $this->setPriority($alias, $priority);
175: }
176:
177: return true;
178: }
179:
180: 181: 182: 183: 184: 185:
186: public function unload($name) {
187: list(, $name) = pluginSplit($name);
188: if (isset($this->_loaded[$name])) {
189: $this->_loaded[$name]->cleanup(ClassRegistry::getObject($this->modelName));
190: parent::unload($name);
191: }
192: foreach ($this->_methods as $m => $callback) {
193: if (is_array($callback) && $callback[0] === $name) {
194: unset($this->_methods[$m]);
195: }
196: }
197: }
198:
199: 200: 201: 202: 203: 204: 205:
206: public function detach($name) {
207: return $this->unload($name);
208: }
209:
210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221:
222: public function dispatchMethod($model, $method, $params = array(), $strict = false) {
223: $method = $this->hasMethod($method, true);
224:
225: if ($strict && empty($method)) {
226: trigger_error(__d('cake_dev', '%s - Method %s not found in any attached behavior', 'BehaviorCollection::dispatchMethod()', $method), E_USER_WARNING);
227: return null;
228: }
229: if (empty($method)) {
230: return array('unhandled');
231: }
232: if (count($method) === 3) {
233: array_unshift($params, $method[2]);
234: unset($method[2]);
235: }
236: return call_user_func_array(
237: array($this->_loaded[$method[0]], $method[1]),
238: array_merge(array(&$model), $params)
239: );
240: }
241:
242: 243: 244: 245: 246: 247:
248: public function methods() {
249: return $this->_methods;
250: }
251:
252: 253: 254: 255: 256: 257: 258: 259: 260:
261: public function hasMethod($method, $callback = false) {
262: if (isset($this->_methods[$method])) {
263: return $callback ? $this->_methods[$method] : true;
264: }
265: foreach ($this->_mappedMethods as $pattern => $target) {
266: if (preg_match($pattern . 'i', $method)) {
267: if ($callback) {
268: $target[] = $method;
269: return $target;
270: }
271: return true;
272: }
273: }
274: return false;
275: }
276:
277: 278: 279: 280: 281: 282:
283: public function implementedEvents() {
284: return array(
285: 'Model.beforeFind' => 'trigger',
286: 'Model.afterFind' => 'trigger',
287: 'Model.beforeValidate' => 'trigger',
288: 'Model.afterValidate' => 'trigger',
289: 'Model.beforeSave' => 'trigger',
290: 'Model.afterSave' => 'trigger',
291: 'Model.beforeDelete' => 'trigger',
292: 'Model.afterDelete' => 'trigger'
293: );
294: }
295:
296: }
297: