Subversion-Projekte lars-tiefland.cakephp

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* SVN FILE: $Id: behavior.php 8004 2009-01-16 20:15:21Z gwoo $ */
3
/**
4
 * Model behaviors base class.
5
 *
6
 * Adds methods and automagic functionality to Cake Models.
7
 *
8
 * PHP versions 4 and 5
9
 *
10
 * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
11
 * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
12
 *
13
 * Licensed under The MIT License
14
 * Redistributions of files must retain the above copyright notice.
15
 *
16
 * @filesource
17
 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
18
 * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
19
 * @package       cake
20
 * @subpackage    cake.cake.libs.model
21
 * @since         CakePHP(tm) v 1.2.0.0
22
 * @version       $Revision: 8004 $
23
 * @modifiedby    $LastChangedBy: gwoo $
24
 * @lastmodified  $Date: 2009-01-16 12:15:21 -0800 (Fri, 16 Jan 2009) $
25
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
26
 */
27
/**
28
 * Model behavior base class.
29
 *
30
 * Defines the Behavior interface, and contains common model interaction functionality.
31
 *
32
 * @package       cake
33
 * @subpackage    cake.cake.libs.model
34
 */
35
class ModelBehavior extends Object {
36
/**
37
 * Contains configuration settings for use with individual model objects.  This
38
 * is used because if multiple models use this Behavior, each will use the same
39
 * object instance.  Individual model settings should be stored as an
40
 * associative array, keyed off of the model name.
41
 *
42
 * @var array
43
 * @access public
44
 * @see Model::$alias
45
 */
46
	var $settings = array();
47
/**
48
 * Allows the mapping of preg-compatible regular expressions to public or
49
 * private methods in this class, where the array key is a /-delimited regular
50
 * expression, and the value is a class method.  Similar to the functionality of
51
 * the findBy* / findAllBy* magic methods.
52
 *
53
 * @var array
54
 * @access public
55
 */
56
	var $mapMethods = array();
57
/**
58
 * Setup this behavior with the specified configuration settings.
59
 *
60
 * @param object $model Model using this behavior
61
 * @param array $config Configuration settings for $model
62
 * @access public
63
 */
64
	function setup(&$model, $config = array()) { }
65
/**
66
 * Clean up any initialization this behavior has done on a model.  Called when a behavior is dynamically
67
 * detached from a model using Model::detach().
68
 *
69
 * @param object $model Model using this behavior
70
 * @access public
71
 * @see BehaviorCollection::detach()
72
 */
73
	function cleanup(&$model) {
74
		if (isset($this->settings[$model->alias])) {
75
			unset($this->settings[$model->alias]);
76
		}
77
	}
78
/**
79
 * Before find callback
80
 *
81
 * @param object $model Model using this behavior
82
 * @param array $queryData Data used to execute this query, i.e. conditions, order, etc.
83
 * @return boolean True if the operation should continue, false if it should abort
84
 * @access public
85
 */
86
	function beforeFind(&$model, $query) { }
87
/**
88
 * After find callback. Can be used to modify any results returned by find and findAll.
89
 *
90
 * @param object $model Model using this behavior
91
 * @param mixed $results The results of the find operation
92
 * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association)
93
 * @return mixed Result of the find operation
94
 * @access public
95
 */
96
	function afterFind(&$model, $results, $primary) { }
97
/**
98
 * Before validate callback
99
 *
100
 * @param object $model Model using this behavior
101
 * @return boolean True if validate operation should continue, false to abort
102
 * @access public
103
 */
104
	function beforeValidate(&$model) { }
105
/**
106
 * Before save callback
107
 *
108
 * @param object $model Model using this behavior
109
 * @return boolean True if the operation should continue, false if it should abort
110
 * @access public
111
 */
112
	function beforeSave(&$model) { }
113
/**
114
 * After save callback
115
 *
116
 * @param object $model Model using this behavior
117
 * @param boolean $created True if this save created a new record
118
 * @access public
119
 */
120
	function afterSave(&$model, $created) { }
121
/**
122
 * Before delete callback
123
 *
124
 * @param object $model Model using this behavior
125
 * @param boolean $cascade If true records that depend on this record will also be deleted
126
 * @return boolean True if the operation should continue, false if it should abort
127
 * @access public
128
 */
129
	function beforeDelete(&$model, $cascade = true) { }
130
/**
131
 * After delete callback
132
 *
133
 * @param object $model Model using this behavior
134
 * @access public
135
 */
136
	function afterDelete(&$model) { }
137
/**
138
 * DataSource error callback
139
 *
140
 * @param object $model Model using this behavior
141
 * @param string $error Error generated in DataSource
142
 * @access public
143
 */
144
	function onError(&$model, $error) { }
145
/**
146
 * Overrides Object::dispatchMethod to account for PHP4's broken reference support
147
 *
148
 * @see Object::dispatchMethod
149
 * @access public
150
 * @return mixed
151
 */
152
	function dispatchMethod(&$model, $method, $params = array()) {
153
		if (empty($params)) {
154
			return $this->{$method}($model);
155
		}
156
		$params = array_values($params);
157
 
158
		switch (count($params)) {
159
			case 1:
160
				return $this->{$method}($model, $params[0]);
161
			case 2:
162
				return $this->{$method}($model, $params[0], $params[1]);
163
			case 3:
164
				return $this->{$method}($model, $params[0], $params[1], $params[2]);
165
			case 4:
166
				return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3]);
167
			case 5:
168
				return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3], $params[4]);
