Source for file Controller.php

Documentation is available at Controller.php

  1. <?php
  2.  
  3. /**
  4. * MVCnPHP - Controller.class.php
  5. *
  6. * This source file is subject to version 2.02 of the PHP license,
  7. * that is bundled with this package in the file LICENSE, and is
  8. * available at through the world-wide-web at
  9. * http://www.php.net/license/2_02.txt.
  10. * If you did not receive a copy of the PHP license and are unable to
  11. * obtain it through the world-wide-web, please send a note to
  12. * license@php.net so we can mail you a copy immediately.
  13. *
  14. * @author Tony Bibbs <tony@geeklog.net>
  15. * @copyright Tony Bibbs 2003
  16. * @package net.geeklog.mvc
  17. * @version $Id: Controller.class.php,v 1.6 2004/03/23 02:39:20 tony Exp $
  18. *
  19. */
  20.  
  21. /**
  22. * Constants used within this MVC framework
  23. */
  24. require_once 'Constants.php';
  25.  
  26. /**
  27. * Loader factory will create a configuration loader.
  28. * Right now we only support PHP arrays and XML files
  29. */
  30. require_once 'LoaderFactory.php';
  31.  
  32. /**
  33. * The controller part of the MVC
  34. *
  35. * This class controls processing for the program
  36. * indiscriminately. The focus is always on the underlying
  37. * views or commands. This class allows you to store application
  38. * messages such as status messages (e.g. something was saved
  39. * successfully) or error messages from failed validation or
  40. * security reasons. By default these are stored in the $_SESSION
  41. * superglobal in $_SESSION['MVC_MESSAGE'] and $_SESSION['MVC_ERRORS']
  42. * respectively (note: the later is an array). If your application is
  43. * not using PHP4 sessions you will want to follow the directions given
  44. * in the documentation for Controller::_clearMessages
  45. *
  46. * @author Tony Bibbs <tony@geeklog.net>
  47. * @package net.geeklog.mvc
  48. *
  49. */
  50. class MVCnPHP_Controller {
  51.  
  52. /**
  53. * @access private
  54. * @var int
  55. */
  56. private $allowedFormMethod = null;
  57. /**
  58. * @access private
  59. * @var array
  60. */
  61. private $request = null;
  62. /**
  63. * @access private
  64. * @var string
  65. */
  66. private $object = null;
  67. /**
  68. * @access private
  69. * @var object
  70. */
  71. private $mapLoader = null;
  72. /**
  73. * @access private
  74. * @var object
  75. */
  76. private $mapping = null;
  77. /**
  78. * @access private
  79. * @var string
  80. */
  81. private $mvcBase = null;
  82. /**
  83. * @access private
  84. * @var string
  85. */
  86. private $viewDir = null;
  87. /**
  88. * @access private
  89. * @var string
  90. */
  91. private $commandDir = null;
  92. /**
  93. * @access private
  94. * @var string
  95. */
  96. private $baseURL = null;
  97. /**
  98. * @access private
  99. * @var int
  100. */
  101. private $configType = null;
  102. /**
  103. * @access private
  104. * @var array
  105. */
  106. private $configData = null;
  107. /**
  108. * Constructor
  109. *
  110. * Loads MVC configuration data and sets up data needed for
  111. * further processing
  112. *
  113. * @author Tony Bibbs <tony@geeklog.net>
  114. * @access public
  115. * @param variant $configData Configuration data
  116. * @param string $configType Denotes type of config data structure, MVC_ARRAY or MVC_XML
  117. * @param string $allowedFormMethod Controller can accept MVC_GET, MVC_POST or MVC_BOTH
  118. *
  119. */
  120. public function __construct($configData, $configType = MVC_XML, $allowedFormMethod = MVC_BOTH)
  121. {
  122. $this->clearMessages();
  123. switch ($allowedFormMethod) {
  124. case MVC_GET:
  125. $this->request = &$_GET;
  126. break;
  127. case MVC_POST:
  128. $this->request = &$_POST;
  129. break;
  130. default:
  131. $this->request = &$_REQUEST;
  132. }
  133. if (!empty($this->request['cmd'])) {
  134. $this->object = $this->request['cmd'];
  135. } else {
  136. $this->object = 'default';
  137. }
  138. $this->configType = $configType;
  139. $this->configData = $configData;
  140. $this->allowedFormMethod = $allowedFormMethod;
  141. }
  142. /**
  143. * Sets location of the base MVC package so other MVC classes can be
  144. * loaded at a later time.
  145. *
  146. * @author Tony Bibbs
  147. * @access public
  148. * @param string $path Path to MVC base dir, must end with trailing slash
  149. * @return boolean True if view path is set, otherwise false
  150. *
  151. */
  152. public function setMVCBase($path)
  153. {
  154. if (!is_dir($path)) {
  155. throw new Exception('Bad path in Controller::setMVCBase');
  156. }
  157. $this->mvcBase = $path;
  158. return true;
  159. }
  160. /**
  161. * Sets location of views for this controller
  162. *
  163. * @author Tony Bibbs
  164. * @access public
  165. * @param string $path Path to view dir, must end with trailing slash
  166. * @return boolean True if view path is set, otherwise false
  167. *
  168. */
  169. public function setViewDir($path)
  170. {
  171. if (!is_dir($path)) {
  172. return false;
  173. }
  174. $this->viewDir = $path;
  175. return true;
  176. }
  177. /**
  178. * Sets location of commands for this controller
  179. *
  180. * @author Tony Bibbs
  181. * @access public
  182. * @param string $path Path to command dir, must end with trailing slash
  183. * @return boolean True if view path is set, otherwise false
  184. *
  185. */
  186. public function setCommandDir($path)
  187. {
  188. if (!is_dir($path)) {
  189. return false;
  190. }
  191. $this->commandDir = $path;
  192. return true;
  193. }
  194. /**
  195. * Sets base URL for this controller
  196. *
  197. * The base url allows all subsequent references to URL's to be relative
  198. * to the URL specified here
  199. *
  200. * @author Tony Bibbs
  201. * @access public
  202. * @param string $url Base URL
  203. *
  204. */
  205. public function setBaseURL($url)
  206. {
  207. $this->baseURL = $url;
  208. }
  209. /**
  210. * Process a request (or forward)
  211. *
  212. * NOTE this now supports the concept of a global forward for
  213. * descendants of the validator class. A global forward is a
  214. * slick way for validators to return control to the calling
  215. * command or view without having define a bunch of them in
  216. * the configuration file.
  217. *
  218. * @author Tony Bibbs <tony@geeklog.net>
  219. * @access public
  220. * @return string Optional, may return HTML string of view
  221. *
  222. */
  223. public function processRequest()
  224. {
  225. // Ensure user isn't submitted data via an illegal method
  226. $this->checkMethod();
  227. if (empty($this->mapping)) {
  228. $this->loadMapping($this->configType, $this->configData);
  229. }
  230. // We should have latest here a valid mapping object
  231. if (is_null($this->mapping->getName())) {
  232. throw new Exception('Unexpected return from MVC command or MVC view. Got no valid mapping object');
  233. }
  234.  
  235. if ($this->mapping->getType() == 'view') {
  236. require_once 'ViewFactory.php';
  237. try {
  238. $view = &MVCnPHP_ViewFactory::getView($this->mapping->getPath(), $this->mapping->getName());
  239. } catch (Exception $e) {
  240. $msg = $e->getMessage();
  241. // It is possible that the constructor for a view may want to do a forward
  242. if ($this->isForward($msg)) {
  243. $this->forwardControl($msg);
  244. unset($msg);
  245. }
  246. $tmpArray = explode(':', $msg);
  247. if ($tmpArray[0] == 'doForward') {
  248. $this->mapping = &$this->mapLoader->getMapping($tmpArray[1], $this->viewDir, $this->commandDir, $this->baseURL);
  249. $this->processRequest();
  250. exit;
  251. }
  252. throw $e;
  253. }
  254. // Got view object fine, now try rendering it
  255. $retval = $view->getView();
  256. // Check that we didn't receive any forwards at this point.
  257. if ($this->isForward($retval)) {
  258. $this->forwardControl($retval);
  259. unset($retval);
  260. }
  261. $tmpArray = explode(':', $retval);
  262. if ($tmpArray[0] == 'doForward') {
  263. $this->mapping = &$this->mapLoader->getMapping($tmpArray[1], $this->viewDir, $this->commandDir, $this->baseURL);
  264. $this->processRequest();
  265. exit;
  266. }
  267. if (!$view->printView()) {
  268. return $retval;
  269. }
  270. } else {
  271. require_once 'CommandFactory.php';
  272. try {
  273. $tmp = &MVCnPHP_CommandFactory::getCommand($this->mapping->getPath(), $this->mapping->getName());
  274. } catch (Exception $e) {
  275. $msg = $e->getMessage();
  276. if ($this->isForward($msg)) {
  277. $this->forwardControl($msg);
  278. unset($msg);
  279. }
  280. $tmpArray = explode(':', $msg);
  281. if ($tmpArray[0] == 'doForward') {
  282. $this->mapping = &$this->mapLoader->getMapping($tmpArray[1], $this->viewDir, $this->commandDir, $this->baseURL);
  283. $this->processRequest();
  284. exit;
  285. }
  286. throw $e;
  287. }
  288. // Must've got an actual command object, try running it
  289. $strForward = $tmp->execute();
  290. // Now check for a forward.
  291. if ($this->isForward($strForward)) {
  292. $this->forwardControl($strForward);
  293. unset($retval);
  294. }
  295.  
  296. $tmpArray = explode(':', $strForward);
  297. if ($tmpArray[0] == 'doForward') {
  298. $this->mapping = &$this->mapLoader->getMapping($tmpArray[1], $this->viewDir, $this->commandDir, $this->baseURL);
  299. $this->processRequest();
  300. exit;
  301. }
  302. // OK if we got here then we got an undefined forward of some kind
  303. //if (!empty($strForward)) {
  304. //print_r($tmp); print_r($_SESSION['forwards']); $_SESSION['forwards'] = ''; exit;
  305. //throw new Exception('Unexpected return from MVC command or MVC view. Got ' . $strForward);
  306. //}
  307. }
  308. }
  309. /**
  310. * Clears any existing errors or messages
  311. *
  312. * This method assumes the application is using PHP4 sessions
  313. * to store MVC errors and messages. If that is not the case,
  314. * the application should create a new class that extends this
  315. * controller and override this function to clear the messages
  316. * in the way desired.
  317. *
  318. * @author Tony Bibs <tony@geeklog.net>
  319. * @access private
  320. *
  321. */
  322. public function clearMessages()
  323. {
  324. unset($_REQUEST[MVC_MESSAGES]);
  325. unset($_REQUEST[MVC_ERRORS]);
  326. }
  327. /**
  328. * Ensures only supported methods are used
  329. *
  330. * @author Tony Bibbs <tony@geeklog.net>
  331. * @access private
  332. *
  333. */
  334. public function checkMethod()
  335. {
  336. switch ($this->allowedFormMethod) {
  337. case MVC_GET:
  338. if (count($_POST) > 0) {
  339. throw new Exception('POST method is not supported by this controller');
  340. }
  341. case MVC_POST:
  342. if (count($_GET) > 0) {
  343. throw new Exception('GET method is not supported by this controller');
  344. }
  345. }
  346. }
  347. /**
  348. * Loads the mapping data for a model or view
  349. *
  350. * This will load only the mapping data needed by the
  351. * current object.
  352. *
  353. * @author Tony Bibbs <tony@geeklog.net>
  354. * @access private
  355. * @param string $configType Dontes the type of config data structure
  356. * @param variant $configData this is the data or file pointer for config data
  357. *
  358. */
  359. public function loadMapping($configType, $configData)
  360. {
  361. if (empty($this->mapLoader)) {
  362. $this->mapLoader = &MVCnPHP_LoaderFactory::getLoader($configType);
  363. }
  364.  
  365. $this->mapping = $this->mapLoader->getMapping($this->object, $this->viewDir, $this->commandDir, $this->baseURL, $configData);
  366.  
  367. // Maybe, we've gotten an invalid mapping object, so we try to
  368. // load the default view
  369. if (is_null($this->mapping->getName())) {
  370. $this->object = 'default';
  371. // We can also call $this->loadMapping after we've set the object to 'default'
  372. // but this may end in an endless loop, so we try to load the default view only once
  373. $this->mapping = &$this->mapLoader->getMapping($this->object, $this->viewDir, $this->commandDir, $this->baseURL, $configData);
  374. }
  375. if (is_null($this->mapping)) {
  376. throw new Exception('Unabled to load a mapping in MVCnPHP_Controller::loadMapping');
  377. }
  378. }
  379. /**
  380. * Determines if string is a name of an existing forward.
  381. *
  382. * Because views can return the HTML string it won't be obvious
  383. * if the call to the view's getView() is a forward or actually
  384. * HTML. This function makes that determintation.
  385. *
  386. * @author Tony Bibbs <tony@geeklog.net>
  387. * @access private
  388. * @param string $name Name of forward to look for
  389. * @return boolean True if forward is found, otherwise false
  390. *
  391. */
  392. public function isForward($name)
  393. {
  394. if ($this->mapping->getForward($name)) {
  395. return true;
  396. } else {
  397. return false;
  398. }
  399. }
  400. /**
  401. * Forwards control onto another command or view
  402. *
  403. * A model will typically try to forward control to a view
  404. * (less likely, but possible, another command) after processing.
  405. * This object handles that forwarding and can even handle forwarding
  406. * to a different URL if configured to do so.
  407. *
  408. * @author Tony Bibbs <tony@geeklog.net>
  409. * @access private
  410. * @param string $name Name of next model or view to go to
  411. *
  412. */
  413. public function forwardControl($name)
  414. {
  415. $forward = $this->mapping->getForward($name);
  416. if ($forward[MVC_TYPE] == 'redirect') {
  417. $url = $this->mapping->getBaseURL() . $forward[MVC_TARGET];
  418. header('Location: ' . $url);
  419. } else {
  420. $this->object = $forward[MVC_TARGET];
  421. $this->mapping = &$this->mapLoader->getMapping($this->object, $this->viewDir, $this->commandDir, $this->baseURL);
  422. $this->processRequest();
  423. }
  424. }
  425. }
  426.  
  427. ?>

Documentation generated on Mon, 7 Mar 2005 22:36:21 -0600 by phpDocumentor 1.3.0RC3