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\Config\Definition\Builder; 13 14use Symfony\Component\Config\Definition\BaseNode; 15use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; 16use Symfony\Component\Config\Definition\NodeInterface; 17 18/** 19 * This class provides a fluent interface for defining a node. 20 * 21 * @author Johannes M. Schmitt <schmittjoh@gmail.com> 22 */ 23abstract class NodeDefinition implements NodeParentInterface 24{ 25 protected $name; 26 protected $normalization; 27 protected $validation; 28 protected $defaultValue; 29 protected $default = false; 30 protected $required = false; 31 protected $deprecationMessage = null; 32 protected $merge; 33 protected $allowEmptyValue = true; 34 protected $nullEquivalent; 35 protected $trueEquivalent = true; 36 protected $falseEquivalent = false; 37 protected $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR; 38 protected $parent; 39 protected $attributes = []; 40 41 public function __construct(?string $name, NodeParentInterface $parent = null) 42 { 43 $this->parent = $parent; 44 $this->name = $name; 45 } 46 47 /** 48 * Sets the parent node. 49 * 50 * @return $this 51 */ 52 public function setParent(NodeParentInterface $parent) 53 { 54 $this->parent = $parent; 55 56 return $this; 57 } 58 59 /** 60 * Sets info message. 61 * 62 * @return $this 63 */ 64 public function info(string $info) 65 { 66 return $this->attribute('info', $info); 67 } 68 69 /** 70 * Sets example configuration. 71 * 72 * @param string|array $example 73 * 74 * @return $this 75 */ 76 public function example($example) 77 { 78 return $this->attribute('example', $example); 79 } 80 81 /** 82 * Sets an attribute on the node. 83 * 84 * @param mixed $value 85 * 86 * @return $this 87 */ 88 public function attribute(string $key, $value) 89 { 90 $this->attributes[$key] = $value; 91 92 return $this; 93 } 94 95 /** 96 * Returns the parent node. 97 * 98 * @return NodeParentInterface|NodeBuilder|NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition|null The builder of the parent node 99 */ 100 public function end() 101 { 102 return $this->parent; 103 } 104 105 /** 106 * Creates the node. 107 * 108 * @param bool $forceRootNode Whether to force this node as the root node 109 * 110 * @return NodeInterface 111 */ 112 public function getNode(bool $forceRootNode = false) 113 { 114 if ($forceRootNode) { 115 $this->parent = null; 116 } 117 118 if (null !== $this->normalization) { 119 $this->normalization->before = ExprBuilder::buildExpressions($this->normalization->before); 120 } 121 122 if (null !== $this->validation) { 123 $this->validation->rules = ExprBuilder::buildExpressions($this->validation->rules); 124 } 125 126 $node = $this->createNode(); 127 $node->setAttributes($this->attributes); 128 129 return $node; 130 } 131 132 /** 133 * Sets the default value. 134 * 135 * @param mixed $value The default value 136 * 137 * @return $this 138 */ 139 public function defaultValue($value) 140 { 141 $this->default = true; 142 $this->defaultValue = $value; 143 144 return $this; 145 } 146 147 /** 148 * Sets the node as required. 149 * 150 * @return $this 151 */ 152 public function isRequired() 153 { 154 $this->required = true; 155 156 return $this; 157 } 158 159 /** 160 * Sets the node as deprecated. 161 * 162 * You can use %node% and %path% placeholders in your message to display, 163 * respectively, the node name and its complete path. 164 * 165 * @return $this 166 */ 167 public function setDeprecated(string $message = 'The child node "%node%" at path "%path%" is deprecated.') 168 { 169 $this->deprecationMessage = $message; 170 171 return $this; 172 } 173 174 /** 175 * Sets the equivalent value used when the node contains null. 176 * 177 * @param mixed $value 178 * 179 * @return $this 180 */ 181 public function treatNullLike($value) 182 { 183 $this->nullEquivalent = $value; 184 185 return $this; 186 } 187 188 /** 189 * Sets the equivalent value used when the node contains true. 190 * 191 * @param mixed $value 192 * 193 * @return $this 194 */ 195 public function treatTrueLike($value) 196 { 197 $this->trueEquivalent = $value; 198 199 return $this; 200 } 201 202 /** 203 * Sets the equivalent value used when the node contains false. 204 * 205 * @param mixed $value 206 * 207 * @return $this 208 */ 209 public function treatFalseLike($value) 210 { 211 $this->falseEquivalent = $value; 212 213 return $this; 214 } 215 216 /** 217 * Sets null as the default value. 218 * 219 * @return $this 220 */ 221 public function defaultNull() 222 { 223 return $this->defaultValue(null); 224 } 225 226 /** 227 * Sets true as the default value. 228 * 229 * @return $this 230 */ 231 public function defaultTrue() 232 { 233 return $this->defaultValue(true); 234 } 235 236 /** 237 * Sets false as the default value. 238 * 239 * @return $this 240 */ 241 public function defaultFalse() 242 { 243 return $this->defaultValue(false); 244 } 245 246 /** 247 * Sets an expression to run before the normalization. 248 * 249 * @return ExprBuilder 250 */ 251 public function beforeNormalization() 252 { 253 return $this->normalization()->before(); 254 } 255 256 /** 257 * Denies the node value being empty. 258 * 259 * @return $this 260 */ 261 public function cannotBeEmpty() 262 { 263 $this->allowEmptyValue = false; 264 265 return $this; 266 } 267 268 /** 269 * Sets an expression to run for the validation. 270 * 271 * The expression receives the value of the node and must return it. It can 272 * modify it. 273 * An exception should be thrown when the node is not valid. 274 * 275 * @return ExprBuilder 276 */ 277 public function validate() 278 { 279 return $this->validation()->rule(); 280 } 281 282 /** 283 * Sets whether the node can be overwritten. 284 * 285 * @return $this 286 */ 287 public function cannotBeOverwritten(bool $deny = true) 288 { 289 $this->merge()->denyOverwrite($deny); 290 291 return $this; 292 } 293 294 /** 295 * Gets the builder for validation rules. 296 * 297 * @return ValidationBuilder 298 */ 299 protected function validation() 300 { 301 if (null === $this->validation) { 302 $this->validation = new ValidationBuilder($this); 303 } 304 305 return $this->validation; 306 } 307 308 /** 309 * Gets the builder for merging rules. 310 * 311 * @return MergeBuilder 312 */ 313 protected function merge() 314 { 315 if (null === $this->merge) { 316 $this->merge = new MergeBuilder($this); 317 } 318 319 return $this->merge; 320 } 321 322 /** 323 * Gets the builder for normalization rules. 324 * 325 * @return NormalizationBuilder 326 */ 327 protected function normalization() 328 { 329 if (null === $this->normalization) { 330 $this->normalization = new NormalizationBuilder($this); 331 } 332 333 return $this->normalization; 334 } 335 336 /** 337 * Instantiate and configure the node according to this definition. 338 * 339 * @return NodeInterface The node instance 340 * 341 * @throws InvalidDefinitionException When the definition is invalid 342 */ 343 abstract protected function createNode(); 344 345 /** 346 * Set PathSeparator to use. 347 * 348 * @return $this 349 */ 350 public function setPathSeparator(string $separator) 351 { 352 if ($this instanceof ParentNodeDefinitionInterface) { 353 foreach ($this->getChildNodeDefinitions() as $child) { 354 $child->setPathSeparator($separator); 355 } 356 } 357 358 $this->pathSeparator = $separator; 359 360 return $this; 361 } 362} 363