File indexing completed on 2024-12-22 05:37:07
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Stdlib 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 */ 0020 0021 /** 0022 * CallbackHandler 0023 * 0024 * A handler for a event, event, filterchain, etc. Abstracts PHP callbacks, 0025 * primarily to allow for lazy-loading and ensuring availability of default 0026 * arguments (currying). 0027 * 0028 * @category Zend 0029 * @package Zend_Stdlib 0030 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0031 * @license http://framework.zend.com/license/new-bsd New BSD License 0032 */ 0033 class Zend_Stdlib_CallbackHandler 0034 { 0035 /** 0036 * @var string|array PHP callback to invoke 0037 */ 0038 protected $callback; 0039 0040 /** 0041 * Did an error occur when testing the validity of the callback? 0042 * @var bool 0043 */ 0044 protected $error = false; 0045 0046 /** 0047 * Callback metadata, if any 0048 * @var array 0049 */ 0050 protected $metadata; 0051 0052 /** 0053 * Constructor 0054 * 0055 * @param string $event Event to which slot is subscribed 0056 * @param string|array|object $callback PHP callback 0057 * @param array $options Options used by the callback handler (e.g., priority) 0058 * @return void 0059 */ 0060 public function __construct($callback, array $metadata = array()) 0061 { 0062 $this->metadata = $metadata; 0063 $this->registerCallback($callback); 0064 } 0065 0066 /** 0067 * Error handler 0068 * 0069 * Used by registerCallback() when calling is_callable() to capture engine warnings. 0070 * 0071 * @param int $errno 0072 * @param string $errstr 0073 * @return void 0074 */ 0075 public function errorHandler($errno, $errstr) 0076 { 0077 $this->error = true; 0078 } 0079 0080 /** 0081 * Registers the callback provided in the constructor 0082 * 0083 * If you have pecl/weakref {@see http://pecl.php.net/weakref} installed, 0084 * this method provides additional behavior. 0085 * 0086 * If a callback is a functor, or an array callback composing an object 0087 * instance, this method will pass the object to a WeakRef instance prior 0088 * to registering the callback. 0089 * 0090 * @param Callable $callback 0091 * @return void 0092 */ 0093 protected function registerCallback($callback) 0094 { 0095 set_error_handler(array($this, 'errorHandler'), E_STRICT); 0096 $callable = is_callable($callback); 0097 restore_error_handler(); 0098 if (!$callable || $this->error) { 0099 // require_once 'Zend/Stdlib/Exception/InvalidCallbackException.php'; 0100 throw new Zend_Stdlib_Exception_InvalidCallbackException('Invalid callback provided; not callable'); 0101 } 0102 0103 // If pecl/weakref is not installed, simply store the callback and return 0104 set_error_handler(array($this, 'errorHandler'), E_WARNING); 0105 $callable = class_exists('WeakRef'); 0106 restore_error_handler(); 0107 if (!$callable || $this->error) { 0108 $this->callback = $callback; 0109 return; 0110 } 0111 0112 // If WeakRef exists, we want to use it. 0113 0114 // If we have a non-closure object, pass it to WeakRef, and then 0115 // register it. 0116 if (is_object($callback) && !$callback instanceof Closure) { 0117 $this->callback = new WeakRef($callback); 0118 return; 0119 } 0120 0121 // If we have a string or closure, register as-is 0122 if (!is_array($callback)) { 0123 $this->callback = $callback; 0124 return; 0125 } 0126 0127 list($target, $method) = $callback; 0128 0129 // If we have an array callback, and the first argument is not an 0130 // object, register as-is 0131 if (!is_object($target)) { 0132 $this->callback = $callback; 0133 return; 0134 } 0135 0136 // We have an array callback with an object as the first argument; 0137 // pass it to WeakRef, and then register the new callback 0138 $target = new WeakRef($target); 0139 $this->callback = array($target, $method); 0140 } 0141 0142 /** 0143 * Retrieve registered callback 0144 * 0145 * @return Callable 0146 */ 0147 public function getCallback() 0148 { 0149 $callback = $this->callback; 0150 0151 // String callbacks -- simply return 0152 if (is_string($callback)) { 0153 return $callback; 0154 } 0155 0156 // WeakRef callbacks -- pull it out of the object and return it 0157 if ($callback instanceof WeakRef) { 0158 return $callback->get(); 0159 } 0160 0161 // Non-WeakRef object callback -- return it 0162 if (is_object($callback)) { 0163 return $callback; 0164 } 0165 0166 // Array callback with WeakRef object -- retrieve the object first, and 0167 // then return 0168 list($target, $method) = $callback; 0169 if ($target instanceof WeakRef) { 0170 return array($target->get(), $method); 0171 } 0172 0173 // Otherwise, return it 0174 return $callback; 0175 } 0176 0177 /** 0178 * Invoke handler 0179 * 0180 * @param array $args Arguments to pass to callback 0181 * @return mixed 0182 */ 0183 public function call(array $args = array()) 0184 { 0185 $callback = $this->getCallback(); 0186 0187 $isPhp54 = version_compare(PHP_VERSION, '5.4.0rc1', '>='); 0188 0189 if ($isPhp54 && is_string($callback)) { 0190 $this->validateStringCallbackFor54($callback); 0191 } 0192 0193 // Minor performance tweak; use call_user_func() until > 3 arguments 0194 // reached 0195 switch (count($args)) { 0196 case 0: 0197 if ($isPhp54) { 0198 return $callback(); 0199 } 0200 return call_user_func($callback); 0201 case 1: 0202 if ($isPhp54) { 0203 return $callback(array_shift($args)); 0204 } 0205 return call_user_func($callback, array_shift($args)); 0206 case 2: 0207 $arg1 = array_shift($args); 0208 $arg2 = array_shift($args); 0209 if ($isPhp54) { 0210 return $callback($arg1, $arg2); 0211 } 0212 return call_user_func($callback, $arg1, $arg2); 0213 case 3: 0214 $arg1 = array_shift($args); 0215 $arg2 = array_shift($args); 0216 $arg3 = array_shift($args); 0217 if ($isPhp54) { 0218 return $callback($arg1, $arg2, $arg3); 0219 } 0220 return call_user_func($callback, $arg1, $arg2, $arg3); 0221 default: 0222 return call_user_func_array($callback, $args); 0223 } 0224 } 0225 0226 /** 0227 * Invoke as functor 0228 * 0229 * @return mixed 0230 */ 0231 public function __invoke() 0232 { 0233 return $this->call(func_get_args()); 0234 } 0235 0236 /** 0237 * Get all callback metadata 0238 * 0239 * @return array 0240 */ 0241 public function getMetadata() 0242 { 0243 return $this->metadata; 0244 } 0245 0246 /** 0247 * Retrieve a single metadatum 0248 * 0249 * @param string $name 0250 * @return mixed 0251 */ 0252 public function getMetadatum($name) 0253 { 0254 if (array_key_exists($name, $this->metadata)) { 0255 return $this->metadata[$name]; 0256 } 0257 return null; 0258 } 0259 0260 /** 0261 * Validate a static method call 0262 * 0263 * Validates that a static method call in PHP 5.4 will actually work 0264 * 0265 * @param string $callback 0266 * @return true 0267 * @throws Zend_Stdlib_Exception_InvalidCallbackException if invalid 0268 */ 0269 protected function validateStringCallbackFor54($callback) 0270 { 0271 if (!strstr($callback, '::')) { 0272 return true; 0273 } 0274 0275 list($class, $method) = explode('::', $callback, 2); 0276 0277 if (!class_exists($class)) { 0278 // require_once 'Zend/Stdlib/Exception/InvalidCallbackException.php'; 0279 throw new Zend_Stdlib_Exception_InvalidCallbackException(sprintf( 0280 'Static method call "%s" refers to a class that does not exist', 0281 $callback 0282 )); 0283 } 0284 0285 $r = new ReflectionClass($class); 0286 if (!$r->hasMethod($method)) { 0287 // require_once 'Zend/Stdlib/Exception/InvalidCallbackException.php'; 0288 throw new Zend_Stdlib_Exception_InvalidCallbackException(sprintf( 0289 'Static method call "%s" refers to a method that does not exist', 0290 $callback 0291 )); 0292 } 0293 $m = $r->getMethod($method); 0294 if (!$m->isStatic()) { 0295 // require_once 'Zend/Stdlib/Exception/InvalidCallbackException.php'; 0296 throw new Zend_Stdlib_Exception_InvalidCallbackException(sprintf( 0297 'Static method call "%s" refers to a method that is not static', 0298 $callback 0299 )); 0300 } 0301 0302 return true; 0303 } 0304 }