PageRenderTime 75ms CodeModel.GetById 19ms app.highlight 45ms RepoModel.GetById 1ms app.codeStats 0ms

/manager/modules/lightvc.php

http://flextrine2.googlecode.com/
PHP | 1424 lines | 588 code | 123 blank | 713 comment | 79 complexity | e029da0f65e5c891a8058fe474328ccf MD5 | raw file
   1<?php
   2/**
   3 * LightVC - A lightweight view-controller framework.
   4 * http://lightvc.org/
   5 * 
   6 * You provide your own model/ORM. We recommend Cough <http://coughphp.com>.
   7 * 
   8 * The purpose of this framework is to provide just a "view-controller"
   9 * setup without all the other junk. Ideally, the classes from other frameworks
  10 * should be reusable but instead they are mostly coupled with their frameworks.
  11 * It's up to you to go get those classes if you need them, or provide your own.
  12 * 
  13 * Additionally, we've decoupled it from any sort of Model so that you can use
  14 * the one you already know and love. And if you don't know one, now is a great
  15 * time to check out CoughPHP. Other ORMs can be found at:
  16 * 
  17 * http://en.wikipedia.org/wiki/List_of_object-relational_mapping_software#PHP
  18 * 
  19 * By providing just the VC, we increase the reusability of not only the
  20 * framework itself, but non-framework components as well.
  21 * 
  22 * The framework is fast. Currently the speed of this framework is unmatched by
  23 * any other PHP framework available today.
  24 * 
  25 * You get to use the classes you've already been using without worrying about
  26 * naming conflicts or inefficiencies from loading both your classes and the
  27 * classes from some other framework.
  28 * 
  29 * LightVC aims to be easier to use, more configurable, and light in footprint.
  30 * 
  31 * @author Anthony Bush
  32 * @version 1.0.4 (2008-03-15)
  33 * @package lightvc
  34 * @see http://lightvc.org/
  35 **/
  36
  37/**
  38 * Configuration class for the LVC suite of classes.
  39 *
  40 * @package lightvc
  41 * @author Anthony Bush
  42 * @since 2007-04-20
  43 **/
  44class Lvc_Config {
  45	protected static $controllerPaths = array();
  46	protected static $controllerSuffix = '.php'; // e.g. _controller.php
  47	protected static $controllerViewPaths = array();
  48	protected static $controllerViewSuffix = '.php'; // e.g. .tpl.php
  49	protected static $layoutViewPaths = array();
  50	protected static $layoutViewSuffix = '.php'; // e.g. .tpl.php
  51	protected static $elementViewPaths = array();
  52	protected static $elementViewSuffix = '.php'; // e.g. .tpl.php
  53	protected static $viewClassName = 'Lvc_View'; // e.g. AppView
  54	protected static $layoutContentVarName = 'layoutContent'; // e.g. content_for_layout
  55	
  56	/**
  57	 * Sets whether or not to send action params as an array or as arguments
  58	 * to the function.
  59	 * 
  60	 * true => action($params)
  61	 * false => action($param1, $param2, $param3, ...)
  62	 *
  63	 * @var boolean
  64	 **/
  65	protected static $sendActionParamsAsArray = false;
  66	
  67	// These may be moved into some sort of routing thing later. For now:
  68	
  69	/**
  70	 * The controller name to use if no controller name can be gathered from the request.
  71	 *
  72	 * @var string
  73	 **/
  74	protected static $defaultControllerName = 'page';
  75	/**
  76	 * The action name to call on the defaultControllerName if no controller name can be gathered from the request.
  77	 *
  78	 * @var string
  79	 **/
  80	protected static $defaultControllerActionName = 'view';
  81	/**
  82	 * The action params to use when calling defaultControllerActionName if no controller name can be gathered from the request.
  83	 *
  84	 * @var string
  85	 **/
  86	protected static $defaultControllerActionParams = array('page_name' => 'home');
  87	/**
  88	 * The default action name to call on a controller if the controller name
  89	 * was gathered from the request, but the action name couldn't be.
  90	 *
  91	 * @var string
  92	 **/
  93	protected static $defaultActionName = 'index';
  94	
  95	// Configuration Methods
  96	
  97	public static function addControllerPath($path) {
  98		self::$controllerPaths[] = $path;
  99	}
 100	public static function setControllerSuffix($suffix) {
 101		self::$controllerSuffix = $suffix;
 102	}
 103	public static function addControllerViewPath($path) {
 104		self::$controllerViewPaths[] = $path;
 105	}
 106	public static function setControllerViewSuffix($suffix) {
 107		self::$controllerViewSuffix = $suffix;
 108	}
 109	public static function addLayoutViewPath($path) {
 110		self::$layoutViewPaths[] = $path;
 111	}
 112	public static function setLayoutViewSuffix($suffix) {
 113		self::$layoutViewSuffix = $suffix;
 114	}
 115	public static function addElementViewPath($path) {
 116		self::$elementViewPaths[] = $path;
 117	}
 118	public static function setElementViewSuffix($suffix) {
 119		self::$elementViewSuffix = $suffix;
 120	}
 121	public static function setViewClassName($className) {
 122		self::$viewClassName = $className;
 123	}
 124	public static function setLayoutContentVarName($varName) {
 125		self::$layoutContentVarName = $varName;
 126	}
 127	public static function getLayoutContentVarName() {
 128		return self::$layoutContentVarName;
 129	}
 130	public static function setSendActionParamsAsArray($bool) {
 131		self::$sendActionParamsAsArray = $bool;
 132	}
 133	public static function getSendActionParamsAsArray() {
 134		return self::$sendActionParamsAsArray;
 135	}
 136	public static function setDefaultControllerName($defaultControllerName) {
 137		self::$defaultControllerName = $defaultControllerName;
 138	}
 139	public static function setDefaultControllerActionName($defaultControllerActionName) {
 140		self::$defaultControllerActionName = $defaultControllerActionName;
 141	}
 142	public static function setDefaultControllerActionParams($defaultControllerActionParams) {
 143		self::$defaultControllerActionParams = $defaultControllerActionParams;
 144	}
 145	public static function setDefaultActionName($defaultActionName) {
 146		self::$defaultActionName = $defaultActionName;
 147	}
 148	public static function getDefaultControllerName() {
 149		return self::$defaultControllerName;
 150	}
 151	public static function getDefaultControllerActionName() {
 152		return self::$defaultControllerActionName;
 153	}
 154	public static function getDefaultControllerActionParams() {
 155		return self::$defaultControllerActionParams;
 156	}
 157	public static function getDefaultActionName() {
 158		return self::$defaultActionName;
 159	}
 160	
 161	// Retrieval Methods
 162	
 163	public static function getController($controllerName) {
 164		foreach (self::$controllerPaths as $path) {
 165			$file = $path . $controllerName . self::$controllerSuffix;
 166			if (file_exists($file)) {
 167				include_once($file);
 168				$controllerClass = self::getControllerClassName($controllerName);
 169				$controller = new $controllerClass();
 170				$controller->setControllerName($controllerName);
 171				return $controller;
 172			}
 173		}
 174		return null;
 175	}
 176	
 177	public static function getControllerClassName($controllerName) {
 178		return str_replace(' ', '', ucwords(str_replace('_', ' ', $controllerName))) . 'Controller';
 179	}
 180	
 181	public static function getActionFunctionName($actionName) {
 182		return 'action' . str_replace(' ', '', ucwords(str_replace('_', ' ', $actionName)));
 183	}
 184	
 185	public static function getControllerView($viewName, &$data = array()) {
 186		return self::getView($viewName, $data, self::$controllerViewPaths, self::$controllerViewSuffix);
 187	}
 188	
 189	public static function getElementView($elementName, &$data = array()) {
 190		return self::getView($elementName, $data, self::$elementViewPaths, self::$elementViewSuffix);
 191	}
 192	
 193	public static function getLayoutView($layoutName, &$data = array()) {
 194		return self::getView($layoutName, $data, self::$layoutViewPaths, self::$layoutViewSuffix);
 195	}
 196	
 197	/**
 198	 * As an Lvc developer, you'll probably want to use `getControllerView`,
 199	 * `getElementView`, or `getLayoutView`.
 200	 * 
 201	 * Example usage:
 202	 * 
 203	 *     // Pass the whole file name and leave off the last parameters
 204	 *     getView('/full/path/to/file/file.php', $data);
 205	 * 
 206	 *     // Pass the view name and specify the paths to scan and the suffix to append.
 207	 *     getView('file', $data, array('/full/path/to/file/'), '.php');
 208	 *
 209	 * @var mixed Lvc_View object if one is found, otherwise null.
 210	 * @see getControllerView(), getElementView(), getLayoutView(), Lvc_Config::setViewClassName()
 211	 **/
 212	public static function getView($viewName, &$data = array(), &$paths = array(''), $suffix = '') {
 213		foreach ($paths as $path) {
 214			$file = $path . $viewName . $suffix;
 215			if (file_exists($file)) {
 216				return new self::$viewClassName($file, $data);
 217			}
 218		}
 219		return null;
 220	}
 221	
 222	public static function dump() {
 223		echo '<pre>';
 224		
 225		echo '<strong>Controller Paths:</strong>' . "\n";
 226		print_r(self::$controllerPaths);
 227		echo '<strong>Controller Suffix:</strong> ' . self::$controllerSuffix . "\n\n";
 228		
 229		echo '<strong>Layout View Paths:</strong>' . "\n";
 230		print_r(self::$layoutViewPaths);
 231		echo '<strong>Layout View Suffix:</strong> '     . self::$layoutViewSuffix . "\n\n";
 232		
 233		echo '<strong>Controller View Paths:</strong>' . "\n";
 234		print_r(self::$controllerViewPaths);
 235		echo '<strong>Controller View Suffix:</strong> '    . self::$controllerViewSuffix . "\n\n";
 236		
 237		echo '<strong>Element View Paths:</strong>' . "\n";
 238		print_r(self::$elementViewPaths);
 239		echo '<strong>Element View Suffix:</strong> '    . self::$elementViewSuffix . "\n\n";
 240		
 241		echo '</pre>';
 242	}
 243}
 244
 245/**
 246 * Lvc classes throw this type of exception.
 247 *
 248 * @package lightvc
 249 * @author Anthony Bush
 250 * @since 2007-04-20
 251 **/
 252class Lvc_Exception extends Exception {
 253	
 254}
 255
 256/**
 257 * A request provides information about what controller and action to run and
 258 * what parameters to run them with.
 259 * 
 260 * @package lightvc
 261 * @author Anthony Bush
 262 * @since 2007-04-20
 263 **/
 264class Lvc_Request {
 265	protected $controllerName = '';
 266	protected $controllerParams = array();
 267	protected $actionName = '';
 268	protected $actionParams = array();
 269	
 270	public function getControllerName() {
 271		return $this->controllerName;
 272	}
 273	public function &getControllerParams() {
 274		return $this->controllerParams;
 275	}
 276	public function getActionName() {
 277		return $this->actionName;
 278	}
 279	public function &getActionParams() {
 280		return $this->actionParams;
 281	}
 282
 283	public function setControllerName($controllerName) {
 284		$this->controllerName = $controllerName;
 285	}
 286	public function setControllerParams(&$controllerParams) {
 287		$this->controllerParams = $controllerParams;
 288	}
 289	public function setActionName($actionName) {
 290		$this->actionName = $actionName;
 291	}
 292	public function setActionParams($actionParams) {
 293		$this->actionParams = $actionParams;
 294	}
 295	
 296	/**
 297	 * Override this in sub request objects to have custom error messages appended to
 298	 * LightVC messages.  For example, when HTTP Requests error, it might be useful
 299	 * to put the requested URL in the error log with the "Unable to load controller"
 300	 * message.
 301	 *
 302	 * @return string
 303	 * @since 2008-03-14
 304	 **/
 305	public function getAdditionalErrorInfo() {
 306		return '';
 307	}
 308}
 309
 310/**
 311 * An HTTP request contains parameters from the GET, POST, PUT, and
 312 * DELETE arena.
 313 *
 314 * @package lightvc
 315 * @author Anthony Bush
 316 * @since 2007-04-20
 317 **/
 318class Lvc_HttpRequest extends Lvc_Request {
 319	protected $params = array();
 320	
 321	public function __construct() {
 322		
 323		$params = array();
 324		
 325		// Save GET data
 326		if (isset($_GET)) {
 327			$params['get'] =& $_GET;
 328		} else {
 329			$params['get'] = array();
 330		}
 331		
 332		// Ensure that we have some mode_rewritten url.
 333		if (!isset($params['get']['url'])) {
 334			$params['get']['url'] = '';
 335		}
 336		
 337		// Save POST data
 338		$params['post'] =& $_POST;
 339		
 340		// Save FILE data (consilidate it with _POST data)
 341		foreach ($_FILES as $name => $data) {
 342			if ($name != 'data') {
 343				$params['post'][$name] = $data;
 344			} else {
 345				// Convert _FILE[data][key][model][field] -> [data][model][field][key]
 346				// so that it matches up with _POST "data"
 347				foreach ($data as $key => $modelData) {
 348					foreach ($modelData as $model => $fields) {
 349						foreach ($fields as $field => $value) {
 350							$params['post']['data'][$model][$field][$key] = $value;
 351						}
 352					}
 353				}
 354			}
 355		}
 356		
 357		// Set params that will be used by routers.
 358		$this->setParams($params);
 359		// An HTTP request will default to passing all the parameters to the controller.
 360		$this->setControllerParams($params);
 361	}
 362	
 363	public function &getParams() {
 364		return $this->params;
 365	}
 366	public function setParams(&$params) {
 367		$this->params = $params;
 368	}
 369	
 370	/**
 371	 * Provides additional error information that might be useful when debugging
 372	 * errors.
 373	 *
 374	 * @return string
 375	 * @since 2008-03-14
 376	 **/
 377	public function getAdditionalErrorInfo() {
 378		if (isset($_SERVER['REQUEST_URI'])) {
 379			return 'Request URL was ' . $_SERVER['REQUEST_URI'];
 380		} else {
 381			return parent::getAdditionalErrorInfo();
 382		}
 383	}
 384}
 385
 386/**
 387 * A router interface must at least provide a route() function that takes a
 388 * request object.
 389 * 
 390 * @package lightvc
 391 * @author Anthony Bush
 392 * @since 2007-04-22
 393 **/
 394interface Lvc_RouterInterface {
 395	/**
 396	 * Set the appropriate controller, action, and action parameters to use on
 397	 * the request object and return true. If no appropriate controller info
 398	 * can be found, return false.
 399	 * 
 400	 * @param mixed $request A request object to route.
 401	 * @return boolean
 402	 * @author Anthony Bush
 403	 * @since 2007-04-22
 404	 **/
 405	public function route($request);
 406}
 407
 408/**
 409 * Routes a request using only GET data.
 410 * 
 411 * You can change the default keys for controller and action detection using
 412 * {@link setControllerKey()} and {@link setActionKey()} respectively.
 413 *
 414 * @package lightvc
 415 * @author Anthony Bush
 416 * @since 2007-04-22
 417 **/
 418class Lvc_GetRouter implements Lvc_RouterInterface {
 419	protected $controllerKey = 'controller';
 420	protected $actionKey = 'action';
 421	protected $actionParamsKey = null;
 422	protected $routes = array();
 423	
 424	public function setControllerKey($controllerKey) {
 425		$this->controllerKey = $controllerKey;
 426	}
 427	public function setActionKey($actionKey) {
 428		$this->actionKey = $actionKey;
 429	}
 430	public function setActionParamsKey($actionParamsKey) {
 431		$this->actionParamsKey = $actionParamsKey;
 432	}
 433	
 434	/**
 435	 * Add a param order for a controller / action.
 436	 * 
 437	 * For example:
 438	 * 
 439	 *     $router->addRoute('pages', 'show_page', array('page_name'));
 440	 * 
 441	 * will route:
 442	 * 
 443	 *     ?controller=pages&action=show_page&page_name=about
 444	 * 
 445	 * to:
 446	 * 
 447	 *     PagesController::actionShowPage('about');
 448	 * 
 449	 * whereas without the route the controller would be invoked with:
 450	 * 
 451	 *     PagesController::actionShowPage();
 452	 * 
 453	 * and you'd have to access the page_name via $this->get['page_name'].
 454	 *
 455	 * @return void
 456	 * @author Anthony Bush
 457	 * @since 2007-05-10
 458	 **/
 459	public function addRoute($controllerName, $actionName, $actionParamsOrder = array()) {
 460		$this->routes[$controllerName][$actionName] = $actionParamsOrder;
 461	}
 462	
 463	/**
 464	 * Set all routes at once. Useful if you want to specify routes in a
 465	 * config file and then pass them to this router all at once. See
 466	 * {@link addRoute()} for routing specifications.
 467	 * 
 468	 * @return void
 469	 * @author Anthony Bush
 470	 * @since 2007-05-10
 471	 **/
 472	public function setRoutes(&$routes) {
 473		$this->routes = $routes;
 474	}
 475	
 476	/**
 477	 * Construct the router and set all routes at once. See {@link setRoutes()}
 478	 * for more info.
 479	 *
 480	 * @return void
 481	 * @author Anthony Bush
 482	 * @see setRoutes()
 483	 * @since 2007-05-10
 484	 **/
 485	public function __construct(&$routes = null) {
 486		if ( ! is_null($routes)) {
 487			$this->setRoutes($routes);
 488		}
 489	}
 490	
 491	/**
 492	 * Attempts to routes a request using only the GET data.
 493	 * 
 494	 * @param Lvc_HttpRequest $request A request object to route.
 495	 * @return boolean
 496	 * @author Anthony Bush
 497	 * @since 2007-04-22
 498	 **/
 499	public function route($request) {
 500		$params = $request->getParams();
 501		
 502		// Use GET parameters to set controller, action, and action params
 503		if (isset($params['get'][$this->controllerKey])) {
 504			
 505			$request->setControllerName($params['get'][$this->controllerKey]);
 506			
 507			if (isset($params['get'][$this->actionKey])) {
 508				$request->setActionName($params['get'][$this->actionKey]);
 509			} else {
 510				$request->setActionName(Lvc_Config::getDefaultActionName());
 511			}
 512			
 513			// Using paramsKey method?
 514			if ( ! is_null($this->actionParamsKey) && isset($params['get'][$this->actionParamsKey])) {
 515				$request->setActionParams($params['get'][$this->actionParamsKey]);
 516			}
 517			// Using routes?
 518			else if ( ! empty($this->routes)) {
 519				if (isset($this->routes[$request->getControllerName()])
 520				 && isset($this->routes[$request->getControllerName()][$request->getActionName()])
 521				) {
 522					$actionParams = array();
 523					foreach ($this->routes[$request->getControllerName()][$request->getActionName()] as $paramName) {
 524						$actionParams[$paramName] = @$params['get'][$paramName];
 525					}
 526					$request->setActionParams($actionParams);
 527				}
 528			}
 529			
 530			return true;
 531		} else {
 532			return false;
 533		}
 534	}
 535}
 536
 537/**
 538 * Attempts to route a request using the GET value for the 'url' key, which
 539 * should be set by the mod_rewrite rules. Any additional "directories" are
 540 * used as parameters for the action (using numeric indexes). Any extra GET
 541 * data is also amended to the action parameters.
 542 * 
 543 * If you need the numeric indexes to map to specific parameter names, use
 544 * the {@link Lvc_ParamOrderRewriteRouter} instead.
 545 * 
 546 * @package lightvc
 547 * @author Anthony Bush
 548 * @since 2007-04-22
 549 **/
 550class Lvc_RewriteRouter implements Lvc_RouterInterface {
 551	/**
 552	 * Attempts to route a request using the GET value for the 'url' key, which
 553	 * should be set by the mod_rewrite rules. Any additional "directories" are
 554	 * used as parameters for the action (using numeric indexes). Any extra GET
 555	 * data is also amended to the action parameters.
 556	 * 
 557	 * @param Lvc_HttpRequest $request A request object to route.
 558	 * @return boolean
 559	 * @author Anthony Bush
 560	 * @since 2007-04-22
 561	 **/
 562	public function route($request) {
 563		$params = $request->getParams();
 564		
 565		if (isset($params['get']['url'])) {
 566			
 567			// Use mod_rewrite's url
 568			$url = explode('/', $params['get']['url']);
 569			$count = count($url);
 570			
 571			// Set controller, action, and some action params from the segmented URL.
 572			if ($count > 0) {
 573				$request->setControllerName($url[0]);
 574				
 575				$actionParams = array();
 576				if ($count > 1) {
 577					$request->setActionName($url[1]);
 578					if ($count > 2) {
 579						for ($i = 2; $i < $count; $i++) {
 580							if ( ! empty($url[$i])) {
 581								$actionParams[] = $url[$i];
 582							}
 583						}
 584					}
 585				}
 586				
 587				$request->setActionParams($actionParams);
 588				return true;
 589			}
 590		}
 591		return false;
 592	}
 593}
 594
 595/**
 596 * Routes a request using mod_rewrite data and regular expressions specified by
 597 * the LightVC user.
 598 * 
 599 * Specify routes using {@link addRoute()}.
 600 *
 601 * @package lightvc
 602 * @author Anthony Bush
 603 * @since 2007-05-08
 604 **/
 605class Lvc_RegexRewriteRouter implements Lvc_RouterInterface {
 606	protected $routes = array();
 607	
 608	/**
 609	 * Specify a regular expression and how it should be routed.
 610	 * 
 611	 * For example:
 612	 * 
 613	 *     $regexRouter->addRoute('|^wee/([^/]+)/?$|', array(
 614	 *         'controller' => 'hello_world',
 615	 *         'action' => 'index',
 616	 *         'action_params' => array(1, 'constant_value')
 617	 *     ));
 618	 * 
 619	 * would map "wee/anything" and "wee/anything/" to:
 620	 * 
 621	 *     HelloWorldController::actionIndex('anything', 'constant_value');
 622	 * 
 623	 * but would not map "wee/anything/anything_else".
 624	 * 
 625	 * The format of the $parsingInfo parameter is as follows:
 626	 * 
 627	 *     'controller' => a hard coded controller name or an integer specifying which match in the regex to use.
 628	 *     'action' => a hard coded action name or an integer specifying which match in the regex to use.
 629	 *     'action_params' => array(
 630	 *         a hard coded action value or an integer specifying which match in the regex to use,
 631	 *         repeat above line as needed,
 632	 *     ),
 633	 *     'additional_params' => a hard coded integer specifying which match in the regex to use for additional parameters. These will be exploded by "/" and added to the action params.
 634	 * 
 635	 * or
 636	 * 
 637	 *     'redirect' => a replacement string that will be used to redirect to.  You can have parts of the original url mapped into the new one (like IDs).  See http://www.php.net/manual/en/function.preg-replace.php's documentation for the replacement parameter.
 638	 * 
 639	 * You can specify as much or as little as you want in the $parsingInfo.
 640	 * That is, if you don't specify the controller name or action name, then
 641	 * the defaults will be used by the Lvc_FrontController.
 642	 * 
 643	 * @param $regex regular expression to match the rewritten part with.
 644	 * @param $parsingInfo an array containing any custom routing info.
 645	 * @return void
 646	 * @author Anthony Bush
 647	 * @since 2007-05-08
 648	 **/
 649	public function addRoute($regex, $parsingInfo = array()) {
 650		$this->routes[$regex] = $parsingInfo;
 651	}
 652	
 653	/**
 654	 * Set all routes at once. Useful if you want to specify routes in a
 655	 * config file and then pass them to this router all at once. See
 656	 * {@link addRoute()} for routing specifications.
 657	 * 
 658	 * @return void
 659	 * @author Anthony Bush
 660	 * @since 2007-05-08
 661	 **/
 662	public function setRoutes(&$routes) {
 663		$this->routes = $routes;
 664	}
 665	
 666	/**
 667	 * Construct the router and set all routes at once. See {@link setRoutes()}
 668	 * for more info.
 669	 *
 670	 * @return void
 671	 * @author Anthony Bush
 672	 * @see setRoutes()
 673	 * @since 2007-05-09
 674	 **/
 675	public function __construct(&$routes = null) {
 676		if ( ! is_null($routes)) {
 677			$this->setRoutes($routes);
 678		}
 679	}
 680	
 681	/**
 682	 * Routes like {@link Lvc_RewriteRouter} does, with the additional check to
 683	 * routes for specifying custom routes based on regular expressions.
 684	 * 
 685	 * @param Lvc_HttpRequest $request A request object to route.
 686	 * @return boolean
 687	 * @author Anthony Bush
 688	 * @since 2007-05-08
 689	 **/
 690	public function route($request) {
 691		$params = $request->getParams();
 692		
 693		if (isset($params['get']['url'])) {
 694			
 695			// Use mod_rewrite's url
 696			$url = $params['get']['url'];
 697			
 698			$matches = array();
 699			foreach ($this->routes as $regex => $parsingInfo) {
 700				if (preg_match($regex, $url, $matches)) {
 701					
 702					// Check for redirect action first
 703					if (isset($parsingInfo['redirect'])) {
 704						$redirectUrl = preg_replace($regex, $parsingInfo['redirect'], $url);
 705						header('Location: ' . $redirectUrl);
 706						exit();
 707					}
 708					
 709					// Get controller name if available
 710					if (isset($parsingInfo['controller'])) {
 711						if (is_int($parsingInfo['controller'])) {
 712							// Get the controller name from the regex matches
 713							$request->setControllerName(@$matches[$parsingInfo['controller']]);
 714						} else {
 715							// Use the constant value
 716							$request->setControllerName($parsingInfo['controller']);
 717						}
 718					}
 719					
 720					// Get action name if available
 721					if (isset($parsingInfo['action'])) {
 722						if (is_int($parsingInfo['action'])) {
 723							// Get the action from the regex matches
 724							$request->setActionName(@$matches[$parsingInfo['action']]);
 725						} else {
 726							// Use the constant value
 727							$request->setActionName($parsingInfo['action']);
 728						}
 729					}
 730					
 731					// Get action parameters
 732					$actionParams = array();
 733					if (isset($parsingInfo['action_params'])) {
 734						foreach ($parsingInfo['action_params'] as $key => $value) {
 735							if (is_int($value)) {
 736								// Get the value from the regex matches
 737								if (isset($matches[$value])) {
 738									$actionParams[$key] = $matches[$value];
 739								} else {
 740									$actionParams[$key] = null;
 741								}
 742							} else {
 743								// Use the constant value
 744								$actionParams[$key] = $value;
 745							}
 746						}
 747					}
 748					if (isset($parsingInfo['additional_params'])) {
 749						if (is_int($parsingInfo['additional_params'])) {
 750							// Get the value from the regex matches
 751							if (isset($matches[$parsingInfo['additional_params']])) {
 752								$actionParams = $actionParams + explode('/', $matches[$parsingInfo['additional_params']]);
 753							}
 754						}
 755					}
 756					
 757					
 758					$request->setActionParams($actionParams);
 759					return true;
 760				} // route matched
 761			} // loop through routes
 762		} // url _GET value set
 763		return false;
 764	}
 765}
 766
 767/**
 768 * FrontController takes a Request object and invokes the appropriate controller
 769 * and action.
 770 * 
 771 * Example Usage:
 772 * 
 773 *     $fc = new Lvc_FrontController();
 774 *     $fc->addRouter(new Lvc_GetRouter());
 775 *     $fc->processRequest(new Lvc_HttpRequest());
 776 * 
 777 * @package lightvc
 778 * @author Anthony Bush
 779 * @since 2007-04-20
 780 **/
 781class Lvc_FrontController {
 782	protected $routers = array();
 783	
 784	/**
 785	 * Add a router to give it a chance to route the request.
 786	 * 
 787	 * The first router to return true to the {@link route()} call
 788	 * will be the last router called, so add them in the order you want them
 789	 * to run.
 790	 *
 791	 * @return void
 792	 * @author Anthony Bush
 793	 **/
 794	public function addRouter(Lvc_RouterInterface $router) {
 795		$this->routers[] = $router;
 796	}
 797	
 798	/**
 799	 * Processes the request data by instantiating the appropriate controller and
 800	 * running the appropriate action.
 801	 *
 802	 * @return void
 803	 * @throws Lvc_Exception
 804	 * @author Anthony Bush
 805	 **/
 806	public function processRequest(Lvc_Request $request) {
 807		try
 808		{
 809			// Give routers a chance to (re)-route the request.
 810			foreach ($this->routers as $router) {
 811				if ($router->route($request)) {
 812					break;
 813				}
 814			}
 815
 816			// If controller name or action name are not set, set them to default.
 817			$controllerName = $request->getControllerName();
 818			if (empty($controllerName)) {
 819				$controllerName = Lvc_Config::getDefaultControllerName();
 820				$actionName     = Lvc_Config::getDefaultControllerActionName();
 821				$actionParams = $request->getActionParams() + Lvc_Config::getDefaultControllerActionParams();
 822				$request->setActionParams($actionParams);
 823			} else {
 824				$actionName = $request->getActionName();
 825				if (empty($actionName)) {
 826					$actionName   = Lvc_Config::getDefaultActionName();
 827				}
 828			}
 829
 830			$controller = Lvc_Config::getController($controllerName);
 831			if (is_null($controller)) {
 832				throw new Lvc_Exception('Unable to load controller "' . $controllerName . '"');
 833			}
 834			$controller->setControllerParams($request->getControllerParams());
 835			$controller->runAction($actionName, $request->getActionParams());
 836		}
 837		catch (Lvc_Exception $e)
 838		{
 839			// Catch exceptions and append additional error info if the request object has anything to say.
 840			$moreInfo = $request->getAdditionalErrorInfo();
 841			if (!empty($moreInfo)) {
 842				throw new Lvc_Exception($e->getMessage() . '. ' . $moreInfo);
 843			} else {
 844				throw $e;
 845			}
 846		}
 847	}
 848}
 849
 850/**
 851 * The base class that all other PageControllers should extend. Depending on the setup,
 852 * you might want an AppController to extend this one, and then have all your controllers
 853 * extend your AppController.
 854 * 
 855 * @package lightvc
 856 * @author Anthony Bush
 857 * @todo Finish up documentation in here...
 858 * @since 2007-04-20
 859 **/
 860class Lvc_PageController {
 861	/**
 862	 * Params is typically a combination of:
 863	 *     _GET (stored in params['get'])
 864	 *     _POST (stored in params['post'])
 865	 *     _FILE (also stored in params['post'])
 866	 *
 867	 * @var array
 868	 **/
 869	protected $params = array();
 870	
 871	/**
 872	 * A reference to $params['post']['data'], typically a combination of:
 873	 *     _POST['data'] (usually holds [Model][field])
 874	 *     _FILE['data'] (usually holds [key][Model][field], but the request object should remap it to [Model][field][key])
 875	 *
 876	 * @var array
 877	 **/
 878	protected $postData = array();
 879	
 880	/**
 881	 * Reference to post data (i.e. $this->params['post'])
 882	 * 
 883	 * @var array
 884	 **/
 885	protected $post = array();
 886	
 887	/**
 888	 * Reference to get data (i.e. $this->params['get'])
 889	 * 
 890	 * @var array
 891	 **/
 892	protected $get = array();
 893	
 894	/**
 895	 * Controller Name (e.g. controller_name, not ControllerNameController)
 896	 *
 897	 * @var string
 898	 **/
 899	protected $controllerName = null;
 900	
 901	/**
 902	 * Action Name (e.g. action_name, not actionActionName)
 903	 *
 904	 * @var string
 905	 **/
 906	protected $actionName = null;
 907	
 908	/**
 909	 * Variables we will pass to the view.
 910	 *
 911	 * @var array()
 912	 **/
 913	protected $viewVars = array();
 914	
 915	/**
 916	 * Have we loaded the view yet?
 917	 *
 918	 * @var boolean
 919	 **/
 920	protected $hasLoadedView = false;
 921	
 922	/**
 923	 * Specifies whether or not to load the default view for the action. If the
 924	 * action should not render any view, set it to false in the sub controller.
 925	 *
 926	 * @var boolean
 927	 **/
 928	protected $loadDefaultView = true;
 929	
 930	/**
 931	 * Don't set this yourself. It's used internally by parent controller /
 932	 * actions to determine whether or not to use the layout value in
 933	 * $layoutOverride rather than in $layout when requesting a sub action.
 934	 * 
 935	 * @var string
 936	 * @see setLayoutOverride(), $layoutOverride
 937	 **/
 938	protected $useLayoutOverride = false;
 939	
 940	/**
 941	 * Don't set this yourself. It's used internally by parent controller /
 942	 * actions to determine which layout to use when requesting a sub action.
 943	 * 
 944	 * @var string
 945	 * @see setLayoutOverride(), $useLayoutOverride
 946	 **/
 947	protected $layoutOverride = null;
 948	
 949	/**
 950	 * Set this in your controller to use a layout.
 951	 *
 952	 * @var string
 953	 **/
 954	protected $layout = null;
 955	
 956	/**
 957	 * An array of view variables specifically for the layout file.
 958	 *
 959	 * @var array
 960	 **/
 961	protected $layoutVars = array();
 962	
 963	/**
 964	 * Set the parameters of the controller.
 965	 * Actions will get their parameters through params['get'].
 966	 * Actions can access the post data as needed.
 967	 * 
 968	 * @param array $params an array of [paramName] => [paramValue] pairs
 969	 * @return void
 970	 * @author Anthony Bush
 971	 **/
 972	public function setControllerParams(&$params) {
 973		$this->params = $params;
 974		// Make a reference to the form data so we can get to it easier.
 975		if (isset($this->params['post']['data'])) {
 976			$this->postData =& $this->params['post']['data'];
 977		}
 978		if (isset($this->params['post'])) {
 979			$this->post =& $this->params['post'];
 980		}
 981		if (isset($this->params['get'])) {
 982			$this->get =& $this->params['get'];
 983		}
 984	}
 985	
 986	/**
 987	 * Don't call this yourself. It's used internally when creating new
 988	 * controllers so the controllers are aware of their name without
 989	 * needing any help from a user setting a member variable or from some
 990	 * reflector class.
 991	 *
 992	 * @return void
 993	 * @author Anthony Bush
 994	 **/
 995	public function setControllerName($controllerName) {
 996		$this->controllerName = $controllerName;
 997	}
 998	
 999	/**
1000	 * Set a variable for the view to use.
1001	 * 
1002	 * @param string $varName variable name to make available in the view
1003	 * @param $value value of the variable.
1004	 * @return void
1005	 * @author Anthony Bush
1006	 **/
1007	public function setVar($varName, $value) {
1008		$this->viewVars[$varName] = $value;
1009	}
1010	
1011	/**
1012	 * Set variables for the view in masse.
1013	 * 
1014	 * @param $varArray an array of [varName] => [value] pairs.
1015	 * @return void
1016	 * @author Anthony Bush
1017	 **/
1018	public function setVars(&$varArray) {
1019		$this->viewVars = $varArray + $this->viewVars;
1020	}
1021	
1022	/**
1023	 * Get the current value for a view variable.
1024	 * 
1025	 * @param string $varName
1026	 * @return mixed
1027	 * @author Anthony Bush
1028	 * @since 2007-11-13
1029	 **/
1030	public function getVar($varName) {
1031		if (isset($this->viewVars[$varName])) {
1032			return $this->viewVars[$varName];
1033		} else {
1034			return null;
1035		}
1036	}
1037	
1038	/**
1039	 * Set a variable for the layout view.
1040	 *
1041	 * @param $varName variable name to make available in the view
1042	 * @param $value value of the variable.
1043	 * @return void
1044	 * @author Anthony Bush
1045	 * @since 2007-05-17
1046	 **/
1047	public function setLayoutVar($varName, $value) {
1048		$this->layoutVars[$varName] = $value;
1049	}
1050	
1051	/**
1052	 * Get the current value for a layout variable.
1053	 * 
1054	 * @param string $varName
1055	 * @return mixed
1056	 * @author Anthony Bush
1057	 * @since 2007-11-13
1058	 **/
1059	public function getLayoutVar($varName) {
1060		if (isset($this->layoutVars[$varName])) {
1061			return $this->layoutVars[$varName];
1062		} else {
1063			return null;
1064		}
1065	}	
1066	
1067	/**
1068	 * Set the layout to use for the view.
1069	 * 
1070	 * @return void
1071	 * @author Anthony Bush
1072	 **/
1073	public function setLayout($layout) {
1074		$this->layout = $layout;
1075	}
1076	
1077	/**
1078	 * Don't call this yourself. It's used internally when requesting sub
1079	 * actions in order to avoid loading the layout multiple times.
1080	 *
1081	 * @return void
1082	 * @see $useLayoutOverride, $layoutOverride
1083	 * @author Anthony Bush
1084	 **/
1085	public function setLayoutOverride($layout) {
1086		$this->useLayoutOverride = true;
1087		$this->layoutOverride = $layout;
1088	}
1089	
1090	/**
1091	 * Returns the action name of this controller
1092	 *
1093	 * @return string
1094	 * @author lzhang
1095	 **/
1096	public function getActionName()
1097	{
1098		return $this->actionName;
1099	}
1100	
1101	/**
1102	 * Determine whether or not the the controller has the specified action.
1103	 * 
1104	 * @param string $actionName the action name to check for.
1105	 * @return boolean
1106	 * @author Anthony Bush
1107	 **/
1108	public function hasAction($actionName) {
1109		if (method_exists($this, Lvc_Config::getActionFunctionName($actionName))) {
1110			return true;
1111		} else {
1112			return false;
1113		}
1114	}
1115	
1116	/**
1117	 * Runs the requested action and returns the output from it.
1118	 * 
1119	 * Typically called by the FrontController.
1120	 * 
1121	 * @param string $actionName the action name to run.
1122	 * @param array $actionParams the parameters to pass to the action.
1123	 * @return string output from running the action.
1124	 * @author Anthony Bush
1125	 **/
1126	public function getActionOutput($actionName, &$actionParams = array()) {
1127		ob_start();
1128		$this->runAction($actionName, $actionParams);
1129		return ob_get_clean();
1130	}
1131	
1132	/**
1133	 * Runs the requested action and outputs its results.
1134	 * 
1135	 * Typically called by the FrontController.
1136	 * 
1137	 * @param string $actionName the action name to run.
1138	 * @param array $actionParams the parameters to pass to the action.
1139	 * @return void
1140	 * @throws Lvc_Exception
1141	 * @author Anthony Bush
1142	 **/
1143	public function runAction($actionName, &$actionParams = array()) {
1144		$this->actionName = $actionName;
1145		$func = Lvc_Config::getActionFunctionName($actionName);
1146		if (method_exists($this, $func)) {
1147			$this->beforeAction();
1148			
1149			// Call the action
1150			if (Lvc_Config::getSendActionParamsAsArray()) {
1151				$this->$func($actionParams);
1152			} else {
1153				call_user_func_array(array($this, $func), $actionParams);
1154			}
1155			
1156			// Load the view
1157			if ( ! $this->hasLoadedView && $this->loadDefaultView) {
1158				$this->loadView($this->controllerName . '/' . $actionName);
1159			}
1160			
1161			$this->afterAction();
1162			return true;
1163		} else {
1164			throw new Lvc_Exception('No action `' . $actionName . '`. Write the `' . $func . '` method');
1165		}
1166	}
1167	
1168	/**
1169	 * Load the requested controller view.
1170	 * 
1171	 * For example, you can load another view in your controller with:
1172	 * 
1173	 *     $this->loadView($this->getControllerName() . '/some_other_action');
1174	 * 
1175	 * Or some other controller with:
1176	 *
1177	 *     $this->loadView('some_other_controller/some_other_action');
1178	 * 
1179	 * Remember, the view for your action will be rendered automatically.
1180	 * 
1181	 * @param string $controllerViewName 'controller_name/action_name' format.
1182	 * @return void
1183	 * @throws Lvc_Exception
1184	 * @author Anthony Bush
1185	 **/
1186	protected function loadView($controllerViewName) {
1187		
1188		$view = Lvc_Config::getControllerView($controllerViewName, $this->viewVars);
1189		if (is_null($view)) {
1190			throw new Lvc_Exception('Unable to load controller view "' . $controllerViewName . '" for controller "' . $this->controllerName . '"');
1191		} else {
1192			$view->setController($this);
1193			$viewContents = $view->getOutput();
1194		}
1195		
1196		if ($this->useLayoutOverride) {
1197			$this->layout = $this->layoutOverride;
1198		}
1199		if ( ! empty($this->layout)) {
1200			// Use an explicit name for this data so we don't override some other variable...
1201			$this->layoutVars[Lvc_Config::getLayoutContentVarName()] = $viewContents;
1202			$layoutView = Lvc_Config::getLayoutView($this->layout, $this->layoutVars);
1203			if (is_null($layoutView)) {
1204				throw new Lvc_Exception('Unable to load layout view "' . $this->layout . '" for controller "' . $this->controllerName . '"');
1205			} else {
1206				$layoutView->setController($this);
1207				$layoutView->output();
1208			}
1209		} else {
1210			echo($viewContents);
1211		}
1212		$this->hasLoadedView = true;
1213	}
1214	
1215	/**
1216	 * Redirect to the specified url. NOTE that this function does not stop
1217	 * execution.
1218	 * 
1219	 * @param string $url URL to redirect to.
1220	 * @return void
1221	 * @author Anthony Bush
1222	 **/
1223	protected function redirect($url) {
1224		header('Location: ' . $url);
1225	}
1226	
1227	/**
1228	 * Execute code before every action.
1229	 * Override this in sub classes
1230	 *
1231	 * @return void
1232	 * @author Anthony Bush
1233	 **/
1234	protected function beforeAction() {
1235		
1236	}
1237	
1238	/**
1239	 * Execute code after every action.
1240	 * Override this in sub classes
1241	 *
1242	 * @return void
1243	 * @author Anthony Bush
1244	 **/
1245	protected function afterAction() {
1246		
1247	}
1248	
1249	/**
1250	 * Use this inside a controller action to get the output from another
1251	 * controller's action. By default, the layout functionality will be
1252	 * disabled for this "sub" action.
1253	 * 
1254	 * Example Usage:
1255	 * 
1256	 *     $enrollmentVerifyBox = $this->requestAction('enrollment_verify', array(), 'eligibility');
1257	 * 
1258	 * @param string $actionName name of action to invoke.
1259	 * @param array $actionParams parameters to invoke the action with.
1260	 * @param string $controllerName optional controller name. Current controller will be used if not specified.
1261	 * @param array $controllerParams optional controller params. Current controller params will be passed on if not specified.
1262	 * @param string $layout optional layout to force for the sub action.
1263	 * @return string output from requested controller's action.
1264	 * @throws Lvc_Exception
1265	 * @author Anthony Bush
1266	 **/
1267	protected function requestAction($actionName, $actionParams = array(), $controllerName = null, $controllerParams = null, $layout = null) {
1268		if (empty($controllerName)) {
1269			$controllerName = $this->controllerName;
1270		}
1271		if (is_null($controllerParams)) {
1272			$controllerParams = $this->params;
1273		}
1274		$controller = Lvc_Config::getController($controllerName);
1275		if (is_null($controller)) {
1276			throw new Lvc_Exception('Unable to load controller "' . $controllerName . '"');
1277		}
1278		$controller->setControllerParams($controllerParams);
1279		$controller->setLayoutOverride($layout);
1280		return $controller->getActionOutput($actionName, $actionParams);
1281	}
1282	
1283	/**
1284	 * Get the controller name. Mostly used internally...
1285	 *
1286	 * @return string controller name
1287	 * @author Anthony Bush
1288	 **/
1289	public function getControllerName() {
1290		return $this->controllerName;
1291	}
1292	
1293	/**
1294	 * Get the controller params. Mostly used internally...
1295	 *
1296	 * @return array controller params
1297	 * @author Anthony Bush
1298	 **/
1299	public function getControllerParams() {
1300		return $this->params;
1301	}
1302}
1303
1304/**
1305 * A View can be outputted or have its output returned (i.e. it's renderable).
1306 * It can not be executed.
1307 * 
1308 * $inc = new Lvc_View('foo.php', array());
1309 * $inc->output();
1310 * $output = $inc->getOutput();
1311 * 
1312 * @package lightvc
1313 * @author Anthony Bush
1314 * @since 2007-04-20
1315 **/
1316class Lvc_View {
1317	/**
1318	 * Full path to file name to be included.
1319	 *
1320	 * @var string
1321	 **/
1322	protected $fileName;
1323	
1324	/**
1325	 * Data to be exposed to the view template file.
1326	 *
1327	 * @var array
1328	 **/
1329	protected $data;
1330	
1331	/**
1332	 * A reference to the parent controller
1333	 *
1334	 * @var Lvc_Controller
1335	 **/
1336	protected $controller;
1337	
1338	/**
1339	 * Construct a view to be rendered.
1340	 *
1341	 * @param string $fileName Full path to file name of the view template file.
1342	 * @param array $data an array of [varName] => [value] pairs. Each varName will be made available to the view.
1343	 * @return void
1344	 * @author Anthony Bush
1345	 **/
1346	public function __construct($fileName, &$data) {
1347		$this->fileName = $fileName;
1348		$this->data = $data;
1349	}
1350	
1351	/**
1352	 * Output the view (aka render).
1353	 *
1354	 * @return void
1355	 * @author Anthony Bush
1356	 **/
1357	public function output() {
1358		extract($this->data, EXTR_SKIP);
1359		include($this->fileName);
1360	}
1361	
1362	/**
1363	 * Return the output of the view.
1364	 *
1365	 * @return string output of view
1366	 * @author Anthony Bush
1367	 **/
1368	public function getOutput() {
1369		ob_start();
1370		$this->output();
1371		return ob_get_clean();
1372	}
1373	
1374	/**
1375	 * Render a sub element from within a view.
1376	 * 
1377	 * Views are not allowed to have business logic, but they can call upon
1378	 * other generic, shared, views, called elements here.
1379	 * 
1380	 * @param string $elementName name of element to render
1381	 * @param array $data optional data to pass to the element.
1382	 * @return void
1383	 * @throws Lvc_Exception
1384	 * @author Anthony Bush
1385	 **/
1386	protected function renderElement($elementName, $data = array()) {
1387		$view = Lvc_Config::getElementView($elementName, $data);
1388		if (!is_null($view)) {
1389			$view->setController($this->controller);
1390			$view->output();
1391		} else {
1392			error_log('Unable to render element "' . $elementName . '"');
1393			// throw new Lvc_Exception('Unable to render element "' . $elementName . '"');
1394		}
1395	}
1396	
1397	/**
1398	 * Set the controller when constructing a view if you want {@link setLayoutVar()}
1399	 * to be callable from a view.
1400	 *
1401	 * @return void
1402	 * @author Anthony Bush
1403	 * @since 2007-05-17
1404	 **/
1405	public function setController($controller) {
1406		$this->controller = $controller;
1407	}
1408	
1409	/**
1410	 * Set a variable for the layout file.  You can set the page title from a static
1411	 * page's view file this way.
1412	 * 
1413	 * @param $varName variable name to make available in the view
1414	 * @param $value value of the variable.
1415	 * @return void
1416	 * @author Anthony Bush
1417	 * @since 2007-05-17
1418	 **/
1419	public function setLayoutVar($varName, $value) {
1420		$this->controller->setLayoutVar($varName, $value);
1421	}
1422}
1423
1424?>