3v4l.org

run code in 200+ php & hhvm versions
Bugs & Features
<?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->getLogger(); $path = $context->getRequestPath(); $last = ''; $parent = null; $current = $root; $controllerPrefix = $context->getControllerPrefix(); if ($context->getAppMode() === 'DEBUG' && $log !== null) { $log->addDebug('Starting Object Dispatch', [ 'request' => $context->getRequestURI(), 'path' => var_export($path, true), 'root' => var_export($root, true) ]); } foreach ($this->routeIterator($path) as $chunk) { if ($context->getAppMode() === '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($controllerPrefix . $current)) { if ($context->getAppMode() === 'DEBUG' && $log !== null) { $log->addDebug('Instantiating current class', [ 'request' => $context->getRequestURI(), 'current' => $current ]); } $resolvedClass = $controllerPrefix . $current; $current = new $resolvedClass($context); } if (is_object($current)) { $parent = $current; } if (in_array($chunk, get_class_methods($parent))) { if ($context->getAppMode() === '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->getAppMode() === '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->getAppMode() === '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($controllerPrefix . $current)) { $resolvedClass = $controllerPrefix . $current; $current = new $resolvedClass($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) { $this->requestURI = isset($config['requestURI']) ? $config['requestURI'] : '/'; $this->appMode = isset($config['appMode']) ? $config['appMode'] : 'DEVELOPMENT'; $this->logger = isset($config['logger']) ? $config['logger'] : null; $this->controllerPrefix = isset($config['controllerPrefix']) ? $config['controllerPrefix'] : __NAMESPACE__ . '\\'; $this->requestPath = explode('/', str_replace('\\', '/', $this->requestURI)); if (end($this->requestPath) === '') { array_pop($this->requestPath); } $this->requestPath = array_reverse($this->requestPath); if (end($this->requestPath) === '') { array_pop($this->requestPath); } } public function getRequestURI() { return $this->requestURI; } public function getRequestPath() { return $this->requestPath; } public function getAppMode() { return $this->appMode; } public function getLogger() { return $this->logger; } public function getControllerPrefix() { return $this->controllerPrefix; } } class ScreenLog { public function __call($methodName, $args) { echo var_export($args, true) . "\n"; } } class RootController { public $home = 'homeController'; public function __invoke($args = array()) { return "Root controller index"; } } $context = new Context([ 'requestURI' => '/', 'logger' => new ScreenLog() ]); $dispatch = new ObjectDispatch(); foreach ($dispatch($context, 'RootController') as $signal) { list($object, $chunk, $path, $isEndpoint) = $signal; if ($isEndpoint) { echo $object->$chunk($path); } }
based on 2Yu0W

Here you find the average performance (time & memory) of each version. A grayed out version indicates it didn't complete successfully (based on exit-code).
Note: hhvm uses a JIT which only kicks in after a few runs. Since we run scripts only once, hhvm always runs slower.

VersionSystem time (s)User time (s)Memory (MiB)
7.2.60.0070.00716.96
7.2.00.0090.00619.61
7.1.200.0040.00415.89
7.1.100.0060.01017.85
7.1.70.0030.01416.97
7.1.60.0130.01319.40
7.1.50.0030.01016.96
7.1.00.0030.07722.27
7.0.200.0590.00614.93
7.0.100.0000.07320.11
7.0.90.0100.08720.10
7.0.80.0130.06020.03
7.0.70.0000.05720.08
7.0.60.0070.07720.02
7.0.50.0230.08320.34
7.0.40.0000.08320.13
7.0.30.0070.05720.06
7.0.20.0130.07720.05
7.0.10.0130.08020.07
7.0.00.0070.06320.08
5.6.280.0070.04021.21
5.6.250.0130.07020.83
5.6.240.0130.08020.85
5.6.230.0100.07720.70
5.6.220.0100.04320.64
5.6.210.0270.06320.73
5.6.200.0170.08021.17
5.6.190.0130.07021.11
5.6.180.0000.08321.10
5.6.170.0030.08321.11
5.6.160.0000.08021.11
5.6.150.0130.06721.11
5.6.140.0070.08320.97
5.6.130.0070.08321.07
5.6.120.0070.08321.09
5.6.110.0100.08321.14
5.6.100.0030.09021.07
5.6.90.0070.04321.16
5.6.80.0070.07720.52
5.6.70.0030.07020.55
5.6.60.0170.06320.51
5.6.50.0070.07020.55
5.6.40.0170.06320.37
5.6.30.0130.07020.52
5.6.20.0100.08020.38
5.6.10.0100.06720.46
5.6.00.0130.05720.48
5.5.380.0070.08320.48
5.5.370.0070.04320.52
5.5.360.0270.06320.43
5.5.350.0100.08020.49
5.5.340.0200.07320.84
5.5.330.0030.05720.89
5.5.320.0070.06321.01
5.5.310.0100.08020.96
5.5.300.0100.08320.84
5.5.290.0100.06720.79
5.5.280.0070.09320.94
5.5.270.0130.08320.85
5.5.260.0200.07320.83
5.5.250.0200.09320.50
5.5.240.0200.04020.38
5.5.230.0200.06720.38
5.5.220.0000.08020.33
5.5.210.0070.07720.18
5.5.200.0070.08020.25
5.5.190.0070.07320.22
5.5.180.0130.07320.18
5.5.160.0030.06320.32
5.5.150.0070.08320.04
5.5.140.0100.06720.34
5.5.130.0030.04720.15
5.5.120.0070.07020.05
5.5.110.0170.06020.21
5.5.100.0130.07720.02
5.5.90.0100.07719.98
5.5.80.0130.07020.15
5.5.70.0030.08320.11
5.5.60.0170.07020.11
5.5.50.0100.07720.10
5.5.40.0100.07320.18
5.5.30.0070.08020.11
5.5.20.0100.04320.19
5.5.10.0070.04720.10
5.5.00.0170.03720.14