Skip to content

Instantly share code, notes, and snippets.

@kevburnsjr
Last active December 12, 2015 02:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kevburnsjr/4702155 to your computer and use it in GitHub Desktop.
Save kevburnsjr/4702155 to your computer and use it in GitHub Desktop.
<?php
class Route {
public $name;
public $uri_templates = array();
public $params = array();
public function __construct($name, $uri_templates) {
$this->name = $name;
$this->uri_templates = $uri_templates;
}
public function match($uri_path_info, $request_params = array()) {
foreach($this->uri_templates as $uri_template) {
$regex = $uri_template;
$regex = preg_replace('/(\{\?[^\/]+\})/', '', $regex);
$regex = preg_replace('/(\{[^\/]+\})/', '([^/]+)', $regex);
$regex = str_replace('~', '\~', $regex);
$match = preg_match('~^'.$regex.'/?$~', $uri_path_info, $vals);
if($match) {
// Parse URI Template
preg_match_all('/\{([^?\}]*)\}/', $uri_template, $keys);
if(count($keys[1]) && count($vals) > 1){
$this->params = array_combine($keys[1], array_slice($vals,1));
}
if(count($request_params)) {
// Pull query params from URL
preg_match('/\{\?([^\}]*)\}/', $uri_template, $valid_keys);
if(count($valid_keys)) {
$valid_keys = explode(',', $valid_keys[1]);
foreach($request_params as $qk => $qv) {
if(in_array($qk, $valid_keys)) {
$this->params[$qk] = $qv;
}
}
}
}
break;
}
}
return $match;
}
public function getUri($params = null) {
$params = $params ?: $this->params;
$url = $this->uri_templates[0];
$url_params = array();
if(is_array($params)) {
foreach($params as $k => $v) {
$regex = '/(\{'.$k.'\})/';
if(preg_match($regex, $url)) {
$url = preg_replace($regex, $v, $url);
} else {
$url_params[$k] = $v;
}
}
}
$url = preg_replace('/(\{[^\/]+\})/', '', $url);
if(count($url_params)) {
$url .= strpos('?', $url) > 0 ? '' : '?';
$url .= http_build_query($url_params);
}
return $url;
}
}
# =============================================================================
# USAGE
# =============================================================================
$routes = array(
'home' => '/',
'session-create' => '/login',
'session-destroy' => '/logout',
'users' => '/users',
'user' => '/user/{id}',
'messages' => '/messages',
'messages-new' => '/messages/new',
'message' => '/message/{id}',
'message-remove' => '/message/{id}/remove',
'search' => '/search{?q}'
);
$uri_parts = explode('?', $_SERVER['REQUEST_URI']);
$path = $uri_parts[0];
$params = $_GET;
$matched_route = null;
foreach($routes as $name => $uri_template) {
$route = new Route($name, array($uri_template));
if($route->match($path, $params)) {
$matched_route = $route;
break;
}
}
if($matched_route) {
echo "Matched route: " . $matched_route->name . "<br/>";
echo "URL: " . $matched_route->getUri() . "<br/><br/>";
$msg_route = new Route('message', array($routes['message']));
echo "Message URL: " . $msg_route->getUri(array('id' => '123')) . "<br/><br/>";
$user_route = new Route('user', array($routes['user']));
echo "User URL: " . $user_route->getUri(array('id' => '456')) . "<br/>";
} else {
throw new Exception('404');
}
@kevburnsjr
Copy link
Author

In real life you'd probably have a router that manages these routes but you get the gist.

@kevburnsjr
Copy link
Author

@taras
Copy link

taras commented Feb 3, 2013

This is exactly what I was looking for. I used url generation and matching from Symfony, but it doesn't have the detail that your solution has. I'm going to merge this code into ScaleUp ( WordPress-ish framework for building web apps ) when I work on routes again.

@heruan
Copy link

heruan commented Apr 11, 2013

This is awesome, unfortunately it doesn't match all RFC 6570 templates (e.g. {+path}/here should match against /foo/bar/here and {/var:1,var} should match against /f/bar). Am I wrong?

@kevburnsjr
Copy link
Author

@heruan - You are correct. This is a partial implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment