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

Source for file Form.php

Documentation is available at Form.php

  1. <?php
  2. /**
  3.  * A package designed to make creating input forms easy for PHP-GTK 2 packages
  4.  * and applications.
  5.  *
  6.  * This package was designed to make it easy to add forms to PHP-GTK 2 packages
  7.  * and applications. The package is modeled after HTML_QuickForm but cannot
  8.  * extend HTML_QuickForm because HTML_QuickForm is tied too tightly to HTML.
  9.  *
  10.  * PHP-GTK 2 forms are very similar to HTML forms. They consist of a handful of
  11.  * different input types and the data is submitted all together when a user
  12.  * clicks the submit button. The difference is how the user values are
  13.  * submitted and stored. HTML forms submit data in an HTTP request using either
  14.  * GET or POST. PHP-GTK 2 forms call a callback which is then responsible for
  15.  * retrieving the values.
  16.  *
  17.  * In HTML_QuickForm, all form elements extend a common base class. In PHP-GTK
  18.  * 2 this is not possible because the elements need to be added to containes.
  19.  * To be added to a container a class must extends GtkWidget. While it is not
  20.  * possible to force all form elements to inherit from the same base class we
  21.  * can use an interface to enforce some consistency.
  22.  *
  23.  * In order to make it possible to create elements on the fly without
  24.  * restricting the number of constructor arguments this package must use the
  25.  * Reflection API. This means that the user must have reflection enabled.
  26.  *
  27.  * @todo At least one group class.
  28.  * @todo Element classes (Done: Text, Password, Submit, Cancel)
  29.  * @todo Rule classes    (Done: Required)
  30.  *
  31.  * @todo Unit tests
  32.  *
  33.  * @author    Scott Mattocks
  34.  * @package   Structures_Form
  35.  * @license   PHP License
  36.  * @version   0.8.0devel
  37.  * @copyright Copyright 2006 Scott Mattocks
  38.  */
  39. class Structures_Form {
  40.  
  41.     // {{{ Constants
  42.     
  43.     /**
  44.      * Error constants.
  45.      *
  46.      * @const
  47.      */
  48.     const ERROR_ADD_ELEMENT_DOUBLEADD          = -1;
  49.     const ERROR_ADD_RULE_DOUBLEADD             = -2;
  50.  
  51.     const ERROR_UNREGISTERED_ELEMENT           = -3;
  52.     const ERROR_UNREGISTERED_RULE              = -4;
  53.     const ERROR_REGISTRATION_ELEMENT           = -5;
  54.     const ERROR_REGISTRATION_ELEMENT_DOUBLEREG = -6;
  55.     const ERROR_REGISTRATION_RULE              = -7;
  56.     const ERROR_REGISTRATION_RULE_DOUBLEREG    = -8;
  57.  
  58.     const ERROR_UNREGISTRATION_ELEMENTINUSE    = -9;
  59.     const ERROR_UNREGISTRATION_ELEMENT         = -10;
  60.     const ERROR_UNREGISTRATION_RULEINUSE       = -11;
  61.     const ERROR_UNREGISTRATION_RULE            = -12;
  62.  
  63.     const ERROR_LACKSINTERFACE_ELEMENT         = -13;
  64.     const ERROR_LACKSINTERFACE_ELEMENTSET      = -14;
  65.     const ERROR_LACKSINTERFACE_RULE            = -15;
  66.     const ERROR_LACKSINTERFACE_GROUP           = -16;
  67.     const ERROR_LACKSINTERFACE_RENDERER        = -17;
  68.  
  69.     const ERROR_NONEXISTENT_CLASS              = -18;
  70.     const ERROR_NONEXISTENT_FILE               = -19;
  71.     const ERROR_NONEXISTENT_ELEMENT            = -20;
  72.     const ERROR_NONEXISTENT_CALLBACK           = -21;
  73.     const ERROR_RENDER_FAILURE                 = -22;
  74.  
  75.     /**
  76.      * ElementSet constants.
  77.      *
  78.      * @const
  79.      */
  80.     const ELEMENTSET_PATH   = 'Structures/Form/Element/';
  81.     const ELEMENTSET_PREFIX = 'Structures_Form_Element_';
  82.  
  83.     /**
  84.      * Defaults
  85.      *
  86.      * @const
  87.      */
  88.     const DEFAULT_ERRORCALLBACK = 'defaultErrorCallback';
  89.  
  90.     /**
  91.      * The name of the rule defining required elements.
  92.      *
  93.      * @const
  94.      */
  95.     const REQUIRED_RULE = 'required';
  96.  
  97.     // }}}
  98.     // {{{ Member Variables
  99.  
  100.     /**
  101.      * The form elements.
  102.      *
  103.      * @access protected
  104.      * @var    array 
  105.      */
  106.     protected $elements = array();
  107.  
  108.     /**
  109.      * The rules to be applied to each element.
  110.      *
  111.      * @access protected
  112.      * @var    array 
  113.      */
  114.     protected $rules = array();
  115.  
  116.     /**
  117.      * The registered element types.
  118.      *
  119.      * @access protected
  120.      * @var    array 
  121.      */
  122.     protected $registeredElements = array();
  123.  
  124.     /**
  125.      * The registered rules.
  126.      *
  127.      * @access protected
  128.      * @var    array 
  129.      */
  130.     protected $registeredRules = array();
  131.  
  132.     /**
  133.      * The method to call after values are collected from a submitted form.
  134.      *
  135.      * @access protected
  136.      * @var    mixed     string or array
  137.      */
  138.     protected $submitCallback;
  139.  
  140.     /**
  141.      * The callback for error handling.
  142.      *
  143.      * @access protected
  144.      * @var    mixed     string or array
  145.      */
  146.     protected $errorCallback;
  147.  
  148.     /**
  149.      * Send the widgets to the callback instead of the values.
  150.      *
  151.      * @access protected
  152.      * @var    boolean 
  153.      */
  154.     protected $submitObjects;
  155.  
  156.     /**
  157.      * The default values for the form elements.
  158.      * 
  159.      * @access protected
  160.      * @var    array 
  161.      */
  162.     protected $defaults = array();
  163.  
  164.     /**
  165.      * A note identifying the required field marker.
  166.      * The renderer should prepend the required symbol to the note.
  167.      *
  168.      * @access protected
  169.      * @var    string 
  170.      */
  171.     protected $requiredNote = ' denotes required field';
  172.  
  173.     /**
  174.      * A symbol identifying the required fields.
  175.      * The renderer should provide some sort of formatting to make the symbol
  176.      * standout.
  177.      *
  178.      * @access protected
  179.      * @var    string 
  180.      */
  181.     protected $requiredSymbol = '*';
  182.  
  183.     /**
  184.      * The default renderer class defined by the element set.
  185.      *
  186.      * @access protected
  187.      * @var    string 
  188.      */
  189.     protected $defaultRenderer;
  190.  
  191.     /**
  192.      * The path to the default renderer defined by the element set.
  193.      *
  194.      * @access protected
  195.      * @var    string 
  196.      */
  197.     protected $defaultRendererPath;
  198.  
  199.     /**
  200.      * The object responsible for displaying the form.
  201.      *
  202.      * @access public
  203.      * @var    object 
  204.      */
  205.     public $renderer;
  206.  
  207.     /**
  208.      * The rendered form.
  209.      * 
  210.      * @access protected
  211.      * @var    mixed 
  212.      */
  213.     protected $renderedForm;
  214.  
  215.     /**
  216.      * An array relating error codes to error messages.
  217.      *
  218.      * @static
  219.      * @access protected
  220.      * @var    array 
  221.      */
  222.     protected static $errorMsgs =
  223.         array(
  224.               Structures_Form::ERROR_ADD_ELEMENT_DOUBLEADD =>
  225.               'Cannot add the element. The element or element name already exists in the form.',
  226.               Structures_Form::ERROR_ADD_RULE_DOUBLEADD =>
  227.               'Cannot create the rule. A rule of this type already exists in the form',
  228.               Structures_Form::ERROR_UNREGISTERED_ELEMENT =>
  229.               'Cannot add the element. The type is not registered.',
  230.               Structures_Form::ERROR_UNREGISTERED_RULE =>
  231.               'Cannot add the rule. The type is not registered.',
  232.               Structures_Form::ERROR_REGISTRATION_ELEMENT =>
  233.               'An error occured while trying to register the element.',
  234.               Structures_Form::ERROR_REGISTRATION_ELEMENT_DOUBLEREG =>
  235.               'The element could not be registered. An element of this type or with the same name is already registered.',
  236.               Structures_Form::ERROR_REGISTRATION_RULE =>
  237.               'An error occured while trying to register the rule.',
  238.               Structures_Form::ERROR_REGISTRATION_RULE_DOUBLEREG =>
  239.               'The rule could not be registered. A rule of this type or with the same name is already registered.',
  240.               Structures_Form::ERROR_UNREGISTRATION_ELEMENTINUSE =>
  241.               'The element could not be unregistered. It is currently in use.',
  242.               Structures_Form::ERROR_UNREGISTRATION_ELEMENT =>
  243.               'An error occured while trying to unregister the element.',
  244.               Structures_Form::ERROR_UNREGISTRATION_RULEINUSE =>
  245.               'The rule could not be unregistered. It is currently in use.',
  246.               Structures_Form::ERROR_UNREGISTRATION_RULE =>
  247.               'An error occured while trying to unregister the rule.',
  248.               Structures_Form::ERROR_LACKSINTERFACE_ELEMENT =>
  249.               'The element could not be added to the form. It does not implement the needed interface.',
  250.               Structures_Form::ERROR_LACKSINTERFACE_ELEMENTSET =>
  251.               'The element set could not be registered. It does not implement the needed interface.',
  252.               Structures_Form::ERROR_LACKSINTERFACE_RULE =>
  253.               'The rule could not be added to the form. It does not implement the needed interface.',
  254.               Structures_Form::ERROR_LACKSINTERFACE_GROUP =>
  255.               'The element group could not be added. It does not implement the needed interface.',
  256.               Structures_Form::ERROR_LACKSINTERFACE_RENDERER =>
  257.               'The renderer could not be set. It does not implement the needed interface.',
  258.               Structures_Form::ERROR_NONEXISTENT_CLASS =>
  259.               'The class does not exist.',
  260.               Structures_Form::ERROR_NONEXISTENT_FILE => 
  261.               'The file could not be read or does not exist.',
  262.               Structures_Form::ERROR_NONEXISTENT_ELEMENT =>
  263.               'The element does not exist.',
  264.               Structures_Form::ERROR_NONEXISTENT_CALLBACK =>
  265.               'The callback does not exist.',
  266.               Structures_Form::ERROR_RENDER_FAILURE =>
  267.               'An error occurred while trying to render the form.'
  268.               );
  269.     
  270.     // }}}
  271.     // {{{ Base 
  272.  
  273.     /**
  274.      * Constructor. Sets the callbacks then registers any default element or
  275.      * rule types.
  276.      *
  277.      * A callback must be passed in so that the form knows what to do with the
  278.      * values that are collected by the form. Optionally, the user may tell the
  279.      * form to pass the widgets themselves, not the values, to the callback.
  280.      * The user may also define an error callback, which will be called if any
  281.      * rules are viloated. If no error callback is defined, the default
  282.      * callback ($this->defaultErrorCallback()) will be used. The default just
  283.      * passes the errors on to the renderer.
  284.      *
  285.      * @access public
  286.      * @param  mixed   $callback      The method to call when the form is
  287.      *                                 submitted.
  288.      * @param  boolean $submitObjects Send the widgets to the callback not the
  289.      *                                 values.
  290.      * @param  mixed   $errorCallback A callback for handling errors.
  291.      * @return void 
  292.      * @throws Structures_Form_Exception
  293.      */
  294.     public function __construct($callback,
  295.                                 $submitObjects = false,
  296.                                 $errorCallback = null
  297.                                 )
  298.     {
  299.         // Make sure the callback exists.
  300.         if (!is_callable($callback)) {
  301.             require_once 'Structures/Form/Exception.php';
  302.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_NONEXISTENT_CALLBACK),
  303.                                                 self::ERROR_NONEXISTENT_CALLBACK
  304.                                                 );
  305.         }
  306.  
  307.         // Set the submit callback.
  308.         $this->submitCallback = $callback;
  309.  
  310.         // Set the error handling callback.
  311.         if (!is_callable($errorCallback)) {
  312.             $errorCallback = array($thisself::DEFAULT_ERRORCALLBACK);
  313.         }
  314.         $this->setErrorCallback($errorCallback);
  315.  
  316.         // Set whether or not we want to send the objects.
  317.         $this->submitObjects = $submitObjects;
  318.  
  319.         // Register default rules.
  320.         $this->registerDefaultRules();
  321.     }
  322.  
  323.     /**
  324.      * Sets the error callback.
  325.      *
  326.      * @access public
  327.      * @param  mixed  $callback A string or array.
  328.      * @return void 
  329.      */
  330.     public function setErrorCallback($callback)
  331.     {
  332.         $this->errorCallback = $callback;
  333.     }
  334.  
  335.     /**
  336.      * Returns the submit callback.
  337.      *
  338.      * @access public
  339.      * @return mixed  A string or array.
  340.      */
  341.     public function getSubmitCallback()
  342.     {
  343.         return $this->submitCallback;
  344.     }
  345.  
  346.     /**
  347.      * Returns the error callback.
  348.      *
  349.      * @access public
  350.      * @return mixed  A string or array.
  351.      */
  352.     public function getErrorCallback()
  353.     {
  354.         return $this->errorCallback;
  355.     }
  356.  
  357.     /**
  358.      * Registers a set of elements.
  359.      *
  360.      * Only elements types that are registered with Structures_Form may be
  361.      * added to the form. This helps to ensure that the elements implement the
  362.      * proper interface.
  363.      *
  364.      * Element sets must also define a default renderer.
  365.      *
  366.      * To avoid overhead, type registration is not checked until the type is
  367.      * added to the form.
  368.      *
  369.      * registerElement may throw an exception that will be passed along to the
  370.      * calling code.
  371.      *
  372.      * @access public
  373.      * @param  string  $elementSet The name of an element set.
  374.      * @return boolean true if the element set was registered.
  375.      * @throws Structures_Form_Exception
  376.      */
  377.     public function registerElementSet($elementSet)
  378.     {
  379.         // Try to get the element set.
  380.         if (!$this->isIncludable(self::ELEMENTSET_PATH . $elementSet '.php')) {
  381.             require_once 'Structures/Form/Exception.php';
  382.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_NONEXISTENT_FILE),
  383.                                                 self::ERROR_NONEXISTENT_FILE
  384.                                                 );
  385.         }
  386.  
  387.         // Include the element set file.
  388.         require_once self::ELEMENTSET_PATH . $elementSet '.php';
  389.  
  390.         // Get the correct element set classname.
  391.         $class = self::ELEMENTSET_PREFIX . $elementSet;
  392.  
  393.         // Instantiate the calss. The class must be instantiated to use
  394.         // instanceof. If there was only one method it might not be worth 
  395.         // instantiating the class but since we have two methods we might as
  396.         // well. I know the methods can be called statically but this is
  397.         // easier.
  398.         $eSet = new $class();
  399.  
  400.         // Check to see if the correct interface is implemented
  401.         require_once 'Structures/Form/ElementSetInterface.php';
  402.         if (!($eSet instanceof Structures_Form_ElementSetInterface)) {
  403.             require_once 'Structures/Form/Exception.php';
  404.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_LACKSINTERFACE_ELEMENTSET),
  405.                                                 self::ERROR_LACKSINTERFACE_ELEMENTSET
  406.                                                 );
  407.         }
  408.  
  409.         // Register the element set.
  410.         foreach ($eSet->getElementSet(as $element{
  411.             $this->registerElement($element[0]$element[1]$element[2]);
  412.         }
  413.         
  414.         // Set the default renderer.
  415.         $renderer $eSet->getDefaultRenderer();
  416.         $this->defaultRenderer     = $renderer['class'];
  417.         $this->defaultRendererPath = $renderer['path'];
  418.  
  419.         return true;
  420.     }
  421.  
  422.     /**
  423.      * Registers the default rule types.
  424.      *
  425.      * Rule objects are used to validate the values of the form elements before
  426.      * they are submitted to the callback. If an elements value violates a rule
  427.      * a rule callback will be called which allows the user to handle errors in
  428.      * their own way. All violations are collected on each submit and then
  429.      * passed off to the rule callback.
  430.      *
  431.      * Only registered rules may be applied to a form element.
  432.      * 
  433.      * All registered rules must implement the Structures_Form_RuleInterface.
  434.      *
  435.      * To avoid overhead, a rule is not checked until it is applied to a form
  436.      * element.
  437.      *
  438.      * @access protected
  439.      * @return void 
  440.      */
  441.     protected function registerDefaultRules()
  442.     {
  443.         // Create an array of the default rules.
  444.         $defaultRules = array(
  445.                               array(self::REQUIRED_RULE,
  446.                                     'Structures_Form_Rule_Required',
  447.                                     'Structures/Form/Rule/Required.php'
  448.                                     ),
  449.                               array('regex',
  450.                                     'Structures_Form_Rule_Regex',
  451.                                     'Structures/Form/Rule/Regex.php'
  452.                                     ),                              
  453.                               array('alpha',
  454.                                     'Structures_Form_Rule_Alpha',
  455.                                     'Structures/Form/Rule/Alpha.php'
  456.                                     ),                              
  457.                               array('numeric',
  458.                                     'Structures_Form_Rule_Numeric',
  459.                                     'Structures/Form/Rule/Numeric.php'
  460.                                     ),                              
  461.                               array('numericrange',
  462.                                     'Structures_Form_Rule_NumericRange',
  463.                                     'Structures/Form/Rule/NumericRange.php'
  464.                                     ),                              
  465.                               array('lengthrange',
  466.                                     'Structures_Form_Rule_LengthRange',
  467.                                     'Structures/Form/Rule/LengthRange.php'
  468.                                     ),                              
  469.                               array('alphanumeric',
  470.                                     'Structures_Form_Rule_AlphaNumeric',
  471.                                     'Structures/Form/Rule/AlphaNumeric.php'
  472.                                     )
  473.                               );
  474.  
  475.         // Register each rule.
  476.         foreach ($defaultRules as $rule{
  477.             $this->registerRule($rule[0]$rule[1]$rule[2]);
  478.         }
  479.     }
  480.  
  481.     /**
  482.      * Returns an error message for the given error code.
  483.      *
  484.      * @static
  485.      * @access public
  486.      * @param  integer $errorCode The error code to get a message for.
  487.      * @return string  An error message.
  488.      */
  489.     public static function getErrorMessage($errorCode)
  490.     {
  491.         return self::$errorMsgs[$errorCode];
  492.     }
  493.  
  494.     // }}}
  495.     // {{{ Elements
  496.  
  497.     /**
  498.      * Creates and adds an element to the form.
  499.      *
  500.      * Only elements types that are registered with Structures_Form may be added
  501.      * to the form. This helps to ensure that the elements implement the proper
  502.      * interface.
  503.      *
  504.      * To avoid overhead, type registration is not checked until the type is
  505.      * added to the form.
  506.      *
  507.      * This method passes any extra arguments on to the element constructor.
  508.      * This allows one method to be used for creating many types of elements.
  509.      * The exact number and type of arguments that is passed to this method
  510.      * varies depending on the type of element to be created. If the wrong
  511.      * number or type of arguments is passed, the element constructor should
  512.      * throw an exception.
  513.      *
  514.      * An exception may be thrown by createElement or addElementObject. If so
  515.      * it will be bubble up through this method.
  516.      *
  517.      * @access public
  518.      * @param  string $type The element type.
  519.      * @param  string $name The element name.
  520.      * @return object Structures_Form element
  521.      * @throws Structures_Form_Exception
  522.      */
  523.     public function addElement($type$name)
  524.     {
  525.         // Freak out if the element name is already in use.
  526.         if ($this->elementExists($name)) {
  527.             require_once 'Structures/Form/Exception.php';
  528.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_ADD_ELEMENT_DOUBLEADD),
  529.                                                 self::ERROR_ADD_ELEMENT_DOUBLEADD
  530.                                                 );
  531.         }
  532.  
  533.         // Create the element.
  534.         $args    func_get_args();
  535.         $element call_user_func_array(array($this'createElement')$args);
  536.  
  537.         // Add the element to the form.
  538.         return $this->addElementObject($element);
  539.     }
  540.     
  541.     /**
  542.      * Inserts an element object at the given location.
  543.      *
  544.      * Only elements types that are registered with Structures_Form may be added
  545.      * to the form. This helps to ensure that the elements implement the proper
  546.      * interface.
  547.      *
  548.      * To avoid overhead, type registration is not checked until the type is
  549.      * added to the form.
  550.      *
  551.      * @access public
  552.      * @param  object $element The element object.
  553.      * @return object The inserted object.
  554.      */
  555.     public function insertElement($element$position = -1)
  556.     {
  557.         // Make sure the type is registered. 
  558.         if (!$this->isElementRegistered($element->getType())) {
  559.             require_once 'Structures/Form/Exception.php';
  560.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_UNREGISTERED_ELEMENT),
  561.                                                 self::ERROR_UNREGISTERED_ELEMENT
  562.                                                 );
  563.         }
  564.         
  565.         // Make sure the element implements the needed interface.
  566.         require_once 'Structures/Form/ElementInterface.php';
  567.         if (!$element instanceof Structures_Form_ElementInterface{
  568.             require_once 'Structures/Form/Exception.php';
  569.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_LACKSINTERFACE_ELEMENT),
  570.                                                 self::ERROR_LACKSINTERFACE_ELEMENT
  571.                                                 );
  572.         }
  573.  
  574.         // Make sure an element with this name isn't already in the form.
  575.         if ($this->elementExists($element->getName())) {
  576.             require_once 'Structures/Form/Exception.php';
  577.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_ADD_ELEMENT_DOUBLEADD),
  578.                                                 self::ERROR_ADD_ELEMENT_DOUBLEADD
  579.                                                 );
  580.         }
  581.  
  582.         // Make sure this specific element was not already added.
  583.         foreach ($this->getAllElements(as $name => $elem{
  584.             if ($elem === $element{
  585.                 require_once 'Structures/Form/Exception.php';
  586.                 throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_ADD_ELEMENT_DOUBLEADD),
  587.                                                     self::ERROR_ADD_ELEMENT_DOUBLEADD
  588.                                                     );
  589.             }
  590.         }
  591.  
  592.         // Add the element to the elements array.
  593.         // Go through the current elements until we find the right position.
  594.         $elements = array();
  595.         $i        = 0;
  596.         foreach ($this->getAllElements(as $name => $elem{
  597.             if ($i++ == $position{
  598.                 $elements[$element->getName()$element;
  599.             }
  600.             $elements[$name$elem;
  601.         }
  602.  
  603.         // If the element was not inserted, add the element to the end.
  604.         if (!$this->elementExists($element->getName())) {
  605.             $elements[$element->getName()$element;
  606.         }
  607.  
  608.         // Update the elements array.
  609.         $this->elements = $elements;
  610.  
  611.         // Return the object.
  612.         return $element;
  613.     }
  614.  
  615.     /**
  616.      * Moves an element object from its current position to the given position.
  617.      *
  618.      * If the position is greater than the number of elements, the element will
  619.      * be added to the end of the form.
  620.      *
  621.      * @access public
  622.      * @param  object  $element  The element to move.
  623.      * @param  integer $position The new position.
  624.      * @return object  The object that was moved.
  625.      */
  626.     public function moveElement($element$position)
  627.     {
  628.         // First remove the element from the form.
  629.         $this->removeElementObject($element);
  630.  
  631.         // Then insert it in the given position.
  632.         return $this->insertElement($element$position);
  633.     }
  634.  
  635.     /**
  636.      * Creates an element but does not add it to the form.
  637.      * 
  638.      * Only elements types that are registered with Structures_Form may be added
  639.      * to the form. This helps to ensure that the elements implement the proper
  640.      * interface.
  641.      *
  642.      * To avoid overhead, type registration is not checked until the type is
  643.      * added to the form.
  644.      *
  645.      * This method passes any extra arguments on to the element constructor.
  646.      * This allows one method to be used for creating many types of elements.
  647.      * The exact number and type of arguments that is passed to this method
  648.      * varies depending on the type of element to be created. If the wrong
  649.      * number or type of arguments is passed, the element constructor should
  650.      * throw an exception.
  651.      *
  652.      * @access public
  653.      * @param  string $type The element type.
  654.      * @param  string $name The element name.
  655.      * @return object Structures_Form element
  656.      * @throws Structures_Form_Exception
  657.      */
  658.     public function createElement($type$name)
  659.     {
  660.         // Make sure the type is registered.
  661.         if (!$this->isElementRegistered($type)) {
  662.             require_once 'Structures/Form/Exception.php';
  663.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_UNREGISTERED_ELEMENT),
  664.                                                 self::ERROR_UNREGISTERED_ELEMENT
  665.                                                 );
  666.         }
  667.  
  668.         // Make sure an element with this name isn't already in the form.
  669.         if ($this->elementExists($name)) {
  670.             require_once 'Structures/Form/Exception.php';
  671.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_ADD_ELEMENT_DOUBLEADD),
  672.                                                 self::ERROR_ADD_ELEMENT_DOUBLEADD
  673.                                                 );
  674.         }
  675.  
  676.         // Include the element class file.
  677.         require_once $this->registeredElements[$type]['path'];
  678.  
  679.         // Get the class name.
  680.         $class $this->registeredElements[$type]['class'];
  681.         
  682.         // Make sure the class exists.
  683.         if (!class_exists($class)) {
  684.             require_once 'Structures/Form/Exception.php';
  685.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_REGISTRATION_ELEMENT),
  686.                                                 self::ERROR_NONEXISTENT_CLASS
  687.                                                 );
  688.         }
  689.  
  690.         // Try to instantiate the class.
  691.         // First add the form as the first argument.
  692.         $args    func_get_args();
  693.         unset($args[0]);
  694.         unset($args[1]);
  695.         array_unshift($args$this);
  696.  
  697.         // Instantiate the class.
  698.         $obj =  call_user_func_array(array(new ReflectionClass($class),
  699.                                            'newInstance'
  700.                                            ),
  701.                                      $args
  702.                                      );
  703.  
  704.         // Set the name of the object.
  705.         $obj->setName($name);
  706.         
  707.         // Return the object.
  708.         return $obj;
  709.     }
  710.  
  711.     /**
  712.      * Removes an element from the form by name.
  713.      *
  714.      * @access public
  715.      * @param  string $name 
  716.      * @return void 
  717.      */
  718.     public function removeElement($name)
  719.     {
  720.         if ($this->elementExists($name)) {
  721.             unset($this->elements[$name]);
  722.         }
  723.     }
  724.  
  725.     /**
  726.      * Adds an element to the form.
  727.      *
  728.      * Only elements types that are registered with Structures_Form may be added
  729.      * to the form. This helps to ensure that the elements implement the proper
  730.      * interface.
  731.      *
  732.      * To avoid overhead, type registration is not checked until the type is
  733.      * added to the form.
  734.      *
  735.      * @access public
  736.      * @param  object $element 
  737.      * @return object The added element.
  738.      */
  739.     public function addElementObject($element)
  740.     {
  741.         return $this->insertElement($element);
  742.     }
  743.  
  744.     /**
  745.      * Removes an element object from the form.
  746.      *
  747.      * This method is useful if an element has been added with the wrong name.
  748.      * Trying to remove it by the name returned by getName() will not work so
  749.      * you must remove it by the object.
  750.      *
  751.      * @access public
  752.      * @param  object $element 
  753.      * @return void 
  754.      */
  755.     public function removeElementObject($element)
  756.     {
  757.         // Loop through the current elements and see if the given element is
  758.         // part of the form.
  759.         foreach ($this->getAllElements(as $name => $elem{
  760.             if ($elem === $element{
  761.                 // Remove by the name the form thinks the element has.
  762.                 $this->removeElement($name);
  763.                 return;
  764.             }
  765.         }
  766.     }
  767.  
  768.     /**
  769.      * Registers an element type with this form.
  770.      *
  771.      * Before an element type can be used in a form, it must be registered.
  772.      * Registering an element type helps to make sure that type names are
  773.      * unique and that element classes implement the needed interface.
  774.      *
  775.      * To avoid overhead, registered classes are not checked until the type is
  776.      * instantiated.
  777.      *
  778.      * @access public
  779.      * @param  string  $name  The name to identify the element type.
  780.      * @param  string  $class The name of the element class.
  781.      * @param  string  $path  The path to the class definition.
  782.      * @return boolean true if the class was registered
  783.      * @throws Structures_Form_Exception
  784.      */
  785.     public function registerElement($name$class$path)
  786.     {
  787.         // Check to see if the element is already registered.
  788.         if ($this->isElementRegistered($name$class)) {
  789.             require_once 'Structures/Form/Exception.php';
  790.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_REGISTRATION_ELEMENT),
  791.                                                 self::ERROR_REGISTRATION_ELEMENT_DOUBLEREG
  792.                                                 );
  793.         }
  794.         
  795.         // If the class is not already declared, make sure the path is
  796.         // readable.
  797.         if (!class_exists($class&& !$this->isIncludable($path)) {
  798.             require_once 'Structures/Form/Exception.php';
  799.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_REGISTRATION_ELEMENT),
  800.                                                 self::ERROR_NONEXISTENT_FILE
  801.                                                 );
  802.         }        
  803.  
  804.         // Add the information to the registered elements array.
  805.         $this->registeredElements[$name= array('class' => $class,
  806.                                                  'path'  => $path
  807.                                                  );
  808.  
  809.         return $this->isElementRegistered($name);
  810.     }
  811.  
  812.     /**
  813.      * Returns whether or not a path is in the include path.
  814.      *
  815.      * @access public
  816.      * @param  string  $path 
  817.      * @return boolean true if the path is in the include path.
  818.      */
  819.     public function isIncludable($path)
  820.     {
  821.         // Break up the include path and check to see if the path is readable.
  822.         foreach (explode(PATH_SEPARATORget_include_path()) as $ip{
  823.             if (file_exists($ip . DIRECTORY_SEPARATOR . $path&&
  824.                 is_readable($ip . DIRECTORY_SEPARATOR . $path)
  825.                 {
  826.                 return true;
  827.             }
  828.         }
  829.         
  830.         // If we got down here, the path is not readable from the include path.
  831.         return false;
  832.     }
  833.  
  834.     /**
  835.      * Unregisters an element type with a form.
  836.      *
  837.      * An element type may not be unregistered if the form contains elements of
  838.      * the given type.
  839.      *
  840.      * You may want to unregister a type to prevent a certain type from being
  841.      * used in the form. For example if your app creates forms on the fly from
  842.      * user supplied data, you can unregister the text type to prevent users
  843.      * from creating a form with free form text entries.
  844.      *
  845.      * @access public
  846.      * @param  string  $type The element type name.
  847.      * @return boolean true if the type was unregistered
  848.      * @throws Structures_Form_Exception
  849.      */
  850.     public function unRegisterElement($type)
  851.     {
  852.         // Check to make sure the element isn't in use.
  853.         if (count($this->getElementsByType($type))) {
  854.             require_once 'Structures/Form/Exception.php';
  855.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_UNREGISTRATION_ELEMENT),
  856.                                                 self::ERROR_UNREGISTRATION_ELEMENTINUSE
  857.                                                 );
  858.         }
  859.  
  860.         // Just unset the element in the registered elements array.
  861.         unset($this->registeredElements[$type]);
  862.         
  863.         return !$this->isElementRegistered($type);
  864.     }
  865.  
  866.     /**
  867.      * Returns whether or not an element type is registered.
  868.      * 
  869.      * @access public
  870.      * @param  string  $type  The name of the element type.
  871.      * @param  string  $class Optional class name to check.
  872.      * @return boolean true if the element type is registered.
  873.      */
  874.     public function isElementRegistered($type$class = null)
  875.     {
  876.         // Check by name first. 
  877.         if (isset($this->registeredElements[$type]&&
  878.             is_array($this->registeredElements[$type])
  879.             {
  880.             return true;
  881.         elseif (!is_null($class)) {
  882.             // Check to see if the element is registered under a different
  883.             // name.
  884.             foreach ($this->registeredElements as $element{
  885.                 // Use a case insensitive comparison.
  886.                 if (strcmp($element['class']$class=== 0{
  887.                     return true;
  888.                 }
  889.             }
  890.         }
  891.             
  892.         // If we made it here, the class is not registered.
  893.         return false;
  894.     }
  895.  
  896.     /**
  897.      * Returns all of the elements in the form of the given type.
  898.      * 
  899.      * @access public
  900.      * @param  string $type The element type name.
  901.      * @return array 
  902.      */
  903.     public function getElementsByType($type)
  904.     {
  905.         // Loop through the elements and check their type.
  906.         $elements = array();
  907.         foreach ($this->getAllElements(as $name => $element{
  908.             if ($element->getType(== $type{
  909.                 $elements[$name$element;
  910.             }
  911.         }
  912.  
  913.         return $elements;
  914.     }
  915.  
  916.     /**
  917.      * Returns the form element with the given name.
  918.      *
  919.      * @access public
  920.      * @param  string $name The element name.
  921.      * @return mixed  Either the object or false if the object was not found.
  922.      */
  923.     public function getElement($name)
  924.     {
  925.         if ($this->elementExists($name)) {
  926.             return $this->elements[$name];
  927.         else {
  928.             // Check the element groups.
  929.             foreach ($this->getGroups(as $group{
  930.                 if ($group->elementExists($name)) {
  931.                     return $group->getElement($name);
  932.                 }
  933.             }
  934.             
  935.             return false;
  936.         }
  937.     }
  938.  
  939.     /**
  940.      * Returns an array of all elements.
  941.      *
  942.      * @access public
  943.      * @return array  An array of elements array(<name> => <element>)
  944.      */
  945.     public function getAllElements()
  946.     {
  947.         return $this->elements;
  948.     }
  949.  
  950.     /**
  951.      * Returns whether or not the given element is associated with this form.
  952.      *
  953.      * @access public
  954.      * @param  string  $name The name of the element to check.
  955.      * @return boolean true if the element is part of this form.
  956.      */
  957.     public function elementExists($name)
  958.     {
  959.         return (isset($this->elements[$name]&& 
  960.                 is_object($this->elements[$name])
  961.                 );
  962.     }
  963.  
  964.     /**
  965.      * Returns whether or not the element object is associated with this form.
  966.      *
  967.      * @access public
  968.      * @param  object  $element The element object.
  969.      * @return boolean true if the element is part of the form.
  970.      */
  971.     public function elementObjectExists($element)
  972.     {
  973.         // Go through all of the elements and check to see if the given element
  974.         // is in the array.
  975.         foreach ($this->getAllElements(as $elem{
  976.             if ($elem === $element{
  977.                 return true;
  978.             }
  979.         }
  980.  
  981.         return false;
  982.     }
  983.  
  984.     /**
  985.      * Returns the current position of the element.
  986.      *
  987.      * @access public
  988.      * @param  string  $name The element name.
  989.      * @return integer The current position.
  990.      */
  991.     public function getPosition($name)
  992.     {
  993.         return array_search($namearray_keys($this->elements));
  994.     }
  995.  
  996.     // }}}
  997.     // {{{ Groups
  998.  
  999.     /**
  1000.      * Disbands a group.
  1001.      *
  1002.      * This method disbands a group. It does not destroy the elements in the
  1003.      * group or even remove them from the form. It simply disbands the group
  1004.      * and makes the elements free floating individuals. The elements are
  1005.      * inserted where the group used to be.
  1006.      *
  1007.      * This method returns an array containing the names of any elements that
  1008.      * were in the group before it was disbanded.
  1009.      *
  1010.      * If the group does not exist, this method will return an empty array.
  1011.      *
  1012.      * @access public
  1013.      * @param  string $group The name of the group to disband.
  1014.      * @return array 
  1015.      */
  1016.     public function disbandGroup($group)
  1017.     {
  1018.         $elements = array();
  1019.         // Make sure the group exists.
  1020.         if ($this->groupExists($group)) {
  1021.             // Grab the group elements.
  1022.             $elements $this->getGroupElements($group);
  1023.  
  1024.             // Grab the current group position.
  1025.             $pos $this->getPosition($group);
  1026.  
  1027.             // Remove the elements from the group.
  1028.             foreach ($elements as $element{
  1029.                 $this->removeElementObjectFromGroup($element);
  1030.             }
  1031.  
  1032.             // Remove the group.
  1033.             $this->removeElement($group);
  1034.  
  1035.             // Reverse the elements and insert them into the position the group
  1036.             // used to occupy.
  1037.             $elements array_reverse($elements);
  1038.             $this->insertElementObject($element$pos);
  1039.         }
  1040.  
  1041.         return $elements;
  1042.     }
  1043.  
  1044.     /**
  1045.      * Returns whether or not the given group exists in this form.
  1046.      *
  1047.      * @access public
  1048.      * @param  string  $name The name of the group.
  1049.      * @return boolean true if the group exists.
  1050.      */
  1051.     public function groupExists($group)
  1052.     {
  1053.         // Check to see if an element with the group name exists.
  1054.         if (!$this->elementExists($group)) {
  1055.             return false;
  1056.         }
  1057.  
  1058.         // See if the element implements the group interface.
  1059.         $element $this->getElement($group);
  1060.         
  1061.         return $this->isGroup($element);
  1062.     }
  1063.  
  1064.     /**
  1065.      * Returns whether or not the element is a group.
  1066.      *
  1067.      * @access public
  1068.      * @param  object  $element The element to check.
  1069.      * @return boolean true if the element is an group.
  1070.      */
  1071.     public function isGroup($element)
  1072.     {
  1073.         require_once 'Structures/Form/GroupInterface.php';
  1074.         return ($element instanceof Structures_Form_GroupInterface);
  1075.     }
  1076.  
  1077.     /**
  1078.      * Returns the group element.
  1079.      *
  1080.      * If the group does not exists, this method returns false.
  1081.      *
  1082.      * @access public
  1083.      * @param  string $group The name of the group.
  1084.      * @return array 
  1085.      */
  1086.     public function getGroup($group)
  1087.     {
  1088.         if ($this->groupExists($group)) {
  1089.             return $this->getElement($group);
  1090.         else {
  1091.             return false;
  1092.         }
  1093.     }
  1094.  
  1095.     /**
  1096.      * Returns an array containing all groups.
  1097.      *
  1098.      * @access public
  1099.      * @return array 
  1100.      */
  1101.     public function getGroups()
  1102.     {
  1103.         // Loop through all the elements and figure out which ones are groups.
  1104.         $groups = array();
  1105.         foreach ($this->getAllElements(as $name => $element{
  1106.             if ($this->isGroup($element)) {
  1107.                 $groups[$name$element;
  1108.             }
  1109.         }
  1110.  
  1111.         return $groups;
  1112.     }
  1113.  
  1114.     /**
  1115.      * Adds an element to a group.
  1116.      * 
  1117.      * A group is just a way to logically and visually group form elements.
  1118.      * Elements can be added to or removed from a group without removing them
  1119.      * from the form all together. However, obviously if the element is removed
  1120.      * from the form, it will be removed from the group. If you readd the same
  1121.      * element object, it will NOT be readded to the group.
  1122.      *
  1123.      * Elements may only be part of one group. If the element is already part
  1124.      * of another group, it will not be added to this group.
  1125.      *
  1126.      * @access public
  1127.      * @param  string  $name  The name of the element.
  1128.      * @param  string  $group The name of the group.
  1129.      * @return boolean true if the element has been added to the group.
  1130.      */
  1131.     public function addElementToGroup($name$group)
  1132.     {
  1133.         // Check to make sure the element exists.
  1134.         if (!$this->elementExists($name)) {
  1135.             return false;
  1136.         }
  1137.  
  1138.         // Make sure the group exists.
  1139.         if (!$this->groupExists($group)) {
  1140.             return false;
  1141.         }
  1142.  
  1143.         // Grab the element.
  1144.         $element $this->getElement($name);
  1145.  
  1146.         // Add it by object.
  1147.         return $this->addElementObjectToGroup($element$group);
  1148.     }
  1149.  
  1150.     /**
  1151.      * Removes an element from a group.
  1152.      *
  1153.      * A group is just a way to logically and visually group form elements.
  1154.      * Elements can be added to or removed from a group without removing them
  1155.      * from the form all together. However, obviously if the element is removed
  1156.      * from the form, it will be removed from the group. If you readd the same
  1157.      * element object, it will NOT be readded to the group.
  1158.      *
  1159.      * @access public
  1160.      * @param  string $element The name of the element.
  1161.      * @param  string $group   The name of the group.
  1162.      * @return object The object that was removed or false
  1163.      */
  1164.     public function removeElementFromGroup($element$group)
  1165.     {
  1166.         // Make sure the group exists.
  1167.         if (!$this->groupExists($group)) {
  1168.             return false;
  1169.         }
  1170.  
  1171.         // Grab the group.
  1172.         $grp $this->getGroup($group);
  1173.  
  1174.         // Check to make sure the element is part of the group.
  1175.         if (!$grp->elementExists($element)) {
  1176.             return false;
  1177.         }
  1178.  
  1179.         // Remove the element from the group.
  1180.         return $this->removeElementObjectFromGroup($grp->getElement($element),
  1181.                                                    $group
  1182.                                                    );
  1183.     }
  1184.  
  1185.     /**
  1186.      * Adds an element object to a group.
  1187.      * 
  1188.      * A group is just a way to logically and visually group form elements.
  1189.      * Elements can be added to or removed from a group without removing them
  1190.      * from the form all together. However, obviously if the element is removed
  1191.      * from the form, it will be removed from the group. If you readd the same
  1192.      * element object, it will NOT be readded to the group.
  1193.      *
  1194.      * This method returns true if the element has been added to the group and
  1195.      * false in all other cases including when the element is not part of the
  1196.      * form or the group does not exist.
  1197.      *
  1198.      * Elements may only be part of one group. If the element is already part
  1199.      * of another group, it will not be added to this group.
  1200.      *
  1201.      * An element must be part of the form before it can be added to the group.
  1202.      * This is normally not a problem as elements should be created using
  1203.      * addElement or createElement.
  1204.      *
  1205.      * @access public
  1206.      * @param  string  $element The element.
  1207.      * @param  string  $group   The name of the group.
  1208.      * @return boolean true if the element has been added to the group.
  1209.      */
  1210.     public function addElementObjectToGroup($element$group)
  1211.     {
  1212.         // Check to make sure the element exists.
  1213.         if (!$this->elementExists($element->getName())) {
  1214.             return false;
  1215.         }
  1216.  
  1217.         // Make sure the group exists.
  1218.         if (!$this->groupExists($group)) {
  1219.             return false;
  1220.         }
  1221.  
  1222.         // Remove the element from the form.
  1223.         $this->removeElementObject($element);
  1224.         
  1225.         // Add the element to the group.
  1226.         $grp $this->getGroup($group);
  1227.  
  1228.         return $grp->addElement($element);
  1229.     }
  1230.  
  1231.     /**
  1232.      * Removes an element object from a group.
  1233.      *
  1234.      * A group is just a way to logically and visually group form elements.
  1235.      * Elements can be added to or removed from a group without removing them
  1236.      * from the form all together. However, obviously if the element is removed
  1237.      * from the form, it will be removed from the group. If you readd the same
  1238.      * element object, it will NOT be readded to the group.
  1239.      *
  1240.      * This method returns true if the element has been removed from the group
  1241.      * and false in all other cases including when the element is not part of
  1242.      * the form or the group does not exist.
  1243.      *
  1244.      * @access public
  1245.      * @param  string $element The element.
  1246.      * @param  string $group   The name of the group.
  1247.      * @return object The object that was removed or false.
  1248.      */
  1249.     public function removeElementObjectFromGroup($element$group)
  1250.     {
  1251.         // Make sure the group exists.
  1252.         if (!$this->groupExists($group)) {
  1253.             return false;
  1254.         }
  1255.         
  1256.         // Grab the group.
  1257.         $grp $this->getGroup($group);
  1258.  
  1259.         // Check to make sure the element is part of the group.
  1260.         if (!$grp->elementExists($element->getName())) {
  1261.             return false;
  1262.         }
  1263.  
  1264.         // Remove the element from the group.
  1265.         $grp->removeElement($element);
  1266.  
  1267.         return $element;
  1268.     }
  1269.  
  1270.     /**
  1271.      * Returns an array containing the names of the elements in the given
  1272.      * group.
  1273.      *
  1274.      * If the group does not exist, this method returns an empty array.
  1275.      *
  1276.      * @access public
  1277.      * @param  string $name The name of the group
  1278.      * @return array 
  1279.      */
  1280.     public function getGroupElements($name)
  1281.     {
  1282.         // Make sure the group exists.
  1283.         if (!$this->groupExists($group)) {
  1284.             return false;
  1285.         }
  1286.         
  1287.         // Grab the group.
  1288.         $grp $this->getGroup($group);
  1289.  
  1290.         return $grp->getAllElements();
  1291.     }
  1292.  
  1293.     // }}}
  1294.     // {{{ Values
  1295.  
  1296.     /**
  1297.      * Returns the current value for all elements in an associative array.
  1298.      * 
  1299.      * The array will be of the form: array(<name> => <value>);
  1300.      *
  1301.      * @access public
  1302.      * @return array 
  1303.      */
  1304.     public function getValues()
  1305.     {
  1306.         // Loop through all the elements and collect their values.
  1307.         $values = array();
  1308.         foreach ($this->getAllElements(as $name => $element{
  1309.             // Check to see if the element is a group.
  1310.             if ($this->isGroup($element)) {
  1311.                 // Merge the values from the group with the other values.
  1312.                 $values array_merge($values$element->getValue());
  1313.             else {
  1314.                 // Just add the element's value to the array.
  1315.                 $values[$name$element->getValue();
  1316.             }
  1317.         }
  1318.  
  1319.         return $values;
  1320.     }
  1321.  
  1322.     /**
  1323.      * Returns the current value for the given element.
  1324.      *
  1325.      * @access public
  1326.      * @param  string $name The element name.
  1327.      * @return mixed  The value of the element or group of elements.
  1328.      * @throws Structures_Form_Exception
  1329.      */
  1330.     public function getValue($name)
  1331.     {
  1332.         // Check to see if the element exists.
  1333.         if (!$this->elementExists($name)) {
  1334.             // Throw an exception because returning any value could be 
  1335.             // misleading.
  1336.             require_once 'Structures/Form/Exception.php';
  1337.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_NONEXISTENT_ELEMENT),
  1338.                                                 self::ERROR_NONEXISTENT_ELEMENT
  1339.                                                 );
  1340.         }
  1341.  
  1342.         // Get the element.
  1343.         $element $this->getElement($name);
  1344.  
  1345.         // Return the element's value. A groups return value will be an array.
  1346.         return $element->getValue();
  1347.     }
  1348.  
  1349.     /**
  1350.      * Sets the values for the form elements.
  1351.      * 
  1352.      * The array passed in should be of the form: array(<name> => <value>)
  1353.      *
  1354.      * @uses setValue()
  1355.      *
  1356.      * @access public
  1357.      * @param  array  $values An array of values.
  1358.      * @return array  An array containing names of elements whose value has
  1359.      *                 chagned
  1360.      */
  1361.     public function setValues($values)
  1362.     {
  1363.         // Loop through the array and try to set the values of each element.
  1364.         $success = array();
  1365.         foreach ($values as $element => $value{
  1366.             // We want to prevent throwing an exception here so check for the 
  1367.             // element first.
  1368.             if ($this->elementExists($element)) {
  1369.                 // Try to set the value for the element.
  1370.                 if ($this->setValue($element$value)) {
  1371.                     // Value was set.
  1372.                     $success[$element;
  1373.                 }
  1374.             }
  1375.         }
  1376.  
  1377.         return $success;
  1378.     }
  1379.  
  1380.     /**
  1381.      * Sets the value for the given element.
  1382.      *
  1383.      * This method returns true if the value appears to have been changed. If
  1384.      * false is returned, it may indicated that the element does not exist,
  1385.      * that the new value was the same as the old value, or that it was frozen.
  1386.      *
  1387.      * @access public
  1388.      * @param  string  $name  The name of the element
  1389.      * @param  mixed   $value The element value.
  1390.      * @return boolean true if the element's value was set.
  1391.      */
  1392.     public function setValue($name$value)
  1393.     {
  1394.         // Check to see if the element exists.
  1395.         if (!$this->elementExists($name)) {
  1396.             // Throw an exception because returning any value could be 
  1397.             // misleading.
  1398.             require_once 'Structures/Form/Exception.php';
  1399.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_NONEXISTENT_ELEMENT),
  1400.                                                 self::ERROR_NONEXISTENT_ELEMENT
  1401.                                                 );
  1402.         }
  1403.  
  1404.         // Get the element.
  1405.         $element $this->getElement($name);
  1406.  
  1407.         // Set the value.
  1408.         return $element->setValue($value);        
  1409.     }
  1410.  
  1411.     /**
  1412.      * Clears the values of all form elements (if possible).
  1413.      *
  1414.      * @access public
  1415.      * @return array  An array of elements whose value has been cleared.
  1416.      */
  1417.     public function clearValues()
  1418.     {
  1419.         // Loop through all elements and call the clearValue method.
  1420.         $success = array();
  1421.         foreach ($this->getAllElements(as $name => $element{
  1422.             if ($element->clearValue()) {
  1423.                 // Value was cleared successfully.
  1424.                 $success[$name;
  1425.             }
  1426.         }
  1427.  
  1428.         return $success;
  1429.     }
  1430.  
  1431.     /**
  1432.      * Sets the values of the elements and make the given values the defautls.
  1433.      *
  1434.      * This method over writes any values currently set!
  1435.      *
  1436.      * @access public
  1437.      * @param  array  $defaults An array of default values.
  1438.      * @return array  An array containing names of elements whose value has
  1439.      *                 changed.
  1440.      */
  1441.     public function setDefaults($defaults)
  1442.     {
  1443.         // Set the array as the defaults.
  1444.         $this->defaults = $defaults;
  1445.  
  1446.         // Set the values.
  1447.         return $this->setValues($defaults);
  1448.     }
  1449.  
  1450.     /**
  1451.      * Restores the elements to their default values.
  1452.      *
  1453.      * This method over writes any values currently set!
  1454.      *
  1455.      * If no defaults were set, this method will return an empty array.
  1456.      *
  1457.      * @access public
  1458.      * @return array  An array containing names of elements whose value has
  1459.      *                 changed.
  1460.      */
  1461.     public function restoreDefaults()
  1462.     {
  1463.         return $this->setValues($this->defaults);
  1464.     }
  1465.  
  1466.     /**
  1467.      * Returns the array of default values.
  1468.      *
  1469.      * @access public
  1470.      * @return array 
  1471.      */
  1472.     public function getDefaults()
  1473.     {
  1474.         return $this->defaults;
  1475.     }
  1476.  
  1477.     /**
  1478.      * Collects submitted values and calls the callback or error callback.
  1479.      *
  1480.      * @access public
  1481.      * @return boolean false to continue processing callbacks.
  1482.      */
  1483.     public function submit()
  1484.     {
  1485.         // Validate the form.
  1486.         $failures $this->validate();
  1487.  
  1488.         // Process the errors. An empty array will clear any existing errors.
  1489.         call_user_func_array($this->getErrorCallback()array($failures));
  1490.  
  1491.         if (!count($failures)) {
  1492.             // Check to see if the form widgets should be submitted instead of
  1493.             // the values.
  1494.             if ($this->submitObjects{
  1495.                 // Send the widgets!
  1496.                 $values $this->getAllElements();
  1497.             else {
  1498.                 // Send the values.
  1499.                 $values $this->getValues();
  1500.             }
  1501.             
  1502.             // Call the user's callback.
  1503.             call_user_func_array($this->getSubmitCallback()array($values));
  1504.             
  1505.             // Continue calling callbacks.
  1506.             return false;
  1507.         else {
  1508.             // Stop processing callbacks.
  1509.             return true;
  1510.         }
  1511.     }
  1512.     
  1513.     /**
  1514.      * Simply passes the error array on to the renderer.
  1515.      *
  1516.      * It might be a good idea to re-render the form after errors are added but
  1517.      * that is the responsibility of the developer and the renderer. It cannot
  1518.      * be determined here if the user wants to re-render. Nor can the form
  1519.      * reliably be changed form here.
  1520.      *
  1521.      * If errors need to be handled in a different way, you should set a custom
  1522.      * error callback either when the form is constructed or by using
  1523.      * setErrorCallback().
  1524.      *
  1525.      * @access public
  1526.      * @param  array  $errors An array of error messages.
  1527.      * @return void 
  1528.      */
  1529.     public function defaultErrorCallback($errors)
  1530.     {
  1531.         // Check to see if the renderer is set. (It almost has to be)
  1532.         if (empty($this->renderer)) {
  1533.             $this->setDefaultRenderer();
  1534.         }
  1535.  
  1536.         // Pass off the errrors.
  1537.         $this->renderer->setErrors($errors);
  1538.     }
  1539.  
  1540.     /**
  1541.      * Validates the values of the form against the applied rules.
  1542.      *
  1543.      * This method returns an array even if there are no errors. If the array
  1544.      * is empty, then no errors occurred or no rules were applied.
  1545.      *
  1546.      * @access public
  1547.      * @return array  An array of error messages
  1548.      */
  1549.     public function validate()
  1550.     {
  1551.         // Call each rule and check for errors.
  1552.         $errors = array();
  1553.         foreach ($this->rules as $name => $rule{
  1554.             $rObj $rule['object'];
  1555.             // Validate the elements.
  1556.             foreach ($rule['elements'as $element{
  1557.                 $result $rObj->validate($this->getElement($element));
  1558.                 if ($result !== true{
  1559.                     // Errors were found.
  1560.                     $errors[$result;
  1561.                 }
  1562.             }
  1563.         }
  1564.  
  1565.         return $errors;
  1566.     }
  1567.  
  1568.     // }}}
  1569.     // {{{ Rules
  1570.  
  1571.     /**
  1572.      * Registers a rule class with the form.
  1573.      *
  1574.      * Rule objects are used to validate the values of the form elements before
  1575.      * they are submitted to the callback. If an elements value violates a rule
  1576.      * a rule callback will be called which allows the user to handle errors in
  1577.      * their own way. All violations are collected on each submit and then
  1578.      * passed off to the rule callback.
  1579.      *
  1580.      * Only registered rules may be applied to a form element.
  1581.      * 
  1582.      * All registered rules must implement the Structures_Form_RuleInterface.
  1583.      *
  1584.      * To avoid overhead, a rule is not checked until it is applied to a form
  1585.      * element.
  1586.      *
  1587.      * @access public
  1588.      * @param  string  $name  A name to identify the rule.
  1589.      * @param  string  $class The name of the rule class.
  1590.      * @param  string  $path  The path to the class definition.
  1591.      * @return boolean true if the rule was registered.
  1592.      * @throws Structures_Form_Exception
  1593.      */
  1594.     public function registerRule($name$class$path)
  1595.     {
  1596.         // Check to see if the element is already registered.
  1597.         if ($this->isRuleRegistered($name$class)) {
  1598.             require_once 'Structures/Form/Exception.php';
  1599.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_REGISTRATION_RULE),
  1600.                                                 self::ERROR_REGISTRATION_RULE_DOUBLEREG
  1601.                                                 );
  1602.         }
  1603.         
  1604.         // If the class is not already declared, make sure the path is
  1605.         // readable.
  1606.         if (!class_exists($class&& !$this->isIncludable($path)) {
  1607.             require_once 'Structures/Form/Exception.php';
  1608.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_REGISTRATION_RULE),
  1609.                                                 self::ERROR_NONEXISTENT_FILE
  1610.                                                 );
  1611.         }        
  1612.  
  1613.         // Add the information to the registered elements array.
  1614.         $this->registeredRules[$name= array('class' => $class,
  1615.                                               'path'  => $path
  1616.                                               );
  1617.  
  1618.         return $this->isElementRegistered($name);
  1619.     }
  1620.  
  1621.     /**
  1622.      * Unregisters a rule with the form.
  1623.      *
  1624.      * Only registered rules may be applied to a form element.
  1625.      *
  1626.      * A rule may not be unregistered if it is currently being applied to one
  1627.      * or more form elements.
  1628.      *
  1629.      * You may want to unregister a rule to prevent it from being applied to a
  1630.      * form. For example, if your app creates forms on the fly, you may want to
  1631.      * unregister a rule to prevent the user from applying a rule that their
  1632.      * PHP installation cannot support (regular expressions may not be
  1633.      * available).
  1634.      * 
  1635.      * @access public
  1636.      * @param  string  $name The name of the rule class.
  1637.      * @return boolean true if the rule was unregistered.
  1638.      * @throws Structures_Form_Exception
  1639.      */
  1640.     public function unRegisterRule($name)
  1641.     {
  1642.         // Make sure the rule is not applied.
  1643.         if ($this->isRuleApplied($name)) {
  1644.             require_once 'Structures/Form/Exception.php';
  1645.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_UNREGISTRATION_RULE),
  1646.                                                 self::ERROR_UNREGISTRATION_RULEINUSE
  1647.                                                 );
  1648.         }
  1649.  
  1650.         // Just unset the registered rule element for this rule.
  1651.         unset($this->registeredRules[$name]);
  1652.  
  1653.         return $this->isRuleRegistered($name);
  1654.     }
  1655.  
  1656.     /**
  1657.      * Returns whether or not a rule is registered.
  1658.      *
  1659.      * @access public
  1660.      * @param  string  $rule  The name of the rule type.
  1661.      * @param  string  $class Optional class name to check.
  1662.      * @return boolean true if the rule is registered.
  1663.      */
  1664.     public function isRuleRegistered($rule$class = null)
  1665.     {
  1666.         // Check by name first. 
  1667.         if (isset($this->registeredRules[$rule]&&
  1668.             is_array($this->registeredRules[$rule])
  1669.             {
  1670.             return true;
  1671.         elseif (!is_null($class)) {
  1672.             // Check to see if the rule is registered under a different name.
  1673.             foreach ($this->registeredRules as $rule{
  1674.                 // Use a case insensitive comparison.
  1675.                 if (strcasecmp($rule['class']$class=== 0{
  1676.                     return true;
  1677.                 }
  1678.             }
  1679.         }
  1680.             
  1681.         // If we made it here, the class is not registered.
  1682.         return false;
  1683.     }
  1684.  
  1685.     /**
  1686.      * Associates a rule with a form element.
  1687.      *
  1688.      * When a form is submitted, the values will be checked against any rules
  1689.      * that have been associated with the elements. A rule of a give type can
  1690.      * only be added to an individual element once. Attempting to add a rule
  1691.      * to the same element twice will have no extra effect.
  1692.      *
  1693.      * This method passes any extra arguments on to the rule constructor.
  1694.      * This allows one method to be used for creating many types of rules.
  1695.      * The exact number and type of arguments that is passed to this method
  1696.      * varies depending on the type of rule to be created. If the wrong
  1697.      * number or type of arguments is passed, the rule constructor should
  1698.      * throw an exception.
  1699.      *
  1700.      * @access public
  1701.      * @param  string  $name  The name of the element.
  1702.      * @param  string  $rule  The name of the rule.
  1703.      * @return boolean true if the rule was applied.
  1704.      */
  1705.     public function addRule($name$rule)
  1706.     {
  1707.         // Check to make sure the element exists.
  1708.         if (!$this->elementExists($name)) {
  1709.             return false;
  1710.         }
  1711.  
  1712.         // Make sure the rule is registered.
  1713.         if (!$this->isRuleRegistered($rule)) {
  1714.             return false;
  1715.         }
  1716.  
  1717.         // See if we need to create the rule object.
  1718.         if (!isset($this->rules[$rule]['object']||
  1719.             !is_object($this->rules[$rule]['object'])
  1720.             {
  1721.             // Create the rule object.
  1722.             $args func_get_args();
  1723.             unset($args[0]);
  1724.          
  1725.             // Create the rule object.
  1726.             $rObj call_user_func_array(array($this'createRule'),
  1727.                                          $args
  1728.                                          );
  1729.             
  1730.             // Set up the rules array.
  1731.             $this->rules[$rule= array('object'   => $rObj,
  1732.                                         'elements' => array());
  1733.         }
  1734.  
  1735.         // Associate the rule with the element.
  1736.         $this->rules[$rule]['elements'][$name;
  1737.         
  1738.         return true;
  1739.     }
  1740.  
  1741.     /**
  1742.      * Creates a rule object.
  1743.      *
  1744.      * When a form is submitted, the values will be checked against any rules
  1745.      * that have been associated with the elements. A rule of a give type can
  1746.      * only be added to an individual element once. Attempting to add a rule
  1747.      * to the same element twice will have no extra effect.
  1748.      *
  1749.      * This method passes any extra arguments on to the rule constructor.
  1750.      * This allows one method to be used for creating many types of rules.
  1751.      * The exact number and type of arguments that is passed to this method
  1752.      * varies depending on the type of rule to be created. If the wrong
  1753.      * number or type of arguments is passed, the rule constructor should
  1754.      * throw an exception.
  1755.      *
  1756.      * @access public
  1757.      * @param  string  $rule  The name of the rule.
  1758.      * @return boolean true if the rule was applied.
  1759.      */
  1760.     public function createRule($rule)
  1761.     {
  1762.         // Make sure the rule is registered.
  1763.         if (!$this->isRuleRegistered($rule)) {
  1764.             require_once 'Structures/Form/Exception.php';
  1765.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_UNREGISTERED_RULE),
  1766.                                                 self::ERROR_UNREGISTERED_RULE
  1767.                                                 );
  1768.         }
  1769.  
  1770.         // Make sure an element with this name isn't already in the form.
  1771.         if ($this->ruleExists($rule)) {
  1772.             require_once 'Structures/Form/Exception.php';
  1773.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_ADD_RULE_DOUBLEADD),
  1774.                                                 self::ERROR_ADD_RULE_DOUBLEADD
  1775.                                                 );
  1776.         }
  1777.  
  1778.         // Include the element class file.
  1779.         require_once $this->registeredRules[$rule]['path'];
  1780.  
  1781.         // Get the class name.
  1782.         $class $this->registeredRules[$rule]['class'];
  1783.         
  1784.         // Make sure the class exists.
  1785.         if (!class_exists($class)) {
  1786.             require_once 'Structures/Form/Exception.php';
  1787.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_REGISTRATION_RULE),
  1788.                                                 self::ERROR_NONEXISTENT_CLASS
  1789.                                                 );
  1790.         }
  1791.  
  1792.         // Try to instantiate the class.
  1793.         // First add the form as the first argument. <-- Not needed.
  1794.         $args    func_get_args();
  1795.         unset($args[0]);
  1796.         //$args[0] = $this;
  1797.  
  1798.         // Next instantiate the class.
  1799.         $rObj call_user_func_array(array(new ReflectionClass($class),
  1800.                                            'newInstance'
  1801.                                            ),
  1802.                                      $args
  1803.                                      );
  1804.  
  1805.         // Make sure the object implements the correct interface.
  1806.         require_once 'Structures/Form/RuleInterface.php';
  1807.         if (!$rObj instanceof Structures_Form_RuleInterface{
  1808.             require_once 'Structures/Form/Exception.php';
  1809.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_LACKSINTERFACE_RULE),
  1810.                                                 self::ERROR_LACKSINTERFACE_RULE
  1811.                                                 );
  1812.         }
  1813.  
  1814.         return $rObj;
  1815.     }
  1816.  
  1817.     /**
  1818.      * Removes a rule from an element.
  1819.      *
  1820.      * When a form is submitted, the values will be checked against any rules
  1821.      * that have been associated with the elements. A rule of a give type can
  1822.      * only be added to an individual element once. Attempting to add a rule
  1823.      * to the same element twice will have no extra effect. Therefore, it only
  1824.      * makes sense to try to remove a rule once.
  1825.      *
  1826.      * This method will return true if the rule is not associated with the
  1827.      * element. This means that it will also return true even if the rule was
  1828.      * never associated with the element in the first place.
  1829.      * 
  1830.      * @access public
  1831.      * @param  string  $element The name of the element to remove the rule from
  1832.      * @param  string  $rule    The name of the rule to remove
  1833.      * @return boolean true if the rule is no longer associated with the
  1834.      *                  element
  1835.      */
  1836.     public function removeRule($element$rule)
  1837.     {
  1838.         // Check to make sure the element exists.
  1839.         if (!$this->elementExists($name)) {
  1840.             return false;
  1841.         }
  1842.  
  1843.         // Make sure the rule is registered.
  1844.         if (!$this->isRuleRegistered($rule)) {
  1845.             return false;
  1846.         }
  1847.  
  1848.         // Remove the element/rule association.
  1849.         if (isset($this->rules[$rule]['elements']&&
  1850.             in_array($this->rules[$rule]['elements']$element)
  1851.             {
  1852.             $key array_search($this->rules[$rule]['elements']$element);
  1853.             unset($this->rules[$rule]['elements'][$key]);
  1854.         }
  1855.  
  1856.         return true;
  1857.     }
  1858.  
  1859.     /**
  1860.      * Associates a rule with a form element object.
  1861.      *
  1862.      * When a form is submitted, the values will be checked against any rules
  1863.      * that have been associated with the elements. A rule of a give type can
  1864.      * only be added to an individual element once. Attempting to add a rule
  1865.      * to the same element twice will have no extra effect.
  1866.      *
  1867.      * This method passes any extra arguments on to the rule constructor.
  1868.      * This allows one method to be used for creating many types of rules.
  1869.      * The exact number and type of arguments that is passed to this method
  1870.      * varies depending on the type of rule to be created. If the wrong
  1871.      * number or type of arguments is passed, the rule constructor should
  1872.      * throw an exception.
  1873.      *
  1874.      * @access public
  1875.      * @param  object  $element The form element object
  1876.      * @param  string  $rule    The form rule name.
  1877.      * @return boolean true if the rule was applied.
  1878.      */
  1879.     public function addRuleToObject($element$rule)
  1880.     {
  1881.         // Check to make sure the element exists.
  1882.         if (!$this->elementExists($element->getName())) {
  1883.             return false;
  1884.         }
  1885.  
  1886.         // Make sure the rule is registered.
  1887.         if (!$this->isRuleRegistered($rule)) {
  1888.             return false;
  1889.         }
  1890.  
  1891.         // Apply the rule to the element.
  1892.         $args    func_get_args();
  1893.         $args[0$element->getName();
  1894.  
  1895.         return call_user_func_array(array($this'addRule')$args);
  1896.     }
  1897.  
  1898.     /**
  1899.      * Removes a rule from a form element object.
  1900.      *
  1901.      * When a form is submitted, the values will be checked against any rules
  1902.      * that have been associated with the elements. A rule of a give type can
  1903.      * only be added to an individual element once. Attempting to add a rule
  1904.      * to the same element twice will have no extra effect. Therefore, it only
  1905.      * makes sense to try to remove a rule once.
  1906.      *
  1907.      * This method will return true if the rule is not associated with the
  1908.      * element. This means that it will also return true even if the rule was
  1909.      * never associated with the element in the first place.
  1910.      * 
  1911.      * @access public
  1912.      * @param  object  $element The form element to remove the rule from.
  1913.      * @param  string  $rule    The rule to remove from the object.
  1914.      * @return boolean true if the rule is no longer associated with the
  1915.      *                  element
  1916.      */
  1917.     public function removeRuleFromObject($element$rule)
  1918.     {
  1919.         // Check to make sure the element exists.
  1920.         if (!$this->elementExists($element->getName())) {
  1921.             return false;
  1922.         }
  1923.  
  1924.         // Make sure the rule is registered.
  1925.         if (!$this->isRuleRegistered($rule)) {
  1926.             return false;
  1927.         }
  1928.         
  1929.         // We only need to disassociate the rule with the name.
  1930.         $this->removeRule($element->getName()$rule);
  1931.     }
  1932.  
  1933.     /**
  1934.      * Returns whether or not the give rule is currently associated with any
  1935.      * form elements.
  1936.      *
  1937.      * @access public
  1938.      * @param  string  $name The name of the form rule to check.
  1939.      * @return boolean true if the rule is currently associated with an element
  1940.      */
  1941.     public function isRuleApplied($name)
  1942.     {
  1943.         return (isset($this->rules[$name]&& 
  1944.                 count($this->rules[$name]['elements'])
  1945.                 );
  1946.     }
  1947.  
  1948.     /**
  1949.      * Returns whether or not a rule with the given name already exists.
  1950.      *
  1951.      * @access public
  1952.      * @param  string  $rule The name fo the rule.
  1953.      * @return boolean true if the name is in use.
  1954.      */
  1955.     public function ruleExists($rule)
  1956.     {
  1957.         return array_key_exists($rule$this->rules);
  1958.     }
  1959.  
  1960.     /**
  1961.      * Sets the require note.
  1962.      *
  1963.      * @access public
  1964.      * @param  string $note The text to use.
  1965.      * @return void 
  1966.      */
  1967.     public function setRequiredNote($note)
  1968.     {
  1969.         $this->requiredNote = $note;
  1970.     }
  1971.     
  1972.     /**
  1973.      * Returns the current required note.
  1974.      *
  1975.      * @access public
  1976.      * @return string 
  1977.      */
  1978.     public function getRequiredNote()
  1979.     {
  1980.         return $this->requiredNote;
  1981.     }
  1982.  
  1983.     /**
  1984.      * Sets the require symbol.
  1985.      *
  1986.      * @access public
  1987.      * @param  string $symbol The text to use.
  1988.      * @return void 
  1989.      */
  1990.     public function setRequiredSymbol($symbol)
  1991.     {
  1992.         $this->requiredSymbol = $symbol;
  1993.     }
  1994.     
  1995.     /**
  1996.      * Returns the current required symbol.
  1997.      *
  1998.      * @access public
  1999.      * @return string 
  2000.      */
  2001.     public function getRequiredSymbol()
  2002.     {
  2003.         return $this->requiredSymbol;
  2004.     }
  2005.  
  2006.     /**
  2007.      * Returns whether or not an element is required.
  2008.      *
  2009.      * @access public
  2010.      * @param  string  $element The name of the element.
  2011.      * @return boolean true if the required rule is applied to the element.
  2012.      */
  2013.     public function isRequired($element)
  2014.     {
  2015.         return (isset($this->rules[self::REQUIRED_RULE]['elements']&&
  2016.                 in_array($element$this->rules[self::REQUIRED_RULE]['elements'])
  2017.                 );
  2018.     }
  2019.  
  2020.     /**
  2021.      * Returns whether or not the given rule is applied to the given element.
  2022.      *
  2023.      * This method returns true if the rule is applied to the element and false
  2024.      * in all other cases including if the rule or element is not defined.
  2025.      *
  2026.      * @access public
  2027.      * @param  string  $element The name of the element.
  2028.      * @param  string  $rule    The name of the rule.
  2029.      * @return boolean true if the rule is applied to the element.
  2030.      */
  2031.     public function isRuleAppliedToElement($element$rule)
  2032.     {
  2033.         return (isset($this->rules[$rule]&&
  2034.                 in_array($this->rules[$rule]['elements']$element)
  2035.                 );
  2036.     }
  2037.  
  2038.     // }}}
  2039.     // {{{ Display
  2040.  
  2041.     /**
  2042.      * Sets a renderer object.
  2043.      *
  2044.      * Renderers must implement Structures_Form_RendererInterface. This helps to
  2045.      * ensure consistency among the API and avoid any fatal errors. A renderer
  2046.      * is used to position and display the form elements within a container
  2047.      * widget.
  2048.      *
  2049.      * Unlike rules and elements, renderers are created on their own (not
  2050.      * through a form method). This is because they do not need to know
  2051.      * thing about the form at construction time and the form does not need to
  2052.      * know anything about the renderer until the form is to be displayed.
  2053.      *
  2054.      * A form may only have one renderer at a time. Setting a second renderer
  2055.      * will overwrite the first. If no renderer is set, the default renderer
  2056.      * will be used.
  2057.      *
  2058.      * @access public
  2059.      * @param  object $renderer An object that implements
  2060.      *                           Structures_Form::RENDERER_INTERFACE
  2061.      * @return void 
  2062.      * @throws Structures_Form_Exception
  2063.      */
  2064.     public function setRenderer($renderer)
  2065.     {
  2066.         // Make sure that the renderer is an object and that it implements the
  2067.         // needed interface.
  2068.         require_once 'Structures/Form/RendererInterface.php';
  2069.         if (!is_object($renderer||
  2070.             !$renderer instanceof Structures_Form_RendererInterface
  2071.             {
  2072.             require_once 'Structures/Form/Exception.php';
  2073.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_LACKSINTERFACE_RENDERER),
  2074.                                                 self::ERROR_LACKSINTERFACE_RENDERER
  2075.                                                 );
  2076.         }
  2077.  
  2078.         // Set the renderer.
  2079.         $this->renderer = $renderer;
  2080.  
  2081.         // Set the form for the renderer.
  2082.         $this->renderer->setForm($this);
  2083.     }
  2084.  
  2085.     /**
  2086.      * Creates a default renderer object.
  2087.      *
  2088.      * This method may be called by group elements that need to renderer
  2089.      * elements but do not have a renderer set.
  2090.      *
  2091.      * @access public
  2092.      * @return object. 
  2093.      */
  2094.     public function getDefaultRenderer()
  2095.     {
  2096.         // Require the default renderer file.
  2097.         require_once $this->defaultRendererPath;
  2098.  
  2099.         // Create an instance of the default renderer.
  2100.         $class $this->defaultRenderer;
  2101.         $obj   = new $class();
  2102.  
  2103.         return $obj;
  2104.     }
  2105.  
  2106.     /**
  2107.      * Creates a default renderer and sets it as the current renderer.
  2108.      *
  2109.      * An exception may be thrown by setRenderer. It will pass through to the
  2110.      * calling function.
  2111.      *
  2112.      * @access protected
  2113.      * @return void 
  2114.      */
  2115.     protected function setDefaultRenderer()
  2116.     {
  2117.         // Get a default renderer.
  2118.         $obj $this->getDefaultRenderer();
  2119.  
  2120.         // Set the renderer.
  2121.         $this->setRenderer($obj);
  2122.     }
  2123.  
  2124.     /**
  2125.      * Returns a container widget holding the form elements.
  2126.      *
  2127.      * Passes the elements, required note and required symbol to the renderer
  2128.      * and then calls renderer().
  2129.      *
  2130.      * Pulling elements out of a renderer is a pain in the ass. Trying to find
  2131.      * the correct widget (or parent widget) can be nearly impossible.
  2132.      * Therefore, even if you remove a widget from the form, it will still
  2133.      * appear if the widget was removed after the form was rendered.
  2134.      *
  2135.      * @access public
  2136.      * @return object container widget holding the form.
  2137.      * @throws Structures_Form_Exception
  2138.      */
  2139.     public function render()
  2140.     {
  2141.         // Check to see if a renderer has been set.
  2142.         if (empty($this->renderer)) {
  2143.             // Try to create a default renderer.
  2144.             try {
  2145.                 require_once 'Structures/Form/Exception.php';
  2146.                 $this->setDefaultRenderer();
  2147.             catch (Structures_Form_Exception $sfe{
  2148.                 // Set a prettier error message but keep the same code.
  2149.                 throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_RENDER_FAILURE),
  2150.                                          $sfe
  2151.                                          );
  2152.             }
  2153.         }
  2154.  
  2155.         // Pass the elements to the renderer.
  2156.         $this->renderer->setElements($this->getAllElements());
  2157.  
  2158.         // Pass the required note and symbol.
  2159.         $this->renderer->setRequiredNote($this->requiredNote);
  2160.         $this->renderer->setRequiredSymbol($this->requiredSymbol);
  2161.  
  2162.         // Try to renderer the form.
  2163.         try {
  2164.             $form $this->renderer->render();
  2165.             
  2166.             // Keep track of the rendered widget.
  2167.             $this->renderedForm = $form;
  2168.  
  2169.             // Return the container widget.
  2170.             return $form;
  2171.         catch (Structures_Form_Exception $sfe{
  2172.             // Set a prettier error message but keep the same code.
  2173.             throw new Structures_Form_Exception(self::getErrorMessage(self::ERROR_RENDER_FAILURE),
  2174.                                                 $sfe
  2175.                                                 );
  2176.         }          
  2177.     }
  2178.  
  2179.     /**
  2180.      * Returns the most recently rendered widget.
  2181.      *
  2182.      * This method will not render the form! It only returns the widget created
  2183.      * when render() was called!
  2184.      *
  2185.      * @access public
  2186.      * @return object The rendered form.
  2187.      */
  2188.     public function getRenderedForm()
  2189.     {
  2190.         return $this->renderedForm;
  2191.     }
  2192.  
  2193.     /**
  2194.      * Changes the UI of the form from the current set of elements to the given
  2195.      * element set.
  2196.      *
  2197.      * This method allows you to quickly and easily change from one element set
  2198.      * to another. In most cases it is only necessary to change renderers but
  2199.      * some renderers may have additional requirements for the elements that
  2200.      * necessitate changing the classes that represent the elements.
  2201.      * 
  2202.      * @access public
  2203.      * @param  object  $elementSet A Structures_Form_Element set.
  2204.      * @return boolean true if the element set was changed successfully.
  2205.      */
  2206.     public function swapElementSet(Structures_Form_ElementSet $elementSet)
  2207.     {
  2208.         // First, get the new element set.
  2209.         foreach ($elementSet as $element{
  2210.             // Next, remove all of the elements for each type.
  2211.             $removed = array();
  2212.             foreach ($this->getElementsByType($element[0]as $elem{
  2213.                 $removed[$elem->getName()$elem;
  2214.                 $this->removeElementObject($elem);
  2215.             }
  2216.                 
  2217.             // Next, unregister the old type and register the new type.
  2218.             $this->unRegisterElement($element[0]);
  2219.             $this->registerElement($element[0]$element[1]$element[2]);
  2220.             
  2221.             // Then create elements of the new type.
  2222.             $new = array();
  2223.             foreach ($removed as $name => $elem{
  2224.                 $newElem $this->createElement($elem->getType(),
  2225.                                                 $name,
  2226.                                                 $elem->getLabel()
  2227.                                                 );
  2228.  
  2229.                 // Set all of the element's data.
  2230.                 $newElem->setValue($elem->getValue());
  2231.                 $elem->isFrozen($newElem->freeze($newElem->unfreeze();
  2232.                 $newElem->setLabel($elem->getLabel());
  2233.  
  2234.                 // Then re-add the new element.
  2235.                 $this->addElementObject($newElem);
  2236.             }
  2237.         }
  2238.  
  2239.         // Finally, set a new default renderer.
  2240.         $renderer $elementSet->getDefaultRenderer();
  2241.         $this->defaultRenderer     = $renderer['class'];
  2242.         $this->defaultRendererPath = $renderer['path'];
  2243.  
  2244.         return true;
  2245.     }
  2246.     // }}}
  2247. }
  2248. ?>

Documentation generated on Mon, 11 Mar 2019 14:44:13 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.