Change BSRouter::route() to BSRouter::dispatch()
[isso.git] / Router.php
1 <?php
2 /*=====================================================================*
3 || ###################################################################
4 || # Blue Static ISSO Framework [#]issoversion[#]
5 || # Copyright ©2002-[#]year[#] Blue Static
6 || #
7 || # This program is free software; you can redistribute it and/or modify
8 || # it under the terms of the GNU General Public License as published by
9 || # the Free Software Foundation; version [#]gpl[#] of the License.
10 || #
11 || # This program is distributed in the hope that it will be useful, but
12 || # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 || # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 || # more details.
15 || #
16 || # You should have received a copy of the GNU General Public License along
17 || # with this program; if not, write to the Free Software Foundation, Inc.,
18 || # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 || ###################################################################
20 \*=====================================================================*/
21
22 /**
23 * Request Router (Router.php)
24 *
25 * @package ISSO
26 */
27
28 require_once('ISSO/Functions.php');
29 require_once('ISSO/RouterController.php');
30
31 /**
32 * Router
33 *
34 * Run route() in a file with a defined routing pattern and send all
35 * requests to that file, this will then load and appropriatley run
36 * the right source files
37 *
38 * @author Blue Static
39 * @copyright Copyright (c)2002 - [#]year[#], Blue Static
40 * @version $Revision$
41 * @package ISSO
42 *
43 */
44 class BSRouter
45 {
46 const MAP_LOADER = 1;
47 const MAP_PARAMS = 2;
48
49 /**
50 * Routing map of request:(controller,:method,args...)
51 * @var array
52 */
53 private $map = array();
54
55 /**
56 * The base URL
57 * @var string
58 */
59 private $basePath;
60
61 /**
62 * Map action for errors
63 * @var string
64 */
65 private $errorAction = ':undefined:';
66
67 /**
68 * Router controller requested
69 * @var string
70 */
71 private $controller;
72
73 /**
74 * Router action requsted
75 * @var string
76 */
77 private $action;
78
79 /**
80 * Request paramaters
81 * @var array
82 */
83 private $params = array();
84
85 // ###################################################################
86 /**
87 * Constructor
88 */
89 public function __construct()
90 {
91 $this->basePath = str_replace(basename($_SERVER['PHP_SELF']), '', $_SERVER['SCRIPT_NAME']);
92 }
93
94 // ###################################################################
95 /**
96 * Listens for routing requests and handles them appropriately
97 */
98 public function dispatch()
99 {
100 $request = str_replace($this->basePath, '', $_SERVER['REQUEST_URI']);
101
102 $params = explode('/', $request);
103 $request = explode('.', $params[0]);
104 $this->request = $request[0];
105 $this->action = ((!isset($request[1]) OR empty($request[1])) ? 'Index' : $request[1]);
106 unset($params[0]);
107
108 if (sizeof($params) == 0)
109 {
110 // do nothing
111 }
112 else if (sizeof($params) == 1)
113 {
114 $this->params[':id'] = $params[1];
115 }
116 else
117 {
118 $key = '';
119 for ($i = 1; $i <= sizeof($params); $i++)
120 {
121 if ($i == 1 AND $params["$i"][0] != '+')
122 {
123 $this->params[':id'] = $params["$i"];
124 continue;
125 }
126
127 if ($params["$i"] == '')
128 {
129 continue;
130 }
131
132 if ($params["$i"][0] == '+')
133 {
134 if (!empty($key))
135 {
136 $this->_error(sprintf(_('The paramter "%1$s" does not have a value'), $key));
137 }
138 $key = substr($params["$i"], 1);
139 }
140 else
141 {
142 if (empty($key))
143 {
144 $this->_error(sprintf(_('The value "%1$s" does not have a paramatized key'), $params["$i"]));
145 }
146 $this->params["$key"] = $params["$i"];
147 $key = '';
148 }
149 }
150 }
151
152 if (isset($this->map[ $this->request ]))
153 {
154 call_user_func_array(array($this, $this->map[ $this->request ][self::MAP_LOADER]), $this->map[ $this->request ][self::MAP_PARAMS]);
155 }
156 else
157 {
158 $this->_error(sprintf(_('Routing request "%1$s" not found in map'), $this->request));
159 }
160 }
161
162 // ###################################################################
163 /**
164 * Sets the error handler of the router; when an error occurs while
165 * routing, this method is called
166 *
167 * @param string Error handler name
168 */
169 public function setErrorAction($name)
170 {
171 $this->errorAction = $name;
172 }
173
174 // ###################################################################
175 /**
176 * Adds a directory of files to the maps, with the file names (without .php)
177 * as the map request name
178 *
179 * @param string Directory path
180 */
181 public function addFileDirectory($path)
182 {
183 $path = BSFunctions::FetchSourcePath($path);
184 $controllers = BSFunctions::ScanDirectory($path);
185
186 foreach ($controllers AS $file)
187 {
188 $controller = str_replace('.php', '', $file);
189 $this->addFileRequest($controller, $path . $file);
190 }
191 }
192
193 // ###################################################################
194 /**
195 * Routes a request to the loading of a file; this is the most common
196 * routing request and it performs no extra functions
197 *
198 * @param string Request name
199 * @param string File name
200 */
201 public function addFileRequest($request, $file)
202 {
203 $this->map["$request"] = array(self::MAP_LOADER => '_loadFile', self::MAP_PARAMS => array($file));
204 }
205
206 // ###################################################################
207 /**
208 * Adds a list of files to the mapping; the controller will become
209 * the file name without the .php extension
210 *
211 * @param string Search path
212 */
213 public function addControllerDirectory($path)
214 {
215 $path = BSFunctions::FetchSourcePath($path);
216 $controllers = BSFunctions::ScanDirectory($path);
217
218 foreach ($controllers AS $file)
219 {
220 $controller = str_replace('.php', '', $file);
221 $this->addController($controller, $path . $controller);
222 }
223 }
224
225 // ###################################################################
226 /**
227 * Routes a request to the controller system
228 *
229 * @param string Request name
230 * @param string Controller name; the file must be named <X>.php and define a class <X>Controller.php
231 */
232 public function addController($request, $file)
233 {
234 $this->map["$request"] = array(self::MAP_LOADER => '_invokeController', self::MAP_PARAMS => array($file));
235 }
236
237 // ###################################################################
238 /**
239 * Includes a given file name
240 *
241 * @param string File name
242 */
243 private function _loadFile($filename)
244 {
245 if (!file_exists($filename))
246 {
247 $this->_error(sprintf(_('Cannot find the file "%1$s"'), $filename));
248 }
249 include($filename);
250 }
251
252 // ###################################################################
253 /**
254 * Loads and then invokes a controller
255 *
256 * @param string File name of the controller
257 */
258 public function _invokeController($controller)
259 {
260 $this->_loadFile($controller);
261
262 $controllerName = basename($controller, '.php');
263 $className = "{$controllerName}Controller";
264
265 if (!class_exists($className))
266 {
267 trigger_error('The controller "' . $className . '" does not exist and therfore cannot have actions forwarded to it');
268 }
269
270 if (!is_subclass_of($className, 'BSRouterController'))
271 {
272 trigger_error('Cannot invoke controller-style actions on a non-controller: requested controller is not of the type BSRouterController');
273 }
274
275 // these go out the door so BSInput will process them and merge them into BSInput->in[]
276 $_GET = $this->params;
277 $request = new $className(BSRegister::LoadModule('Input'));
278
279 if (!method_exists($className, $this->action))
280 {
281 trigger_error('Invalid action called on controller: ' . $className . ' does not respond to ' . $this->action . '()');
282 }
283
284 $request->{$this->action}();
285 }
286
287 // ###################################################################
288 /**
289 * Sends an error to the error action handler
290 *
291 * @param string Error message
292 */
293 private function _error($errMsg)
294 {
295 if (function_exists($this->errorAction) OR (is_array($this->errorAction) AND method_exists($this->errorAction[0], $this->errorAction[1])))
296 {
297 $this->errorAction($errMsg);
298 }
299 else
300 {
301 trigger_error($errMsg);
302 }
303 exit;
304 }
305 }
306
307 /*=====================================================================*
308 || ###################################################################
309 || # $HeadURL$
310 || # $Id$
311 || ###################################################################
312 \*=====================================================================*/
313 ?>