PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/HTML/QuickForm2/Container.php

https://github.com/quarkness/piwik
PHP | 487 lines | 242 code | 33 blank | 212 comment | 33 complexity | 907e655970384bc7761709412e756b25 MD5 | raw file
  1. <?php
  2. /**
  3. * Base class for simple HTML_QuickForm2 containers
  4. *
  5. * PHP version 5
  6. *
  7. * LICENSE:
  8. *
  9. * Copyright (c) 2006-2010, Alexey Borzov <avb@php.net>,
  10. * Bertrand Mansion <golgote@mamasam.com>
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions
  15. * are met:
  16. *
  17. * * Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. * * Redistributions in binary form must reproduce the above copyright
  20. * notice, this list of conditions and the following disclaimer in the
  21. * documentation and/or other materials provided with the distribution.
  22. * * The names of the authors may not be used to endorse or promote products
  23. * derived from this software without specific prior written permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  26. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  27. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  28. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  29. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  30. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  31. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  32. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  33. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  34. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  35. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. * @category HTML
  38. * @package HTML_QuickForm2
  39. * @author Alexey Borzov <avb@php.net>
  40. * @author Bertrand Mansion <golgote@mamasam.com>
  41. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  42. * @version SVN: $Id: Container.php 300722 2010-06-24 10:15:52Z mansion $
  43. * @link http://pear.php.net/package/HTML_QuickForm2
  44. */
  45. /**
  46. * Base class for all HTML_QuickForm2 elements
  47. */
  48. // require_once 'HTML/QuickForm2/Node.php';
  49. /**
  50. * Abstract base class for simple QuickForm2 containers
  51. *
  52. * @category HTML
  53. * @package HTML_QuickForm2
  54. * @author Alexey Borzov <avb@php.net>
  55. * @author Bertrand Mansion <golgote@mamasam.com>
  56. * @version Release: @package_version@
  57. */
  58. abstract class HTML_QuickForm2_Container extends HTML_QuickForm2_Node
  59. implements IteratorAggregate, Countable
  60. {
  61. /**
  62. * Array of elements contained in this container
  63. * @var array
  64. */
  65. protected $elements = array();
  66. public function setName($name)
  67. {
  68. $this->attributes['name'] = (string)$name;
  69. return $this;
  70. }
  71. public function toggleFrozen($freeze = null)
  72. {
  73. if (null !== $freeze) {
  74. foreach ($this as $child) {
  75. $child->toggleFrozen($freeze);
  76. }
  77. }
  78. return parent::toggleFrozen($freeze);
  79. }
  80. public function persistentFreeze($persistent = null)
  81. {
  82. if (null !== $persistent) {
  83. foreach ($this as $child) {
  84. $child->persistentFreeze($persistent);
  85. }
  86. }
  87. return parent::persistentFreeze($persistent);
  88. }
  89. /**
  90. * Whether container prepends its name to names of contained elements
  91. *
  92. * @return bool
  93. */
  94. protected function prependsName()
  95. {
  96. return false;
  97. }
  98. /**
  99. * Returns the element's value
  100. *
  101. * The default implementation for Containers is to return an array with
  102. * contained elements' values. The array is indexed the same way $_GET and
  103. * $_POST arrays would be for these elements.
  104. *
  105. * @return array|null
  106. */
  107. public function getValue()
  108. {
  109. $values = array();
  110. foreach ($this as $child) {
  111. $value = $child->getValue();
  112. if (null !== $value) {
  113. if ($child instanceof HTML_QuickForm2_Container
  114. && !$child->prependsName()
  115. ) {
  116. $values = self::arrayMerge($values, $value);
  117. } else {
  118. $name = $child->getName();
  119. if (!strpos($name, '[')) {
  120. $values[$name] = $value;
  121. } else {
  122. $tokens = explode('[', str_replace(']', '', $name));
  123. $valueAry =& $values;
  124. do {
  125. $token = array_shift($tokens);
  126. if (!isset($valueAry[$token])) {
  127. $valueAry[$token] = array();
  128. }
  129. $valueAry =& $valueAry[$token];
  130. } while (count($tokens) > 1);
  131. $valueAry[$tokens[0]] = $value;
  132. }
  133. }
  134. }
  135. }
  136. return empty($values)? null: $this->applyFilters($values);
  137. }
  138. /**
  139. * Merges two arrays
  140. *
  141. * Merges two arrays like the PHP function array_merge_recursive does,
  142. * the difference being that existing integer keys will not be renumbered.
  143. *
  144. * @param array
  145. * @param array
  146. * @return array resulting array
  147. */
  148. protected static function arrayMerge($a, $b)
  149. {
  150. foreach ($b as $k => $v) {
  151. if (!is_array($v) || isset($a[$k]) && !is_array($a[$k])) {
  152. $a[$k] = $v;
  153. } else {
  154. $a[$k] = self::arrayMerge(isset($a[$k])? $a[$k]: array(), $v);
  155. }
  156. }
  157. return $a;
  158. }
  159. /**
  160. * Returns an array of this container's elements
  161. *
  162. * @return array Container elements
  163. */
  164. public function getElements()
  165. {
  166. return $this->elements;
  167. }
  168. /**
  169. * Appends an element to the container
  170. *
  171. * If the element was previously added to the container or to another
  172. * container, it is first removed there.
  173. *
  174. * @param HTML_QuickForm2_Node Element to add
  175. * @return HTML_QuickForm2_Node Added element
  176. * @throws HTML_QuickForm2_InvalidArgumentException
  177. */
  178. public function appendChild(HTML_QuickForm2_Node $element)
  179. {
  180. if ($this === $element->getContainer()) {
  181. $this->removeChild($element);
  182. }
  183. $element->setContainer($this);
  184. $this->elements[] = $element;
  185. return $element;
  186. }
  187. /**
  188. * Appends an element to the container (possibly creating it first)
  189. *
  190. * If the first parameter is an instance of HTML_QuickForm2_Node then all
  191. * other parameters are ignored and the method just calls {@link appendChild()}.
  192. * In the other case the element is first created via
  193. * {@link HTML_QuickForm2_Factory::createElement()} and then added via the
  194. * same method. This is a convenience method to reduce typing and ease
  195. * porting from HTML_QuickForm.
  196. *
  197. * @param string|HTML_QuickForm2_Node Either type name (treated
  198. * case-insensitively) or an element instance
  199. * @param mixed Element name
  200. * @param mixed Element attributes
  201. * @param array Element-specific data
  202. * @return HTML_QuickForm2_Node Added element
  203. * @throws HTML_QuickForm2_InvalidArgumentException
  204. * @throws HTML_QuickForm2_NotFoundException
  205. */
  206. public function addElement($elementOrType, $name = null, $attributes = null,
  207. array $data = array())
  208. {
  209. if ($elementOrType instanceof HTML_QuickForm2_Node) {
  210. return $this->appendChild($elementOrType);
  211. } else {
  212. return $this->appendChild(HTML_QuickForm2_Factory::createElement(
  213. $elementOrType, $name, $attributes, $data
  214. ));
  215. }
  216. }
  217. /**
  218. * Removes the element from this container
  219. *
  220. * If the reference object is not given, the element will be appended.
  221. *
  222. * @param HTML_QuickForm2_Node Element to remove
  223. * @return HTML_QuickForm2_Node Removed object
  224. */
  225. public function removeChild(HTML_QuickForm2_Node $element)
  226. {
  227. if ($element->getContainer() !== $this) {
  228. throw new HTML_QuickForm2_NotFoundException(
  229. "Element with name '".$element->getName()."' was not found"
  230. );
  231. }
  232. foreach ($this as $key => $child){
  233. if ($child === $element) {
  234. unset($this->elements[$key]);
  235. $element->setContainer(null);
  236. break;
  237. }
  238. }
  239. return $element;
  240. }
  241. /**
  242. * Returns an element if its id is found
  243. *
  244. * @param string Element id to find
  245. * @return HTML_QuickForm2_Node|null
  246. */
  247. public function getElementById($id)
  248. {
  249. foreach ($this->getRecursiveIterator() as $element) {
  250. if ($id == $element->getId()) {
  251. return $element;
  252. }
  253. }
  254. return null;
  255. }
  256. /**
  257. * Returns an array of elements which name corresponds to element
  258. *
  259. * @param string Elements name to find
  260. * @return array
  261. */
  262. public function getElementsByName($name)
  263. {
  264. $found = array();
  265. foreach ($this->getRecursiveIterator() as $element) {
  266. if ($element->getName() == $name) {
  267. $found[] = $element;
  268. }
  269. }
  270. return $found;
  271. }
  272. /**
  273. * Inserts an element in the container
  274. *
  275. * If the reference object is not given, the element will be appended.
  276. *
  277. * @param HTML_QuickForm2_Node Element to insert
  278. * @param HTML_QuickForm2_Node Reference to insert before
  279. * @return HTML_QuickForm2_Node Inserted element
  280. */
  281. public function insertBefore(HTML_QuickForm2_Node $element, HTML_QuickForm2_Node $reference = null)
  282. {
  283. if (null === $reference) {
  284. return $this->appendChild($element);
  285. }
  286. $offset = 0;
  287. foreach ($this as $child) {
  288. if ($child === $reference) {
  289. if ($this === $element->getContainer()) {
  290. $this->removeChild($element);
  291. }
  292. $element->setContainer($this);
  293. array_splice($this->elements, $offset, 0, array($element));
  294. return $element;
  295. }
  296. $offset++;
  297. }
  298. throw new HTML_QuickForm2_NotFoundException(
  299. "Reference element with name '".$reference->getName()."' was not found"
  300. );
  301. }
  302. /**
  303. * Returns a recursive iterator for the container elements
  304. *
  305. * @return HTML_QuickForm2_ContainerIterator
  306. */
  307. public function getIterator()
  308. {
  309. return new HTML_QuickForm2_ContainerIterator($this);
  310. }
  311. /**
  312. * Returns a recursive iterator iterator for the container elements
  313. *
  314. * @param int mode passed to RecursiveIteratorIterator
  315. * @return RecursiveIteratorIterator
  316. */
  317. public function getRecursiveIterator($mode = RecursiveIteratorIterator::SELF_FIRST)
  318. {
  319. return new RecursiveIteratorIterator(
  320. new HTML_QuickForm2_ContainerIterator($this), $mode
  321. );
  322. }
  323. /**
  324. * Returns the number of elements in the container
  325. *
  326. * @return int
  327. */
  328. public function count()
  329. {
  330. return count($this->elements);
  331. }
  332. /**
  333. * Called when the element needs to update its value from form's data sources
  334. *
  335. * The default behaviour is just to call the updateValue() methods of
  336. * contained elements, since default Container doesn't have any value itself
  337. */
  338. public function updateValue()
  339. {
  340. foreach ($this as $child) {
  341. $child->updateValue();
  342. }
  343. }
  344. /**
  345. * Performs the server-side validation
  346. *
  347. * This method also calls validate() on all contained elements.
  348. *
  349. * @return boolean Whether the container and all contained elements are valid
  350. */
  351. protected function validate()
  352. {
  353. $valid = parent::validate();
  354. foreach ($this as $child) {
  355. $valid = $child->validate() && $valid;
  356. }
  357. return $valid;
  358. }
  359. /**
  360. * Appends an element to the container, creating it first
  361. *
  362. * The element will be created via {@link HTML_QuickForm2_Factory::createElement()}
  363. * and then added via the {@link appendChild()} method.
  364. * The element type is deduced from the method name.
  365. * This is a convenience method to reduce typing.
  366. *
  367. * @param mixed Element name
  368. * @param mixed Element attributes
  369. * @param array Element-specific data
  370. * @return HTML_QuickForm2_Node Added element
  371. * @throws HTML_QuickForm2_InvalidArgumentException
  372. * @throws HTML_QuickForm2_NotFoundException
  373. */
  374. public function __call($m, $a)
  375. {
  376. if (preg_match('/^(add)([a-zA-Z0-9_]+)$/', $m, $match)) {
  377. if ($match[1] == 'add') {
  378. $type = strtolower($match[2]);
  379. $name = isset($a[0]) ? $a[0] : null;
  380. $attr = isset($a[1]) ? $a[1] : null;
  381. $data = isset($a[2]) ? $a[2] : array();
  382. return $this->addElement($type, $name, $attr, $data);
  383. }
  384. }
  385. trigger_error("Fatal error: Call to undefined method ".get_class($this)."::".$m."()", E_USER_ERROR);
  386. }
  387. /**
  388. * Renders the container using the given renderer
  389. *
  390. * @param HTML_QuickForm2_Renderer Renderer instance
  391. * @return HTML_QuickForm2_Renderer
  392. */
  393. public function render(HTML_QuickForm2_Renderer $renderer)
  394. {
  395. foreach ($this->rules as $rule) {
  396. if ($rule[1] & HTML_QuickForm2_Rule::RUNAT_CLIENT) {
  397. $renderer->getJavascriptBuilder()->addRule($rule[0]);
  398. }
  399. }
  400. $renderer->startContainer($this);
  401. foreach ($this as $element) {
  402. $element->render($renderer);
  403. }
  404. $renderer->finishContainer($this);
  405. return $renderer;
  406. }
  407. public function __toString()
  408. {
  409. // require_once 'HTML/QuickForm2/Renderer.php';
  410. return $this->render(HTML_QuickForm2_Renderer::factory('default'))->__toString();
  411. }
  412. /**
  413. * Returns Javascript code for getting the element's value
  414. *
  415. * @return string
  416. */
  417. public function getJavascriptValue()
  418. {
  419. $args = array();
  420. foreach ($this as $child) {
  421. if ($child instanceof HTML_QuickForm2_Container) {
  422. $args[] = $child->getJavascriptValue();
  423. } else {
  424. $args[] = "'" . $child->getId() . "'";
  425. }
  426. }
  427. return 'qf.form.getContainerValue(' . implode(', ', $args) . ')';
  428. }
  429. }
  430. /**
  431. * Implements a recursive iterator for the container elements
  432. *
  433. * @category HTML
  434. * @package HTML_QuickForm2
  435. * @author Alexey Borzov <avb@php.net>
  436. * @author Bertrand Mansion <golgote@mamasam.com>
  437. * @version Release: @package_version@
  438. */
  439. class HTML_QuickForm2_ContainerIterator extends RecursiveArrayIterator implements RecursiveIterator
  440. {
  441. public function __construct(HTML_QuickForm2_Container $container)
  442. {
  443. parent::__construct($container->getElements());
  444. }
  445. public function hasChildren()
  446. {
  447. return $this->current() instanceof HTML_QuickForm2_Container;
  448. }
  449. public function getChildren()
  450. {
  451. return new HTML_QuickForm2_ContainerIterator($this->current());
  452. }
  453. }
  454. ?>