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: public function init($modelName, $behaviors = array()) {
63: $this->modelName = $modelName;
64:
65: if (!empty($behaviors)) {
66: foreach (BehaviorCollection::normalizeObjectArray($behaviors) as $behavior => $config) {
67: $this->load($config['class'], $config['settings']);
68: }
69: }
70: }
71:
72: 73: 74: 75: 76: 77: 78: 79:
80: public function attach($behavior, $config = array()) {
81: return $this->load($behavior, $config);
82: }
83:
84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103:
104: public function load($behavior, $config = array()) {
105: if (is_array($config) && isset($config['className'])) {
106: $alias = $behavior;
107: $behavior = $config['className'];
108: }
109: $configDisabled = isset($config['enabled']) && $config['enabled'] === false;
110: unset($config['enabled'], $config['className']);
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: if (!empty($plugin)) {
134: ClassRegistry::addObject($plugin . '.' . $class, $this->_loaded[$alias]);
135: }
136: }
137: } elseif (isset($this->_loaded[$alias]->settings) && isset($this->_loaded[$alias]->settings[$this->modelName])) {
138: if ($config !== null && $config !== false) {
139: $config = array_merge($this->_loaded[$alias]->settings[$this->modelName], $config);
140: } else {
141: $config = array();
142: }
143: }
144: if (empty($config)) {
145: $config = array();
146: }
147: $this->_loaded[$alias]->setup(ClassRegistry::getObject($this->modelName), $config);
148:
149: foreach ($this->_loaded[$alias]->mapMethods as $method => $methodAlias) {
150: $this->_mappedMethods[$method] = array($alias, $methodAlias);
151: }
152: $methods = get_class_methods($this->_loaded[$alias]);
153: $parentMethods = array_flip(get_class_methods('ModelBehavior'));
154: $callbacks = array(
155: 'setup', 'cleanup', 'beforeFind', 'afterFind', 'beforeSave', 'afterSave',
156: 'beforeDelete', 'afterDelete', 'onError'
157: );
158:
159: foreach ($methods as $m) {
160: if (!isset($parentMethods[$m])) {
161: $methodAllowed = (
162: $m[0] != '_' && !array_key_exists($m, $this->_methods) &&
163: !in_array($m, $callbacks)
164: );
165: if ($methodAllowed) {
166: $this->_methods[$m] = array($alias, $m);
167: }
168: }
169: }
170:
171: if (!in_array($alias, $this->_enabled) && !$configDisabled) {
172: $this->enable($alias);
173: } else {
174: $this->disable($alias);
175: }
176: return true;
177: }
178:
179: 180: 181: 182: 183: 184:
185: public function unload($name) {
186: list($plugin, $name) = pluginSplit($name);
187: if (isset($this->_loaded[$name])) {
188: $this->_loaded[$name]->cleanup(ClassRegistry::getObject($this->modelName));
189: parent::unload($name);
190: }
191: foreach ($this->_methods as $m => $callback) {
192: if (is_array($callback) && $callback[0] == $name) {
193: unset($this->_methods[$m]);
194: }
195: }
196: }
197:
198: 199: 200: 201: 202: 203: 204:
205: public function detach($name) {
206: return $this->unload($name);
207: }
208:
209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220:
221: public function dispatchMethod($model, $method, $params = array(), $strict = false) {
222: $method = $this->hasMethod($method, true);
223:
224: if ($strict && empty($method)) {
225: trigger_error(__d('cake_dev', "BehaviorCollection::dispatchMethod() - Method %s not found in any attached behavior", $method), E_USER_WARNING);
226: return null;
227: }
228: if (empty($method)) {
229: return array('unhandled');
230: }
231: if (count($method) === 3) {
232: array_unshift($params, $method[2]);
233: unset($method[2]);
234: }
235: return call_user_func_array(
236: array($this->_loaded[$method[0]], $method[1]),
237: array_merge(array(&$model), $params)
238: );
239: }
240:
241: 242: 243: 244: 245: 246:
247: public function methods() {
248: return $this->_methods;
249: }
250:
251: 252: 253: 254: 255: 256: 257: 258: 259:
260: public function hasMethod($method, $callback = false) {
261: if (isset($this->_methods[$method])) {
262: return $callback ? $this->_methods[$method] : true;
263: }
264: foreach ($this->_mappedMethods as $pattern => $target) {
265: if (preg_match($pattern . 'i', $method)) {
266: if ($callback) {
267: $target[] = $method;
268: return $target;
269: }
270: return true;
271: }
272: }
273: return false;
274: }
275:
276: 277: 278: 279: 280: 281:
282: public function implementedEvents() {
283: return array(
284: 'Model.beforeFind' => 'trigger',
285: 'Model.afterFind' => 'trigger',
286: 'Model.beforeValidate' => 'trigger',
287: 'Model.afterValidate' => 'trigger',
288: 'Model.beforeSave' => 'trigger',
289: 'Model.afterSave' => 'trigger',
290: 'Model.beforeDelete' => 'trigger',
291: 'Model.afterDelete' => 'trigger'
292: );
293: }
294:
295: }
296: