Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Validator\Mapping\Loader; Chris@0: Chris@0: use Symfony\Component\Validator\Mapping\ClassMetadata; Chris@0: use Symfony\Component\Yaml\Exception\ParseException; Chris@0: use Symfony\Component\Yaml\Parser as YamlParser; Chris@14: use Symfony\Component\Yaml\Yaml; Chris@0: Chris@0: /** Chris@0: * Loads validation metadata from a YAML file. Chris@0: * Chris@0: * @author Bernhard Schussek Chris@0: */ Chris@0: class YamlFileLoader extends FileLoader Chris@0: { Chris@0: /** Chris@0: * An array of YAML class descriptions. Chris@0: * Chris@0: * @var array Chris@0: */ Chris@0: protected $classes = null; Chris@0: Chris@0: /** Chris@0: * Caches the used YAML parser. Chris@0: * Chris@0: * @var YamlParser Chris@0: */ Chris@0: private $yamlParser; Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function loadClassMetadata(ClassMetadata $metadata) Chris@0: { Chris@0: if (null === $this->classes) { Chris@0: $this->loadClassesFromYaml(); Chris@0: } Chris@0: Chris@0: if (isset($this->classes[$metadata->getClassName()])) { Chris@0: $classDescription = $this->classes[$metadata->getClassName()]; Chris@0: Chris@0: $this->loadClassMetadataFromYaml($metadata, $classDescription); Chris@0: Chris@0: return true; Chris@0: } Chris@0: Chris@0: return false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Return the names of the classes mapped in this file. Chris@0: * Chris@0: * @return string[] The classes names Chris@0: */ Chris@0: public function getMappedClasses() Chris@0: { Chris@0: if (null === $this->classes) { Chris@0: $this->loadClassesFromYaml(); Chris@0: } Chris@0: Chris@0: return array_keys($this->classes); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Parses a collection of YAML nodes. Chris@0: * Chris@0: * @param array $nodes The YAML nodes Chris@0: * Chris@0: * @return array An array of values or Constraint instances Chris@0: */ Chris@0: protected function parseNodes(array $nodes) Chris@0: { Chris@17: $values = []; Chris@0: Chris@0: foreach ($nodes as $name => $childNodes) { Chris@17: if (is_numeric($name) && \is_array($childNodes) && 1 === \count($childNodes)) { Chris@0: $options = current($childNodes); Chris@0: Chris@17: if (\is_array($options)) { Chris@0: $options = $this->parseNodes($options); Chris@0: } Chris@0: Chris@0: $values[] = $this->newConstraint(key($childNodes), $options); Chris@0: } else { Chris@17: if (\is_array($childNodes)) { Chris@0: $childNodes = $this->parseNodes($childNodes); Chris@0: } Chris@0: Chris@0: $values[$name] = $childNodes; Chris@0: } Chris@0: } Chris@0: Chris@0: return $values; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Loads the YAML class descriptions from the given file. Chris@0: * Chris@0: * @param string $path The path of the YAML file Chris@0: * Chris@0: * @return array The class descriptions Chris@0: * Chris@0: * @throws \InvalidArgumentException If the file could not be loaded or did Chris@0: * not contain a YAML array Chris@0: */ Chris@0: private function parseFile($path) Chris@0: { Chris@14: $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($path, &$prevErrorHandler) { Chris@14: $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$path.'"$0', $message) : $message; Chris@14: Chris@14: return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false; Chris@14: }); Chris@14: Chris@0: try { Chris@14: $classes = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT); Chris@0: } catch (ParseException $e) { Chris@0: throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e); Chris@14: } finally { Chris@14: restore_error_handler(); Chris@0: } Chris@0: Chris@0: // empty file Chris@0: if (null === $classes) { Chris@17: return []; Chris@0: } Chris@0: Chris@0: // not an array Chris@17: if (!\is_array($classes)) { Chris@0: throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $this->file)); Chris@0: } Chris@0: Chris@0: return $classes; Chris@0: } Chris@0: Chris@0: private function loadClassesFromYaml() Chris@0: { Chris@0: if (null === $this->yamlParser) { Chris@0: $this->yamlParser = new YamlParser(); Chris@0: } Chris@0: Chris@0: $this->classes = $this->parseFile($this->file); Chris@0: Chris@0: if (isset($this->classes['namespaces'])) { Chris@0: foreach ($this->classes['namespaces'] as $alias => $namespace) { Chris@0: $this->addNamespaceAlias($alias, $namespace); Chris@0: } Chris@0: Chris@0: unset($this->classes['namespaces']); Chris@0: } Chris@0: } Chris@0: Chris@0: private function loadClassMetadataFromYaml(ClassMetadata $metadata, array $classDescription) Chris@0: { Chris@0: if (isset($classDescription['group_sequence_provider'])) { Chris@0: $metadata->setGroupSequenceProvider( Chris@0: (bool) $classDescription['group_sequence_provider'] Chris@0: ); Chris@0: } Chris@0: Chris@0: if (isset($classDescription['group_sequence'])) { Chris@0: $metadata->setGroupSequence($classDescription['group_sequence']); Chris@0: } Chris@0: Chris@17: if (isset($classDescription['constraints']) && \is_array($classDescription['constraints'])) { Chris@0: foreach ($this->parseNodes($classDescription['constraints']) as $constraint) { Chris@0: $metadata->addConstraint($constraint); Chris@0: } Chris@0: } Chris@0: Chris@17: if (isset($classDescription['properties']) && \is_array($classDescription['properties'])) { Chris@0: foreach ($classDescription['properties'] as $property => $constraints) { Chris@0: if (null !== $constraints) { Chris@0: foreach ($this->parseNodes($constraints) as $constraint) { Chris@0: $metadata->addPropertyConstraint($property, $constraint); Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@17: if (isset($classDescription['getters']) && \is_array($classDescription['getters'])) { Chris@0: foreach ($classDescription['getters'] as $getter => $constraints) { Chris@0: if (null !== $constraints) { Chris@0: foreach ($this->parseNodes($constraints) as $constraint) { Chris@0: $metadata->addGetterConstraint($getter, $constraint); Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: }