3v4l.org

run code in 300+ PHP versions simultaneously
<?php namespace Magnus; class ObjectDispatch { public function routeIterator(&$path) { while (!empty($path)) { yield end($path); array_pop($path); /* This prevents having to put back a value in the event of a * readjustment in the dispatch path. * Testing indicates that it's better to do array maninpulation than it is * to implement SplDoublyLinkedList for deque behavior. Likewise, * simply tracking the index is a bit slower and can add complexity * when dealing with reorients/redispatches. */ } } public function __invoke($context, $root) { $log = $context->logger; $path = $context->getRequestPath(); $last = ''; $parent = null; $current = $root; if ($context->appMode === 'DEBUG' && $log !== null) { $log->addDebug('Starting Object Dispatch', [ 'request' => $context->getRequestURI(), 'path' => var_export($path, true), 'root' => var_export($root, true) ]); } foreach (routeIterator($path) as $chunk) { if ($context->appMode === 'DEBUG' && $log !== null) { $log->addDebug('Beginning dispatch step.', [ 'chunk' => $chunk, 'path' => var_export($path, true), 'current' => var_export($current, true) ]); } if (!is_object($current) || class_exists($context->controllerPrefix . $current)) { if ($context->appMode === 'DEBUG' && $log !== null) { $log->addDebug('Instantiating current class', [ 'request' => $context->getRequestURI(), 'current' => $current ]); } $current = $context->controllerPrefix . $current($context); } if (is_object($current)) { $parent = $current; } if (in_array($chunk, get_class_methods($parent))) { if ($context->appMode === 'DEBUG' && $log !== null) { $log->addDebug('Found an endpoint', [ 'request' => $context->getRequestURI(), 'isEndpoint' => true, 'parent' => var_export($parent, true), 'handler' => $chunk, 'arguments' => var_export($path, true) ]); } yield array($parent, $chunk, $path, true); } elseif (in_array($chunk, get_object_vars($parent))) { if ($context->appMode === 'DEBUG' && $log !== null) { $log->addDebug('Found a property', [ 'request' => $context->getRequestURI(), 'property' => $chunk, 'parent' => var_export($parent, true) ]); } $current = $parent->chunk; } elseif (method_exists($parent, 'lookup')) { try { list($current, $consumed) = $parent->lookup($path); $chunk = implode('/', $consumed); $path = array_slice($path, 0, count($path) - count($consumed)); } catch (Exception $e) { throw new HTTPNotFound(); } } else { throw new HTTPNotFound(); } yield array(explode('/', $last), $parent, false); $last = $chunk; } if ($context->appMode === 'DEBUG' && $log !== null) { $log->addDebug('No endpoint found', [ 'request' => $context->getRequestURI(), 'current' => var_export($current), 'parent' => var_export($parent) ]); } if (!is_object($current) && class_exists($context->controllerPrefix . $current)) { $current = new $context->controllerPrefix . $current($context); } if (is_callable($current)) { yield array($current, $chunk, $path, true); } elseif (is_callable($parent)) { yield array($parent, $chunk, $path, true); } } } class Context { protected $requestURI; protected $requestPath; protected $appMode; protected $logger; protected $controllerPrefix; public function __construct(Array $config) { foreach (get_object_vars($this) as $property) { $this->$property = isset($config[$property]) ? $config[$property] : null; } } public function getRequestURI() { return $this->requestURI; } public function getRequestPath() { if (!is_array($this->requestPath)) { $this->requestPath = explode('/', str_replace('\\', '/', $this->requestURI)); } return $this->requestPath; } } class RootController { public $home = 'homeController'; public function __invoke($args = array()) { return "Root controller index"; } } $context = new Context([ 'requestURI' => '/', 'appMode' => 'development' ]); $dispatch = new ObjectDispatch(); foreach ($dispatch($context, 'rootController') as $signal) { echo var_export($signal, true) . "\n"; }

preferences:
138.67 ms | 402 KiB | 5 Q