PageRenderTime 8ms CodeModel.GetById 19ms app.highlight 32ms RepoModel.GetById 21ms app.codeStats 0ms

/modules/lightvc.php

https://github.com/conantp/OpenBeerDB-API
PHP | 1425 lines | 588 code | 124 blank | 713 comment | 79 complexity | 7f1b035f0948ab0b5a002410e9161550 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 = 'list';
  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			
 810			// Give routers a chance to (re)-route the request.
 811			foreach ($this->routers as $router) {
 812				if ($router->route($request)) {
 813					break;
 814				}
 815			}
 816
 817			// If controller name or action name are not set, set them to default.
 818			$controllerName = $request->getControllerName();
 819			if (empty($controllerName)) {
 820				$controllerName = Lvc_Config::getDefaultControllerName();
 821				$actionName     = Lvc_Config::getDefaultControllerActionName();
 822				$actionParams = $request->getActionParams() + Lvc_Config::getDefaultControllerActionParams();
 823				$request->setActionParams($actionParams);
 824			} else {
 825				$actionName = $request->getActionName();
 826				if (empty($actionName)) {
 827					$actionName   = Lvc_Config::getDefaultActionName();
 828				}
 829			}
 830
 831			$controller = Lvc_Config::getController($controllerName);
 832			if (is_null($controller)) {
 833				throw new Lvc_Exception('Unable to load controller "' . $controllerName . '"');
 834			}
 835			$controller->setControllerParams($request->getControllerParams());
 836			$controller->runAction($actionName, $request->getActionParams());
 837		}
 838		catch (Lvc_Exception $e)
 839		{
 840			// Catch exceptions and append additional error info if the request object has anything to say.
 841			$moreInfo = $request->getAdditionalErrorInfo();
 842			if (!empty($moreInfo)) {
 843				throw new Lvc_Exception($e->getMessage() . '. ' . $moreInfo);
 844			} else {
 845				throw $e;
 846			}
 847		}
 848	}
 849}
 850
 851/**
 852 * The base class that all other PageControllers should extend. Depending on the setup,
 853 * you might want an AppController to extend this one, and then have all your controllers
 854 * extend your AppController.
 855 * 
 856 * @package lightvc
 857 * @author Anthony Bush
 858 * @todo Finish up documentation in here...
 859 * @since 2007-04-20
 860 **/
 861class Lvc_PageController {
 862	/**
 863	 * Params is typically a combination of:
 864	 *     _GET (stored in params['get'])
 865	 *     _POST (stored in params['post'])
 866	 *     _FILE (also stored in params['post'])
 867	 *
 868	 * @var array
 869	 **/
 870	protected $params = array();
 871	
 872	/**
 873	 * A reference to $params['post']['data'], typically a combination of:
 874	 *     _POST['data'] (usually holds [Model][field])
 875	 *     _FILE['data'] (usually holds [key][Model][field], but the request object should remap it to [Model][field][key])
 876	 *
 877	 * @var array
 878	 **/
 879	protected $postData = array();
 880	
 881	/**
 882	 * Reference to post data (i.e. $this->params['post'])
 883	 * 
 884	 * @var array
 885	 **/
 886	protected $post = array();
 887	
 888	/**
 889	 * Reference to get data (i.e. $this->params['get'])
 890	 * 
 891	 * @var array
 892	 **/
 893	protected $get = array();
 894	
 895	/**
 896	 * Controller Name (e.g. controller_name, not ControllerNameController)
 897	 *
 898	 * @var string
 899	 **/
 900	protected $controllerName = null;
 901	
 902	/**
 903	 * Action Name (e.g. action_name, not actionActionName)
 904	 *
 905	 * @var string
 906	 **/
 907	protected $actionName = null;
 908	
 909	/**
 910	 * Variables we will pass to the view.
 911	 *
 912	 * @var array()
 913	 **/
 914	protected $viewVars = array();
 915	
 916	/**
 917	 * Have we loaded the view yet?
 918	 *
 919	 * @var boolean
 920	 **/
 921	protected $hasLoadedView = false;
 922	
 923	/**
 924	 * Specifies whether or not to load the default view for the action. If the
 925	 * action should not render any view, set it to false in the sub controller.
 926	 *
 927	 * @var boolean
 928	 **/
 929	protected $loadDefaultView = true;
 930	
 931	/**
 932	 * Don't set this yourself. It's used internally by parent controller /
 933	 * actions to determine whether or not to use the layout value in
 934	 * $layoutOverride rather than in $layout when requesting a sub action.
 935	 * 
 936	 * @var string
 937	 * @see setLayoutOverride(), $layoutOverride
 938	 **/
 939	protected $useLayoutOverride = false;
 940	
 941	/**
 942	 * Don't set this yourself. It's used internally by parent controller /
 943	 * actions to determine which layout to use when requesting a sub action.
 944	 * 
 945	 * @var string
 946	 * @see setLayoutOverride(), $useLayoutOverride
 947	 **/
 948	protected $layoutOverride = null;
 949	
 950	/**
 951	 * Set this in your controller to use a layout.
 952	 *
 953	 * @var string
 954	 **/
 955	protected $layout = null;
 956	
 957	/**
 958	 * An array of view variables specifically for the layout file.
 959	 *
 960	 * @var array
 961	 **/
 962	protected $layoutVars = array();
 963	
 964	/**
 965	 * Set the parameters of the controller.
 966	 * Actions will get their parameters through params['get'].
 967	 * Actions can access the post data as needed.
 968	 * 
 969	 * @param array $params an array of [paramName] => [paramValue] pairs
 970	 * @return void
 971	 * @author Anthony Bush
 972	 **/
 973	public function setControllerParams(&$params) {
 974		$this->params = $params;
 975		// Make a reference to the form data so we can get to it easier.
 976		if (isset($this->params['post']['data'])) {
 977			$this->postData =& $this->params['post']['data'];
 978		}
 979		if (isset($this->params['post'])) {
 980			$this->post =& $this->params['post'];
 981		}
 982		if (isset($this->params['get'])) {
 983			$this->get =& $this->params['get'];
 984		}
 985	}
 986	
 987	/**
 988	 * Don't call this yourself. It's used internally when creating new
 989	 * controllers so the controllers are aware of their name without
 990	 * needing any help from a user setting a member variable or from some
 991	 * reflector class.
 992	 *
 993	 * @return void
 994	 * @author Anthony Bush
 995	 **/
 996	public function setControllerName($controllerName) {
 997		$this->controllerName = $controllerName;
 998	}
 999	
1000	/**
1001	 * Set a variable for the view to use.
1002	 * 
1003	 * @param string $varName variable name to make available in the view
1004	 * @param $value value of the variable.
1005	 * @return void
1006	 * @author Anthony Bush
1007	 **/
1008	public function setVar($varName, $value) {
1009		$this->viewVars[$varName] = $value;
1010	}
1011	
1012	/**
1013	 * Set variables for the view in masse.
1014	 * 
1015	 * @param $varArray an array of [varName] => [value] pairs.
1016	 * @return void
1017	 * @author Anthony Bush
1018	 **/
1019	public function setVars(&$varArray) {
1020		$this->viewVars = $varArray + $this->viewVars;
1021	}
1022	
1023	/**
1024	 * Get the current value for a view variable.
1025	 * 
1026	 * @param string $varName
1027	 * @return mixed
1028	 * @author Anthony Bush
1029	 * @since 2007-11-13
1030	 **/
1031	public function getVar($varName) {
1032		if (isset($this->viewVars[$varName])) {
1033			return $this->viewVars[$varName];
1034		} else {
1035			return null;
1036		}
1037	}
1038	
1039	/**
1040	 * Set a variable for the layout view.
1041	 *
1042	 * @param $varName variable name to make available in the view
1043	 * @param $value value of the variable.
1044	 * @return void
1045	 * @author Anthony Bush
1046	 * @since 2007-05-17
1047	 **/
1048	public function setLayoutVar($varName, $value) {
1049		$this->layoutVars[$varName] = $value;
1050	}
1051	
1052	/**
1053	 * Get the current value for a layout variable.
1054	 * 
1055	 * @param string $varName
1056	 * @return mixed
1057	 * @author Anthony Bush
1058	 * @since 2007-11-13
1059	 **/
1060	public function getLayoutVar($varName) {
1061		if (isset($this->layoutVars[$varName])) {
1062			return $this->layoutVars[$varName];
1063		} else {
1064			return null;
1065		}
1066	}	
1067	
1068	/**
1069	 * Set the layout to use for the view.
1070	 * 
1071	 * @return void
1072	 * @author Anthony Bush
1073	 **/
1074	public function setLayout($layout) {
1075		$this->layout = $layout;
1076	}
1077	
1078	/**
1079	 * Don't call this yourself. It's used internally when requesting sub
1080	 * actions in order to avoid loading the layout multiple times.
1081	 *
1082	 * @return void
1083	 * @see $useLayoutOverride, $layoutOverride
1084	 * @author Anthony Bush
1085	 **/
1086	public function setLayoutOverride($layout) {
1087		$this->useLayoutOverride = true;
1088		$this->layoutOverride = $layout;
1089	}
1090	
1091	/**
1092	 * Returns the action name of this controller
1093	 *
1094	 * @return string
1095	 * @author lzhang
1096	 **/
1097	public function getActionName()
1098	{
1099		return $this->actionName;
1100	}
1101	
1102	/**
1103	 * Determine whether or not the the controller has the specified action.
1104	 * 
1105	 * @param string $actionName the action name to check for.
1106	 * @return boolean
1107	 * @author Anthony Bush
1108	 **/
1109	public function hasAction($actionName) {
1110		if (method_exists($this, Lvc_Config::getActionFunctionName($actionName))) {
1111			return true;
1112		} else {
1113			return false;
1114		}
1115	}
1116	
1117	/**
1118	 * Runs the requested action and returns the output from it.
1119	 * 
1120	 * Typically called by the FrontController.
1121	 * 
1122	 * @param string $actionName the action name to run.
1123	 * @param array $actionParams the parameters to pass to the action.
1124	 * @return string output from running the action.
1125	 * @author Anthony Bush
1126	 **/
1127	public function getActionOutput($actionName, &$actionParams = array()) {
1128		ob_start();
1129		$this->runAction($actionName, $actionParams);
1130		return ob_get_clean();
1131	}
1132	
1133	/**
1134	 * Runs the requested action and outputs its results.
1135	 * 
1136	 * Typically called by the FrontController.
1137	 * 
1138	 * @param string $actionName the action name to run.
1139	 * @param array $actionParams the parameters to pass to the action.
1140	 * @return void
1141	 * @throws Lvc_Exception
1142	 * @author Anthony Bush
1143	 **/
1144	public function runAction($actionName, &$actionParams = array()) {
1145		$this->actionName = $actionName;
1146		$func = Lvc_Config::getActionFunctionName($actionName);
1147		if (method_exists($this, $func)) {
1148			$this->beforeAction();
1149			
1150			// Call the action
1151			if (Lvc_Config::getSendActionParamsAsArray()) {
1152				$this->$func($actionParams);
1153			} else {
1154				call_user_func_array(array($this, $func), $actionParams);
1155			}
1156			
1157			// Load the view
1158			if ( ! $this->hasLoadedView && $this->loadDefaultView) {
1159				$this->loadView($this->controllerName . '/' . $actionName);
1160			}
1161			
1162			$this->afterAction();
1163			return true;
1164		} else {
1165			throw new Lvc_Exception('No action `' . $actionName . '`. Write the `' . $func . '` method');
1166		}
1167	}
1168	
1169	/**
1170	 * Load the requested controller view.
1171	 * 
1172	 * For example, you can load another view in your controller with:
1173	 * 
1174	 *     $this->loadView($this->getControllerName() . '/some_other_action');
1175	 * 
1176	 * Or some other controller with:
1177	 *
1178	 *     $this->loadView('some_other_controller/some_other_action');
1179	 * 
1180	 * Remember, the view for your action will be rendered automatically.
1181	 * 
1182	 * @param string $controllerViewName 'controller_name/action_name' format.
1183	 * @return void
1184	 * @throws Lvc_Exception
1185	 * @author Anthony Bush
1186	 **/
1187	protected function loadView($controllerViewName) {
1188		
1189		$view = Lvc_Config::getControllerView($controllerViewName, $this->viewVars);
1190		if (is_null($view)) {
1191			throw new Lvc_Exception('Unable to load controller view "' . $controllerViewName . '" for controller "' . $this->controllerName . '"');
1192		} else {
1193			$view->setController($this);
1194			$viewContents = $view->getOutput();
1195		}
1196		
1197		if ($this->useLayoutOverride) {
1198			$this->layout = $this->layoutOverride;
1199		}
1200		if ( ! empty($this->layout)) {
1201			// Use an explicit name for this data so we don't override some other variable...
1202			$this->layoutVars[Lvc_Config::getLayoutContentVarName()] = $viewContents;
1203			$layoutView = Lvc_Config::getLayoutView($this->layout, $this->layoutVars);
1204			if (is_null($layoutView)) {
1205				throw new Lvc_Exception('Unable to load layout view "' . $this->layout . '" for controller "' . $this->controllerName . '"');
1206			} else {
1207				$layoutView->setController($this);
1208				$layoutView->output();
1209			}
1210		} else {
1211			echo($viewContents);
1212		}
1213		$this->hasLoadedView = true;
1214	}
1215	
1216	/**
1217	 * Redirect to the specified url. NOTE that this function does not stop
1218	 * execution.
1219	 * 
1220	 * @param string $url URL to redirect to.
1221	 * @return void
1222	 * @author Anthony Bush
1223	 **/
1224	protected function redirect($url) {
1225		header('Location: ' . $url);
1226	}
1227	
1228	/**
1229	 * Execute code before every action.
1230	 * Override this in sub classes
1231	 *
1232	 * @return void
1233	 * @author Anthony Bush
1234	 **/
1235	protected function beforeAction() {
1236		
1237	}
1238	
1239	/**
1240	 * Execute code after every action.
1241	 * Override this in sub classes
1242	 *
1243	 * @return void
1244	 * @author Anthony Bush
1245	 **/
1246	protected function afterAction() {
1247		
1248	}
1249	
1250	/**
1251	 * Use this inside a controller action to get the output from another
1252	 * controller's action. By default, the layout functionality will be
1253	 * disabled for this "sub" action.
1254	 * 
1255	 * Example Usage:
1256	 * 
1257	 *     $enrollmentVerifyBox = $this->requestAction('enrollment_verify', array(), 'eligibility');
1258	 * 
1259	 * @param string $actionName name of action to invoke.
1260	 * @param array $actionParams parameters to invoke the action with.
1261	 * @param string $controllerName optional controller name. Current controller will be used if not specified.
1262	 * @param array $controllerParams optional controller params. Current controller params will be passed on if not specified.
1263	 * @param string $layout optional layout to force for the sub action.
1264	 * @return string output from requested controller's action.
1265	 * @throws Lvc_Exception
1266	 * @author Anthony Bush
1267	 **/
1268	protected function requestAction($actionName, $actionParams = array(), $controllerName = null, $controllerParams = null, $layout = null) {
1269		if (empty($controllerName)) {
1270			$controllerName = $this->controllerName;
1271		}
1272		if (is_null($controllerParams)) {
1273			$controllerParams = $this->params;
1274		}
1275		$controller = Lvc_Config::getController($controllerName);
1276		if (is_null($controller)) {
1277			throw new Lvc_Exception('Unable to load controller "' . $controllerName . '"');
1278		}
1279		$controller->setControllerParams($controllerParams);
1280		$controller->setLayoutOverride($layout);
1281		return $controller->getActionOutput($actionName, $actionParams);
1282	}
1283	
1284	/**
1285	 * Get the controller name. Mostly used internally...
1286	 *
1287	 * @return string controller name
1288	 * @author Anthony Bush
1289	 **/
1290	public function getControllerName() {
1291		return $this->controllerName;
1292	}
1293	
1294	/**
1295	 * Get the controller params. Mostly used internally...
1296	 *
1297	 * @return array controller params
1298	 * @author Anthony Bush
1299	 **/
1300	public function getControllerParams() {
1301		return $this->params;
1302	}
1303}
1304
1305/**
1306 * A View can be outputted or have its output returned (i.e. it's renderable).
1307 * It can not be executed.
1308 * 
1309 * $inc = new Lvc_View('foo.php', array());
1310 * $inc->output();
1311 * $output = $inc->getOutput();
1312 * 
1313 * @package lightvc
1314 * @author Anthony Bush
1315 * @since 2007-04-20
1316 **/
1317class Lvc_View {
1318	/**
1319	 * Full path to file name to be included.
1320	 *
1321	 * @var string
1322	 **/
1323	protected $fileName;
1324	
1325	/**
1326	 * Data to be exposed to the view template file.
1327	 *
1328	 * @var array
1329	 **/
1330	protected $data;
1331	
1332	/**
1333	 * A reference to the parent controller
1334	 *
1335	 * @var Lvc_Controller
1336	 **/
1337	protected $controller;
1338	
1339	/**
1340	 * Construct a view to be rendered.
1341	 *
1342	 * @param string $fileName Full path to file name of the view template file.
1343	 * @param array $data an array of [varName] => [value] pairs. Each varName will be made available to the view.
1344	 * @return void
1345	 * @author Anthony Bush
1346	 **/
1347	public function __construct($fileName, &$data) {
1348		$this->fileName = $fileName;
1349		$this->data = $data;
1350	}
1351	
1352	/**
1353	 * Output the view (aka render).
1354	 *
1355	 * @return void
1356	 * @author Anthony Bush
1357	 **/
1358	public function output() {
1359		extract($this->data, EXTR_SKIP);
1360		include($this->fileName);
1361	}
1362	
1363	/**
1364	 * Return the output of the view.
1365	 *
1366	 * @return string output of view
1367	 * @author Anthony Bush
1368	 **/
1369	public function getOutput() {
1370		ob_start();
1371		$this->output();
1372		return ob_get_clean();
1373	}
1374	
1375	/**
1376	 * Render a sub element from within a view.
1377	 * 
1378	 * Views are not allowed to have business logic, but they can call upon
1379	 * other generic, shared, views, called elements here.
1380	 * 
1381	 * @param string $elementName name of element to render
1382	 * @param array $data optional data to pass to the element.
1383	 * @return void
1384	 * @throws Lvc_Exception
1385	 * @author Anthony Bush
1386	 **/
1387	protected function renderElement($elementName, $data = array()) {
1388		$view = Lvc_Config::getElementView($elementName, $data);
1389		if (!is_null($view)) {
1390			$view->setController($this->controller);
1391			$view->output();
1392		} else {
1393			error_log('Unable to render element "' . $elementName . '"');
1394			// throw new Lvc_Exception('Unable to render element "' . $elementName . '"');
1395		}
1396	}
1397	
1398	/**
1399	 * Set the controller when constructing a view if you want {@link setLayoutVar()}
1400	 * to be callable from a view.
1401	 *
1402	 * @return void
1403	 * @author Anthony Bush
1404	 * @since 2007-05-17
1405	 **/
1406	public function setController($controller) {
1407		$this->controller = $controller;
1408	}
1409	
1410	/**
1411	 * Set a variable for the layout file.  You can set the page title from a static
1412	 * page's view file this way.
1413	 * 
1414	 * @param $varName variable name to make available in the view
1415	 * @param $value value of the variable.
1416	 * @return void
1417	 * @author Anthony Bush
1418	 * @since 2007-05-17
1419	 **/
1420	public function setLayoutVar($varName, $value) {
1421		$this->controller->setLayoutVar($varName, $value);
1422	}
1423}
1424
1425?>