1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Deals with Collections of objects. Keeping registries of those objects,
* loading and constructing new objects and triggering callbacks. Each subclass needs
* to implement its own load() functionality.
*
* All core subclasses of ObjectCollection by convention loaded objects are stored
* in `$this->_loaded`. Enabled objects are stored in `$this->_enabled`. In addition
* the all support an `enabled` option that controls the enabled/disabled state of the object
* when loaded.
*
* @package Cake.Utility
* @since CakePHP(tm) v 2.0
*/
abstract class ObjectCollection {
/**
* List of the currently-enabled objects
*
* @var array
*/
protected $_enabled = array();
/**
* A hash of loaded objects, indexed by name
*
* @var array
*/
protected $_loaded = array();
/**
* Default object priority. A non zero integer.
*
* @var int
*/
public $defaultPriority = 10;
/**
* Loads a new object onto the collection. Can throw a variety of exceptions
*
* Implementations of this class support a `$options['enabled']` flag which enables/disables
* a loaded object.
*
* @param string $name Name of object to load.
* @param array $options Array of configuration options for the object to be constructed.
* @return object the constructed object
*/
abstract public function load($name, $options = array());
/**
* Trigger a callback method on every object in the collection.
* Used to trigger methods on objects in the collection. Will fire the methods in the
* order they were attached.
*
* ### Options
*
* - `breakOn` Set to the value or values you want the callback propagation to stop on.
* Can either be a scalar value, or an array of values to break on. Defaults to `false`.
*
* - `break` Set to true to enabled breaking. When a trigger is broken, the last returned value
* will be returned. If used in combination with `collectReturn` the collected results will be returned.
* Defaults to `false`.
*
* - `collectReturn` Set to true to collect the return of each object into an array.
* This array of return values will be returned from the trigger() call. Defaults to `false`.
*
* - `modParams` Allows each object the callback gets called on to modify the parameters to the next object.
* Setting modParams to an integer value will allow you to modify the parameter with that index.
* Any non-null value will modify the parameter index indicated.
* Defaults to false.
*
*
* @param string $callback|CakeEvent Method to fire on all the objects. Its assumed all the objects implement
* the method you are calling. If an instance of CakeEvent is provided, then then Event name will parsed to
* get the callback name. This is done by getting the last word after any dot in the event name
* (eg. `Model.afterSave` event will trigger the `afterSave` callback)
* @param array $params Array of parameters for the triggered callback.
* @param array $options Array of options.
* @return mixed Either the last result or all results if collectReturn is on.
* @throws CakeException when modParams is used with an index that does not exist.
*/
public function trigger($callback, $params = array(), $options = array()) {
if (empty($this->_enabled)) {
return true;
}
if ($callback instanceof CakeEvent) {
$event = $callback;
if (is_array($event->data)) {
$params =& $event->data;
}
if (empty($event->omitSubject)) {
$subject = $event->subject();
}
//TODO: Temporary BC check, while we move all the triggers system into the CakeEventManager
foreach (array('break', 'breakOn', 'collectReturn', 'modParams') as $opt) {
if (isset($event->{$opt})) {
$options[$opt] = $event->{$opt};
}
}
$parts = explode('.', $event->name());
$callback = array_pop($parts);
}
$options = array_merge(
array(
'break' => false,
'breakOn' => false,
'collectReturn' => false,
'modParams' => false
),
$options
);
$collected = array();
$list = array_keys($this->_enabled);
if ($options['modParams'] !== false && !isset($params[$options['modParams']])) {
throw new CakeException(__d('cake_dev', 'Cannot use modParams with indexes that do not exist.'));
}
foreach ($list as $name) {
$result = call_user_func_array(array($this->_loaded[$name], $callback), compact('subject') + $params);
if ($options['collectReturn'] === true) {
$collected[] = $result;
}
if (
$options['break'] && ($result === $options['breakOn'] ||
(is_array($options['breakOn']) && in_array($result, $options['breakOn'], true)))
) {
return $result;
} elseif ($options['modParams'] !== false && !in_array($result, array(true, false, null), true)) {
$params[$options['modParams']] = $result;
}
}
if ($options['modParams'] !== false) {
return $params[$options['modParams']];
}
return $options['collectReturn'] ? $collected : $result;
}
/**
* Provide public read access to the loaded objects
*
* @param string $name Name of property to read
* @return mixed
*/
public function __get($name) {
if (isset($this->_loaded[$name])) {
return $this->_loaded[$name];
}
return null;
}
/**
* Provide isset access to _loaded
*
* @param string $name Name of object being checked.
* @return boolean
*/
public function __isset($name) {
return isset($this->_loaded[$name]);
}
/**
* Enables callbacks on an object or array of objects
*
* @param string|array $name CamelCased name of the object(s) to enable (string or array)
* @param boolean Prioritize enabled list after enabling object(s)
* @return void
*/
public function enable($name, $prioritize = true) {
$enabled = false;
foreach ((array)$name as $object) {
if (isset($this->_loaded[$object]) && !isset($this->_enabled[$object])) {
$priority = isset($this->_loaded[$object]->settings['priority']) ? $this->_loaded[$object]->settings['priority'] : $this->defaultPriority;
$this->_enabled[$object] = array($priority);
$enabled = true;
}
}
if ($prioritize && $enabled) {
$this->prioritize();
}
}
/**
* Prioritize list of enabled object
*
* @return array Prioritized list of object
*/
public function prioritize() {
$i = 1;
foreach ($this->_enabled as $name => $priority) {
$priority[1] = $i++;
$this->_enabled[$name] = $priority;
}
asort($this->_enabled);
return $this->_enabled;
}
/**
* Set priority for an object or array of objects
*
* @param string|array $name CamelCased name of the object(s) to enable (string or array)
* If string the second param $priority is used else it should be an associative array
* with keys as object names and values as priorities to set.
* @param integer|null Integer priority to set or null for default
* @return void
*/
public function setPriority($name, $priority = null) {
if (is_string($name)) {
$name = array($name => $priority);
}
foreach ($name as $obj => $prio) {
if (isset($this->_loaded[$obj])) {
if (is_null($prio)) {
$prio = $this->defaultPriority;
}
$this->_loaded[$obj]->settings['priority'] = $prio;
if (isset($this->_enabled[$obj])) {
$this->_enabled[$obj] = array($prio);
}
}
}
$this->prioritize();
}
/**
* Disables callbacks on a object or array of objects. Public object methods are still
* callable as normal.
*
* @param string|array $name CamelCased name of the objects(s) to disable (string or array)
* @return void
*/
public function disable($name) {
foreach ((array)$name as $object) {
unset($this->_enabled[$object]);
}
}
/**
* Gets the list of currently-enabled objects, or, the current status of a single objects
*
* @param string $name Optional. The name of the object to check the status of. If omitted,
* returns an array of currently-enabled object
* @return mixed If $name is specified, returns the boolean status of the corresponding object.
* Otherwise, returns an array of all enabled objects.
*/
public function enabled($name = null) {
if (!empty($name)) {
return isset($this->_enabled[$name]);
}
return array_keys($this->_enabled);
}
/**
* Gets the list of attached objects, or, whether the given object is attached
*
* @param string $name Optional. The name of the behavior to check the status of. If omitted,
* returns an array of currently-attached behaviors
* @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
* Otherwise, returns an array of all attached behaviors.
*/
public function attached($name = null) {
if (!empty($name)) {
return isset($this->_loaded[$name]);
}
return array_keys($this->_loaded);
}
/**
* Name of the object to remove from the collection
*
* @param string $name Name of the object to delete.
* @return void
*/
public function unload($name) {
list($plugin, $name) = pluginSplit($name);
unset($this->_loaded[$name]);
unset($this->_enabled[$name]);
}
/**
* Adds or overwrites an instantiated object to the collection
*
* @param string $name Name of the object
* @param Object $object The object to use
* @return array Loaded objects
*/
public function set($name = null, $object = null) {
if (!empty($name) && !empty($object)) {
list($plugin, $name) = pluginSplit($name);
$this->_loaded[$name] = $object;
}
return $this->_loaded;
}
/**
* Normalizes an object array, creates an array that makes lazy loading
* easier
*
* @param array $objects Array of child objects to normalize.
* @return array Array of normalized objects.
*/
public static function normalizeObjectArray($objects) {
$normal = array();
foreach ($objects as $i => $objectName) {
$options = array();
if (!is_int($i)) {
$options = (array)$objectName;
$objectName = $i;
}
list($plugin, $name) = pluginSplit($objectName);
$normal[$name] = array('class' => $objectName, 'settings' => $options);
}
return $normal;
}
}
|