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