Source for file QuickForm.php
Documentation is available at QuickForm.php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Adam Daniel <adaniel1@eesus.jnj.com> |
// | Bertrand Mansion <bmansion@mamasam.com> |
// +----------------------------------------------------------------------+
// $Id: QuickForm.php,v 1.160 2005/08/04 21:15:23 avb Exp $
require_once('PEAR.php');
require_once('HTML/Common.php');
$GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] =
'group' =>array ('HTML/QuickForm/group.php','HTML_QuickForm_group'),
'hidden' =>array ('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'),
'reset' =>array ('HTML/QuickForm/reset.php','HTML_QuickForm_reset'),
'checkbox' =>array ('HTML/QuickForm/checkbox.php','HTML_QuickForm_checkbox'),
'file' =>array ('HTML/QuickForm/file.php','HTML_QuickForm_file'),
'image' =>array ('HTML/QuickForm/image.php','HTML_QuickForm_image'),
'password' =>array ('HTML/QuickForm/password.php','HTML_QuickForm_password'),
'radio' =>array ('HTML/QuickForm/radio.php','HTML_QuickForm_radio'),
'button' =>array ('HTML/QuickForm/button.php','HTML_QuickForm_button'),
'submit' =>array ('HTML/QuickForm/submit.php','HTML_QuickForm_submit'),
'select' =>array ('HTML/QuickForm/select.php','HTML_QuickForm_select'),
'hiddenselect' =>array ('HTML/QuickForm/hiddenselect.php','HTML_QuickForm_hiddenselect'),
'text' =>array ('HTML/QuickForm/text.php','HTML_QuickForm_text'),
'textarea' =>array ('HTML/QuickForm/textarea.php','HTML_QuickForm_textarea'),
'link' =>array ('HTML/QuickForm/link.php','HTML_QuickForm_link'),
'advcheckbox' =>array ('HTML/QuickForm/advcheckbox.php','HTML_QuickForm_advcheckbox'),
'date' =>array ('HTML/QuickForm/date.php','HTML_QuickForm_date'),
'static' =>array ('HTML/QuickForm/static.php','HTML_QuickForm_static'),
'header' =>array ('HTML/QuickForm/header.php', 'HTML_QuickForm_header'),
'html' =>array ('HTML/QuickForm/html.php', 'HTML_QuickForm_html'),
'hierselect' =>array ('HTML/QuickForm/hierselect.php', 'HTML_QuickForm_hierselect'),
'autocomplete' =>array ('HTML/QuickForm/autocomplete.php', 'HTML_QuickForm_autocomplete'),
'xbutton' =>array ('HTML/QuickForm/xbutton.php','HTML_QuickForm_xbutton')
$GLOBALS['_HTML_QuickForm_registered_rules'] = array (
'required' => array ('html_quickform_rule_required', 'HTML/QuickForm/Rule/Required.php'),
'maxlength' => array ('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
'minlength' => array ('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
'rangelength' => array ('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
'email' => array ('html_quickform_rule_email', 'HTML/QuickForm/Rule/Email.php'),
'regex' => array ('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'lettersonly' => array ('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'alphanumeric' => array ('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'numeric' => array ('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'nopunctuation' => array ('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'nonzero' => array ('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
'callback' => array ('html_quickform_rule_callback', 'HTML/QuickForm/Rule/Callback.php'),
'compare' => array ('html_quickform_rule_compare', 'HTML/QuickForm/Rule/Compare.php')
* Error codes for the QuickForm interface, which will be mapped to textual messages
* in the QuickForm::errorMessage() function. If you are to add a new error code, be
* sure to add the textual messages to the QuickForm::errorMessage() function as well
define('QUICKFORM_ERROR', -1 );
define('QUICKFORM_INVALID_RULE', -2 );
define('QUICKFORM_NONEXIST_ELEMENT', -3 );
define('QUICKFORM_INVALID_FILTER', -4 );
define('QUICKFORM_UNREGISTERED_ELEMENT', -5 );
define('QUICKFORM_INVALID_ELEMENT_NAME', -6 );
define('QUICKFORM_INVALID_PROCESS', -7 );
define('QUICKFORM_DEPRECATED', -8 );
define('QUICKFORM_INVALID_DATASOURCE', -9 );
* Create, validate and process HTML forms
* @author Adam Daniel <adaniel1@eesus.jnj.com>
* @author Bertrand Mansion <bmansion@mamasam.com>
* Array containing the form fields
var $_elements = array ();
* Array containing element name to index map
var $_elementIndex = array ();
* Array containing indexes of duplicate elements
var $_duplicateIndex = array ();
* Array containing required field IDs
var $_required = array ();
* Prefix message in javascript alert if error
* Postfix message in javascript alert if error
* Datasource object implementing the informal
* Array of default form values
var $_defaultValues = array ();
* Array of constant form values
var $_constantValues = array ();
* Array of submitted form values
var $_submitValues = array ();
* Array of submitted form files
* Value for maxfilesize hidden element if form contains file input
* Flag to know if all fields are frozen
* Array containing the form rules
* Form rules, global variety
var $_formRules = array ();
* Array containing the validation errors
* Note for required fields in the form
var $_requiredNote = '<span style="font-size:80%; color:#ff0000;">*</span><span style="font-size:80%;"> denotes required field</span>';
* Whether the form was submitted
var $_flagSubmitted = false;
* @param string $formName Form's name.
* @param string $method (optional)Form's method defaults to 'POST'
* @param string $action (optional)Form's action
* @param string $target (optional)Form's target defaults to '_self'
* @param mixed $attributes (optional)Extra attributes for <form> tag
* @param bool $trackSubmit (optional)Whether to track if the form was submitted by adding a special hidden field
function HTML_QuickForm($formName= '', $method= 'post', $action= '', $target= '', $attributes=null , $trackSubmit = false )
HTML_Common ::HTML_Common ($attributes);
$method = (strtoupper($method) == 'GET') ? 'get' : 'post';
$action = ($action == '') ? $_SERVER['PHP_SELF'] : $action;
$target = empty ($target) ? array () : array ('target' => $target);
$attributes = array ('action'=> $action, 'method'=> $method, 'name'=> $formName, 'id'=> $formName) + $target;
$this->updateAttributes ($attributes);
if (!$trackSubmit || isset ($_REQUEST['_qf__' . $formName])) {
$this->_submitValues = $this->_recursiveFilter ('stripslashes', 'get' == $method? $_GET: $_POST);
foreach ($_FILES as $keyFirst => $valFirst) {
foreach ($valFirst as $keySecond => $valSecond) {
if ('name' == $keySecond) {
$this->_submitFiles[$keyFirst][$keySecond] = $this->_recursiveFilter ('stripslashes', $valSecond);
$this->_submitValues = 'get' == $method? $_GET: $_POST;
unset ($this->_submitValues['_qf__' . $formName]);
$this->addElement('hidden', '_qf__' . $formName, null );
if (preg_match('/^([0-9]+)([a-zA-Z]*)$/', ini_get('upload_max_filesize'), $matches)) {
// see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
* Returns the current API version
// {{{ registerElementType()
* Registers a new element type
* @param string $typeName Name of element type
* @param string $include Include path for element type
* @param string $className Element class name
$GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($typeName)] = array ($include, $className);
} // end func registerElementType
* Registers a new validation rule
* @param string $ruleName Name of validation rule
* @param string $type Either: 'regex', 'function' or 'rule' for an HTML_QuickForm_Rule object
* @param string $data1 Name of function, regular expression or HTML_QuickForm_Rule classname
* @param string $data2 Object parent of above function or HTML_QuickForm_Rule file path
function registerRule($ruleName, $type, $data1, $data2 = null )
include_once('HTML/QuickForm/RuleRegistry.php');
$registry->registerRule ($ruleName, $type, $data1, $data2);
} // end func registerRule
* Returns true if element is in the form
* @param string $element form name of element to check
return isset ($this->_elementIndex[$element]);
} // end func elementExists
* Sets a datasource object for this form object
* Datasource default and constant values will feed the QuickForm object if
* the datasource implements defaultValues() and constantValues() methods.
* @param object $datasource datasource object implementing the informal datasource protocol
* @param mixed $defaultsFilter string or array of filter(s) to apply to default values
* @param mixed $constantsFilter string or array of filter(s) to apply to constants values
function setDatasource(&$datasource, $defaultsFilter = null , $constantsFilter = null )
$this->_datasource = & $datasource;
if (is_callable(array ($datasource, 'defaultValues'))) {
$this->setDefaults($datasource->defaultValues ($this), $defaultsFilter);
if (is_callable(array ($datasource, 'constantValues'))) {
$this->setConstants($datasource->constantValues ($this), $constantsFilter);
return PEAR ::raiseError (null , QUICKFORM_INVALID_DATASOURCE, null , E_USER_WARNING , "Datasource is not an object in QuickForm::setDatasource()", 'HTML_QuickForm_Error', true );
} // end func setDatasource
* Initializes default form values
* @param array $defaultValues values used to fill the form
* @param mixed $filter (optional) filter(s) to apply to all default values
function setDefaults($defaultValues = null , $filter = null )
foreach ($filter as $val) {
return PEAR ::raiseError (null , QUICKFORM_INVALID_FILTER, null , E_USER_WARNING , "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true );
$defaultValues = $this->_recursiveFilter ($val, $defaultValues);
return PEAR ::raiseError (null , QUICKFORM_INVALID_FILTER, null , E_USER_WARNING , "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true );
$defaultValues = $this->_recursiveFilter ($filter, $defaultValues);
$this->_elements[$key]->onQuickFormEvent ('updateValue', null , $this);
} // end func setDefaults
* Initializes constant form values.
* These values won't get overridden by POST or GET vars
* @param array $constantValues values used to fill the form
* @param mixed $filter (optional) filter(s) to apply to all default values
function setConstants($constantValues = null , $filter = null )
foreach ($filter as $val) {
return PEAR ::raiseError (null , QUICKFORM_INVALID_FILTER, null , E_USER_WARNING , "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true );
$constantValues = $this->_recursiveFilter ($val, $constantValues);
return PEAR ::raiseError (null , QUICKFORM_INVALID_FILTER, null , E_USER_WARNING , "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true );
$constantValues = $this->_recursiveFilter ($filter, $constantValues);
$this->_elements[$key]->onQuickFormEvent ('updateValue', null , $this);
} // end func setConstants
* Sets the value of MAX_FILE_SIZE hidden element
* @param int $bytes Size in bytes
$el->updateAttributes (array ('value' => $this->_maxFileSize));
} // end func setMaxFileSize
* Returns the value of MAX_FILE_SIZE hidden element
* @return int max file size in bytes
} // end func getMaxFileSize
* Creates a new form element of the given type.
* This method accepts variable number of parameters, their
* meaning and count depending on $elementType
* @param string $elementType type of element to add (text, textarea, file...)
* @return object extended class of HTML_element
* @throws HTML_QuickForm_Error
} // end func createElement
* Returns a form element of the given type
* @param string $event event to send to newly created element ('createElement' or 'addElement')
* @param string $type element type
* @param array $args arguments for event
* @return object a new element
* @throws HTML_QuickForm_Error
function &_loadElement ($event, $type, $args)
$error = PEAR ::raiseError (null , QUICKFORM_UNREGISTERED_ELEMENT, null , E_USER_WARNING , " Element '$type' does not exist in HTML_QuickForm::_loadElement()" , 'HTML_QuickForm_Error', true );
$className = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][1 ];
$includeFile = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][0 ];
include_once($includeFile);
$elementObject = & new $className();
for ($i = 0; $i < 5; $i++ ) {
$err = $elementObject->onQuickFormEvent ($event, $args, $this);
} // end func _loadElement
* Adds an element into the form
* If $element is a string representing element type, then this
* method accepts variable number of parameters, their meaning
* and count depending on $element
* @param mixed $element element object or type of element to add (text, textarea, file...)
* @return object reference to element
* @throws HTML_QuickForm_Error
$elementObject = &$element;
$elementObject->onQuickFormEvent ('updateValue', null , $this);
$elementObject = & $this->_loadElement ('addElement', $element, array_slice($args, 1 ));
if (PEAR ::isError ($elementObject)) {
$elementName = $elementObject->getName ();
// Add the element if it is not an incompatible duplicate
if (!empty ($elementName) && isset ($this->_elementIndex[$elementName])) {
if ($this->_elements[$this->_elementIndex[$elementName]]->getType () ==
$elementObject->getType ()) {
$this->_elements[] = & $elementObject;
$this->_duplicateIndex[$elementName][] = end($elKeys);
$error = PEAR ::raiseError (null , QUICKFORM_INVALID_ELEMENT_NAME, null , E_USER_WARNING , " Element '$elementName' already exists in HTML_QuickForm::addElement()" , 'HTML_QuickForm_Error', true );
$this->_elements[] = & $elementObject;
$this->_elementIndex[$elementName] = end($elKeys);
$elementObject->freeze ();
// {{{ insertElementBefore()
* Inserts a new element right before the other element
* Warning: it is not possible to check whether the $element is already
* added to the form, therefore if you want to move the existing form
* element to a new position, you'll have to use removeElement():
* $form->insertElementBefore($form->removeElement('foo', false), 'bar');
* @param object HTML_QuickForm_element Element to insert
* @param string Name of the element before which the new one is inserted
* @return object HTML_QuickForm_element reference to inserted element
* @throws HTML_QuickForm_Error
if (!empty ($this->_duplicateIndex[$nameAfter])) {
$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 );
$error = PEAR ::raiseError (null , QUICKFORM_NONEXIST_ELEMENT, null , E_USER_WARNING , " Element '$nameAfter' does not exist in HTML_QuickForm::insertElementBefore()" , 'HTML_QuickForm_Error', true );
$elementName = $element->getName ();
$targetIdx = $this->_elementIndex[$nameAfter];
// Like in addElement(), check that it's not an incompatible duplicate
if (!empty ($elementName) && isset ($this->_elementIndex[$elementName])) {
if ($this->_elements[$this->_elementIndex[$elementName]]->getType () != $element->getType ()) {
$error = PEAR ::raiseError (null , QUICKFORM_INVALID_ELEMENT_NAME, null , E_USER_WARNING , " Element '$elementName' already exists in HTML_QuickForm::insertElementBefore()" , 'HTML_QuickForm_Error', true );
// Move all the elements after added back one place, reindex _elementIndex and/or _duplicateIndex
for ($i = end($elKeys); $i >= $targetIdx; $i-- ) {
if (isset ($this->_elements[$i])) {
$currentName = $this->_elements[$i]->getName ();
$this->_elements[$i + 1 ] = & $this->_elements[$i];
if ($this->_elementIndex[$currentName] == $i) {
$this->_elementIndex[$currentName] = $i + 1;
$dupIdx = array_search($i, $this->_duplicateIndex[$currentName]);
$this->_duplicateIndex[$currentName][$dupIdx] = $i + 1;
unset ($this->_elements[$i]);
// Put the element in place finally
$this->_elements[$targetIdx] = & $element;
$this->_elementIndex[$elementName] = $targetIdx;
$this->_duplicateIndex[$elementName][] = $targetIdx;
$element->onQuickFormEvent ('updateValue', null , $this);
// If not done, the elements will appear in reverse order
* @param array $elements array of elements composing the group
* @param string $name (optional)group name
* @param string $groupLabel (optional)group label
* @param string $separator (optional)string to separate elements
* @param string $appendName (optional)specify whether the group name should be
* used in the form element name ex: group[element]
* @return object reference to added group of elements
function &addGroup($elements, $name=null , $groupLabel= '', $separator=null , $appendName = true )
if (0 == strlen ($name)) {
$name = 'qf_group_' . $anonGroups++;
$group = & $this->addElement ('group', $name, $groupLabel, $elements, $separator, $appendName);
* Returns a reference to the element
* @param string $element Element name
* @return object reference to element
* @throws HTML_QuickForm_Error
if (isset ($this->_elementIndex[$element])) {
return $this->_elements[$this->_elementIndex[$element]];
$error = PEAR ::raiseError (null , QUICKFORM_NONEXIST_ELEMENT, null , E_USER_WARNING , " Element '$element' does not exist in HTML_QuickForm::getElement()" , 'HTML_QuickForm_Error', true );
// {{{ &getElementValue()
* Returns the element's raw value
* This returns the value as submitted by the form (not filtered)
* or set via setDefaults() or setConstants()
* @param string $element Element name
* @return mixed element value
* @throws HTML_QuickForm_Error
if (!isset ($this->_elementIndex[$element])) {
$error = PEAR ::raiseError (null , QUICKFORM_NONEXIST_ELEMENT, null , E_USER_WARNING , " Element '$element' does not exist in HTML_QuickForm::getElementValue()" , 'HTML_QuickForm_Error', true );
$value = $this->_elements[$this->_elementIndex[$element]]->getValue ();
if (isset ($this->_duplicateIndex[$element])) {
foreach ($this->_duplicateIndex[$element] as $index) {
if (null !== ($v = $this->_elements[$index]->getValue ())) {
$value = (null === $value)? $v: array ($value, $v);
} // end func getElementValue
* Returns the elements value after submit and filter
* @param string Element name
* @return mixed submitted element value or null if not set
if (isset ($this->_submitValues[$elementName]) || isset ($this->_submitFiles[$elementName])) {
$value = isset ($this->_submitValues[$elementName])? $this->_submitValues[$elementName]: array ();
} elseif (false !== ($pos = strpos($elementName, '['))) {
$base = substr($elementName, 0 , $pos);
$idx = "['" . str_replace(array (']', '['), array ('', "']['"), substr($elementName, $pos + 1 , -1 )) . "']";
if (isset ($this->_submitValues[$base])) {
$value = eval (" return (isset(\$this->_submitValues['{$base}']{$idx})) ? \$this->_submitValues['{$base}']{$idx} : null;" );
$props = array ('name', 'type', 'size', 'tmp_name', 'error');
$code = " if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" .
foreach ($props as $prop) {
$code .= " \$v = HTML_QuickForm::arrayMerge(\$v, \$this->_reindexFiles(\$this->_submitFiles['{$base}']['{$prop}']{$idx}, '{$prop}'));\n";
$fileValue = eval ($code . " return \$v;\n}\n");
if (null !== $fileValue) {
// This is only supposed to work for groups with appendName = false
if (null === $value && 'group' == $this->getElementType($elementName)) {
$elements = & $group->getElements ();
$name = $group->getElementName ($key);
// prevent endless recursion in case of radios and such
if ($name != $elementName) {
} // end func getSubmitValue
* A helper function to change the indexes in $_FILES array
* @param mixed Some value from the $_FILES array
* @param string The key from the $_FILES array that should be appended
function _reindexFiles ($value, $key)
return array ($key => $value);
foreach ($value as $k => $v) {
$ret[$k] = $this->_reindexFiles ($v, $key);
* Returns error corresponding to validated element
* @param string $element Name of form element to check
* @return string error message corresponding to checked element
if (isset ($this->_errors[$element])) {
return $this->_errors[$element];
} // end func getElementError
* Set error message for a form element
* @param string $element Name of form element to set error for
* @param string $message Error message
$this->_errors[$element] = $message;
} // end func setElementError
* Returns the type of the given element
* @param string $element Name of form element
* @return string Type of the element, false if the element is not found
if (isset ($this->_elementIndex[$element])) {
return $this->_elements[$this->_elementIndex[$element]]->getType ();
} // end func getElementType
// {{{ updateElementAttr()
* Updates Attributes for one or more elements
* @param mixed $elements Array of element names/objects or string of elements to be updated
* @param mixed $attrs Array or sting of html attributes
$elements = split('[ ]?,[ ]?', $elements);
if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) {
$elements[$key]->updateAttributes ($attrs);
} elseif (isset ($this->_elementIndex[$elements[$key]])) {
$this->_elements[$this->_elementIndex[$elements[$key]]]->updateAttributes ($attrs);
if (isset ($this->_duplicateIndex[$elements[$key]])) {
foreach ($this->_duplicateIndex[$elements[$key]] as $index) {
$this->_elements[$index]->updateAttributes ($attrs);
} // end func updateElementAttr
* The method "unlinks" an element from the form, returning the reference
* to the element object. If several elements named $elementName exist,
* it removes the first one, leaving the others intact.
* @param string $elementName The element name
* @param boolean $removeRules True if rules for this element are to be removed too
* @return object HTML_QuickForm_element a reference to the removed element
* @throws HTML_QuickForm_Error
if (!isset ($this->_elementIndex[$elementName])) {
$error = PEAR ::raiseError (null , QUICKFORM_NONEXIST_ELEMENT, null , E_USER_WARNING , " Element '$elementName' does not exist in HTML_QuickForm::removeElement()" , 'HTML_QuickForm_Error', true );
$el = & $this->_elements[$this->_elementIndex[$elementName]];
unset ($this->_elements[$this->_elementIndex[$elementName]]);
if (empty ($this->_duplicateIndex[$elementName])) {
unset ($this->_elementIndex[$elementName]);
$this->_elementIndex[$elementName] = array_shift($this->_duplicateIndex[$elementName]);
unset ($this->_rules[$elementName]);
} // end func removeElement
* Adds a validation rule for the given field
* If the element is in fact a group, it will be considered as a whole.
* To validate grouped elements as separated entities,
* use addGroupRule instead of addRule.
* @param string $element Form element name
* @param string $message Message to display for invalid data
* @param string $type Rule type, use getRegisteredRules() to get types
* @param string $format (optional)Required for extra rule data
* @param string $validation (optional)Where to perform validation: "server", "client"
* @param boolean $reset Client-side validation: reset the form element to its original value if there is an error?
* @param boolean $force Force the rule to be applied, even if the target form element does not exist
* @throws HTML_QuickForm_Error
function addRule($element, $message, $type, $format=null , $validation= 'server', $reset = false , $force = false )
return PEAR ::raiseError (null , QUICKFORM_NONEXIST_ELEMENT, null , E_USER_WARNING , " Element '$element' does not exist in HTML_QuickForm::addRule()" , 'HTML_QuickForm_Error', true );
foreach ($element as $el) {
return PEAR ::raiseError (null , QUICKFORM_NONEXIST_ELEMENT, null , E_USER_WARNING , " Element '$el' does not exist in HTML_QuickForm::addRule()" , 'HTML_QuickForm_Error', true );
return PEAR ::raiseError (null , QUICKFORM_INVALID_RULE, null , E_USER_WARNING , " Rule '$type' is not registered in HTML_QuickForm::addRule()" , 'HTML_QuickForm_Error', true );
if ($type == 'required' || $type == 'uploadedfile') {
$this->_required[] = $element;
if (!isset ($this->_rules[$element])) {
$this->_rules[$element] = array ();
if ($validation == 'client') {
$this->updateAttributes (array ('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
$this->_rules[$element][] = array (
'validation' => $validation,
'dependent' => $dependent
* Adds a validation rule for the given group of elements
* Only groups with a name can be assigned a validation rule
* Use addGroupRule when you need to validate elements inside the group.
* Use addRule if you need to validate the group as a whole. In this case,
* the same rule will be applied to all elements in the group.
* Use addRule if you need to validate the group against a function.
* @param string $group Form group name
* @param mixed $arg1 Array for multiple elements or error message string for one element
* @param string $type (optional)Rule type use getRegisteredRules() to get types
* @param string $format (optional)Required for extra rule data
* @param int $howmany (optional)How many valid elements should be in the group
* @param string $validation (optional)Where to perform validation: "server", "client"
* @param bool $reset Client-side: whether to reset the element's value to its original state if validation failed.
* @throws HTML_QuickForm_Error
function addGroupRule($group, $arg1, $type= '', $format=null , $howmany=0 , $validation = 'server', $reset = false )
return PEAR ::raiseError (null , QUICKFORM_NONEXIST_ELEMENT, null , E_USER_WARNING , " Group '$group' does not exist in HTML_QuickForm::addGroupRule()" , 'HTML_QuickForm_Error', true );
foreach ($arg1 as $elementIndex => $rules) {
$elementName = $groupObj->getElementName ($elementIndex);
foreach ($rules as $rule) {
$format = (isset ($rule[2 ])) ? $rule[2 ] : null;
$validation = (isset ($rule[3 ]) && 'client' == $rule[3 ])? 'client': 'server';
$reset = isset ($rule[4 ]) && $rule[4 ];
return PEAR ::raiseError (null , QUICKFORM_INVALID_RULE, null , E_USER_WARNING , " Rule '$type' is not registered in HTML_QuickForm::addGroupRule()" , 'HTML_QuickForm_Error', true );
$this->_rules[$elementName][] = array (
'validation' => $validation,
if ('required' == $type || 'uploadedfile' == $type) {
$groupObj->_required [] = $elementName;
$this->_required[] = $elementName;
if ('client' == $validation) {
$this->updateAttributes (array ('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
if ($required > 0 && count($groupObj->getElements ()) == $required) {
$this->_required[] = $group;
return PEAR ::raiseError (null , QUICKFORM_INVALID_RULE, null , E_USER_WARNING , " Rule '$type' is not registered in HTML_QuickForm::addGroupRule()" , 'HTML_QuickForm_Error', true );
// addGroupRule() should also handle <select multiple>
if (is_a($groupObj, 'html_quickform_group')) {
// Radios need to be handled differently when required
if ($type == 'required' && $groupObj->getGroupType () == 'radio') {
$howmany = ($howmany == 0 ) ? 1 : $howmany;
$howmany = ($howmany == 0 ) ? count($groupObj->getElements ()) : $howmany;
$this->_rules[$group][] = array ('type' => $type,
'validation' => $validation,
if ($type == 'required') {
$this->_required[] = $group;
if ($validation == 'client') {
$this->updateAttributes (array ('onsubmit' => 'try { var myValidator = validate_' . $this->_attributes['id'] . '; } catch(e) { return true; } return myValidator(this);'));
} // end func addGroupRule
* Adds a global validation rule
* This should be used when for a rule involving several fields or if
* you want to use some completely custom validation for your form.
* The rule function/method should return true in case of successful
* validation and array('element name' => 'error') when there were errors.
* @param mixed Callback, either function name or array(&$object, 'method')
* @throws HTML_QuickForm_Error
return PEAR ::raiseError (null , QUICKFORM_INVALID_RULE, null , E_USER_WARNING , 'Callback function does not exist in HTML_QuickForm::addFormRule()', 'HTML_QuickForm_Error', true );
$this->_formRules[] = $rule;
* Applies a data filter for the given field(s)
* @param mixed $element Form element name or array of such names
* @param mixed $filter Callback, either function name or array(&$object, 'method')
return PEAR ::raiseError (null , QUICKFORM_INVALID_FILTER, null , E_USER_WARNING , "Callback function does not exist in QuickForm::applyFilter()", 'HTML_QuickForm_Error', true );
if ($element == '__ALL__') {
$this->_submitValues = $this->_recursiveFilter ($filter, $this->_submitValues);
$element = array ($element);
foreach ($element as $elName) {
if (false === strpos($elName, '[')) {
$this->_submitValues[$elName] = $this->_recursiveFilter ($filter, $value);
$idx = "['" . str_replace(array (']', '['), array ('', "']['"), $elName) . "']";
eval (" \$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);" );
} // end func applyFilter
// {{{ _recursiveFilter()
* Recursively apply a filter function
* @param string $filter filter to apply
* @param mixed $value submitted values
function _recursiveFilter ($filter, $value)
foreach ($value as $k => $v) {
$cleanValues[$k] = $this->_recursiveFilter ($filter, $value[$k]);
} // end func _recursiveFilter
* Merges two array like the PHP function array_merge but recursively.
* The main difference is that existing keys will not be renumbered
* @param array $a original array
* @param array $b array which will be merged into first one
* @return array merged array
foreach ($b as $k => $v) {
if (isset ($a[$k]) && !is_array($a[$k])) {
// {{{ isTypeRegistered()
* Returns whether or not the form element type is supported
* @param string $type Form element type
return isset ($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type]);
} // end func isTypeRegistered
// {{{ getRegisteredTypes()
* Returns an array of registered element types
return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']);
} // end func getRegisteredTypes
// {{{ isRuleRegistered()
* Returns whether or not the given rule is supported
* @param string $name Validation rule name
* @param bool Whether to automatically register subclasses of HTML_QuickForm_Rule
* @return mixed true if previously registered, false if not, new rule name if auto-registering worked
if (is_scalar($name) && isset ($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) {
} elseif (!$autoRegister) {
// automatically register the rule if requested
include_once 'HTML/QuickForm/RuleRegistry.php';
if ('html_quickform_rule' == strtolower($parent)) {
$registry->registerRule ($ruleName, null , $name);
} // end func isRuleRegistered
// {{{ getRegisteredRules()
* Returns an array of registered validation rules
return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']);
} // end func getRegisteredRules
// {{{ isElementRequired()
* Returns whether or not the form element is required
* @param string $element Form element name
return in_array($element, $this->_required, true );
} // end func isElementRequired
* Returns whether or not the form element is frozen
* @param string $element Form element name
if (isset ($this->_elementIndex[$element])) {
return $this->_elements[$this->_elementIndex[$element]]->isFrozen ();
} // end func isElementFrozen
* Sets JavaScript warning messages
* @param string $pref Prefix warning
* @param string $post Postfix warning
} // end func setJsWarnings
* @param string $note Message indicating some elements are required
$this->_requiredNote = $note;
} // end func setRequiredNote
* Returns the required note
return $this->_requiredNote;
} // end func getRequiredNote
* Performs the server side validation
* @return boolean true if no error found
if (count($this->_rules) == 0 && count($this->_formRules) == 0 &&
include_once('HTML/QuickForm/RuleRegistry.php');
foreach ($this->_rules as $target => $rules) {
foreach ($rules as $elementName => $rule) {
if ((isset ($rule['group']) && isset ($this->_errors[$rule['group']])) ||
isset ($this->_errors[$target])) {
// If element is not required and is empty, we shouldn't validate it
if (!isset ($submitValue) || '' == $submitValue) {
// Fix for bug #3501: we shouldn't validate not uploaded files, either.
// Unfortunately, we can't just use $element->isUploadedFile() since
// the element in question can be buried in group. Thus this hack.
if (false === ($pos = strpos($target, '['))) {
$base = substr($target, 0 , $pos);
$idx = "['" . str_replace(array (']', '['), array ('', "']['"), substr($target, $pos + 1 , -1 )) . "']";
eval (" \$isUpload = isset(\$this->_submitFiles['{$base}']['name']{$idx});" );
if ($isUpload && (!isset ($submitValue['error']) || 0 != $submitValue['error'])) {
if (isset ($rule['dependent']) && is_array($rule['dependent'])) {
$values = array ($submitValue);
foreach ($rule['dependent'] as $elName) {
$result = $registry->validate ($rule['type'], $values, $rule['format'], true );
} elseif (is_array($submitValue) && !isset ($rule['howmany'])) {
$result = $registry->validate ($rule['type'], $submitValue, $rule['format'], true );
$result = $registry->validate ($rule['type'], $submitValue, $rule['format'], false );
if (!$result || (!empty ($rule['howmany']) && $rule['howmany'] > (int) $result)) {
if (isset ($rule['group'])) {
$this->_errors[$rule['group']] = $rule['message'];
$this->_errors[$target] = $rule['message'];
// process the global rules now
foreach ($this->_formRules as $rule) {
return PEAR ::raiseError (null , QUICKFORM_ERROR, null , E_USER_WARNING , 'Form rule callback returned invalid value in HTML_QuickForm::validate()', 'HTML_QuickForm_Error', true );
return (0 == count($this->_errors));
* Displays elements without HTML input tags
* @param mixed $elementList array or string of element(s) to be frozen
* @throws HTML_QuickForm_Error
function freeze($elementList=null )
if (!isset ($elementList)) {
$this->_freezeAll = true;
$elementList = preg_split('/[ ]*,[ ]*/', $elementList);
$name = $this->_elements[$key]->getName ();
if ($this->_freezeAll || isset ($elementList[$name])) {
$this->_elements[$key]->freeze ();
unset ($elementList[$name]);
if (!empty ($elementList)) {
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 );
* Returns whether or not the whole form is frozen
return $this->_freezeAll;
* Performs the form data processing
* @param mixed $callback Callback, either function name or array(&$object, 'method')
* @param bool $mergeFiles Whether uploaded files should be processed too
* @throws HTML_QuickForm_Error
function process($callback, $mergeFiles = true )
return PEAR ::raiseError (null , QUICKFORM_INVALID_PROCESS, null , E_USER_WARNING , "Callback function does not exist in QuickForm::process()", 'HTML_QuickForm_Error', true );
* @param object An HTML_QuickForm_Renderer object
$renderer->startForm ($this);
$element = & $this->_elements[$key];
$elementName = $element->getName ();
$element->accept ($renderer, $required, $error);
$renderer->finishForm ($this);
* Returns a reference to default renderer object
* @return object a default renderer object
if (!isset ($GLOBALS['_HTML_QuickForm_default_renderer'])) {
include_once('HTML/QuickForm/Renderer/Default.php');
return $GLOBALS['_HTML_QuickForm_default_renderer'];
} // end func defaultRenderer
* Returns an HTML version of the form
* @param string $in_data (optional) Any extra data to insert right
* before form is rendered. Useful when using templates.
* @return string Html version of the form
function toHtml ($in_data = null )
return $renderer->toHtml ();
// {{{ getValidationScript()
* Returns the client side validation script
* @return string Javascript to perform validation, empty string if no 'client' rules were added
if (empty ($this->_rules) || empty ($this->_attributes['onsubmit'])) {
include_once('HTML/QuickForm/RuleRegistry.php');
foreach ($this->_rules as $elementName => $rules) {
foreach ($rules as $rule) {
if ('client' == $rule['validation']) {
$dependent = isset ($rule['dependent']) && is_array($rule['dependent']);
$rule['message'] = strtr($rule['message'], $js_escape);
if (isset ($rule['group'])) {
// No JavaScript validation for frozen elements
if ($group->isFrozen ()) {
$elements = & $group->getElements ();
if ($elementName == $group->getElementName ($key)) {
$element = & $elements[$key];
foreach ($rule['dependent'] as $idx => $elName) {
// No JavaScript validation for frozen elements
if (is_object($element) && $element->isFrozen ()) {
if ($element[$key]->isFrozen ()) {
$test[] = $registry->getValidationScript ($element, $elementName, $rule);
"\n<script type=\"text/javascript\">\n" .
"function validate_" . $this->_attributes['id'] . "(frm) {\n" .
" var errFlag = new Array();\n" .
" var _qfGroups = {};\n" .
"\n if (_qfMsg != '') {\n" .
" _qfMsg = '" . strtr($this->_jsPrefix, $js_escape) . "' + _qfMsg;\n" .
" _qfMsg = _qfMsg + '\\n" . strtr($this->_jsPostfix, $js_escape) . "';\n" .
} // end func getValidationScript
* Returns the values submitted by the form
* @param bool Whether uploaded files should be returned too
} // end func getSubmitValues
* Returns the form's contents in an array.
* The description of the array structure is in HTML_QuickForm_Renderer_Array docs
* @param bool Whether to collect hidden elements (passed to the Renderer's constructor)
* @return array of form contents
function toArray($collectHidden = false )
include_once 'HTML/QuickForm/Renderer/Array.php';
return $renderer->toArray ();
* Returns a 'safe' element's value
* This method first tries to find a cleaned-up submitted value,
* it will return a value set by setValue()/setDefaults()/setConstants()
* if submitted value does not exist for the given element.
* @param string Name of an element
if (!isset ($this->_elementIndex[$element])) {
return PEAR ::raiseError (null , QUICKFORM_NONEXIST_ELEMENT, null , E_USER_WARNING , " Element '$element' does not exist in HTML_QuickForm::getElementValue()" , 'HTML_QuickForm_Error', true );
$value = $this->_elements[$this->_elementIndex[$element]]->exportValue ($this->_submitValues, false );
if (isset ($this->_duplicateIndex[$element])) {
foreach ($this->_duplicateIndex[$element] as $index) {
if (null !== ($v = $this->_elements[$index]->exportValue ($this->_submitValues, false ))) {
$value = (null === $value)? $v: array ($value, $v);
* Returns 'safe' elements' values
* Unlike getSubmitValues(), this will return only the values
* corresponding to the elements present in the form.
* @param mixed Array/string of element names, whose values we want. If not set then return all elements.
* @return array An assoc array of elements' values
* @throws HTML_QuickForm_Error
if (null === $elementList) {
// iterate over all elements, calling their exportValue() methods
$value = $this->_elements[$key]->exportValue ($this->_submitValues, true );
// This shit throws a bogus warning in PHP 4.3.x
foreach ($elementList as $elementName) {
if (PEAR ::isError ($value)) {
$values[$elementName] = $value;
* Tells whether the form was already submitted
* This is useful since the _submitFiles and _submitValues arrays
* may be completely empty after the trackSubmit value is removed.
return $this->_flagSubmitted;
* Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error)
* @param mixed result code
* @return bool whether $value is an error
return (is_object($value) && is_a($value, 'html_quickform_error'));
* Return a textual error message for an QuickForm error code
* @return string error message
// make the variable static so that it only has to do the defining on the first call
// define the varies error messages
if (!isset ($errorMessages)) {
// If this is an error object, then grab the corresponding error code
$value = $value->getCode ();
// return the textual error message corresponding to the code
return isset ($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR];
} // end func errorMessage
} // end class HTML_QuickForm
* Prefix for all error messages
* Creates a quickform error object, extending the PEAR_Error class
* @param int $code the error code
* @param int $mode the reaction to the error, either return, die or trigger/callback
* @param int $level intensity of the error (PHP error code)
* @param mixed $debuginfo any information that can inform user as to nature of the error
$level = E_USER_NOTICE , $debuginfo = null )
$this->PEAR_Error (" Invalid error code: $code" , QUICKFORM_ERROR, $mode, $level, $debuginfo);
} // end class HTML_QuickForm_Error
Documentation generated on Mon, 11 Mar 2019 14:16:35 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|