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\Config\Util\XmlUtils; Chris@0: use Symfony\Component\Validator\Exception\MappingException; Chris@0: use Symfony\Component\Validator\Mapping\ClassMetadata; Chris@0: Chris@0: /** Chris@0: * Loads validation metadata from an XML file. Chris@0: * Chris@0: * @author Bernhard Schussek Chris@0: */ Chris@0: class XmlFileLoader extends FileLoader Chris@0: { Chris@0: /** Chris@0: * The XML nodes of the mapping file. Chris@0: * Chris@0: * @var \SimpleXMLElement[]|null Chris@0: */ Chris@0: protected $classes; 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->loadClassesFromXml(); Chris@0: } Chris@0: Chris@0: if (isset($this->classes[$metadata->getClassName()])) { Chris@0: $classDescription = $this->classes[$metadata->getClassName()]; Chris@0: Chris@0: $this->loadClassMetadataFromXml($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->loadClassesFromXml(); Chris@0: } Chris@0: Chris@0: return array_keys($this->classes); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Parses a collection of "constraint" XML nodes. Chris@0: * Chris@0: * @param \SimpleXMLElement $nodes The XML nodes Chris@0: * Chris@0: * @return array The Constraint instances Chris@0: */ Chris@0: protected function parseConstraints(\SimpleXMLElement $nodes) Chris@0: { Chris@17: $constraints = []; Chris@0: Chris@0: foreach ($nodes as $node) { Chris@17: if (\count($node) > 0) { Chris@17: if (\count($node->value) > 0) { Chris@0: $options = $this->parseValues($node->value); Chris@17: } elseif (\count($node->constraint) > 0) { Chris@0: $options = $this->parseConstraints($node->constraint); Chris@17: } elseif (\count($node->option) > 0) { Chris@0: $options = $this->parseOptions($node->option); Chris@0: } else { Chris@17: $options = []; Chris@0: } Chris@17: } elseif (\strlen((string) $node) > 0) { Chris@0: $options = XmlUtils::phpize(trim($node)); Chris@0: } else { Chris@0: $options = null; Chris@0: } Chris@0: Chris@0: $constraints[] = $this->newConstraint((string) $node['name'], $options); Chris@0: } Chris@0: Chris@0: return $constraints; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Parses a collection of "value" XML nodes. Chris@0: * Chris@0: * @param \SimpleXMLElement $nodes The XML nodes Chris@0: * Chris@0: * @return array The values Chris@0: */ Chris@0: protected function parseValues(\SimpleXMLElement $nodes) Chris@0: { Chris@17: $values = []; Chris@0: Chris@0: foreach ($nodes as $node) { Chris@17: if (\count($node) > 0) { Chris@17: if (\count($node->value) > 0) { Chris@0: $value = $this->parseValues($node->value); Chris@17: } elseif (\count($node->constraint) > 0) { Chris@0: $value = $this->parseConstraints($node->constraint); Chris@0: } else { Chris@17: $value = []; Chris@0: } Chris@0: } else { Chris@0: $value = trim($node); Chris@0: } Chris@0: Chris@0: if (isset($node['key'])) { Chris@0: $values[(string) $node['key']] = $value; Chris@0: } else { Chris@0: $values[] = $value; Chris@0: } Chris@0: } Chris@0: Chris@0: return $values; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Parses a collection of "option" XML nodes. Chris@0: * Chris@0: * @param \SimpleXMLElement $nodes The XML nodes Chris@0: * Chris@0: * @return array The options Chris@0: */ Chris@0: protected function parseOptions(\SimpleXMLElement $nodes) Chris@0: { Chris@17: $options = []; Chris@0: Chris@0: foreach ($nodes as $node) { Chris@17: if (\count($node) > 0) { Chris@17: if (\count($node->value) > 0) { Chris@0: $value = $this->parseValues($node->value); Chris@17: } elseif (\count($node->constraint) > 0) { Chris@0: $value = $this->parseConstraints($node->constraint); Chris@0: } else { Chris@17: $value = []; Chris@0: } Chris@0: } else { Chris@0: $value = XmlUtils::phpize($node); Chris@17: if (\is_string($value)) { Chris@0: $value = trim($value); Chris@0: } Chris@0: } Chris@0: Chris@0: $options[(string) $node['name']] = $value; Chris@0: } Chris@0: Chris@0: return $options; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Loads the XML class descriptions from the given file. Chris@0: * Chris@0: * @param string $path The path of the XML file Chris@0: * Chris@0: * @return \SimpleXMLElement The class descriptions Chris@0: * Chris@0: * @throws MappingException If the file could not be loaded Chris@0: */ Chris@0: protected function parseFile($path) Chris@0: { Chris@0: try { Chris@0: $dom = XmlUtils::loadFile($path, __DIR__.'/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd'); Chris@0: } catch (\Exception $e) { Chris@0: throw new MappingException($e->getMessage(), $e->getCode(), $e); Chris@0: } Chris@0: Chris@0: return simplexml_import_dom($dom); Chris@0: } Chris@0: Chris@0: private function loadClassesFromXml() Chris@0: { Chris@0: // This method may throw an exception. Do not modify the class' Chris@0: // state before it completes Chris@0: $xml = $this->parseFile($this->file); Chris@0: Chris@17: $this->classes = []; Chris@0: Chris@0: foreach ($xml->namespace as $namespace) { Chris@0: $this->addNamespaceAlias((string) $namespace['prefix'], trim((string) $namespace)); Chris@0: } Chris@0: Chris@0: foreach ($xml->class as $class) { Chris@0: $this->classes[(string) $class['name']] = $class; Chris@0: } Chris@0: } Chris@0: Chris@0: private function loadClassMetadataFromXml(ClassMetadata $metadata, \SimpleXMLElement $classDescription) Chris@0: { Chris@17: if (\count($classDescription->{'group-sequence-provider'}) > 0) { Chris@0: $metadata->setGroupSequenceProvider(true); Chris@0: } Chris@0: Chris@0: foreach ($classDescription->{'group-sequence'} as $groupSequence) { Chris@17: if (\count($groupSequence->value) > 0) { Chris@0: $metadata->setGroupSequence($this->parseValues($groupSequence[0]->value)); Chris@0: } Chris@0: } Chris@0: Chris@0: foreach ($this->parseConstraints($classDescription->constraint) as $constraint) { Chris@0: $metadata->addConstraint($constraint); Chris@0: } Chris@0: Chris@0: foreach ($classDescription->property as $property) { Chris@0: foreach ($this->parseConstraints($property->constraint) as $constraint) { Chris@0: $metadata->addPropertyConstraint((string) $property['name'], $constraint); Chris@0: } Chris@0: } Chris@0: Chris@0: foreach ($classDescription->getter as $getter) { Chris@0: foreach ($this->parseConstraints($getter->constraint) as $constraint) { Chris@0: $metadata->addGetterConstraint((string) $getter['property'], $constraint); Chris@0: } Chris@0: } Chris@0: } Chris@0: }