blob: 48cdbc690d91b42964c1ba786de6f1b6820723b3 [file] [log] [blame]
Nico Huberee52fbc2023-06-24 11:52:57 +00001<?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 -&gt; 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
73function 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 */
119function _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 */
135function _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?>