Skip to content

Instantly share code, notes, and snippets.

@hakre

hakre/SCL.php Secret

Created June 30, 2012 18:14
Show Gist options
  • Save hakre/c31c9e3f27b4e51b6d88 to your computer and use it in GitHub Desktop.
Save hakre/c31c9e3f27b4e51b6d88 to your computer and use it in GitHub Desktop.
The Standard Callback Library**
<?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);
}
}
<?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