2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
3 // +----------------------------------------------------------------------+
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 // +----------------------------------------------------------------------+
22 require_once('PEAR.php');
23 require_once('HTML/Common.php');
25 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] =
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 'xbutton' =>array('HTML/QuickForm/xbutton.php','HTML_QuickForm_xbutton')
52 $GLOBALS['_HTML_QuickForm_registered_rules'] = array(
53 'required' => array('html_quickform_rule_required', 'HTML/QuickForm/Rule/Required.php'),
54 'maxlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
55 'minlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
56 'rangelength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
57 'email' => array('html_quickform_rule_email', 'HTML/QuickForm/Rule/Email.php'),
58 'regex' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
59 'lettersonly' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
60 'alphanumeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
61 'numeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
62 'nopunctuation' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
63 'nonzero' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
64 'callback' => array('html_quickform_rule_callback', 'HTML/QuickForm/Rule/Callback.php'),
65 'compare' => array('html_quickform_rule_compare', 'HTML/QuickForm/Rule/Compare.php')
71 * Error codes for the QuickForm interface, which will be mapped to textual messages
72 * in the QuickForm::errorMessage() function. If you are to add a new error code, be
73 * sure to add the textual messages to the QuickForm::errorMessage() function as well
76 define('QUICKFORM_OK', 1);
77 define('QUICKFORM_ERROR', -1);
78 define('QUICKFORM_INVALID_RULE', -2);
79 define('QUICKFORM_NONEXIST_ELEMENT', -3);
80 define('QUICKFORM_INVALID_FILTER', -4);
81 define('QUICKFORM_UNREGISTERED_ELEMENT', -5);
82 define('QUICKFORM_INVALID_ELEMENT_NAME', -6);
83 define('QUICKFORM_INVALID_PROCESS', -7);
84 define('QUICKFORM_DEPRECATED', -8);
85 define('QUICKFORM_INVALID_DATASOURCE', -9);
90 * Create, validate and process HTML forms
92 * @author Adam Daniel <adaniel1@eesus.jnj.com>
93 * @author Bertrand Mansion <bmansion@mamasam.com>
97 class HTML_QuickForm
extends HTML_Common
{
101 * Array containing the form fields
106 var $_elements = array();
109 * Array containing element name to index map
114 var $_elementIndex = array();
117 * Array containing indexes of duplicate elements
122 var $_duplicateIndex = array();
125 * Array containing required field IDs
130 var $_required = array();
133 * Prefix message in javascript alert if error
138 var $_jsPrefix = 'Invalid information entered.';
141 * Postfix message in javascript alert if error
146 var $_jsPostfix = 'Please correct these fields.';
149 * Datasource object implementing the informal
150 * datasource protocol
158 * Array of default form values
163 var $_defaultValues = array();
166 * Array of constant form values
171 var $_constantValues = array();
174 * Array of submitted form values
179 var $_submitValues = array();
182 * Array of submitted form files
187 var $_submitFiles = array();
190 * Value for maxfilesize hidden element if form contains file input
195 var $_maxFileSize = 1048576; // 1 Mb = 1048576
198 * Flag to know if all fields are frozen
203 var $_freezeAll = false;
206 * Array containing the form rules
211 var $_rules = array();
214 * Form rules, global variety
218 var $_formRules = array();
221 * Array containing the validation errors
226 var $_errors = array();
229 * Note for required fields in the form
234 var $_requiredNote = '<span style="font-size:80%; color:#ff0000;">*</span><span style="font-size:80%;"> denotes required field</span>';
237 * Whether the form was submitted
241 var $_flagSubmitted = false;
248 * @param string $formName Form's name.
249 * @param string $method (optional)Form's method defaults to 'POST'
250 * @param string $action (optional)Form's action
251 * @param string $target (optional)Form's target defaults to '_self'
252 * @param mixed $attributes (optional)Extra attributes for <form> tag
253 * @param bool $trackSubmit (optional)Whether to track if the form was submitted by adding a special hidden field
256 function HTML_QuickForm($formName='', $method='post', $action='', $target='', $attributes=null, $trackSubmit = false)
258 HTML_Common
::HTML_Common($attributes);
259 $method = (strtoupper($method) == 'GET') ?
'get' : 'post';
260 $action = ($action == '') ?
$_SERVER['PHP_SELF'] : $action;
261 $target = empty($target) ?
array() : array('target' => $target);
262 $attributes = array('action'=>$action, 'method'=>$method, 'name'=>$formName, 'id'=>$formName) +
$target;
263 $this->updateAttributes($attributes);
264 if (!$trackSubmit ||
isset($_REQUEST['_qf__' . $formName])) {
265 if (1 == get_magic_quotes_gpc()) {
266 $this->_submitValues
= $this->_recursiveFilter('stripslashes', 'get' == $method?
$_GET: $_POST);
267 foreach ($_FILES as $keyFirst => $valFirst) {
268 foreach ($valFirst as $keySecond => $valSecond) {
269 if ('name' == $keySecond) {
270 $this->_submitFiles
[$keyFirst][$keySecond] = $this->_recursiveFilter('stripslashes', $valSecond);
272 $this->_submitFiles
[$keyFirst][$keySecond] = $valSecond;
277 $this->_submitValues
= 'get' == $method?
$_GET: $_POST;
278 $this->_submitFiles
= $_FILES;
280 $this->_flagSubmitted
= count($this->_submitValues
) > 0 ||
count($this->_submitFiles
) > 0;
283 unset($this->_submitValues
['_qf__' . $formName]);
284 $this->addElement('hidden', '_qf__' . $formName, null);
286 if (preg_match('/^([0-9]+)([a-zA-Z]*)$/', ini_get('upload_max_filesize'), $matches)) {
287 // see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
288 switch (strtoupper($matches['2'])) {
290 $this->_maxFileSize
= $matches['1'] * 1073741824;
293 $this->_maxFileSize
= $matches['1'] * 1048576;
296 $this->_maxFileSize
= $matches['1'] * 1024;
299 $this->_maxFileSize
= $matches['1'];
308 * Returns the current API version
314 function apiVersion()
317 } // end func apiVersion
320 // {{{ registerElementType()
323 * Registers a new element type
325 * @param string $typeName Name of element type
326 * @param string $include Include path for element type
327 * @param string $className Element class name
332 function registerElementType($typeName, $include, $className)
334 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($typeName)] = array($include, $className);
335 } // end func registerElementType
338 // {{{ registerRule()
341 * Registers a new validation rule
343 * @param string $ruleName Name of validation rule
344 * @param string $type Either: 'regex', 'function' or 'rule' for an HTML_QuickForm_Rule object
345 * @param string $data1 Name of function, regular expression or HTML_QuickForm_Rule classname
346 * @param string $data2 Object parent of above function or HTML_QuickForm_Rule file path
351 function registerRule($ruleName, $type, $data1, $data2 = null)
353 include_once('HTML/QuickForm/RuleRegistry.php');
354 $registry =& HTML_QuickForm_RuleRegistry
::singleton();
355 $registry->registerRule($ruleName, $type, $data1, $data2);
356 } // end func registerRule
359 // {{{ elementExists()
362 * Returns true if element is in the form
364 * @param string $element form name of element to check
369 function elementExists($element=null)
371 return isset($this->_elementIndex
[$element]);
372 } // end func elementExists
375 // {{{ setDatasource()
378 * Sets a datasource object for this form object
380 * Datasource default and constant values will feed the QuickForm object if
381 * the datasource implements defaultValues() and constantValues() methods.
383 * @param object $datasource datasource object implementing the informal datasource protocol
384 * @param mixed $defaultsFilter string or array of filter(s) to apply to default values
385 * @param mixed $constantsFilter string or array of filter(s) to apply to constants values
390 function setDatasource(&$datasource, $defaultsFilter = null, $constantsFilter = null)
392 if (is_object($datasource)) {
393 $this->_datasource
=& $datasource;
394 if (is_callable(array($datasource, 'defaultValues'))) {
395 $this->setDefaults($datasource->defaultValues($this), $defaultsFilter);
397 if (is_callable(array($datasource, 'constantValues'))) {
398 $this->setConstants($datasource->constantValues($this), $constantsFilter);
401 return PEAR
::raiseError(null, QUICKFORM_INVALID_DATASOURCE
, null, E_USER_WARNING
, "Datasource is not an object in QuickForm::setDatasource()", 'HTML_QuickForm_Error', true);
403 } // end func setDatasource
409 * Initializes default form values
411 * @param array $defaultValues values used to fill the form
412 * @param mixed $filter (optional) filter(s) to apply to all default values
417 function setDefaults($defaultValues = null, $filter = null)
419 if (is_array($defaultValues)) {
420 if (isset($filter)) {
421 if (is_array($filter) && (2 != count($filter) ||
!is_callable($filter))) {
422 foreach ($filter as $val) {
423 if (!is_callable($val)) {
424 return PEAR
::raiseError(null, QUICKFORM_INVALID_FILTER
, null, E_USER_WARNING
, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
426 $defaultValues = $this->_recursiveFilter($val, $defaultValues);
429 } elseif (!is_callable($filter)) {
430 return PEAR
::raiseError(null, QUICKFORM_INVALID_FILTER
, null, E_USER_WARNING
, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
432 $defaultValues = $this->_recursiveFilter($filter, $defaultValues);
435 $this->_defaultValues
= HTML_QuickForm
::arrayMerge($this->_defaultValues
, $defaultValues);
436 foreach (array_keys($this->_elements
) as $key) {
437 $this->_elements
[$key]->onQuickFormEvent('updateValue', null, $this);
440 } // end func setDefaults
443 // {{{ setConstants()
446 * Initializes constant form values.
447 * These values won't get overridden by POST or GET vars
449 * @param array $constantValues values used to fill the form
450 * @param mixed $filter (optional) filter(s) to apply to all default values
456 function setConstants($constantValues = null, $filter = null)
458 if (is_array($constantValues)) {
459 if (isset($filter)) {
460 if (is_array($filter) && (2 != count($filter) ||
!is_callable($filter))) {
461 foreach ($filter as $val) {
462 if (!is_callable($val)) {
463 return PEAR
::raiseError(null, QUICKFORM_INVALID_FILTER
, null, E_USER_WARNING
, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
465 $constantValues = $this->_recursiveFilter($val, $constantValues);
468 } elseif (!is_callable($filter)) {
469 return PEAR
::raiseError(null, QUICKFORM_INVALID_FILTER
, null, E_USER_WARNING
, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
471 $constantValues = $this->_recursiveFilter($filter, $constantValues);
474 $this->_constantValues
= HTML_QuickForm
::arrayMerge($this->_constantValues
, $constantValues);
475 foreach (array_keys($this->_elements
) as $key) {
476 $this->_elements
[$key]->onQuickFormEvent('updateValue', null, $this);
479 } // end func setConstants
482 // {{{ setMaxFileSize()
485 * Sets the value of MAX_FILE_SIZE hidden element
487 * @param int $bytes Size in bytes
492 function setMaxFileSize($bytes = 0)
495 $this->_maxFileSize
= $bytes;
497 if (!$this->elementExists('MAX_FILE_SIZE')) {
498 $this->addElement('hidden', 'MAX_FILE_SIZE', $this->_maxFileSize
);
500 $el =& $this->getElement('MAX_FILE_SIZE');
501 $el->updateAttributes(array('value' => $this->_maxFileSize
));
503 } // end func setMaxFileSize
506 // {{{ getMaxFileSize()
509 * Returns the value of MAX_FILE_SIZE hidden element
513 * @return int max file size in bytes
515 function getMaxFileSize()
517 return $this->_maxFileSize
;
518 } // end func getMaxFileSize
521 // {{{ &createElement()
524 * Creates a new form element of the given type.
526 * This method accepts variable number of parameters, their
527 * meaning and count depending on $elementType
529 * @param string $elementType type of element to add (text, textarea, file...)
532 * @return object extended class of HTML_element
533 * @throws HTML_QuickForm_Error
535 function &createElement($elementType)
537 $args = func_get_args();
538 $element =& HTML_QuickForm
::_loadElement('createElement', $elementType, array_slice($args, 1));
540 } // end func createElement
543 // {{{ _loadElement()
546 * Returns a form element of the given type
548 * @param string $event event to send to newly created element ('createElement' or 'addElement')
549 * @param string $type element type
550 * @param array $args arguments for event
553 * @return object a new element
554 * @throws HTML_QuickForm_Error
556 function &_loadElement($event, $type, $args)
558 $type = strtolower($type);
559 if (!HTML_QuickForm
::isTypeRegistered($type)) {
560 $error = PEAR
::raiseError(null, QUICKFORM_UNREGISTERED_ELEMENT
, null, E_USER_WARNING
, "Element '$type' does not exist in HTML_QuickForm::_loadElement()", 'HTML_QuickForm_Error', true);
563 $className = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][1];
564 $includeFile = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][0];
565 include_once($includeFile);
566 $elementObject =& new $className();
567 for ($i = 0; $i < 5; $i++
) {
568 if (!isset($args[$i])) {
572 $err = $elementObject->onQuickFormEvent($event, $args, $this);
576 return $elementObject;
577 } // end func _loadElement
583 * Adds an element into the form
585 * If $element is a string representing element type, then this
586 * method accepts variable number of parameters, their meaning
587 * and count depending on $element
589 * @param mixed $element element object or type of element to add (text, textarea, file...)
591 * @return object reference to element
593 * @throws HTML_QuickForm_Error
595 function &addElement($element)
597 if (is_object($element) && is_subclass_of($element, 'html_quickform_element')) {
598 $elementObject = &$element;
599 $elementObject->onQuickFormEvent('updateValue', null, $this);
601 $args = func_get_args();
602 $elementObject =& $this->_loadElement('addElement', $element, array_slice($args, 1));
603 if (PEAR
::isError($elementObject)) {
604 return $elementObject;
607 $elementName = $elementObject->getName();
609 // Add the element if it is not an incompatible duplicate
610 if (!empty($elementName) && isset($this->_elementIndex
[$elementName])) {
611 if ($this->_elements
[$this->_elementIndex
[$elementName]]->getType() ==
612 $elementObject->getType()) {
613 $this->_elements
[] =& $elementObject;
614 $elKeys = array_keys($this->_elements
);
615 $this->_duplicateIndex
[$elementName][] = end($elKeys);
617 $error = PEAR
::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME
, null, E_USER_WARNING
, "Element '$elementName' already exists in HTML_QuickForm::addElement()", 'HTML_QuickForm_Error', true);
621 $this->_elements
[] =& $elementObject;
622 $elKeys = array_keys($this->_elements
);
623 $this->_elementIndex
[$elementName] = end($elKeys);
625 if ($this->_freezeAll
) {
626 $elementObject->freeze();
629 return $elementObject;
630 } // end func addElement
633 // {{{ insertElementBefore()
636 * Inserts a new element right before the other element
638 * Warning: it is not possible to check whether the $element is already
639 * added to the form, therefore if you want to move the existing form
640 * element to a new position, you'll have to use removeElement():
641 * $form->insertElementBefore($form->removeElement('foo', false), 'bar');
645 * @param object HTML_QuickForm_element Element to insert
646 * @param string Name of the element before which the new one is inserted
647 * @return object HTML_QuickForm_element reference to inserted element
648 * @throws HTML_QuickForm_Error
650 function &insertElementBefore(&$element, $nameAfter)
652 if (!empty($this->_duplicateIndex
[$nameAfter])) {
653 $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);
655 } elseif (!$this->elementExists($nameAfter)) {
656 $error = PEAR
::raiseError(null, QUICKFORM_NONEXIST_ELEMENT
, null, E_USER_WARNING
, "Element '$nameAfter' does not exist in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true);
659 $elementName = $element->getName();
660 $targetIdx = $this->_elementIndex
[$nameAfter];
662 // Like in addElement(), check that it's not an incompatible duplicate
663 if (!empty($elementName) && isset($this->_elementIndex
[$elementName])) {
664 if ($this->_elements
[$this->_elementIndex
[$elementName]]->getType() != $element->getType()) {
665 $error = PEAR
::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME
, null, E_USER_WARNING
, "Element '$elementName' already exists in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true);
670 // Move all the elements after added back one place, reindex _elementIndex and/or _duplicateIndex
671 $elKeys = array_keys($this->_elements
);
672 for ($i = end($elKeys); $i >= $targetIdx; $i--) {
673 if (isset($this->_elements
[$i])) {
674 $currentName = $this->_elements
[$i]->getName();
675 $this->_elements
[$i +
1] =& $this->_elements
[$i];
676 if ($this->_elementIndex
[$currentName] == $i) {
677 $this->_elementIndex
[$currentName] = $i +
1;
679 $dupIdx = array_search($i, $this->_duplicateIndex
[$currentName]);
680 $this->_duplicateIndex
[$currentName][$dupIdx] = $i +
1;
682 unset($this->_elements
[$i]);
685 // Put the element in place finally
686 $this->_elements
[$targetIdx] =& $element;
688 $this->_elementIndex
[$elementName] = $targetIdx;
690 $this->_duplicateIndex
[$elementName][] = $targetIdx;
692 $element->onQuickFormEvent('updateValue', null, $this);
693 if ($this->_freezeAll
) {
696 // If not done, the elements will appear in reverse order
697 ksort($this->_elements
);
705 * Adds an element group
706 * @param array $elements array of elements composing the group
707 * @param string $name (optional)group name
708 * @param string $groupLabel (optional)group label
709 * @param string $separator (optional)string to separate elements
710 * @param string $appendName (optional)specify whether the group name should be
711 * used in the form element name ex: group[element]
712 * @return object reference to added group of elements
717 function &addGroup($elements, $name=null, $groupLabel='', $separator=null, $appendName = true)
719 static $anonGroups = 1;
721 if (0 == strlen($name)) {
722 $name = 'qf_group_' . $anonGroups++
;
725 $group =& $this->addElement('group', $name, $groupLabel, $elements, $separator, $appendName);
727 } // end func addGroup
733 * Returns a reference to the element
735 * @param string $element Element name
738 * @return object reference to element
739 * @throws HTML_QuickForm_Error
741 function &getElement($element)
743 if (isset($this->_elementIndex
[$element])) {
744 return $this->_elements
[$this->_elementIndex
[$element]];
746 $error = PEAR
::raiseError(null, QUICKFORM_NONEXIST_ELEMENT
, null, E_USER_WARNING
, "Element '$element' does not exist in HTML_QuickForm::getElement()", 'HTML_QuickForm_Error', true);
749 } // end func getElement
752 // {{{ &getElementValue()
755 * Returns the element's raw value
757 * This returns the value as submitted by the form (not filtered)
758 * or set via setDefaults() or setConstants()
760 * @param string $element Element name
763 * @return mixed element value
764 * @throws HTML_QuickForm_Error
766 function &getElementValue($element)
768 if (!isset($this->_elementIndex
[$element])) {
769 $error = PEAR
::raiseError(null, QUICKFORM_NONEXIST_ELEMENT
, null, E_USER_WARNING
, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
772 $value = $this->_elements
[$this->_elementIndex
[$element]]->getValue();
773 if (isset($this->_duplicateIndex
[$element])) {
774 foreach ($this->_duplicateIndex
[$element] as $index) {
775 if (null !== ($v = $this->_elements
[$index]->getValue())) {
776 if (is_array($value)) {
779 $value = (null === $value)?
$v: array($value, $v);
785 } // end func getElementValue
788 // {{{ getSubmitValue()
791 * Returns the elements value after submit and filter
793 * @param string Element name
796 * @return mixed submitted element value or null if not set
798 function getSubmitValue($elementName)
801 if (isset($this->_submitValues
[$elementName]) ||
isset($this->_submitFiles
[$elementName])) {
802 $value = isset($this->_submitValues
[$elementName])?
$this->_submitValues
[$elementName]: array();
803 if (is_array($value) && isset($this->_submitFiles
[$elementName])) {
804 foreach ($this->_submitFiles
[$elementName] as $k => $v) {
805 $value = HTML_QuickForm
::arrayMerge($value, $this->_reindexFiles($this->_submitFiles
[$elementName][$k], $k));
809 } elseif ('file' == $this->getElementType($elementName)) {
810 return $this->getElementValue($elementName);
812 } elseif (false !== ($pos = strpos($elementName, '['))) {
813 $base = substr($elementName, 0, $pos);
814 $idx = "['" . str_replace(array(']', '['), array('', "']['"), substr($elementName, $pos +
1, -1)) . "']";
815 if (isset($this->_submitValues
[$base])) {
816 $value = eval("return (isset(\$this->_submitValues['{$base}']{$idx})) ? \$this->_submitValues['{$base}']{$idx} : null;");
819 if ((is_array($value) ||
null === $value) && isset($this->_submitFiles
[$base])) {
820 $props = array('name', 'type', 'size', 'tmp_name', 'error');
821 $code = "if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" .
825 foreach ($props as $prop) {
826 $code .= " \$v = HTML_QuickForm::arrayMerge(\$v, \$this->_reindexFiles(\$this->_submitFiles['{$base}']['{$prop}']{$idx}, '{$prop}'));\n";
828 $fileValue = eval($code . " return \$v;\n}\n");
829 if (null !== $fileValue) {
830 $value = null === $value?
$fileValue: HTML_QuickForm
::arrayMerge($value, $fileValue);
835 // This is only supposed to work for groups with appendName = false
836 if (null === $value && 'group' == $this->getElementType($elementName)) {
837 $group =& $this->getElement($elementName);
838 $elements =& $group->getElements();
839 foreach (array_keys($elements) as $key) {
840 $name = $group->getElementName($key);
841 // prevent endless recursion in case of radios and such
842 if ($name != $elementName) {
843 if (null !== ($v = $this->getSubmitValue($name))) {
850 } // end func getSubmitValue
853 // {{{ _reindexFiles()
856 * A helper function to change the indexes in $_FILES array
858 * @param mixed Some value from the $_FILES array
859 * @param string The key from the $_FILES array that should be appended
862 function _reindexFiles($value, $key)
864 if (!is_array($value)) {
865 return array($key => $value);
868 foreach ($value as $k => $v) {
869 $ret[$k] = $this->_reindexFiles($v, $key);
876 // {{{ getElementError()
879 * Returns error corresponding to validated element
881 * @param string $element Name of form element to check
884 * @return string error message corresponding to checked element
886 function getElementError($element)
888 if (isset($this->_errors
[$element])) {
889 return $this->_errors
[$element];
891 } // end func getElementError
894 // {{{ setElementError()
897 * Set error message for a form element
899 * @param string $element Name of form element to set error for
900 * @param string $message Error message, if empty then removes the current error message
905 function setElementError($element, $message = null)
907 if (!empty($message)) {
908 $this->_errors
[$element] = $message;
910 unset($this->_errors
[$element]);
912 } // end func setElementError
915 // {{{ getElementType()
918 * Returns the type of the given element
920 * @param string $element Name of form element
923 * @return string Type of the element, false if the element is not found
925 function getElementType($element)
927 if (isset($this->_elementIndex
[$element])) {
928 return $this->_elements
[$this->_elementIndex
[$element]]->getType();
931 } // end func getElementType
934 // {{{ updateElementAttr()
937 * Updates Attributes for one or more elements
939 * @param mixed $elements Array of element names/objects or string of elements to be updated
940 * @param mixed $attrs Array or sting of html attributes
945 function updateElementAttr($elements, $attrs)
947 if (is_string($elements)) {
948 $elements = split('[ ]?,[ ]?', $elements);
950 foreach (array_keys($elements) as $key) {
951 if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) {
952 $elements[$key]->updateAttributes($attrs);
953 } elseif (isset($this->_elementIndex
[$elements[$key]])) {
954 $this->_elements
[$this->_elementIndex
[$elements[$key]]]->updateAttributes($attrs);
955 if (isset($this->_duplicateIndex
[$elements[$key]])) {
956 foreach ($this->_duplicateIndex
[$elements[$key]] as $index) {
957 $this->_elements
[$index]->updateAttributes($attrs);
962 } // end func updateElementAttr
965 // {{{ removeElement()
970 * The method "unlinks" an element from the form, returning the reference
971 * to the element object. If several elements named $elementName exist,
972 * it removes the first one, leaving the others intact.
974 * @param string $elementName The element name
975 * @param boolean $removeRules True if rules for this element are to be removed too
978 * @return object HTML_QuickForm_element a reference to the removed element
979 * @throws HTML_QuickForm_Error
981 function &removeElement($elementName, $removeRules = true)
983 if (!isset($this->_elementIndex
[$elementName])) {
984 $error = PEAR
::raiseError(null, QUICKFORM_NONEXIST_ELEMENT
, null, E_USER_WARNING
, "Element '$elementName' does not exist in HTML_QuickForm::removeElement()", 'HTML_QuickForm_Error', true);
987 $el =& $this->_elements
[$this->_elementIndex
[$elementName]];
988 unset($this->_elements
[$this->_elementIndex
[$elementName]]);
989 if (empty($this->_duplicateIndex
[$elementName])) {
990 unset($this->_elementIndex
[$elementName]);
992 $this->_elementIndex
[$elementName] = array_shift($this->_duplicateIndex
[$elementName]);
995 unset($this->_rules
[$elementName], $this->_errors
[$elementName]);
998 } // end func removeElement
1004 * Adds a validation rule for the given field
1006 * If the element is in fact a group, it will be considered as a whole.
1007 * To validate grouped elements as separated entities,
1008 * use addGroupRule instead of addRule.
1010 * @param string $element Form element name
1011 * @param string $message Message to display for invalid data
1012 * @param string $type Rule type, use getRegisteredRules() to get types
1013 * @param string $format (optional)Required for extra rule data
1014 * @param string $validation (optional)Where to perform validation: "server", "client"
1015 * @param boolean $reset Client-side validation: reset the form element to its original value if there is an error?
1016 * @param boolean $force Force the rule to be applied, even if the target form element does not exist
1019 * @throws HTML_QuickForm_Error
1021 function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false)
1024 if (!is_array($element) && !$this->elementExists($element)) {
1025 return PEAR
::raiseError(null, QUICKFORM_NONEXIST_ELEMENT
, null, E_USER_WARNING
, "Element '$element' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
1026 } elseif (is_array($element)) {
1027 foreach ($element as $el) {
1028 if (!$this->elementExists($el)) {
1029 return PEAR
::raiseError(null, QUICKFORM_NONEXIST_ELEMENT
, null, E_USER_WARNING
, "Element '$el' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
1034 if (false === ($newName = $this->isRuleRegistered($type, true))) {
1035 return PEAR
::raiseError(null, QUICKFORM_INVALID_RULE
, null, E_USER_WARNING
, "Rule '$type' is not registered in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
1036 } elseif (is_string($newName)) {
1039 if (is_array($element)) {
1040 $dependent = $element;
1041 $element = array_shift($dependent);
1045 if ($type == 'required' ||
$type == 'uploadedfile') {
1046 $this->_required
[] = $element;
1048 if (!isset($this->_rules
[$element])) {
1049 $this->_rules
[$element] = array();
1051 if ($validation == 'client') {
1052 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes
['id'] . '; } catch(e) { return true; } return myValidator(this);'));
1054 $this->_rules
[$element][] = array(
1056 'format' => $format,
1057 'message' => $message,
1058 'validation' => $validation,
1060 'dependent' => $dependent
1062 } // end func addRule
1065 // {{{ addGroupRule()
1068 * Adds a validation rule for the given group of elements
1070 * Only groups with a name can be assigned a validation rule
1071 * Use addGroupRule when you need to validate elements inside the group.
1072 * Use addRule if you need to validate the group as a whole. In this case,
1073 * the same rule will be applied to all elements in the group.
1074 * Use addRule if you need to validate the group against a function.
1076 * @param string $group Form group name
1077 * @param mixed $arg1 Array for multiple elements or error message string for one element
1078 * @param string $type (optional)Rule type use getRegisteredRules() to get types
1079 * @param string $format (optional)Required for extra rule data
1080 * @param int $howmany (optional)How many valid elements should be in the group
1081 * @param string $validation (optional)Where to perform validation: "server", "client"
1082 * @param bool $reset Client-side: whether to reset the element's value to its original state if validation failed.
1085 * @throws HTML_QuickForm_Error
1087 function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false)
1089 if (!$this->elementExists($group)) {
1090 return PEAR
::raiseError(null, QUICKFORM_NONEXIST_ELEMENT
, null, E_USER_WARNING
, "Group '$group' does not exist in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
1093 $groupObj =& $this->getElement($group);
1094 if (is_array($arg1)) {
1096 foreach ($arg1 as $elementIndex => $rules) {
1097 $elementName = $groupObj->getElementName($elementIndex);
1098 foreach ($rules as $rule) {
1099 $format = (isset($rule[2])) ?
$rule[2] : null;
1100 $validation = (isset($rule[3]) && 'client' == $rule[3])?
'client': 'server';
1101 $reset = isset($rule[4]) && $rule[4];
1103 if (false === ($newName = $this->isRuleRegistered($type, true))) {
1104 return PEAR
::raiseError(null, QUICKFORM_INVALID_RULE
, null, E_USER_WARNING
, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
1105 } elseif (is_string($newName)) {
1109 $this->_rules
[$elementName][] = array(
1111 'format' => $format,
1112 'message' => $rule[0],
1113 'validation' => $validation,
1117 if ('required' == $type ||
'uploadedfile' == $type) {
1118 $groupObj->_required
[] = $elementName;
1119 $this->_required
[] = $elementName;
1122 if ('client' == $validation) {
1123 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes
['id'] . '; } catch(e) { return true; } return myValidator(this);'));
1127 if ($required > 0 && count($groupObj->getElements()) == $required) {
1128 $this->_required
[] = $group;
1130 } elseif (is_string($arg1)) {
1131 if (false === ($newName = $this->isRuleRegistered($type, true))) {
1132 return PEAR
::raiseError(null, QUICKFORM_INVALID_RULE
, null, E_USER_WARNING
, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
1133 } elseif (is_string($newName)) {
1137 // addGroupRule() should also handle <select multiple>
1138 if (is_a($groupObj, 'html_quickform_group')) {
1139 // Radios need to be handled differently when required
1140 if ($type == 'required' && $groupObj->getGroupType() == 'radio') {
1141 $howmany = ($howmany == 0) ?
1 : $howmany;
1143 $howmany = ($howmany == 0) ?
count($groupObj->getElements()) : $howmany;
1147 $this->_rules
[$group][] = array('type' => $type,
1148 'format' => $format,
1150 'validation' => $validation,
1151 'howmany' => $howmany,
1153 if ($type == 'required') {
1154 $this->_required
[] = $group;
1156 if ($validation == 'client') {
1157 $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes
['id'] . '; } catch(e) { return true; } return myValidator(this);'));
1160 } // end func addGroupRule
1163 // {{{ addFormRule()
1166 * Adds a global validation rule
1168 * This should be used when for a rule involving several fields or if
1169 * you want to use some completely custom validation for your form.
1170 * The rule function/method should return true in case of successful
1171 * validation and array('element name' => 'error') when there were errors.
1174 * @param mixed Callback, either function name or array(&$object, 'method')
1175 * @throws HTML_QuickForm_Error
1177 function addFormRule($rule)
1179 if (!is_callable($rule)) {
1180 return PEAR
::raiseError(null, QUICKFORM_INVALID_RULE
, null, E_USER_WARNING
, 'Callback function does not exist in HTML_QuickForm::addFormRule()', 'HTML_QuickForm_Error', true);
1182 $this->_formRules
[] = $rule;
1186 // {{{ applyFilter()
1189 * Applies a data filter for the given field(s)
1191 * @param mixed $element Form element name or array of such names
1192 * @param mixed $filter Callback, either function name or array(&$object, 'method')
1196 function applyFilter($element, $filter)
1198 if (!is_callable($filter)) {
1199 return PEAR
::raiseError(null, QUICKFORM_INVALID_FILTER
, null, E_USER_WARNING
, "Callback function does not exist in QuickForm::applyFilter()", 'HTML_QuickForm_Error', true);
1201 if ($element == '__ALL__') {
1202 $this->_submitValues
= $this->_recursiveFilter($filter, $this->_submitValues
);
1204 if (!is_array($element)) {
1205 $element = array($element);
1207 foreach ($element as $elName) {
1208 $value = $this->getSubmitValue($elName);
1209 if (null !== $value) {
1210 if (false === strpos($elName, '[')) {
1211 $this->_submitValues
[$elName] = $this->_recursiveFilter($filter, $value);
1213 $idx = "['" . str_replace(array(']', '['), array('', "']['"), $elName) . "']";
1214 eval("\$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);");
1219 } // end func applyFilter
1222 // {{{ _recursiveFilter()
1225 * Recursively apply a filter function
1227 * @param string $filter filter to apply
1228 * @param mixed $value submitted values
1231 * @return cleaned values
1233 function _recursiveFilter($filter, $value)
1235 if (is_array($value)) {
1236 $cleanValues = array();
1237 foreach ($value as $k => $v) {
1238 $cleanValues[$k] = $this->_recursiveFilter($filter, $v);
1240 return $cleanValues;
1242 return call_user_func($filter, $value);
1244 } // end func _recursiveFilter
1252 * Merges two array like the PHP function array_merge but recursively.
1253 * The main difference is that existing keys will not be renumbered
1254 * if they are integers.
1257 * @param array $a original array
1258 * @param array $b array which will be merged into first one
1259 * @return array merged array
1261 function arrayMerge($a, $b)
1263 foreach ($b as $k => $v) {
1265 if (isset($a[$k]) && !is_array($a[$k])) {
1268 if (!isset($a[$k])) {
1271 $a[$k] = HTML_QuickForm
::arrayMerge($a[$k], $v);
1278 } // end func arrayMerge
1281 // {{{ isTypeRegistered()
1284 * Returns whether or not the form element type is supported
1286 * @param string $type Form element type
1291 function isTypeRegistered($type)
1293 return isset($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($type)]);
1294 } // end func isTypeRegistered
1297 // {{{ getRegisteredTypes()
1300 * Returns an array of registered element types
1306 function getRegisteredTypes()
1308 return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']);
1309 } // end func getRegisteredTypes
1312 // {{{ isRuleRegistered()
1315 * Returns whether or not the given rule is supported
1317 * @param string $name Validation rule name
1318 * @param bool Whether to automatically register subclasses of HTML_QuickForm_Rule
1321 * @return mixed true if previously registered, false if not, new rule name if auto-registering worked
1323 function isRuleRegistered($name, $autoRegister = false)
1325 if (is_scalar($name) && isset($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) {
1327 } elseif (!$autoRegister) {
1330 // automatically register the rule if requested
1331 include_once 'HTML/QuickForm/RuleRegistry.php';
1333 if (is_object($name) && is_a($name, 'html_quickform_rule')) {
1334 $ruleName = !empty($name->name
)?
$name->name
: strtolower(get_class($name));
1335 } elseif (is_string($name) && class_exists($name)) {
1336 $parent = strtolower($name);
1338 if ('html_quickform_rule' == strtolower($parent)) {
1339 $ruleName = strtolower($name);
1342 } while ($parent = get_parent_class($parent));
1345 $registry =& HTML_QuickForm_RuleRegistry
::singleton();
1346 $registry->registerRule($ruleName, null, $name);
1349 } // end func isRuleRegistered
1352 // {{{ getRegisteredRules()
1355 * Returns an array of registered validation rules
1361 function getRegisteredRules()
1363 return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']);
1364 } // end func getRegisteredRules
1367 // {{{ isElementRequired()
1370 * Returns whether or not the form element is required
1372 * @param string $element Form element name
1377 function isElementRequired($element)
1379 return in_array($element, $this->_required
, true);
1380 } // end func isElementRequired
1383 // {{{ isElementFrozen()
1386 * Returns whether or not the form element is frozen
1388 * @param string $element Form element name
1393 function isElementFrozen($element)
1395 if (isset($this->_elementIndex
[$element])) {
1396 return $this->_elements
[$this->_elementIndex
[$element]]->isFrozen();
1399 } // end func isElementFrozen
1402 // {{{ setJsWarnings()
1405 * Sets JavaScript warning messages
1407 * @param string $pref Prefix warning
1408 * @param string $post Postfix warning
1413 function setJsWarnings($pref, $post)
1415 $this->_jsPrefix
= $pref;
1416 $this->_jsPostfix
= $post;
1417 } // end func setJsWarnings
1420 // {{{ setRequiredNote()
1423 * Sets required-note
1425 * @param string $note Message indicating some elements are required
1430 function setRequiredNote($note)
1432 $this->_requiredNote
= $note;
1433 } // end func setRequiredNote
1436 // {{{ getRequiredNote()
1439 * Returns the required note
1445 function getRequiredNote()
1447 return $this->_requiredNote
;
1448 } // end func getRequiredNote
1454 * Performs the server side validation
1457 * @return boolean true if no error found
1461 if (count($this->_rules
) == 0 && count($this->_formRules
) == 0 &&
1462 $this->isSubmitted()) {
1463 return (0 == count($this->_errors
));
1464 } elseif (!$this->isSubmitted()) {
1468 include_once('HTML/QuickForm/RuleRegistry.php');
1469 $registry =& HTML_QuickForm_RuleRegistry
::singleton();
1471 foreach ($this->_rules
as $target => $rules) {
1472 $submitValue = $this->getSubmitValue($target);
1474 foreach ($rules as $rule) {
1475 if ((isset($rule['group']) && isset($this->_errors
[$rule['group']])) ||
1476 isset($this->_errors
[$target])) {
1479 // If element is not required and is empty, we shouldn't validate it
1480 if (!$this->isElementRequired($target)) {
1481 if (!isset($submitValue) ||
'' == $submitValue) {
1483 // Fix for bug #3501: we shouldn't validate not uploaded files, either.
1484 // Unfortunately, we can't just use $element->isUploadedFile() since
1485 // the element in question can be buried in group. Thus this hack.
1486 } elseif (is_array($submitValue)) {
1487 if (false === ($pos = strpos($target, '['))) {
1488 $isUpload = !empty($this->_submitFiles
[$target]);
1490 $base = substr($target, 0, $pos);
1491 $idx = "['" . str_replace(array(']', '['), array('', "']['"), substr($target, $pos +
1, -1)) . "']";
1492 eval("\$isUpload = isset(\$this->_submitFiles['{$base}']['name']{$idx});");
1494 if ($isUpload && (!isset($submitValue['error']) ||
0 != $submitValue['error'])) {
1499 if (isset($rule['dependent']) && is_array($rule['dependent'])) {
1500 $values = array($submitValue);
1501 foreach ($rule['dependent'] as $elName) {
1502 $values[] = $this->getSubmitValue($elName);
1504 $result = $registry->validate($rule['type'], $values, $rule['format'], true);
1505 } elseif (is_array($submitValue) && !isset($rule['howmany'])) {
1506 $result = $registry->validate($rule['type'], $submitValue, $rule['format'], true);
1508 $result = $registry->validate($rule['type'], $submitValue, $rule['format'], false);
1511 if (!$result ||
(!empty($rule['howmany']) && $rule['howmany'] > (int)$result)) {
1512 if (isset($rule['group'])) {
1513 $this->_errors
[$rule['group']] = $rule['message'];
1515 $this->_errors
[$target] = $rule['message'];
1521 // process the global rules now
1522 foreach ($this->_formRules
as $rule) {
1523 if (true !== ($res = call_user_func($rule, $this->_submitValues
, $this->_submitFiles
))) {
1524 if (is_array($res)) {
1525 $this->_errors +
= $res;
1527 return PEAR
::raiseError(null, QUICKFORM_ERROR
, null, E_USER_WARNING
, 'Form rule callback returned invalid value in HTML_QuickForm::validate()', 'HTML_QuickForm_Error', true);
1532 return (0 == count($this->_errors
));
1533 } // end func validate
1539 * Displays elements without HTML input tags
1541 * @param mixed $elementList array or string of element(s) to be frozen
1544 * @throws HTML_QuickForm_Error
1546 function freeze($elementList=null)
1548 if (!isset($elementList)) {
1549 $this->_freezeAll
= true;
1550 $elementList = array();
1552 if (!is_array($elementList)) {
1553 $elementList = preg_split('/[ ]*,[ ]*/', $elementList);
1555 $elementList = array_flip($elementList);
1558 foreach (array_keys($this->_elements
) as $key) {
1559 $name = $this->_elements
[$key]->getName();
1560 if ($this->_freezeAll ||
isset($elementList[$name])) {
1561 $this->_elements
[$key]->freeze();
1562 unset($elementList[$name]);
1566 if (!empty($elementList)) {
1567 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);
1570 } // end func freeze
1576 * Returns whether or not the whole form is frozen
1584 return $this->_freezeAll
;
1585 } // end func isFrozen
1591 * Performs the form data processing
1593 * @param mixed $callback Callback, either function name or array(&$object, 'method')
1594 * @param bool $mergeFiles Whether uploaded files should be processed too
1597 * @throws HTML_QuickForm_Error
1599 function process($callback, $mergeFiles = true)
1601 if (!is_callable($callback)) {
1602 return PEAR
::raiseError(null, QUICKFORM_INVALID_PROCESS
, null, E_USER_WARNING
, "Callback function does not exist in QuickForm::process()", 'HTML_QuickForm_Error', true);
1604 $values = ($mergeFiles === true) ? HTML_QuickForm
::arrayMerge($this->_submitValues
, $this->_submitFiles
) : $this->_submitValues
;
1605 return call_user_func($callback, $values);
1606 } // end func process
1612 * Accepts a renderer
1614 * @param object An HTML_QuickForm_Renderer object
1619 function accept(&$renderer)
1621 $renderer->startForm($this);
1622 foreach (array_keys($this->_elements
) as $key) {
1623 $element =& $this->_elements
[$key];
1624 $elementName = $element->getName();
1625 $required = ($this->isElementRequired($elementName) && !$element->isFrozen());
1626 $error = $this->getElementError($elementName);
1627 $element->accept($renderer, $required, $error);
1629 $renderer->finishForm($this);
1630 } // end func accept
1633 // {{{ defaultRenderer()
1636 * Returns a reference to default renderer object
1640 * @return object a default renderer object
1642 function &defaultRenderer()
1644 if (!isset($GLOBALS['_HTML_QuickForm_default_renderer'])) {
1645 include_once('HTML/QuickForm/Renderer/Default.php');
1646 $GLOBALS['_HTML_QuickForm_default_renderer'] =& new HTML_QuickForm_Renderer_Default();
1648 return $GLOBALS['_HTML_QuickForm_default_renderer'];
1649 } // end func defaultRenderer
1655 * Returns an HTML version of the form
1657 * @param string $in_data (optional) Any extra data to insert right
1658 * before form is rendered. Useful when using templates.
1660 * @return string Html version of the form
1664 function toHtml ($in_data = null)
1666 if (!is_null($in_data)) {
1667 $this->addElement('html', $in_data);
1669 $renderer =& $this->defaultRenderer();
1670 $this->accept($renderer);
1671 return $renderer->toHtml();
1672 } // end func toHtml
1675 // {{{ getValidationScript()
1678 * Returns the client side validation script
1682 * @return string Javascript to perform validation, empty string if no 'client' rules were added
1684 function getValidationScript()
1686 if (empty($this->_rules
) ||
empty($this->_attributes
['onsubmit'])) {
1690 include_once('HTML/QuickForm/RuleRegistry.php');
1691 $registry =& HTML_QuickForm_RuleRegistry
::singleton();
1702 foreach ($this->_rules
as $elementName => $rules) {
1703 foreach ($rules as $rule) {
1704 if ('client' == $rule['validation']) {
1707 $dependent = isset($rule['dependent']) && is_array($rule['dependent']);
1708 $rule['message'] = strtr($rule['message'], $js_escape);
1710 if (isset($rule['group'])) {
1711 $group =& $this->getElement($rule['group']);
1712 // No JavaScript validation for frozen elements
1713 if ($group->isFrozen()) {
1716 $elements =& $group->getElements();
1717 foreach (array_keys($elements) as $key) {
1718 if ($elementName == $group->getElementName($key)) {
1719 $element =& $elements[$key];
1723 } elseif ($dependent) {
1725 $element[] =& $this->getElement($elementName);
1726 foreach ($rule['dependent'] as $elName) {
1727 $element[] =& $this->getElement($elName);
1730 $element =& $this->getElement($elementName);
1732 // No JavaScript validation for frozen elements
1733 if (is_object($element) && $element->isFrozen()) {
1735 } elseif (is_array($element)) {
1736 foreach (array_keys($element) as $key) {
1737 if ($element[$key]->isFrozen()) {
1743 $test[] = $registry->getValidationScript($element, $elementName, $rule);
1747 if (count($test) > 0) {
1749 "\n<script type=\"text/javascript\">\n" .
1751 "function validate_" . $this->_attributes
['id'] . "(frm) {\n" .
1752 " var value = '';\n" .
1753 " var errFlag = new Array();\n" .
1754 " var _qfGroups = {};\n" .
1755 " _qfMsg = '';\n\n" .
1757 "\n if (_qfMsg != '') {\n" .
1758 " _qfMsg = '" . strtr($this->_jsPrefix
, $js_escape) . "' + _qfMsg;\n" .
1759 " _qfMsg = _qfMsg + '\\n" . strtr($this->_jsPostfix
, $js_escape) . "';\n" .
1760 " alert(_qfMsg);\n" .
1761 " return false;\n" .
1769 } // end func getValidationScript
1772 // {{{ getSubmitValues()
1775 * Returns the values submitted by the form
1779 * @param bool Whether uploaded files should be returned too
1782 function getSubmitValues($mergeFiles = false)
1784 return $mergeFiles? HTML_QuickForm
::arrayMerge($this->_submitValues
, $this->_submitFiles
): $this->_submitValues
;
1785 } // end func getSubmitValues
1791 * Returns the form's contents in an array.
1793 * The description of the array structure is in HTML_QuickForm_Renderer_Array docs
1797 * @param bool Whether to collect hidden elements (passed to the Renderer's constructor)
1798 * @return array of form contents
1800 function toArray($collectHidden = false)
1802 include_once 'HTML/QuickForm/Renderer/Array.php';
1803 $renderer =& new HTML_QuickForm_Renderer_Array($collectHidden);
1804 $this->accept($renderer);
1805 return $renderer->toArray();
1806 } // end func toArray
1809 // {{{ exportValue()
1812 * Returns a 'safe' element's value
1814 * This method first tries to find a cleaned-up submitted value,
1815 * it will return a value set by setValue()/setDefaults()/setConstants()
1816 * if submitted value does not exist for the given element.
1818 * @param string Name of an element
1822 function exportValue($element)
1824 if (!isset($this->_elementIndex
[$element])) {
1825 return PEAR
::raiseError(null, QUICKFORM_NONEXIST_ELEMENT
, null, E_USER_WARNING
, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
1827 $value = $this->_elements
[$this->_elementIndex
[$element]]->exportValue($this->_submitValues
, false);
1828 if (isset($this->_duplicateIndex
[$element])) {
1829 foreach ($this->_duplicateIndex
[$element] as $index) {
1830 if (null !== ($v = $this->_elements
[$index]->exportValue($this->_submitValues
, false))) {
1831 if (is_array($value)) {
1834 $value = (null === $value)?
$v: array($value, $v);
1843 // {{{ exportValues()
1846 * Returns 'safe' elements' values
1848 * Unlike getSubmitValues(), this will return only the values
1849 * corresponding to the elements present in the form.
1851 * @param mixed Array/string of element names, whose values we want. If not set then return all elements.
1853 * @return array An assoc array of elements' values
1854 * @throws HTML_QuickForm_Error
1856 function exportValues($elementList = null)
1859 if (null === $elementList) {
1860 // iterate over all elements, calling their exportValue() methods
1861 foreach (array_keys($this->_elements
) as $key) {
1862 $value = $this->_elements
[$key]->exportValue($this->_submitValues
, true);
1863 if (is_array($value)) {
1864 // This shit throws a bogus warning in PHP 4.3.x
1865 $values = HTML_QuickForm
::arrayMerge($values, $value);
1869 if (!is_array($elementList)) {
1870 $elementList = array_map('trim', explode(',', $elementList));
1872 foreach ($elementList as $elementName) {
1873 $value = $this->exportValue($elementName);
1874 if (PEAR
::isError($value)) {
1877 $values[$elementName] = $value;
1884 // {{{ isSubmitted()
1887 * Tells whether the form was already submitted
1889 * This is useful since the _submitFiles and _submitValues arrays
1890 * may be completely empty after the trackSubmit value is removed.
1895 function isSubmitted()
1897 return $this->_flagSubmitted
;
1905 * Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error)
1908 * @param mixed result code
1909 * @return bool whether $value is an error
1911 function isError($value)
1913 return (is_object($value) && is_a($value, 'html_quickform_error'));
1914 } // end func isError
1917 // {{{ errorMessage()
1920 * Return a textual error message for an QuickForm error code
1923 * @param int error code
1924 * @return string error message
1926 function errorMessage($value)
1928 // make the variable static so that it only has to do the defining on the first call
1929 static $errorMessages;
1931 // define the varies error messages
1932 if (!isset($errorMessages)) {
1933 $errorMessages = array(
1934 QUICKFORM_OK
=> 'no error',
1935 QUICKFORM_ERROR
=> 'unknown error',
1936 QUICKFORM_INVALID_RULE
=> 'the rule does not exist as a registered rule',
1937 QUICKFORM_NONEXIST_ELEMENT
=> 'nonexistent html element',
1938 QUICKFORM_INVALID_FILTER
=> 'invalid filter',
1939 QUICKFORM_UNREGISTERED_ELEMENT
=> 'unregistered element',
1940 QUICKFORM_INVALID_ELEMENT_NAME
=> 'element already exists',
1941 QUICKFORM_INVALID_PROCESS
=> 'process callback does not exist',
1942 QUICKFORM_DEPRECATED
=> 'method is deprecated',
1943 QUICKFORM_INVALID_DATASOURCE
=> 'datasource is not an object'
1947 // If this is an error object, then grab the corresponding error code
1948 if (HTML_QuickForm
::isError($value)) {
1949 $value = $value->getCode();
1952 // return the textual error message corresponding to the code
1953 return isset($errorMessages[$value]) ?
$errorMessages[$value] : $errorMessages[QUICKFORM_ERROR
];
1954 } // end func errorMessage
1957 } // end class HTML_QuickForm
1959 class HTML_QuickForm_Error
extends PEAR_Error
{
1964 * Prefix for all error messages
1967 var $error_message_prefix = 'QuickForm Error: ';
1973 * Creates a quickform error object, extending the PEAR_Error class
1975 * @param int $code the error code
1976 * @param int $mode the reaction to the error, either return, die or trigger/callback
1977 * @param int $level intensity of the error (PHP error code)
1978 * @param mixed $debuginfo any information that can inform user as to nature of the error
1980 function HTML_QuickForm_Error($code = QUICKFORM_ERROR
, $mode = PEAR_ERROR_RETURN
,
1981 $level = E_USER_NOTICE
, $debuginfo = null)
1983 if (is_int($code)) {
1984 $this->PEAR_Error(HTML_QuickForm
::errorMessage($code), $code, $mode, $level, $debuginfo);
1986 $this->PEAR_Error("Invalid error code: $code", QUICKFORM_ERROR
, $mode, $level, $debuginfo);
1991 } // end class HTML_QuickForm_Error