Nico Huber | ee52fbc | 2023-06-24 11:52:57 +0000 | [diff] [blame^] | 1 | <?php |
| 2 | /* |
| 3 | * SimpleWeb |
| 4 | * |
| 5 | * Copyright (C) Kelvin Mo 2009 |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU General Public |
| 9 | * License as published by the Free Software Foundation; either |
| 10 | * version 2 of the License, or (at your option) any later version. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public |
| 18 | * License along with this program; if not, write to the Free |
| 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 20 | * |
| 21 | * $Id$ |
| 22 | */ |
| 23 | |
| 24 | /** |
| 25 | * Simpleweb is a minimalist web framework. It is similar to {@link http://webpy.org web.py}, |
| 26 | * but in PHP. |
| 27 | * |
| 28 | * The key to Simpleweb is the <i>route array</i>. The route array is an array that maps |
| 29 | * URLs (called <i>patterns</i>) to PHP functions or methods (called <i>routes</i>). |
| 30 | * |
| 31 | * Patterns are regular expressions, which are tested against the URL one at a time. |
| 32 | * Subpatterns (i.e. patterns within parentheses) are then passed on as arguments |
| 33 | * to the route. |
| 34 | * |
| 35 | * Routes are either functions, static methods or object methods. A function is |
| 36 | * denoted by the function name. A static method is denoted by the class name, |
| 37 | * followed by :: then the method name. An object method is denoted by the class |
| 38 | * name, followed by -> then the method name. An instance of the class will be |
| 39 | * created before an object method is called. |
| 40 | * |
| 41 | * An example of a routes array is given below: |
| 42 | * |
| 43 | * <code> |
| 44 | * <?php |
| 45 | * $routes = array( |
| 46 | * 'a' => 'function1', |
| 47 | * 'b/(.+)' => 'function2', // arguments |
| 48 | * 'c' => 'ClassA::method', // static method |
| 49 | * 'd' => 'ClassB->method', // object method |
| 50 | * ); |
| 51 | * ?> |
| 52 | * </code> |
| 53 | * |
| 54 | * Once the route array is populated, the {@link simpleweb_run()} function |
| 55 | * is then called to handle the URL. |
| 56 | * |
| 57 | * @package simpleweb |
| 58 | * @since 0.7 |
| 59 | */ |
| 60 | |
| 61 | /** |
| 62 | * Handles a supplied request, based on a set of routes. |
| 63 | * |
| 64 | * @param array $routes the routes array, as described in {@link simpleweb.inc this page} |
| 65 | * @param string $request_path the request path against which the routes are applied. If |
| 66 | * NULL, then the request URI supplied by the web server will be used. |
| 67 | * @param string $not_found_route the default route if none of the patterns match. If |
| 68 | * NULL, then an HTTP 404 error is raised |
| 69 | * @return mixed the result from calling the route. |
| 70 | * |
| 71 | */ |
| 72 | |
| 73 | function simpleweb_run($routes, $request_path = NULL, $not_found_route = NULL) { |
| 74 | if ($request_path == NULL) { |
| 75 | // We take the request path from the request URI |
| 76 | $request_path = $_SERVER['REQUEST_URI']; |
| 77 | |
| 78 | // Strip off all parts to the script file name. Sadly, PHP is historically |
| 79 | // buggy in its treatment of SCRIPT_NAME, so we need to try a few methods |
| 80 | // to strip them |
| 81 | $script_name = basename($_SERVER['SCRIPT_NAME']); |
| 82 | $script_dir = dirname($_SERVER['SCRIPT_NAME']); |
| 83 | |
| 84 | if (strpos($request_path, $script_name) !== false) { |
| 85 | $request_path = substr($request_path, strpos($request_path, $script_name) + strlen($script_name)); |
| 86 | } elseif ($script_dir != '/') { |
| 87 | $request_path = str_replace($script_dir, '', $request_path); |
| 88 | } |
| 89 | |
| 90 | $request_path = trim($request_path, '/'); |
| 91 | } |
| 92 | |
| 93 | // Strip off GET parameters when passed in SAPI CGI mode |
| 94 | $request_path = strtok($request_path, '?'); |
| 95 | |
| 96 | foreach ($routes as $pattern => $route) { |
| 97 | |
| 98 | if (!isset($route)) continue; |
| 99 | $regex = '#^' . trim($pattern, '/') . '$#i'; |
| 100 | |
| 101 | if (!preg_match($regex, $request_path, $args) > 0) continue; |
| 102 | |
| 103 | $args = (count($args) > 1) ? array_slice($args, 1) : array(); |
| 104 | return _simpleweb_invoke($route, $args); |
| 105 | } |
| 106 | |
| 107 | if ($not_found_route) return _simpleweb_invoke($not_found_route, array($request_path)); |
| 108 | |
| 109 | _simpleweb_not_found(); |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Invokes a route. |
| 114 | * |
| 115 | * @param string $route the route |
| 116 | * @param array $args the arguments |
| 117 | * @return mixed the result from calling the route. |
| 118 | */ |
| 119 | function _simpleweb_invoke($route, $args = array()) { |
| 120 | if (strpos($route, '::') !== false) { |
| 121 | list($class, $method) = split($route, '::', 2); |
| 122 | return call_user_func_array(array($class, $method), $args); |
| 123 | } elseif(strpos($route, '->') !== false) { |
| 124 | list($class, $method) = split($route, '->', 2); |
| 125 | $object &= new $class; |
| 126 | return call_user_func_array(array($object, $method), $args); |
| 127 | } else { |
| 128 | return call_user_func_array($route, $args); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * Displays a HTTP 404 Not Found error and exits. |
| 134 | */ |
| 135 | function _simpleweb_not_found() { |
| 136 | switch ($_SERVER['REDIRECT_STATUS']) { |
| 137 | case '403': |
| 138 | $status = '403 Forbidden'; |
| 139 | break; |
| 140 | case '404': |
| 141 | default: |
| 142 | $status = '404 Not Found'; |
| 143 | break; |
| 144 | } |
| 145 | |
| 146 | if (substr(PHP_SAPI, 0, 3) === 'cgi') { |
| 147 | header('Status: ' . $status); |
| 148 | } else { |
| 149 | header($_SERVER['SERVER_PROTOCOL'] . ' ' . $status); |
| 150 | } |
| 151 | header('Content-Type: text/html'); |
| 152 | |
| 153 | print '<!doctype html><html><head><title>' . $status . '</title></head><body><h1>' . $status . '</h1></body></html>'; |
| 154 | |
| 155 | exit; |
| 156 | } |
| 157 | ?> |