1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Toro (router) |
4
|
|
|
* |
5
|
|
|
* We needed to hack Toro and decided to copy it into the framework |
6
|
|
|
* instead of extending it. It seems to be abandoned so we made a good choice. |
7
|
|
|
* Taken from https://github.com/anandkunal/ToroPHP |
8
|
|
|
* @package erdiko/core |
9
|
|
|
*/ |
10
|
|
|
namespace erdiko\core; |
11
|
|
|
|
12
|
|
|
class Toro |
13
|
|
|
{ |
14
|
|
|
public static function serve($routes) |
|
|
|
|
15
|
|
|
{ |
16
|
|
|
ToroHook::fire('before_request', compact('routes')); |
17
|
|
|
|
18
|
|
|
// Determine action (and set default) |
19
|
|
|
if (empty($_SERVER['REQUEST_METHOD'])) { |
20
|
|
|
$_SERVER['REQUEST_METHOD'] = 'GET'; |
21
|
|
|
} |
22
|
|
|
$action = strtolower($_SERVER['REQUEST_METHOD']); // e.g. get, put, post, delete |
23
|
|
|
|
24
|
|
|
$path_info = '/'; |
25
|
|
|
if (!empty($_SERVER['PATH_INFO'])) { |
26
|
|
|
$path_info = $_SERVER['PATH_INFO']; |
27
|
|
|
} elseif (!empty($_SERVER['ORIG_PATH_INFO']) && $_SERVER['ORIG_PATH_INFO'] !== '/index.php') { |
28
|
|
|
$path_info = $_SERVER['ORIG_PATH_INFO']; |
29
|
|
|
} else { |
30
|
|
|
if (!empty($_SERVER['REQUEST_URI'])) { |
31
|
|
|
$path_info = (strpos($_SERVER['REQUEST_URI'], '?') > 0) ? |
32
|
|
|
strstr($_SERVER['REQUEST_URI'], '?', true) : $_SERVER['REQUEST_URI']; |
33
|
|
|
} |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
$discovered_handler = null; |
37
|
|
|
$arguments = array(); |
38
|
|
|
|
39
|
|
|
if (isset($routes[$path_info])) { |
40
|
|
|
$discovered_handler = $routes[$path_info]; |
41
|
|
|
} elseif ($routes) { |
42
|
|
|
$tokens = array( |
43
|
|
|
':string' => '([a-zA-Z]+)', |
44
|
|
|
':number' => '([0-9]+)', |
45
|
|
|
':alpha' => '([a-zA-Z0-9-_]+)', |
46
|
|
|
':action' => '([a-zA-Z0-9-_/]+)' |
47
|
|
|
); |
48
|
|
|
|
49
|
|
|
// Search through routes and find first match |
50
|
|
|
foreach ($routes as $pattern => $handler_name) { |
51
|
|
|
$patternRep = strtr($pattern, $tokens); |
52
|
|
|
if (preg_match('#^/?' . $patternRep . '/?$#', $path_info, $matches)) { |
53
|
|
|
$discovered_handler = $handler_name; |
54
|
|
|
$regex_matches = $matches; |
55
|
|
|
$params = isset($regex_matches[1]) ? explode("/", $regex_matches[1]) : array(); |
56
|
|
|
|
57
|
|
|
// See if it is an action |
58
|
|
|
$isAction = preg_match("/:action/", $pattern); |
59
|
|
|
|
60
|
|
|
// Determine action and arguments |
61
|
|
|
if (count($params) > 1 && !$isAction) { |
62
|
|
|
// @todo add different parsers here...possibly pass route function in routes.json |
63
|
|
|
$action .= ucfirst($params[0]); |
64
|
|
|
unset($params[0]); |
65
|
|
|
|
66
|
|
|
foreach ($params as $param) { |
67
|
|
|
$arguments[] = $param; |
68
|
|
|
} |
69
|
|
|
} else { |
70
|
|
|
unset($regex_matches[0]); |
71
|
|
|
$arguments = $regex_matches; // Toro compatible |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
break; |
75
|
|
|
} |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
$result = null; |
80
|
|
|
$handler_instance = null; |
81
|
|
|
|
82
|
|
|
if ($discovered_handler) { |
83
|
|
|
if (is_string($discovered_handler)) { |
84
|
|
|
$handler_instance = new $discovered_handler(); |
85
|
|
|
} elseif (is_callable($discovered_handler)) { |
86
|
|
|
$handler_instance = $discovered_handler(); |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
if ($handler_instance) { |
91
|
|
|
if (static::is_xhr_request() && method_exists($handler_instance, $action . '_xhr')) { |
|
|
|
|
92
|
|
|
header('Content-type: application/json'); // @todo support xml |
93
|
|
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); |
94
|
|
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); |
95
|
|
|
header('Cache-Control: no-store, no-cache, must-revalidate'); |
96
|
|
|
header('Cache-Control: post-check=0, pre-check=0', false); |
97
|
|
|
header("Access-Control-Allow-Origin: *"); // @todo make this a parameter? |
98
|
|
|
header('Pragma: no-cache'); |
99
|
|
|
$action .= '_xhr'; |
100
|
|
|
$handler_instance->setIsXhrRequest(1); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
if (is_callable(array($handler_instance, $action))) { |
104
|
|
|
try { |
105
|
|
|
$handler_instance->setPathInfo($path_info); |
106
|
|
|
ToroHook::fire('before_handler', compact('routes', 'discovered_handler', 'action', 'arguments')); |
107
|
|
|
$handler_instance->_before(); |
108
|
|
|
|
109
|
|
|
// Action call |
110
|
|
|
call_user_func_array(array($handler_instance, $action), $arguments); |
111
|
|
|
|
112
|
|
|
$handler_instance->_after(); |
113
|
|
|
$handler_instance->send(); // render the response and return the data |
114
|
|
|
ToroHook::fire( |
115
|
|
|
'after_handler', |
116
|
|
|
compact('routes', 'discovered_handler', 'action', 'arguments', 'result') |
117
|
|
|
); |
118
|
|
|
|
119
|
|
|
} catch (\Exception $e) { |
120
|
|
|
ToroHook::fire('500', array('error' => $e->getMessage(), 'path_info' => $path_info)); |
121
|
|
|
} |
122
|
|
|
} else { |
123
|
|
|
ToroHook::fire('404', compact('discovered_handler', 'action', 'arguments', 'path_info')); |
124
|
|
|
} |
125
|
|
|
} else { |
126
|
|
|
ToroHook::fire('404', compact('discovered_handler', 'action', 'arguments', 'path_info')); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
ToroHook::fire('after_request', compact('routes', 'discovered_handler', 'action', 'arguments', 'result')); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
private static function is_xhr_request() |
|
|
|
|
133
|
|
|
{ |
134
|
|
|
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'; |
135
|
|
|
} |
136
|
|
|
} |
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: