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