- Added a BSPrinterRootElementForm
[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 dispatch() 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. You can use mod_rewrite like this:
37 *
38 * RewriteEngine on
39 * RewriteCond %{REQUEST_URI} !static/(.*)$
40 * RewriteRule ^(.*)$ index.php
41 *
42 * This will prevent the rewriting on any file in the static/ directory.
43 * If your server cannot do this rewriting, then simply define this constant:
44 * ISSO_ROUTER_NO_REWRITE as TRUE and it will switch to standard URLs
45 *
46 * @author Blue Static
47 * @copyright Copyright (c)2002 - [#]year[#], Blue Static
48 * @version $Revision$
49 * @package ISSO
50 *
51 */
52 class BSRouter
53 {
54 const MAP_LOADER = 1;
55 const MAP_PARAMS = 2;
56
57 /**
58 * Routing map of request:(controller,:method,args...)
59 * @var array
60 */
61 private $map = array();
62
63 /**
64 * The base URL
65 * @var string
66 */
67 private $basePath;
68
69 /**
70 * Map action for errors
71 * @var string
72 */
73 private $errorAction = ':undefined:';
74
75 /**
76 * Router controller requested
77 * @var string
78 */
79 private $controller;
80
81 /**
82 * Router action requsted
83 * @var string
84 */
85 private $action;
86
87 /**
88 * Request paramaters
89 * @var array
90 */
91 private $params = array();
92
93 // ###################################################################
94 /**
95 * Constructor
96 */
97 public function __construct()
98 {
99 $this->basePath = str_replace(basename($_SERVER['PHP_SELF']), '', $_SERVER['SCRIPT_NAME']);
100 }
101
102 // ###################################################################
103 /**
104 * Listens for routing requests and handles them appropriately
105 */
106 public function dispatch()
107 {
108 if ((defined('ISSO_ROUTER_NO_REWRITE') AND constant('ISSO_ROUTER_NO_REWRITE')) OR isset($_REQUEST['controller']))
109 {
110 $this->request = $_REQUEST['controller'];
111 $this->action = ((!isset($_REQUEST['action']) OR empty($_REQUEST['action'])) ? 'Index' : $_REQUEST['action']);
112 $this->params = $_GET;
113 }
114 else
115 {
116 $request = str_replace($this->basePath, '', $_SERVER['REQUEST_URI']);
117
118 $params = explode('/', $request);
119 $request = explode('.', $params[0]);
120 $this->request = $request[0];
121 $this->action = ((!isset($request[1]) OR empty($request[1])) ? 'Index' : $request[1]);
122 unset($params[0]);
123
124 if (sizeof($params) == 0)
125 {
126 // do nothing
127 }
128 else if (sizeof($params) == 1)
129 {
130 $this->params[':id'] = $params[1];
131 }
132 else
133 {
134 $key = '';
135 for ($i = 1; $i <= sizeof($params); $i++)
136 {
137 if ($i == 1 AND $params["$i"][0] != '+')
138 {
139 $this->params[':id'] = $params["$i"];
140 continue;
141 }
142
143 if ($params["$i"] == '')
144 {
145 continue;
146 }
147
148 if ($params["$i"][0] == '+')
149 {
150 if (!empty($key))
151 {
152 $this->_error(sprintf(_('The paramter "%1$s" does not have a value'), $key));
153 }
154 $key = substr($params["$i"], 1);
155 }
156 else
157 {
158 if (empty($key))
159 {
160 $this->_error(sprintf(_('The value "%1$s" does not have a paramatized key'), $params["$i"]));
161 }
162 $this->params["$key"] = $params["$i"];
163 $key = '';
164 }
165 }
166 }
167 }
168
169 if (isset($this->map[ $this->request ]))
170 {
171 call_user_func_array(array($this, $this->map[ $this->request ][self::MAP_LOADER]), $this->map[ $this->request ][self::MAP_PARAMS]);
172 }
173 else
174 {
175 $this->_error(sprintf(_('Routing request "%1$s" not found in map'), $this->request));
176 }
177 }
178
179 // ###################################################################
180 /**
181 * Sets the error handler of the router; when an error occurs while
182 * routing, this method is called
183 *
184 * @param string Error handler name
185 */
186 public function setErrorAction($name)
187 {
188 $this->errorAction = $name;
189 }
190
191 // ###################################################################
192 /**
193 * Adds a directory of files to the maps, with the file names (without .php)
194 * as the map request name
195 *
196 * @param string Directory path
197 */
198 public function addFileDirectory($path)
199 {
200 $path = BSFunctions::FetchSourcePath($path);
201 $controllers = BSFunctions::ScanDirectory($path);
202
203 foreach ($controllers AS $file)
204 {
205 $controller = str_replace('.php', '', $file);
206 $this->addFileRequest($controller, $path . $file);
207 }
208 }
209
210 // ###################################################################
211 /**
212 * Routes a request to the loading of a file; this is the most common
213 * routing request and it performs no extra functions
214 *
215 * @param string Request name
216 * @param string File name
217 */
218 public function addFileRequest($request, $file)
219 {
220 $this->map["$request"] = array(self::MAP_LOADER => '_loadFile', self::MAP_PARAMS => array($file));
221 }
222
223 // ###################################################################
224 /**
225 * Adds a list of files to the mapping; the controller will become
226 * the file name without the .php extension
227 *
228 * @param string Search path
229 */
230 public function addControllerDirectory($path)
231 {
232 $path = BSFunctions::FetchSourcePath($path);
233 $controllers = BSFunctions::ScanDirectory($path);
234
235 foreach ($controllers AS $file)
236 {
237 $controller = str_replace('.php', '', $file);
238 $this->addController($controller, $path . $controller);
239 }
240 }
241
242 // ###################################################################
243 /**
244 * Routes a request to the controller system
245 *
246 * @param string Request name
247 * @param string Controller name; the file must be named <X>.php and define a class <X>Controller.php
248 */
249 public function addController($request, $file)
250 {
251 $this->map["$request"] = array(self::MAP_LOADER => '_invokeController', self::MAP_PARAMS => array($file));
252 }
253
254 // ###################################################################
255 /**
256 * Includes a given file name
257 *
258 * @param string File name
259 */
260 private function _loadFile($filename)
261 {
262 if (!file_exists($filename))
263 {
264 $this->_error(sprintf(_('Cannot find the file "%1$s"'), $filename));
265 }
266 include($filename);
267 }
268
269 // ###################################################################
270 /**
271 * Loads and then invokes a controller
272 *
273 * @param string File name of the controller
274 */
275 public function _invokeController($controller)
276 {
277 $this->_loadFile($controller);
278
279 $controllerName = basename($controller, '.php');
280 $className = "{$controllerName}Controller";
281
282 if (!class_exists($className))
283 {
284 trigger_error('The controller "' . $className . '" does not exist and therfore cannot have actions forwarded to it');
285 }
286
287 if (!is_subclass_of($className, 'BSRouterController'))
288 {
289 trigger_error('Cannot invoke controller-style actions on a non-controller: requested controller is not of the type BSRouterController');
290 }
291
292 // these go out the door so BSInput will process them and merge them into BSInput->in[]
293 $_GET = $this->params;
294 $request = new $className(BSRegister::LoadModule('Input'));
295
296 if (!method_exists($className, $this->action))
297 {
298 trigger_error('Invalid action called on controller: ' . $className . ' does not respond to ' . $this->action . '()');
299 }
300
301 $request->{$this->action}();
302 }
303
304 // ###################################################################
305 /**
306 * Sends an error to the error action handler
307 *
308 * @param string Error message
309 */
310 private function _error($errMsg)
311 {
312 if (function_exists($this->errorAction) OR (is_array($this->errorAction) AND method_exists($this->errorAction[0], $this->errorAction[1])))
313 {
314 $this->errorAction($errMsg);
315 }
316 else
317 {
318 trigger_error($errMsg);
319 }
320 exit;
321 }
322 }
323
324 /*=====================================================================*
325 || ###################################################################
326 || # $HeadURL$
327 || # $Id$
328 || ###################################################################
329 \*=====================================================================*/
330 ?>