method or * closure, they all have the same behaviour. This callable is an extension of * native PHP callable (aka callback) to integrate Hoa's structures. * * @copyright Copyright © 2007-2017 Hoa community * @license New BSD License */ class Xcallable { /** * Callback, with the PHP format. * * @var mixed */ protected $_callback = null; /** * Callable hash. * * @var string */ protected $_hash = null; /** * Build a callback. * Accepted forms: * * `'function'`, * * `'class::method'`, * * `'class', 'method'`, * * `$object, 'method'`, * * `$object, ''`, * * `function (…) { … }`, * * `['class', 'method']`, * * `[$object, 'method']`. * * @param mixed $call First callable part. * @param mixed $able Second callable part (if needed). */ public function __construct($call, $able = '') { if ($call instanceof \Closure) { $this->_callback = $call; return; } if (!is_string($able)) { throw new Exception( 'Bad callback form; the able part must be a string.', 0 ); } if ('' === $able) { if (is_string($call)) { if (false === strpos($call, '::')) { if (!function_exists($call)) { throw new Exception( 'Bad callback form; function %s does not exist.', 1, $call ); } $this->_callback = $call; return; } list($call, $able) = explode('::', $call); } elseif (is_object($call)) { if ($call instanceof Stream\IStream\Out) { $able = null; } elseif (method_exists($call, '__invoke')) { $able = '__invoke'; } else { throw new Exception( 'Bad callback form; an object but without a known ' . 'method.', 2 ); } } elseif (is_array($call) && isset($call[0])) { if (!isset($call[1])) { return $this->__construct($call[0]); } return $this->__construct($call[0], $call[1]); } else { throw new Exception( 'Bad callback form.', 3 ); } } $this->_callback = [$call, $able]; return; } /** * Call the callable. * * @param ... * @return mixed */ public function __invoke() { $arguments = func_get_args(); $valid = $this->getValidCallback($arguments); return call_user_func_array($valid, $arguments); } /** * Distribute arguments according to an array. * * @param array $arguments Arguments. * @return mixed */ public function distributeArguments(array $arguments) { return call_user_func_array([$this, '__invoke'], $arguments); } /** * Get a valid callback in the PHP meaning. * * @param array &$arguments Arguments (could determine method on an * object if not precised). * @return mixed */ public function getValidCallback(array &$arguments = []) { $callback = $this->_callback; $head = null; if (isset($arguments[0])) { $head = &$arguments[0]; } // If method is undetermined, we find it (we understand event bucket and // stream). if (null !== $head && is_array($callback) && null === $callback[1]) { if ($head instanceof Event\Bucket) { $head = $head->getData(); } switch ($type = gettype($head)) { case 'string': if (1 === strlen($head)) { $method = 'writeCharacter'; } else { $method = 'writeString'; } break; case 'boolean': case 'integer': case 'array': $method = 'write' . ucfirst($type); break; case 'double': $method = 'writeFloat'; break; default: $method = 'writeAll'; $head = $head . "\n"; } $callback[1] = $method; } return $callback; } /** * Get hash. * Will produce: * * function#…; * * class#…::…; * * object(…)#…::…; * * closure(…). * * @return string */ public function getHash() { if (null !== $this->_hash) { return $this->_hash; } $_ = &$this->_callback; if (is_string($_)) { return $this->_hash = 'function#' . $_; } if (is_array($_)) { return $this->_hash = (is_object($_[0]) ? 'object(' . spl_object_hash($_[0]) . ')' . '#' . get_class($_[0]) : 'class#' . $_[0]) . '::' . (null !== $_[1] ? $_[1] : '???'); } return $this->_hash = 'closure(' . spl_object_hash($_) . ')'; } /** * Get appropriated reflection instance. * * @param ... * @return \Reflector */ public function getReflection() { $arguments = func_get_args(); $valid = $this->getValidCallback($arguments); if (is_string($valid)) { return new \ReflectionFunction($valid); } if ($valid instanceof \Closure) { return new \ReflectionFunction($valid); } if (is_array($valid)) { if (is_string($valid[0])) { if (false === method_exists($valid[0], $valid[1])) { return new \ReflectionClass($valid[0]); } return new \ReflectionMethod($valid[0], $valid[1]); } $object = new \ReflectionObject($valid[0]); if (null === $valid[1]) { return $object; } return $object->getMethod($valid[1]); } } /** * Return the hash. * * @return string */ public function __toString() { return $this->getHash(); } }