-
-
Save hakre/c31c9e3f27b4e51b6d88 to your computer and use it in GitHub Desktop.
The Standard Callback Library**
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class A | |
{ | |
function someMethod($a, $b, &$c) | |
{ | |
$c = 'Referenced: '. $c; | |
$result = sprintf("a - %s, b - %s", $a, $b); | |
$a = 'deleted'; | |
$b = 'deleted'; | |
return $result; | |
} | |
function __invoke($a, $b, &$c) { return $this->someMethod($a, $b, $c);} | |
} | |
$a = 2; | |
$c = '**'; | |
$argsOrdered = array('a' => $a, 'b' => 1, 2 => &$c); | |
$argsDisordered = array(2 => &$c, 'b' => 1, 'a' => $a); | |
$argsInvalid = array(1); | |
$test = 'trim'; | |
$test = array('A', 'someMethod'); # static method, array style | |
// $test = function() {}; | |
$obj = new A(); | |
$test = $obj; | |
// $test = array($obj, 'someMethod'); | |
$caller = new ReflectedCallback($test); | |
echo $caller->getCallableName(), "\n"; | |
echo $caller->invokeArgs($argsOrdered), "\n\$a: $a\n\$c: $c\n"; | |
return; | |
//$rm = new ReflectionMethod('A', 'someMethod'); | |
//$caller = new ReflectedCallback($rm->getClosure(new A())); | |
//echo $caller->invokeArgs($originalOrdered), "\n\$a: $a\n\$c: $c\n"; | |
/** | |
* Callback Decorator | |
* | |
* Providing reflection and invoking | |
* with named function arguments. | |
* | |
* @version 0.0.1 | |
* @author hakre <http:://hakre.wordpress.com/> | |
*/ | |
class ReflectedCallback | |
{ | |
private $callback; | |
private $class; | |
private $name; | |
public function __construct($callback) | |
{ | |
$this->setCallback($callback); | |
} | |
private function setCallback($callback) | |
{ | |
if (!is_callable($callback, TRUE, $callable)) | |
{ | |
throw new InvalidArgumentException('Invalid callback.'); | |
} | |
$this->callback = $callback; | |
list($this->name, $this->class) = array_reverse(explode('::', $callable, 2)) + array(1 => null); | |
} | |
public function getCallableName() | |
{ | |
$callable = $this->name; | |
if ($this->class) $callable = $this->class . '::' . $callable; | |
return $callable; | |
} | |
/** | |
* original callback | |
* | |
* @return callback | |
*/ | |
public function getCallback() | |
{ | |
return $this->callback; | |
} | |
/** | |
* @return object|null object of the callback function, NULL for static and global functions | |
*/ | |
public function getObject() | |
{ | |
$callback = $this->callback; | |
if (is_object($callback)) | |
return $callback; | |
if (is_array($callback) && is_object($callback[0])) | |
return $callback[0]; | |
} | |
/** | |
* @return ReflectionFunctionAbstract | |
*/ | |
public function getReflectionFunction() | |
{ | |
if ($object = $this->getObject()) | |
{ | |
return new ReflectionMethod($object, $this->name); | |
} | |
if ($this->class) | |
{ | |
return new ReflectionMethod($this->class, $this->name); | |
} | |
return new ReflectionFunction($this->name); | |
} | |
public function invokeArgs($args) | |
{ | |
$args = $this->arrangeByParameterNames($args, $this->getReflectionFunction()); | |
return call_user_func_array($this->callback, $args); | |
} | |
/** | |
* @return Array | |
*/ | |
private function arrangeByParameterNames(Array $args, ReflectionFunctionAbstract $function) | |
{ | |
if (!$args) return $args; | |
$parameters = $function->getParameters(); | |
foreach($parameters as $index => $param) | |
{ | |
if (isset($args[$param->name])) | |
{ | |
$parameters[$index] = &$args[$param->name]; | |
} | |
else if (isset($args[$index])) | |
{ | |
$parameters[$index] = &$args[$index]; | |
} | |
else | |
{ | |
try | |
{ | |
$parameters[$index] = $param->getDefaultValue(); | |
} catch(ReflectionException $e) { | |
throw new ReflectionException(sprintf("Parameter $%s (#%d) is not optional for callback %s", $param->name, $index+1, $this->getCallableName()), $e->getCode()); | |
} | |
} | |
} | |
return $parameters; | |
} | |
} | |
// $rm = new ReflectionParameters($rm); | |
class ReflectionParameters | |
{ | |
private $function; | |
public function __construct(ReflectionFunctionAbstract $function) | |
{ | |
$this->function = $function; | |
} | |
public function invokeArgs() | |
{ | |
$function = $this->function; | |
list($args, $object) = array_reverse(func_get_args()) + array(array(), NULL); | |
$args = $this->sortArgsByFunctionParameterName($function, $args); | |
var_dump($args); | |
if ($function instanceof ReflectionFunction) | |
{ | |
return $function->invokeArgs($args); | |
} | |
if($function instanceof ReflectionMethod) | |
{ | |
return $function->invokeArgs($object, $args); | |
} | |
throw new UnexpectedValueException('Unknown function-type %s.', get_class($function)); | |
} | |
/** | |
* @return Array | |
*/ | |
private function sortArgsByFunctionParameterName(Array $args, ReflectionFunctionAbstract $function) | |
{ | |
if (!$args) return $args; | |
$parameters = $function->getParameters(); | |
foreach($parameters as $index => $param) | |
{ | |
if (isset($args[$param->name])) | |
$parameters[$index] = &$args[$param->name]; | |
elseif (isset($args[$index])) | |
$parameters[$index] = &$args[$index]; | |
else | |
$parameters[$index] = $param->getDefaultValue(); | |
} | |
return $parameters; | |
} | |
} | |
echo $rm->invokeArgsOriginal(new A(), $originalOrdered), "\n"; | |
echo $rm->invokeArgsOriginal(new A(), ArgsSolver::byName($rm, $argsDisordered)), "\n"; | |
echo $rm->invokeArgs(new A(), $argsDisordered), "\n\$a: $a\n\$c: $c\n"; | |
class ArgsSolver | |
{ | |
private $args; | |
private $function; | |
public function __construct(array $args = array()) | |
{ | |
$this->setArgs($args); | |
} | |
public function setArgs(array $args = array()) | |
{ | |
$this->args = $args; | |
} | |
public static function byName(ReflectionFunctionAbstract $function, array $args = array()) | |
{ | |
$solver = new static($args); | |
return $solver->forFunction($function); | |
} | |
/** | |
* @return Array | |
*/ | |
public function forFunction(ReflectionFunctionAbstract $function, $consume = TRUE) | |
{ | |
$args = $this->args; | |
if ($consume) unset($this->args); | |
if (!$args) return $args; | |
$parameters = $function->getParameters(); | |
foreach($parameters as $index => $param) | |
{ | |
if (isset($args[$param->name])) | |
$parameters[$index] = &$args[$param->name]; | |
elseif (isset($args[$index])) | |
$parameters[$index] = &$args[$index]; | |
else | |
$parameters[$index] = $param->getDefaultValue(); | |
} | |
unset($args); | |
return $parameters; | |
} | |
} | |
class ReflectionMethodA extends ReflectionMethod | |
{ | |
public function invokeArgsOriginal($object, $args = array()) | |
{ | |
return parent::invokeArgs($object, $args); | |
} | |
public function invokeArgs($object, $args = array()) | |
{ | |
if (!$args) return parent::$invokeArgs($object, $args); | |
$parameters = $this->getParameters(); | |
foreach($parameters as $index => $param) | |
{ | |
if (isset($args[$param->name])) | |
$parameters[$index] = &$args[$param->name]; | |
elseif (isset($args[$index])) | |
$parameters[$index] = &$args[$index]; | |
else | |
$parameters[$index] = $param->getDefaultValue(); | |
} | |
unset($args); | |
var_dump($parameters); | |
return parent::invokeArgs($object, $parameters); | |
} | |
private function resolveArgsToNames(array &$args, array $names) | |
{ | |
$keys = array_keys($args); | |
$intKeys = array_filter($keys, 'is_int'); | |
var_dump($intKeys); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
Namespace SCL; | |
use \ReflectionClass; | |
/* | |
* The Standard Callback Library | |
* | |
* Standard Library for callbacks and events. | |
* | |
*/ | |
/** | |
* The InvokeableDEF Interface | |
* | |
* Interface used internally, implementing classes need to use an | |
* Interface that extends from it. | |
*/ | |
Interface InvokeableDEF {}; | |
/** | |
* Objects implementing the Invokeable interface can be invoked (called) | |
* and therefore can represent an invokation. | |
* | |
*/ | |
Interface Invokeable extends InvokeableDEF | |
{ | |
public function invoke(array $args = array()); | |
} | |
/** | |
* Interface to create an external invokation | |
* | |
*/ | |
Interface InvokeableAggregate extends InvokeableDEF | |
{ | |
/** | |
* @return Invokeable | |
*/ | |
public function getInvokeable(); | |
} | |
/** | |
* Interface to define an invokeable implementation | |
* of it's own. | |
* | |
*/ | |
Interface InvokeableImpl extends Invokeable | |
{ | |
/** | |
* Identifier of the Invokable | |
* in it's Implementation. | |
* | |
* Return an empty string if the Invokable is anonymous. | |
* | |
* @return string | |
*/ | |
public function getIdentification(); | |
} | |
/** | |
* Class representing an Invokation. | |
* | |
* Resolves calltime resultion of the invokable function. | |
* | |
*/ | |
class Invokation implements Invokeable, InvokeableAggregate | |
{ | |
private $invokable; | |
public function __construct($invokeable) | |
{ | |
$this->invokable = $invokeable; | |
} | |
public function isInvokable(InvokeableDEF $invokeable) | |
{ | |
if ($invokeable instanceof Invokeable) | |
return true; | |
if ($invokeable instanceof InvokeableAggregate) | |
{ | |
return NULL !== $this->getInvokeableOf($invokeable); | |
} | |
return FALSE; | |
} | |
/** | |
* get Invokable from an object | |
* | |
* @param mixed $object | |
* @return Invokable from object or NULL if there is none. | |
*/ | |
public function getInvokeableOf($object) | |
{ | |
if ($object instanceof $this) | |
return $object; | |
while(!$object instanceof Invokeable && $object instanceof InvokeableAggregate) | |
{ | |
$object = $object->getInvokeable(); | |
} | |
if ($object instanceof Invokeable) | |
{ | |
return $object; | |
} | |
return NULL; | |
} | |
public function invoke(array $args = array()) | |
{ | |
$this->getInvokeable()->invoke($args); | |
} | |
public function getInvokeable() | |
{ | |
return $this->getInvokeableOf($this->invokable); | |
} | |
} | |
/** | |
* An Invokeable Object for a standard PHP callback | |
* | |
*/ | |
class InvokableCallbackImpl implements InvokeableImpl | |
{ | |
private $callback; | |
public function __construct($callback) | |
{ | |
$this->callback = $callback; | |
} | |
public function invoke(array $args = array()) | |
{ | |
return call_user_func_array($this->callback, $args); | |
} | |
public function getIdentification() | |
{ | |
$r = is_callable($this->callback, TRUE, $callable_name); | |
return $callable_name; | |
} | |
} | |
/** | |
* An Invokable object constructor with optional default arguments. | |
* | |
*/ | |
class InvokableConstructorImpl implements Invokeable, InvokeableImpl | |
{ | |
private $class; | |
private $defaultArgs; | |
public function __construct($class, array $args = NULL) | |
{ | |
$this->injectClass((string) $class); | |
$this->defaultArgs = $args; | |
} | |
public function getIdentification() | |
{ | |
return $this->class; | |
} | |
public function invoke(array $args = array()) | |
{ | |
if (!func_num_args() && $this->defaultArgs) | |
$args = $this->defaultArgs; | |
return $this->newInstanceArgs($args); | |
} | |
protected function injectClass($class) | |
{ | |
$this->class = $class; | |
} | |
protected function newInstanceArgs(array $args) | |
{ | |
$refl = new ReflectionClass($this->class); | |
return $refl->newInstanceArgs($args); | |
} | |
} | |
/** | |
* An Invokable Factory constructor | |
* | |
*/ | |
class InvokeableFactoryImpl extends InvokableConstructorImpl | |
{ | |
private $class; | |
public function __construct($class, array $args = NULL) | |
{ | |
$this->class = $class; | |
parent::__construct($class, $args); | |
} | |
public function invoke(array $args = array()) | |
{ | |
$concreteClass = $this->concreteClass($this->class, $args); | |
if (NULL === $args) | |
{ | |
return parent::invoke(); | |
} | |
return parent::invoke($args); | |
} | |
function concreteClass($class, &$args) | |
{ | |
return (string) $class; | |
} | |
public function getIdentification() | |
{ | |
return $this->class; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment