Skip to content

Instantly share code, notes, and snippets.

@noodlehaus
Last active August 29, 2015 14:12
Show Gist options
  • Save noodlehaus/3e7243dafd2e003b561c to your computer and use it in GitHub Desktop.
Save noodlehaus/3e7243dafd2e003b561c to your computer and use it in GitHub Desktop.
routing functions from dispatch
<?php
# @author Jesus A. Domingo
# @license MIT <http://noodlehaus.mit-license.org>
namespace noodlehaus\pico;
# returns the value for an http request header
function request_header($name) {
$headers = &$GLOBALS['app/state']['request_headers'];
# first call, prime it
if (!count($headers)) {
# if we're not in CLI, use it
if (function_exists('getallheaders')) {
$headers = array_change_key_case(getallheaders());
} else {
# manual header extraction (CLI + test)
$special = array('CONTENT_LENGTH', 'CONTENT_MD5', 'CONTENT_TYPE');
# get the rest of the headers out
foreach ($_SERVER as $name => $data) {
if (0 === strpos($name, 'HTTP_')) {
$name = strtolower(str_replace('_', '-', substr($name, 5)));
$headers[$name] = $data;
} else if (in_array($name, $special)) {
$name = strtolower(str_replace('_', '-', $name));
$headers[$name] = $data;
}
}
}
}
# normalize the input then try to fetch
$name = trim(strtolower($name));
return isset($headers[$name]) ? $headers[$name] : null;
}
# function for mapping actions to routes
function map() {
$args = func_get_args();
$data = &$GLOBALS['app/state']['routes'];
# try to figure out how we were called
switch (count($args)) {
# (method, path, handler)
case 3:
foreach ((array) $args[0] as $verb) {
$data['explicit'][strtoupper($verb)][] = array(
'/'.trim($args[1], '/'),
$args[2]
);
}
break;
# (path, handler)
case 2:
foreach ((array) $args[0] as $path) {
$data['any'][] = array('/'.trim($path, '/'), $args[1]);
}
break;
# any method and any path (just one for this, replace ref)
case 1:
$data['all'] = $args[0];
break;
# everything else
default:
throw new BadFunctionCallException(
'Invalid number of arguments.',
500
);
}
}
# hooks actions to route symbols
function hook($name, $func) {
$GLOBALS['app/state']['routes']['hooks'][$name] = $func;
}
# dispatches the current request against our handlers/actions
function run() {
$args = func_get_args();
$data = &$GLOBALS['app/state']['routes'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$path = trim($path, '/');
$path = trim($path, '/');
$verb = strtoupper($_SERVER['REQUEST_METHOD']);
# for POST requests, check for method override header or _method
if ($verb == 'POST') {
if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
$verb = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
} else {
$verb = isset($_POST['_method']) ? strtoupper($_POST['_method']) : $verb;
}
}
# set any mapping as base, then append exp_mapping if any
$maps = $data['any'];
if (isset($data['explicit'][$verb])) {
$maps = array_merge($data['explicit'][$verb], $maps);
}
$rexp = null;
$func = null;
$vals = null;
# try to see if we have any matching route
foreach ($maps as $temp) {
list($rexp, $call) = $temp;
$rexp = trim($rexp, '/');
$rexp = preg_replace('@\{(\w+)\}@', '(?<\1>[^/]+)', $rexp);
if (!preg_match('@^'.$rexp.'$@', $path, $vals)) {
continue;
}
$func = $call;
break;
}
# valid handler, try to parse out route symbol values
if ($func && is_callable($func)) {
# remove top group from vals
array_shift($vals);
# extract route symbols and run the hook()s
if ($vals) {
# extract any route symbol values
$toks = array_filter(array_keys($vals), 'is_string');
$vals = array_map('urldecode', array_intersect_key(
$vals,
array_flip($toks)
));
# if we have symbol hooks, run them
if (count($data['hooks'])) {
foreach ($vals as $key => $val) {
if (!isset($data['hooks'][$key])) {
continue;
}
$hook = $data['hooks'][$key];
$vals[$key] = call_user_func_array(
$hook,
array_merge((array) $val, $args)
);
}
}
# insert symbol values before dispatch() args
array_unshift($args, $vals);
}
} else {
# no matching handler, try to use 'all', or 404
if (is_callable($data['all'])) {
$func = $data['all'];
} else {
$func = function () {
\header("{$_SERVER['SERVER_PROTOCOL']} 404 Not found", true, 404);
echo "Not found\n";
exit;
};
}
}
return call_user_func_array($func, $args);
}
# state (routes, handlers, etc)
$GLOBALS['app/state'] = array(
'stash' => array(),
'request_headers' => array(),
'routes' => array(
'all' => null,
'any' => array(),
'hooks' => array(),
'explicit' => array(),
'errors' => array()
)
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment