FormFieldRegistry.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\DomCrawler;
  11. use Symfony\Component\DomCrawler\Field\FormField;
  12. /**
  13. * This is an internal class that must not be used directly.
  14. *
  15. * @internal
  16. */
  17. class FormFieldRegistry
  18. {
  19. private $fields = [];
  20. private $base;
  21. /**
  22. * Adds a field to the registry.
  23. */
  24. public function add(FormField $field)
  25. {
  26. $segments = $this->getSegments($field->getName());
  27. $target = &$this->fields;
  28. while ($segments) {
  29. if (!\is_array($target)) {
  30. $target = [];
  31. }
  32. $path = array_shift($segments);
  33. if ('' === $path) {
  34. $target = &$target[];
  35. } else {
  36. $target = &$target[$path];
  37. }
  38. }
  39. $target = $field;
  40. }
  41. /**
  42. * Removes a field and its children from the registry.
  43. *
  44. * @param string $name The fully qualified name of the base field
  45. */
  46. public function remove($name)
  47. {
  48. $segments = $this->getSegments($name);
  49. $target = &$this->fields;
  50. while (\count($segments) > 1) {
  51. $path = array_shift($segments);
  52. if (!\array_key_exists($path, $target)) {
  53. return;
  54. }
  55. $target = &$target[$path];
  56. }
  57. unset($target[array_shift($segments)]);
  58. }
  59. /**
  60. * Returns the value of the field and its children.
  61. *
  62. * @param string $name The fully qualified name of the field
  63. *
  64. * @return mixed The value of the field
  65. *
  66. * @throws \InvalidArgumentException if the field does not exist
  67. */
  68. public function &get($name)
  69. {
  70. $segments = $this->getSegments($name);
  71. $target = &$this->fields;
  72. while ($segments) {
  73. $path = array_shift($segments);
  74. if (!\array_key_exists($path, $target)) {
  75. throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path));
  76. }
  77. $target = &$target[$path];
  78. }
  79. return $target;
  80. }
  81. /**
  82. * Tests whether the form has the given field.
  83. *
  84. * @param string $name The fully qualified name of the field
  85. *
  86. * @return bool Whether the form has the given field
  87. */
  88. public function has($name)
  89. {
  90. try {
  91. $this->get($name);
  92. return true;
  93. } catch (\InvalidArgumentException $e) {
  94. return false;
  95. }
  96. }
  97. /**
  98. * Set the value of a field and its children.
  99. *
  100. * @param string $name The fully qualified name of the field
  101. * @param mixed $value The value
  102. *
  103. * @throws \InvalidArgumentException if the field does not exist
  104. */
  105. public function set($name, $value)
  106. {
  107. $target = &$this->get($name);
  108. if ((!\is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) {
  109. $target->setValue($value);
  110. } elseif (\is_array($value)) {
  111. $fields = self::create($name, $value);
  112. foreach ($fields->all() as $k => $v) {
  113. $this->set($k, $v);
  114. }
  115. } else {
  116. throw new \InvalidArgumentException(sprintf('Cannot set value on a compound field "%s".', $name));
  117. }
  118. }
  119. /**
  120. * Returns the list of field with their value.
  121. *
  122. * @return FormField[] The list of fields as [string] Fully qualified name => (mixed) value)
  123. */
  124. public function all()
  125. {
  126. return $this->walk($this->fields, $this->base);
  127. }
  128. /**
  129. * Creates an instance of the class.
  130. *
  131. * This function is made private because it allows overriding the $base and
  132. * the $values properties without any type checking.
  133. *
  134. * @param string $base The fully qualified name of the base field
  135. * @param array $values The values of the fields
  136. *
  137. * @return static
  138. */
  139. private static function create($base, array $values)
  140. {
  141. $registry = new static();
  142. $registry->base = $base;
  143. $registry->fields = $values;
  144. return $registry;
  145. }
  146. /**
  147. * Transforms a PHP array in a list of fully qualified name / value.
  148. *
  149. * @param array $array The PHP array
  150. * @param string $base The name of the base field
  151. * @param array $output The initial values
  152. *
  153. * @return array The list of fields as [string] Fully qualified name => (mixed) value)
  154. */
  155. private function walk(array $array, $base = '', array &$output = [])
  156. {
  157. foreach ($array as $k => $v) {
  158. $path = empty($base) ? $k : sprintf('%s[%s]', $base, $k);
  159. if (\is_array($v)) {
  160. $this->walk($v, $path, $output);
  161. } else {
  162. $output[$path] = $v;
  163. }
  164. }
  165. return $output;
  166. }
  167. /**
  168. * Splits a field name into segments as a web browser would do.
  169. *
  170. * getSegments('base[foo][3][]') = ['base', 'foo, '3', ''];
  171. *
  172. * @param string $name The name of the field
  173. *
  174. * @return string[] The list of segments
  175. */
  176. private function getSegments($name)
  177. {
  178. if (preg_match('/^(?P<base>[^[]+)(?P<extra>(\[.*)|$)/', $name, $m)) {
  179. $segments = [$m['base']];
  180. while (!empty($m['extra'])) {
  181. $extra = $m['extra'];
  182. if (preg_match('/^\[(?P<segment>.*?)\](?P<extra>.*)$/', $extra, $m)) {
  183. $segments[] = $m['segment'];
  184. } else {
  185. $segments[] = $extra;
  186. }
  187. }
  188. return $segments;
  189. }
  190. return [$name];
  191. }
  192. }