-
Notifications
You must be signed in to change notification settings - Fork 238
/
Object.php
274 lines (251 loc) · 10.3 KB
/
Object.php
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
<?php
/**
* Lithium: the most rad php framework
*
* @copyright Copyright 2011, Union of RAD (http://union-of-rad.org)
* @license http://opensource.org/licenses/bsd-license.php The BSD License
*/
namespace lithium\core;
use lithium\core\Libraries;
use lithium\util\collection\Filters;
/**
* Base class in Lithium's hierarchy, from which all concrete classes inherit. This class defines
* several conventions for how classes in Lithium should be structured:
*
* - **Universal constructor**: Any class which defines a `__construct()` method should take
* exactly one parameter (`$config`), and that parameter should always be an array. Any settings
* passed to the constructor will be stored in the `$_config` property of the object.
* - **Initialization / automatic configuration**: After the constructor, the `_init()` method is
* called. This method can be used to initialize the object, keeping complex logic and
* high-overhead or difficult to test operations out of the constructor. This method is called
* automatically by `Object::__construct()`, but may be disabled by passing `'init' => false` to
* the constructor. The initializer is also used for automatically assigning object properties.
* See the documentation for the `_init()` method for more details.
* - **Filters**: The `Object` class implements two methods which allow an object to easily
* implement filterable methods. The `_filter()` method allows methods to be implemented as
* filterable, and the `applyFilter()` method allows filters to be wrapped around them.
* - **Testing / misc.**: The `__set_state()` method provides a default implementation of the PHP
* magic method (works with `var_export()`) which can instantiate an object with a static method
* call. Finally, the `_stop()` method may be used instead of `exit()`, as it can be overridden
* for testing purposes.
*
* @see lithium\core\StaticObject
*/
class Object {
/**
* Stores configuration information for object instances at time of construction.
* **Do not override.** Pass any additional variables to `parent::__construct()`.
*
* @var array
*/
protected $_config = array();
/**
* Holds an array of values that should be processed on initialization. Each value should have
* a matching protected property (prefixed with `_`) defined in the class. If the property is
* an array, the property name should be the key and the value should be `'merge'`. See the
* `_init()` method for more details.
*
* @see lithium\core\Object::_init()
* @var array
*/
protected $_autoConfig = array();
/**
* Contains a 2-dimensional array of filters applied to this object's methods, indexed by method
* name. See the associated methods for more details.
*
* @see lithium\core\Object::_filter()
* @see lithium\core\Object::applyFilter()
* @var array
*/
protected $_methodFilters = array();
/**
* Parents of the current class.
*
* @see lithium\core\Object::_parents()
* @var array
*/
protected static $_parents = array();
/**
* Initializes class configuration (`$_config`), and assigns object properties using the
* `_init()` method, unless otherwise specified by configuration. See below for details.
*
* @see lithium\core\Object::$_config
* @see lithium\core\Object::_init()
* @param array $config The configuration options which will be assigned to the `$_config`
* property. This method accepts one configuration option:
* - `'init'` _boolean_: Controls constructor behavior for calling the `_init()`
* method. If `false`, the method is not called, otherwise it is. Defaults to
* `true`.
* @return void
*/
public function __construct(array $config = array()) {
$defaults = array('init' => true);
$this->_config = $config + $defaults;
if ($this->_config['init']) {
$this->_init();
}
}
/**
* Initializer function called by the constructor unless constructor `'init'` flag set to
* `false`. May be used for testing purposes, where objects need to be manipulated in an
* un-initialized state, or for high-overhead operations that require more control that the
* constructor provides. Additionally, this method iterates over the `$_autoConfig` property
* to automatically assign configuration settings to their corresponding properties.
*
* For example, given the following: {{{
* class Bar extends \lithium\core\Object {
* protected $_autoConfig = array('foo');
* protected $_foo;
* }
*
* $instance = new Bar(array('foo' => 'value'));
* }}}
*
* The `$_foo` property of `$instance` would automatically be set to `'value'`. If `$_foo` was
* an array, `$_autoConfig` could be set to `array('foo' => 'merge')`, and the constructor value
* of `'foo'` would be merged with the default value of `$_foo` and assigned to it.
*
* @see lithium\core\Object::$_autoConfig
* @return void
*/
protected function _init() {
foreach ($this->_autoConfig as $key => $flag) {
if (!isset($this->_config[$key]) && !isset($this->_config[$flag])) {
continue;
}
$property = "_{$key}";
if ($flag === 'merge') {
$property = '_' . $key;
$this->{$property} = $this->_config[$key] + $this->{$property};
} else {
$this->{"_$flag"} = $this->_config[$flag];
}
}
}
/**
* Apply a closure to a method of the current object instance.
*
* @see lithium\core\Object::_filter()
* @see lithium\util\collection\Filters
* @param mixed $method The name of the method to apply the closure to. Can either be a single
* method name as a string, or an array of method names.
* @param closure $filter The closure that is used to filter the method(s).
* @return void
*/
public function applyFilter($method, $filter = null) {
foreach ((array) $method as $m) {
if (!isset($this->_methodFilters[$m])) {
$this->_methodFilters[$m] = array();
}
$this->_methodFilters[$m][] = $filter;
}
}
/**
* Calls a method on this object with the given parameters. Provides an OO wrapper
* for call_user_func_array, and improves performance by using straight method calls
* in most cases.
*
* @param string $method Name of the method to call
* @param array $params Parameter list to use when calling $method
* @return mixed Returns the result of the method call
*/
public function invokeMethod($method, $params = array()) {
switch (count($params)) {
case 0:
return $this->{$method}();
case 1:
return $this->{$method}($params[0]);
case 2:
return $this->{$method}($params[0], $params[1]);
case 3:
return $this->{$method}($params[0], $params[1], $params[2]);
case 4:
return $this->{$method}($params[0], $params[1], $params[2], $params[3]);
case 5:
return $this->{$method}($params[0], $params[1], $params[2], $params[3], $params[4]);
default:
return call_user_func_array(array(&$this, $method), $params);
}
}
/**
* PHP magic method used in conjunction with `var_export()` to allow objects to be
* re-instantiated with their pre-existing properties and values intact. This method can be
* called statically on any class that extends `Object` to return an instance of it.
*
* @param array $data An array of properties and values with which to re-instantiate the object.
* These properties can be both public and protected.
* @return object Returns an instance of the requested object with the given properties set.
*/
public static function __set_state($data) {
$class = get_called_class();
$object = new $class();
foreach ($data as $property => $value) {
$object->{$property} = $value;
}
return $object;
}
/**
* Returns an instance of a class with given `config`. The `name` could be a key from the
* `classes` array, a fully-namespaced class name, or an object. Typically this method is used
* in `_init` to create the dependencies used in the current class.
*
* @param string|object $name A `classes` key or fully-namespaced class name.
* @param array $options The configuration passed to the constructor.
* @return object
*/
protected function _instance($name, array $options = array()) {
if (is_string($name) && isset($this->_classes[$name])) {
$name = $this->_classes[$name];
}
return Libraries::instance(null, $name, $options);
}
/**
* Executes a set of filters against a method by taking a method's main implementation as a
* callback, and iteratively wrapping the filters around it. This, along with the `Filters`
* class, is the core of Lithium's filters system. This system allows you to "reach into" an
* object's methods which are marked as _filterable_, and intercept calls to those methods,
* optionally modifying parameters or return values.
*
* @see lithium\core\Object::applyFilter()
* @see lithium\util\collection\Filters
* @param string $method The name of the method being executed, usually the value of
* `__METHOD__`.
* @param array $params An associative array containing all the parameters passed into
* the method.
* @param Closure $callback The method's implementation, wrapped in a closure.
* @param array $filters Additional filters to apply to the method for this call only.
* @return mixed Returns the return value of `$callback`, modified by any filters passed in
* `$filters` or applied with `applyFilter()`.
*/
protected function _filter($method, $params, $callback, $filters = array()) {
list($class, $method) = explode('::', $method);
if (empty($this->_methodFilters[$method]) && empty($filters)) {
return $callback($this, $params, null);
}
$f = isset($this->_methodFilters[$method]) ? $this->_methodFilters[$method] : array();
$data = array_merge($f, $filters, array($callback));
return Filters::run($this, $params, compact('data', 'class', 'method'));
}
/**
* Gets and caches an array of the parent methods of a class.
*
* @return array Returns an array of parent classes for the current class.
*/
protected static function _parents() {
$class = get_called_class();
if (!isset(self::$_parents[$class])) {
self::$_parents[$class] = class_parents($class);
}
return self::$_parents[$class];
}
/**
* Exit immediately. Primarily used for overrides during testing.
*
* @param integer|string $status integer range 0 to 254, string printed on exit
* @return void
*/
protected function _stop($status = 0) {
exit($status);
}
}
?>