home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / QuickForm.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  65.6 KB  |  1,809 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4.0                                                      |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Adam Daniel <adaniel1@eesus.jnj.com>                        |
  17. // |          Bertrand Mansion <bmansion@mamasam.com>                     |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: QuickForm.php,v 1.139 2004/03/20 11:23:10 avb Exp $
  21.  
  22. require_once('PEAR.php');
  23. require_once('HTML/Common.php');
  24.  
  25. $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] = 
  26.         array(
  27.             'group'         =>array('HTML/QuickForm/group.php','HTML_QuickForm_group'),
  28.             'hidden'        =>array('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'),
  29.             'reset'         =>array('HTML/QuickForm/reset.php','HTML_QuickForm_reset'),
  30.             'checkbox'      =>array('HTML/QuickForm/checkbox.php','HTML_QuickForm_checkbox'),
  31.             'file'          =>array('HTML/QuickForm/file.php','HTML_QuickForm_file'),
  32.             'image'         =>array('HTML/QuickForm/image.php','HTML_QuickForm_image'),
  33.             'password'      =>array('HTML/QuickForm/password.php','HTML_QuickForm_password'),
  34.             'radio'         =>array('HTML/QuickForm/radio.php','HTML_QuickForm_radio'),
  35.             'button'        =>array('HTML/QuickForm/button.php','HTML_QuickForm_button'),
  36.             'submit'        =>array('HTML/QuickForm/submit.php','HTML_QuickForm_submit'),
  37.             'select'        =>array('HTML/QuickForm/select.php','HTML_QuickForm_select'),
  38.             'hiddenselect'  =>array('HTML/QuickForm/hiddenselect.php','HTML_QuickForm_hiddenselect'),
  39.             'text'          =>array('HTML/QuickForm/text.php','HTML_QuickForm_text'),
  40.             'textarea'      =>array('HTML/QuickForm/textarea.php','HTML_QuickForm_textarea'),
  41.             'link'          =>array('HTML/QuickForm/link.php','HTML_QuickForm_link'),
  42.             'advcheckbox'   =>array('HTML/QuickForm/advcheckbox.php','HTML_QuickForm_advcheckbox'),
  43.             'date'          =>array('HTML/QuickForm/date.php','HTML_QuickForm_date'),
  44.             'static'        =>array('HTML/QuickForm/static.php','HTML_QuickForm_static'),
  45.             'header'        =>array('HTML/QuickForm/header.php', 'HTML_QuickForm_header'),
  46.             'html'          =>array('HTML/QuickForm/html.php', 'HTML_QuickForm_html'),
  47.             'hierselect'    =>array('HTML/QuickForm/hierselect.php', 'HTML_QuickForm_hierselect'),
  48.             'autocomplete'  =>array('HTML/QuickForm/autocomplete.php', 'HTML_QuickForm_autocomplete')
  49.         );
  50.  
  51. $GLOBALS['_HTML_QuickForm_registered_rules'] = array(
  52.     'required'      => array('html_quickform_rule_required', 'HTML/QuickForm/Rule/Required.php'),
  53.     'maxlength'     => array('html_quickform_rule_range',    'HTML/QuickForm/Rule/Range.php'),
  54.     'minlength'     => array('html_quickform_rule_range',    'HTML/QuickForm/Rule/Range.php'),
  55.     'rangelength'   => array('html_quickform_rule_range',    'HTML/QuickForm/Rule/Range.php'),
  56.     'email'         => array('html_quickform_rule_email',    'HTML/QuickForm/Rule/Email.php'),
  57.     'regex'         => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
  58.     'lettersonly'   => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
  59.     'alphanumeric'  => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
  60.     'numeric'       => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
  61.     'nopunctuation' => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
  62.     'nonzero'       => array('html_quickform_rule_regex',    'HTML/QuickForm/Rule/Regex.php'),
  63.     'callback'      => array('html_quickform_rule_callback', 'HTML/QuickForm/Rule/Callback.php'),
  64.     'compare'       => array('html_quickform_rule_compare',  'HTML/QuickForm/Rule/Compare.php')
  65. );
  66.  
  67. // {{{ error codes
  68.  
  69. /*
  70.  * Error codes for the QuickForm interface, which will be mapped to textual messages
  71.  * in the QuickForm::errorMessage() function.  If you are to add a new error code, be
  72.  * sure to add the textual messages to the QuickForm::errorMessage() function as well
  73.  */
  74.  
  75. define('QUICKFORM_OK',                      1);
  76. define('QUICKFORM_ERROR',                  -1);
  77. define('QUICKFORM_INVALID_RULE',           -2);
  78. define('QUICKFORM_NONEXIST_ELEMENT',       -3);
  79. define('QUICKFORM_INVALID_FILTER',         -4);
  80. define('QUICKFORM_UNREGISTERED_ELEMENT',   -5);
  81. define('QUICKFORM_INVALID_ELEMENT_NAME',   -6);
  82. define('QUICKFORM_INVALID_PROCESS',        -7);
  83. define('QUICKFORM_DEPRECATED',             -8);
  84. define('QUICKFORM_INVALID_DATASOURCE',     -9);
  85.  
  86. // }}}
  87.  
  88. /**
  89. * Create, validate and process HTML forms
  90. *
  91. * @author      Adam Daniel <adaniel1@eesus.jnj.com>
  92. * @author      Bertrand Mansion <bmansion@mamasam.com>
  93. * @version     2.0
  94. * @since       PHP 4.0.3pl1
  95. */
  96. class HTML_QuickForm extends HTML_Common {
  97.     // {{{ properties
  98.  
  99.     /**
  100.      * Array containing the form fields
  101.      * @since     1.0
  102.      * @var  array
  103.      * @access   private
  104.      */
  105.     var $_elements = array();
  106.  
  107.     /**
  108.      * Array containing element name to index map
  109.      * @since     1.1
  110.      * @var  array
  111.      * @access   private
  112.      */
  113.     var $_elementIndex = array();
  114.  
  115.     /**
  116.      * Array containing indexes of duplicate elements
  117.      * @since     2.10
  118.      * @var  array
  119.      * @access   private
  120.      */
  121.     var $_duplicateIndex = array();
  122.  
  123.     /**
  124.      * Array containing required field IDs
  125.      * @since     1.0
  126.      * @var  array
  127.      * @access   private
  128.      */ 
  129.     var $_required = array();
  130.  
  131.     /**
  132.      * Prefix message in javascript alert if error
  133.      * @since     1.0
  134.      * @var  string
  135.      * @access   public
  136.      */ 
  137.     var $_jsPrefix = 'Invalid information entered.';
  138.  
  139.     /**
  140.      * Postfix message in javascript alert if error
  141.      * @since     1.0
  142.      * @var  string
  143.      * @access   public
  144.      */ 
  145.     var $_jsPostfix = 'Please correct these fields.';
  146.  
  147.     /**
  148.      * Datasource object implementing the informal
  149.      * datasource protocol
  150.      * @since     3.3
  151.      * @var  object
  152.      * @access   private
  153.      */
  154.     var $_datasource;
  155.  
  156.     /**
  157.      * Array of default form values
  158.      * @since     2.0
  159.      * @var  array
  160.      * @access   private
  161.      */
  162.     var $_defaultValues = array();
  163.  
  164.     /**
  165.      * Array of constant form values
  166.      * @since     2.0
  167.      * @var  array
  168.      * @access   private
  169.      */
  170.     var $_constantValues = array();
  171.  
  172.     /**
  173.      * Array of submitted form values
  174.      * @since     1.0
  175.      * @var  array
  176.      * @access   private
  177.      */
  178.     var $_submitValues = array();
  179.  
  180.     /**
  181.      * Array of submitted form files
  182.      * @since     1.0
  183.      * @var  integer
  184.      * @access   public
  185.      */
  186.     var $_submitFiles = array();
  187.  
  188.     /**
  189.      * Value for maxfilesize hidden element if form contains file input
  190.      * @since     1.0
  191.      * @var  integer
  192.      * @access   public
  193.      */
  194.     var $_maxFileSize = 1048576; // 1 Mb = 1048576
  195.  
  196.     /**
  197.      * Flag to know if all fields are frozen
  198.      * @since     1.0
  199.      * @var  boolean
  200.      * @access   private
  201.      */
  202.     var $_freezeAll = false;
  203.  
  204.     /**
  205.      * Array containing the form rules
  206.      * @since     1.0
  207.      * @var  array
  208.      * @access   private
  209.      */
  210.     var $_rules = array();
  211.  
  212.     /**
  213.      * Form rules, global variety
  214.      * @var     array
  215.      * @access  private
  216.      */
  217.     var $_formRules = array();
  218.  
  219.     /**
  220.      * Array containing the validation errors
  221.      * @since     1.0
  222.      * @var  array
  223.      * @access   private
  224.      */
  225.     var $_errors = array();
  226.  
  227.     /**
  228.      * Note for required fields in the form
  229.      * @var       string
  230.      * @since     1.0
  231.      * @access    public
  232.      */
  233.     var $_requiredNote = '<span style="font-size:80%; color:#ff0000;">*</span><span style="font-size:80%;"> denotes required field</span>';
  234.  
  235.     // }}}
  236.     // {{{ constructor
  237.  
  238.     /**
  239.      * Class constructor
  240.      * @param    string      $formName          Form's name.
  241.      * @param    string      $method            (optional)Form's method defaults to 'POST'
  242.      * @param    string      $action            (optional)Form's action
  243.      * @param    string      $target            (optional)Form's target defaults to '_self'
  244.      * @param    mixed       $attributes        (optional)Extra attributes for <form> tag
  245.      * @param    bool        $trackSubmit       (optional)Whether to track if the form was submitted by adding a special hidden field
  246.      * @access   public
  247.      */
  248.     function HTML_QuickForm($formName='', $method='post', $action='', $target='_self', $attributes=null, $trackSubmit = false)
  249.     {
  250.         HTML_Common::HTML_Common($attributes);
  251.         $method = (strtoupper($method) == 'GET') ? 'get' : 'post';
  252.         $action = ($action == '') ? $_SERVER['PHP_SELF'] : $action;
  253.         $target = (empty($target) || $target == '_self') ? array() : array('target' => $target);
  254.         $attributes = array('action'=>$action, 'method'=>$method, 'name'=>$formName, 'id'=>$formName) + $target;
  255.         $this->updateAttributes($attributes);
  256.         if (!$trackSubmit || isset($_REQUEST['_qf__' . $formName])) {
  257.             if (1 == get_magic_quotes_gpc()) {
  258.                 $this->_submitValues = $this->_recursiveFilter('stripslashes', 'get' == $method? $_GET: $_POST);
  259.             } else {
  260.                 $this->_submitValues = 'get' == $method? $_GET: $_POST;
  261.             }
  262.             $this->_submitFiles =& $_FILES;
  263.         }
  264.         if ($trackSubmit) {
  265.             unset($this->_submitValues['_qf__' . $formName]);
  266.             $this->addElement('hidden', '_qf__' . $formName, null);
  267.         }
  268.     } // end constructor
  269.  
  270.     // }}}
  271.     // {{{ apiVersion()
  272.  
  273.     /**
  274.      * Returns the current API version
  275.      *
  276.      * @since     1.0
  277.      * @access    public
  278.      * @return    float
  279.      */
  280.     function apiVersion()
  281.     {
  282.         return 3.2;
  283.     } // end func apiVersion
  284.  
  285.     // }}}
  286.     // {{{ registerElementType()
  287.  
  288.     /**
  289.      * Registers a new element type
  290.      *
  291.      * @param     string    $typeName   Name of element type
  292.      * @param     string    $include    Include path for element type
  293.      * @param     string    $className  Element class name
  294.      * @since     1.0
  295.      * @access    public
  296.      * @return    void
  297.      */
  298.     function registerElementType($typeName, $include, $className)
  299.     {
  300.         $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($typeName)] = array($include, $className);
  301.     } // end func registerElementType
  302.  
  303.     // }}}
  304.     // {{{ registerRule()
  305.  
  306.     /**
  307.      * Registers a new validation rule
  308.      *
  309.      * @param     string    $ruleName   Name of validation rule
  310.      * @param     string    $type       Either: 'regex', 'function' or 'rule' for an HTML_QuickForm_Rule object
  311.      * @param     string    $data1      Name of function, regular expression or HTML_QuickForm_Rule classname
  312.      * @param     string    $data2      Object parent of above function or HTML_QuickForm_Rule file path
  313.      * @since     1.0
  314.      * @access    public
  315.      * @return    void
  316.      */
  317.     function registerRule($ruleName, $type, $data1, $data2 = null)
  318.     {
  319.         include_once('HTML/QuickForm/RuleRegistry.php');
  320.         $registry =& HTML_QuickForm_RuleRegistry::singleton();
  321.         $registry->registerRule($ruleName, $type, $data1, $data2);
  322.     } // end func registerRule
  323.  
  324.     // }}}
  325.     // {{{ elementExists()
  326.  
  327.     /**
  328.      * Returns true if element is in the form
  329.      *
  330.      * @param     string   $element         form name of element to check
  331.      * @since     1.0
  332.      * @access    public
  333.      * @return    boolean
  334.      */
  335.     function elementExists($element=null)
  336.     {
  337.         return isset($this->_elementIndex[$element]);
  338.     } // end func elementExists
  339.  
  340.     // }}}
  341.     // {{{ setDatasource()
  342.  
  343.     /**
  344.      * Sets a datasource object for this form object
  345.      *
  346.      * Datasource default and constant values will feed the QuickForm object if
  347.      * the datasource implements defaultValues() and constantValues() methods.
  348.      *
  349.      * @param     object   $datasource          datasource object implementing the informal datasource protocol
  350.      * @param     mixed    $defaultsFilter      string or array of filter(s) to apply to default values
  351.      * @param     mixed    $constantsFilter     string or array of filter(s) to apply to constants values
  352.      * @since     3.3
  353.      * @access    public
  354.      * @return    void
  355.      */
  356.     function setDatasource(&$datasource, $defaultsFilter = null, $constantsFilter = null)
  357.     {
  358.         if (is_object($datasource)) {
  359.             $this->_datasource =& $datasource;
  360.             if (is_callable(array($datasource, 'defaultValues'))) {
  361.                 $this->setDefaults($datasource->defaultValues($this), $defaultsFilter);
  362.             }
  363.             if (is_callable(array($datasource, 'constantValues'))) {
  364.                 $this->setConstants($datasource->constantValues($this), $constantsFilter);
  365.             }
  366.         } else {
  367.             return PEAR::raiseError(null, QUICKFORM_INVALID_DATASOURCE, null, E_USER_WARNING, "Datasource is not an object in QuickForm::setDatasource()", 'HTML_QuickForm_Error', true);
  368.         }
  369.     } // end func setDatasource
  370.  
  371.     // }}}
  372.     // {{{ setDefaults()
  373.  
  374.     /**
  375.      * Initializes default form values
  376.      *
  377.      * @param     array    $defaultValues       values used to fill the form
  378.      * @param     mixed    $filter              (optional) filter(s) to apply to all default values
  379.      * @since     1.0
  380.      * @access    public
  381.      * @return    void
  382.      */
  383.     function setDefaults($defaultValues = null, $filter = null)
  384.     {
  385.         if (is_array($defaultValues)) {
  386.             if (isset($filter)) {
  387.                 if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) {
  388.                     foreach ($filter as $val) {
  389.                         if (!is_callable($val)) {
  390.                             return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
  391.                         } else {
  392.                             $defaultValues = $this->_recursiveFilter($val, $defaultValues);
  393.                         }
  394.                     }
  395.                 } elseif (!is_callable($filter)) {
  396.                     return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
  397.                 } else {
  398.                     $defaultValues = $this->_recursiveFilter($filter, $defaultValues);
  399.                 }
  400.             }
  401.             $this->_defaultValues = HTML_QuickForm::arrayMerge($this->_defaultValues, $defaultValues);
  402.             foreach (array_keys($this->_elements) as $key) {
  403.                 $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
  404.             }
  405.         }
  406.     } // end func setDefaults
  407.  
  408.     // }}}
  409.     // {{{ setConstants()
  410.  
  411.     /**
  412.      * Initializes constant form values.
  413.      * These values won't get overridden by POST or GET vars
  414.      *
  415.      * @param     array   $constantValues        values used to fill the form    
  416.      * @param     mixed    $filter              (optional) filter(s) to apply to all default values    
  417.      *
  418.      * @since     2.0
  419.      * @access    public
  420.      * @return    void
  421.      */
  422.     function setConstants($constantValues = null, $filter = null)
  423.     {
  424.         if (is_array($constantValues)) {
  425.             if (isset($filter)) {
  426.                 if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) {
  427.                     foreach ($filter as $val) {
  428.                         if (!is_callable($val)) {
  429.                             return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
  430.                         } else {
  431.                             $constantValues = $this->_recursiveFilter($val, $constantValues);
  432.                         }
  433.                     }
  434.                 } elseif (!is_callable($filter)) {
  435.                     return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
  436.                 } else {
  437.                     $constantValues = $this->_recursiveFilter($filter, $constantValues);
  438.                 }
  439.             }
  440.             $this->_constantValues = HTML_QuickForm::arrayMerge($this->_constantValues, $constantValues);
  441.             foreach (array_keys($this->_elements) as $key) {
  442.                 $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
  443.             }
  444.         }
  445.     } // end func setConstants
  446.  
  447.     // }}}
  448.     // {{{ setMaxFileSize()
  449.  
  450.     /**
  451.      * Sets the value of MAX_FILE_SIZE hidden element
  452.      *
  453.      * @param     int    $bytes    Size in bytes
  454.      * @since     3.0
  455.      * @access    public
  456.      * @return    void
  457.      */
  458.     function setMaxFileSize($bytes = 0)
  459.     {
  460.         if ($bytes > 0) {
  461.             $this->_maxFileSize = $bytes;
  462.         }
  463.         if (!$this->elementExists('MAX_FILE_SIZE')) {
  464.             $this->addElement('hidden', 'MAX_FILE_SIZE', $this->_maxFileSize);
  465.         } else {
  466.             $el =& $this->getElement('MAX_FILE_SIZE');
  467.             $el->updateAttributes(array('value' => $this->_maxFileSize));
  468.         }
  469.     } // end func setMaxFileSize
  470.  
  471.     // }}}
  472.     // {{{ getMaxFileSize()
  473.  
  474.     /**
  475.      * Returns the value of MAX_FILE_SIZE hidden element
  476.      *
  477.      * @since     3.0
  478.      * @access    public
  479.      * @return    int   max file size in bytes
  480.      */
  481.     function getMaxFileSize()
  482.     {
  483.         return $this->_maxFileSize;
  484.     } // end func getMaxFileSize
  485.  
  486.     // }}}
  487.     // {{{ &createElement()
  488.  
  489.     /**
  490.      * Creates a new form element of the given type.
  491.      * 
  492.      * This method accepts variable number of parameters, their 
  493.      * meaning and count depending on $elementType
  494.      *
  495.      * @param     string     $elementType    type of element to add (text, textarea, file...)
  496.      * @since     1.0
  497.      * @access    public
  498.      * @return    object extended class of HTML_element
  499.      * @throws    HTML_QuickForm_Error
  500.      */
  501.     function &createElement($elementType)
  502.     {
  503.         $args = func_get_args();
  504.         return HTML_QuickForm::_loadElement('createElement', $elementType, array_slice($args, 1));
  505.     } // end func createElement
  506.     
  507.     // }}}
  508.     // {{{ _loadElement()
  509.  
  510.     /**
  511.      * Returns a form element of the given type
  512.      *
  513.      * @param     string   $event   event to send to newly created element ('createElement' or 'addElement')
  514.      * @param     string   $type    element type
  515.      * @param     array    $args    arguments for event
  516.      * @since     2.0
  517.      * @access    private
  518.      * @return    object    a new element
  519.      * @throws    HTML_QuickForm_Error
  520.      */
  521.     function &_loadElement($event, $type, $args)
  522.     {
  523.         $type = strtolower($type);
  524.         if (!HTML_QuickForm::isTypeRegistered($type)) {
  525.             return PEAR::raiseError(null, QUICKFORM_UNREGISTERED_ELEMENT, null, E_USER_WARNING, "Element '$type' does not exist in HTML_QuickForm::_loadElement()", 'HTML_QuickForm_Error', true);
  526.         }
  527.         $className = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][1];
  528.         $includeFile = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][0];
  529.         include_once($includeFile);
  530.         $elementObject =& new $className();
  531.         for ($i = 0; $i < 5; $i++) {
  532.             if (!isset($args[$i])) {
  533.                 $args[$i] = null;
  534.             }
  535.         }
  536.         $err = $elementObject->onQuickFormEvent($event, $args, $this);
  537.         if ($err !== true) {
  538.             return $err;
  539.         }
  540.         return $elementObject;
  541.     } // end func _loadElement
  542.  
  543.     // }}}
  544.     // {{{ addElement()
  545.  
  546.     /**
  547.      * Adds an element into the form
  548.      * 
  549.      * If $element is a string representing element type, then this 
  550.      * method accepts variable number of parameters, their meaning 
  551.      * and count depending on $element
  552.      *
  553.      * @param    mixed      $element        element object or type of element to add (text, textarea, file...)
  554.      * @since    1.0
  555.      * @return   object     reference to element
  556.      * @access   public
  557.      * @throws   HTML_QuickForm_Error
  558.      */
  559.     function &addElement($element)
  560.     {
  561.         if (is_object($element) && is_subclass_of($element, 'html_quickform_element')) {
  562.            $elementObject = &$element;
  563.            $elementObject->onQuickFormEvent('updateValue', null, $this);
  564.         } else {
  565.             $args = func_get_args();
  566.             $elementObject =& $this->_loadElement('addElement', $element, array_slice($args, 1));
  567.             if (PEAR::isError($elementObject)) {
  568.                 return $elementObject;
  569.             }
  570.         }
  571.         $elementName = $elementObject->getName();
  572.  
  573.         // Add the element if it is not an incompatible duplicate
  574.         if (!empty($elementName) && isset($this->_elementIndex[$elementName])) {
  575.             if ($this->_elements[$this->_elementIndex[$elementName]]->getType() ==
  576.                 $elementObject->getType()) {
  577.                 $this->_elements[] =& $elementObject;
  578.                 $this->_duplicateIndex[$elementName][] = end(array_keys($this->_elements));
  579.             } else {
  580.                 return PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::addElement()", 'HTML_QuickForm_Error', true);
  581.             }
  582.         } else {
  583.             $this->_elements[] =& $elementObject;
  584.             $this->_elementIndex[$elementName] = end(array_keys($this->_elements));
  585.         }
  586.  
  587.         return $elementObject;
  588.     } // end func addElement
  589.     
  590.     // }}}
  591.     // {{{ addGroup()
  592.  
  593.     /**
  594.      * Adds an element group
  595.      * @param    array      $elements       array of elements composing the group
  596.      * @param    string     $name           (optional)group name
  597.      * @param    string     $groupLabel     (optional)group label
  598.      * @param    string     $separator      (optional)string to separate elements
  599.      * @param    string     $appendName     (optional)specify whether the group name should be
  600.      *                                      used in the form element name ex: group[element]
  601.      * @return   object     reference to added group of elements
  602.      * @since    2.8
  603.      * @access   public
  604.      * @throws   PEAR_Error
  605.      */
  606.     function &addGroup($elements, $name=null, $groupLabel='', $separator=null, $appendName = true)
  607.     {
  608.         static $anonGroups = 1;
  609.  
  610.         if (0 == strlen($name)) {
  611.             $name       = 'qf_group_' . $anonGroups++;
  612.             $appendName = false;
  613.         }
  614.         return $this->addElement('group', $name, $groupLabel, $elements, $separator, $appendName);
  615.     } // end func addGroup
  616.     
  617.     // }}}
  618.     // {{{ &getElement()
  619.  
  620.     /**
  621.      * Returns a reference to the element
  622.      *
  623.      * @param     string     $element    Element name
  624.      * @since     2.0
  625.      * @access    public
  626.      * @return    object     reference to element
  627.      * @throws    HTML_QuickForm_Error
  628.      */
  629.     function &getElement($element)
  630.     {
  631.         if (isset($this->_elementIndex[$element])) {
  632.             return $this->_elements[$this->_elementIndex[$element]];
  633.         } else {
  634.             return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElement()", 'HTML_QuickForm_Error', true);
  635.         }
  636.     } // end func getElement
  637.  
  638.     // }}}
  639.     // {{{ &getElementValue()
  640.  
  641.     /**
  642.      * Returns the element's raw value
  643.      * 
  644.      * This returns the value as submitted by the form (not filtered) 
  645.      * or set via setDefaults() or setConstants()
  646.      *
  647.      * @param     string     $element    Element name
  648.      * @since     2.0
  649.      * @access    public
  650.      * @return    mixed     element value
  651.      * @throws    HTML_QuickForm_Error
  652.      */
  653.     function &getElementValue($element)
  654.     {
  655.         if (!isset($this->_elementIndex[$element])) {
  656.             return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
  657.         }
  658.         $value = $this->_elements[$this->_elementIndex[$element]]->getValue();
  659.         if (isset($this->_duplicateIndex[$element])) {
  660.             foreach ($this->_duplicateIndex[$element] as $index) {
  661.                 if (null !== ($v = $this->_elements[$index]->getValue())) {
  662.                     if (is_array($value)) {
  663.                         $value[] = $v;
  664.                     } else {
  665.                         $value = (null === $value)? $v: array($value, $v);
  666.                     }
  667.                 }
  668.             }
  669.         }
  670.         return $value;
  671.     } // end func getElementValue
  672.  
  673.     // }}}
  674.     // {{{ getSubmitValue()
  675.  
  676.     /**
  677.      * Returns the elements value after submit and filter
  678.      *
  679.      * @param     string     Element name
  680.      * @since     2.0
  681.      * @access    public
  682.      * @return    mixed     submitted element value or null if not set
  683.      */    
  684.     function getSubmitValue($elementName)
  685.     {
  686.         $value = null;
  687.         if (isset($this->_submitValues[$elementName]) || isset($this->_submitFiles[$elementName])) {
  688.             $value = isset($this->_submitValues[$elementName])? $this->_submitValues[$elementName]: array();
  689.             if (is_array($value) && isset($this->_submitFiles[$elementName])) {
  690.                 foreach ($this->_submitFiles[$elementName] as $k => $v) {
  691.                     $value = HTML_QuickForm::arrayMerge($value, $this->_reindexFiles($this->_submitFiles[$elementName][$k], $k));
  692.                 }
  693.             }
  694.  
  695.         } elseif ('file' == $this->getElementType($elementName)) {
  696.             return $this->getElementValue($elementName);
  697.  
  698.         } elseif ('group' == $this->getElementType($elementName)) {
  699.             $group    =& $this->getElement($elementName);
  700.             $elements =& $group->getElements();
  701.             foreach (array_keys($elements) as $key) {
  702.                 $name = $group->getElementName($key);
  703.                 if ($name != $elementName) {
  704.                     // filter out radios
  705.                     $value[$name] = $this->getSubmitValue($name);
  706.                 }
  707.             }
  708.  
  709.         } elseif (false !== ($pos = strpos($elementName, '['))) {
  710.             $base = substr($elementName, 0, $pos);
  711.             $idx  = "['" . str_replace(array(']', '['), array('', "']['"), substr($elementName, $pos + 1, -1)) . "']";
  712.             if (isset($this->_submitValues[$base])) {
  713.                 $value = eval("return (isset(\$this->_submitValues['{$base}']{$idx})) ? \$this->_submitValues['{$base}']{$idx} : null;");
  714.             }
  715.  
  716.             if (null === $value && isset($this->_submitFiles[$base])) {
  717.                 $props = array('name', 'type', 'size', 'tmp_name', 'error');
  718.                 $code  = "if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" .
  719.                          "    return null;\n" .
  720.                          "} else {\n" .
  721.                          "    \$v = array();\n";
  722.                 foreach ($props as $prop) {
  723.                     $code .= "    \$v['{$prop}'] = \$this->_submitFiles['{$base}']['{$prop}']{$idx};\n";
  724.                 }
  725.                 $value = eval($code . "    return \$v;\n}\n");
  726.             }
  727.         }
  728.         return $value;
  729.     } // end func getSubmitValue
  730.  
  731.     // }}}
  732.     // {{{ _reindexFiles()
  733.  
  734.    /**
  735.     * A helper function to change the indexes in $_FILES array
  736.     *
  737.     * @param  mixed   Some value from the $_FILES array
  738.     * @param  string  The key from the $_FILES array that should be appended
  739.     * @return array
  740.     */
  741.     function _reindexFiles($value, $key)
  742.     {
  743.         if (!is_array($value)) {
  744.             return array($key => $value);
  745.         } else {
  746.             $ret = array();
  747.             foreach ($value as $k => $v) {
  748.                 $ret[$k] = $this->_reindexFiles($v, $key);
  749.             }
  750.             return $ret;
  751.         }
  752.     }
  753.  
  754.     // }}}
  755.     // {{{ getElementError()
  756.  
  757.     /**
  758.      * Returns error corresponding to validated element
  759.      *
  760.      * @param     string    $element        Name of form element to check
  761.      * @since     1.0
  762.      * @access    public
  763.      * @return    string    error message corresponding to checked element
  764.      */
  765.     function getElementError($element)
  766.     {
  767.         if (isset($this->_errors[$element])) {
  768.             return $this->_errors[$element];
  769.         }
  770.     } // end func getElementError
  771.     
  772.     // }}}
  773.     // {{{ setElementError()
  774.  
  775.     /**
  776.      * Set error message for a form element
  777.      *
  778.      * @param     string    $element    Name of form element to set error for
  779.      * @param     string    $message    Error message
  780.      * @since     1.0       
  781.      * @access    public
  782.      * @return    void
  783.      */
  784.     function setElementError($element,$message)
  785.     {
  786.         $this->_errors[$element] = $message;
  787.     } // end func setElementError
  788.          
  789.      // }}}
  790.      // {{{ getElementType()
  791.  
  792.      /**
  793.       * Returns the type of the given element
  794.       *
  795.       * @param      string    $element    Name of form element
  796.       * @since      1.1
  797.       * @access     public
  798.       * @return     string    Type of the element, false if the element is not found
  799.       */
  800.      function getElementType($element)
  801.      {
  802.          if (isset($this->_elementIndex[$element])) {
  803.              return $this->_elements[$this->_elementIndex[$element]]->getType();
  804.          }
  805.          return false;
  806.      } // end func getElementType
  807.  
  808.      // }}}
  809.      // {{{ updateElementAttr()
  810.  
  811.     /**
  812.      * Updates Attributes for one or more elements
  813.      *
  814.      * @param      mixed    $elements   Array of element names/objects or string of elements to be updated
  815.      * @param      mixed    $attrs      Array or sting of html attributes
  816.      * @since      2.10
  817.      * @access     public
  818.      * @return     void
  819.      */
  820.     function updateElementAttr($elements, $attrs)
  821.     {
  822.         if (is_string($elements)) {
  823.             $elements = split('[ ]?,[ ]?', $elements);
  824.         }
  825.         foreach (array_keys($elements) as $key) {
  826.             if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) {
  827.                 $elements[$key]->updateAttributes($attrs);
  828.             } elseif (isset($this->_elementIndex[$elements[$key]])) {
  829.                 $this->_elements[$this->_elementIndex[$elements[$key]]]->updateAttributes($attrs);
  830.                 if (isset($this->_duplicateIndex[$elements[$key]])) {
  831.                     foreach ($this->_duplicateIndex[$elements[$key]] as $index) {
  832.                         $this->_elements[$index]->updateAttributes($attrs);
  833.                     }
  834.                 }
  835.             }
  836.         }
  837.     } // end func updateElementAttr
  838.  
  839.     // }}}
  840.     // {{{ removeElement()
  841.  
  842.     /**
  843.      * Removes an element
  844.      *
  845.      * @param string    $elementName The element name
  846.      * @param boolean   $removeRules True if rules for this element are to be removed too                     
  847.      *
  848.      * @access public
  849.      * @since 2.0
  850.      * @return void
  851.      * @throws HTML_QuickForm_Error
  852.      */
  853.    function removeElement($elementName, $removeRules = true)
  854.     {
  855.         if (isset($this->_elementIndex[$elementName])) {
  856.             unset($this->_elements[$this->_elementIndex[$elementName]]);
  857.             unset($this->_elementIndex[$elementName]);
  858.             if ($removeRules) {
  859.                 unset($this->_rules[$elementName]);
  860.             }
  861.         } else {
  862.             return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$elementName' does not exist in HTML_QuickForm::removeElement()", 'HTML_QuickForm_Error', true);
  863.         }
  864.     } // end func removeElement
  865.  
  866.     // }}}
  867.     // {{{ addRule()
  868.  
  869.     /**
  870.      * Adds a validation rule for the given field
  871.      *
  872.      * If the element is in fact a group, it will be considered as a whole.
  873.      * To validate grouped elements as separated entities, 
  874.      * use addGroupRule instead of addRule.
  875.      *
  876.      * @param    string     $element       Form element name
  877.      * @param    string     $message       Message to display for invalid data
  878.      * @param    string     $type          Rule type, use getRegisteredRules() to get types
  879.      * @param    string     $format        (optional)Required for extra rule data
  880.      * @param    string     $validation    (optional)Where to perform validation: "server", "client"
  881.      * @param    boolean    $reset         Client-side validation: reset the form element to its original value if there is an error?
  882.      * @param    boolean    $force         Force the rule to be applied, even if the target form element does not exist
  883.      * @since    1.0
  884.      * @access   public
  885.      * @throws   HTML_QuickForm_Error
  886.      */
  887.     function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false)
  888.     {
  889.         if (!$force) {
  890.             if (!is_array($element) && !$this->elementExists($element)) {
  891.                 return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
  892.             } elseif (is_array($element)) {
  893.                 foreach ($element as $el) {
  894.                     if (!$this->elementExists($el)) {
  895.                         return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$el' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
  896.                     }
  897.                 }
  898.             }
  899.         }
  900.         if (false === ($newName = $this->isRuleRegistered($type, true))) {
  901.             return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
  902.         } elseif (is_string($newName)) {
  903.             $type = $newName;
  904.         }
  905.         if (is_array($element)) {
  906.             $dependent = $element;
  907.             $element   = array_shift($dependent);
  908.         } else {
  909.             $dependent = null;
  910.         }
  911.         if ($type == 'required' || $type == 'uploadedfile') {
  912.             $this->_required[] = $element;
  913.         }
  914.         if (!isset($this->_rules[$element])) {
  915.             $this->_rules[$element] = array();
  916.         }
  917.         if ($validation == 'client') {
  918.             $this->updateAttributes(array('onsubmit'=>'return validate_'.$this->_attributes['id'] . '(this);'));
  919.         }
  920.         $this->_rules[$element][] = array(
  921.             'type'        => $type,
  922.             'format'      => $format,
  923.             'message'     => $message,
  924.             'validation'  => $validation,
  925.             'reset'       => $reset,
  926.             'dependent'   => $dependent
  927.         );
  928.     } // end func addRule
  929.  
  930.     // }}}
  931.     // {{{ addGroupRule()
  932.  
  933.     /**
  934.      * Adds a validation rule for the given group of elements
  935.      *
  936.      * Only groups with a name can be assigned a validation rule
  937.      * Use addGroupRule when you need to validate elements inside the group.
  938.      * Use addRule if you need to validate the group as a whole. In this case,
  939.      * the same rule will be applied to all elements in the group.
  940.      * Use addRule if you need to validate the group against a function.
  941.      *
  942.      * @param    string     $group         Form group name
  943.      * @param    mixed      $arg1          Array for multiple elements or error message string for one element
  944.      * @param    string     $type          (optional)Rule type use getRegisteredRules() to get types
  945.      * @param    string     $format        (optional)Required for extra rule data
  946.      * @param    int        $howmany       (optional)How many valid elements should be in the group
  947.      * @param    string     $validation    (optional)Where to perform validation: "server", "client"
  948.      * @param    bool       $reset         Client-side: whether to reset the element's value to its original state if validation failed.
  949.      * @since    2.5
  950.      * @access   public
  951.      * @throws   HTML_QuickForm_Error
  952.      */
  953.     function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false)
  954.     {
  955.         if (!$this->elementExists($group)) {
  956.             return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Group '$group' does not exist in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
  957.         }
  958.  
  959.         $groupObj =& $this->getElement($group);
  960.         if (is_array($arg1)) {
  961.             $required = 0;
  962.             foreach ($arg1 as $elementIndex => $rules) {
  963.                 $elementName = $groupObj->getElementName($elementIndex);
  964.                 foreach ($rules as $rule) {
  965.                     $format = (isset($rule[2])) ? $rule[2] : null;
  966.                     $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server';
  967.                     $reset = isset($rule[4]) && $rule[4];
  968.                     $type = $rule[1];
  969.                     if (false === ($newName = $this->isRuleRegistered($type, true))) {
  970.                         return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
  971.                     } elseif (is_string($newName)) {
  972.                         $type = $newName;
  973.                     }
  974.  
  975.                     $this->_rules[$elementName][] = array(
  976.                                                         'type'        => $type,
  977.                                                         'format'      => $format, 
  978.                                                         'message'     => $rule[0],
  979.                                                         'validation'  => $validation,
  980.                                                         'reset'       => $reset,
  981.                                                         'group'       => $group);
  982.  
  983.                     if ('required' == $type || 'uploadedfile' == $type) {
  984.                         $groupObj->_required[] = $elementName;
  985.                         $this->_required[] = $elementName;
  986.                         $required++;
  987.                     }
  988.                     if ('client' == $validation) {
  989.                         $this->updateAttributes(array('onsubmit'=>'return validate_'.$this->_attributes['id'] . '(this);'));
  990.                     }
  991.                 }
  992.             }
  993.             if ($required > 0 && count($groupObj->getElements()) == $required) {
  994.                 $this->_required[] = $group;
  995.             }
  996.         } elseif (is_string($arg1)) {
  997.             if (false === ($newName = $this->isRuleRegistered($type, true))) {
  998.                 return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
  999.             } elseif (is_string($newName)) {
  1000.                 $type = $newName;
  1001.             }
  1002.  
  1003.             // Radios need to be handled differently when required
  1004.             if ($type == 'required' && $groupObj->getGroupType() == 'radio') {
  1005.                 $howmany = ($howmany == 0) ? 1 : $howmany;
  1006.             } else {
  1007.                 $howmany = ($howmany == 0) ? count($groupObj->getElements()) : $howmany;
  1008.             }
  1009.  
  1010.             $this->_rules[$group][] = array('type'       => $type,
  1011.                                             'format'     => $format, 
  1012.                                             'message'    => $arg1,
  1013.                                             'validation' => $validation,
  1014.                                             'howmany'    => $howmany,
  1015.                                             'reset'      => $reset);
  1016.             if ($type == 'required') {
  1017.                 $this->_required[] = $group;
  1018.             }
  1019.             if ($validation == 'client') {
  1020.                 $this->updateAttributes(array('onsubmit'=>'return validate_'.$this->_attributes['id'] . '(this);'));
  1021.             }
  1022.         }
  1023.     } // end func addGroupRule
  1024.  
  1025.     // }}}
  1026.     // {{{ addFormRule()
  1027.  
  1028.    /**
  1029.     * Adds a global validation rule 
  1030.     * 
  1031.     * This should be used when for a rule involving several fields or if
  1032.     * you want to use some completely custom validation for your form.
  1033.     * The rule function/method should return true in case of successful 
  1034.     * validation and array('element name' => 'error') when there were errors.
  1035.     * 
  1036.     * @access   public
  1037.     * @param    mixed   Callback, either function name or array(&$object, 'method')
  1038.     * @throws   HTML_QuickForm_Error
  1039.     */
  1040.     function addFormRule($rule)
  1041.     {
  1042.         if (!is_callable($rule)) {
  1043.             return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, 'Callback function does not exist in HTML_QuickForm::addFormRule()', 'HTML_QuickForm_Error', true);
  1044.         }
  1045.         $this->_formRules[] = $rule;
  1046.     }
  1047.     
  1048.     // }}}
  1049.     // {{{ applyFilter()
  1050.  
  1051.     /**
  1052.      * Applies a data filter for the given field(s)
  1053.      *
  1054.      * @param    mixed     $element       Form element name or array of such names
  1055.      * @param    mixed     $filter        Callback, either function name or array(&$object, 'method')
  1056.      * @since    2.0
  1057.      * @access   public
  1058.      */
  1059.     function applyFilter($element, $filter)
  1060.     {
  1061.         if (!is_callable($filter)) {
  1062.             return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::applyFilter()", 'HTML_QuickForm_Error', true);
  1063.         }
  1064.         if ($element == '__ALL__') {
  1065.             $this->_submitValues = $this->_recursiveFilter($filter, $this->_submitValues);
  1066.         } else {
  1067.             if (!is_array($element)) {
  1068.                 $element = array($element);
  1069.             }
  1070.             foreach ($element as $elName) {
  1071.                 $value = $this->getSubmitValue($elName);
  1072.                 if (null !== $value) {
  1073.                     if (false === strpos($elName, '[')) {
  1074.                         $this->_submitValues[$elName] = $this->_recursiveFilter($filter, $value);
  1075.                     } else {
  1076.                         $idx  = "['" . str_replace(array(']', '['), array('', "']['"), $elName) . "']";
  1077.                         eval("\$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);");
  1078.                     }
  1079.                 }
  1080.             }
  1081.         }
  1082.     } // end func applyFilter
  1083.  
  1084.     // }}}
  1085.     // {{{ _recursiveFilter()
  1086.  
  1087.     /**
  1088.      * Recursively apply a filter function
  1089.      *
  1090.      * @param     string   $filter    filter to apply
  1091.      * @param     mixed    $value     submitted values
  1092.      * @since     2.0
  1093.      * @access    private
  1094.      * @return    cleaned values
  1095.      */
  1096.     function _recursiveFilter($filter, $value)
  1097.     {
  1098.         if (is_array($value)) {
  1099.             $cleanValues = array();
  1100.             foreach ($value as $k => $v) {
  1101.                 $cleanValues[$k] = $this->_recursiveFilter($filter, $value[$k]);
  1102.             }
  1103.             return $cleanValues;
  1104.         } else {
  1105.             return call_user_func($filter, $value);
  1106.         }
  1107.     } // end func _recursiveFilter
  1108.  
  1109.     // }}}
  1110.     // {{{ arrayMerge()
  1111.  
  1112.    /**
  1113.     * Merges two arrays
  1114.     *
  1115.     * Merges two array like the PHP function array_merge but recursively.
  1116.     * The main difference is that existing keys will not be renumbered
  1117.     * if they are integers.
  1118.     *
  1119.     * @access   puplic
  1120.     * @param    array   $a  original array
  1121.     * @param    array   $b  array which will be merged into first one
  1122.     * @return   array   merged array
  1123.     */
  1124.     function arrayMerge($a, $b)
  1125.     {
  1126.         foreach ($b as $k => $v) {
  1127.             if (is_array($v)) {
  1128.                 if (isset($a[$k]) && !is_array($a[$k])) {
  1129.                     $a[$k] = $v;
  1130.                 } else {
  1131.                     if (!isset($a[$k])) {
  1132.                         $a[$k] = array();
  1133.                     }
  1134.                     $a[$k] = HTML_QuickForm::arrayMerge($a[$k], $v);
  1135.                 }
  1136.             } else {
  1137.                 $a[$k] = $v;
  1138.             }
  1139.         }
  1140.         return $a;
  1141.     } // end func arrayMerge
  1142.  
  1143.     // }}}
  1144.     // {{{ isTypeRegistered()
  1145.  
  1146.     /**
  1147.      * Returns whether or not the form element type is supported
  1148.      *
  1149.      * @param     string   $type     Form element type
  1150.      * @since     1.0
  1151.      * @access    public
  1152.      * @return    boolean
  1153.      */
  1154.     function isTypeRegistered($type)
  1155.     {
  1156.         return isset($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type]);
  1157.     } // end func isTypeRegistered
  1158.  
  1159.     // }}}
  1160.     // {{{ getRegisteredTypes()
  1161.  
  1162.     /**
  1163.      * Returns an array of registered element types
  1164.      *
  1165.      * @since     1.0
  1166.      * @access    public
  1167.      * @return    array
  1168.      */
  1169.     function getRegisteredTypes()
  1170.     {
  1171.         return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']);
  1172.     } // end func getRegisteredTypes
  1173.  
  1174.     // }}}
  1175.     // {{{ isRuleRegistered()
  1176.  
  1177.     /**
  1178.      * Returns whether or not the given rule is supported
  1179.      *
  1180.      * @param     string   $name    Validation rule name
  1181.      * @param     bool     Whether to automatically register subclasses of HTML_QuickForm_Rule
  1182.      * @since     1.0
  1183.      * @access    public
  1184.      * @return    mixed    true if previously registered, false if not, new rule name if auto-registering worked
  1185.      */
  1186.     function isRuleRegistered($name, $autoRegister = false)
  1187.     {
  1188.         if (is_scalar($name) && isset($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) {
  1189.             return true;
  1190.         } elseif (!$autoRegister) {
  1191.             return false;
  1192.         }
  1193.         // automatically register the rule if requested
  1194.         include_once 'HTML/QuickForm/RuleRegistry.php';
  1195.         $ruleName = false;
  1196.         if (is_object($name) && is_a($name, 'html_quickform_rule')) {
  1197.             $ruleName = !empty($name->name)? $name->name: strtolower(get_class($name));
  1198.         } elseif (is_string($name) && class_exists($name)) {
  1199.             $parent = strtolower($name);
  1200.             do {
  1201.                 if ('html_quickform_rule' == strtolower($parent)) {
  1202.                     $ruleName = strtolower($name);
  1203.                     break;
  1204.                 }
  1205.             } while ($parent = get_parent_class($parent));
  1206.         }
  1207.         if ($ruleName) {
  1208.             $registry =& HTML_QuickForm_RuleRegistry::singleton();
  1209.             $registry->registerRule($ruleName, null, $name);
  1210.         }
  1211.         return $ruleName;
  1212.     } // end func isRuleRegistered
  1213.  
  1214.     // }}}
  1215.     // {{{ getRegisteredRules()
  1216.  
  1217.     /**
  1218.      * Returns an array of registered validation rules
  1219.      *
  1220.      * @since     1.0
  1221.      * @access    public
  1222.      * @return    array
  1223.      */
  1224.     function getRegisteredRules()
  1225.     {
  1226.         return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']);
  1227.     } // end func getRegisteredRules
  1228.  
  1229.     // }}}
  1230.     // {{{ isElementRequired()
  1231.  
  1232.     /**
  1233.      * Returns whether or not the form element is required
  1234.      *
  1235.      * @param     string   $element     Form element name
  1236.      * @since     1.0
  1237.      * @access    public
  1238.      * @return    boolean
  1239.      */
  1240.     function isElementRequired($element)
  1241.     {
  1242.         return in_array($element, $this->_required, true);
  1243.     } // end func isElementRequired
  1244.  
  1245.     // }}}
  1246.     // {{{ isElementFrozen()
  1247.  
  1248.     /**
  1249.      * Returns whether or not the form element is frozen
  1250.      *
  1251.      * @param     string   $element     Form element name
  1252.      * @since     1.0
  1253.      * @access    public
  1254.      * @return    boolean
  1255.      */
  1256.     function isElementFrozen($element)
  1257.     {
  1258.          if (isset($this->_elementIndex[$element])) {
  1259.              return $this->_elements[$this->_elementIndex[$element]]->isFrozen();
  1260.          }
  1261.          return false;
  1262.     } // end func isElementFrozen
  1263.  
  1264.     // }}}
  1265.     // {{{ setJsWarnings()
  1266.  
  1267.     /**
  1268.      * Sets JavaScript warning messages
  1269.      *
  1270.      * @param     string   $pref        Prefix warning
  1271.      * @param     string   $post        Postfix warning
  1272.      * @since     1.1
  1273.      * @access    public
  1274.      * @return    void
  1275.      */
  1276.     function setJsWarnings($pref, $post)
  1277.     {
  1278.         $this->_jsPrefix = $pref;
  1279.         $this->_jsPostfix = $post;
  1280.     } // end func setJsWarnings
  1281.     
  1282.     // }}}
  1283.     // {{{ setRequiredNote()
  1284.  
  1285.     /**
  1286.      * Sets required-note
  1287.      *
  1288.      * @param     string   $note        Message indicating some elements are required
  1289.      * @since     1.1
  1290.      * @access    public
  1291.      * @return    void
  1292.      */
  1293.     function setRequiredNote($note)
  1294.     {
  1295.         $this->_requiredNote = $note;
  1296.     } // end func setRequiredNote
  1297.  
  1298.     // }}}
  1299.     // {{{ getRequiredNote()
  1300.  
  1301.     /**
  1302.      * Returns the required note
  1303.      *
  1304.      * @since     2.0
  1305.      * @access    public
  1306.      * @return    string
  1307.      */
  1308.     function getRequiredNote()
  1309.     {
  1310.         return $this->_requiredNote;
  1311.     } // end func getRequiredNote
  1312.  
  1313.     // }}}
  1314.     // {{{ validate()
  1315.  
  1316.     /**
  1317.      * Performs the server side validation
  1318.      * @access    public
  1319.      * @since     1.0
  1320.      * @return    boolean   true if no error found
  1321.      */
  1322.     function validate()
  1323.     {
  1324.         if (count($this->_rules) == 0 && count($this->_formRules) == 0 && 
  1325.             (count($this->_submitValues) > 0 || count($this->_submitFiles) > 0)) {
  1326.             return true;
  1327.         } elseif (count($this->_submitValues) == 0 && count($this->_submitFiles) == 0) {
  1328.             return false;
  1329.         }
  1330.  
  1331.         include_once('HTML/QuickForm/RuleRegistry.php');
  1332.         $registry =& HTML_QuickForm_RuleRegistry::singleton();
  1333.  
  1334.         foreach ($this->_rules as $target => $rules) {
  1335.             $submitValue = $this->getSubmitValue($target);
  1336.  
  1337.             foreach ($rules as $elementName => $rule) {
  1338.                 if ((isset($rule['group']) && isset($this->_errors[$rule['group']])) ||
  1339.                      isset($this->_errors[$target])) {
  1340.                     continue 2;
  1341.                 }
  1342.                 if ((!isset($submitValue) || $submitValue == '') && 
  1343.                      !$this->isElementRequired($target)) {
  1344.                     // Element is not required
  1345.                     continue 2;
  1346.                 }
  1347.                 if (isset($rule['dependent']) && is_array($rule['dependent'])) {
  1348.                     $values = array($submitValue);
  1349.                     foreach ($rule['dependent'] as $elName) {
  1350.                         $values[] = $this->getSubmitValue($elName);
  1351.                     }
  1352.                     $result = $registry->validate($rule['type'], $values, $rule['format'], true);
  1353.                 } elseif (is_array($submitValue) && !isset($rule['howmany'])) {
  1354.                     $result = $registry->validate($rule['type'], $submitValue, $rule['format'], true);
  1355.                 } else {
  1356.                     $result = $registry->validate($rule['type'], $submitValue, $rule['format'], false);
  1357.                 }
  1358.  
  1359.                 if (!$result || (!empty($rule['howmany']) && $rule['howmany'] > (int)$result)) {
  1360.                     if (isset($rule['group'])) {
  1361.                         $this->_errors[$rule['group']] = $rule['message'];
  1362.                     } else {
  1363.                         $this->_errors[$target] = $rule['message'];
  1364.                     }
  1365.                 }
  1366.             }
  1367.         }
  1368.  
  1369.         // process the global rules now
  1370.         foreach ($this->_formRules as $rule) {
  1371.             if (true !== ($res = call_user_func($rule, $this->_submitValues, $this->_submitFiles))) {
  1372.                 if (is_array($res)) {
  1373.                     $this->_errors += $res;
  1374.                 } else {
  1375.                     return PEAR::raiseError(null, QUICKFORM_ERROR, null, E_USER_WARNING, 'Form rule callback returned invalid value in HTML_QuickForm::validate()', 'HTML_QuickForm_Error', true);
  1376.                 }
  1377.             }
  1378.         }
  1379.  
  1380.         return (0 == count($this->_errors));
  1381.     } // end func validate
  1382.  
  1383.     // }}}
  1384.     // {{{ freeze()
  1385.  
  1386.     /**
  1387.      * Displays elements without HTML input tags
  1388.      *
  1389.      * @param    mixed   $elementList       array or string of element(s) to be frozen
  1390.      * @since     1.0
  1391.      * @access   public
  1392.      * @throws   HTML_QuickForm_Error
  1393.      */
  1394.     function freeze($elementList=null)
  1395.     {
  1396.         $elementFlag = false;
  1397.         if (isset($elementList) && !is_array($elementList)) {
  1398.             $elementList = split('[ ]*,[ ]*', $elementList);
  1399.         } elseif (!isset($elementList)) {
  1400.             $this->_freezeAll = true;
  1401.         }
  1402.  
  1403.         foreach ($this->_elements as $key => $val) {
  1404.             // need to get the element by reference
  1405.             $element = &$this->_elements[$key];
  1406.             if (is_object($element)) {
  1407.                 $name = $element->getName();
  1408.                 if ($this->_freezeAll || in_array($name, $elementList)) {
  1409.                     $elementFlag = true;
  1410.                     $element->freeze();
  1411.                 }
  1412.             }
  1413.         }
  1414.  
  1415.         if (!$elementFlag) {
  1416.             return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::freeze()", 'HTML_QuickForm_Error', true);
  1417.         }
  1418.         return true;
  1419.     } // end func freeze
  1420.         
  1421.     // }}}
  1422.     // {{{ isFrozen()
  1423.  
  1424.     /**
  1425.      * Returns whether or not the whole form is frozen
  1426.      *
  1427.      * @since     3.0
  1428.      * @access    public
  1429.      * @return    boolean
  1430.      */
  1431.     function isFrozen()
  1432.     {
  1433.          return $this->_freezeAll;
  1434.     } // end func isFrozen
  1435.  
  1436.     // }}}
  1437.     // {{{ process()
  1438.  
  1439.     /**
  1440.      * Performs the form data processing
  1441.      *
  1442.      * @param    mixed     $callback        Callback, either function name or array(&$object, 'method')
  1443.      * @param    bool      $mergeFiles      Whether uploaded files should be processed too
  1444.      * @since    1.0
  1445.      * @access   public
  1446.      * @throws   HTML_QuickForm_Error
  1447.      */
  1448.     function process($callback, $mergeFiles = true)
  1449.     {
  1450.         if (!is_callable($callback)) {
  1451.             return PEAR::raiseError(null, QUICKFORM_INVALID_PROCESS, null, E_USER_WARNING, "Callback function does not exist in QuickForm::process()", 'HTML_QuickForm_Error', true);
  1452.         }
  1453.         $values = ($mergeFiles === true) ? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles) : $this->_submitValues;
  1454.         return call_user_func($callback, $values);
  1455.     } // end func process
  1456.  
  1457.     // }}}
  1458.     // {{{ accept()
  1459.  
  1460.    /**
  1461.     * Accepts a renderer
  1462.     *
  1463.     * @param object     An HTML_QuickForm_Renderer object
  1464.     * @since 3.0
  1465.     * @access public
  1466.     * @return void
  1467.     */
  1468.     function accept(&$renderer)
  1469.     {
  1470.         $renderer->startForm($this);
  1471.         foreach (array_keys($this->_elements) as $key) {
  1472.             $element =& $this->_elements[$key];
  1473.             if ($this->_freezeAll) {
  1474.                 $element->freeze();
  1475.             }
  1476.             $elementName = $element->getName();
  1477.             $required    = ($this->isElementRequired($elementName) && $this->_freezeAll == false);
  1478.             $error       = $this->getElementError($elementName);
  1479.             $element->accept($renderer, $required, $error);
  1480.         }
  1481.         $renderer->finishForm($this);
  1482.     } // end func accept
  1483.  
  1484.     // }}}
  1485.     // {{{ defaultRenderer()
  1486.  
  1487.    /**
  1488.     * Returns a reference to default renderer object
  1489.     *
  1490.     * @access public
  1491.     * @since 3.0
  1492.     * @return object a default renderer object
  1493.     */
  1494.     function &defaultRenderer()
  1495.     {
  1496.         if (!isset($GLOBALS['_HTML_QuickForm_default_renderer'])) {
  1497.             include_once('HTML/QuickForm/Renderer/Default.php');
  1498.             $GLOBALS['_HTML_QuickForm_default_renderer'] =& new HTML_QuickForm_Renderer_Default();
  1499.         }
  1500.         return $GLOBALS['_HTML_QuickForm_default_renderer'];
  1501.     } // end func defaultRenderer
  1502.  
  1503.     // }}}
  1504.     // {{{ toHtml ()
  1505.  
  1506.     /**
  1507.      * Returns an HTML version of the form
  1508.      *
  1509.      * @param string $in_data (optional) Any extra data to insert right
  1510.      *               before form is rendered.  Useful when using templates.
  1511.      *
  1512.      * @return   string     Html version of the form
  1513.      * @since     1.0
  1514.      * @access   public
  1515.      */
  1516.     function toHtml ($in_data = null)
  1517.     {
  1518.         if (!is_null($in_data)) {
  1519.             $this->addElement('html', $in_data);
  1520.         }
  1521.         $renderer =& $this->defaultRenderer();
  1522.         $this->accept($renderer);
  1523.         return $renderer->toHtml();
  1524.     } // end func toHtml
  1525.  
  1526.     // }}}
  1527.     // {{{ getValidationScript()
  1528.  
  1529.     /**
  1530.      * Returns the client side validation script
  1531.      *
  1532.      * @since     2.0
  1533.      * @access    public
  1534.      * @return    string    Javascript to perform validation, empty string if no 'client' rules were added
  1535.      */
  1536.     function getValidationScript()
  1537.     {
  1538.         if (empty($this->_rules) || $this->_freezeAll || empty($this->_attributes['onsubmit'])) {
  1539.             return '';
  1540.         }
  1541.  
  1542.         include_once('HTML/QuickForm/RuleRegistry.php');
  1543.         $registry =& HTML_QuickForm_RuleRegistry::singleton();
  1544.         $test = array();
  1545.         $js_escape = array(
  1546.             "\r"    => '\r',
  1547.             "\n"    => '\n',
  1548.             "\t"    => '\t',
  1549.             "'"     => "\\'",
  1550.             '"'     => '\"',
  1551.             '\\'    => '\\\\'
  1552.         );
  1553.  
  1554.         foreach ($this->_rules as $elementName => $rules) {
  1555.             foreach ($rules as $rule) {
  1556.                 if ('client' == $rule['validation']) {
  1557.                     $dependent  = isset($rule['dependent']) && is_array($rule['dependent']);
  1558.                     $rule['message'] = strtr($rule['message'], $js_escape);
  1559.  
  1560.                     if (isset($rule['group'])) {
  1561.                         $group    =& $this->getElement($rule['group']);
  1562.                         $elements =& $group->getElements();
  1563.                         foreach (array_keys($elements) as $key) {
  1564.                             if ($elementName == $group->getElementName($key)) {
  1565.                                 $element =& $elements[$key];
  1566.                                 break;
  1567.                             }
  1568.                         }
  1569.                     } elseif ($dependent) {
  1570.                         $element   =  array();
  1571.                         $element[] =& $this->getElement($elementName);
  1572.                         foreach ($rule['dependent'] as $idx => $elName) {
  1573.                             $element[] =& $this->getElement($elName);
  1574.                         }
  1575.                     } else {
  1576.                         $element =& $this->getElement($elementName);
  1577.                     }
  1578.  
  1579.                     $test[] = $registry->getValidationScript($element, $elementName, $rule);
  1580.                     unset($element);
  1581.                 }
  1582.             }
  1583.         }
  1584.         if (count($test) > 0) {
  1585.             return
  1586.                 "\n<script type=\"text/javascript\">\n" .
  1587.                 "<!-- \n" . 
  1588.                 "function validate_" . $this->_attributes['id'] . "(frm) {\n" .
  1589.                 "  var value = '';\n" .
  1590.                 "  var errFlag = new Array();\n" .
  1591.                 "  _qfMsg = '';\n\n" .
  1592.                 join("\n", $test) .
  1593.                 "\n  if (_qfMsg != '') {\n" .
  1594.                 "    _qfMsg = '" . strtr($this->_jsPrefix, $js_escape) . "' + _qfMsg;\n" .
  1595.                 "    _qfMsg = _qfMsg + '\\n" . strtr($this->_jsPostfix, $js_escape) . "';\n" .
  1596.                 "    alert(_qfMsg);\n" .
  1597.                 "    return false;\n" .
  1598.                 "  }\n" .
  1599.                 "  return true;\n" .
  1600.                 "}\n" .
  1601.                 "//-->\n" .
  1602.                 "</script>";
  1603.         }
  1604.         return '';
  1605.     } // end func getValidationScript
  1606.  
  1607.     // }}}
  1608.     // {{{ getSubmitValues()
  1609.  
  1610.     /**
  1611.      * Returns the values submitted by the form
  1612.      *
  1613.      * @since     2.0
  1614.      * @access    public
  1615.      * @param     bool      Whether uploaded files should be returned too
  1616.      * @return    array
  1617.      */
  1618.     function getSubmitValues($mergeFiles = false)
  1619.     {
  1620.         return $mergeFiles? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles): $this->_submitValues;
  1621.     } // end func getSubmitValues
  1622.  
  1623.     // }}}
  1624.     // {{{ toArray()
  1625.  
  1626.     /**
  1627.      * Returns the form's contents in an array.
  1628.      *
  1629.      * The description of the array structure is in HTML_QuickForm_Renderer_Array docs
  1630.      * 
  1631.      * @since     2.0
  1632.      * @access    public
  1633.      * @return    array of form contents
  1634.      */
  1635.     function toArray()
  1636.     {
  1637.         include_once 'HTML/QuickForm/Renderer/Array.php';
  1638.         $renderer =& new HTML_QuickForm_Renderer_Array();
  1639.         $this->accept($renderer);
  1640.         return $renderer->toArray();
  1641.      } // end func toArray
  1642.  
  1643.     // }}}
  1644.     // {{{ exportValue()
  1645.  
  1646.     /**
  1647.      * Returns a 'safe' element's value
  1648.      * 
  1649.      * This method first tries to find a cleaned-up submitted value,
  1650.      * it will return a value set by setValue()/setDefaults()/setConstants()
  1651.      * if submitted value does not exist for the given element.
  1652.      *
  1653.      * @param  string   Name of an element
  1654.      * @access public
  1655.      * @return mixed
  1656.      */
  1657.     function exportValue($element)
  1658.     {
  1659.         if (!isset($this->_elementIndex[$element])) {
  1660.             return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
  1661.         }
  1662.         $value = $this->_elements[$this->_elementIndex[$element]]->exportValue($this->_submitValues, false);
  1663.         if (isset($this->_duplicateIndex[$element])) {
  1664.             foreach ($this->_duplicateIndex[$element] as $index) {
  1665.                 if (null !== ($v = $this->_elements[$index]->exportValue($this->_submitValues, false))) {
  1666.                     if (is_array($value)) {
  1667.                         $value[] = $v;
  1668.                     } else {
  1669.                         $value = (null === $value)? $v: array($value, $v);
  1670.                     }
  1671.                 }
  1672.             }
  1673.         }
  1674.         return $value;
  1675.     }
  1676.  
  1677.     // }}}
  1678.     // {{{ exportValues()
  1679.  
  1680.     /**
  1681.      * Returns 'safe' elements' values
  1682.      *
  1683.      * Unlike getSubmitValues(), this will return only the values 
  1684.      * corresponding to the elements present in the form.
  1685.      * 
  1686.      * @param   mixed   Array/string of element names, whose values we want. If not set then return all elements.
  1687.      * @access  public
  1688.      * @return  array   An assoc array of elements' values
  1689.      * @throws  HTML_QuickForm_Error
  1690.      */
  1691.     function exportValues($elementList = null)
  1692.     {
  1693.         $values = array();
  1694.         if (null === $elementList) {
  1695.             // iterate over all elements, calling their exportValue() methods
  1696.             foreach (array_keys($this->_elements) as $key) {
  1697.                 $value = $this->_elements[$key]->exportValue($this->_submitValues, true);
  1698.                 if (is_array($value)) {
  1699.                     // This shit throws a bogus warning in PHP 4.3.x
  1700.                     $values = HTML_QuickForm::arrayMerge($values, $value);
  1701.                 }
  1702.             }
  1703.         } else {
  1704.             if (!is_array($elementList)) {
  1705.                 $elementList = array_map('trim', explode(',', $elementList));
  1706.             }
  1707.             foreach ($elementList as $elementName) {
  1708.                 $value = $this->exportValue($elementName);
  1709.                 if (PEAR::isError($value)) {
  1710.                     return $value;
  1711.                 }
  1712.                 $values[$elementName] = $value;
  1713.             }
  1714.         }
  1715.         return $values;
  1716.     }
  1717.  
  1718.     // }}}
  1719.     // {{{ isError()
  1720.  
  1721.     /**
  1722.      * Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error)
  1723.      *
  1724.      * @access public
  1725.      * @param mixed     result code
  1726.      * @return bool     whether $value is an error
  1727.      */
  1728.     function isError($value)
  1729.     {
  1730.         return (is_object($value) && is_a($value, 'html_quickform_error'));
  1731.     } // end func isError
  1732.  
  1733.     // }}}
  1734.     // {{{ errorMessage()
  1735.  
  1736.     /**
  1737.      * Return a textual error message for an QuickForm error code
  1738.      *
  1739.      * @access  public
  1740.      * @param   int     error code
  1741.      * @return  string  error message
  1742.      */
  1743.     function errorMessage($value)
  1744.     {
  1745.         // make the variable static so that it only has to do the defining on the first call
  1746.         static $errorMessages;
  1747.  
  1748.         // define the varies error messages
  1749.         if (!isset($errorMessages)) {
  1750.             $errorMessages = array(
  1751.                 QUICKFORM_OK                    => 'no error',
  1752.                 QUICKFORM_ERROR                 => 'unknown error',
  1753.                 QUICKFORM_INVALID_RULE          => 'the rule does not exist as a registered rule',
  1754.                 QUICKFORM_NONEXIST_ELEMENT      => 'nonexistent html element',
  1755.                 QUICKFORM_INVALID_FILTER        => 'invalid filter',
  1756.                 QUICKFORM_UNREGISTERED_ELEMENT  => 'unregistered element',
  1757.                 QUICKFORM_INVALID_ELEMENT_NAME  => 'element already exists',
  1758.                 QUICKFORM_INVALID_PROCESS       => 'process callback does not exist',
  1759.                 QUICKFORM_DEPRECATED            => 'method is deprecated',
  1760.                 QUICKFORM_INVALID_DATASOURCE    => 'datasource is not an object'
  1761.             );
  1762.         }
  1763.  
  1764.         // If this is an error object, then grab the corresponding error code
  1765.         if (HTML_QuickForm::isError($value)) {
  1766.             $value = $value->getCode();
  1767.         }
  1768.  
  1769.         // return the textual error message corresponding to the code
  1770.         return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR];
  1771.     } // end func errorMessage
  1772.  
  1773.     // }}}
  1774. } // end class HTML_QuickForm
  1775.  
  1776. class HTML_QuickForm_Error extends PEAR_Error {
  1777.  
  1778.     // {{{ properties
  1779.  
  1780.     /**
  1781.     * Prefix for all error messages
  1782.     * @var string
  1783.     */
  1784.     var $error_message_prefix = 'QuickForm Error: ';
  1785.  
  1786.     // }}}
  1787.     // {{{ constructor
  1788.  
  1789.     /**
  1790.     * Creates a quickform error object, extending the PEAR_Error class
  1791.     *
  1792.     * @param int   $code the error code
  1793.     * @param int   $mode the reaction to the error, either return, die or trigger/callback
  1794.     * @param int   $level intensity of the error (PHP error code)
  1795.     * @param mixed $debuginfo any information that can inform user as to nature of the error
  1796.     */
  1797.     function HTML_QuickForm_Error($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN,
  1798.                          $level = E_USER_NOTICE, $debuginfo = null)
  1799.     {
  1800.         if (is_int($code)) {
  1801.             $this->PEAR_Error(HTML_QuickForm::errorMessage($code), $code, $mode, $level, $debuginfo);
  1802.         } else {
  1803.             $this->PEAR_Error("Invalid error code: $code", QUICKFORM_ERROR, $mode, $level, $debuginfo);
  1804.         }
  1805.     }
  1806.  
  1807.     // }}}
  1808. } // end class HTML_QuickForm_Error
  1809. ?>