Skip to content

Commit

Permalink
Layered Handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
Owen Winkler committed Oct 22, 2014
1 parent 77347ed commit 973986c
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 60 deletions.
32 changes: 25 additions & 7 deletions index.php
Expand Up @@ -76,8 +76,8 @@
*/
$app
->route(
'user',
'/user/:user',
'author',
'/author/:user',
function(Request $request, Response $response) {
$response['output'] = '<pre>' . print_r($request['user'], 1) . '</pre>';
return $response->render('debug.php');
Expand Down Expand Up @@ -378,12 +378,20 @@ public function add_response(Response $response) {
return $response->render('template.tpl');
});

class UserController extends \Microsite\Controller {
class UserHandler extends \Microsite\Handler {

public function load(App $app) {
parent::load($app);
$app->route('do_routed', '/routed/:number', [$this, 'do_routed'])
->validate_fields([':number' => '[0-9]+']);
}

/**
* @url /any
*/
public function do_any() {
public function do_any(App $app) {
echo 'do any';
var_dump($app);
}

/**
Expand All @@ -401,14 +409,24 @@ public function do_get() {
public function do_post() {
echo 'do post';
}

/**
* @param Request $request
* @url /value/:value
*/
public function do_value(Request $request) {
echo $request['value'];
}

public function do_routed(Request $request) {
echo 'routed. The number was: ' . $request['number'];
}
}

$app->route('user', '/user', \Microsite\Controller::mount('UserController'));
$app->route('user', '/user', \Microsite\Handler::mount('UserHandler'));


/**
* Run the app to match and dispatch routes
*/
$app();

?>
21 changes: 15 additions & 6 deletions src/lib/Microsite/App.php
Expand Up @@ -7,7 +7,7 @@
* @package Microsite
* @method array template_dirs() Return an array of potential template directories
* @method Renderers\PHPRenderer renderer() Obtain the default/active renderer for the app
* @method void header() Output a header
* @method void header() Output an HTTP header
*/
class App
{
Expand Down Expand Up @@ -191,7 +191,7 @@ public function run() {
/**
* Allow this object to be executed directly
* Example: $app = new App(); $app();
* @param \Microsite\App|null $parent A parent App instance with relevant properties
* @param App|null $app A parent App instance with relevant properties
* @return bool|string Upon successful execution, the string of output produced, otherwise false
*/
public function __invoke($app = null) {
Expand Down Expand Up @@ -272,21 +272,31 @@ protected function dispatch_object($name, $args) {
*/
public function exec_params($handler) {
$result = false;
// Unwind DIObjects
while($handler instanceof DIObject) {
$handler = $handler($this);
}
if(is_string($handler) && method_exists($this, $handler)) {
$rf = new \ReflectionMethod($this, $handler);
$exec_params = $this->params_from_reflection($rf);
$result = call_user_func_array([$this, $handler], $exec_params);
}
elseif(is_string($handler) && class_exists($handler, true)) {
$handler::get_instance($this);
$result = call_user_func_array($this, [$this]);
elseif($handler instanceof Handler) {
$newapp = new App();
$handler->load($newapp);
$result = $newapp($this);
}
elseif(is_callable($handler)) {
// Do some magic...
$exec_params = [];
if($handler instanceof App) {
$exec_params[] = $this;
}
elseif(is_array($handler)) {
list($object, $method) = $handler;
$rm = new \ReflectionMethod($object, $method);
$exec_params = $this->params_from_reflection($rm);
}
else {
$rf = new \ReflectionFunction($handler);
$exec_params = $this->params_from_reflection($rf);
Expand Down Expand Up @@ -337,5 +347,4 @@ protected function params_from_reflection(\ReflectionFunctionAbstract $rf) {
public function __call($name, $args) {
return $this->dispatch_object($name, $args);
}

}
38 changes: 0 additions & 38 deletions src/lib/Microsite/Controller.php

This file was deleted.

2 changes: 2 additions & 0 deletions src/lib/Microsite/DIObject.php
Expand Up @@ -11,6 +11,8 @@ class DIObject
protected $callback;
protected $shared;
protected $result = null;
/** @var null|Callable $handler */
protected $handler = null;

/**
* Create a new DIObject instance
Expand Down
49 changes: 47 additions & 2 deletions src/lib/Microsite/Handler.php
Expand Up @@ -2,17 +2,62 @@

namespace Microsite;

class Handler
/**
* Class Handler
* @package Microsite
*/
class Handler extends Singleton
{
protected static $instances = array();

/**
* Add routes to an App from this class by inferring details from PHPDoc @url and @method properties
* @param App $app
*/
public function load(App $app) {
$class = get_called_class();
$methods = get_class_methods($class);
foreach($methods as $class_method) {
$method_props = [];
$rm = new \ReflectionMethod($this, $class_method);
$phpdoc = preg_split('/\r\n|\n|\r/', preg_replace('%^\s*/?\*+((?<=\*)/|[ \t]*)%m', '', $rm->getDocComment()));
foreach($phpdoc as $docline) {
if(preg_match('#^@(url|method)\s+(\S+)$#', $docline, $matches)) {
$method_props[$matches[1]] = $matches[2];
}
}
if(isset($method_props['url'])) {
$route = $app->route($class . '::' . $class_method, $method_props['url'], $rm->getClosure($this));
if(isset($method_props['method'])) {
$route->via($method_props['method']);
}
}
}
}


/**
* Mount a Handler class at a specific route, load it only when
* Return a DIObject that when invoked will return an instance of the named Controller class
* @param string $controller_classname The name of the Controller class to create
* @return DIObject An object that can be invoked to create the controller of the type specified
*/
public static function mount($controller_classname)
{
return new DIObject(function(App $app) use($controller_classname) {
/** @var Handler $controller */
$controller = new $controller_classname($app);
return $controller;
}, true);
}

/**
* Queue a method to be used to handle a request within a Route
* Note regarding two string parameters: It is annoying that these can't be a class name and method outside of a string,
* but referencing them as actual PHP language values instead of strings causes them to be loaded and not late-bound,
* which defeats the purpose of this class, which is to prepare grouped methods in a class that aren't loaded unless
* they're required for execution.
* @param string $method The Handler class that contains the handler method
* @param string $class The class that will handle this request
* @param string $method The method within the descendant class to call to handle this request
* @return callable A Closure used in a call to App::Route() that is used as a handler
*/
Expand Down
10 changes: 8 additions & 2 deletions src/lib/Microsite/Route.php
Expand Up @@ -7,7 +7,7 @@

class Route
{
private $url;
public $url;
private $handlers = array();
private $validators = array();
private $orig_url;
Expand All @@ -33,7 +33,7 @@ public function __construct($url) {
*/
public function add_handler($handler) {
$this->handlers[] = $handler;
if($handler instanceof App || $handler instanceof RouteMatcher) {
if(is_object($handler)) {
$this->url->fluid = true;
}
return $this;
Expand Down Expand Up @@ -84,6 +84,12 @@ public function build($vars = []) {
return $this->url->build($vars);
}

/**
* Convert a URL parameter into a real value
* @param string $var The name of the URL parameter
* @param Callable $fn($value, $fieldname) A function to call to convert this parameter into a value
* @return Route $this Fluent interface.
*/
public function convert($var, $fn) {
$this->url->convert($var, $fn);
return $this;
Expand Down
8 changes: 4 additions & 4 deletions src/lib/Microsite/Singleton.php
Expand Up @@ -4,7 +4,7 @@

class Singleton {

private static $instances = [];
private static $singleton_instances = [];


/**
Expand All @@ -14,12 +14,12 @@ class Singleton {
public static function get_instance()
{
$class = get_called_class();
if (!isset(self::$instances[$class])) {
if (!isset(self::$singleton_instances[$class])) {
$args = func_get_args();
$r_class = new \ReflectionClass($class);
self::$instances[$class] = $r_class->newInstanceArgs($args);
self::$singleton_instances[$class] = $r_class->newInstanceArgs($args);
}
return self::$instances[$class];
return self::$singleton_instances[$class];
}

/**
Expand Down
Binary file modified src/microsite.phar
Binary file not shown.
2 changes: 1 addition & 1 deletion views/home.php
Expand Up @@ -19,7 +19,7 @@
<li><a href="/hiya/user">Regex route</a></li>
<li><a href="/valid/ok">Use function validation on a route parameter (passes)</a></li>
<li><a href="/valid/bad">Use function validation on a route parameter (fails)</a></li>
<li><a href="/user/4">Convert a URL parameter into a useful object</a></li>
<li><a href="/author/4">Convert a URL parameter into a useful object</a></li>
<li><a href="/number/4">Validate only even arguments</a></li>
<li><a href="/number/3">Validate only odd arguments</a></li>
<li><a href="/admin/plugins">Layered plugins URL into admin app URL</a></li>
Expand Down

0 comments on commit 973986c

Please sign in to comment.