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