4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Validator\Mapping\Loader;
14 use Symfony\Component\Validator\Mapping\ClassMetadata;
15 use Symfony\Component\Yaml\Exception\ParseException;
16 use Symfony\Component\Yaml\Parser as YamlParser;
17 use Symfony\Component\Yaml\Yaml;
20 * Loads validation metadata from a YAML file.
22 * @author Bernhard Schussek <bschussek@gmail.com>
24 class YamlFileLoader extends FileLoader
27 * An array of YAML class descriptions.
31 protected $classes = null;
34 * Caches the used YAML parser.
43 public function loadClassMetadata(ClassMetadata $metadata)
45 if (null === $this->classes) {
46 $this->loadClassesFromYaml();
49 if (isset($this->classes[$metadata->getClassName()])) {
50 $classDescription = $this->classes[$metadata->getClassName()];
52 $this->loadClassMetadataFromYaml($metadata, $classDescription);
61 * Return the names of the classes mapped in this file.
63 * @return string[] The classes names
65 public function getMappedClasses()
67 if (null === $this->classes) {
68 $this->loadClassesFromYaml();
71 return array_keys($this->classes);
75 * Parses a collection of YAML nodes.
77 * @param array $nodes The YAML nodes
79 * @return array An array of values or Constraint instances
81 protected function parseNodes(array $nodes)
85 foreach ($nodes as $name => $childNodes) {
86 if (is_numeric($name) && \is_array($childNodes) && 1 === \count($childNodes)) {
87 $options = current($childNodes);
89 if (\is_array($options)) {
90 $options = $this->parseNodes($options);
93 $values[] = $this->newConstraint(key($childNodes), $options);
95 if (\is_array($childNodes)) {
96 $childNodes = $this->parseNodes($childNodes);
99 $values[$name] = $childNodes;
107 * Loads the YAML class descriptions from the given file.
109 * @param string $path The path of the YAML file
111 * @return array The class descriptions
113 * @throws \InvalidArgumentException If the file could not be loaded or did
114 * not contain a YAML array
116 private function parseFile($path)
118 $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($path, &$prevErrorHandler) {
119 $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$path.'"$0', $message) : $message;
121 return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false;
125 $classes = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT);
126 } catch (ParseException $e) {
127 throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e);
129 restore_error_handler();
133 if (null === $classes) {
138 if (!\is_array($classes)) {
139 throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $this->file));
145 private function loadClassesFromYaml()
147 if (null === $this->yamlParser) {
148 $this->yamlParser = new YamlParser();
151 $this->classes = $this->parseFile($this->file);
153 if (isset($this->classes['namespaces'])) {
154 foreach ($this->classes['namespaces'] as $alias => $namespace) {
155 $this->addNamespaceAlias($alias, $namespace);
158 unset($this->classes['namespaces']);
162 private function loadClassMetadataFromYaml(ClassMetadata $metadata, array $classDescription)
164 if (isset($classDescription['group_sequence_provider'])) {
165 $metadata->setGroupSequenceProvider(
166 (bool) $classDescription['group_sequence_provider']
170 if (isset($classDescription['group_sequence'])) {
171 $metadata->setGroupSequence($classDescription['group_sequence']);
174 if (isset($classDescription['constraints']) && \is_array($classDescription['constraints'])) {
175 foreach ($this->parseNodes($classDescription['constraints']) as $constraint) {
176 $metadata->addConstraint($constraint);
180 if (isset($classDescription['properties']) && \is_array($classDescription['properties'])) {
181 foreach ($classDescription['properties'] as $property => $constraints) {
182 if (null !== $constraints) {
183 foreach ($this->parseNodes($constraints) as $constraint) {
184 $metadata->addPropertyConstraint($property, $constraint);
190 if (isset($classDescription['getters']) && \is_array($classDescription['getters'])) {
191 foreach ($classDescription['getters'] as $getter => $constraints) {
192 if (null !== $constraints) {
193 foreach ($this->parseNodes($constraints) as $constraint) {
194 $metadata->addGetterConstraint($getter, $constraint);