Cgiapp2
[ class tree: Cgiapp2 ] [ index: Cgiapp2 ] [ all elements ]

Source for file FrontController.class.php

Documentation is available at FrontController.class.php

  1. <?php
  2. /**
  3. * Cgiapp2 - Framework for building reusable web-applications
  4. *
  5. * A PHP5 port of perl's CGI::Application, a framework for building reusable web
  6. * applications.
  7. *
  8. * @package Cgiapp2
  9. * @author Matthew Weier O'Phinney <mweierophinney@gmail.com>; based on
  10. * CGI::Application, by Jesse Erlbaum <jesse@erlbaum.net>, et. al.
  11. * @copyright (c) 2004 - present, Matthew Weier O'Phinney
  12. * @license BSD License (http://www.opensource.org/licenses/bsd-license.php)
  13. * @category Tools and Utilities
  14. * @tutorial Cgiapp2/Cgiapp2.cls
  15. * @version $Id:$
  16. */
  17.  
  18. /**
  19. * Extends Cgiapp2
  20. */
  21. require_once 'Cgiapp2.class.php';
  22.  
  23. /**
  24. * Cgiapp2_FrontController
  25. *
  26. * Basic front controller structure that builds the dispatch table based on
  27. * classes that are attached to the controller via {@link attach()}. Any class
  28. * attached in such a manner then has all public static methods available as
  29. * 'views' of that class. Such methods should accept a Cgiapp2_FrontController
  30. * object (basically a Cgiapp2 object) as their sole argument.
  31. *
  32. * Any return values from these static methods are then passed to
  33. * {@link tmpl_assign()} (note: you should probably return an associative
  34. * array), and the template named in the style 'namepace/view.tpl' is loaded
  35. * with those values and returned to the controller.
  36. *
  37. * A basic instance script might then look like:
  38. * <code>
  39. * require_once 'Cgiapp2/FrontController.php';
  40. *
  41. * $controller = new Cgiapp2_FrontController(array(
  42. * 'TMPL_PATH' => '/path/to/templates',
  43. * ));
  44. *
  45. * // Try to load dispatch cache file
  46. * if (!$controller->attachFromFile('/path/to/dispatch.cache')) {
  47. * // Load required classes
  48. * require_once 'Views/Gallery.php';
  49. * require_once 'Views/Wiki.php';
  50. * require_once 'Views/Contact.php';
  51. *
  52. * // Attach classes under namespaces and with default view methods
  53. * $controller->attach('Views_Gallery', 'gallery', 'album');
  54. * $controller->attach('Views_Wiki', 'wiki', 'page');
  55. * $controller->attach('Views_Contact', 'contact', 'form');
  56. *
  57. * // Create dispatch cache file
  58. * $controller->createHandlerFile('/path/to/dispatch.cache');
  59. * }
  60. *
  61. * // Run controller
  62. * $controller->run();
  63. * </code>
  64. *
  65. * A special 'page' namespace is reserved and initially set as the default
  66. * handler. This corresponds to the {@link page()} method, and simply loads
  67. * static page content from 'page/REQUESTED_VIEW.tpl'.
  68. *
  69. * If using a dispatch cache file (which is recommended), view classes should be
  70. * named such that an __autoload() function will load the class. An __autoload()
  71. * implementation has been provided that utilizes PEAR's naming scheme to find
  72. * classes.
  73. *
  74. * @package Cgiapp2
  75. * @version @release-version@
  76. */
  77. class Cgiapp2_FrontController extends Cgiapp2
  78. {
  79. /**
  80. * Array of handler methods. Each key is a 'namespace' that will appear in
  81. * the request URI, and points to an array with the keys 'views' (an
  82. * associative array of view => callback) and 'default' (the default view).
  83. * @var array
  84. * @access protected
  85. */
  86. protected $_handlers = array();
  87.  
  88. /**
  89. * Current method. The 'class' element of the namespace accessed via the
  90. * requested URI.
  91. * @var string
  92. * @access protected
  93. */
  94. protected $_currentMethod = null;
  95.  
  96. /**
  97. * Current view. The 'clasews' element of the namespace accessed via the
  98. * requested URI.
  99. * @var string
  100. * @access protected
  101. */
  102. protected $_currentView = null;
  103.  
  104. /**
  105. * Default namespace to use if none passed in URI.
  106. * @var string
  107. * @access protected
  108. */
  109. protected $DEFAULT_METHOD;
  110.  
  111. /**
  112. * Default view template to use for static page views; defaults to 'index'.
  113. * @var string
  114. * @access protected
  115. */
  116. protected $DEFAULT_PAGE_VIEW;
  117.  
  118. /**
  119. * Index within PATH_INFO/REQUEST_URI where method may be found. Defaults to
  120. * 0.
  121. * @var int
  122. * @access protected
  123. */
  124. protected $METHOD_INDEX;
  125.  
  126. /**
  127. * Index within PATH_INFO/REQUEST_URI where view may be found. Defaults to
  128. * 1.
  129. * @var int
  130. * @access protected
  131. */
  132. protected $VIEW_INDEX;
  133.  
  134. /**
  135. * Setup
  136. *
  137. * @access public
  138. */
  139. public function setup()
  140. {
  141. $this->run_modes(array('handler', 'page'), true);
  142. $this->start_mode('handler');
  143. $this->mode_param('set_mode');
  144.  
  145. // Set default method to 'page' if not set by caller
  146. if (!$this->param('default_method')) {
  147. $this->param('default_method', 'page');
  148. }
  149.  
  150. // Set the default page view if not set by caller
  151. if (!$this->param('default_page_view')) {
  152. $this->param('default_page_view', 'index');
  153. }
  154. }
  155.  
  156. /**
  157. * Attach a class as a handler
  158. *
  159. * Attaches a class as a handler under $namespace. $defaultView indicates
  160. * the default method to use when no view is provided.
  161. *
  162. * Throws an exception if the class is not found, or the view does not exist
  163. * in it.
  164. *
  165. * @access public
  166. * @param string $class
  167. * @param string $namespace
  168. * @param string $defaultView
  169. * @return void
  170. */
  171. public function attach($class, $namespace, $defaultView)
  172. {
  173. // Handle this class differently
  174. if ($class == __CLASS__) {
  175. // Do nothing!
  176. return true;
  177. }
  178.  
  179. // Get reflection object
  180. if (is_string($class) && class_exists($class)) {
  181. $reflection = new ReflectionClass($class);
  182. } else {
  183. throw new Cgiapp2_Exception('Invalid handler');
  184. }
  185.  
  186. $defaultExists = false;
  187. $views = array();
  188. foreach ($reflection->getMethods() as $method) {
  189. if ($method->isPublic() && $method->isStatic()) {
  190. // Create callback
  191. $methodName = $method->getName();
  192. $views[$methodName] = array($class, $methodName);
  193.  
  194. if ($defaultView == $methodName) {
  195. $defaultExists = true;
  196. }
  197. }
  198. }
  199.  
  200. if (!$defaultExists) {
  201. throw new Cgiapp2_Exception("Default view does not exist: $class::$defaultView");
  202. }
  203.  
  204. $this->_handlers[$namespace] = array(
  205. 'class' => $class,
  206. 'default' => $defaultView,
  207. 'views' => $views
  208. );
  209.  
  210. return true;
  211. }
  212.  
  213. /**
  214. * Attaches handlers from a cached file
  215. *
  216. * Returns a boolean, indicating success or failure. False is also returned
  217. * if no elements are found in {@link $_handlers} after loading from the
  218. * file.
  219. *
  220. * @access public
  221. * @param string $file
  222. * @return bool
  223. */
  224. public function attachFromFile($file)
  225. {
  226. if (!file_exists($file) || !is_readable($file)) {
  227. return false;
  228. }
  229.  
  230. if (false === ($cached = @file_get_contents($file))) {
  231. return false;
  232. }
  233.  
  234. $handlers = @unserialize($cached);
  235.  
  236. foreach ($handlers as $method => $info) {
  237. $this->_handlers[$method] = $info;
  238. }
  239.  
  240. return true;
  241. }
  242.  
  243. /**
  244. * Set the default handler
  245. *
  246. * Sets the default handler to $method, assuming $method exists.
  247. *
  248. * @access public
  249. * @param string $method
  250. * @return bool
  251. * @throws Cgiapp2_Exception
  252. */
  253. public function setDefaultHandler($method)
  254. {
  255. if (!isset($this->_handlers[$method])) {
  256. throw new Cgiapp2_Exception('Cannot set default method; does not exist');
  257. }
  258.  
  259. $this->param('default_method', $method);
  260.  
  261. return true;
  262. }
  263.  
  264. /**
  265. * Creates a handler cache file
  266. *
  267. * Creates a handler cache file at $file from the available handlers in
  268. * {@link $_handlers}. Returns boolean success or failure.
  269. *
  270. * @access public
  271. * @param string $file
  272. * @return bool
  273. */
  274. public function createHandlerFile($file)
  275. {
  276. if (!file_exists($file) && !is_writable(dirname($file))) {
  277. return false;
  278. }
  279.  
  280. // Store
  281. if (0 === @file_put_contents($file, serialize($this->_handlers))) {
  282. return false;
  283. }
  284.  
  285. return true;
  286. }
  287.  
  288. /**
  289. * Set run mode
  290. *
  291. * Sets run mode to {@link handler()} for all requests. In addition, sets
  292. * the values for {@link $_currentMethod} and {@link $_currentView} based on
  293. * the {@link path_info() PATH_INFO} segments, setting to default values for
  294. * each if not found.
  295. *
  296. * {@link $_currentMethod} is set to the 'class' element of the
  297. * corresponding {$link $_handlers} element. {@link $_currentView} is set to
  298. * the view requested.
  299. *
  300. * If the {@link $DEFAULT_METHOD} is 'page', it returns 'page' as the run
  301. * mode, passing execution on to {@link page()}. The {@link $_currentView}
  302. * is defined from the path, or from {@link $DEFAULT_PAGE_VIEW}.
  303. *
  304. * @access protected
  305. * @return string
  306. */
  307. protected function set_mode()
  308. {
  309. $path = $this->path_info();
  310.  
  311. if (!$methodIndex = $this->param('method_index')) {
  312. $methodIndex = 0;
  313. }
  314.  
  315. if (false === ($viewIndex = $this->param('page_index'))) {
  316. $viewIndex = 1;
  317. }
  318.  
  319. // Get method
  320. $method = $this->param('default_method');
  321. if (!empty($path) && isset($path[$methodIndex])) {
  322. $try = $path[$methodIndex];
  323. if (isset($this->_handlers[$try])) {
  324. $method = $try;
  325. }
  326. }
  327.  
  328. // Handle 'page' requests
  329. if ('page' == $method) {
  330. // Page views are handled differently.
  331. $view = $this->param('default_page_view');
  332. if (isset($path[$viewIndex])) {
  333. $view = $path[$viewIndex];
  334. }
  335.  
  336. $this->_currentMethod = 'page';
  337. $this->_currentView = $view;
  338.  
  339. // Use page() for run mode
  340. return 'page';
  341. }
  342.  
  343. // All other requests
  344. // Get view
  345. $view = $this->_handlers[$method]['default'];
  346. if (isset($path[$viewIndex])
  347. && ('page' != $method)
  348. && isset($this->_handlers[$method]['views'][$path[$viewIndex]]))
  349. {
  350. $view = $path[$viewIndex];
  351. }
  352.  
  353. // Set current method and view
  354. $this->_currentMethod = $method;
  355. $this->_currentView = $view;
  356.  
  357. // Use handler for run mode
  358. return 'handler';
  359. }
  360.  
  361. /**
  362. * Return the current method
  363. *
  364. * @access public
  365. * @return string
  366. */
  367. public function getMethod()
  368. {
  369. return $this->_currentMethod;
  370. }
  371.  
  372. /**
  373. * Return the current view
  374. *
  375. * @access public
  376. * @return string
  377. */
  378. public function getView()
  379. {
  380. return $this->_currentView;
  381. }
  382.  
  383. /**
  384. * Forwards request to different controller and view
  385. *
  386. * If $method is null, defaults to the {@link $DEFAULT_METHOD}. If $view is
  387. * null or not a view in the selected $method, the default view for that
  388. * method will be used. If $method is 'page', and no $view is provided, the
  389. * view selected will be {@link $DEFAULT_PAGE_VIEW}.
  390. *
  391. * When done, the values for {@link $_currentMethod} and
  392. * {@link $currentView} are reset.
  393. *
  394. * @access public
  395. * @param string $method
  396. * @param string $view
  397. * @return void
  398. */
  399. public function forward($method = null, $view = null)
  400. {
  401. // Set method
  402. if ((null === $method) || !isset($this->_handlers[$method])) {
  403. $method = $this->param('default_method');
  404. }
  405.  
  406. // Handle 'page' requests
  407. if ((null === $view) && ('page' == $method)) {
  408. // Page views are handled differently.
  409. $view = $this->param('default_page_view');
  410. } elseif ((null === $view)
  411. || !isset($this->_handlers[$method]['views'][$view]))
  412. {
  413. $view = $this->_handlers[$method]['default'];
  414. }
  415.  
  416. // Set current method and view
  417. $this->_currentMethod = $method;
  418. $this->_currentView = $view;
  419. }
  420.  
  421. /**
  422. * Change the view
  423. *
  424. * Allows a handler to change the view; particularly useful for changing the
  425. * template used from a handler.
  426. *
  427. * @access public
  428. * @param string $view
  429. */
  430. public function setView($view)
  431. {
  432. $this->_currentView = $view;
  433. }
  434.  
  435. /**
  436. * Dispatch handler
  437. *
  438. * Dispatches by calling {@link $_currentMethod}::{@link $_currentView}; the
  439. * return value of that callback is then returned.
  440. *
  441. * @access public
  442. * @return string
  443. */
  444. public function handler()
  445. {
  446. $method = $this->_currentMethod;
  447. if ('page' == $method) {
  448. return $this->page();
  449. }
  450.  
  451. $view = $this->_currentView;
  452.  
  453. return call_user_func($this->_handlers[$method]['views'][$view], $this);
  454. }
  455.  
  456. /**
  457. * Show a static page
  458. *
  459. * Displays a static page based on the view: page/{@link $_currentView}.tpl.
  460. *
  461. * @access public
  462. * @return string
  463. */
  464. public function page()
  465. {
  466. return $this->load_tmpl("page/{$this->_currentView}.tpl");
  467. }
  468. }
  469.  
  470. if (!function_exists('__autoload')) {
  471. /**
  472. * Autoload implementation
  473. *
  474. * Autoload class files based on PEAR naming conventions
  475. *
  476. * @access public
  477. * @param string
  478. */
  479. function __autoload($class)
  480. {
  481. // use pear functionality
  482. $file = str_replace('_', '/', $class) . '.php';
  483. if (! @include_once($file)) {
  484. throw new <a href="../Cgiapp2/Cgiapp2_Exception.html">Cgiapp2_Exception</a>('Unable to load class file ' . $file);
  485. }
  486. }

Documentation generated on Sat, 03 Jun 2006 10:48:42 -0400 by phpDocumentor 1.3.0RC5