|
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\Config\Definition; |
|
13 |
|
14 use Symfony\Component\Config\Definition\Exception\Exception; |
|
15 use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; |
|
16 use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; |
|
17 use Symfony\Component\Config\Definition\Builder\NodeDefinition; |
|
18 |
|
19 /** |
|
20 * The base node class |
|
21 * |
|
22 * @author Johannes M. Schmitt <schmittjoh@gmail.com> |
|
23 */ |
|
24 abstract class BaseNode implements NodeInterface |
|
25 { |
|
26 protected $name; |
|
27 protected $parent; |
|
28 protected $normalizationClosures; |
|
29 protected $finalValidationClosures; |
|
30 protected $allowOverwrite; |
|
31 protected $required; |
|
32 protected $equivalentValues; |
|
33 |
|
34 /** |
|
35 * Constructor. |
|
36 * |
|
37 * @param string $name The name of the node |
|
38 * @param NodeInterface $parent The parent of this node |
|
39 * @throws \InvalidArgumentException if the name contains a period. |
|
40 */ |
|
41 public function __construct($name, NodeInterface $parent = null) |
|
42 { |
|
43 if (false !== strpos($name, '.')) { |
|
44 throw new \InvalidArgumentException('The name must not contain ".".'); |
|
45 } |
|
46 |
|
47 $this->name = $name; |
|
48 $this->parent = $parent; |
|
49 $this->normalizationClosures = array(); |
|
50 $this->finalValidationClosures = array(); |
|
51 $this->allowOverwrite = true; |
|
52 $this->required = false; |
|
53 $this->equivalentValues = array(); |
|
54 } |
|
55 |
|
56 /** |
|
57 * Adds an equivalent value. |
|
58 * |
|
59 * @param mixed $originalValue |
|
60 * @param mixed $equivalentValue |
|
61 */ |
|
62 public function addEquivalentValue($originalValue, $equivalentValue) |
|
63 { |
|
64 $this->equivalentValues[] = array($originalValue, $equivalentValue); |
|
65 } |
|
66 |
|
67 /** |
|
68 * Set this node as required. |
|
69 * |
|
70 * @param Boolean $boolean Required node |
|
71 */ |
|
72 public function setRequired($boolean) |
|
73 { |
|
74 $this->required = (Boolean) $boolean; |
|
75 } |
|
76 |
|
77 /** |
|
78 * Sets if this node can be overridden. |
|
79 * |
|
80 * @param Boolean $allow |
|
81 */ |
|
82 public function setAllowOverwrite($allow) |
|
83 { |
|
84 $this->allowOverwrite = (Boolean) $allow; |
|
85 } |
|
86 |
|
87 /** |
|
88 * Sets the closures used for normalization. |
|
89 * |
|
90 * @param array $closures An array of Closures used for normalization |
|
91 */ |
|
92 public function setNormalizationClosures(array $closures) |
|
93 { |
|
94 $this->normalizationClosures = $closures; |
|
95 } |
|
96 |
|
97 /** |
|
98 * Sets the closures used for final validation. |
|
99 * |
|
100 * @param array $closures An array of Closures used for final validation |
|
101 */ |
|
102 public function setFinalValidationClosures(array $closures) |
|
103 { |
|
104 $this->finalValidationClosures = $closures; |
|
105 } |
|
106 |
|
107 /** |
|
108 * Checks if this node is required. |
|
109 * |
|
110 * @return Boolean |
|
111 */ |
|
112 public function isRequired() |
|
113 { |
|
114 return $this->required; |
|
115 } |
|
116 |
|
117 /** |
|
118 * Returns the name of this node |
|
119 * |
|
120 * @return string The Node's name. |
|
121 */ |
|
122 public function getName() |
|
123 { |
|
124 return $this->name; |
|
125 } |
|
126 |
|
127 /** |
|
128 * Retrieves the path of this node. |
|
129 * |
|
130 * @return string The Node's path |
|
131 */ |
|
132 public function getPath() |
|
133 { |
|
134 $path = $this->name; |
|
135 |
|
136 if (null !== $this->parent) { |
|
137 $path = $this->parent->getPath().'.'.$path; |
|
138 } |
|
139 |
|
140 return $path; |
|
141 } |
|
142 |
|
143 /** |
|
144 * Merges two values together. |
|
145 * |
|
146 * @param mixed $leftSide |
|
147 * @param mixed $rightSide |
|
148 * @return mixed The merged value |
|
149 * @throws ForbiddenOverwriteException |
|
150 */ |
|
151 public final function merge($leftSide, $rightSide) |
|
152 { |
|
153 if (!$this->allowOverwrite) { |
|
154 throw new ForbiddenOverwriteException(sprintf( |
|
155 'Configuration path "%s" cannot be overwritten. You have to ' |
|
156 .'define all options for this path, and any of its sub-paths in ' |
|
157 .'one configuration section.', |
|
158 $this->getPath() |
|
159 )); |
|
160 } |
|
161 |
|
162 $this->validateType($leftSide); |
|
163 $this->validateType($rightSide); |
|
164 |
|
165 return $this->mergeValues($leftSide, $rightSide); |
|
166 } |
|
167 |
|
168 /** |
|
169 * Normalizes a value, applying all normalization closures. |
|
170 * |
|
171 * @param mixed $value Value to normalize. |
|
172 * |
|
173 * @return mixed The normalized value. |
|
174 */ |
|
175 public final function normalize($value) |
|
176 { |
|
177 // run custom normalization closures |
|
178 foreach ($this->normalizationClosures as $closure) { |
|
179 $value = $closure($value); |
|
180 } |
|
181 |
|
182 // replace value with their equivalent |
|
183 foreach ($this->equivalentValues as $data) { |
|
184 if ($data[0] === $value) { |
|
185 $value = $data[1]; |
|
186 } |
|
187 } |
|
188 |
|
189 // validate type |
|
190 $this->validateType($value); |
|
191 |
|
192 // normalize value |
|
193 return $this->normalizeValue($value); |
|
194 } |
|
195 |
|
196 /** |
|
197 * Finalizes a value, applying all finalization closures. |
|
198 * |
|
199 * @param mixed $value The value to finalize |
|
200 * @return mixed The finalized value |
|
201 */ |
|
202 public final function finalize($value) |
|
203 { |
|
204 $this->validateType($value); |
|
205 |
|
206 $value = $this->finalizeValue($value); |
|
207 |
|
208 // Perform validation on the final value if a closure has been set. |
|
209 // The closure is also allowed to return another value. |
|
210 foreach ($this->finalValidationClosures as $closure) { |
|
211 try { |
|
212 $value = $closure($value); |
|
213 } catch (Exception $correctEx) { |
|
214 throw $correctEx; |
|
215 } catch (\Exception $invalid) { |
|
216 throw new InvalidConfigurationException(sprintf( |
|
217 'Invalid configuration for path "%s": %s', |
|
218 $this->getPath(), |
|
219 $invalid->getMessage() |
|
220 ), $invalid->getCode(), $invalid); |
|
221 } |
|
222 } |
|
223 |
|
224 return $value; |
|
225 } |
|
226 |
|
227 /** |
|
228 * Validates the type of a Node. |
|
229 * |
|
230 * @param mixed $value The value to validate |
|
231 * @throws InvalidTypeException when the value is invalid |
|
232 */ |
|
233 abstract protected function validateType($value); |
|
234 |
|
235 /** |
|
236 * Normalizes the value. |
|
237 * |
|
238 * @param mixed $value The value to normalize. |
|
239 * @return mixed The normalized value |
|
240 */ |
|
241 abstract protected function normalizeValue($value); |
|
242 |
|
243 /** |
|
244 * Merges two values together |
|
245 * |
|
246 * @param mixed $leftSide |
|
247 * @param mixed $rightSide |
|
248 * @return mixed The merged value |
|
249 */ |
|
250 abstract protected function mergeValues($leftSide, $rightSide); |
|
251 |
|
252 /** |
|
253 * Finalizes a value |
|
254 * |
|
255 * @param mixed $value The value to finalize |
|
256 * @return mixed The finalized value |
|
257 */ |
|
258 abstract protected function finalizeValue($value); |
|
259 } |