1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
21:
22: App::uses('ObjectCollection', 'Utility');
23: App::uses('CakeEventListener', 'Event');
24:
25: 26: 27: 28: 29: 30: 31:
32: class BehaviorCollection extends ObjectCollection implements CakeEventListener {
33:
34: 35: 36: 37: 38:
39: public $modelName = null;
40:
41: 42: 43: 44: 45:
46: protected $_methods = array();
47:
48: 49: 50: 51: 52:
53: protected $_mappedMethods = array();
54:
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 $behavior => $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: unset($config['enabled'], $config['className']);
112:
113: list($plugin, $name) = pluginSplit($behavior, true);
114: if (!isset($alias)) {
115: $alias = $name;
116: }
117:
118: $class = $name . 'Behavior';
119:
120: App::uses($class, $plugin . 'Model/Behavior');
121: if (!class_exists($class)) {
122: throw new MissingBehaviorException(array(
123: 'class' => $class,
124: 'plugin' => substr($plugin, 0, -1)
125: ));
126: }
127:
128: if (!isset($this->{$alias})) {
129: if (ClassRegistry::isKeySet($class)) {
130: $this->_loaded[$alias] = ClassRegistry::getObject($class);
131: } else {
132: $this->_loaded[$alias] = new $class();
133: ClassRegistry::addObject($class, $this->_loaded[$alias]);
134: if (!empty($plugin)) {
135: ClassRegistry::addObject($plugin . '.' . $class, $this->_loaded[$alias]);
136: }
137: }
138: } elseif (isset($this->_loaded[$alias]->settings) && isset($this->_loaded[$alias]->settings[$this->modelName])) {
139: if ($config !== null && $config !== false) {
140: $config = array_merge($this->_loaded[$alias]->settings[$this->modelName], $config);
141: } else {
142: $config = array();
143: }
144: }
145: if (empty($config)) {
146: $config = array();
147: }
148: $this->_loaded[$alias]->setup(ClassRegistry::getObject($this->modelName), $config);
149:
150: foreach ($this->_loaded[$alias]->mapMethods as $method => $methodAlias) {
151: $this->_mappedMethods[$method] = array($alias, $methodAlias);
152: }
153: $methods = get_class_methods($this->_loaded[$alias]);
154: $parentMethods = array_flip(get_class_methods('ModelBehavior'));
155: $callbacks = array(
156: 'setup', 'cleanup', 'beforeFind', 'afterFind', 'beforeSave', 'afterSave',
157: 'beforeDelete', 'afterDelete', 'onError'
158: );
159:
160: foreach ($methods as $m) {
161: if (!isset($parentMethods[$m])) {
162: $methodAllowed = (
163: $m[0] != '_' && !array_key_exists($m, $this->_methods) &&
164: !in_array($m, $callbacks)
165: );
166: if ($methodAllowed) {
167: $this->_methods[$m] = array($alias, $m);
168: }
169: }
170: }
171:
172: if (!in_array($alias, $this->_enabled) && !$configDisabled) {
173: $this->enable($alias);
174: } else {
175: $this->disable($alias);
176: }
177: return true;
178: }
179:
180: 181: 182: 183: 184: 185:
186: public function unload($name) {
187: list($plugin, $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', "BehaviorCollection::dispatchMethod() - Method %s not found in any attached behavior", $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.beforeSave' => 'trigger',
289: 'Model.afterSave' => 'trigger',
290: 'Model.beforeDelete' => 'trigger',
291: 'Model.afterDelete' => 'trigger'
292: );
293: }
294:
295: }
296: