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

Source for file Wizard.php

Documentation is available at Wizard.php

  1. <?php
  2. /* vim: set number autoindent tabstop=2 shiftwidth=2 softtabstop=2: */
  3.  
  4. /**
  5. * A class that implements a Wizard with HTML_QuickForm.
  6. *
  7. * PHP versions 4 and 5
  8. *
  9. * LICENSE: This source file is subject to version 3.0 of the PHP license
  10. * that is available through the world-wide-web at the following URI:
  11. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  12. * the PHP License and are unable to obtain it through the web, please
  13. * send a note to license@php.net so we can mail you a copy immediately.
  14. *
  15. * @category HTML
  16. * @package HTML_QuickForm_Wizard
  17. * @author Fabio Ambrosanio <fabio@ambrosanio.com>
  18. * @license http://www.php.net/license/3_01.txt PHP
  19. * @version @package_version@
  20. *
  21. * $Id: Wizard.php,v 1.8 2007/04/19 06:09:06 fabamb Exp $
  22. */
  23.  
  24. require_once 'HTML/QuickForm/Controller.php';
  25. require_once 'HTML/QuickForm/Action/Display.php';
  26. require_once 'HTML/QuickForm/Action/Next.php';
  27. require_once 'HTML/QuickForm/Action/Back.php';
  28. require_once 'HTML/QuickForm/Action/Display.php';
  29.  
  30. // Load some default action handlers
  31. require_once(dirname(__FILE__).'/actions/back.php');
  32. require_once(dirname(__FILE__).'/actions/next.php');
  33. require_once(dirname(__FILE__).'/actions/skip.php');
  34. require_once(dirname(__FILE__).'/actions/jump.php');
  35. require_once(dirname(__FILE__).'/actions/clear.php');
  36.  
  37. define("WIZARD_DEFAULT_INPUT_VALUE", "_default_");
  38.  
  39. /**
  40. * A class that implements a Wizard with HTML_QuickForm.
  41. *
  42. * This class implements a wizard whoese pages and flows are described by a FSM (Finite State Machine).
  43. * Next page is determined from the actual page, the "internal" state of the wizard and a set of rules.
  44. *
  45. * @author Fabio Ambrosanio <fabio@ambrosanio.com>
  46. * @category HTML
  47. * @package HTML_QuickForm_Wizard
  48. * @version @package_version@
  49. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  50. */
  51. class HTML_QuickForm_Wizard extends HTML_QuickForm_Controller
  52. {
  53. /**
  54. * List of states (pages in form of pageName => className)
  55. * of the wizard (FSM's set of states)
  56. *
  57. * @var array
  58. */
  59. var $states = array();
  60.  
  61. /**
  62. * The first state (page) of the wizard
  63. * (FSM's starting state)
  64. *
  65. * @var string
  66. */
  67. var $startingState;
  68.  
  69. /**
  70. * List of final states (pages) in which the wizard stops
  71. * (FSM's final states)
  72. *
  73. * @var array
  74. */
  75. var $finalStates = array();
  76.  
  77. /**
  78. * Set of paths from a state to another
  79. * (FSM's transitions set)
  80. *
  81. * @var array
  82. */
  83. var $delta = array();
  84.  
  85. /**
  86. * Function that determines the current input of the FSM,
  87. * as an array of values, used to establish the next page of the wizard
  88. *
  89. * @var function
  90. */
  91. var $inputProducer = null;
  92.  
  93.  
  94. /**
  95. * Constructor
  96. *
  97. * @param string $name name of the wizard
  98. * @param boolean $modal modal mode flag
  99. * @return Wizard
  100. */
  101. function HTML_QuickForm_Wizard($name, $modal = true)
  102. {
  103. // call parent constructor
  104. parent::HTML_QuickForm_Controller($name, $modal);
  105.  
  106. // store the stack of pages into session
  107. if (!isset($_SESSION["_" . $_SERVER['PHP_SELF']. '_stack'])) {
  108. $_SESSION["_" . $_SERVER['PHP_SELF']. '_stack'] = array();
  109. }
  110.  
  111. // sets default actions
  112. $this->addAction('display', new HTML_QuickForm_Action_Display());
  113. $this->addAction('back', new HTML_QuickForm_Wizard_back());
  114. $this->addAction('next', new HTML_QuickForm_Wizard_next());
  115. $this->addAction('jump', new HTML_QuickForm_Wizard_jump());
  116. $this->addAction('clear', new HTML_QuickForm_Wizard_clear());
  117. $this->addAction('skip', new HTML_QuickForm_Wizard_skip());
  118. }
  119.  
  120. // {{{ addPage
  121.  
  122.  
  123. /**
  124. * Add a page to the wizard
  125. *
  126. * @param string $pageName name of the page
  127. * @param string $className class of the page
  128. * @param boolean $isStart if true the page represents the start state of the FSM
  129. * @param boolean $isFinal if true the page represents a final state of the FSM
  130. */
  131. function addPage($pageName, $className, $isStart, $isFinal = false)
  132. {
  133. $this->states[$pageName] = $className;
  134. if (count($this->states) == 1) {
  135. $this->startingState = $pageName;
  136. }
  137. if ($isStart) {
  138. $this->startingState = $pageName;
  139. }
  140. if ($isFinal && !in_array($pageName, $this->finalStates)) {
  141. $this->finalStates[] = $pageName;
  142. }
  143.  
  144. // adds page to the controller
  145. $page = new $className($pageName);
  146. parent::addPage($page);
  147.  
  148. // sets the custom 'back' action
  149. $page->addAction('back', new HTML_QuickForm_Wizard_back());
  150.  
  151. // sets the custom 'next' action
  152. $page->addAction('next', new HTML_QuickForm_Wizard_next());
  153. }
  154.  
  155. /**
  156. * Add a transition from a state (page) to another
  157. *
  158. * @param string $fromPage
  159. * @param mixed $input
  160. * @param string $toPage
  161. */
  162. function addTransition($fromPage, $input, $toPage)
  163. {
  164. if (is_null($input) || (string)$input == "") {
  165. $input = WIZARD_DEFAULT_INPUT_VALUE;
  166. }
  167. $this->delta[$fromPage][$input] = $toPage;
  168. }
  169.  
  170.  
  171. /**
  172. * Sets the producer of input to the FSM
  173. *
  174. * @param function $inputProducer new function that produces current input of the FSM
  175. */
  176. function setInputProducer($inputProducer)
  177. {
  178. $this->inputProducer =& $inputProducer;
  179. }
  180.  
  181. /**
  182. * Checks if the specified page is final
  183. *
  184. * @param string $pageName
  185. * @return boolean true if $pageName is a final page, false otherwise
  186. */
  187. function isFinal($pageName)
  188. {
  189. return in_array($pageName, $this->finalStates);
  190. }
  191.  
  192. /**
  193. * Returns an array representing the FSM
  194. *
  195. * @return array
  196. */
  197. function getFSM()
  198. {
  199. // builds fsm
  200. $fsm = array();
  201. foreach($this->states as $name => $className) {
  202. $state = array(
  203. 'className' => $className
  204. );
  205. if ($this->startingState == $name) {
  206. $state['isStart'] = "true";
  207. }
  208. if (in_array($name, $this->finalStates)) {
  209. $state['isFinal'] = "true";
  210. }
  211.  
  212. $rules = array();
  213. $_delta = $this->delta[$name];
  214. if (is_array($_delta)) foreach ($_delta as $input => $next) {
  215. $rules[] = array(
  216. 'input' => $input,
  217. 'next' => $next,
  218. );
  219. }
  220.  
  221. if (!empty($rules)) {
  222. $state[] = $rules;
  223. }
  224.  
  225. $fsm[$name] = $state;
  226. }
  227. return $fsm;
  228. }
  229.  
  230.  
  231. /**
  232. * Set the FSM of the wizard
  233. *
  234. * @param array $fsm array representing a FSM
  235. */
  236. function setFSM($fsm)
  237. {
  238. // reset status
  239. $this->states = $this->finalStates = $this->delta = array();
  240. $this->startingState = null;
  241.  
  242. // loop on pages
  243. foreach($fsm as $page) {
  244. $isStart = $isFinal = false;
  245. if (!isset($page['name']) || ($pageName = $page['name']) == "" ) {
  246. PEAR::raiseError("HTML_QuickForm_Wizard: Cannot find page name", 0, PEAR_ERROR_TRIGGER);
  247. }
  248.  
  249. if (!isset($page['class']) || ($className = $page['class']) == "" ) {
  250. PEAR::raiseError("HTML_QuickForm_Wizard: Cannot find page class", 0, PEAR_ERROR_TRIGGER);
  251. }
  252.  
  253. if (isset($page['start'])) {
  254. $isStart = $page['start'];
  255. }
  256. if (isset($page['final'])) {
  257. $isFinal = $page['final'];
  258. }
  259. if (isset($page['file'])) {
  260. include_once($page['file']);
  261. }
  262.  
  263. // add the page to the wizard
  264. $this->addPage($pageName, $className, $isStart, $isFinal);
  265.  
  266. // add transactions
  267. if (is_array($rules = $page['rules'])) {
  268. foreach ($rules as $rule) {
  269. $this->addTransition($pageName, $rule['input'], $rule['next']);
  270. }
  271. }
  272. }
  273. }
  274.  
  275. /**
  276. * Import the FSM from a XML document
  277. *
  278. * @param string $xml filename of a XML document or XML itself
  279. * @param boolean $isFile if true $xml represents a filename
  280. */
  281. function fromXML($xml, $isFile = false)
  282. {
  283. if ($isFile) {
  284. $xml = file_get_contents($xml);
  285. }
  286.  
  287. include_once('XML/Unserializer.php');
  288. $options = array(
  289. 'parseAttributes' => true,
  290. 'tagMap' => array('page' => "", 'rule' => "")
  291. );
  292. $xu = new XML_Unserializer($options);
  293. $status = $xu->unserialize($xml);
  294. if (PEAR::isError($status)) {
  295. PEAR::raiseError($status->getMessage(), $status->getCode(), PEAR_ERROR_TRIGGER);
  296. }
  297.  
  298. $fsm = $xu->getUnserializedData();
  299. $this->setFSM($fsm);
  300. }
  301.  
  302.  
  303. /**
  304. * Resets the wizard
  305. *
  306. */
  307. function reset()
  308. {
  309. $this->container(true);
  310. $_SESSION["_" . $_SERVER['PHP_SELF']. '_stack'] = array();
  311. }
  312.  
  313. /**
  314. * Returns the name of the current page
  315. *
  316. * @return string
  317. */
  318. function getCurrentPage()
  319. {
  320. $stack = $_SESSION["_" . $_SERVER['PHP_SELF']. '_stack'];
  321. if (!is_array($stack) or empty($stack)) {
  322. return $this->startingState;
  323. }
  324. reset($stack);
  325. return end($stack);
  326. }
  327.  
  328. /**
  329. * Returns the name of the previous page
  330. *
  331. * @param string $pageName
  332. * @return string
  333. */
  334. function getBackName($pageName)
  335. {
  336. // gets back page from stack
  337. $backName = $this->_pop($pageName);
  338. return $backName;
  339. }
  340.  
  341. /**
  342. * Returns the name of the next page
  343. * (overloading of Controller method)
  344. *
  345. * @return string
  346. */
  347. function getNextName($pageName)
  348. {
  349. $nextName = $this->_getNextPage($pageName);
  350. if (!$nextName) {
  351. $nextName = parent::getNextName($pageName);
  352. }
  353.  
  354. if ($nextName) {
  355. $this->_push($pageName, $nextName);
  356. }
  357. return $nextName;
  358. }
  359.  
  360. /**
  361. * Returns the names of all pages
  362. *
  363. * @return array
  364. */
  365. function getPageNames()
  366. {
  367. return array_keys($this->_pages);
  368. }
  369.  
  370. /**
  371. * Load values into pages' elements
  372. *
  373. * @param array $values values to put into pages' elements
  374. */
  375. function loadValues($values)
  376. {
  377. $data =& $this->container();
  378. $_pages = $this->getPageNames();
  379. foreach($values as $key => $v) {
  380. if (in_array($key, $_pages)) {
  381. $data['values'][$key] = $v;
  382. } else {
  383. $data[$key] = $v;
  384. }
  385. }
  386. }
  387.  
  388.  
  389. /**
  390. * Returns next page that match specified page and current input of the FSM
  391. *
  392. * @return string
  393. */
  394. function _getNextPage($pageName)
  395. {
  396. $input = $this->_getInput();
  397.  
  398. $nextPage = null;
  399.  
  400. // search $delta for next pages
  401. $rules = $this->delta[$pageName];
  402. if (!is_array($input)) {
  403. if (isset($rules[$input])) {
  404. $nextPage = $rules[$input];
  405. }
  406. } else foreach($input as $i) {
  407. if (isset($rules[$i])) {
  408. $nextPage = $rules[$i];
  409. break;
  410. }
  411. }
  412.  
  413. // if no page found, try with default input
  414. if (is_null($nextPage)) {
  415. if (isset($rules[WIZARD_DEFAULT_INPUT_VALUE])) {
  416. $nextPage = $rules[WIZARD_DEFAULT_INPUT_VALUE];
  417. }
  418. }
  419.  
  420. return $nextPage;
  421. }
  422.  
  423.  
  424. /**
  425. * Returns the current input of the wizard
  426. *
  427. * @return array
  428. */
  429. function _getInput()
  430. {
  431. $input = array();
  432. if (is_callable($this->inputProducer)) {
  433. $input = call_user_func($this->inputProducer, $this->container());
  434. }
  435. return $input;
  436. }
  437.  
  438.  
  439. /**
  440. * Pushes the new page into the stack of pages
  441. *
  442. * @param string $old actual page
  443. * @param string $new new page
  444. */
  445. function _push($old, $new)
  446. {
  447. $stack = $_SESSION["_" . $_SERVER['PHP_SELF']. '_stack'];
  448. if (!is_array($stack)) $stack = array();
  449.  
  450. reset($stack);
  451.  
  452. # se la vecchia pagina non è nello stack (perchè?) la inserisco
  453. if (!in_array($old, $stack)) {
  454. array_push($stack, $old);
  455. }
  456. # se la nuova pagina è già nello stack (perchè?) correggo lo stack
  457. if (in_array($new, $stack)) {
  458. $pos = array_search($new, $stack);
  459. array_splice($stack, $pos);
  460. }
  461. # inserisco la nuova pagina nello stack
  462. array_push($stack, $new);
  463. $_SESSION["_" . $_SERVER['PHP_SELF']. '_stack'] = $stack;
  464. }
  465.  
  466. /**
  467. * Pops the page previous the specified page from the stack
  468. *
  469. * @param string $current current page
  470. * @return string page in stack before specified page
  471. */
  472. function _pop($current)
  473. {
  474. $stack = $_SESSION["_" . $_SERVER['PHP_SELF']. '_stack'];
  475. if (!is_array($stack)) $stack = array();
  476.  
  477. reset($stack);
  478.  
  479. # se la pagina attuale è contenuta nello stack (come ci aspettiamo)
  480. # elimino gli elementi dello stack sopra la pagina
  481. if (in_array($current, $stack)) {
  482. $pos = array_search($current, $stack);
  483. array_splice($stack, $pos);
  484. }
  485. $back = end($stack);
  486. $_SESSION["_" . $_SERVER['PHP_SELF']. '_stack'] = $stack;
  487.  
  488. if ("$back" == '') $back = $current;
  489.  
  490. return $back;
  491. }
  492. }
  493. ?>

Documentation generated on Thu, 19 Apr 2007 08:13:27 +0200 by phpDocumentor 1.3.0RC3