PageRenderTime 28ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php

https://bitbucket.org/hill2steve/mobileroom
PHP | 420 lines | 204 code | 56 blank | 160 comment | 26 complexity | 3666df5c498b117a89085a3c0cd9a2bb MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Config\Definition\Builder;
  11. use Symfony\Component\Config\Definition\NodeInterface;
  12. use Symfony\Component\Config\Definition\ArrayNode;
  13. use Symfony\Component\Config\Definition\PrototypedArrayNode;
  14. use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;
  15. /**
  16. * This class provides a fluent interface for defining an array node.
  17. *
  18. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  19. */
  20. class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinitionInterface
  21. {
  22. protected $performDeepMerging;
  23. protected $ignoreExtraKeys;
  24. protected $children;
  25. protected $prototype;
  26. protected $atLeastOne;
  27. protected $allowNewKeys;
  28. protected $key;
  29. protected $removeKeyItem;
  30. protected $addDefaults;
  31. protected $addDefaultChildren;
  32. protected $nodeBuilder;
  33. /**
  34. * {@inheritDoc}
  35. */
  36. public function __construct($name, NodeParentInterface $parent = null)
  37. {
  38. parent::__construct($name, $parent);
  39. $this->children = array();
  40. $this->addDefaults = false;
  41. $this->addDefaultChildren = false;
  42. $this->allowNewKeys = true;
  43. $this->atLeastOne = false;
  44. $this->allowEmptyValue = true;
  45. $this->performDeepMerging = true;
  46. $this->nullEquivalent = array();
  47. $this->trueEquivalent = array();
  48. }
  49. /**
  50. * Sets a custom children builder.
  51. *
  52. * @param NodeBuilder $builder A custom NodeBuilder
  53. */
  54. public function setBuilder(NodeBuilder $builder)
  55. {
  56. $this->nodeBuilder = $builder;
  57. }
  58. /**
  59. * Returns a builder to add children nodes.
  60. *
  61. * @return NodeBuilder
  62. */
  63. public function children()
  64. {
  65. return $this->getNodeBuilder();
  66. }
  67. /**
  68. * Sets a prototype for child nodes.
  69. *
  70. * @param string $type the type of node
  71. *
  72. * @return NodeDefinition
  73. */
  74. public function prototype($type)
  75. {
  76. return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this);
  77. }
  78. /**
  79. * Adds the default value if the node is not set in the configuration.
  80. *
  81. * This method is applicable to concrete nodes only (not to prototype nodes).
  82. * If this function has been called and the node is not set during the finalization
  83. * phase, it's default value will be derived from its children default values.
  84. *
  85. * @return ArrayNodeDefinition
  86. */
  87. public function addDefaultsIfNotSet()
  88. {
  89. $this->addDefaults = true;
  90. return $this;
  91. }
  92. /**
  93. * Adds children with a default value when none are defined.
  94. *
  95. * @param integer|string|array|null $children The number of children|The child name|The children names to be added
  96. *
  97. * This method is applicable to prototype nodes only.
  98. *
  99. * @return ArrayNodeDefinition
  100. */
  101. public function addDefaultChildrenIfNoneSet($children = null)
  102. {
  103. $this->addDefaultChildren = $children;
  104. return $this;
  105. }
  106. /**
  107. * Requires the node to have at least one element.
  108. *
  109. * This method is applicable to prototype nodes only.
  110. *
  111. * @return ArrayNodeDefinition
  112. */
  113. public function requiresAtLeastOneElement()
  114. {
  115. $this->atLeastOne = true;
  116. return $this;
  117. }
  118. /**
  119. * Disallows adding news keys in a subsequent configuration.
  120. *
  121. * If used all keys have to be defined in the same configuration file.
  122. *
  123. * @return ArrayNodeDefinition
  124. */
  125. public function disallowNewKeysInSubsequentConfigs()
  126. {
  127. $this->allowNewKeys = false;
  128. return $this;
  129. }
  130. /**
  131. * Sets a normalization rule for XML configurations.
  132. *
  133. * @param string $singular The key to remap
  134. * @param string $plural The plural of the key for irregular plurals
  135. *
  136. * @return ArrayNodeDefinition
  137. */
  138. public function fixXmlConfig($singular, $plural = null)
  139. {
  140. $this->normalization()->remap($singular, $plural);
  141. return $this;
  142. }
  143. /**
  144. * Sets the attribute which value is to be used as key.
  145. *
  146. * This is useful when you have an indexed array that should be an
  147. * associative array. You can select an item from within the array
  148. * to be the key of the particular item. For example, if "id" is the
  149. * "key", then:
  150. *
  151. * array(
  152. * array('id' => 'my_name', 'foo' => 'bar'),
  153. * );
  154. *
  155. * becomes
  156. *
  157. * array(
  158. * 'my_name' => array('foo' => 'bar'),
  159. * );
  160. *
  161. * If you'd like "'id' => 'my_name'" to still be present in the resulting
  162. * array, then you can set the second argument of this method to false.
  163. *
  164. * This method is applicable to prototype nodes only.
  165. *
  166. * @param string $name The name of the key
  167. * @param Boolean $removeKeyItem Whether or not the key item should be removed.
  168. *
  169. * @return ArrayNodeDefinition
  170. */
  171. public function useAttributeAsKey($name, $removeKeyItem = true)
  172. {
  173. $this->key = $name;
  174. $this->removeKeyItem = $removeKeyItem;
  175. return $this;
  176. }
  177. /**
  178. * Sets whether the node can be unset.
  179. *
  180. * @param Boolean $allow
  181. *
  182. * @return ArrayNodeDefinition
  183. */
  184. public function canBeUnset($allow = true)
  185. {
  186. $this->merge()->allowUnset($allow);
  187. return $this;
  188. }
  189. /**
  190. * Disables the deep merging of the node.
  191. *
  192. * @return ArrayNodeDefinition
  193. */
  194. public function performNoDeepMerging()
  195. {
  196. $this->performDeepMerging = false;
  197. return $this;
  198. }
  199. /**
  200. * Allows extra config keys to be specified under an array without
  201. * throwing an exception.
  202. *
  203. * Those config values are simply ignored. This should be used only
  204. * in special cases where you want to send an entire configuration
  205. * array through a special tree that processes only part of the array.
  206. *
  207. * @return ArrayNodeDefinition
  208. */
  209. public function ignoreExtraKeys()
  210. {
  211. $this->ignoreExtraKeys = true;
  212. return $this;
  213. }
  214. /**
  215. * Appends a node definition.
  216. *
  217. * $node = new ArrayNodeDefinition()
  218. * ->children()
  219. * ->scalarNode('foo')->end()
  220. * ->scalarNode('baz')->end()
  221. * ->end()
  222. * ->append($this->getBarNodeDefinition())
  223. * ;
  224. *
  225. * @param NodeDefinition $node A NodeDefinition instance
  226. *
  227. * @return ArrayNodeDefinition This node
  228. */
  229. public function append(NodeDefinition $node)
  230. {
  231. $this->children[$node->name] = $node->setParent($this);
  232. return $this;
  233. }
  234. /**
  235. * Returns a node builder to be used to add children and prototype
  236. *
  237. * @return NodeBuilder The node builder
  238. */
  239. protected function getNodeBuilder()
  240. {
  241. if (null === $this->nodeBuilder) {
  242. $this->nodeBuilder = new NodeBuilder();
  243. }
  244. return $this->nodeBuilder->setParent($this);
  245. }
  246. /**
  247. * {@inheritDoc}
  248. */
  249. protected function createNode()
  250. {
  251. if (null === $this->prototype) {
  252. $node = new ArrayNode($this->name, $this->parent);
  253. $this->validateConcreteNode($node);
  254. $node->setAddIfNotSet($this->addDefaults);
  255. foreach ($this->children as $child) {
  256. $child->parent = $node;
  257. $node->addChild($child->getNode());
  258. }
  259. } else {
  260. $node = new PrototypedArrayNode($this->name, $this->parent);
  261. $this->validatePrototypeNode($node);
  262. if (null !== $this->key) {
  263. $node->setKeyAttribute($this->key, $this->removeKeyItem);
  264. }
  265. if (true === $this->atLeastOne) {
  266. $node->setMinNumberOfElements(1);
  267. }
  268. if ($this->default) {
  269. $node->setDefaultValue($this->defaultValue);
  270. }
  271. if (false !== $this->addDefaultChildren) {
  272. $node->setAddChildrenIfNoneSet($this->addDefaultChildren);
  273. if ($this->prototype instanceof static && null === $this->prototype->prototype) {
  274. $this->prototype->addDefaultsIfNotSet();
  275. }
  276. }
  277. $this->prototype->parent = $node;
  278. $node->setPrototype($this->prototype->getNode());
  279. }
  280. $node->setAllowNewKeys($this->allowNewKeys);
  281. $node->addEquivalentValue(null, $this->nullEquivalent);
  282. $node->addEquivalentValue(true, $this->trueEquivalent);
  283. $node->addEquivalentValue(false, $this->falseEquivalent);
  284. $node->setPerformDeepMerging($this->performDeepMerging);
  285. $node->setRequired($this->required);
  286. $node->setIgnoreExtraKeys($this->ignoreExtraKeys);
  287. if (null !== $this->normalization) {
  288. $node->setNormalizationClosures($this->normalization->before);
  289. $node->setXmlRemappings($this->normalization->remappings);
  290. }
  291. if (null !== $this->merge) {
  292. $node->setAllowOverwrite($this->merge->allowOverwrite);
  293. $node->setAllowFalse($this->merge->allowFalse);
  294. }
  295. if (null !== $this->validation) {
  296. $node->setFinalValidationClosures($this->validation->rules);
  297. }
  298. return $node;
  299. }
  300. /**
  301. * Validate the configuration of a concrete node.
  302. *
  303. * @param NodeInterface $node The related node
  304. *
  305. * @throws InvalidDefinitionException When an error is detected in the configuration
  306. */
  307. protected function validateConcreteNode(ArrayNode $node)
  308. {
  309. $path = $node->getPath();
  310. if (null !== $this->key) {
  311. throw new InvalidDefinitionException(
  312. sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path)
  313. );
  314. }
  315. if (true === $this->atLeastOne) {
  316. throw new InvalidDefinitionException(
  317. sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path)
  318. );
  319. }
  320. if ($this->default) {
  321. throw new InvalidDefinitionException(
  322. sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path)
  323. );
  324. }
  325. if (false !== $this->addDefaultChildren) {
  326. throw new InvalidDefinitionException(
  327. sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path)
  328. );
  329. }
  330. }
  331. /**
  332. * Validate the configuration of a prototype node.
  333. *
  334. * @param NodeInterface $node The related node
  335. *
  336. * @throws InvalidDefinitionException When an error is detected in the configuration
  337. */
  338. protected function validatePrototypeNode(PrototypedArrayNode $node)
  339. {
  340. $path = $node->getPath();
  341. if ($this->addDefaults) {
  342. throw new InvalidDefinitionException(
  343. sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path)
  344. );
  345. }
  346. if (false !== $this->addDefaultChildren) {
  347. if ($this->default) {
  348. throw new InvalidDefinitionException(
  349. sprintf('A default value and default children might not be used together at path "%s"', $path)
  350. );
  351. }
  352. if (null !== $this->key && (null === $this->addDefaultChildren || is_integer($this->addDefaultChildren) && $this->addDefaultChildren > 0)) {
  353. throw new InvalidDefinitionException(
  354. sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path)
  355. );
  356. }
  357. if (null === $this->key && (is_string($this->addDefaultChildren) || is_array($this->addDefaultChildren))) {
  358. throw new InvalidDefinitionException(
  359. sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path)
  360. );
  361. }
  362. }
  363. }
  364. }