Skip to content

Instantly share code, notes, and snippets.

@wyrfel
Last active February 5, 2017 11:28
Show Gist options
  • Save wyrfel/633c5f8f4225bd753d01 to your computer and use it in GitHub Desktop.
Save wyrfel/633c5f8f4225bd753d01 to your computer and use it in GitHub Desktop.
Callback Helper to extend capabilities of php built-ins that allow passing of custom callback functions
<?php
namespace Wyrfel\Helper;
use Exception;
/**
* Callback helper
*
* This allows passing parameters to callbacks for php built-ins that take a callback
* parameter without providing the option to specify additional arguments.
*
* The invocation arguments can be prepended (default) or appended to the
* instantiation arguments, they can be ignored altogether or
* override the instantiation arguments
*
* @package Wyrfel\Helper
*/
class Callback
{
const PREPEND_INVOCATION_ARGS = 1;
const APPEND_INVOCATION_ARGS = 2;
const IGNORE_INVOCATION_ARGS = 3;
const OVERRIDE_INSTANTIATION_ARGS = 4;
/**
* @var stdClass - the object
*/
protected $object;
/**
* @var string - the method name
*/
protected $method;
/**
* @var array - the arguments
*/
protected $arguments;
/**
* @var int - the argument merge behaviour
*/
protected $mode = self::PREPEND_INVOCATION_ARGS;
/**
* internally stores the object, method name and arguments given
*
* @param $object - the object
* @param string $method - the method name
*/
public function __construct($object, $method)
{
if (!is_object($object)) {
throw new Exception(sprintf('The first parameter to the Callable constructor must be an object, %s given', gettype($object)));
}
if (!is_string($method)) {
throw new Exception(sprintf('The second parameter to the Callable constructor must be a method name, %s given', gettype($method)));
}
if (!is_callable($object, $method)) {
throw new Exception(sprintf('%s::%s is not callable', get_class($object), $method));
}
$this->object = $object;
$this->method = $method;
$this->arguments = array_slice(func_get_args(), 2);
}
/**
* invokes the method on the object with the given arguments
*/
public function __invoke()
{
$arguments = $this->mergeArguments(func_get_args());
return call_user_func_array(array($this->object, $this->method), $arguments);
}
/**
* sets the invocation behaviour to append invocation arguments
* to the instantiation arguments
*
* @return $this
*/
public function appendInvocationArguments()
{
$this->mode = self::APPEND_INVOCATION_ARGS;
return $this;
}
/**
* sets the invocation behaviour to ignore invocation arguments alltogether
*
* @return $this
*/
public function ignoreInvocationArguments()
{
$this->mode = self::IGNORE_INVOCATION_ARGS;
return $this;
}
/**
* sets the invocation behaviour to prepend invocation arguments
* to the instantiation arguments
*
* @return $this
*/
public function prependInvocationArguments()
{
$this->mode = self::PREPEND_INVOCATION_ARGS;
return $this;
}
/**
* sets the invocation behaviour to override instantiation arguments
* with invocation arguments based on their position
*
* (the usefulness of this is questionable)
*
* @return $this
*/
public function overrideInstantiationArguments()
{
$this->mode = self::OVERRIDE_INSTANTIATION_ARGS;
return $this;
}
/**
* merges the invocation arguments with the instantiation arguments
* according to the set mode
*
* @param array $invocationArguments
*
* @return array
*/
protected function mergeArguments(array $invocationArguments)
{
if ($this->mode === self::IGNORE_INVOCATION_ARGS) {
return $this->arguments;
}
if ($this->mode === self::APPEND_INVOCATION_ARGS) {
return array_merge($this->arguments, $invocationArguments);
}
if ($this->mode === self::OVERRIDE_INSTANTIATION_ARGS) {
$tmp = $this->arguments;
foreach ($invocationArguments as $k => $v) {
$tmp[$k] = $v;
}
return $tmp;
}
return array_merge($invocationArguments, $this->arguments);
}
}
<?php
namespace Wyrfel\Helper;
use PHPUnit_Framework_TestCase;
/**
* tests the callback helper
*
* @package Wyrfel\Helper
*/
class CallbackTest extends PHPUnit_Framework_TestCase
{
const RETURN_VALUE = 'abcd';
/**
* tests the default behaviour
*/
public function testDefault()
{
$object = $this->getMock('Wyrfel\Helper\Callback', array('__invoke'), array(), '', false);
$object->expects($this->any())
->method('__invoke')
->with('a', 'b', 'c', 'd')
->will($this->returnValue(self::RETURN_VALUE));
$callback = new Callback($object, '__invoke', 'c', 'd');
$this->assertSame(self::RETURN_VALUE, $callback('a', 'b'));
}
/**
* tests the prependInvocationArguments behaviour
*/
public function testPrepend()
{
$object = $this->getMock('Shared\Helper\Callback', array('__invoke'), array(), '', false);
$object->expects($this->any())
->method('__invoke')
->with('a', 'b', 'c', 'd')
->will($this->returnValue(self::RETURN_VALUE));
$callback = new Callback($object, '__invoke', 'c', 'd');
$this->assertSame($callback, $callback->prependInvocationArguments());
$this->assertSame(self::RETURN_VALUE, $callback('a', 'b'));
}
/**
* tests the appendInvocationArguments
*/
public function testAppend()
{
$object = $this->getMock('Wyrfel\Helper\Callback', array('__invoke'), array(), '', false);
$object->expects($this->any())
->method('__invoke')
->with('c', 'd', 'a', 'b')
->will($this->returnValue(self::RETURN_VALUE));
$callback = new Callback($object, '__invoke', 'c', 'd');
$this->assertSame($callback, $callback->appendInvocationArguments());
$this->assertSame(self::RETURN_VALUE, $callback('a', 'b'));
}
/**
* tests the ignoreInvocationArguments behaviour
*/
public function testIgnore()
{
$object = $this->getMock('Wyrfel\Helper\Callback', array('__invoke'), array(), '', false);
$object->expects($this->any())
->method('__invoke')
->with('c', 'd')
->will($this->returnValue(self::RETURN_VALUE));
$callback = new Callback($object, '__invoke', 'c', 'd');
$this->assertSame($callback, $callback->appendInvocationArguments());
$this->assertSame(self::RETURN_VALUE, $callback('a', 'b'));
}
/**
* tests the overrideInvocationArguments behaviour
*/
public function testOverride()
{
$object = $this->getMock('Wyrfel\Helper\Callback', array('__invoke'), array(), '', false);
$object->expects($this->any())
->method('__invoke')
->with('a', 'd')
->will($this->returnValue(self::RETURN_VALUE));
$callback = new Callback($object, '__invoke', 'c', 'd');
$this->assertSame($callback, $callback->overrideInstantiationArguments());
$this->assertSame(self::RETURN_VALUE, $callback('a'));
}
/**
* tests that it works without invocation arguments
*/
public function testEmptyInvocationArgs()
{
$object = $this->getMock('Wyrfel\Helper\Callback', array('__invoke'), array(), '', false);
$object->expects($this->any())
->method('__invoke')
->with('c', 'd')
->will($this->returnValue(self::RETURN_VALUE));
$callback = new Callback($object, '__invoke', 'c', 'd');
$this->assertSame(self::RETURN_VALUE, $callback());
}
/**
* tests that it works without instantiation arguments
*/
public function testEmptyInstantiationArgs()
{
$object = $this->getMock('Wyrfel\Helper\Callback', array('__invoke'), array(), '', false);
$object->expects($this->any())
->method('__invoke')
->with('a', 'b')
->will($this->returnValue(self::RETURN_VALUE));
$callback = new Callback($object, '__invoke');
$this->assertSame(self::RETURN_VALUE, $callback('a', 'b'));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment