PageRenderTime 26ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/zf2/vendor/Zend/Stdlib/CallbackHandler.php

http://github.com/eryx/php-framework-benchmark
PHP | 365 lines | 163 code | 43 blank | 159 comment | 29 complexity | c973d4c9d8dfbd3699fc079c0895d3f5 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, Apache-2.0, LGPL-2.1, LGPL-3.0, BSD-2-Clause
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Stdlib
  17. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. */
  20. /**
  21. * @namespace
  22. */
  23. namespace Zend\Stdlib;
  24. use Closure,
  25. WeakRef;
  26. /**
  27. * CallbackHandler
  28. *
  29. * A handler for a event, event, filterchain, etc. Abstracts PHP callbacks,
  30. * primarily to allow for lazy-loading and ensuring availability of default
  31. * arguments (currying).
  32. *
  33. * @category Zend
  34. * @package Zend_Stdlib
  35. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  36. * @license http://framework.zend.com/license/new-bsd New BSD License
  37. */
  38. class CallbackHandler
  39. {
  40. /**
  41. * @var string|array PHP callback to invoke
  42. */
  43. protected $callback;
  44. /**
  45. * @var string Event to which this handle is subscribed
  46. */
  47. protected $event;
  48. /**
  49. * Until callback has been validated, mark as invalid
  50. * @var bool
  51. */
  52. protected $isValidCallback = false;
  53. /**
  54. * Callback options, if any
  55. * @var array
  56. */
  57. protected $options;
  58. /**
  59. * Constructor
  60. *
  61. * @param string $event Event to which slot is subscribed
  62. * @param string|array|object $callback PHP callback
  63. * @param array $options Options used by the callback handler (e.g., priority)
  64. * @return void
  65. */
  66. public function __construct($event, $callback, array $options = array())
  67. {
  68. $this->event = $event;
  69. $this->options = $options;
  70. $this->registerCallback($callback);
  71. }
  72. /**
  73. * Registers the callback provided in the constructor
  74. *
  75. * If you have pecl/weakref {@see http://pecl.php.net/weakref} installed,
  76. * this method provides additional behavior.
  77. *
  78. * If a callback is a functor, or an array callback composing an object
  79. * instance, this method will pass the object to a WeakRef instance prior
  80. * to registering the callback. See {@link isValid()} for more information
  81. * on how this affects execution.
  82. *
  83. * @param callback $callback
  84. * @return void
  85. */
  86. protected function registerCallback($callback)
  87. {
  88. // If pecl/weakref is not installed, simply register it
  89. if (!class_exists('WeakRef', false)) {
  90. $this->callback = $callback;
  91. return;
  92. }
  93. // If we have a non-closure object, pass it to WeakRef, and then
  94. // register it.
  95. if (is_object($callback) && !$callback instanceof Closure) {
  96. $this->callback = new WeakRef($callback);
  97. return;
  98. }
  99. // If we have a string or closure, register as-is
  100. if (!is_array($callback)) {
  101. $this->callback = $callback;
  102. return;
  103. }
  104. list($target, $method) = $callback;
  105. // If we have an array callback, and the first argument is not an
  106. // object, register as-is
  107. if (!is_object($target)) {
  108. $this->callback = $callback;
  109. return;
  110. }
  111. // We have an array callback with an object as the first argument;
  112. // pass it to WeakRef, and then register the new callback
  113. $target = new WeakRef($target);
  114. $this->callback = array($target, $method);
  115. }
  116. /**
  117. * Get event to which handler is subscribed
  118. *
  119. * @return string
  120. */
  121. public function getEvent()
  122. {
  123. return $this->event;
  124. }
  125. /**
  126. * Retrieve registered callback
  127. *
  128. * @return Callback
  129. * @throws Exception\InvalidCallbackException If callback is invalid
  130. */
  131. public function getCallback()
  132. {
  133. if (!$this->isValid()) {
  134. throw new Exception\InvalidCallbackException('Invalid callback provided; not callable');
  135. }
  136. $callback = $this->callback;
  137. if (is_string($callback)) {
  138. return $callback;
  139. }
  140. if ($callback instanceof WeakRef) {
  141. return $callback->get();
  142. }
  143. if (is_object($callback)) {
  144. return $callback;
  145. }
  146. list($target, $method) = $callback;
  147. if ($target instanceof WeakRef) {
  148. return array($target->get(), $method);
  149. }
  150. return $callback;
  151. }
  152. /**
  153. * Invoke handler
  154. *
  155. * @param array $args Arguments to pass to callback
  156. * @return mixed
  157. */
  158. public function call(array $args = array())
  159. {
  160. $callback = $this->getCallback();
  161. return call_user_func_array($callback, $args);
  162. }
  163. /**
  164. * Get all callback options
  165. *
  166. * @return array
  167. */
  168. public function getOptions()
  169. {
  170. return $this->options;
  171. }
  172. /**
  173. * Retrieve a single option
  174. *
  175. * @param string $name
  176. * @return mixed
  177. */
  178. public function getOption($name)
  179. {
  180. if (array_key_exists($name, $this->options)) {
  181. return $this->options[$name];
  182. }
  183. return null;
  184. }
  185. /**
  186. * Is the composed callback valid?
  187. *
  188. * Typically, this method simply checks to see if we have a valid callback.
  189. * In a few situations, it does more.
  190. *
  191. * * If we have a string callback, we pass execution to
  192. * {@link validateStringCallback()}.
  193. * * If we have an object callback, we test to see if that object is a
  194. * WeakRef {@see http://pecl.php.net/weakref}. If so, we return the value
  195. * of its valid() method. Otherwise, we return the result of is_callable().
  196. * * If we have a callback array with a string in the first position, we
  197. * pass execution to {@link validateArrayCallback()}.
  198. * * If we have a callback array with an object in the first position, we
  199. * test to see if that object is a WeakRef (@see http://pecl.php.net/weakref).
  200. * If so, we return the value of its valid() method. Otherwise, we return
  201. * the result of is_callable() on the callback.
  202. *
  203. * WeakRef is used to allow listeners to go out of scope. This functionality
  204. * is turn-key if you have pecl/weakref installed; otherwise, you will have
  205. * to manually remove listeners before destroying an object referenced in a
  206. * listener.
  207. *
  208. * @return bool
  209. */
  210. public function isValid()
  211. {
  212. // If we've already tested this, we can move on. Note: if a callback
  213. // composes a WeakRef, this will never get set, and thus result in
  214. // validation on each call.
  215. if ($this->isValidCallback) {
  216. return $this->callback;
  217. }
  218. $callback = $this->callback;
  219. if (is_string($callback)) {
  220. return $this->validateStringCallback($callback);
  221. }
  222. if ($callback instanceof WeakRef) {
  223. return $callback->valid();
  224. }
  225. if (is_object($callback) && is_callable($callback)) {
  226. $this->isValidCallback = true;
  227. return true;
  228. }
  229. if (!is_array($callback)) {
  230. return false;
  231. }
  232. list($target, $method) = $callback;
  233. if ($target instanceof WeakRef) {
  234. if (!$target->valid()) {
  235. return false;
  236. }
  237. $target = $target->get();
  238. return is_callable(array($target, $method));
  239. }
  240. return $this->validateArrayCallback($callback);
  241. }
  242. /**
  243. * Validate a string callback
  244. *
  245. * Check first if the string provided is callable. If not see if it is a
  246. * valid class name; if so, determine if the object is invokable.
  247. *
  248. * @param string $callback
  249. * @return bool
  250. */
  251. protected function validateStringCallback($callback)
  252. {
  253. if (is_callable($callback)) {
  254. $this->isValidCallback = true;
  255. return true;
  256. }
  257. if (!class_exists($callback)) {
  258. return false;
  259. }
  260. // check __invoke before instantiating
  261. if (!method_exists($callback, '__invoke')) {
  262. return false;
  263. }
  264. $object = new $callback();
  265. $this->callback = $object;
  266. $this->isValidCallback = true;
  267. return true;
  268. }
  269. /**
  270. * Validate an array callback
  271. *
  272. * @param array $callback
  273. * @return bool
  274. */
  275. protected function validateArrayCallback(array $callback)
  276. {
  277. $context = $callback[0];
  278. $method = $callback[1];
  279. if (is_string($context)) {
  280. // Dealing with a class/method callback, and class provided is a string classname
  281. if (!class_exists($context)) {
  282. return false;
  283. }
  284. // We need to determine if we need to instantiate the class first
  285. $r = new \ReflectionClass($context);
  286. if (!$r->hasMethod($method)) {
  287. // Explicit method does not exist
  288. if (!$r->hasMethod('__callStatic') && !$r->hasMethod('__call')) {
  289. return false;
  290. }
  291. if ($r->hasMethod('__callStatic')) {
  292. // We have a __callStatic defined, so the original callback is valid
  293. $this->isValidCallback = true;
  294. return $callback;
  295. }
  296. // We have __call defined, so we need to instantiate the class
  297. // first, and redefine the callback
  298. $object = new $context();
  299. $this->callback = array($object, $method);
  300. $this->isValidCallback = true;
  301. return $this->callback;
  302. }
  303. // Explicit method exists
  304. $rMethod = $r->getMethod($method);
  305. if ($rMethod->isStatic()) {
  306. // Method is static, so original callback is fine
  307. $this->isValidCallback = true;
  308. return $callback;
  309. }
  310. // Method is an instance method; instantiate object and redefine callback
  311. $object = new $context();
  312. $this->callback = array($object, $method);
  313. $this->isValidCallback = true;
  314. return $this->callback;
  315. } elseif (is_callable($callback)) {
  316. // The
  317. $this->isValidCallback = true;
  318. return $callback;
  319. }
  320. return false;
  321. }
  322. }