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