forked from noodlehaus/dispatch
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdispatch.php
More file actions
137 lines (116 loc) · 3.79 KB
/
dispatch.php
File metadata and controls
137 lines (116 loc) · 3.79 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
125
126
127
128
129
130
131
132
133
134
135
136
137
<?php
# @author noodlehaus
# @license MIT
# returns by ref the route stack singleton
function &context() {
static $context = ['actions' => [], 'midware' => []];
return $context;
}
# creates a middleware hook against a path
function hook($path, callable $func) {
$context = &context();
array_push($context['midware'], middleware($path, $func));
}
# creates a middleware to be used by hook()
function middleware($path, callable $func) {
return function ($rpath, ...$args) use ($path, $func) {
};
}
# dispatch sapi request against routes context
function dispatch(...$args) {
$verb = strtoupper($_SERVER['REQUEST_METHOD']);
$path = '/'.trim(parse_url($_SERVER['REQUEST_URI'], PHP_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()['actions'], $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['actions'], 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($path, PHP_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) && flush();
}
# 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());
}