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: | $languages = _cfg('languages');
|
102: | if (count($languages) <= 1) {
|
103: | _cfg('translationEnabled', false);
|
104: | }
|
105: | }
|
106: |
|
107: | |
108: | |
109: | |
110: | |
111: | |
112: | |
113: |
|
114: | public static function request()
|
115: | {
|
116: | global $lc_baseURL;
|
117: | global $lc_languages;
|
118: | global $lc_lang;
|
119: | global $lc_langInURI;
|
120: |
|
121: | $lc_langInURI = _getLangInURI();
|
122: | if ($lc_langInURI === false) {
|
123: | $lc_lang = $lang = _cfg('defaultLang');
|
124: | } else {
|
125: | $lc_lang = $lang = $lc_langInURI;
|
126: | }
|
127: |
|
128: | if (isset($_GET[ROUTE]) && is_string($_GET[ROUTE])) {
|
129: |
|
130: | $path = $_GET[ROUTE];
|
131: | if (isset($_GET['lang']) && $_GET['lang']) {
|
132: | $lang = strip_tags(urldecode($_GET['lang']));
|
133: | $lang = rtrim($lang, '/');
|
134: | if (array_key_exists($lang, $lc_languages)) {
|
135: | $lc_lang = $lang;
|
136: | }
|
137: | }
|
138: | } elseif (isset($_SERVER['REQUEST_URI'])) {
|
139: |
|
140: |
|
141: | $requestPath = urldecode(strtok($_SERVER['REQUEST_URI'], '?'));
|
142: | $requestPath = str_replace($lc_baseURL, '', ltrim($requestPath, '/'));
|
143: | $requestPath = ltrim($requestPath, '/');
|
144: |
|
145: | if ($lang) {
|
146: | $lc_lang = $lang;
|
147: | $path = trim($requestPath, '/');
|
148: | if (strpos($path, $lc_lang) === 0) {
|
149: | $path = substr($path, strlen($lang));
|
150: | }
|
151: | } else {
|
152: | $path = trim($requestPath);
|
153: | }
|
154: |
|
155: |
|
156: |
|
157: |
|
158: |
|
159: | if ($path == basename($_SERVER['PHP_SELF'])) {
|
160: | $path = '';
|
161: | }
|
162: | } else {
|
163: |
|
164: | $path = '';
|
165: | }
|
166: |
|
167: |
|
168: |
|
169: |
|
170: | $path = trim($path, '/');
|
171: |
|
172: | if (!defined('WEB_ROOT')) {
|
173: | $baseUrl = _baseUrlWithProtocol();
|
174: | if ($baseUrl) {
|
175: |
|
176: | define('WEB_ROOT', $baseUrl . '/');
|
177: |
|
178: | define('WEB_APP_ROOT', WEB_ROOT . APP_DIR . '/');
|
179: |
|
180: | define('HOME', WEB_ROOT);
|
181: | }
|
182: | }
|
183: |
|
184: | session_set('lang', $lc_lang);
|
185: |
|
186: | return $path;
|
187: | }
|
188: |
|
189: | |
190: | |
191: | |
192: | |
193: | |
194: | |
195: | |
196: | |
197: | |
198: |
|
199: | public function add($name, $path, $to, $method = 'GET', $patterns = null)
|
200: | {
|
201: | $this->name = $name;
|
202: |
|
203: | $method = explode('|', strtoupper($method));
|
204: | $methods = array_filter($method, function ($value) {
|
205: | return in_array($value, array('GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'CONNECT', 'TRACE'));
|
206: | });
|
207: |
|
208: | if (count($methods) == 0) {
|
209: | $methods = array('GET');
|
210: | }
|
211: |
|
212: | $methods[] = 'OPTIONS';
|
213: | $methods = array_unique($methods);
|
214: |
|
215: | self::$routes[$this->name] = array(
|
216: | 'path' => $path,
|
217: | 'to' => $to,
|
218: | 'method' => $methods,
|
219: | 'patterns' => $patterns
|
220: | );
|
221: |
|
222: | return $this;
|
223: | }
|
224: |
|
225: | |
226: | |
227: | |
228: | |
229: | |
230: | |
231: | |
232: | |
233: |
|
234: | public function map($path, $to, $method = 'GET', $patterns = null)
|
235: | {
|
236: | return $this->add($this->name, $path, $to, $method, $patterns);
|
237: | }
|
238: |
|
239: | |
240: | |
241: | |
242: | |
243: |
|
244: | public static function match()
|
245: | {
|
246: | if (PHP_SAPI === 'cli' && _cfg('env') != ENV_TEST) {
|
247: | return false;
|
248: | }
|
249: |
|
250: | $realPath = explode('/', route_path());
|
251: | $routes = self::$routes;
|
252: |
|
253: | if (!(is_array($routes) && count($routes))) {
|
254: | return false;
|
255: | }
|
256: |
|
257: | $matchedRoute = array_filter($routes, function ($array) use ($realPath) {
|
258: | $last = array_pop($realPath);
|
259: | $path = '/' . implode('/', $realPath);
|
260: | if ($array['path'] == $path && in_array($_SERVER['REQUEST_METHOD'], $array['method'])
|
261: | && file_exists(APP_ROOT . $array['to'] . _DS_ . $last . '.php')) {
|
262: | return true;
|
263: | }
|
264: |
|
265: | return false;
|
266: | });
|
267: |
|
268: | if (count($matchedRoute)) {
|
269: | return false;
|
270: | }
|
271: |
|
272: | $found = false;
|
273: | foreach ($routes as $key => $value) {
|
274: | $patternPath = explode('/', trim($value['path'], '/'));
|
275: | if (count($realPath) !== count($patternPath)) {
|
276: | continue;
|
277: | }
|
278: |
|
279: | $vars = array();
|
280: | $matchedPath = array();
|
281: | foreach ($patternPath as $i => $segment) {
|
282: | if ($segment === $realPath[$i]) {
|
283: | $matchedPath[$i] = $segment;
|
284: | } else {
|
285: | if (preg_match('/([a-z0-9\-_\.]*)?{([a-z0-9\_]+)}([a-z0-9\-_\.]*)?/i', $segment, $matches)) {
|
286: | $name = $matches[2];
|
287: | $var = $realPath[$i];
|
288: |
|
289: | if ($matches[1]) {
|
290: | $var = ltrim($var, $matches[1] . '{');
|
291: | }
|
292: |
|
293: | if ($matches[3]) {
|
294: | $var = rtrim($var, '}' . $matches[3]);
|
295: | }
|
296: |
|
297: | if (isset($value['patterns'][$name]) && $value['patterns'][$name]) {
|
298: | $regex = $value['patterns'][$name];
|
299: | if (!preg_match('/^' . $regex . '$/', $var)) {
|
300: | _header(400);
|
301: | throw new \InvalidArgumentException(sprintf('The URL does not satisfy the argument value "%s" for "%s".', $var, $regex));
|
302: | }
|
303: | }
|
304: |
|
305: | $vars[$name] = $var;
|
306: | $matchedPath[$i] = $realPath[$i];
|
307: |
|
308: | continue;
|
309: | }
|
310: | break;
|
311: | }
|
312: | }
|
313: |
|
314: | if (route_path() === implode('/', $matchedPath)) {
|
315: |
|
316: | $matchedRoute = array_filter($routes, function ($array) use ($value) {
|
317: | return $array['path'] == $value['path'] && in_array($_SERVER['REQUEST_METHOD'], $array['method']);
|
318: | });
|
319: |
|
320: | if (count($matchedRoute)) {
|
321: | $key = array_keys($matchedRoute)[0];
|
322: | $value = $matchedRoute[$key];
|
323: | $found = true;
|
324: | break;
|
325: | } else {
|
326: | if (!in_array($_SERVER['REQUEST_METHOD'], $value['method'])) {
|
327: | _header(405);
|
328: | throw new \RuntimeException(sprintf('The URL does not allow the method "%s" for "%s".', $_SERVER['REQUEST_METHOD'], $key));
|
329: | }
|
330: | }
|
331: | }
|
332: | }
|
333: |
|
334: | if ($found) {
|
335: | self::$matchedRouteName = $key;
|
336: | $toRoute = trim($value['to'], '/');
|
337: | $_GET[ROUTE] = $toRoute;
|
338: | $_GET = array_merge($_GET, $vars);
|
339: | return $toRoute;
|
340: | }
|
341: |
|
342: | return false;
|
343: | }
|
344: |
|
345: | |
346: | |
347: | |
348: | |
349: | |
350: |
|
351: | public static function getPathByName($name)
|
352: | {
|
353: | return isset(self::$routes[$name]) ? trim(self::$routes[$name]['path'], '/') : null;
|
354: | }
|
355: |
|
356: | |
357: | |
358: | |
359: | |
360: |
|
361: | public static function clean()
|
362: | {
|
363: | self::$routes = array();
|
364: | }
|
365: |
|
366: | |
367: | |
368: | |
369: | |
370: | |
371: |
|
372: | public static function group($prefix, $callback)
|
373: | {
|
374: | $before = self::$routes;
|
375: |
|
376: | $callback();
|
377: |
|
378: | $groupRoutes = array_splice(self::$routes, count($before));
|
379: | foreach ($groupRoutes as $name => $route) {
|
380: | $route['path'] = '/' . ltrim($prefix, '/') . '/' . trim($route['path'], '/');
|
381: | $groupRoutes[$name] = $route;
|
382: | }
|
383: |
|
384: | self::$routes += $groupRoutes;
|
385: | }
|
386: |
|
387: | |
388: | |
389: | |
390: | |
391: | |
392: |
|
393: | public static function getAbsolutePathToRoot($q)
|
394: | {
|
395: |
|
396: | $_page = ROOT . $q;
|
397: |
|
398: | if (!(is_file($_page) && file_exists($_page))) {
|
399: |
|
400: | $_page = APP_ROOT . $q;
|
401: |
|
402: | $_seg = explode('/', $q);
|
403: | if (is_dir($_page)) {
|
404: | _cfg('cleanRoute', $q);
|
405: | } else {
|
406: | array_pop($_seg);
|
407: | _cfg('cleanRoute', implode('/', $_seg));
|
408: | }
|
409: | }
|
410: |
|
411: |
|
412: | if (is_dir($_page)) {
|
413: | foreach (array('index', 'view') as $pg) {
|
414: | $page = $_page . '/' . $pg . '.php';
|
415: | if (is_file($page) && file_exists($page)) {
|
416: | $_page = $page;
|
417: | break;
|
418: | }
|
419: | }
|
420: | } else {
|
421: | $pathInfo = pathinfo($_page);
|
422: | if (!isset($pathInfo['extension'])) {
|
423: | $_page .= '.php';
|
424: | }
|
425: | }
|
426: |
|
427: | return $_page;
|
428: | }
|
429: | }
|
430: | |