FormFieldRegistry.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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 based on the fully qualifed name and its children from the registry.
  43. */
  44. public function remove(string $name)
  45. {
  46. $segments = $this->getSegments($name);
  47. $target = &$this->fields;
  48. while (\count($segments) > 1) {
  49. $path = array_shift($segments);
  50. if (!\is_array($target) || !\array_key_exists($path, $target)) {
  51. return;
  52. }
  53. $target = &$target[$path];
  54. }
  55. unset($target[array_shift($segments)]);
  56. }
  57. /**
  58. * Returns the value of the field based on the fully qualifed name and its children.
  59. *
  60. * @return FormField|FormField[]|FormField[][] The value of the field
  61. *
  62. * @throws \InvalidArgumentException if the field does not exist
  63. */
  64. public function &get(string $name)
  65. {
  66. $segments = $this->getSegments($name);
  67. $target = &$this->fields;
  68. while ($segments) {
  69. $path = array_shift($segments);
  70. if (!\is_array($target) || !\array_key_exists($path, $target)) {
  71. throw new \InvalidArgumentException(sprintf('Unreachable field "%s".', $path));
  72. }
  73. $target = &$target[$path];
  74. }
  75. return $target;
  76. }
  77. /**
  78. * Tests whether the form has the given field based on the fully qualified name.
  79. *
  80. * @return bool Whether the form has the given field
  81. */
  82. public function has(string $name): bool
  83. {
  84. try {
  85. $this->get($name);
  86. return true;
  87. } catch (\InvalidArgumentException $e) {
  88. return false;
  89. }
  90. }
  91. /**
  92. * Set the value of a field based on the fully qualified name and its children.
  93. *
  94. * @param mixed $value The value
  95. *
  96. * @throws \InvalidArgumentException if the field does not exist
  97. */
  98. public function set(string $name, $value)
  99. {
  100. $target = &$this->get($name);
  101. if ((!\is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) {
  102. $target->setValue($value);
  103. } elseif (\is_array($value)) {
  104. $registry = new static();
  105. $registry->base = $name;
  106. $registry->fields = $value;
  107. foreach ($registry->all() as $k => $v) {
  108. $this->set($k, $v);
  109. }
  110. } else {
  111. throw new \InvalidArgumentException(sprintf('Cannot set value on a compound field "%s".', $name));
  112. }
  113. }
  114. /**
  115. * Returns the list of field with their value.
  116. *
  117. * @return FormField[] The list of fields as [string] Fully qualified name => (mixed) value)
  118. */
  119. public function all(): array
  120. {
  121. return $this->walk($this->fields, $this->base);
  122. }
  123. /**
  124. * Transforms a PHP array in a list of fully qualified name / value.
  125. */
  126. private function walk(array $array, ?string $base = '', array &$output = []): array
  127. {
  128. foreach ($array as $k => $v) {
  129. $path = empty($base) ? $k : sprintf('%s[%s]', $base, $k);
  130. if (\is_array($v)) {
  131. $this->walk($v, $path, $output);
  132. } else {
  133. $output[$path] = $v;
  134. }
  135. }
  136. return $output;
  137. }
  138. /**
  139. * Splits a field name into segments as a web browser would do.
  140. *
  141. * getSegments('base[foo][3][]') = ['base', 'foo, '3', ''];
  142. *
  143. * @return string[] The list of segments
  144. */
  145. private function getSegments(string $name): array
  146. {
  147. if (preg_match('/^(?P<base>[^[]+)(?P<extra>(\[.*)|$)/', $name, $m)) {
  148. $segments = [$m['base']];
  149. while (!empty($m['extra'])) {
  150. $extra = $m['extra'];
  151. if (preg_match('/^\[(?P<segment>.*?)\](?P<extra>.*)$/', $extra, $m)) {
  152. $segments[] = $m['segment'];
  153. } else {
  154. $segments[] = $extra;
  155. }
  156. }
  157. return $segments;
  158. }
  159. return [$name];
  160. }
  161. }