PageRenderTime 106ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 0ms

/system/library/PEAR/HTML/QuickForm2/Controller.php

https://bitbucket.org/spekkionu/passworddb
PHP | 488 lines | 213 code | 32 blank | 243 comment | 26 complexity | fe8451755f985474a58a5cfe7a690ad9 MD5 | raw file
Possible License(s): BSD-2-Clause
  1. <?php
  2. /**
  3. * Class implementing the Page Controller pattern for multipage forms
  4. *
  5. * PHP version 5
  6. *
  7. * LICENSE:
  8. *
  9. * Copyright (c) 2006-2012, Alexey Borzov <avb@php.net>,
  10. * Bertrand Mansion <golgote@mamasam.com>
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions
  15. * are met:
  16. *
  17. * * Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. * * Redistributions in binary form must reproduce the above copyright
  20. * notice, this list of conditions and the following disclaimer in the
  21. * documentation and/or other materials provided with the distribution.
  22. * * The names of the authors may not be used to endorse or promote products
  23. * derived from this software without specific prior written permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  26. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  27. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  28. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  29. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  30. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  31. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  32. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  33. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  34. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  35. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. * @category HTML
  38. * @package HTML_QuickForm2
  39. * @author Alexey Borzov <avb@php.net>
  40. * @author Bertrand Mansion <golgote@mamasam.com>
  41. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  42. * @version SVN: $Id: Controller.php 325701 2012-05-15 15:00:09Z avb $
  43. * @link http://pear.php.net/package/HTML_QuickForm2
  44. */
  45. /** The class representing a page of a multipage form */
  46. require_once 'HTML/QuickForm2/Controller/Page.php';
  47. /** Object wrapping around session variable used to store controller data */
  48. require_once 'HTML/QuickForm2/Controller/SessionContainer.php';
  49. /** Class presenting the values stored in session by Controller as submitted ones */
  50. require_once 'HTML/QuickForm2/DataSource/Session.php';
  51. /**
  52. * Class implementing the Page Controller pattern for multipage forms
  53. *
  54. * This class keeps track of pages and (default) action handlers for the form,
  55. * it manages $_SESSION container for the form values, allows setting
  56. * DataSources for the form as a whole and getting its value.
  57. *
  58. * @category HTML
  59. * @package HTML_QuickForm2
  60. * @author Alexey Borzov <avb@php.net>
  61. * @author Bertrand Mansion <golgote@mamasam.com>
  62. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  63. * @version Release: 2.0.0
  64. * @link http://pear.php.net/package/HTML_QuickForm2
  65. */
  66. class HTML_QuickForm2_Controller implements IteratorAggregate
  67. {
  68. /**
  69. * Key in $_REQUEST array that contains the ID of the Controller
  70. */
  71. const KEY_ID = '_qfc_id';
  72. /**
  73. * Key in $_SESSION array that contains the Controller data (needs ID substituted via sprintf())
  74. */
  75. const KEY_CONTAINER = '_%s_container';
  76. /**
  77. * Whether the form is a wizard
  78. * @var boolean
  79. */
  80. protected $wizard = true;
  81. /**
  82. * Whether Controller ID should be sent in GET and POST parameters
  83. * @var boolean
  84. */
  85. protected $propagate = true;
  86. /**
  87. * Controller ID
  88. * @var string
  89. */
  90. protected $id = null;
  91. /**
  92. * Contains the pages (instances of HTML_QuickForm2_Controller_Page) of the multipage form
  93. * @var array
  94. */
  95. protected $pages = array();
  96. /**
  97. * Contains the mapping of action names to handlers (objects implementing HTML_QuickForm2_Controller_Action)
  98. * @var array
  99. */
  100. protected $handlers = array();
  101. /**
  102. * The action extracted from HTTP request: array('page', 'action')
  103. * @var array
  104. */
  105. protected $actionName = null;
  106. /**
  107. * A wrapper around session variable used to store form data
  108. * @var HTML_QuickForm2_Controller_SessionContainer
  109. */
  110. protected $sessionContainer = null;
  111. /**
  112. * Finds a controller name in $_REQUEST
  113. *
  114. * @return string|null Returns nulle if either a KEY_ID is not present
  115. * in $_REQUEST or KEY_CONTAINER is not present in
  116. * $_SESSION
  117. */
  118. public static function findControllerID()
  119. {
  120. if (empty($_REQUEST[self::KEY_ID])
  121. || empty($_SESSION[sprintf(self::KEY_CONTAINER, $_REQUEST[self::KEY_ID])])
  122. ) {
  123. return null;
  124. } else {
  125. return $_REQUEST[self::KEY_ID];
  126. }
  127. }
  128. /**
  129. * Class constructor
  130. *
  131. * Sets the form ID, whether to send this ID in POST and GET parameters,
  132. * wizard / non-wizard behaviour.
  133. *
  134. * Different forms should be given different IDs, as they are used to store
  135. * values in session. If $id is empty, the controller will try to find it
  136. * in $_REQUEST, throwing the exception if this fails.
  137. *
  138. * Wizard forms only allow going to the next page if all the previous ones
  139. * are valid.
  140. *
  141. * @param string $id Form ID
  142. * @param boolean $wizard Whether the form is a wizard
  143. * @param boolean $propagateId Whether form's ID should be sent with
  144. * GET and POST parameters
  145. *
  146. * @throws HTML_QuickForm2_NotFoundException if ID is not given and cannot
  147. * be found in $_REQUEST, or session container is empty
  148. */
  149. public function __construct($id = null, $wizard = true, $propagateId = false)
  150. {
  151. if (empty($id)) {
  152. $propagateId = true;
  153. $id = self::findControllerID();
  154. }
  155. if (empty($id)) {
  156. throw new HTML_QuickForm2_NotFoundException(
  157. 'Controller ID not available in $_REQUEST or session ' .
  158. 'container is empty, please provide ID to constructor'
  159. );
  160. }
  161. $this->id = $id;
  162. $this->wizard = (bool)$wizard;
  163. $this->propagate = (bool)$propagateId;
  164. }
  165. /**
  166. * Returns whether the form is a wizard
  167. *
  168. * @return boolean
  169. */
  170. public function isWizard()
  171. {
  172. return $this->wizard;
  173. }
  174. /**
  175. * Returns the form ID
  176. *
  177. * @return string
  178. */
  179. public function getId()
  180. {
  181. return $this->id;
  182. }
  183. /**
  184. * Returns whether to send form id with GET and POST parameters
  185. *
  186. * @return boolean
  187. */
  188. public function propagateId()
  189. {
  190. return $this->propagate;
  191. }
  192. /**
  193. * Returns the session container with the controller data
  194. *
  195. * @return HTML_QuickForm2_Controller_SessionContainer
  196. */
  197. public function getSessionContainer()
  198. {
  199. if (empty($this->sessionContainer)) {
  200. $this->sessionContainer = new HTML_QuickForm2_Controller_SessionContainer($this);
  201. }
  202. return $this->sessionContainer;
  203. }
  204. /**
  205. * Removes the session variable containing the controller data
  206. */
  207. public function destroySessionContainer()
  208. {
  209. unset($_SESSION[sprintf(self::KEY_CONTAINER, $this->id)]);
  210. $this->sessionContainer = null;
  211. }
  212. /**
  213. * Extracts the name of the page and the action to perform with it from HTTP request data
  214. *
  215. * @return array first element is page name, second is action name
  216. */
  217. public function getActionName()
  218. {
  219. if (is_array($this->actionName)) {
  220. return $this->actionName;
  221. }
  222. if (empty($this->pages)) {
  223. throw new HTML_QuickForm2_NotFoundException('No pages added to the form');
  224. }
  225. $names = array_map('preg_quote', array_keys($this->pages));
  226. $regex = '/^_qf_(' . implode('|', $names) . ')_(.+?)(_x)?$/';
  227. foreach (array_keys($_REQUEST) as $key) {
  228. if (preg_match($regex, $key, $matches)) {
  229. $this->actionName = array($matches[1], $matches[2]);
  230. break;
  231. }
  232. }
  233. if (!is_array($this->actionName)) {
  234. reset($this->pages);
  235. $this->actionName = array(key($this->pages), 'display');
  236. }
  237. return $this->actionName;
  238. }
  239. /**
  240. * Processes the request
  241. *
  242. * This finds the page, the action to perform with it and passes the action
  243. * to the page's handle() method.
  244. *
  245. * @return mixed Return value of action handler
  246. * @throws HTML_QuickForm2_Exception
  247. */
  248. public function run()
  249. {
  250. list($page, $action) = $this->getActionName();
  251. return $this->pages[$page]->handle($action);
  252. }
  253. /**
  254. * Adds a handler for a specific action
  255. *
  256. * @param string $actionName action name
  257. * @param HTML_QuickForm2_Controller_Action $action the handler for the action
  258. */
  259. public function addHandler($actionName, HTML_QuickForm2_Controller_Action $action)
  260. {
  261. $this->handlers[$actionName] = $action;
  262. }
  263. /**
  264. * Handles an action
  265. *
  266. * This will be called if the page itself does not have a handler for a
  267. * specific action. The method also loads and uses default handlers for
  268. * common actions, if specific ones were not added.
  269. *
  270. * @param HTML_QuickForm2_Controller_Page $page form page
  271. * @param string $actionName action name
  272. *
  273. * @return mixed Return value of action handler
  274. * @throws HTML_QuickForm2_NotFoundException if handler for an action is missing
  275. */
  276. public function handle(HTML_QuickForm2_Controller_Page $page, $actionName)
  277. {
  278. if (!isset($this->handlers[$actionName])
  279. && in_array($actionName, array('next', 'back', 'submit', 'display', 'jump'))
  280. ) {
  281. $className = 'HTML_QuickForm2_Controller_Action_' . ucfirst($actionName);
  282. HTML_QuickForm2_Loader::loadClass($className);
  283. $this->addHandler($actionName, new $className());
  284. }
  285. if (isset($this->handlers[$actionName])) {
  286. return $this->handlers[$actionName]->perform($page, $actionName);
  287. } else {
  288. throw new HTML_QuickForm2_NotFoundException(
  289. "Unhandled action '{$actionName}' for page '{$page->getForm()->getId()}'"
  290. );
  291. }
  292. }
  293. /**
  294. * Adds a new page to the form
  295. *
  296. * @param HTML_QuickForm2_Controller_Page $page
  297. */
  298. public function addPage(HTML_QuickForm2_Controller_Page $page)
  299. {
  300. $pageId = $page->getForm()->getId();
  301. if (!empty($this->pages[$pageId])) {
  302. throw new HTML_QuickForm2_InvalidArgumentException(
  303. "Duplicate page ID '{$pageId}'"
  304. );
  305. }
  306. $page->setController($this);
  307. $this->pages[$pageId] = $page;
  308. }
  309. /**
  310. * Returns a page
  311. *
  312. * @param string $pageId Page ID
  313. *
  314. * @return HTML_QuickForm2_Controller_Page
  315. * @throws HTML_QuickForm2_NotFoundException if there is no page with
  316. * the given ID
  317. */
  318. public function getPage($pageId)
  319. {
  320. if (!empty($this->pages[$pageId])) {
  321. return $this->pages[$pageId];
  322. } else {
  323. throw new HTML_QuickForm2_NotFoundException(
  324. "Unknown page '{$pageId}'"
  325. );
  326. }
  327. }
  328. /**
  329. * Returns the page preceding the given one
  330. *
  331. * @param HTML_QuickForm2_Controller_Page $reference
  332. *
  333. * @return HTML_QuickForm2_Controller_Page|null
  334. */
  335. public function previousPage(HTML_QuickForm2_Controller_Page $reference)
  336. {
  337. $previous = null;
  338. foreach ($this->pages as $page) {
  339. if ($page === $reference) {
  340. return $previous;
  341. }
  342. $previous = $page;
  343. }
  344. return null;
  345. }
  346. /**
  347. * Returns the page following the given one
  348. *
  349. * @param HTML_QuickForm2_Controller_Page $reference
  350. *
  351. * @return HTML_QuickForm2_Controller_Page|null
  352. */
  353. public function nextPage(HTML_QuickForm2_Controller_Page $reference)
  354. {
  355. $previous = null;
  356. foreach ($this->pages as $page) {
  357. if ($previous === $reference) {
  358. return $page;
  359. }
  360. $previous = $page;
  361. }
  362. return null;
  363. }
  364. /**
  365. * Checks whether the pages of the controller are valid
  366. *
  367. * @param HTML_QuickForm2_Controller_Page $reference If given, check only
  368. * the pages before (not including) that page
  369. *
  370. * @return bool
  371. */
  372. public function isValid(HTML_QuickForm2_Controller_Page $reference = null)
  373. {
  374. $container = $this->getSessionContainer();
  375. foreach ($this->pages as $id => $page) {
  376. if ($reference === $page) {
  377. return true;
  378. }
  379. if (!$container->getValidationStatus($id)) {
  380. // We should handle the possible situation when the user has never
  381. // seen a page of a non-modal multipage form
  382. if (!$this->isWizard()
  383. && null === $container->getValidationStatus($id)
  384. ) {
  385. // Empty Session datasource makes the form look submitted
  386. $page->getForm()->setDatasources(array_merge(
  387. $container->getDatasources(),
  388. array(new HTML_QuickForm2_DataSource_Session(array()))
  389. ));
  390. // This will store the "submitted" values in session and
  391. // return validation status
  392. if ($page->storeValues()) {
  393. continue;
  394. }
  395. }
  396. return false;
  397. }
  398. }
  399. return true;
  400. }
  401. /**
  402. * Returns the first page that failed validation
  403. *
  404. * @return HTML_QuickForm2_Controller_Page|null
  405. */
  406. public function getFirstInvalidPage()
  407. {
  408. foreach ($this->pages as $id => $page) {
  409. if (!$this->getSessionContainer()->getValidationStatus($id)) {
  410. return $page;
  411. }
  412. }
  413. return null;
  414. }
  415. /**
  416. * Adds a new data source to the Controller
  417. *
  418. * Note that Controller data sources are stored in session, so your data source
  419. * implementation should properly handle its (un)serialization.
  420. *
  421. * @param HTML_QuickForm2_DataSource $datasource Data source
  422. */
  423. public function addDataSource(HTML_QuickForm2_DataSource $datasource)
  424. {
  425. $this->getSessionContainer()->storeDatasources(
  426. array_merge(
  427. $this->getSessionContainer()->getDatasources(), array($datasource)
  428. )
  429. );
  430. }
  431. /**
  432. * Returns the form's values
  433. *
  434. * @return array
  435. */
  436. public function getValue()
  437. {
  438. $values = array();
  439. foreach (array_keys($this->pages) as $id) {
  440. $values = HTML_QuickForm2_Container::arrayMerge(
  441. $values, $this->getSessionContainer()->getValues($id)
  442. );
  443. }
  444. return $values;
  445. }
  446. /**
  447. * Returns an Iterator for the form's pages
  448. *
  449. * @return ArrayIterator
  450. */
  451. public function getIterator()
  452. {
  453. return new ArrayIterator($this->pages);
  454. }
  455. }
  456. ?>