169
			default:
170
				array_unshift($params, $model);
171
				return call_user_func_array(array(&$this, $method), $params);
172
			break;
173
		}
174
	}
175
/**
176
 * If $model's whitelist property is non-empty, $field will be added to it.
177
 * Note: this method should *only* be used in beforeValidate or beforeSave to ensure
178
 * that it only modifies the whitelist for the current save operation.  Also make sure
179
 * you explicitly set the value of the field which you are allowing.
180
 *
181
 * @param object $model Model using this behavior
182
 * @param string $field Field to be added to $model's whitelist
183
 * @access protected
184
 * @return void
185
 */
186
	function _addToWhitelist(&$model, $field) {
187
		if (is_array($field)) {
188
			foreach ($field as $f) {
189
				$this->_addToWhitelist($model, $f);
190
			}
191
			return;
192
		}
193
		if (!empty($model->whitelist) && !in_array($field, $model->whitelist)) {
194
			$model->whitelist[] = $field;
195
		}
196
	}
197
}
198
 
199
/**
200
 * Model behavior collection class.
201
 *
202
 * Defines the Behavior interface, and contains common model interaction functionality.
203
 *
204
 * @package       cake
205
 * @subpackage    cake.cake.libs.model
206
 */
207
class BehaviorCollection extends Object {
208
 
209
/**
210
 * Stores a reference to the attached name
211
 *
212
 * @var object
213
 */
214
	var $modelName = null;
215
/**
216
 * Lists the currently-attached behavior objects
217
 *
218
 * @var array
219
 * @access private
220
 */
221
	var $_attached = array();
222
/**
223
 * Lists the currently-attached behavior objects which are disabled
224
 *
225
 * @var array
226
 * @access private
227
 */
228
	var $_disabled = array();
229
/**
230
 * Keeps a list of all methods of attached behaviors
231
 *
232
 * @var array
233
 */
234
	var $__methods = array();
235
/**
236
 * Keeps a list of all methods which have been mapped with regular expressions
237
 *
238
 * @var array
239
 */
240
	var $__mappedMethods = array();
241
/**
242
 * Attaches a model object and loads a list of behaviors
243
 *
244
 * @access public
245
 * @return void
246
 */
247
	function init($modelName, $behaviors = array()) {
248
		$this->modelName = $modelName;
249
 
250
		if (!empty($behaviors)) {
251
			foreach (Set::normalize($behaviors) as $behavior => $config) {
252
				$this->attach($behavior, $config);
253
			}
254
		}
255
	}
256
/**
257
 * Attaches a behavior to a model
258
 *
259
 * @param string $behavior CamelCased name of the behavior to load
260
 * @param array $config Behavior configuration parameters
261
 * @return boolean True on success, false on failure
262
 * @access public
263
 */
264
	function attach($behavior, $config = array()) {
265
		$name = $behavior;
266
		if (strpos($behavior, '.')) {
267
			list($plugin, $name) = explode('.', $behavior, 2);
268
		}
269
		$class = $name . 'Behavior';
270
 
271
		if (!App::import('Behavior', $behavior)) {
272
			return false;
273
		}
274
 
275
		if (!isset($this->{$name})) {
276
			if (PHP5) {
277
				$this->{$name} = new $class;
278
			} else {
279
				$this->{$name} =& new $class;
280
			}
281
		} elseif (isset($this->{$name}->settings) && isset($this->{$name}->settings[$this->modelName])) {
282
			if (!empty($config)) {
283
				$config = array_merge($this->{$name}->settings[$this->modelName], $config);
284
			} else {
285
				$config = array();
286
			}
287
		}
288
		if (empty($config)) {
289
			$config = array();
290
		}
291
		$this->{$name}->setup(ClassRegistry::getObject($this->modelName), $config);
292
 
293
		foreach ($this->{$name}->mapMethods as $method => $alias) {
294
			$this->__mappedMethods[$method] = array($alias, $name);
295
		}
296
		$methods = get_class_methods($this->{$name});
297
		$parentMethods = array_flip(get_class_methods('ModelBehavior'));
298
		$callbacks = array('setup' => true, 'cleanup' => true, 'beforeFind' => true, 'afterFind' => true, 'beforeSave' => true, 'afterSave' => true, 'beforeDelete' => true, 'afterDelete' => true, 'afterError' => true);
299
 
300
		foreach ($methods as $m) {
301
			if (!isset($parentMethods[$m])) {
302
				if ($m[0] != '_' && !array_key_exists($m, $this->__methods) && !isset($callbacks[$m])) {
303
					$this->__methods[$m] = array($m, $name);
304
				}
305
			}
306
		}
307
 
308
		if (!in_array($name, $this->_attached)) {
309
			$this->_attached[] = $name;
310
		}
311
		if (in_array($name, $this->_disabled) && !(isset($config['enabled']) && $config['enabled'] === false)) {
312
			$this->enable($name);
313
		} elseif (isset($config['enabled']) && $config['enabled'] === false) {
314
			$this->disable($name);
315
		}
316
		return true;
317
	}
318
/**
319
 * Detaches a behavior from a model
320
 *
321
 * @param string $name CamelCased name of the behavior to unload
322
 * @return void
323
 * @access public
324
 */
325
	function detach($name) {
326
		if (isset($this->{$name})) {
327
			$this->{$name}->cleanup(ClassRegistry::getObject($this->modelName));
328
			unset($this->{$name});
329
		}
330
		foreach ($this->__methods as $m => $callback) {
331
			if (is_array($callback) && $callback[1] == $name) {
332
				unset($this->__methods[$m]);
333
			}
334
		}
335
		$this->_attached = array_values(array_diff($this->_attached, (array)$name));
336
	}
337
/**
338
 * Enables callbacks on a behavior or array of behaviors
339
 *
340
 * @param mixed $name CamelCased name of the behavior(s) to enable (string or array)
341
 * @return void
342
 * @access public
343
 */
344
	function enable($name) {
345
		$this->_disabled = array_diff($this->_disabled, (array)$name);
346
	}
347
/**
348
 * Disables callbacks on a behavior or array of behaviors.  Public behavior methods are still
349
 * callable as normal.
350
 *
351
 * @param mixed $name CamelCased name of the behavior(s) to disable (string or array)
352
 * @return void
353
 * @access public
354
 */
355
	function disable($name) {
356
		foreach ((array)$name as $behavior) {
357
			if (in_array($behavior, $this->_attached) && !in_array($behavior, $this->_disabled)) {
358
				$this->_disabled[] = $behavior;
359
			}
360
		}
361
	}
362
/**
363
 * Gets the list of currently-enabled behaviors, or, the current status of a single behavior
364
 *
365
 * @param string $name Optional.  The name of the behavior to check the status of.  If omitted,
366
 *						returns an array of currently-enabled behaviors
367
 * @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
368
 *               Otherwise, returns an array of all enabled behaviors.
369
 * @access public
370
 */
371
	function enabled($name = null) {
372
		if (!empty($name)) {
373
			return (in_array($name, $this->_attached) && !in_array($name, $this->_disabled));
374
		}
375
		return array_diff($this->_attached, $this->_disabled);
376
	}
377
/**
378
 * Dispatches a behavior method
379
 *
380
 * @return array All methods for all behaviors attached to this object
381
 * @access public
382
 */
383
	function dispatchMethod(&$model, $method, $params = array(), $strict = false) {
384
		$methods = array_keys($this->__methods);
385
		foreach ($methods as $key => $value) {
386
			$methods[$key] = strtolower($value);
387
		}
388
		$method = strtolower($method);
389
		$check = array_flip($methods);
390
		$found = isset($check[$method]);
391
		$call = null;
392
 
393
		if ($strict && !$found) {
394
			trigger_error("BehaviorCollection::dispatchMethod() - Method {$method} not found in any attached behavior", E_USER_WARNING);
395
			return null;
396
		} elseif ($found) {
397
			$methods = array_combine($methods, array_values($this->__methods));
398
			$call = $methods[$method];
399
		} else {
400
			$count = count($this->__mappedMethods);
401
			$mapped = array_keys($this->__mappedMethods);
402
 
403
			for ($i = 0; $i < $count; $i++) {
404
				if (preg_match($mapped[$i] . 'i', $method)) {
405
					$call = $this->__mappedMethods[$mapped[$i]];
406
					array_unshift($params, $method);
407
					break;
408
				}
409
			}
410
		}
411
 
412
		if (!empty($call)) {
413
			return $this->{$call[1]}->dispatchMethod($model, $call[0], $params);
414
		}
415
		return array('unhandled');
416
	}
417
/**
418
 * Dispatches a behavior callback on all attached behavior objects
419
 *
420
 * @param model $model
421
 * @param string $callback
422
 * @param array $params
423
 * @param array $options
424
 * @return mixed
425
 * @access public
426
 */
427
	function trigger(&$model, $callback, $params = array(), $options = array()) {
428
		if (empty($this->_attached)) {
429
			return true;
430
		}
431
		$_params = $params;
432
		$options = array_merge(array('break' => false, 'breakOn' => array(null, false), 'modParams' => false), $options);
433
		$count = count($this->_attached);
434
 
435
		for ($i = 0; $i < $count; $i++) {
436
			$name = $this->_attached[$i];
437
			if (in_array($name, $this->_disabled)) {
438
				continue;
439
			}
440
			$result = $this->{$name}->dispatchMethod($model, $callback, $params);
441
 
442
			if ($options['break'] && ($result === $options['breakOn'] || (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true)))) {
443
				return $result;
444
			} elseif ($options['modParams'] && is_array($result)) {
445
				$params[0] = $result;
446
			}
447
		}
448
		if ($options['modParams'] && isset($params[0])) {
449
			return $params[0];
450
		}
451
		return true;
452
	}
453
/**
454
 * Gets the method list for attached behaviors, i.e. all public, non-callback methods
455
 *
456
 * @return array All public methods for all behaviors attached to this collection
457
 * @access public
458
 */
459
	function methods() {
460
		return $this->__methods;
461
	}
462
/**
463
 * Gets the list of attached behaviors, or, whether the given behavior is attached
464
 *
465
 * @param string $name Optional.  The name of the behavior to check the status of.  If omitted,
466
 *						returns an array of currently-attached behaviors
467
 * @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
468
 *               Otherwise, returns an array of all attached behaviors.
469
 * @access public
470
 */
471
	function attached($name = null) {
472
		if (!empty($name)) {
473
			return (in_array($name, $this->_attached));
474
		}
475
		return $this->_attached;
476
	}
477
}
478
?>