Created
October 10, 2014 00:43
-
-
Save jm42/f2a690fd2c55a4e6206b to your computer and use it in GitHub Desktop.
Traversal Router
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| // This was the first test to create what then became Traba. | |
| // | |
| // https://github.com/guide42/traba | |
| // | |
| $request = array( | |
| 'path_info' => '/user/123/edit', | |
| 'query' => array( | |
| 'name' => 'John Doe', | |
| 'age' => 42, | |
| ), | |
| ); | |
| // {{{ Fast an ugly traversal implementation | |
| function split_path_info($path) | |
| { | |
| $parts = array(); | |
| foreach (explode('/', $path) as $part) { | |
| if (empty($part) || $part === '.') { | |
| continue; | |
| } | |
| if ($part === '..') { | |
| if ($parts) { | |
| unset($parts[count($parts) - 1]); | |
| continue; | |
| } | |
| throw new RuntimeException(''); | |
| } | |
| $parts[] = $part; | |
| } | |
| return $parts; | |
| } | |
| function get_path_info($context) | |
| { | |
| if (isset($context['request']['path_info'])) { | |
| return $context['request']['path_info']; | |
| } | |
| return '/'; | |
| } | |
| function traverser($context, $root) | |
| { | |
| $path = get_path_info($context); | |
| if ($path === '/') { | |
| return array( | |
| 'context' => $root, | |
| 'name' => null, | |
| 'subpath' => array(), | |
| 'traversed' => array(), | |
| ); | |
| } | |
| $parts = split_path_info($path); | |
| foreach($parts as $i => $segment) { | |
| if (!(is_array($root) || $root instanceof \ArrayAccess) || !isset($root[$segment])) { | |
| return array( | |
| 'context' => $root, | |
| 'name' => $segment, | |
| 'subpath' => array_slice($parts, $i + 1), | |
| 'traversed' => array_slice($parts, 0, $i), | |
| ); | |
| } | |
| $root = $root[$segment]; | |
| } | |
| return array( | |
| 'context' => $root, | |
| 'name' => null, | |
| 'subpath' => array(), | |
| 'traversed' => $parts, | |
| ); | |
| } | |
| // }}} | |
| // {{{ Example of utilization of the traversal algorithm | |
| class User { | |
| public $name; | |
| public $age; | |
| public function __construct($name, $age) { | |
| $this->name = $name; | |
| $this->age = $age; | |
| } | |
| } | |
| class UserDispatcher implements \ArrayAccess | |
| { | |
| private $users = null; | |
| public function init() { | |
| if ($this->users === null) { | |
| $this->users = array( | |
| 123 => new User('Not John', 24), | |
| ); | |
| } | |
| } | |
| public function offsetExists($offset) { | |
| $this->init(); | |
| return isset($this->users[$offset]); | |
| } | |
| public function offsetGet($offset) { | |
| $this->init(); | |
| return $this->users[$offset]; | |
| } | |
| public function offsetSet($offset, $value) {} | |
| public function offsetUnset($offset) {} | |
| } | |
| $context = array('request' => $request); | |
| $root = array( | |
| 'user' => new UserDispatcher(), | |
| ); | |
| $matched = traverser($context, $root); | |
| // }}} | |
| // {{{ Implementation of the matcher | |
| function matcher(array $registry, array $matched) | |
| { | |
| $context = $matched['context']; | |
| $name = $matched['name'] ? $matched['name'] : 'index'; | |
| foreach ($registry as $view) { | |
| if ($view[0] === $name && $context instanceof $view[1]) { | |
| return $view[2]; | |
| } | |
| } | |
| return null; | |
| } | |
| // }}} | |
| // {{{ Use the matcher | |
| function user_edit($request, User $user) | |
| { | |
| $user->name = $request['query']['name']; | |
| $user->age = $request['query']['age']; | |
| return 'OK'; | |
| } | |
| function user_index($request, User $user) | |
| { | |
| return 'Name=' . $user->name . '; Age=' . $user->age; | |
| } | |
| function not_found($request, $context) | |
| { | |
| return '404'; | |
| } | |
| $registry = array( | |
| // | name | context type | callback | | |
| array( 'index', 'User' , 'user_index' ), | |
| array( 'edit' , 'User' , 'user_edit' ), | |
| ); | |
| $view = matcher($registry, $matched); | |
| if ($view === null) { | |
| $view = 'not_found'; | |
| } | |
| // }}} | |
| // {{{ | |
| $response = call_user_func($view, $request, $matched['context']); | |
| echo $response . PHP_EOL; | |
| // }}} | |
| // Hope nobody will kill me for so ugly code |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment