Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,11 @@ Respect\Rest uses a different approach to validate route parameters:
})->when(function($documentId) {
return is_numeric($documentId) && $documentId > 0;
});
// Routines can also be called using class and method names.
$r3->get('/documents/*', function($documentId) {
/** do something */
})->when('SomeClass_name', 'someMethod_name');
// You can also pass any instance that implements the __invoke() magic method to any routine.
```

1. This will match the route only if the callback on *when* is matched.
Expand Down
11 changes: 8 additions & 3 deletions library/Respect/Rest/Routines/AbstractRoutine.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
namespace Respect\Rest\Routines;

use InvalidArgumentException;
use ReflectionFunction;
use ReflectionMethod;
use Respect\Rest\Routes\AbstractRoute;

/** Base class for callback routines */
abstract class AbstractRoutine implements Routinable
Expand All @@ -15,9 +12,17 @@ abstract class AbstractRoutine implements Routinable

public function __construct($callback)
{
if (is_string($callback) && class_exists($callback) && method_exists($callback, '__invoke'))
return $this->callback = $callback;

if (!is_callable($callback))
throw new InvalidArgumentException('Routine callback must be... guess what... callable!');

$this->callback = $callback;
}

protected function getCallback()
{
return $this->callback;
}
}
56 changes: 49 additions & 7 deletions library/Respect/Rest/Routines/AbstractSyncedRoutine.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,68 @@
use InvalidArgumentException;
use ReflectionFunction;
use ReflectionMethod;
use Respect\Rest\Routes\AbstractRoute;
use ReflectionObject;
use ReflectionClass;
use Closure;
use Respect\Rest\Routes\AbstractRoute,
Respect\Rest\Request;

/** Base class for routines that sync parameters */
abstract class AbstractSyncedRoutine extends AbstractRoutine implements ParamSynced
{

/**
* @var Reflector
*/
protected $reflection;

/**
* Return parameters that can be used with the routine.
*
* @return array
*/
public function getParameters()
{
return $this->getReflection()->getParameters();
$reflection = $this->getReflection();
if (!$reflection instanceof ReflectionObject && !$reflection instanceof ReflectionClass)
return $this->getReflection()->getParameters();

return array();
}

/**
* Executes the routine and return its result.
*
* @param Respect\Rest\Request $request
* @param array $params
* @return mixed
*/
public function execute(Request $request, $params)
{
$callback = $this->getCallback();
if (is_string($callback)) {
$reflection = $this->getReflection();
$routineInstance = $reflection->newInstanceArgs($params);
return $routineInstance();
}
return call_user_func_array($callback, $params);
}

/** Returns a concrete ReflectionFunctionAbstract for this routine callback */
/**
* Returns a concrete ReflectionFunctionAbstract for this routine callback.
*
* @return Reflector
*/
protected function getReflection()
{
if (is_array($this->callback))
return new ReflectionMethod($this->callback[0], $this->callback[1]);
$callback = $this->getCallback();
if (is_array($callback))
return new ReflectionMethod($callback[0], $callback[1]);
else if ($callback instanceof Closure)
return new ReflectionFunction($callback);
else if (is_string($callback))
return new ReflectionClass($callback);
else
return new ReflectionFunction($this->callback);
return new ReflectionObject($callback);
}

}
2 changes: 1 addition & 1 deletion library/Respect/Rest/Routines/By.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class By extends AbstractSyncedRoutine implements ProxyableBy

public function by(Request $request, $params)
{
return call_user_func_array($this->callback, $params);
return $this->execute($request, $params);
}

}
2 changes: 1 addition & 1 deletion library/Respect/Rest/Routines/Through.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Through extends AbstractSyncedRoutine implements ProxyableThrough

public function through(Request $request, $params)
{
return call_user_func_array($this->callback, $params);
return $this->execute($request, $params);
}

}
Expand Down
2 changes: 1 addition & 1 deletion library/Respect/Rest/Routines/When.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class When extends AbstractSyncedRoutine implements ProxyableWhen

public function when(Request $request, $params)
{
$valid = call_user_func_array($this->callback, $params);
$valid = $this->execute($request, $params);

if (!$valid)
header('HTTP/1.1 400');
Expand Down
1 change: 1 addition & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
$paths[] = $path;
}

$paths[] = realpath(__DIR__.DIRECTORY_SEPARATOR.'src');
natsort($paths);
array_unshift($paths, dirname(__DIR__) .'/library');
set_include_path(implode(PATH_SEPARATOR, array_unique($paths)));
Expand Down
84 changes: 81 additions & 3 deletions tests/legacy/Respect/Rest/Routines/AbstractSyncedRoutineTest.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<?php
namespace Respect\Rest\Routines;

use Stubs\Routines\ByClassWithInvoke;

/**
* @covers Respect\Rest\Routines\ParamSynced
* @author Nick Lombard <github@jigsoft.co.za>
*/
use \ReflectionParameterr;

class AbstractSyncedRoutineTest extends \PHPUnit_Framework_TestCase
{
/**
Expand Down Expand Up @@ -37,7 +37,7 @@ protected function tearDown()
}

/**
* @covers Respect\Rest\Routines\AbstractSyncedRoutine::getParameters
* @covers Respect\Rest\Routines\AbstractSyncedRoutine
*/
public function testGetParameters()
{
Expand All @@ -48,4 +48,82 @@ public function testGetParameters()
$this->assertEquals('blogId', $parameters[1]->name);
$this->assertInstanceOf('ReflectionParameter', $parameters[0]);
}

/**
* @covers Respect\Rest\Routines\AbstractSyncedRoutine
* @covers Respect\Rest\Routines\AbstractRoutine
*/
public function test_getParameters_with_an_array()
{
$class = 'Respect\Rest\Routines\AbstractSyncedRoutine';
$callback = array('DateTime', 'createFromFormat');
$stub = $this->getMockBuilder($class)
->setMethods(array('getCallback'))
->disableOriginalConstructor()
->getMock();
$stub->expects($this->any())
->method('getCallback')
->will($this->returnValue($callback));

$this->assertContainsOnlyInstancesOf(
$expected = 'ReflectionParameter',
$result = $stub->getParameters()
);
$this->assertCount(
$expected = 3,
$result
);
}

/**
* @covers Respect\Rest\Routines\AbstractSyncedRoutine
* @covers Respect\Rest\Routines\AbstractRoutine
*/
public function test_getParameters_with_function()
{
$class = 'Respect\Rest\Routines\AbstractSyncedRoutine';
$callback = function($name) { return 'Hello '.$name; };
$stub = $this->getMockBuilder($class)
->setMethods(array('getCallback'))
->disableOriginalConstructor()
->getMock();
$stub->expects($this->any())
->method('getCallback')
->will($this->returnValue($callback));

$this->assertContainsOnlyInstancesOf(
$expected = 'ReflectionParameter',
$result = $stub->getParameters()
);
$this->assertCount(
$expected = 1,
$result
);
}

/**
* @covers Respect\Rest\Routines\AbstractSyncedRoutine
* @covers Respect\Rest\Routines\AbstractRoutine
*/
public function test_getParameters_with_callable_instance()
{
$stub = new ByClassWithInvoke;
$this->assertTrue(
is_callable($stub),
'Callable instance does not pass the is_callable test.'
);
$class = 'Respect\Rest\Routines\AbstractSyncedRoutine';
$callback = function($name) { return 'Hello '.$name; };
$routine = $this->getMockBuilder($class)
->setMethods(array('getCallback'))
->disableOriginalConstructor()
->getMock();
$routine->expects($this->any())
->method('getCallback')
->will($this->returnValue($stub));
$this->assertCount(
$expected = 0,
$result = $routine->getParameters()
);
}
}
75 changes: 63 additions & 12 deletions tests/legacy/Respect/Rest/Routines/ByTest.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
<?php
namespace Respect\Rest\Routines;

use Respect\Rest\Request;
use Respect\Rest\Request,
Respect\Rest\Router;
use Stubs\Routines\ByClassWithInvoke;

/**
* @covers Respect\Rest\Routines\By
* @author Nick Lombard <github@jigsoft.co.za>
*/
class ByTest extends \PHPUnit_Framework_TestCase
{
/**
* @var By
*/
protected $object;

/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
Expand All @@ -37,12 +34,66 @@ protected function tearDown()
/**
* @covers Respect\Rest\Routines\By::by
*/
public function testBy()
public function test_by_with_an_anonymous_function()
{
$request = new Request();
$params = array();
$routine = new By(function() { return 'from by callback'; });
$this->assertEquals('from by callback', $routine->by($request, $params));
}

/**
* @covers Respect\Rest\Routines\By
* @covers Respect\Rest\Routines\AbstractSyncedRoutine
*/
public function test_by_on_a_route()
{
$router = new Router();
$router->get('/', function() { return 'route'; })
->by(function() { return 'by'; });
// By does not affect the output of the route.
$this->assertEquals(
$expected = 'route',
(string) $router->dispatch('GET', '/')
);
}

/**
* @covers Respect\Rest\Routines\By
* @covers Respect\Rest\Routines\AbstractSyncedRoutine
*/
public function test_by_on_a_route_with_classname()
{
$router = new Router();
$router->get('/', function() { return 'route'; })
->by('Stubs\Routines\ByClassWithInvoke');
// By does not affect the output of the route.
$this->assertEquals(
$expected = 'route',
(string) $router->dispatch('GET', '/')
);
}

/**
* @covers Respect\Rest\Routines\By
* @covers Respect\Rest\Routines\AbstractSyncedRoutine
*/
public function test_by_with_a_callable_class_on_a_route()
{
$request = @new Request();
$params = array();
$alias = &$this->object;
$this->assertEquals('from by callback',
$alias->by($request, $params));
$router = new Router;
$routine = new ByClassWithInvoke;
$router->get('/', function() { return 'route'; })
->by($routine);
// By does not affect the output of the route.
$this->assertEquals(
$expected = 'route',
(string) $router->dispatch('GET', '/')
);
$this->assertAttributeEquals(
$expected = true,
'invoked',
$routine,
'Routine was not invoked!'
);
}
}
25 changes: 24 additions & 1 deletion tests/legacy/Respect/Rest/Routines/WhenTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<?php
namespace Respect\Rest\Routines {
use Respect\Rest\Request;

use Respect\Rest\Request,
Respect\Rest\Router;
use Stubs\Routines\WhenAlwaysTrue;

/**
* @covers Respect\Rest\Routines\When
* @author Nick Lombard <github@jigsoft.co.za>
Expand Down Expand Up @@ -53,6 +57,25 @@ public function testWhen()
$this->assertFalse($alias->when($request, $params));
$this->assertArrayHasKey('HTTP/1.1 400', $header);
}

public function test_when_with_a_callable_class_within_a_route()
{
$router = new Router;
$routine = new WhenAlwaysTrue;
$router->get('/', function() { return 'route'; })
->by($routine);
// By does not affect the output of the route.
$this->assertEquals(
$expected = 'route',
(string) $router->dispatch('GET', '/')
);
$this->assertAttributeEquals(
$expected = true,
'invoked',
$routine,
'Routine was not invoked!'
);
}
}

if (!function_exists(__NAMESPACE__.'\\header')) {
Expand Down
Loading