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

Source for file Formagic.php

Documentation is available at Formagic.php

  1. <?php
  2. /**
  3.  * Formagic
  4.  *
  5.  * LICENCE
  6.  *
  7.  * This source file is subject to the new BSD license that is bundled
  8.  * with this package in the file LICENSE.txt.
  9.  * It is also available through the world-wide-web at
  10.  * http://formagic.weasle.de/licence.txt
  11.  * If you did not receive a copy of the license and are unable to
  12.  * obtain it through the world-wide-web, please send an email
  13.  * to licence@weasle.de so we can send you a copy immediately.
  14.  *
  15.  * @category    Formagic
  16.  * @package     Formagic
  17.  * @author      Florian Sonnenburg
  18.  * @copyright   Copyright (c) 2007 Florian Sonnenburg
  19.  * @license     http://formagic.weasle.de/licence.txt     New BSD License
  20.  * @revision    $Revision: 19 $
  21.  */
  22.  
  23. require_once 'Item/Item.php';
  24. require_once 'Renderer/Renderer.php';
  25.  
  26. /**
  27.  * Formagic main and interface class
  28.  *
  29.  * Highly extensible formgenerator with various rendering options, form
  30.  * validation and multipage support.
  31.  *
  32.  * @category    Formagic
  33.  * @package     Formagic
  34.  * @author      Florian Sonnenburg
  35.  * @copyright   Copyright (c) 2007 Florian Sonnenburg
  36.  * @version     $Id: Formagic.php 19 2007-08-22 22:02:47Z meweasle $
  37.  ***/
  38. class Formagic
  39. {
  40.  
  41.     /**
  42.      * Form name, used for eg. form action tag in HTML renderer
  43.      * @var string 
  44.      */
  45.     public $name = 'undefined';
  46.  
  47.     /**
  48.      * Form submission method (POST or GET)
  49.      * @var string 
  50.      */
  51.     public $method = 'post';
  52.  
  53.     /**
  54.      * Item values that are not effected by POST/GET
  55.      * @var array 
  56.      ***/
  57.     public $constantValues = array();
  58.  
  59.     /**
  60.      * Points to either $_POST or $_GET
  61.      * @var array 
  62.      */
  63.     public $submitValues;
  64.  
  65.     /**
  66.      * Array of predefined values for this Formagic object
  67.      * @var array 
  68.      */
  69.     public $defaultValues;
  70.  
  71.     /**
  72.      * Type of renderer used to display form
  73.      * @see Formagic::setRenderer()
  74.      * @var string 
  75.      */
  76.     private $_renderer null;
  77.  
  78.     /**
  79.      * Enables or disabled submission tracking. Contains string with variable
  80.      * name if enabled or FALSE if disabled
  81.      * @var mixed String or FALSE
  82.      */
  83.     private $_trackSubmission '__fm';
  84.  
  85.     /**
  86.      * container item holding all items added to Formagic object
  87.      * @var object 
  88.      */
  89.     private $_itemHolder;
  90.  
  91.     /**
  92.      * Array of directories custom classes are searched for
  93.      * @var array 
  94.      */
  95.     private $_pluginDir;
  96.  
  97.     /**
  98.      * Caches result of method Formagic::validate()
  99.      * @var string 
  100.      * @see Formagic::validate();
  101.      */
  102.     private $_isValid null;
  103.  
  104.     /**
  105.      * Flag that defines if FormagicItems contained by Formagic object can be
  106.      * edited
  107.      * @var boolean 
  108.      ***/
  109.     private $_isBlocked false;
  110.  
  111.     /**
  112.      * Contains Formagic form messages (errors, notices, debugs)
  113.      * @var array 
  114.      ***/
  115.     private $_formMessages array();
  116.  
  117.     /**
  118.      * Valid callback to be executed when form is submitted
  119.      * @var mixed 
  120.      ***/
  121.     private $_executeCallback;
  122.  
  123.     /**
  124.      * Name of currently active page
  125.      * @var string 
  126.      * @see Formagic::addItem();
  127.      ***/
  128.     private $_currentPage 'default';
  129.  
  130.     /**
  131.      * Action for form tag
  132.      * @var string 
  133.      ***/
  134.     private $_formAction null;
  135.  
  136.     const VERSION           '0.2.3b';
  137.     const API_VERSION       '1.2';
  138.     const PAGE_TRACKING_KEY '__fm_rp';
  139.     const PAGE_REQUEST_KEY  '__fm_sw';
  140.     const ERROR             1;
  141.     const NOTICE            2;
  142.     const DEBUG             3;
  143.  
  144.     /**
  145.      * Consructor
  146.      *
  147.      * @param array $options 
  148.      * @throws Formagic_Exception
  149.      * @return void 
  150.      ***/
  151.     public function __construct($name='formagic'$options null)
  152.     {
  153.         // call event
  154.         $this->_execEvent('onCreate');
  155.  
  156.         $this->name = $name;
  157.  
  158.         // process options
  159.         if ($options{
  160.             $this->setOption($options);
  161.         }
  162.  
  163.         // set pointer to submission pool (GET or POST)
  164.         if ($this->method == 'post'{
  165.             $this->submitValues =$_POST;
  166.         elseif ($this->method == 'get'{
  167.             $this->submitValues =$_GET;
  168.         else {
  169.             throw new Formagic_Exception("Formagic submit method {$this->methodnot " .
  170.                                 "supported");
  171.         }
  172.  
  173.         // set item container of main Formagic object
  174.         $this->_itemHolder $this->createItem('container''__fm_itemholder');
  175.  
  176.         // handle trackSubmission
  177.         if ($this->_trackSubmission{
  178.             $this->_trackSubmission .= "_" $this->name;
  179.             $tsi $this->addItem('hidden',$this->_trackSubmission,array('default'=>1));
  180.             $tsi->isExecuteItem false;
  181.         }
  182.  
  183. //        // set saved values
  184. //        switch($this->_multipageStorage) {
  185. //            case 'hidden':
  186. //                if (isset($this->submitValues['__fm_sv'])) {
  187. //                    $this->savedValues =& $this->submitValues['__fm_sv'];
  188. //                }
  189. //                break;
  190. //            case 'session':
  191. //                // $this->savedValues =& $_SESSION['__fmsv'];
  192. //                break;
  193. //            default:
  194. //                throw new Formagic_Exception("Storage engine {$this->storage} for " .
  195. //                                    "multipaged forms not supported");
  196. //        } // switch
  197.     }
  198.  
  199.     /**
  200.      * Sets Formagic option $name to $value
  201.      *
  202.      * Triggers warning if option $name is not supported.
  203.      *
  204.      * @param string $name Formagic option name or array with name => value
  205.      * @param mixed $value value of option $name
  206.      * @throws Formagic_Exception
  207.      * @return boolean
  208.      */
  209.     public function setOption($options, $value=null)
  210.     {
  211.         if (!is_array($options)) {
  212.             $options = array($options => $value);
  213.         }
  214.         foreach($options as $name => $value) {
  215.             switch($name) {
  216.                 // set custom action
  217.                 case 'action':
  218.                     if (is_string($value)) {
  219.                         $this->_formAction = $value;
  220.                     } else {
  221.                         throw new <a href="../Formagic/Formagic_Exception.html">Formagic_Exception</a>('Action has to be string value');
  222.                     }
  223.                     break;
  224.                 // set custom renderer
  225.                 case 'renderer':
  226.                     $this->setRenderer($value);
  227.                     break;
  228.                 // set plugin dir
  229.                 case 'FormagicPluginDir':
  230.                     $this->_pluginDir = (array)$value;
  231.                     break;
  232.                 // set submission method
  233.                 case 'method':
  234.                     $this->method =  $value;
  235.                     break;
  236.                 // add hidden input for submission tracking if enabled
  237.                 case 'trackSubmission':
  238.                     $this->_trackSubmission = $value;
  239.                     break;
  240.                 // set default values
  241.                 case 'defaultValues':
  242.                     $this->setDefaultValues($value);
  243.                     break;
  244.                 // set storage engine for saved values
  245.                 case 'multipageStorage':
  246.                     $this->_multipageStorage = $value;
  247.                     break;
  248.                 case 'executeCallback':
  249.                     $this->setExecuteCallback($value);
  250.                 default:
  251.                     throw new <a href="../Formagic/Formagic_Exception.html">Formagic_Exception</a>("Option $name not supported");
  252.             } // switch
  253.         }
  254.         return true;
  255.     }
  256.  
  257.     /**
  258.      * Defines renderer for current Formagic object
  259.      *
  260.      * @param string $renderer FormagicRenderer object or string with name of
  261.      *        renderer class
  262.      * @return boolean
  263.      */
  264.     public function setRenderer($renderer)
  265.     {
  266.         if ($renderer instanceOf Formagic_Renderer) {
  267.             $this->_renderer = $renderer;
  268.             return true;
  269.         }
  270.  
  271.         $class = 'Formagic_Renderer_' . $renderer;
  272.         try {
  273.             $this->loadClass($class);
  274.         } catch (<a href="../Formagic/Formagic_Exception.html">Formagic_Exception</a> $e) {
  275.             return false;
  276.         }
  277.  
  278.         $this->_renderer = new $class();
  279.         return true;
  280.     }
  281.  
  282.     /**
  283.      * Formagic::setDefaultValues()
  284.      *
  285.      * @param array $values
  286.      * @return array
  287.      */
  288.     public function setDefaultValues($values)
  289.     {
  290.         $this->defaultValues = array_merge($this->defaultValues,$values);
  291.         return $this->defaultValues;
  292.     }
  293.  
  294.     /**
  295.      * Set callback property
  296.      *
  297.      * $callback has to be a valid PHP callback variable.
  298.      *
  299.      * @param mixed $callback
  300.      * @return void
  301.      */
  302.     public function setExecuteCallback($callback)
  303.     {
  304.         $this->_executeCallback = $callback;
  305.     }
  306.  
  307.     /**
  308.      * Destructor
  309.      *
  310.      * Restores formerly manipulated include path.
  311.      *
  312.      * @return void
  313.      **/
  314.     public function __destruct()
  315.     {
  316.         $this->_execEvent('onDestruct');
  317.     }
  318.  
  319.     /**
  320.      * Returns result for string casting of Formagic object
  321.      *
  322.      * @return string
  323.      **/
  324.     public function __toString()
  325.     {
  326.         $numItems = $this->countItems();
  327.         $submitted = (int)$this->isSubmitted();
  328.         $str = "Formagic [name: '{$this->name}'].
  329.                "[items loaded: '$numItems'][isSubmitted: '$submitted'].
  330.                "<br />";
  331.         // $str = "<pre>". print_r($this->_items,true) ."</pre>";
  332.         return $str;
  333.     }
  334.  
  335. //    public function __call($method, $arguments)
  336. //    {
  337. //
  338. //    }
  339.     /**
  340.      * Returns number of items added to Formagic object
  341.      *
  342.      * Counts total number of items recursively
  343.      *
  344.      * @return integer
  345.      */
  346.     public function countItems()
  347.     {
  348.         return $this->_itemHolder->countItems();
  349.     }
  350.  
  351.     /**
  352.      * Adds Formagic item object to array of first level form items
  353.      *
  354.      * Creates new item object and adds this or adds passed FormagicItem object
  355.      *
  356.      * @param mixed $type String with item type or FormagicItem object
  357.      * @param string $name String with item name. NULL if $type is FormagicItem
  358.      *        object
  359.      * @param array $args Array with additional item information. NULL if $type
  360.      *        is FormagicItem object
  361.      * @return object FormagicItem object
  362.      */
  363.     public function addItem($type, $name, $args=null)
  364.     {
  365.         return $this->_itemHolder->addItem($type, $name, $args);
  366.     }
  367.  
  368.     /**
  369.      * Creates and returns FormagicItem object
  370.      *
  371.      * Tries to load correct object class and creates new object. Returns object
  372.      * if successfull, false if not.
  373.      *
  374.      * @param string $type
  375.      * @param string $name
  376.      * @param array $args
  377.      * @return Formagic_Item
  378.      */
  379.     public function &createItem($type, $name, $args=null)
  380.     {
  381.         $class = 'Formagic_Item_' . ucFirst($type);
  382.         $this->loadClass($class);
  383.  
  384.         $obj = new $class($type, $name, $args, $this);
  385.         if ($obj->_execEvent('onCreate')) {
  386.             return $obj;
  387.         }
  388.         throw new <a href="../Formagic/Formagic_Exception.html">Formagic_Exception</a>('Event onCreate of item object returned error');
  389.     }
  390.  
  391.     /**
  392.      * Returns pointer to Formagic items array
  393.      *
  394.      * @return array
  395.      */
  396.     public function &getItemHolder()
  397.     {
  398.         return $this->_itemHolder;
  399.     }
  400.  
  401.     /**
  402.      * Returns current form action
  403.      *
  404.      * @return string
  405.      */
  406.     public function getFormAction()
  407.     {
  408.         if (!$this->_formAction) {
  409.             $this->_formAction = $_SERVER['PHP_SELF'];
  410.         }
  411.         return $this->_formAction;
  412.     }
  413.  
  414.     /**
  415.      * Checks if form is submitted and all rules apply
  416.      *
  417.      * Iterates through all items added to the form. If any rule is violated,
  418.      * iteration is stopped. Returns true if no rules are violated.
  419.      * The result of validate() is cached.
  420.      *
  421.      * @return boolean
  422.      */
  423.     public function validate()
  424.     {
  425.         // return cached validation result if present
  426.         if (is_bool($this->_isValid)) {
  427.             return $this->_isValid;
  428.         }
  429.  
  430.         // check event
  431.         if (!$this->_execEvent('onValidation')) {
  432.             return $this->_isValid = false;
  433.         }
  434.  
  435.         // Assume that validation is negative if form is not submitted
  436.         if (!$this->isSubmitted()) {
  437.             return $this->_isValid = false;
  438.         }
  439.  
  440.         $this->_isValid = true;
  441.         $items = $this->_itemHolder->getItems();
  442.  
  443.         $messageWritten = false;
  444.         foreach($items as $item) {
  445.  
  446.             $res = $item->validate();
  447.             if ($res instanceOf <a href="../Rule/Formagic_Rule.html">Formagic_Rule</a>) {
  448.                 if (!$messageWritten) {
  449.                     $this->setMessage('Bitte markierte Eingabefelder pr&uuml;fen', self::ERROR);
  450.                     $messageWritten = true;
  451.                 }
  452.                 $this->_isValid = false;
  453.             } elseif($res == false) {
  454.                 $this->_isValid = false;
  455.             }
  456.         }
  457.  
  458.         // check event
  459.         if (!$this->_execEvent('onValidationComplete')) {
  460.             return $this->_isValid = false;
  461.         }
  462.  
  463.         return $this->_isValid;
  464.     }
  465.  
  466.     /**
  467.      * Returns output generated by renderer
  468.      *
  469.      * Loads renderer class and calls renderer::fetch()
  470.      *
  471.      * @return string Renderer result
  472.      */
  473.     public function fetch()
  474.     {
  475.         $this->_itemHolder->_execEvent('onFetch');
  476.         if (!($this->_renderer instanceOf <a href="../Renderer/Formagic_Renderer.html">Formagic_Renderer</a>)) {
  477.             $this->setRenderer('HTML');
  478.         }
  479.         return $this->_renderer->fetch($this);
  480.     }
  481.  
  482.     /**
  483.      * Runs execute callback
  484.      *
  485.      * @throws Formagic_Exception
  486.      * @return void
  487.      */
  488.     public function execute()
  489.     {
  490.         $arr = $this->getValues();
  491.         if (!isset($this->_executeCallback)) {
  492.             throw new <a href="../Formagic/Formagic_Exception.html">Formagic_Exception</a>('No valid callback to execute');
  493.         }
  494.         if (
  495.             (is_string($this->_executeCallback) && !function_exists($this->_executeCallback))
  496.             || (is_array($this->_executeCallback) &&
  497.                !method_exists($this->_executeCallback[0], $this->_executeCallback[1]))
  498.         ) {
  499.             throw new <a href="../Formagic/Formagic_Exception.html">Formagic_Exception</a>('Method or function does not exist');
  500.         }
  501.         call_user_func($this->_executeCallback, $arr);
  502.     }
  503.  
  504.     /**
  505.      * Returns item value from Formagic value pool
  506.      *
  507.      * @param string $name
  508.      * @return mixed
  509.      */
  510.     public function &getValue($name)
  511.     {
  512.         if (isset($this->constantValues[$name])) {
  513.             return $this->constantValues[$name];
  514.         }
  515.         if (isset($this->submitValues[$name])) {
  516.             return $this->submitValues[$name];
  517.         }
  518. //        if (isset($this->savedValues[$name])) {
  519. //            return $this->savedValues[$name];
  520. //        }
  521.         return $this->defaultValues[$name];
  522.     }
  523.  
  524.     /**
  525.      * Returns array with values from all added items
  526.      *
  527.      * @return array
  528.      */
  529.     public function getValues()
  530.     {
  531.         $items = $this->_itemHolder->getItems();
  532.         $values = array();
  533.         $this->_getValueFromItemRecursive($items, $values);
  534.         return $values;
  535.     }
  536.  
  537.     /**
  538.      * Recursively iterates through $items and fills $values
  539.      *
  540.      * @param array $items
  541.      * @param array $values
  542.      * @return void
  543.      */
  544.     private function _getValueFromItemRecursive($items, &$values)
  545.     {
  546.         foreach($items as $item) {
  547.             if ($item instanceOf <a href="../Item/Formagic_Item_Container.html">Formagic_Item_Container</a>) {
  548.                 $this->_getValueFromItemRecursive($item->getItems(), $values);
  549.             } else {
  550.                 if ($item->isPostItem && strpos($item->name, '__fm') === false) {
  551.                     $values[$item->name] = $this->getValue($item->name);
  552.                 }
  553.             }
  554.         }
  555.     }
  556.  
  557.     /**
  558.      * Checks if HTML form is submitted
  559.      *
  560.      * Check result is true for following rules:
  561.      * - if submission tracking is enabled in Formagic options and the
  562.      * submission variable is present
  563.      * - if submission tracking is disabled, but either GET or POST values
  564.      * (dependent on chosen submission method) are present
  565.      *
  566.      * @return boolean
  567.      */
  568.     public function isSubmitted()
  569.     {
  570.         // Return value of trackSubmission variable if tracking enabled
  571.         if ($this->_trackSubmission) {
  572.             return !empty($this->submitValues[$this->_trackSubmission]);
  573.         }
  574.  
  575.         // If tracking disabled, guess submission status from $_POST/$_GET
  576.         if (count($this->submitValues)) {
  577.             return true;
  578.         }
  579.         return false;
  580.     }
  581.  
  582.     /**
  583.      * Freezes form
  584.      *
  585.      * @return boolean
  586.      */
  587.     public function block()
  588.     {
  589.         $this->_isBlocked = true;
  590.         $items =& $this->_itemHolder->getItems();
  591.         foreach($items as &$item) {
  592.             $item->block();
  593.         }
  594.         return true;
  595.     }
  596.  
  597.     /**
  598.      * Adds $message to Formagic message stack
  599.      *
  600.      * Predefined $levels:
  601.      *  - ERROR
  602.      *  - NOTICE
  603.      *  - DEBUG
  604.      *
  605.      * Other levels can be used if neccessary. Corresponding level has to be
  606.      * used in Formagic::getMessages()
  607.      *
  608.      * @see Formagic::getMessages()
  609.      * @param string $message
  610.      * @param string $level
  611.      * @return string
  612.      */
  613.     public function setMessage($message, $level)
  614.     {
  615.         $this->_formMessages[$level][] = $message;
  616.         return $message;
  617.     }
  618.  
  619.     /**
  620.      * Returns Formagic messages of level $level or empty array if no messages
  621.      * of that level.
  622.      *
  623.      * @param string $level
  624.      * @throws Formagic_Exception
  625.      * @return array
  626.      */
  627.     public function getMessages($level=null)
  628.     {
  629.         if (!$level) {
  630.             return $this->_formMessages;
  631.         }
  632.         if (isset($this->_formMessages[$level])) {
  633.             return $this->_formMessages[$level];
  634.         } else {
  635.             throw new <a href="../Formagic/Formagic_Exception.html">Formagic_Exception</a>('No such message level as "' . $level . '"');
  636.         }
  637.     }
  638.  
  639.     /**
  640.      * Includes Formagic extension class
  641.      *
  642.      * Skipped if class is already loaded. loadClass() tries to load from any
  643.      * extension directories defined. Returns true if successful, false if not.
  644.      *
  645.      * @param string $class Class name. File name is $class.php
  646.      * @throws Formagic_Exception
  647.      * @return boolean
  648.      */
  649.     public function loadClass($class)
  650.     {
  651.         if (class_exists($class)) {
  652.             return true;
  653.         }
  654.  
  655.         $chunks = explode('_', $class);
  656.         $file = '.'.DIRECTORY_SEPARATOR.$chunks[1].DIRECTORY_SEPARATOR.$chunks[2].'.php';
  657.         if (!include($file)) {
  658.             throw new <a href="../Formagic/Formagic_Exception.html">Formagic_Exception</a>("Formagic item class \"$class\" could not be loaded");
  659.         }
  660.         return true;
  661.     }
  662.  
  663.     /**
  664.      * Formagic event dispatcher
  665.      *
  666.      * @param string $event Event to be triggered
  667.      * @return boolean
  668.      */
  669.     protected function _execEvent($event)
  670.     {
  671.         switch($event) {
  672.             case 'onValidationComplete':
  673.             case 'onValidation':
  674.             case 'onCreate':
  675.             case 'onDestruct':
  676.             case 'onAddItem':
  677.                 break;
  678.             default:
  679.                 throw new <a href="../Formagic/Formagic_Exception.html">Formagic_Exception</a>("Event $event not supported");
  680.         } // switch
  681.         return true;
  682.     }
  683.  
  684. }
  685.  
  686. /**
  687.  * Generic Formagic Exception class
  688.  *
  689.  * @category    Formagic
  690.  * @package     Formagic
  691.  * @author      Florian Sonnenburg
  692.  * @copyright   Copyright (c) 2007 Florian Sonnenburg
  693.  * @version $Id: Formagic.php 19 2007-08-22 22:02:47Z meweasle $
  694.  */
  695. class Formagic_Exception extends Exception
  696. {

Documentation generated on Thu, 23 Aug 2007 00:29:38 +0200 by phpDocumentor 1.4.0