1<?php 2/** 3 * Base class for HTML_QuickForm2 groups 4 * 5 * PHP version 5 6 * 7 * LICENSE 8 * 9 * This source file is subject to BSD 3-Clause License that is bundled 10 * with this package in the file LICENSE and available at the URL 11 * https://raw.githubusercontent.com/pear/HTML_QuickForm2/trunk/docs/LICENSE 12 * 13 * @category HTML 14 * @package HTML_QuickForm2 15 * @author Alexey Borzov <avb@php.net> 16 * @author Bertrand Mansion <golgote@mamasam.com> 17 * @copyright 2006-2021 Alexey Borzov <avb@php.net>, Bertrand Mansion <golgote@mamasam.com> 18 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License 19 * @link https://pear.php.net/package/HTML_QuickForm2 20 */ 21 22/** 23 * Base class for all HTML_QuickForm2 containers 24 */ 25require_once 'HTML/QuickForm2/Container.php'; 26 27/** 28 * Base class for QuickForm2 groups of elements 29 * 30 * @category HTML 31 * @package HTML_QuickForm2 32 * @author Alexey Borzov <avb@php.net> 33 * @author Bertrand Mansion <golgote@mamasam.com> 34 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License 35 * @version Release: 2.2.2 36 * @link https://pear.php.net/package/HTML_QuickForm2 37 */ 38class HTML_QuickForm2_Container_Group extends HTML_QuickForm2_Container 39{ 40 /** 41 * Group name 42 * If set, group name will be used as prefix for contained 43 * element names, like groupname[elementname]. 44 * @var string 45 */ 46 protected $name; 47 48 /** 49 * Previous group name 50 * Stores the previous group name when the group name is changed. 51 * Used to restore children names if necessary. 52 * @var string 53 */ 54 protected $previousName; 55 56 public function getType() 57 { 58 return 'group'; 59 } 60 61 protected function prependsName() 62 { 63 return strlen($this->name) > 0; 64 } 65 66 protected function getChildValues($filtered = false) 67 { 68 $value = parent::getChildValues($filtered); 69 if (!$this->prependsName()) { 70 return $value; 71 72 } elseif (!strpos($this->getName(), '[')) { 73 return isset($value[$this->getName()])? $value[$this->getName()]: null; 74 75 } else { 76 $tokens = explode('[', str_replace(']', '', $this->getName())); 77 $valueAry =& $value; 78 do { 79 $token = array_shift($tokens); 80 if (!isset($valueAry[$token])) { 81 return null; 82 } 83 $valueAry =& $valueAry[$token]; 84 } while ($tokens); 85 return $valueAry; 86 } 87 } 88 89 public function setValue($value) 90 { 91 // Prepare a mapper for element names as array 92 $prefixLength = $this->prependsName() ? substr_count($this->getName(), '[') + 1 : 0; 93 $nameParts = $groupValues = []; 94 95 /* @var $child HTML_QuickForm2_Node */ 96 foreach ($this as $i => $child) { 97 $tokens = explode('[', str_replace(']', '', $child->getName())); 98 if ($prefixLength) { 99 $tokens = array_slice($tokens, $prefixLength); 100 } 101 $nameParts[] = $tokens; 102 if ($child instanceof self) { 103 $groupValues[$i] = []; 104 } 105 } 106 107 // Iterate over values to find corresponding element 108 109 $index = 0; 110 111 foreach ((array)$value as $k => $v) { 112 foreach ($nameParts as $i => $tokens) { 113 $val = [$k => $v]; 114 do { 115 $token = array_shift($tokens); 116 $numeric = false; 117 if ($token == "") { 118 // special case for a group of checkboxes 119 if (empty($tokens) && is_array($val) 120 && $this->elements[$i] instanceof HTML_QuickForm2_Element_InputCheckbox 121 ) { 122 if (in_array($this->elements[$i]->getAttribute('value'), 123 array_map('strval', $val), true) 124 ) { 125 $this->elements[$i]->setAttribute('checked'); 126 // don't want to remove 'checked' on next iteration 127 unset($nameParts[$i]); 128 } else { 129 $this->elements[$i]->removeAttribute('checked'); 130 } 131 continue 2; 132 } 133 // Deal with numeric indexes in values 134 $token = $index; 135 $numeric = true; 136 } 137 if (!is_array($val) || !isset($val[$token])) { 138 // Not found, skip next iterations 139 continue 2; 140 141 } else { 142 // Found a value 143 $val = $val[$token]; 144 if ($numeric) { 145 $index += 1; 146 } 147 } 148 149 } while (!empty($tokens)); 150 151 // Found a value corresponding to element name 152 $child = $this->elements[$i]; 153 if ($child instanceof self) { 154 $groupValues[$i] += (array)$val; 155 } else { 156 $child->setValue($val); 157 // Speed up next iterations 158 unset($nameParts[$i]); 159 } 160 if (!($child instanceof HTML_QuickForm2_Element_InputRadio)) { 161 break; 162 } 163 } 164 } 165 foreach (array_keys($nameParts) as $i) { 166 $this->elements[$i]->setValue(isset($groupValues[$i]) ? $groupValues[$i] : null); 167 } 168 169 return $this; 170 } 171 172 173 public function getName() 174 { 175 return $this->name; 176 } 177 178 public function setName($name) 179 { 180 $this->previousName = $this->name; 181 $this->name = $name; 182 foreach ($this as $child) { 183 $this->renameChild($child); 184 } 185 return $this; 186 } 187 188 /** 189 * Prepends group's name to contained element's name 190 * 191 * Used when adding an element to the group or changing group's name 192 * 193 * @param HTML_QuickForm2_Node $element 194 * 195 * @return HTML_QuickForm2_Node 196 */ 197 protected function renameChild(HTML_QuickForm2_Node $element) 198 { 199 $tokens = explode('[', str_replace(']', '', $element->getName())); 200 // Child has already been renamed by its group before 201 if ($this === $element->getContainer() && strlen($this->previousName)) { 202 $gtokens = explode('[', str_replace(']', '', $this->previousName)); 203 if ($gtokens === array_slice($tokens, 0, count($gtokens))) { 204 array_splice($tokens, 0, count($gtokens)); 205 } 206 } 207 208 if (strlen($this->name)) { 209 $element->setName($this->name . '[' . implode('][', $tokens) . ']'); 210 } elseif (strlen($this->previousName)) { 211 $elname = array_shift($tokens); 212 foreach ($tokens as $token) { 213 $elname .= '[' . $token . ']'; 214 } 215 $element->setName($elname); 216 } 217 218 return $element; 219 } 220 221 /** 222 * Appends an element to the container 223 * 224 * If the element was previously added to the container or to another 225 * container, it is first removed there. 226 * 227 * @param HTML_QuickForm2_Node $element Element to add 228 * 229 * @return HTML_QuickForm2_Node Added element 230 * @throws HTML_QuickForm2_InvalidArgumentException 231 */ 232 public function appendChild(HTML_QuickForm2_Node $element) 233 { 234 if (null !== ($container = $element->getContainer())) { 235 $container->removeChild($element); 236 } 237 // Element can be renamed only after being removed from container 238 $this->renameChild($element); 239 240 $element->setContainer($this); 241 $this->elements[] = $element; 242 return $element; 243 } 244 245 /** 246 * Removes the element from this container 247 * 248 * If the reference object is not given, the element will be appended. 249 * 250 * @param HTML_QuickForm2_Node $element Element to remove 251 * 252 * @return HTML_QuickForm2_Node Removed object 253 */ 254 public function removeChild(HTML_QuickForm2_Node $element) 255 { 256 $element = parent::removeChild($element); 257 if ($this->prependsName()) { 258 $name = preg_replace( 259 '/^' . preg_quote($this->getName(), '/') . '\[([^\]]*)\]/', 260 '\1', $element->getName() 261 ); 262 $element->setName($name); 263 } 264 return $element; 265 } 266 267 /** 268 * Inserts an element in the container 269 * 270 * If the reference object is not given, the element will be appended. 271 * 272 * @param HTML_QuickForm2_Node $element Element to insert 273 * @param HTML_QuickForm2_Node $reference Reference to insert before 274 * 275 * @return HTML_QuickForm2_Node Inserted element 276 */ 277 public function insertBefore(HTML_QuickForm2_Node $element, HTML_QuickForm2_Node $reference = null) 278 { 279 if (null === $reference) { 280 return $this->appendChild($element); 281 } 282 return parent::insertBefore($this->renameChild($element), $reference); 283 } 284 285 /** 286 * Sets string(s) to separate grouped elements 287 * 288 * @param string|array $separator Use a string for one separator, array for 289 * alternating separators 290 * 291 * @return $this 292 */ 293 public function setSeparator($separator) 294 { 295 $this->data['separator'] = $separator; 296 return $this; 297 } 298 299 /** 300 * Returns string(s) to separate grouped elements 301 * 302 * @return string|array Separator, null if not set 303 */ 304 public function getSeparator() 305 { 306 return isset($this->data['separator'])? $this->data['separator']: null; 307 } 308 309 /** 310 * Renders the group using the given renderer 311 * 312 * @param HTML_QuickForm2_Renderer $renderer 313 * 314 * @return HTML_QuickForm2_Renderer 315 */ 316 public function render(HTML_QuickForm2_Renderer $renderer) 317 { 318 $renderer->startGroup($this); 319 foreach ($this as $element) { 320 $element->render($renderer); 321 } 322 $this->renderClientRules($renderer->getJavascriptBuilder()); 323 $renderer->finishGroup($this); 324 return $renderer; 325 } 326 327 public function __toString() 328 { 329 HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_Renderer'); 330 331 $renderer = $this->render( 332 HTML_QuickForm2_Renderer::factory('default') 333 ->setTemplateForId($this->getId(), '{content}') 334 ); 335 return $renderer->__toString() 336 . $renderer->getJavascriptBuilder()->getSetupCode(null, true); 337 } 338} 339?>