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