1: | <?php
|
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: | |
11: | |
12: | |
13: | |
14: |
|
15: |
|
16: | namespace LucidFrame\Core;
|
17: |
|
18: | |
19: | |
20: |
|
21: | class Router
|
22: | {
|
23: |
|
24: | static protected $routes = array();
|
25: |
|
26: | static protected $matchedRouteName;
|
27: |
|
28: | protected $name;
|
29: |
|
30: | |
31: | |
32: | |
33: | |
34: |
|
35: | public function __construct($name)
|
36: | {
|
37: | $this->name = $name;
|
38: | }
|
39: |
|
40: | |
41: | |
42: |
|
43: | public static function getRoutes()
|
44: | {
|
45: | return self::$routes;
|
46: | }
|
47: |
|
48: | |
49: | |
50: |
|
51: | public static function getMatchedName()
|
52: | {
|
53: | return self::$matchedRouteName;
|
54: | }
|
55: |
|
56: | |
57: | |
58: |
|
59: | public function getName()
|
60: | {
|
61: | return $this->name;
|
62: | }
|
63: |
|
64: | |
65: | |
66: |
|
67: | public static function init()
|
68: | {
|
69: | if (!isset($_SERVER['HTTP_REFERER'])) {
|
70: | $_SERVER['HTTP_REFERER'] = '';
|
71: | }
|
72: |
|
73: | if (!isset($_SERVER['SERVER_PROTOCOL']) ||
|
74: | ($_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.0' && $_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.1')) {
|
75: | $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0';
|
76: | }
|
77: |
|
78: | if (isset($_SERVER['HTTP_HOST'])) {
|
79: |
|
80: |
|
81: |
|
82: | $_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']);
|
83: | if (!_validHost($_SERVER['HTTP_HOST'])) {
|
84: |
|
85: | header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
|
86: | exit;
|
87: | }
|
88: | } else {
|
89: |
|
90: |
|
91: | $_SERVER['HTTP_HOST'] = '';
|
92: | }
|
93: |
|
94: |
|
95: |
|
96: |
|
97: |
|
98: | $_GET[ROUTE] = Router::request();
|
99: | _cfg('cleanRoute', $_GET[ROUTE]);
|
100: | }
|
101: |
|
102: | |
103: | |
104: | |
105: | |
106: | |
107: | |
108: |
|
109: | public static function request()
|
110: | {
|
111: | global $lc_baseURL;
|
112: | global $lc_languages;
|
113: | global $lc_lang;
|
114: | global $lc_langInURI;
|
115: |
|
116: | $lc_langInURI = _getLangInURI();
|
117: | if ($lc_langInURI === false) {
|
118: | $lc_lang = $lang = _cfg('defaultLang');
|
119: | } else {
|
120: | $lc_lang = $lang = $lc_langInURI;
|
121: | }
|
122: |
|
123: | if (isset($_GET[ROUTE]) && is_string($_GET[ROUTE])) {
|
124: |
|
125: | $path = $_GET[ROUTE];
|
126: | if (isset($_GET['lang']) && $_GET['lang']) {
|
127: | $lang = strip_tags(urldecode($_GET['lang']));
|
128: | $lang = rtrim($lang, '/');
|
129: | if (array_key_exists($lang, $lc_languages)) {
|
130: | $lc_lang = $lang;
|
131: | }
|
132: | }
|
133: | } elseif (isset($_SERVER['REQUEST_URI'])) {
|
134: |
|
135: |
|
136: | $requestPath = urldecode(strtok($_SERVER['REQUEST_URI'], '?'));
|
137: | $requestPath = str_replace($lc_baseURL, '', ltrim($requestPath, '/'));
|
138: | $requestPath = ltrim($requestPath, '/');
|
139: |
|
140: | if ($lang) {
|
141: | $lc_lang = $lang;
|
142: | $path = trim($requestPath, '/');
|
143: | if (strpos($path, $lc_lang) === 0) {
|
144: | $path = substr($path, strlen($lang));
|
145: | }
|
146: | } else {
|
147: | $path = trim($requestPath);
|
148: | }
|
149: |
|
150: |
|
151: |
|
152: |
|
153: |
|
154: | if ($path == basename($_SERVER['PHP_SELF'])) {
|
155: | $path = '';
|
156: | }
|
157: | } else {
|
158: |
|
159: | $path = '';
|
160: | }
|
161: |
|
162: |
|
163: |
|
164: |
|
165: | $path = trim($path, '/');
|
166: |
|
167: | if (!defined('WEB_ROOT')) {
|
168: | $baseUrl = _baseUrlWithProtocol();
|
169: | if ($baseUrl) {
|
170: |
|
171: | define('WEB_ROOT', $baseUrl . '/');
|
172: |
|
173: | define('WEB_APP_ROOT', WEB_ROOT . APP_DIR . '/');
|
174: |
|
175: | define('HOME', WEB_ROOT);
|
176: | }
|
177: | }
|
178: |
|
179: | session_set('lang', $lc_lang);
|
180: |
|
181: | return $path;
|
182: | }
|
183: |
|
184: | |
185: | |
186: | |
187: | |
188: | |
189: | |
190: | |
191: | |
192: | |
193: |
|
194: | public function add($name, $path, $to, $method = 'GET', $patterns = null)
|
195: | {
|
196: | $this->name = $name;
|
197: |
|
198: | $method = explode('|', strtoupper($method));
|
199: | $methods = array_filter($method, function ($value) {
|
200: | return in_array($value, array('GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'CONNECT', 'TRACE'));
|
201: | });
|
202: |
|
203: | if (count($methods) == 0) {
|
204: | $methods = array('GET');
|
205: | }
|
206: |
|
207: | $methods[] = 'OPTIONS';
|
208: | $methods = array_unique($methods);
|
209: |
|
210: | self::$routes[$this->name] = array(
|
211: | 'path' => $path,
|
212: | 'to' => $to,
|
213: | 'method' => $methods,
|
214: | 'patterns' => $patterns
|
215: | );
|
216: |
|
217: | return $this;
|
218: | }
|
219: |
|
220: | |
221: | |
222: | |
223: | |
224: | |
225: | |
226: | |
227: | |
228: |
|
229: | public function map($path, $to, $method = 'GET', $patterns = null)
|
230: | {
|
231: | return $this->add($this->name, $path, $to, $method, $patterns);
|
232: | }
|
233: |
|
234: | |
235: | |
236: | |
237: | |
238: |
|
239: | public static function match()
|
240: | {
|
241: | if (PHP_SAPI === 'cli' && _cfg('env') != ENV_TEST) {
|
242: | return false;
|
243: | }
|
244: |
|
245: | $realPath = explode('/', route_path());
|
246: | $routes = self::$routes;
|
247: |
|
248: | if (!(is_array($routes) && count($routes))) {
|
249: | return false;
|
250: | }
|
251: |
|
252: | $matchedKey = null;
|
253: | $matchedRoute = array_filter($routes, function ($array, $key) use ($realPath, &$matchedKey) {
|
254: | if ($array['to'] instanceof \Closure) {
|
255: | $path = '/' . implode('/', $realPath);
|
256: | if ($array['path'] == $path && in_array($_SERVER['REQUEST_METHOD'], $array['method'])) {
|
257: | $matchedKey = $key;
|
258: | return true;
|
259: | }
|
260: | } else {
|
261: | $last = array_pop($realPath);
|
262: | $path = '/' . implode('/', $realPath);
|
263: | if ($array['path'] == $path && in_array($_SERVER['REQUEST_METHOD'], $array['method'])
|
264: | && file_exists(APP_ROOT . $array['to'] . _DS_ . $last . '.php')) {
|
265: | $matchedKey = $key;
|
266: | return true;
|
267: | }
|
268: | }
|
269: |
|
270: | return false;
|
271: | }, ARRAY_FILTER_USE_BOTH);
|
272: |
|
273: | if (count($matchedRoute)) {
|
274: | if (isset($matchedRoute[$matchedKey]) && $matchedRoute[$matchedKey]['to'] instanceof \Closure) {
|
275: | return $matchedRoute[$matchedKey]['to'];
|
276: | }
|
277: |
|
278: | return false;
|
279: | }
|
280: |
|
281: | $found = false;
|
282: | foreach ($routes as $key => $value) {
|
283: | $patternPath = explode('/', trim($value['path'], '/'));
|
284: | if (count($realPath) !== count($patternPath)) {
|
285: | continue;
|
286: | }
|
287: |
|
288: | $vars = array();
|
289: | $matchedPath = array();
|
290: | foreach ($patternPath as $i => $segment) {
|
291: | if ($segment === $realPath[$i]) {
|
292: | $matchedPath[$i] = $segment;
|
293: | } else {
|
294: | if (preg_match('/([a-z0-9\-_\.]*)?{([a-z0-9\_]+)}([a-z0-9\-_\.]*)?/i', $segment, $matches)) {
|
295: | $name = $matches[2];
|
296: | $var = $realPath[$i];
|
297: |
|
298: | if ($matches[1]) {
|
299: | $var = ltrim($var, $matches[1] . '{');
|
300: | }
|
301: |
|
302: | if ($matches[3]) {
|
303: | $var = rtrim($var, '}' . $matches[3]);
|
304: | }
|
305: |
|
306: | if (isset($value['patterns'][$name]) && $value['patterns'][$name]) {
|
307: | $regex = $value['patterns'][$name];
|
308: | if (!preg_match('/^' . $regex . '$/', $var)) {
|
309: | _header(400);
|
310: | throw new \InvalidArgumentException(sprintf('The URL does not satisfy the argument value "%s" for "%s".', $var, $regex));
|
311: | }
|
312: | }
|
313: |
|
314: | $vars[$name] = $var;
|
315: | $matchedPath[$i] = $realPath[$i];
|
316: |
|
317: | continue;
|
318: | }
|
319: | break;
|
320: | }
|
321: | }
|
322: |
|
323: | if (route_path() === implode('/', $matchedPath)) {
|
324: |
|
325: | $matchedRoute = array_filter($routes, function ($array) use ($value) {
|
326: | return $array['path'] == $value['path'] && in_array($_SERVER['REQUEST_METHOD'], $array['method']);
|
327: | });
|
328: |
|
329: | if (count($matchedRoute)) {
|
330: | $key = array_keys($matchedRoute)[0];
|
331: | $value = $matchedRoute[$key];
|
332: | $found = true;
|
333: | break;
|
334: | } else {
|
335: | if (!in_array($_SERVER['REQUEST_METHOD'], $value['method'])) {
|
336: | _header(405);
|
337: | throw new \RuntimeException(sprintf('The URL does not allow the method "%s" for "%s".', $_SERVER['REQUEST_METHOD'], $key));
|
338: | }
|
339: | }
|
340: | }
|
341: | }
|
342: |
|
343: | if ($found && !empty($key) && !empty($value)) {
|
344: | self::$matchedRouteName = $key;
|
345: |
|
346: | $toRoute = $value['to'];
|
347: | if (is_string($value['to'])) {
|
348: | $toRoute = trim($value['to'], '/');
|
349: | }
|
350: |
|
351: | $_GET[ROUTE] = $toRoute;
|
352: | if ($value['to'] instanceof \Closure) {
|
353: | $_GET[ROUTE . '_path'] = trim($value['path'], '/');
|
354: | }
|
355: |
|
356: | if (!empty($vars)) {
|
357: | $_GET = array_merge($_GET, $vars);
|
358: | }
|
359: |
|
360: | return $toRoute;
|
361: | }
|
362: |
|
363: | return false;
|
364: | }
|
365: |
|
366: | |
367: | |
368: | |
369: | |
370: | |
371: |
|
372: | public static function getPathByName($name)
|
373: | {
|
374: | return isset(self::$routes[$name]) ? trim(self::$routes[$name]['path'], '/') : null;
|
375: | }
|
376: |
|
377: | |
378: | |
379: | |
380: | |
381: |
|
382: | public static function clean()
|
383: | {
|
384: | self::$routes = array();
|
385: | }
|
386: |
|
387: | |
388: | |
389: | |
390: | |
391: | |
392: |
|
393: | public static function group($prefix, $callback)
|
394: | {
|
395: | $before = self::$routes;
|
396: |
|
397: | $callback();
|
398: |
|
399: | $groupRoutes = array_splice(self::$routes, count($before));
|
400: | foreach ($groupRoutes as $name => $route) {
|
401: | $route['path'] = '/' . ltrim($prefix, '/') . '/' . trim($route['path'], '/');
|
402: | $groupRoutes[$name] = $route;
|
403: | }
|
404: |
|
405: | self::$routes += $groupRoutes;
|
406: | }
|
407: |
|
408: | |
409: | |
410: | |
411: | |
412: | |
413: |
|
414: | public static function getAbsolutePathToRoot($q)
|
415: | {
|
416: |
|
417: | $_page = ROOT . $q;
|
418: |
|
419: | if (!(is_file($_page) && file_exists($_page))) {
|
420: |
|
421: | $_page = APP_ROOT . $q;
|
422: |
|
423: | $_seg = explode('/', $q);
|
424: | if (is_dir($_page)) {
|
425: | _cfg('cleanRoute', $q);
|
426: | } else {
|
427: | array_pop($_seg);
|
428: | _cfg('cleanRoute', implode('/', $_seg));
|
429: | }
|
430: | }
|
431: |
|
432: |
|
433: | if (is_dir($_page)) {
|
434: | foreach (array('index', 'view') as $pg) {
|
435: | $page = $_page . '/' . $pg . '.php';
|
436: | if (is_file($page) && file_exists($page)) {
|
437: | $_page = $page;
|
438: | break;
|
439: | }
|
440: | }
|
441: | } else {
|
442: | $pathInfo = pathinfo($_page);
|
443: | if (!isset($pathInfo['extension'])) {
|
444: | $_page .= '.php';
|
445: | }
|
446: | }
|
447: |
|
448: | return $_page;
|
449: | }
|
450: | }
|
451: | |