Object.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
  6. * @license http://opensource.org/licenses/bsd-license.php The BSD License
  7. */
  8. namespace lithium\core;
  9. use lithium\core\Libraries;
  10. use lithium\util\collection\Filters;
  11. use lithium\analysis\Inspector;
  12. /**
  13. * Base class in Lithium's hierarchy, from which all concrete classes inherit. This class defines
  14. * several conventions for how classes in Lithium should be structured:
  15. *
  16. * - **Universal constructor**: Any class which defines a `__construct()` method should take
  17. * exactly one parameter (`$config`), and that parameter should always be an array. Any settings
  18. * passed to the constructor will be stored in the `$_config` property of the object.
  19. * - **Initialization / automatic configuration**: After the constructor, the `_init()` method is
  20. * called. This method can be used to initialize the object, keeping complex logic and
  21. * high-overhead or difficult to test operations out of the constructor. This method is called
  22. * automatically by `Object::__construct()`, but may be disabled by passing `'init' => false` to
  23. * the constructor. The initializer is also used for automatically assigning object properties.
  24. * See the documentation on the `_init()` method for more details.
  25. * - **Filters**: The `Object` class implements two methods which allow an object to easily
  26. * implement filterable methods. The `_filter()` method allows methods to be implemented as
  27. * filterable, and the `applyFilter()` method allows filters to be wrapped around them.
  28. * - **Testing / misc.**: The `__set_state()` method provides a default implementation of the PHP
  29. * magic method (works with `var_export()`) which can instantiate an object with a static method
  30. * call. Finally, the `_stop()` method may be used instead of `exit()`, as it can be overridden
  31. * for testing purposes.
  32. *
  33. * @see lithium\core\StaticObject
  34. */
  35. class Object {
  36. /**
  37. * Stores configuration information for object instances at time of construction.
  38. * **Do not override.** Pass any additional variables to `parent::__construct()`.
  39. *
  40. * @var array
  41. */
  42. protected $_config = array();
  43. /**
  44. * Holds an array of values that should be processed on initialization. Each value should have
  45. * a matching protected property (prefixed with `_`) defined in the class. If the property is
  46. * an array, the property name should be the key and the value should be `'merge'`. See the
  47. * `_init()` method for more details.
  48. *
  49. * @see lithium\core\Object::_init()
  50. * @var array
  51. */
  52. protected $_autoConfig = array();
  53. /**
  54. * Contains a 2-dimensional array of filters applied to this object's methods, indexed by method
  55. * name. See the associated methods for more details.
  56. *
  57. * @see lithium\core\Object::_filter()
  58. * @see lithium\core\Object::applyFilter()
  59. * @var array
  60. */
  61. protected $_methodFilters = array();
  62. /**
  63. * Parents of the current class.
  64. *
  65. * @see lithium\core\Object::_parents()
  66. * @var array
  67. */
  68. protected static $_parents = array();
  69. /**
  70. * Initializes class configuration (`$_config`), and assigns object properties using the
  71. * `_init()` method, unless otherwise specified by configuration. See below for details.
  72. *
  73. * @see lithium\core\Object::$_config
  74. * @see lithium\core\Object::_init()
  75. * @param array $config The configuration options which will be assigned to the `$_config`
  76. * property. This method accepts one configuration option:
  77. * - `'init'` _boolean_: Controls constructor behavior for calling the `_init()`
  78. * method. If `false`, the method is not called, otherwise it is. Defaults to
  79. * `true`.
  80. */
  81. public function __construct(array $config = array()) {
  82. $defaults = array('init' => true);
  83. $this->_config = $config + $defaults;
  84. if ($this->_config['init']) {
  85. $this->_init();
  86. }
  87. }
  88. /**
  89. * Initializer function called by the constructor unless the constructor `'init'` flag is set
  90. * to `false`. May be used for testing purposes, where objects need to be manipulated in an
  91. * un-initialized state, or for high-overhead operations that require more control than the
  92. * constructor provides. Additionally, this method iterates over the `$_autoConfig` property
  93. * to automatically assign configuration settings to their corresponding properties.
  94. *
  95. * For example, given the following: {{{
  96. * class Bar extends \lithium\core\Object {
  97. * protected $_autoConfig = array('foo');
  98. * protected $_foo;
  99. * }
  100. *
  101. * $instance = new Bar(array('foo' => 'value'));
  102. * }}}
  103. *
  104. * The `$_foo` property of `$instance` would automatically be set to `'value'`. If `$_foo` was
  105. * an array, `$_autoConfig` could be set to `array('foo' => 'merge')`, and the constructor value
  106. * of `'foo'` would be merged with the default value of `$_foo` and assigned to it.
  107. *
  108. * @see lithium\core\Object::$_autoConfig
  109. * @return void
  110. */
  111. protected function _init() {
  112. foreach ($this->_autoConfig as $key => $flag) {
  113. if (!isset($this->_config[$key]) && !isset($this->_config[$flag])) {
  114. continue;
  115. }
  116. if ($flag === 'merge') {
  117. $this->{"_{$key}"} = $this->_config[$key] + $this->{"_{$key}"};
  118. } else {
  119. $this->{"_$flag"} = $this->_config[$flag];
  120. }
  121. }
  122. }
  123. /**
  124. * Apply a closure to a method of the current object instance.
  125. *
  126. * @see lithium\core\Object::_filter()
  127. * @see lithium\util\collection\Filters
  128. * @param mixed $method The name of the method to apply the closure to. Can either be a single
  129. * method name as a string, or an array of method names. Can also be false to remove
  130. * all filters on the current object.
  131. * @param closure $filter The closure that is used to filter the method(s), can also be false
  132. * to remove all the current filters for the given method.
  133. * @return void
  134. */
  135. public function applyFilter($method, $filter = null) {
  136. if ($method === false) {
  137. $this->_methodFilters = array();
  138. return;
  139. }
  140. foreach ((array) $method as $m) {
  141. if (!isset($this->_methodFilters[$m]) || $filter === false) {
  142. $this->_methodFilters[$m] = array();
  143. }
  144. if ($filter !== false) {
  145. $this->_methodFilters[$m][] = $filter;
  146. }
  147. }
  148. }
  149. /**
  150. * Calls a method on this object with the given parameters. Provides an OO wrapper
  151. * for call_user_func_array, and improves performance by using straight method calls
  152. * in most cases.
  153. *
  154. * @param string $method Name of the method to call
  155. * @param array $params Parameter list to use when calling $method
  156. * @return mixed Returns the result of the method call
  157. */
  158. public function invokeMethod($method, $params = array()) {
  159. switch (count($params)) {
  160. case 0:
  161. return $this->{$method}();
  162. case 1:
  163. return $this->{$method}($params[0]);
  164. case 2:
  165. return $this->{$method}($params[0], $params[1]);
  166. case 3:
  167. return $this->{$method}($params[0], $params[1], $params[2]);
  168. case 4:
  169. return $this->{$method}($params[0], $params[1], $params[2], $params[3]);
  170. case 5:
  171. return $this->{$method}($params[0], $params[1], $params[2], $params[3], $params[4]);
  172. default:
  173. return call_user_func_array(array(&$this, $method), $params);
  174. }
  175. }
  176. /**
  177. * PHP magic method used in conjunction with `var_export()` to allow objects to be
  178. * re-instantiated with their pre-existing properties and values intact. This method can be
  179. * called statically on any class that extends `Object` to return an instance of it.
  180. *
  181. * @param array $data An array of properties and values with which to re-instantiate the object.
  182. * These properties can be both public and protected.
  183. * @return object Returns an instance of the requested object with the given properties set.
  184. */
  185. public static function __set_state($data) {
  186. $class = get_called_class();
  187. $object = new $class();
  188. foreach ($data as $property => $value) {
  189. $object->{$property} = $value;
  190. }
  191. return $object;
  192. }
  193. /**
  194. * Will determine if a method can be called.
  195. *
  196. * @param string $method Method name.
  197. * @param bool $internal Interal call or not.
  198. * @return bool
  199. */
  200. public function respondsTo($method, $internal = false) {
  201. return Inspector::isCallable($this, $method, $internal);
  202. }
  203. /**
  204. * Returns an instance of a class with given `config`. The `name` could be a key from the
  205. * `classes` array, a fully-namespaced class name, or an object. Typically this method is used
  206. * in `_init` to create the dependencies used in the current class.
  207. *
  208. * @param string|object $name A `classes` key or fully-namespaced class name.
  209. * @param array $options The configuration passed to the constructor.
  210. * @return object
  211. */
  212. protected function _instance($name, array $options = array()) {
  213. if (is_string($name) && isset($this->_classes[$name])) {
  214. $name = $this->_classes[$name];
  215. }
  216. return Libraries::instance(null, $name, $options);
  217. }
  218. /**
  219. * Executes a set of filters against a method by taking a method's main implementation as a
  220. * callback, and iteratively wrapping the filters around it. This, along with the `Filters`
  221. * class, is the core of Lithium's filters system. This system allows you to "reach into" an
  222. * object's methods which are marked as _filterable_, and intercept calls to those methods,
  223. * optionally modifying parameters or return values.
  224. *
  225. * @see lithium\core\Object::applyFilter()
  226. * @see lithium\util\collection\Filters
  227. * @param string $method The name of the method being executed, usually the value of
  228. * `__METHOD__`.
  229. * @param array $params An associative array containing all the parameters passed into
  230. * the method.
  231. * @param Closure $callback The method's implementation, wrapped in a closure.
  232. * @param array $filters Additional filters to apply to the method for this call only.
  233. * @return mixed Returns the return value of `$callback`, modified by any filters passed in
  234. * `$filters` or applied with `applyFilter()`.
  235. */
  236. protected function _filter($method, $params, $callback, $filters = array()) {
  237. list($class, $method) = explode('::', $method);
  238. if (empty($this->_methodFilters[$method]) && empty($filters)) {
  239. return $callback($this, $params, null);
  240. }
  241. $f = isset($this->_methodFilters[$method]) ? $this->_methodFilters[$method] : array();
  242. $data = array_merge($f, $filters, array($callback));
  243. return Filters::run($this, $params, compact('data', 'class', 'method'));
  244. }
  245. /**
  246. * Gets and caches an array of the parent methods of a class.
  247. *
  248. * @return array Returns an array of parent classes for the current class.
  249. */
  250. protected static function _parents() {
  251. $class = get_called_class();
  252. if (!isset(self::$_parents[$class])) {
  253. self::$_parents[$class] = class_parents($class);
  254. }
  255. return self::$_parents[$class];
  256. }
  257. /**
  258. * Exit immediately. Primarily used for overrides during testing.
  259. *
  260. * @param integer|string $status integer range 0 to 254, string printed on exit
  261. * @return void
  262. */
  263. protected function _stop($status = 0) {
  264. exit($status);
  265. }
  266. }
  267. ?>