| /** | |
| * Classes for <select> elements | |
| * | |
| * PHP version 5 | |
| * | |
| * LICENSE: | |
| * | |
| * Copyright (c) 2006-2010, Alexey Borzov <avb@php.net>, | |
| * Bertrand Mansion <golgote@mamasam.com> | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * | |
| * * Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * * Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * * The names of the authors may not be used to endorse or promote products | |
| * derived from this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS | |
| * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * @category HTML | |
| * @package HTML_QuickForm2 | |
| * @author Alexey Borzov <avb@php.net> | |
| * @author Bertrand Mansion <golgote@mamasam.com> | |
| * @license http://opensource.org/licenses/bsd-license.php New BSD License | |
| * @version SVN: $Id: Select.php 300722 2010-06-24 10:15:52Z mansion $ | |
| * @link http://pear.php.net/package/HTML_QuickForm2 | |
| */ | |
| /** | |
| * Base class for simple HTML_QuickForm2 elements | |
| */ | |
| // require_once 'HTML/QuickForm2/Element.php'; | |
| /** | |
| * Implements a recursive iterator for options arrays | |
| * | |
| * @category HTML | |
| * @package HTML_QuickForm2 | |
| * @author Alexey Borzov <avb@php.net> | |
| * @author Bertrand Mansion <golgote@mamasam.com> | |
| * @version Release: @package_version@ | |
| */ | |
| class HTML_QuickForm2_Element_Select_OptionIterator extends RecursiveArrayIterator | |
| implements RecursiveIterator | |
| { | |
| public function hasChildren(): bool | |
| { | |
| return $this->current() instanceof HTML_QuickForm2_Element_Select_OptionContainer; | |
| } | |
| public function getChildren(): HTML_QuickForm2_Element_Select_OptionIterator | |
| { | |
| return new HTML_QuickForm2_Element_Select_OptionIterator( | |
| $this->current()->getOptions() | |
| ); | |
| } | |
| } | |
| /** | |
| * Collection of <option>s and <optgroup>s | |
| * | |
| * This class handles the output of <option> tags. The class is not intended to | |
| * be used directly. | |
| * | |
| * @category HTML | |
| * @package HTML_QuickForm2 | |
| * @author Alexey Borzov <avb@php.net> | |
| * @author Bertrand Mansion <golgote@mamasam.com> | |
| * @version Release: @package_version@ | |
| */ | |
| class HTML_QuickForm2_Element_Select_OptionContainer extends HTML_Common2 | |
| implements IteratorAggregate, Countable | |
| { | |
| /** | |
| * List of options and optgroups in this container | |
| * | |
| * Options are stored as arrays (for performance reasons), optgroups as | |
| * instances of Optgroup class. | |
| * | |
| * @var array | |
| */ | |
| protected $options = array(); | |
| /** | |
| * Reference to parent <select>'s values | |
| * @var array | |
| */ | |
| protected $values; | |
| /** | |
| * Reference to parent <select>'s possible values | |
| * @var array | |
| */ | |
| protected $possibleValues; | |
| /** | |
| * Class constructor | |
| * | |
| * @param array Reference to values of parent <select> element | |
| * @param array Reference to possible values of parent <select> element | |
| */ | |
| public function __construct(&$values, &$possibleValues) | |
| { | |
| $this->values =& $values; | |
| $this->possibleValues =& $possibleValues; | |
| } | |
| /** | |
| * Adds a new option | |
| * | |
| * Please note that if you pass 'selected' attribute in the $attributes | |
| * parameter then this option's value will be added to <select>'s values. | |
| * | |
| * @param string Option text | |
| * @param string 'value' attribute for <option> tag | |
| * @param mixed Additional attributes for <option> tag (either as a | |
| * string or as an associative array) | |
| */ | |
| public function addOption($text, $value, $attributes = null) | |
| { | |
| if (null === $attributes) { | |
| $attributes = array('value' => (string)$value); | |
| } else { | |
| $attributes = self::prepareAttributes($attributes); | |
| if (isset($attributes['selected'])) { | |
| // the 'selected' attribute will be set in __toString() | |
| unset($attributes['selected']); | |
| if (!in_array($value, $this->values)) { | |
| $this->values[] = $value; | |
| } | |
| } | |
| $attributes['value'] = (string)$value; | |
| } | |
| if (!isset($attributes['disabled'])) { | |
| $this->possibleValues[(string)$value] = true; | |
| } | |
| $this->options[] = array('text' => $text, 'attr' => $attributes); | |
| } | |
| /** | |
| * Adds a new optgroup | |
| * | |
| * @param string 'label' attribute for optgroup tag | |
| * @param mixed Additional attributes for <optgroup> tag (either as a | |
| * string or as an associative array) | |
| * @return HTML_QuickForm2_Element_Select_Optgroup | |
| */ | |
| public function addOptgroup($label, $attributes = null) | |
| { | |
| $optgroup = new HTML_QuickForm2_Element_Select_Optgroup( | |
| $this->values, $this->possibleValues, | |
| $label, $attributes | |
| ); | |
| $this->options[] = $optgroup; | |
| return $optgroup; | |
| } | |
| /** | |
| * Returns an array of contained options | |
| * | |
| * @return array | |
| */ | |
| public function getOptions() | |
| { | |
| return $this->options; | |
| } | |
| public function __toString() | |
| { | |
| $indentLvl = $this->getIndentLevel(); | |
| $indent = $this->getIndent() . self::getOption('indent'); | |
| $linebreak = self::getOption('linebreak'); | |
| $html = ''; | |
| $strValues = array_map('strval', $this->values); | |
| foreach ($this->options as $option) { | |
| if (is_array($option)) { | |
| if (in_array($option['attr']['value'], $strValues, true)) { | |
| $option['attr']['selected'] = 'selected'; | |
| } | |
| $html .= $indent . '<option' . | |
| self::getAttributesString($option['attr']) . | |
| '>' . $option['text'] . '</option>' . $linebreak; | |
| } elseif ($option instanceof HTML_QuickForm2_Element_Select_OptionContainer) { | |
| $option->setIndentLevel($indentLvl + 1); | |
| $html .= $option->__toString(); | |
| } | |
| } | |
| return $html; | |
| } | |
| /** | |
| * Returns an iterator over contained elements | |
| * | |
| * @return HTML_QuickForm2_Element_Select_OptionIterator | |
| */ | |
| public function getIterator(): HTML_QuickForm2_Element_Select_OptionIterator | |
| { | |
| return new HTML_QuickForm2_Element_Select_OptionIterator($this->options); | |
| } | |
| /** | |
| * Returns a recursive iterator over contained elements | |
| * | |
| * @return RecursiveIteratorIterator | |
| */ | |
| public function getRecursiveIterator(): RecursiveIteratorIterator | |
| { | |
| return new RecursiveIteratorIterator( | |
| new HTML_QuickForm2_Element_Select_OptionIterator($this->options), | |
| RecursiveIteratorIterator::SELF_FIRST | |
| ); | |
| } | |
| /** | |
| * Returns the number of options in the container | |
| * | |
| * @return int | |
| */ | |
| public function count(): int | |
| { | |
| return count($this->options); | |
| } | |
| } | |
| /** | |
| * Class representing an <optgroup> tag | |
| * | |
| * Do not instantiate this class yourself, use | |
| * {@link HTML_QuickForm2_Element_Select::addOptgroup()} method | |
| * | |
| * @category HTML | |
| * @package HTML_QuickForm2 | |
| * @author Alexey Borzov <avb@php.net> | |
| * @author Bertrand Mansion <golgote@mamasam.com> | |
| * @version Release: @package_version@ | |
| */ | |
| class HTML_QuickForm2_Element_Select_Optgroup | |
| extends HTML_QuickForm2_Element_Select_OptionContainer | |
| { | |
| /** | |
| * Class constructor | |
| * | |
| * @param array Reference to values of parent <select> element | |
| * @param array Reference to possible values of parent <select> element | |
| * @param string 'label' attribute for optgroup tag | |
| * @param mixed Additional attributes for <optgroup> tag (either as a | |
| * string or as an associative array) | |
| */ | |
| public function __construct(&$values, &$possibleValues, $label, $attributes = null) | |
| { | |
| parent::__construct($values, $possibleValues); | |
| $this->setAttributes($attributes); | |
| $this->attributes['label'] = (string)$label; | |
| } | |
| public function __toString() | |
| { | |
| $indent = $this->getIndent(); | |
| $linebreak = self::getOption('linebreak'); | |
| return $indent . '<optgroup' . $this->getAttributes(true) . '>' . | |
| $linebreak . parent::__toString() . $indent . '</optgroup>' . $linebreak; | |
| } | |
| } | |
| /** | |
| * Class representing a <select> element | |
| * | |
| * @category HTML | |
| * @package HTML_QuickForm2 | |
| * @author Alexey Borzov <avb@php.net> | |
| * @author Bertrand Mansion <golgote@mamasam.com> | |
| * @version Release: @package_version@ | |
| */ | |
| class HTML_QuickForm2_Element_Select extends HTML_QuickForm2_Element | |
| { | |
| protected $persistent = true; | |
| /** | |
| * Values for the select element (i.e. values of the selected options) | |
| * @var array | |
| */ | |
| protected $values = array(); | |
| /** | |
| * Possible values for select elements | |
| * | |
| * A value is considered possible if it is present as a value attribute of | |
| * some option and that option is not disabled. | |
| * @var array | |
| */ | |
| protected $possibleValues = array(); | |
| /** | |
| * Object containing options for the <select> element | |
| * @var HTML_QuickForm2_Element_Select_OptionContainer | |
| */ | |
| protected $optionContainer; | |
| /** | |
| * Enable intrinsic validation by default | |
| * @var array | |
| */ | |
| protected $data = array('intrinsic_validation' => true); | |
| /** | |
| * Class constructor | |
| * | |
| * Select element can understand the following keys in $data parameter: | |
| * - 'options': data to populate element's options with. Passed to | |
| * {@link loadOptions()} method. | |
| * - 'intrinsic_validation': setting this to false will disable | |
| * that validation, {@link getValue()} will then return all submit | |
| * values, not just those corresponding to options present in the | |
| * element. May be useful in AJAX scenarios. | |
| * | |
| * @param string Element name | |
| * @param mixed Attributes (either a string or an array) | |
| * @param array Additional element data | |
| * @throws HTML_QuickForm2_InvalidArgumentException if junk is given in $options | |
| */ | |
| public function __construct($name = null, $attributes = null, array $data = array()) | |
| { | |
| $options = isset($data['options'])? $data['options']: array(); | |
| unset($data['options']); | |
| parent::__construct($name, $attributes, $data); | |
| $this->loadOptions($options); | |
| } | |
| public function getType() | |
| { | |
| return 'select'; | |
| } | |
| public function __toString() | |
| { | |
| if ($this->frozen) { | |
| return $this->getFrozenHtml(); | |
| } else { | |
| if (empty($this->attributes['multiple'])) { | |
| $attrString = $this->getAttributes(true); | |
| } else { | |
| $this->attributes['name'] .= '[]'; | |
| $attrString = $this->getAttributes(true); | |
| $this->attributes['name'] = substr($this->attributes['name'], 0, -2); | |
| } | |
| $indent = $this->getIndent(); | |
| return $indent . '<select' . $attrString . '>' . | |
| self::getOption('linebreak') . | |
| $this->optionContainer->__toString() . | |
| $indent . '</select>'; | |
| } | |
| } | |
| protected function getFrozenHtml() | |
| { | |
| if (null === ($value = $this->getValue())) { | |
| return ' '; | |
| } | |
| $valueHash = is_array($value)? array_flip($value): array($value => true); | |
| $options = array(); | |
| foreach ($this->optionContainer->getRecursiveIterator() as $child) { | |
| if (is_array($child) && isset($valueHash[$child['attr']['value']]) && | |
| empty($child['attr']['disabled'])) | |
| { | |
| $options[] = $child['text']; | |
| } | |
| } | |
| $html = implode('<br />', $options); | |
| if ($this->persistent) { | |
| $name = $this->attributes['name'] . | |
| (empty($this->attributes['multiple'])? '': '[]'); | |
| // Only use id attribute if doing single hidden input | |
| $idAttr = (1 == count($valueHash))? array('id' => $this->getId()): array(); | |
| foreach ($valueHash as $key => $item) { | |
| $html .= '<input type="hidden"' . self::getAttributesString(array( | |
| 'name' => $name, | |
| 'value' => $key | |
| ) + $idAttr) . ' />'; | |
| } | |
| } | |
| return $html; | |
| } | |
| /** | |
| * Returns the value of the <select> element | |
| * | |
| * Please note that the returned value may not necessarily be equal to that | |
| * passed to {@link setValue()}. It passes "intrinsic validation" confirming | |
| * that such value could possibly be submitted by this <select> element. | |
| * Specifically, this method will return null if the elements "disabled" | |
| * attribute is set, it will not return values if there are no options having | |
| * such a "value" attribute or if such options' "disabled" attribute is set. | |
| * It will also only return a scalar value for single selects, mimicking | |
| * the common browsers' behaviour. | |
| * | |
| * @return mixed "value" attribute of selected option in case of single | |
| * select, array of selected options' "value" attributes in | |
| * case of multiple selects, null if no options selected | |
| */ | |
| public function getValue() | |
| { | |
| if (!empty($this->attributes['disabled']) || 0 == count($this->values) | |
| || ($this->data['intrinsic_validation'] | |
| && (0 == count($this->optionContainer) || 0 == count($this->possibleValues))) | |
| ) { | |
| return null; | |
| } | |
| $values = array(); | |
| foreach ($this->values as $value) { | |
| if (!$this->data['intrinsic_validation'] || !empty($this->possibleValues[$value])) { | |
| $values[] = $value; | |
| } | |
| } | |
| if (0 == count($values)) { | |
| return null; | |
| } elseif (!empty($this->attributes['multiple'])) { | |
| return $this->applyFilters($values); | |
| } elseif (1 == count($values)) { | |
| return $this->applyFilters($values[0]); | |
| } else { | |
| // The <select> is not multiple, but several options are to be | |
| // selected. At least IE and Mozilla select the last selected | |
| // option in this case, we should do the same | |
| foreach ($this->optionContainer->getRecursiveIterator() as $child) { | |
| if (is_array($child) && in_array($child['attr']['value'], $values)) { | |
| $lastValue = $child['attr']['value']; | |
| } | |
| } | |
| return $this->applyFilters($lastValue); | |
| } | |
| } | |
| public function setValue($value) | |
| { | |
| if (is_array($value)) { | |
| $this->values = array_values($value); | |
| } else { | |
| $this->values = array($value); | |
| } | |
| return $this; | |
| } | |
| /** | |
| * Loads <option>s (and <optgroup>s) for select element | |
| * | |
| * The method expects a array of options and optgroups: | |
| * <pre> | |
| * array( | |
| * 'option value 1' => 'option text 1', | |
| * ... | |
| * 'option value N' => 'option text N', | |
| * 'optgroup label 1' => array( | |
| * 'option value' => 'option text', | |
| * ... | |
| * ), | |
| * ... | |
| * ) | |
| * </pre> | |
| * If value is a scalar, then array key is treated as "value" attribute of | |
| * <option> and value as this <option>'s text. If value is an array, then | |
| * key is treated as a "label" attribute of <optgroup> and value as an | |
| * array of <option>s for this <optgroup>. | |
| * | |
| * If you need to specify additional attributes for <option> and <optgroup> | |
| * tags, then you need to use {@link addOption()} and {@link addOptgroup()} | |
| * methods instead of this one. | |
| * | |
| * @param array | |
| * @throws HTML_QuickForm2_InvalidArgumentException if junk is given in $options | |
| * @return HTML_QuickForm2_Element_Select | |
| */ | |
| public function loadOptions(array $options) | |
| { | |
| $this->possibleValues = array(); | |
| $this->optionContainer = new HTML_QuickForm2_Element_Select_OptionContainer( | |
| $this->values, $this->possibleValues | |
| ); | |
| $this->loadOptionsFromArray($this->optionContainer, $options); | |
| return $this; | |
| } | |
| /** | |
| * Adds options from given array into given container | |
| * | |
| * @param HTML_QuickForm2_Element_Select_OptionContainer options will be | |
| * added to this container | |
| * @param array options array | |
| */ | |
| protected function loadOptionsFromArray( | |
| HTML_QuickForm2_Element_Select_OptionContainer $container, $options | |
| ) | |
| { | |
| foreach ($options as $key => $value) { | |
| if (is_array($value)) { | |
| $optgroup = $container->addOptgroup($key); | |
| $this->loadOptionsFromArray($optgroup, $value); | |
| } else { | |
| $container->addOption($value, $key); | |
| } | |
| } | |
| } | |
| /** | |
| * Adds a new option | |
| * | |
| * Please note that if you pass 'selected' attribute in the $attributes | |
| * parameter then this option's value will be added to <select>'s values. | |
| * | |
| * @param string Option text | |
| * @param string 'value' attribute for <option> tag | |
| * @param mixed Additional attributes for <option> tag (either as a | |
| * string or as an associative array) | |
| */ | |
| public function addOption($text, $value, $attributes = null) | |
| { | |
| return $this->optionContainer->addOption($text, $value, $attributes); | |
| } | |
| /** | |
| * Adds a new optgroup | |
| * | |
| * @param string 'label' attribute for optgroup tag | |
| * @param mixed Additional attributes for <optgroup> tag (either as a | |
| * string or as an associative array) | |
| * @return HTML_QuickForm2_Element_Select_Optgroup | |
| */ | |
| public function addOptgroup($label, $attributes = null) | |
| { | |
| return $this->optionContainer->addOptgroup($label, $attributes); | |
| } | |
| public function updateValue() | |
| { | |
| if (!$this->getAttribute('multiple')) { | |
| parent::updateValue(); | |
| } else { | |
| $name = $this->getName(); | |
| foreach ($this->getDataSources() as $ds) { | |
| if (null !== ($value = $ds->getValue($name)) || | |
| $ds instanceof HTML_QuickForm2_DataSource_Submit) | |
| { | |
| $this->setValue(null === $value? array(): $value); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| } | |