Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\Validator\Mapping\Loader;
|
Chris@0
|
13
|
Chris@0
|
14 use Symfony\Component\Validator\Mapping\ClassMetadata;
|
Chris@0
|
15 use Symfony\Component\Yaml\Exception\ParseException;
|
Chris@0
|
16 use Symfony\Component\Yaml\Parser as YamlParser;
|
Chris@14
|
17 use Symfony\Component\Yaml\Yaml;
|
Chris@0
|
18
|
Chris@0
|
19 /**
|
Chris@0
|
20 * Loads validation metadata from a YAML file.
|
Chris@0
|
21 *
|
Chris@0
|
22 * @author Bernhard Schussek <bschussek@gmail.com>
|
Chris@0
|
23 */
|
Chris@0
|
24 class YamlFileLoader extends FileLoader
|
Chris@0
|
25 {
|
Chris@0
|
26 /**
|
Chris@0
|
27 * An array of YAML class descriptions.
|
Chris@0
|
28 *
|
Chris@0
|
29 * @var array
|
Chris@0
|
30 */
|
Chris@0
|
31 protected $classes = null;
|
Chris@0
|
32
|
Chris@0
|
33 /**
|
Chris@0
|
34 * Caches the used YAML parser.
|
Chris@0
|
35 *
|
Chris@0
|
36 * @var YamlParser
|
Chris@0
|
37 */
|
Chris@0
|
38 private $yamlParser;
|
Chris@0
|
39
|
Chris@0
|
40 /**
|
Chris@0
|
41 * {@inheritdoc}
|
Chris@0
|
42 */
|
Chris@0
|
43 public function loadClassMetadata(ClassMetadata $metadata)
|
Chris@0
|
44 {
|
Chris@0
|
45 if (null === $this->classes) {
|
Chris@0
|
46 $this->loadClassesFromYaml();
|
Chris@0
|
47 }
|
Chris@0
|
48
|
Chris@0
|
49 if (isset($this->classes[$metadata->getClassName()])) {
|
Chris@0
|
50 $classDescription = $this->classes[$metadata->getClassName()];
|
Chris@0
|
51
|
Chris@0
|
52 $this->loadClassMetadataFromYaml($metadata, $classDescription);
|
Chris@0
|
53
|
Chris@0
|
54 return true;
|
Chris@0
|
55 }
|
Chris@0
|
56
|
Chris@0
|
57 return false;
|
Chris@0
|
58 }
|
Chris@0
|
59
|
Chris@0
|
60 /**
|
Chris@0
|
61 * Return the names of the classes mapped in this file.
|
Chris@0
|
62 *
|
Chris@0
|
63 * @return string[] The classes names
|
Chris@0
|
64 */
|
Chris@0
|
65 public function getMappedClasses()
|
Chris@0
|
66 {
|
Chris@0
|
67 if (null === $this->classes) {
|
Chris@0
|
68 $this->loadClassesFromYaml();
|
Chris@0
|
69 }
|
Chris@0
|
70
|
Chris@0
|
71 return array_keys($this->classes);
|
Chris@0
|
72 }
|
Chris@0
|
73
|
Chris@0
|
74 /**
|
Chris@0
|
75 * Parses a collection of YAML nodes.
|
Chris@0
|
76 *
|
Chris@0
|
77 * @param array $nodes The YAML nodes
|
Chris@0
|
78 *
|
Chris@0
|
79 * @return array An array of values or Constraint instances
|
Chris@0
|
80 */
|
Chris@0
|
81 protected function parseNodes(array $nodes)
|
Chris@0
|
82 {
|
Chris@17
|
83 $values = [];
|
Chris@0
|
84
|
Chris@0
|
85 foreach ($nodes as $name => $childNodes) {
|
Chris@17
|
86 if (is_numeric($name) && \is_array($childNodes) && 1 === \count($childNodes)) {
|
Chris@0
|
87 $options = current($childNodes);
|
Chris@0
|
88
|
Chris@17
|
89 if (\is_array($options)) {
|
Chris@0
|
90 $options = $this->parseNodes($options);
|
Chris@0
|
91 }
|
Chris@0
|
92
|
Chris@0
|
93 $values[] = $this->newConstraint(key($childNodes), $options);
|
Chris@0
|
94 } else {
|
Chris@17
|
95 if (\is_array($childNodes)) {
|
Chris@0
|
96 $childNodes = $this->parseNodes($childNodes);
|
Chris@0
|
97 }
|
Chris@0
|
98
|
Chris@0
|
99 $values[$name] = $childNodes;
|
Chris@0
|
100 }
|
Chris@0
|
101 }
|
Chris@0
|
102
|
Chris@0
|
103 return $values;
|
Chris@0
|
104 }
|
Chris@0
|
105
|
Chris@0
|
106 /**
|
Chris@0
|
107 * Loads the YAML class descriptions from the given file.
|
Chris@0
|
108 *
|
Chris@0
|
109 * @param string $path The path of the YAML file
|
Chris@0
|
110 *
|
Chris@0
|
111 * @return array The class descriptions
|
Chris@0
|
112 *
|
Chris@0
|
113 * @throws \InvalidArgumentException If the file could not be loaded or did
|
Chris@0
|
114 * not contain a YAML array
|
Chris@0
|
115 */
|
Chris@0
|
116 private function parseFile($path)
|
Chris@0
|
117 {
|
Chris@14
|
118 $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($path, &$prevErrorHandler) {
|
Chris@14
|
119 $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$path.'"$0', $message) : $message;
|
Chris@14
|
120
|
Chris@14
|
121 return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false;
|
Chris@14
|
122 });
|
Chris@14
|
123
|
Chris@0
|
124 try {
|
Chris@14
|
125 $classes = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT);
|
Chris@0
|
126 } catch (ParseException $e) {
|
Chris@0
|
127 throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e);
|
Chris@14
|
128 } finally {
|
Chris@14
|
129 restore_error_handler();
|
Chris@0
|
130 }
|
Chris@0
|
131
|
Chris@0
|
132 // empty file
|
Chris@0
|
133 if (null === $classes) {
|
Chris@17
|
134 return [];
|
Chris@0
|
135 }
|
Chris@0
|
136
|
Chris@0
|
137 // not an array
|
Chris@17
|
138 if (!\is_array($classes)) {
|
Chris@0
|
139 throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $this->file));
|
Chris@0
|
140 }
|
Chris@0
|
141
|
Chris@0
|
142 return $classes;
|
Chris@0
|
143 }
|
Chris@0
|
144
|
Chris@0
|
145 private function loadClassesFromYaml()
|
Chris@0
|
146 {
|
Chris@0
|
147 if (null === $this->yamlParser) {
|
Chris@0
|
148 $this->yamlParser = new YamlParser();
|
Chris@0
|
149 }
|
Chris@0
|
150
|
Chris@0
|
151 $this->classes = $this->parseFile($this->file);
|
Chris@0
|
152
|
Chris@0
|
153 if (isset($this->classes['namespaces'])) {
|
Chris@0
|
154 foreach ($this->classes['namespaces'] as $alias => $namespace) {
|
Chris@0
|
155 $this->addNamespaceAlias($alias, $namespace);
|
Chris@0
|
156 }
|
Chris@0
|
157
|
Chris@0
|
158 unset($this->classes['namespaces']);
|
Chris@0
|
159 }
|
Chris@0
|
160 }
|
Chris@0
|
161
|
Chris@0
|
162 private function loadClassMetadataFromYaml(ClassMetadata $metadata, array $classDescription)
|
Chris@0
|
163 {
|
Chris@0
|
164 if (isset($classDescription['group_sequence_provider'])) {
|
Chris@0
|
165 $metadata->setGroupSequenceProvider(
|
Chris@0
|
166 (bool) $classDescription['group_sequence_provider']
|
Chris@0
|
167 );
|
Chris@0
|
168 }
|
Chris@0
|
169
|
Chris@0
|
170 if (isset($classDescription['group_sequence'])) {
|
Chris@0
|
171 $metadata->setGroupSequence($classDescription['group_sequence']);
|
Chris@0
|
172 }
|
Chris@0
|
173
|
Chris@17
|
174 if (isset($classDescription['constraints']) && \is_array($classDescription['constraints'])) {
|
Chris@0
|
175 foreach ($this->parseNodes($classDescription['constraints']) as $constraint) {
|
Chris@0
|
176 $metadata->addConstraint($constraint);
|
Chris@0
|
177 }
|
Chris@0
|
178 }
|
Chris@0
|
179
|
Chris@17
|
180 if (isset($classDescription['properties']) && \is_array($classDescription['properties'])) {
|
Chris@0
|
181 foreach ($classDescription['properties'] as $property => $constraints) {
|
Chris@0
|
182 if (null !== $constraints) {
|
Chris@0
|
183 foreach ($this->parseNodes($constraints) as $constraint) {
|
Chris@0
|
184 $metadata->addPropertyConstraint($property, $constraint);
|
Chris@0
|
185 }
|
Chris@0
|
186 }
|
Chris@0
|
187 }
|
Chris@0
|
188 }
|
Chris@0
|
189
|
Chris@17
|
190 if (isset($classDescription['getters']) && \is_array($classDescription['getters'])) {
|
Chris@0
|
191 foreach ($classDescription['getters'] as $getter => $constraints) {
|
Chris@0
|
192 if (null !== $constraints) {
|
Chris@0
|
193 foreach ($this->parseNodes($constraints) as $constraint) {
|
Chris@0
|
194 $metadata->addGetterConstraint($getter, $constraint);
|
Chris@0
|
195 }
|
Chris@0
|
196 }
|
Chris@0
|
197 }
|
Chris@0
|
198 }
|
Chris@0
|
199 }
|
Chris@0
|
200 }
|