2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
5 * Create, validate and process HTML forms
9 * LICENSE: This source file is subject to version 3.01 of the PHP license
10 * that is available through the world-wide-web at the following URI:
11 * http://www.php.net/license/3_01.txt If you did not receive a copy of
12 * the PHP License and are unable to obtain it through the web, please
13 * send a note to license@php.net so we can mail you a copy immediately.
16 * @package HTML_QuickForm
17 * @author Adam Daniel <adaniel1@eesus.jnj.com>
18 * @author Bertrand Mansion <bmansion@mamasam.com>
19 * @author Alexey Borzov <avb@php.net>
20 * @copyright 2001-2011 The PHP Group
21 * @license http://www.php.net/license/3_01.txt PHP License 3.01
23 * @link http://pear.php.net/package/HTML_QuickForm
27 * PEAR and PEAR_Error classes, for error handling
29 require_once 'PEAR.php';
31 * Base class for all HTML classes
33 require_once 'HTML/Common.php';
35 * Static utility methods
37 require_once 'HTML/QuickForm/utils.php';
40 * Element types known to HTML_QuickForm
41 * @see HTML_QuickForm::registerElementType(), HTML_QuickForm::getRegisteredTypes(),
42 * HTML_QuickForm::isTypeRegistered()
43 * @global array $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']
45 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] =
47 'group' =>array('HTML/QuickForm/group.php','HTML_QuickForm_group'),
48 'hidden' =>array('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'),
49 'reset' =>array('HTML/QuickForm/reset.php','HTML_QuickForm_reset'),
50 'checkbox' =>array('HTML/QuickForm/checkbox.php','HTML_QuickForm_checkbox'),
51 'file' =>array('HTML/QuickForm/file.php','HTML_QuickForm_file'),
52 'image' =>array('HTML/QuickForm/image.php','HTML_QuickForm_image'),
53 'password' =>array('HTML/QuickForm/password.php','HTML_QuickForm_password'),
54 'radio' =>array('HTML/QuickForm/radio.php','HTML_QuickForm_radio'),
55 'button' =>array('HTML/QuickForm/button.php','HTML_QuickForm_button'),
56 'submit' =>array('HTML/QuickForm/submit.php','HTML_QuickForm_submit'),
57 'select' =>array('HTML/QuickForm/select.php','HTML_QuickForm_select'),
58 'hiddenselect' =>array('HTML/QuickForm/hiddenselect.php','HTML_QuickForm_hiddenselect'),
59 'text' =>array('HTML/QuickForm/text.php','HTML_QuickForm_text'),
60 'textarea' =>array('HTML/QuickForm/textarea.php','HTML_QuickForm_textarea'),
61 'link' =>array('HTML/QuickForm/link.php','HTML_QuickForm_link'),
62 'advcheckbox' =>array('HTML/QuickForm/advcheckbox.php','HTML_QuickForm_advcheckbox'),
63 'date' =>array('HTML/QuickForm/date.php','HTML_QuickForm_date'),
64 'static' =>array('HTML/QuickForm/static.php','HTML_QuickForm_static'),
65 'header' =>array('HTML/QuickForm/header.php', 'HTML_QuickForm_header'),
66 'html' =>array('HTML/QuickForm/html.php', 'HTML_QuickForm_html'),
67 'hierselect' =>array('HTML/QuickForm/hierselect.php', 'HTML_QuickForm_hierselect'),
68 'autocomplete' =>array('HTML/QuickForm/autocomplete.php', 'HTML_QuickForm_autocomplete'),
69 'xbutton' =>array('HTML/QuickForm/xbutton.php','HTML_QuickForm_xbutton')
73 * Validation rules known to HTML_QuickForm
74 * @see HTML_QuickForm::registerRule(), HTML_QuickForm::getRegisteredRules(),
75 * HTML_QuickForm::isRuleRegistered()
76 * @global array $GLOBALS['_HTML_QuickForm_registered_rules']
78 $GLOBALS['_HTML_QuickForm_registered_rules'] = array(
79 'required' => array('html_quickform_rule_required', 'HTML/QuickForm/Rule/Required.php'),
80 'maxlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
81 'minlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
82 'rangelength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
83 'email' => array('html_quickform_rule_email', 'HTML/QuickForm/Rule/Email.php'),
84 'regex' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
85 'lettersonly' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
86 'alphanumeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
87 'numeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
88 'nopunctuation' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
89 'nonzero' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
90 'callback' => array('html_quickform_rule_callback', 'HTML/QuickForm/Rule/Callback.php'),
91 'compare' => array('html_quickform_rule_compare', 'HTML/QuickForm/Rule/Compare.php')
97 * Error codes for HTML_QuickForm
99 * Codes are mapped to textual messages by errorMessage() method, if you add a
100 * new code be sure to add a new message for it to errorMessage()
102 * @see HTML_QuickForm::errorMessage()
104 define('QUICKFORM_OK', 1);
105 define('QUICKFORM_ERROR', -1);
106 define('QUICKFORM_INVALID_RULE', -2);
107 define('QUICKFORM_NONEXIST_ELEMENT', -3);
108 define('QUICKFORM_INVALID_FILTER', -4);
109 define('QUICKFORM_UNREGISTERED_ELEMENT', -5);
110 define('QUICKFORM_INVALID_ELEMENT_NAME', -6);
111 define('QUICKFORM_INVALID_PROCESS', -7);
112 define('QUICKFORM_DEPRECATED', -8);
113 define('QUICKFORM_INVALID_DATASOURCE', -9);
119 * Create, validate and process HTML forms
122 * @package HTML_QuickForm
123 * @author Adam Daniel <adaniel1@eesus.jnj.com>
124 * @author Bertrand Mansion <bmansion@mamasam.com>
125 * @author Alexey Borzov <avb@php.net>
126 * @version Release: 3.2.16
128 class HTML_QuickForm extends HTML_Common
133 * Array containing the form fields
138 var $_elements = array();
141 * Array containing element name to index map
146 var $_elementIndex = array();
149 * Array containing indexes of duplicate elements
154 var $_duplicateIndex = array();
157 * Array containing required field IDs
162 var $_required = array();
165 * Prefix message in javascript alert if error
170 var $_jsPrefix = 'Invalid information entered.';
173 * Postfix message in javascript alert if error
178 var $_jsPostfix = 'Please correct these fields.';
181 * Datasource object implementing the informal
182 * datasource protocol
190 * Array of default form values
195 var $_defaultValues = array();
198 * Array of constant form values
203 var $_constantValues = array();
206 * Array of submitted form values
211 var $_submitValues = array();
214 * Array of submitted form files
219 var $_submitFiles = array();
222 * Value for maxfilesize hidden element if form contains file input
227 var $_maxFileSize = 1048576; // 1 Mb = 1048576
230 * Flag to know if all fields are frozen
235 var $_freezeAll = false;
238 * Array containing the form rules
243 var $_rules = array();
246 * Form rules, global variety
250 var $_formRules = array();
253 * Array containing the validation errors
258 var $_errors = array();
261 * Note for required fields in the form
266 var $_requiredNote = '<span style="font-size:80%; color:#ff0000;">*</span><span style="font-size:80%;"> denotes required field</span>';
269 * Whether the form was submitted
273 var $_flagSubmitted = false;
280 * @param string $formName Form's name.
281 * @param string $method (optional)Form's method defaults to 'POST'
282 * @param string $action (optional)Form's action
283 * @param string $target (optional)Form's target defaults to '_self'
284 * @param mixed $attributes (optional)Extra attributes for <form> tag
285 * @param bool $trackSubmit (optional)Whether to track if the form was submitted by adding a special hidden field
288 function HTML_QuickForm($formName='', $method='post', $action='', $target='', $attributes=null, $trackSubmit = false)
290 HTML_Common::HTML_Common($attributes);
291 $method = (strtoupper($method) == 'GET') ? 'get' : 'post';
292 $action = ($action == '') ? $_SERVER['PHP_SELF'] : $action;
293 $target = empty($target) ? array() : array('target' => $target);
294 $attributes = array('action'=>$action, 'method'=>$method, 'name'=>$formName, 'id'=>$formName) + $target;
295 $this->updateAttributes($attributes);
296 if (!$trackSubmit || isset($_REQUEST['_qf__' . $formName])) {
297 if (1 == get_magic_quotes_gpc()) {
298 $this->_submitValues = $this->_recursiveFilter('stripslashes', 'get' == $method? $_GET: $_POST);
299 foreach ($_FILES as $keyFirst => $valFirst) {
300 foreach ($valFirst as $keySecond => $valSecond) {
301 if ('name' == $keySecond) {
302 $this->_submitFiles[$keyFirst][$keySecond] = $this->_recursiveFilter('stripslashes', $valSecond);
304 $this->_submitFiles[$keyFirst][$keySecond] = $valSecond;
309 $this->_submitValues = 'get' == $method? $_GET: $_POST;
310 $this->_submitFiles = $_FILES;
312 $this->_flagSubmitted = count($this->_submitValues) > 0 || count($this->_submitFiles) > 0;
315 unset($this->_submitValues['_qf__' . $formName]);
316 $this->addElement('hidden', '_qf__' . $formName, null);
318 if (preg_match('/^([0-9]+)([a-zA-Z]*)$/', ini_get('upload_max_filesize'), $matches)) {
319 // see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
320 switch (strtoupper($matches['2'])) {
322 $this->_maxFileSize = $matches['1'] * 1073741824;
325 $this->_maxFileSize = $matches['1'] * 1048576;
328 $this->_maxFileSize = $matches['1'] * 1024;
331 $this->_maxFileSize = $matches['1'];
340 * Returns the current API version
346 function apiVersion()
349 } // end func apiVersion
352 // {{{ registerElementType()
355 * Registers a new element type
357 * @param string $typeName Name of element type
358 * @param string $include Include path for element type
359 * @param string $className Element class name
364 function registerElementType($typeName, $include, $className)
366 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($typeName)] = array($include, $className);
367 } // end func registerElementType
370 // {{{ registerRule()
373 * Registers a new validation rule
375 * @param string $ruleName Name of validation rule
376 * @param string $type Either: 'regex', 'function' or 'rule' for an HTML_QuickForm_Rule object
377 * @param string $data1 Name of function, regular expression or HTML_QuickForm_Rule classname
378 * @param string $data2 Object parent of above function or HTML_QuickForm_Rule file path
383 function registerRule($ruleName, $type, $data1, $data2 = null)
385 include_once('HTML/QuickForm/RuleRegistry.php');
386 $registry =& HTML_QuickForm_RuleRegistry::singleton();
387 $registry->registerRule($ruleName, $type, $data1, $data2);
388 } // end func registerRule
391 // {{{ elementExists()
394 * Returns true if element is in the form
396 * @param string $element form name of element to check
401 function elementExists($element=null)
403 return isset($this->_elementIndex[$element]);
404 } // end func elementExists
407 // {{{ setDatasource()
410 * Sets a datasource object for this form object
412 * Datasource default and constant values will feed the QuickForm object if
413 * the datasource implements defaultValues() and constantValues() methods.
415 * @param object $datasource datasource object implementing the informal datasource protocol
416 * @param mixed $defaultsFilter string or array of filter(s) to apply to default values
417 * @param mixed $constantsFilter string or array of filter(s) to apply to constants values
421 * @throws HTML_QuickForm_Error
423 function setDatasource(&$datasource, $defaultsFilter = null, $constantsFilter = null)
425 if (is_object($datasource)) {
426 $this->_datasource =& $datasource;
427 if (is_callable(array($datasource, 'defaultValues'))) {
428 $this->setDefaults($datasource->defaultValues($this), $defaultsFilter);
430 if (is_callable(array($datasource, 'constantValues'))) {
431 $this->setConstants($datasource->constantValues($this), $constantsFilter);
434 return PEAR::raiseError(null, QUICKFORM_INVALID_DATASOURCE, null, E_USER_WARNING, "Datasource is not an object in QuickForm::setDatasource()", 'HTML_QuickForm_Error', true);
436 } // end func setDatasource
442 * Initializes default form values
444 * @param array $defaultValues values used to fill the form
445 * @param mixed $filter (optional) filter(s) to apply to all default values
449 * @throws HTML_QuickForm_Error
451 function setDefaults($defaultValues = null, $filter = null)
453 if (is_array($defaultValues)) {
454 if (isset($filter)) {
455 if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) {
456 foreach ($filter as $val) {
457 if (!is_callable($val)) {
458 return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
460 $defaultValues = $this->_recursiveFilter($val, $defaultValues);
463 } elseif (!is_callable($filter)) {
464 return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
466 $defaultValues = $this->_recursiveFilter($filter, $defaultValues);
469 $this->_defaultValues = HTML_QuickForm::arrayMerge($this->_defaultValues, $defaultValues);
470 foreach (array_keys($this->_elements) as $key) {
471 $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
474 } // end func setDefaults
477 // {{{ setConstants()
480 * Initializes constant form values.
481 * These values won't get overridden by POST or GET vars
483 * @param array $constantValues values used to fill the form
484 * @param mixed $filter (optional) filter(s) to apply to all default values
489 * @throws HTML_QuickForm_Error
491 function setConstants($constantValues = null, $filter = null)
493 if (is_array($constantValues)) {
494 if (isset($filter)) {
495 if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) {
496 foreach ($filter as $val) {
497 if (!is_callable($val)) {
498 return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
500 $constantValues = $this->_recursiveFilter($val, $constantValues);
503 } elseif (!is_callable($filter)) {
504 return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
506 $constantValues = $this->_recursiveFilter($filter, $constantValues);
509 $this->_constantValues = HTML_QuickForm::arrayMerge($this->_constantValues, $constantValues);
510 foreach (array_keys($this->_elements) as $key) {
511 $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
514 } // end func setConstants
517 // {{{ setMaxFileSize()
520 * Sets the value of MAX_FILE_SIZE hidden element
522 * @param int $bytes Size in bytes
527 function setMaxFileSize($bytes = 0)
530 $this->_maxFileSize = $bytes;
532 if (!$this->elementExists('MAX_FILE_SIZE')) {
533 $this->addElement('hidden', 'MAX_FILE_SIZE', $this->_maxFileSize);
535 $el =& $this->getElement('MAX_FILE_SIZE');
536 $el->updateAttributes(array('value' => $this->_maxFileSize));
538 } // end func setMaxFileSize
541 // {{{ getMaxFileSize()
544 * Returns the value of MAX_FILE_SIZE hidden element
548 * @return int max file size in bytes
550 function getMaxFileSize()
552 return $this->_maxFileSize;
553 } // end func getMaxFileSize
556 // {{{ &createElement()
559 * Creates a new form element of the given type.
561 * This method accepts variable number of parameters, their
562 * meaning and count depending on $elementType
564 * @param string $elementType type of element to add (text, textarea, file...)
567 * @return HTML_QuickForm_Element
568 * @throws HTML_QuickForm_Error
570 function &createElement($elementType)
572 $args = func_get_args();
573 $element =& HTML_QuickForm::_loadElement('createElement', $elementType, array_slice($args, 1), null);
575 } // end func createElement
578 // {{{ _loadElement()
581 * Returns a form element of the given type
583 * @param string $event event to send to newly created element ('createElement' or 'addElement')
584 * @param string $type element type
585 * @param array $args arguments for event
588 * @return HTML_QuickForm_Element
589 * @throws HTML_QuickForm_Error
591 function &_loadElement($event, $type, $args, $form)
593 $type = strtolower($type);
594 if (!HTML_QuickForm::isTypeRegistered($type)) {
595 $error = PEAR::raiseError(null, QUICKFORM_UNREGISTERED_ELEMENT, null, E_USER_WARNING, "Element '$type' does not exist in HTML_QuickForm::_loadElement()", 'HTML_QuickForm_Error', true);
598 $className = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][1];
599 $includeFile = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][0];
600 include_once($includeFile);
601 $elementObject = new $className();
602 for ($i = 0; $i < 5; $i++) {
603 if (!isset($args[$i])) {
607 $err = $elementObject->onQuickFormEvent($event, $args, $form);
611 return $elementObject;
612 } // end func _loadElement
618 * Adds an element into the form
620 * If $element is a string representing element type, then this
621 * method accepts variable number of parameters, their meaning
622 * and count depending on $element
624 * @param mixed $element element object or type of element to add (text, textarea, file...)
626 * @return HTML_QuickForm_Element a reference to newly added element
628 * @throws HTML_QuickForm_Error
630 function &addElement($element)
632 if (is_object($element) && is_subclass_of($element, 'html_quickform_element')) {
633 $elementObject = &$element;
634 $elementObject->onQuickFormEvent('updateValue', null, $this);
636 $args = func_get_args();
637 $elementObject =& $this->_loadElement('addElement', $element, array_slice($args, 1), $this);
638 if (PEAR::isError($elementObject)) {
639 return $elementObject;
642 $elementName = $elementObject->getName();
644 // Add the element if it is not an incompatible duplicate
645 if (!empty($elementName) && isset($this->_elementIndex[$elementName])) {
646 if ($this->_elements[$this->_elementIndex[$elementName]]->getType() ==
647 $elementObject->getType()) {
648 $this->_elements[] =& $elementObject;
649 $elKeys = array_keys($this->_elements);
650 $this->_duplicateIndex[$elementName][] = end($elKeys);
652 $error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::addElement()", 'HTML_QuickForm_Error', true);
656 $this->_elements[] =& $elementObject;
657 $elKeys = array_keys($this->_elements);
658 $this->_elementIndex[$elementName] = end($elKeys);
660 if ($this->_freezeAll) {
661 $elementObject->freeze();
664 return $elementObject;
665 } // end func addElement
668 // {{{ insertElementBefore()
671 * Inserts a new element right before the other element
673 * Warning: it is not possible to check whether the $element is already
674 * added to the form, therefore if you want to move the existing form
675 * element to a new position, you'll have to use removeElement():
676 * $form->insertElementBefore($form->removeElement('foo', false), 'bar');
680 * @param HTML_QuickForm_element Element to insert
681 * @param string Name of the element before which the new
683 * @return HTML_QuickForm_element reference to inserted element
684 * @throws HTML_QuickForm_Error
686 function &insertElementBefore(&$element, $nameAfter)
688 if (!empty($this->_duplicateIndex[$nameAfter])) {
689 $error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, 'Several elements named "' . $nameAfter . '" exist in HTML_QuickForm::insertElementBefore().', 'HTML_QuickForm_Error', true);
691 } elseif (!$this->elementExists($nameAfter)) {
692 $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$nameAfter' does not exist in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true);
695 $elementName = $element->getName();
696 $targetIdx = $this->_elementIndex[$nameAfter];
698 // Like in addElement(), check that it's not an incompatible duplicate
699 if (!empty($elementName) && isset($this->_elementIndex[$elementName])) {
700 if ($this->_elements[$this->_elementIndex[$elementName]]->getType() != $element->getType()) {
701 $error = PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true);
706 // Move all the elements after added back one place, reindex _elementIndex and/or _duplicateIndex
707 $elKeys = array_keys($this->_elements);
708 for ($i = end($elKeys); $i >= $targetIdx; $i--) {
709 if (isset($this->_elements[$i])) {
710 $currentName = $this->_elements[$i]->getName();
711 $this->_elements[$i + 1] =& $this->_elements[$i];
712 if ($this->_elementIndex[$currentName] == $i) {
713 $this->_elementIndex[$currentName] = $i + 1;
715 $dupIdx = array_search($i, $this->_duplicateIndex[$currentName]);
716 $this->_duplicateIndex[$currentName][$dupIdx] = $i + 1;
718 unset($this->_elements[$i]);
721 // Put the element in place finally
722 $this->_elements[$targetIdx] =& $element;
724 $this->_elementIndex[$elementName] = $targetIdx;
726 $this->_duplicateIndex[$elementName][] = $targetIdx;
728 $element->onQuickFormEvent('updateValue', null, $this);
729 if ($this->_freezeAll) {
732 // If not done, the elements will appear in reverse order
733 ksort($this->_elements);
741 * Adds an element group
742 * @param array $elements array of elements composing the group
743 * @param string $name (optional)group name
744 * @param string $groupLabel (optional)group label
745 * @param string $separator (optional)string to separate elements
746 * @param string $appendName (optional)specify whether the group name should be
747 * used in the form element name ex: group[element]
748 * @return HTML_QuickForm_group reference to a newly added group
751 * @throws HTML_QuickForm_Error
753 function &addGroup($elements, $name=null, $groupLabel='', $separator=null, $appendName = true)
755 static $anonGroups = 1;
757 if (0 == strlen($name)) {
758 $name = 'qf_group_' . $anonGroups++;
761 $group =& $this->addElement('group', $name, $groupLabel, $elements, $separator, $appendName);
763 } // end func addGroup
769 * Returns a reference to the element
771 * @param string $element Element name
774 * @return HTML_QuickForm_element reference to element
775 * @throws HTML_QuickForm_Error
777 function &getElement($element)
779 if (isset($this->_elementIndex[$element])) {
780 return $this->_elements[$this->_elementIndex[$element]];
782 $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElement()", 'HTML_QuickForm_Error', true);
785 } // end func getElement
788 // {{{ &getElementValue()
791 * Returns the element's raw value
793 * This returns the value as submitted by the form (not filtered)
794 * or set via setDefaults() or setConstants()
796 * @param string $element Element name
799 * @return mixed element value
800 * @throws HTML_QuickForm_Error
802 function &getElementValue($element)
804 if (!isset($this->_elementIndex[$element])) {
805 $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
808 $value = $this->_elements[$this->_elementIndex[$element]]->getValue();
809 if (isset($this->_duplicateIndex[$element])) {
810 foreach ($this->_duplicateIndex[$element] as $index) {
811 if (null !== ($v = $this->_elements[$index]->getValue())) {
812 if (is_array($value)) {
815 $value = (null === $value)? $v: array($value, $v);
821 } // end func getElementValue
824 // {{{ getSubmitValue()
827 * Returns the elements value after submit and filter
829 * @param string Element name
832 * @return mixed submitted element value or null if not set
834 function getSubmitValue($elementName)
837 if (isset($this->_submitValues[$elementName]) || isset($this->_submitFiles[$elementName])) {
838 $value = isset($this->_submitValues[$elementName])? $this->_submitValues[$elementName]: array();
839 if (is_array($value) && isset($this->_submitFiles[$elementName])) {
840 foreach ($this->_submitFiles[$elementName] as $k => $v) {
841 $value = HTML_QuickForm::arrayMerge($value, $this->_reindexFiles($this->_submitFiles[$elementName][$k], $k));
845 } elseif ('file' == $this->getElementType($elementName)) {
846 return $this->getElementValue($elementName);
848 } elseif (false !== ($pos = strpos($elementName, '['))) {
850 array('\\', '\''), array('\\\\', '\\\''),
851 substr($elementName, 0, $pos)
855 array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
856 substr($elementName, $pos + 1, -1)
858 $idx = "['" . $keys . "']";
859 $keyArray = explode("']['", $keys);
861 if (isset($this->_submitValues[$base])) {
862 $value = HTML_QuickForm_utils::recursiveValue($this->_submitValues[$base], $keyArray, NULL);
865 if ((is_array($value) || null === $value) && isset($this->_submitFiles[$base])) {
866 $props = array('name', 'type', 'size', 'tmp_name', 'error');
867 $code = "if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" .
871 foreach ($props as $prop) {
872 $code .= " \$v = HTML_QuickForm::arrayMerge(\$v, \$this->_reindexFiles(\$this->_submitFiles['{$base}']['{$prop}']{$idx}, '{$prop}'));\n";
874 $fileValue = eval($code . " return \$v;\n}\n");
875 if (null !== $fileValue) {
876 $value = null === $value? $fileValue: HTML_QuickForm::arrayMerge($value, $fileValue);
881 // This is only supposed to work for groups with appendName = false
882 if (null === $value && 'group' == $this->getElementType($elementName)) {
883 $group =& $this->getElement($elementName);
884 $elements =& $group->getElements();
885 foreach (array_keys($elements) as $key) {
886 $name = $group->getElementName($key);
887 // prevent endless recursion in case of radios and such
888 if ($name != $elementName) {
889 if (null !== ($v = $this->getSubmitValue($name))) {
896 } // end func getSubmitValue
899 // {{{ _reindexFiles()
902 * A helper function to change the indexes in $_FILES array
904 * @param mixed Some value from the $_FILES array
905 * @param string The key from the $_FILES array that should be appended
908 function _reindexFiles($value, $key)
910 if (!is_array($value)) {
911 return array($key => $value);
914 foreach ($value as $k => $v) {
915 $ret[$k] = $this->_reindexFiles($v, $key);
922 // {{{ getElementError()
925 * Returns error corresponding to validated element
927 * @param string $element Name of form element to check
930 * @return string error message corresponding to checked element
932 function getElementError($element)
934 if (isset($this->_errors[$element])) {
935 return $this->_errors[$element];
937 } // end func getElementError
940 // {{{ setElementError()
943 * Set error message for a form element
945 * @param string $element Name of form element to set error for
946 * @param string $message Error message, if empty then removes the current error message
951 function setElementError($element, $message = null)
953 if (!empty($message)) {
954 $this->_errors[$element] = $message;
956 unset($this->_errors[$element]);
958 } // end func setElementError
961 // {{{ getElementType()
964 * Returns the type of the given element
966 * @param string $element Name of form element
969 * @return string Type of the element, false if the element is not found
971 function getElementType($element)
973 if (isset($this->_elementIndex[$element])) {
974 return $this->_elements[$this->_elementIndex[$element]]->getType();
977 } // end func getElementType
980 // {{{ updateElementAttr()
983 * Updates Attributes for one or more elements
985 * @param mixed $elements Array of element names/objects or string of elements to be updated
986 * @param mixed $attrs Array or sting of html attributes
991 function updateElementAttr($elements, $attrs)
993 if (is_string($elements)) {
994 $elements = preg_split('/[ ]?,[ ]?/', $elements);
996 foreach (array_keys($elements) as $key) {
997 if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) {
998 $elements[$key]->updateAttributes($attrs);
999 } elseif (isset($this->_elementIndex[$elements[$key]])) {
1000 $this->_elements[$this->_elementIndex[$elements[$key]]]->updateAttributes($attrs);
1001 if (isset($this->_duplicateIndex[$elements[$key]])) {
1002 foreach ($this->_duplicateIndex[$elements[$key]] as $index) {
1003 $this->_elements[$index]->updateAttributes($attrs);
1008 } // end func updateElementAttr
1011 // {{{ removeElement()
1014 * Removes an element
1016 * The method "unlinks" an element from the form, returning the reference
1017 * to the element object. If several elements named $elementName exist,
1018 * it removes the first one, leaving the others intact.
1020 * @param string $elementName The element name
1021 * @param boolean $removeRules True if rules for this element are to be removed too
1024 * @return HTML_QuickForm_element a reference to the removed element
1025 * @throws HTML_QuickForm_Error
1027 function &removeElement($elementName, $removeRules = true)
1029 if (!isset($this->_elementIndex[$elementName])) {
1030 $error = PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$elementName' does not exist in HTML_QuickForm::removeElement()", 'HTML_QuickForm_Error', true);
1033 $el =& $this->_elements[$this->_elementIndex[$elementName]];
1034 unset($this->_elements[$this->_elementIndex[$elementName]]);
1035 if (empty($this->_duplicateIndex[$elementName])) {
1036 unset($this->_elementIndex[$elementName]);
1038 $this->_elementIndex[$elementName] = array_shift($this->_duplicateIndex[$elementName]);
1041 $this->_required = array_diff($this->_required, array($elementName));
1042 unset($this->_rules[$elementName], $this->_errors[$elementName]);
1043 if ('group' == $el->getType()) {
1044 foreach (array_keys($el->getElements()) as $key) {
1045 unset($this->_rules[$el->getElementName($key)]);
1050 } // end func removeElement
1056 * Adds a validation rule for the given field
1058 * If the element is in fact a group, it will be considered as a whole.
1059 * To validate grouped elements as separated entities,
1060 * use addGroupRule instead of addRule.
1062 * @param string $element Form element name
1063 * @param string $message Message to display for invalid data
1064 * @param string $type Rule type, use getRegisteredRules() to get types
1065 * @param string $format (optional)Required for extra rule data
1066 * @param string $validation (optional)Where to perform validation: "server", "client"
1067 * @param boolean $reset Client-side validation: reset the form element to its original value if there is an error?
1068 * @param boolean $force Force the rule to be applied, even if the target form element does not exist
1071 * @throws HTML_QuickForm_Error
1073 function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false)
1076 if (!is_array($element) && !$this->elementExists($element)) {
1077 return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
1078 } elseif (is_array($element)) {
1079 foreach ($element as $el) {
1080 if (!$this->elementExists($el)) {
1081 return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$el' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
1086 if (false === ($newName = $this->isRuleRegistered($type, true))) {
1087 return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
1088 } elseif (is_string($newName)) {
1091 if (is_array($element)) {
1092 $dependent = $element;
1093 $element = array_shift($dependent);
1097 if ($type == 'required' || $type == 'uploadedfile') {
1098 $this->_required[] = $element;
1100 if (!isset($this->_rules[$element])) {
1101 $this->_rules[$element] = array();
1103 if ($validation == 'client') {
1104 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
1106 $this->_rules[$element][] = array(
1108 'format' => $format,
1109 'message' => $message,
1110 'validation' => $validation,
1112 'dependent' => $dependent
1114 } // end func addRule
1117 // {{{ addGroupRule()
1120 * Adds a validation rule for the given group of elements
1122 * Only groups with a name can be assigned a validation rule
1123 * Use addGroupRule when you need to validate elements inside the group.
1124 * Use addRule if you need to validate the group as a whole. In this case,
1125 * the same rule will be applied to all elements in the group.
1126 * Use addRule if you need to validate the group against a function.
1128 * @param string $group Form group name
1129 * @param mixed $arg1 Array for multiple elements or error message string for one element
1130 * @param string $type (optional)Rule type use getRegisteredRules() to get types
1131 * @param string $format (optional)Required for extra rule data
1132 * @param int $howmany (optional)How many valid elements should be in the group
1133 * @param string $validation (optional)Where to perform validation: "server", "client"
1134 * @param bool $reset Client-side: whether to reset the element's value to its original state if validation failed.
1137 * @throws HTML_QuickForm_Error
1139 function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false)
1141 if (!$this->elementExists($group)) {
1142 return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Group '$group' does not exist in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
1145 $groupObj =& $this->getElement($group);
1146 if (is_array($arg1)) {
1148 foreach ($arg1 as $elementIndex => $rules) {
1149 $elementName = $groupObj->getElementName($elementIndex);
1150 foreach ($rules as $rule) {
1151 $format = (isset($rule[2])) ? $rule[2] : null;
1152 $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server';
1153 $reset = isset($rule[4]) && $rule[4];
1155 if (false === ($newName = $this->isRuleRegistered($type, true))) {
1156 return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
1157 } elseif (is_string($newName)) {
1161 $this->_rules[$elementName][] = array(
1163 'format' => $format,
1164 'message' => $rule[0],
1165 'validation' => $validation,
1169 if ('required' == $type || 'uploadedfile' == $type) {
1170 $groupObj->_required[] = $elementName;
1171 $this->_required[] = $elementName;
1174 if ('client' == $validation) {
1175 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
1179 if ($required > 0 && count($groupObj->getElements()) == $required) {
1180 $this->_required[] = $group;
1182 } elseif (is_string($arg1)) {
1183 if (false === ($newName = $this->isRuleRegistered($type, true))) {
1184 return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
1185 } elseif (is_string($newName)) {
1189 // addGroupRule() should also handle <select multiple>
1190 if (is_a($groupObj, 'html_quickform_group')) {
1191 // Radios need to be handled differently when required
1192 if ($type == 'required' && $groupObj->getGroupType() == 'radio') {
1193 $howmany = ($howmany == 0) ? 1 : $howmany;
1195 $howmany = ($howmany == 0) ? count($groupObj->getElements()) : $howmany;
1199 $this->_rules[$group][] = array('type' => $type,
1200 'format' => $format,
1202 'validation' => $validation,
1203 'howmany' => $howmany,
1205 if ($type == 'required') {
1206 $this->_required[] = $group;
1208 if ($validation == 'client') {
1209 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
1212 } // end func addGroupRule
1215 // {{{ addFormRule()
1218 * Adds a global validation rule
1220 * This should be used when for a rule involving several fields or if
1221 * you want to use some completely custom validation for your form.
1222 * The rule function/method should return true in case of successful
1223 * validation and array('element name' => 'error') when there were errors.
1226 * @param mixed Callback, either function name or array(&$object, 'method')
1227 * @throws HTML_QuickForm_Error
1229 function addFormRule($rule)
1231 if (!is_callable($rule)) {
1232 return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, 'Callback function does not exist in HTML_QuickForm::addFormRule()', 'HTML_QuickForm_Error', true);
1234 $this->_formRules[] = $rule;
1238 // {{{ applyFilter()
1241 * Applies a data filter for the given field(s)
1243 * @param mixed $element Form element name or array of such names
1244 * @param mixed $filter Callback, either function name or array(&$object, 'method')
1247 * @throws HTML_QuickForm_Error
1249 function applyFilter($element, $filter)
1251 if (!is_callable($filter)) {
1252 return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::applyFilter()", 'HTML_QuickForm_Error', true);
1254 if ($element == '__ALL__') {
1255 $this->_submitValues = $this->_recursiveFilter($filter, $this->_submitValues);
1257 if (!is_array($element)) {
1258 $element = array($element);
1260 foreach ($element as $elName) {
1261 $value = $this->getSubmitValue($elName);
1262 if (null !== $value) {
1263 if (false === strpos($elName, '[')) {
1264 $this->_submitValues[$elName] = $this->_recursiveFilter($filter, $value);
1266 $idx = "['" . str_replace(
1267 array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
1270 eval("\$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);");
1275 } // end func applyFilter
1278 // {{{ _recursiveFilter()
1281 * Recursively apply a filter function
1283 * @param string $filter filter to apply
1284 * @param mixed $value submitted values
1287 * @return cleaned values
1289 function _recursiveFilter($filter, $value)
1291 if (is_array($value)) {
1292 $cleanValues = array();
1293 foreach ($value as $k => $v) {
1294 $cleanValues[$k] = $this->_recursiveFilter($filter, $v);
1296 return $cleanValues;
1298 return call_user_func($filter, $value);
1300 } // end func _recursiveFilter
1308 * Merges two array like the PHP function array_merge but recursively.
1309 * The main difference is that existing keys will not be renumbered
1310 * if they are integers.
1313 * @param array $a original array
1314 * @param array $b array which will be merged into first one
1315 * @return array merged array
1317 function arrayMerge($a, $b)
1319 foreach ($b as $k => $v) {
1321 if (isset($a[$k]) && !is_array($a[$k])) {
1324 if (!isset($a[$k])) {
1327 $a[$k] = HTML_QuickForm::arrayMerge($a[$k], $v);
1334 } // end func arrayMerge
1337 // {{{ isTypeRegistered()
1340 * Returns whether or not the form element type is supported
1342 * @param string $type Form element type
1347 function isTypeRegistered($type)
1349 return isset($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($type)]);
1350 } // end func isTypeRegistered
1353 // {{{ getRegisteredTypes()
1356 * Returns an array of registered element types
1362 function getRegisteredTypes()
1364 return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']);
1365 } // end func getRegisteredTypes
1368 // {{{ isRuleRegistered()
1371 * Returns whether or not the given rule is supported
1373 * @param string $name Validation rule name
1374 * @param bool Whether to automatically register subclasses of HTML_QuickForm_Rule
1377 * @return mixed true if previously registered, false if not, new rule name if auto-registering worked
1379 function isRuleRegistered($name, $autoRegister = false)
1381 if (is_scalar($name) && isset($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) {
1383 } elseif (!$autoRegister) {
1386 // automatically register the rule if requested
1387 include_once 'HTML/QuickForm/RuleRegistry.php';
1389 if (is_object($name) && is_a($name, 'html_quickform_rule')) {
1390 $ruleName = !empty($name->name)? $name->name: strtolower(get_class($name));
1391 } elseif (is_string($name) && class_exists($name)) {
1392 $parent = strtolower($name);
1394 if ('html_quickform_rule' == strtolower($parent)) {
1395 $ruleName = strtolower($name);
1398 } while ($parent = get_parent_class($parent));
1401 $registry =& HTML_QuickForm_RuleRegistry::singleton();
1402 $registry->registerRule($ruleName, null, $name);
1405 } // end func isRuleRegistered
1408 // {{{ getRegisteredRules()
1411 * Returns an array of registered validation rules
1417 function getRegisteredRules()
1419 return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']);
1420 } // end func getRegisteredRules
1423 // {{{ isElementRequired()
1426 * Returns whether or not the form element is required
1428 * @param string $element Form element name
1433 function isElementRequired($element)
1435 return in_array($element, $this->_required, true);
1436 } // end func isElementRequired
1439 // {{{ isElementFrozen()
1442 * Returns whether or not the form element is frozen
1444 * @param string $element Form element name
1449 function isElementFrozen($element)
1451 if (isset($this->_elementIndex[$element])) {
1452 return $this->_elements[$this->_elementIndex[$element]]->isFrozen();
1455 } // end func isElementFrozen
1458 // {{{ setJsWarnings()
1461 * Sets JavaScript warning messages
1463 * @param string $pref Prefix warning
1464 * @param string $post Postfix warning
1469 function setJsWarnings($pref, $post)
1471 $this->_jsPrefix = $pref;
1472 $this->_jsPostfix = $post;
1473 } // end func setJsWarnings
1476 // {{{ setRequiredNote()
1479 * Sets required-note
1481 * @param string $note Message indicating some elements are required
1486 function setRequiredNote($note)
1488 $this->_requiredNote = $note;
1489 } // end func setRequiredNote
1492 // {{{ getRequiredNote()
1495 * Returns the required note
1501 function getRequiredNote()
1503 return $this->_requiredNote;
1504 } // end func getRequiredNote
1510 * Performs the server side validation
1513 * @return boolean true if no error found
1514 * @throws HTML_QuickForm_Error
1518 if (count($this->_rules) == 0 && count($this->_formRules) == 0 &&
1519 $this->isSubmitted()) {
1520 return (0 == count($this->_errors));
1521 } elseif (!$this->isSubmitted()) {
1525 include_once('HTML/QuickForm/RuleRegistry.php');
1526 $registry =& HTML_QuickForm_RuleRegistry::singleton();
1528 foreach ($this->_rules as $target => $rules) {
1529 $submitValue = $this->getSubmitValue($target);
1531 foreach ($rules as $rule) {
1532 if ((isset($rule['group']) && isset($this->_errors[$rule['group']])) ||
1533 isset($this->_errors[$target])) {
1536 // If element is not required and is empty, we shouldn't validate it
1537 if (!$this->isElementRequired($target)) {
1538 if (!isset($submitValue) || '' == $submitValue) {
1540 // Fix for bug #3501: we shouldn't validate not uploaded files, either.
1541 // Unfortunately, we can't just use $element->isUploadedFile() since
1542 // the element in question can be buried in group. Thus this hack.
1543 // See also bug #12014, we should only consider a file that has
1544 // status UPLOAD_ERR_NO_FILE as not uploaded, in all other cases
1545 // validation should be performed, so that e.g. 'maxfilesize' rule
1546 // will display an error if status is UPLOAD_ERR_INI_SIZE
1547 // or UPLOAD_ERR_FORM_SIZE
1548 } elseif (is_array($submitValue)) {
1549 if (false === ($pos = strpos($target, '['))) {
1550 $isUpload = !empty($this->_submitFiles[$target]);
1552 $base = str_replace(
1553 array('\\', '\''), array('\\\\', '\\\''),
1554 substr($target, 0, $pos)
1556 $idx = "['" . str_replace(
1557 array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
1558 substr($target, $pos + 1, -1)
1560 eval("\$isUpload = isset(\$this->_submitFiles['{$base}']['name']{$idx});");
1562 if ($isUpload && (!isset($submitValue['error']) || UPLOAD_ERR_NO_FILE == $submitValue['error'])) {
1567 if (isset($rule['dependent']) && is_array($rule['dependent'])) {
1568 $values = array($submitValue);
1569 foreach ($rule['dependent'] as $elName) {
1570 $values[] = $this->getSubmitValue($elName);
1572 $result = $registry->validate($rule['type'], $values, $rule['format'], true);
1573 } elseif (is_array($submitValue) && !isset($rule['howmany'])) {
1574 $result = $registry->validate($rule['type'], $submitValue, $rule['format'], true);
1576 $result = $registry->validate($rule['type'], $submitValue, $rule['format'], false);
1579 if (!$result || (!empty($rule['howmany']) && $rule['howmany'] > (int)$result)) {
1580 if (isset($rule['group'])) {
1581 $this->_errors[$rule['group']] = $rule['message'];
1583 $this->_errors[$target] = $rule['message'];
1589 // process the global rules now
1590 foreach ($this->_formRules as $rule) {
1591 if (true !== ($res = call_user_func($rule, $this->_submitValues, $this->_submitFiles))) {
1592 if (is_array($res)) {
1593 $this->_errors += $res;
1595 return PEAR::raiseError(null, QUICKFORM_ERROR, null, E_USER_WARNING, 'Form rule callback returned invalid value in HTML_QuickForm::validate()', 'HTML_QuickForm_Error', true);
1600 return (0 == count($this->_errors));
1601 } // end func validate
1607 * Displays elements without HTML input tags
1609 * @param mixed $elementList array or string of element(s) to be frozen
1612 * @throws HTML_QuickForm_Error
1614 function freeze($elementList=null)
1616 if (!isset($elementList)) {
1617 $this->_freezeAll = true;
1618 $elementList = array();
1620 if (!is_array($elementList)) {
1621 $elementList = preg_split('/[ ]*,[ ]*/', $elementList);
1623 $elementList = array_flip($elementList);
1626 foreach (array_keys($this->_elements) as $key) {
1627 $name = $this->_elements[$key]->getName();
1628 if ($this->_freezeAll || isset($elementList[$name])) {
1629 $this->_elements[$key]->freeze();
1630 unset($elementList[$name]);
1634 if (!empty($elementList)) {
1635 return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Nonexistant element(s): '" . implode("', '", array_keys($elementList)) . "' in HTML_QuickForm::freeze()", 'HTML_QuickForm_Error', true);
1638 } // end func freeze
1644 * Returns whether or not the whole form is frozen
1652 return $this->_freezeAll;
1653 } // end func isFrozen
1659 * Performs the form data processing
1661 * @param mixed $callback Callback, either function name or array(&$object, 'method')
1662 * @param bool $mergeFiles Whether uploaded files should be processed too
1665 * @throws HTML_QuickForm_Error
1666 * @return mixed Whatever value the $callback function returns
1668 function process($callback, $mergeFiles = true)
1670 if (!is_callable($callback)) {
1671 return PEAR::raiseError(null, QUICKFORM_INVALID_PROCESS, null, E_USER_WARNING, "Callback function does not exist in QuickForm::process()", 'HTML_QuickForm_Error', true);
1673 $values = ($mergeFiles === true) ? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles) : $this->_submitValues;
1674 return call_user_func($callback, $values);
1675 } // end func process
1681 * Accepts a renderer
1683 * @param object An HTML_QuickForm_Renderer object
1688 function accept(&$renderer)
1690 $renderer->startForm($this);
1691 foreach (array_keys($this->_elements) as $key) {
1692 $element =& $this->_elements[$key];
1693 $elementName = $element->getName();
1694 $required = ($this->isElementRequired($elementName) && !$element->isFrozen());
1695 $error = $this->getElementError($elementName);
1696 $element->accept($renderer, $required, $error);
1698 $renderer->finishForm($this);
1699 } // end func accept
1702 // {{{ defaultRenderer()
1705 * Returns a reference to default renderer object
1709 * @return object a default renderer object
1711 function &defaultRenderer()
1713 if (!isset($GLOBALS['_HTML_QuickForm_default_renderer'])) {
1714 include_once('HTML/QuickForm/Renderer/Default.php');
1715 $GLOBALS['_HTML_QuickForm_default_renderer'] = new HTML_QuickForm_Renderer_Default();
1717 return $GLOBALS['_HTML_QuickForm_default_renderer'];
1718 } // end func defaultRenderer
1724 * Returns an HTML version of the form
1726 * @param string $in_data (optional) Any extra data to insert right
1727 * before form is rendered. Useful when using templates.
1729 * @return string Html version of the form
1733 function toHtml ($in_data = null)
1735 if (!is_null($in_data)) {
1736 $this->addElement('html', $in_data);
1738 $renderer =& $this->defaultRenderer();
1739 $this->accept($renderer);
1740 return $renderer->toHtml();
1741 } // end func toHtml
1744 // {{{ getValidationScript()
1747 * Returns the client side validation script
1751 * @return string Javascript to perform validation, empty string if no 'client' rules were added
1753 function getValidationScript()
1755 if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) {
1759 include_once('HTML/QuickForm/RuleRegistry.php');
1760 $registry =& HTML_QuickForm_RuleRegistry::singleton();
1771 foreach ($this->_rules as $elementName => $rules) {
1772 foreach ($rules as $rule) {
1773 if ('client' == $rule['validation']) {
1776 $dependent = isset($rule['dependent']) && is_array($rule['dependent']);
1777 $rule['message'] = strtr($rule['message'], $js_escape);
1779 if (isset($rule['group'])) {
1780 $group =& $this->getElement($rule['group']);
1781 // No JavaScript validation for frozen elements
1782 if ($group->isFrozen()) {
1785 $elements =& $group->getElements();
1786 foreach (array_keys($elements) as $key) {
1787 if ($elementName == $group->getElementName($key)) {
1788 $element =& $elements[$key];
1792 } elseif ($dependent) {
1794 $element[] =& $this->getElement($elementName);
1795 foreach ($rule['dependent'] as $elName) {
1796 $element[] =& $this->getElement($elName);
1799 $element =& $this->getElement($elementName);
1801 // No JavaScript validation for frozen elements
1802 if (is_object($element) && $element->isFrozen()) {
1804 } elseif (is_array($element)) {
1805 foreach (array_keys($element) as $key) {
1806 if ($element[$key]->isFrozen()) {
1812 $test[] = $registry->getValidationScript($element, $elementName, $rule);
1816 if (count($test) > 0) {
1818 "\n<script type=\"text/javascript\">\n" .
1820 "function validate_" . $this->_attributes['id'] . "(frm) {\n" .
1821 " var value = '';\n" .
1822 " var errFlag = new Array();\n" .
1823 " var _qfGroups = {};\n" .
1824 " _qfMsg = '';\n\n" .
1826 "\n if (_qfMsg != '') {\n" .
1827 " _qfMsg = '" . strtr($this->_jsPrefix, $js_escape) . "' + _qfMsg;\n" .
1828 " _qfMsg = _qfMsg + '\\n" . strtr($this->_jsPostfix, $js_escape) . "';\n" .
1829 " alert(_qfMsg);\n" .
1830 " return false;\n" .
1838 } // end func getValidationScript
1841 // {{{ getSubmitValues()
1844 * Returns the values submitted by the form
1848 * @param bool Whether uploaded files should be returned too
1851 function getSubmitValues($mergeFiles = false)
1853 return $mergeFiles? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles): $this->_submitValues;
1854 } // end func getSubmitValues
1860 * Returns the form's contents in an array.
1862 * The description of the array structure is in HTML_QuickForm_Renderer_Array docs
1866 * @param bool Whether to collect hidden elements (passed to the Renderer's constructor)
1867 * @return array of form contents
1869 function toArray($collectHidden = false)
1871 include_once 'HTML/QuickForm/Renderer/Array.php';
1872 $renderer = new HTML_QuickForm_Renderer_Array($collectHidden);
1873 $this->accept($renderer);
1874 return $renderer->toArray();
1875 } // end func toArray
1878 // {{{ exportValue()
1881 * Returns a 'safe' element's value
1883 * This method first tries to find a cleaned-up submitted value,
1884 * it will return a value set by setValue()/setDefaults()/setConstants()
1885 * if submitted value does not exist for the given element.
1887 * @param string Name of an element
1890 * @throws HTML_QuickForm_Error
1892 function exportValue($element)
1894 if (!isset($this->_elementIndex[$element])) {
1895 return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
1897 $value = $this->_elements[$this->_elementIndex[$element]]->exportValue($this->_submitValues, false);
1898 if (isset($this->_duplicateIndex[$element])) {
1899 foreach ($this->_duplicateIndex[$element] as $index) {
1900 if (null !== ($v = $this->_elements[$index]->exportValue($this->_submitValues, false))) {
1901 if (is_array($value)) {
1904 $value = (null === $value)? $v: array($value, $v);
1913 // {{{ exportValues()
1916 * Returns 'safe' elements' values
1918 * Unlike getSubmitValues(), this will return only the values
1919 * corresponding to the elements present in the form.
1921 * @param mixed Array/string of element names, whose values we want. If not set then return all elements.
1923 * @return array An assoc array of elements' values
1924 * @throws HTML_QuickForm_Error
1926 function exportValues($elementList = null)
1929 if (null === $elementList) {
1930 // iterate over all elements, calling their exportValue() methods
1931 foreach (array_keys($this->_elements) as $key) {
1932 $value = $this->_elements[$key]->exportValue($this->_submitValues, true);
1933 if (is_array($value)) {
1934 // This shit throws a bogus warning in PHP 4.3.x
1935 $values = HTML_QuickForm::arrayMerge($values, $value);
1939 if (!is_array($elementList)) {
1940 $elementList = array_map('trim', explode(',', $elementList));
1942 foreach ($elementList as $elementName) {
1943 $value = $this->exportValue($elementName);
1944 if (PEAR::isError($value)) {
1947 $values[$elementName] = $value;
1954 // {{{ isSubmitted()
1957 * Tells whether the form was already submitted
1959 * This is useful since the _submitFiles and _submitValues arrays
1960 * may be completely empty after the trackSubmit value is removed.
1965 function isSubmitted()
1967 return $this->_flagSubmitted;
1975 * Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error)
1978 * @param mixed result code
1979 * @return bool whether $value is an error
1982 function isError($value)
1984 return (is_object($value) && is_a($value, 'html_quickform_error'));
1985 } // end func isError
1988 // {{{ errorMessage()
1991 * Return a textual error message for an QuickForm error code
1994 * @param int error code
1995 * @return string error message
1998 function errorMessage($value)
2000 // make the variable static so that it only has to do the defining on the first call
2001 static $errorMessages;
2003 // define the varies error messages
2004 if (!isset($errorMessages)) {
2005 $errorMessages = array(
2006 QUICKFORM_OK => 'no error',
2007 QUICKFORM_ERROR => 'unknown error',
2008 QUICKFORM_INVALID_RULE => 'the rule does not exist as a registered rule',
2009 QUICKFORM_NONEXIST_ELEMENT => 'nonexistent html element',
2010 QUICKFORM_INVALID_FILTER => 'invalid filter',
2011 QUICKFORM_UNREGISTERED_ELEMENT => 'unregistered element',
2012 QUICKFORM_INVALID_ELEMENT_NAME => 'element already exists',
2013 QUICKFORM_INVALID_PROCESS => 'process callback does not exist',
2014 QUICKFORM_DEPRECATED => 'method is deprecated',
2015 QUICKFORM_INVALID_DATASOURCE => 'datasource is not an object'
2019 // If this is an error object, then grab the corresponding error code
2020 if (HTML_QuickForm::isError($value)) {
2021 $value = $value->getCode();
2024 // return the textual error message corresponding to the code
2025 return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR];
2026 } // end func errorMessage
2029 } // end class HTML_QuickForm
2032 * Class for errors thrown by HTML_QuickForm package
2035 * @package HTML_QuickForm
2036 * @author Adam Daniel <adaniel1@eesus.jnj.com>
2037 * @author Bertrand Mansion <bmansion@mamasam.com>
2038 * @version Release: 3.2.16
2040 class HTML_QuickForm_Error extends PEAR_Error {
2045 * Prefix for all error messages
2048 var $error_message_prefix = 'QuickForm Error: ';
2054 * Creates a quickform error object, extending the PEAR_Error class
2056 * @param int $code the error code
2057 * @param int $mode the reaction to the error, either return, die or trigger/callback
2058 * @param int $level intensity of the error (PHP error code)
2059 * @param mixed $debuginfo any information that can inform user as to nature of the error
2061 function HTML_QuickForm_Error($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN,
2062 $level = E_USER_NOTICE, $debuginfo = null)
2064 if (is_int($code)) {
2065 $this->PEAR_Error(HTML_QuickForm::errorMessage($code), $code, $mode, $level, $debuginfo);
2067 $this->PEAR_Error("Invalid error code: $code", QUICKFORM_ERROR, $mode, $level, $debuginfo);
2072 } // end class HTML_QuickForm_Error