Repository navigation
Expand file tree
/
Copy pathdispatch.php
More file actions
124 lines (106 loc) · 3.41 KB
/
Copy pathdispatch.php
File metadata and controls
124 lines (106 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<?php
# @author noodlehaus
# @license MIT
# returns by ref the route stack singleton
function &context() {
static $context = [];
return $context;
}
# dispatch sapi request against routes context
function dispatch(...$args) {
$verb = strtoupper($_SERVER['REQUEST_METHOD']);
$path = '/'.trim(parse_url(/sitelet?url=https%3A%2F%2Fgithub.com%2Frobokoder%2Fdispatch%2Fblob%2Fmaster%2F%24_SERVER%5B%26%23039%3BREQUEST_URI%26%23039%3B%5D%2C%2520PHP_URL_PATH), '/');
# post method override
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;
}
}
$responder = serve(context(), $verb, $path, ...$args);
$responder();
}
# creates an action and puts it into the routes stack
function route($verb, $path, callable $func) {
$context = &context();
array_push($context, action($verb, $path, $func));
}
# creates a route handler
function action($verb, $path, callable $func) {
return function ($rverb, $rpath) use ($verb, $path, $func) {
$rexp = preg_replace('@:(\w+)@', '(?<\1>[^/]+)', $path);
if (
strtoupper($rverb) !== strtoupper($verb) ||
!preg_match("@^{$rexp}$@", $rpath, $caps)
) {
return [];
}
return [$func, array_slice($caps, 1)];
};
}
# performs a lookup against actions for verb + path
function match(array $actions, $verb, $path) {
$cverb = strtoupper(trim($verb));
$cpath = '/'.trim(rawurldecode(parse_url(/sitelet?url=https%3A%2F%2Fgithub.com%2Frobokoder%2Fdispatch%2Fblob%2Fmaster%2F%24path%2C%2520PHP_URL_PATH)), '/');
# test verb + path against route handlers
foreach ($actions as $test) {
$match = $test($cverb, $cpath);
if (!empty($match)) {
return $match;
}
}
return [];
}
# creates standard response
function response($body, $code = 200, array $headers = []) {
return function () use ($body, $code, $headers) {
render($body, $code, $headers);
};
}
# creates redirect response
function redirect($location, $code = 302) {
return function () use ($location, $code) {
render('', $code, ['location' => $location]);
};
}
# dispatches method + path against route stack
function serve(array $actions, $verb, $path, ...$args) {
$pair = match($actions, $verb, $path);
$func = array_shift($pair) ?: function () { return response('', 404, []); };
$caps = array_shift($pair) ?: null;
return empty($caps) ? $func(...$args) : $func($caps, ...$args);
}
# renders request response to the output buffer (ref: zend-diactoros)
function render($body, $code = 200, $headers = []) {
http_response_code($code);
array_walk($headers, function ($value, $key) {
if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $key)) {
throw new InvalidArgumentException("Invalid header name - {$key}");
}
$values = is_array($value) ? $value : [$value];
foreach ($values as $val) {
if (
preg_match("#(?:(?:(?<!\r)\n)|(?:\r(?!\n))|(?:\r\n(?![ \t])))#", $val) ||
preg_match('/[^\x09\x0a\x0d\x20-\x7E\x80-\xFE]/', $val)
) {
throw new InvalidArgumentException("Invalid header value - {$val}");
}
}
header($key.': '.implode(',', $values));
});
print $body;
}
# creates an page-rendering action
function page($path, array $vars = []) {
return function () use ($path, $vars) {
return response(phtml($path, $vars));
};
}
# renders and returns the content of a template
function phtml($path, array $vars = []) {
ob_start();
extract($vars, EXTR_SKIP);
require "{$path}.phtml";
return trim(ob_get_clean());
}