Submit
Path:
~
/
/
usr
/
local
/
psa
/
admin
/
plib
/
vendor
/
jms
/
serializer
/
src
/
File Content:
XmlDeserializationVisitor.php
<?php declare(strict_types=1); namespace JMS\Serializer; use JMS\Serializer\Exception\InvalidArgumentException; use JMS\Serializer\Exception\LogicException; use JMS\Serializer\Exception\NotAcceptableException; use JMS\Serializer\Exception\RuntimeException; use JMS\Serializer\Exception\XmlErrorException; use JMS\Serializer\Metadata\ClassMetadata; use JMS\Serializer\Metadata\PropertyMetadata; use JMS\Serializer\Visitor\DeserializationVisitorInterface; final class XmlDeserializationVisitor extends AbstractVisitor implements NullAwareVisitorInterface, DeserializationVisitorInterface { /** * @var \SplStack */ private $objectStack; /** * @var \SplStack */ private $metadataStack; /** * @var \SplStack */ private $objectMetadataStack; /** * @var object|null */ private $currentObject; /** * @var ClassMetadata|PropertyMetadata|null */ private $currentMetadata; /** * @var bool */ private $disableExternalEntities; /** * @var string[] */ private $doctypeAllowList; /** * @var int */ private $options; public function __construct( bool $disableExternalEntities = true, array $doctypeAllowList = [], int $options = 0 ) { $this->objectStack = new \SplStack(); $this->metadataStack = new \SplStack(); $this->objectMetadataStack = new \SplStack(); $this->disableExternalEntities = $disableExternalEntities; $this->doctypeAllowList = $doctypeAllowList; $this->options = $options; } /** * {@inheritdoc} */ public function prepare($data) { $data = $this->emptyStringToSpaceCharacter($data); $previous = libxml_use_internal_errors(true); libxml_clear_errors(); $previousEntityLoaderState = null; if (\LIBXML_VERSION < 20900) { // phpcs:ignore Generic.PHP.DeprecatedFunctions.Deprecated $previousEntityLoaderState = libxml_disable_entity_loader($this->disableExternalEntities); } if (false !== stripos($data, '<!doctype')) { $internalSubset = $this->getDomDocumentTypeEntitySubset($data); if (!in_array($internalSubset, $this->doctypeAllowList, true)) { throw new InvalidArgumentException(sprintf( 'The document type "%s" is not allowed. If it is safe, you may add it to the allowlist configuration.', $internalSubset, )); } } $doc = simplexml_load_string($data, 'SimpleXMLElement', $this->options); libxml_use_internal_errors($previous); if (\LIBXML_VERSION < 20900) { // phpcs:ignore Generic.PHP.DeprecatedFunctions.Deprecated libxml_disable_entity_loader($previousEntityLoaderState); } if (false === $doc) { throw new XmlErrorException(libxml_get_last_error()); } return $doc; } /** * @param mixed $data */ private function emptyStringToSpaceCharacter($data): string { return '' === $data ? ' ' : (string) $data; } /** * {@inheritdoc} */ public function visitNull($data, array $type) { return null; } /** * {@inheritdoc} */ public function visitString($data, array $type): string { $this->assertValueCanBeCastToString($data); return (string) $data; } /** * {@inheritdoc} */ public function visitBoolean($data, array $type): bool { $this->assertValueCanBeCastToString($data); $data = (string) $data; if ('true' === $data || '1' === $data) { return true; } elseif ('false' === $data || '0' === $data) { return false; } else { throw new RuntimeException(sprintf('Could not convert data to boolean. Expected "true", "false", "1" or "0", but got %s.', json_encode($data))); } } /** * {@inheritdoc} */ public function visitInteger($data, array $type): int { $this->assertValueCanBeCastToInt($data); return (int) $data; } /** * {@inheritdoc} */ public function visitDouble($data, array $type): float { $this->assertValueCanCastToFloat($data); return (float) $data; } /** * {@inheritdoc} */ public function visitArray($data, array $type): array { // handle key-value-pairs if (null !== $this->currentMetadata && $this->currentMetadata->xmlKeyValuePairs) { if (2 !== count($type['params'])) { throw new RuntimeException('The array type must be specified as "array<K,V>" for Key-Value-Pairs.'); } $this->revertCurrentMetadata(); [$keyType, $entryType] = $type['params']; $result = []; foreach ($data as $key => $v) { $k = $this->navigator->accept($key, $keyType); $result[$k] = $this->navigator->accept($v, $entryType); } return $result; } $entryName = null !== $this->currentMetadata && $this->currentMetadata->xmlEntryName ? $this->currentMetadata->xmlEntryName : 'entry'; $namespace = null !== $this->currentMetadata && $this->currentMetadata->xmlEntryNamespace ? $this->currentMetadata->xmlEntryNamespace : null; if (null === $namespace && $this->objectMetadataStack->count()) { $classMetadata = $this->objectMetadataStack->top(); $namespace = $classMetadata->xmlNamespaces[''] ?? $namespace; if (null === $namespace) { $namespaces = $data->getDocNamespaces(); if (isset($namespaces[''])) { $namespace = $namespaces['']; } } } if (null !== $namespace) { $prefix = uniqid('ns-'); $data->registerXPathNamespace($prefix, $namespace); $nodes = $data->xpath(sprintf('%s:%s', $prefix, $entryName)); } else { $nodes = $data->xpath($entryName); } if (null === $nodes || !\count($nodes)) { return []; } switch (\count($type['params'])) { case 0: throw new RuntimeException(sprintf('The array type must be specified either as "array<T>", or "array<K,V>".')); case 1: $result = []; foreach ($nodes as $v) { $result[] = $this->navigator->accept($v, $type['params'][0]); } return $result; case 2: if (null === $this->currentMetadata) { throw new RuntimeException('Maps are not supported on top-level without metadata.'); } [$keyType, $entryType] = $type['params']; $result = []; $nodes = $data->children($namespace)->$entryName; foreach ($nodes as $v) { $attrs = $v->attributes(); if (!isset($attrs[$this->currentMetadata->xmlKeyAttribute])) { throw new RuntimeException(sprintf('The key attribute "%s" must be set for each entry of the map.', $this->currentMetadata->xmlKeyAttribute)); } $k = $this->navigator->accept($attrs[$this->currentMetadata->xmlKeyAttribute], $keyType); $result[$k] = $this->navigator->accept($v, $entryType); } return $result; default: throw new LogicException(sprintf('The array type does not support more than 2 parameters, but got %s.', json_encode($type['params']))); } } /** * {@inheritdoc} */ public function visitDiscriminatorMapProperty($data, ClassMetadata $metadata): string { switch (true) { // Check XML attribute without namespace for discriminatorFieldName case $metadata->xmlDiscriminatorAttribute && null === $metadata->xmlDiscriminatorNamespace && isset($data->attributes()->{$metadata->discriminatorFieldName}): return (string) $data->attributes()->{$metadata->discriminatorFieldName}; // Check XML attribute with namespace for discriminatorFieldName case $metadata->xmlDiscriminatorAttribute && null !== $metadata->xmlDiscriminatorNamespace && isset($data->attributes($metadata->xmlDiscriminatorNamespace)->{$metadata->discriminatorFieldName}): return (string) $data->attributes($metadata->xmlDiscriminatorNamespace)->{$metadata->discriminatorFieldName}; // Check XML element with namespace for discriminatorFieldName case !$metadata->xmlDiscriminatorAttribute && null !== $metadata->xmlDiscriminatorNamespace && isset($data->children($metadata->xmlDiscriminatorNamespace)->{$metadata->discriminatorFieldName}): return (string) $data->children($metadata->xmlDiscriminatorNamespace)->{$metadata->discriminatorFieldName}; // Check XML element for discriminatorFieldName case isset($data->{$metadata->discriminatorFieldName}): return (string) $data->{$metadata->discriminatorFieldName}; default: throw new LogicException(sprintf( 'The discriminator field name "%s" for base-class "%s" was not found in input data.', $metadata->discriminatorFieldName, $metadata->name, )); } } public function startVisitingObject(ClassMetadata $metadata, object $object, array $type): void { $this->setCurrentObject($object); $this->objectMetadataStack->push($metadata); } /** * {@inheritdoc} */ public function visitProperty(PropertyMetadata $metadata, $data) { $name = $metadata->serializedName; if (true === $metadata->inline) { if (!$metadata->type) { throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name); } return $this->navigator->accept($data, $metadata->type); } if ($metadata->xmlAttribute) { $attributes = $data->attributes($metadata->xmlNamespace); if (isset($attributes[$name])) { if (!$metadata->type) { throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name); } return $this->navigator->accept($attributes[$name], $metadata->type); } throw new NotAcceptableException(); } if (0 === strpos($name, '@')) { $attributeName = substr($name, 1); $attributes = $data->attributes($metadata->xmlNamespace); if (isset($attributes[$attributeName])) { if (!$metadata->type) { throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name); } return $this->navigator->accept($attributes[$attributeName], $metadata->type); } throw new NotAcceptableException(sprintf('Attribute "%s" (derived from serializedName "%s") in namespace "%s" not found for property %s::$%s. XML: %s', $attributeName, $name, $metadata->xmlNamespace ?? '[none]', $metadata->class, $metadata->name, $data->asXML())); } if (false !== strpos($name, '/@')) { [$elementName, $attributeName] = explode('/@', $name, 2); $childDataNode = null; if ('' === $metadata->xmlNamespace) { // Element explicitly in NO namespace $xpathQuery = "./*[local-name()='" . $elementName . "' and (namespace-uri()='' or not(namespace-uri()))]"; $matchingNodes = $data->xpath($xpathQuery); if (!empty($matchingNodes)) { $childDataNode = $matchingNodes[0]; } } elseif ($metadata->xmlNamespace) { // Element in a specific namespace URI $childrenInNs = $data->children($metadata->xmlNamespace); if (isset($childrenInNs->$elementName)) { $childDataNode = $childrenInNs->$elementName; } } else { // xmlNamespace is null: element in default namespace (or no namespace if no default is active) $childrenInDefaultOrNoNs = $data->children(null); if (isset($childrenInDefaultOrNoNs->$elementName)) { $childDataNode = $childrenInDefaultOrNoNs->$elementName; } } if (!$childDataNode || !$childDataNode->getName()) { if (null === $metadata->xmlNamespace) { $ns = '[default/none]'; } else { $ns = '' === $metadata->xmlNamespace ? '[none]' : $metadata->xmlNamespace; } throw new NotAcceptableException(sprintf('Child element "%s" for attribute access not found (element namespace: %s). Property %s::$%s. XML: %s', $elementName, $ns, $metadata->class, $metadata->name, $data->asXML())); } $attributeTargetNs = $metadata->xmlNamespace && '' !== $metadata->xmlNamespace ? $metadata->xmlNamespace : null; $attributes = $childDataNode->attributes($attributeTargetNs); if (isset($attributes[$attributeName])) { if (!$metadata->type) { throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name); } return $this->navigator->accept($attributes[$attributeName], $metadata->type); } throw new NotAcceptableException(sprintf('Attribute "%s" on element "%s" not found (attribute namespace: %s). Property %s::$%s. XML: %s', $attributeName, $elementName, $attributeTargetNs ?? '[none]', $metadata->class, $metadata->name, $data->asXML())); } if ($metadata->xmlValue) { if (!$metadata->type) { throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name); } return $this->navigator->accept($data, $metadata->type); } if ($metadata->xmlCollection) { $enclosingElem = $data; if (!$metadata->xmlCollectionInline) { $enclosingElem = $data->children($metadata->xmlNamespace)->$name; } $this->setCurrentMetadata($metadata); if (!$metadata->type) { throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name); } $v = $this->navigator->accept($enclosingElem, $metadata->type); $this->revertCurrentMetadata(); return $v; } if ($metadata->xmlNamespace) { $node = $data->children($metadata->xmlNamespace)->$name; if (!$node->count()) { throw new NotAcceptableException(); } } elseif ('' === $metadata->xmlNamespace) { // See #1087 - element must be like: <element xmlns="" /> - https://www.w3.org/TR/REC-xml-names/#iri-use // Use of an empty string in a namespace declaration turns it into an "undeclaration". $nodes = $data->xpath('./' . $name); if (empty($nodes)) { throw new NotAcceptableException(); } $node = reset($nodes); } else { $namespaces = $data->getDocNamespaces(); if (isset($namespaces[''])) { $prefix = uniqid('ns-'); $data->registerXPathNamespace($prefix, $namespaces['']); $nodes = $data->xpath('./' . $prefix . ':' . $name); } else { $nodes = $data->xpath('./' . $name); } if (empty($nodes)) { throw new NotAcceptableException(); } $node = reset($nodes); } if ($metadata->xmlKeyValuePairs) { $this->setCurrentMetadata($metadata); } if (!$metadata->type) { throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name); } return $this->navigator->accept($node, $metadata->type); } /** * {@inheritdoc} */ public function endVisitingObject(ClassMetadata $metadata, $data, array $type): object { $rs = $this->currentObject; $this->objectMetadataStack->pop(); $this->revertCurrentObject(); return $rs; } public function setCurrentObject(object $object): void { $this->objectStack->push($this->currentObject); $this->currentObject = $object; } public function getCurrentObject(): ?object { return $this->currentObject; } public function revertCurrentObject(): ?object { return $this->currentObject = $this->objectStack->pop(); } public function setCurrentMetadata(PropertyMetadata $metadata): void { $this->metadataStack->push($this->currentMetadata); $this->currentMetadata = $metadata; } /** * @return ClassMetadata|PropertyMetadata|null */ public function getCurrentMetadata() { return $this->currentMetadata; } /** * @return ClassMetadata|PropertyMetadata|null */ public function revertCurrentMetadata() { return $this->currentMetadata = $this->metadataStack->pop(); } /** * {@inheritdoc} */ public function getResult($data) { $this->navigator = null; return $data; } /** * Retrieves internalSubset even in bugfixed php versions */ private function getDomDocumentTypeEntitySubset(string $data): string { $startPos = $endPos = stripos($data, '<!doctype'); $braces = 0; do { $char = $data[$endPos++]; if ('<' === $char) { ++$braces; } if ('>' === $char) { --$braces; } } while ($braces > 0); $internalSubset = substr($data, $startPos, $endPos - $startPos); $internalSubset = str_replace(["\n", "\r"], '', $internalSubset); $internalSubset = preg_replace('/\s{2,}/', ' ', $internalSubset); $internalSubset = str_replace(['[ <!', '> ]>'], ['[<!', '>]>'], $internalSubset); return $internalSubset; } /** * {@inheritdoc} */ public function isNull($value): bool { if ($value instanceof \SimpleXMLElement) { // Workaround for https://bugs.php.net/bug.php?id=75168 and https://github.com/schmittjoh/serializer/issues/817 // If the "name" is empty means that we are on an not-existent node and subsequent operations on the object will trigger the warning: // "Node no longer exists" if ('' === $value->getName()) { // @todo should be "true", but for collections needs a default collection value. maybe something for the 2.0 return false; } $xsiAttributes = $value->attributes('http://www.w3.org/2001/XMLSchema-instance'); if ( isset($xsiAttributes['nil']) && ('true' === (string) $xsiAttributes['nil'] || '1' === (string) $xsiAttributes['nil']) ) { return true; } } return null === $value; } }
Submit
FILE
FOLDER
INFO
Name
Size
Permission
Action
Accessor
---
0755
Annotation
---
0755
Builder
---
0755
Construction
---
0755
ContextFactory
---
0755
EventDispatcher
---
0755
Exception
---
0755
Exclusion
---
0755
Expression
---
0755
GraphNavigator
---
0755
Handler
---
0755
Metadata
---
0755
Naming
---
0755
Ordering
---
0755
Twig
---
0755
Type
---
0755
Visitor
---
0755
AbstractVisitor.php
3185 bytes
0644
ArrayTransformerInterface.php
917 bytes
0644
Context.php
6262 bytes
0644
DeserializationContext.php
775 bytes
0644
Functions.php
414 bytes
0644
GraphNavigator.php
1077 bytes
0644
GraphNavigatorInterface.php
1104 bytes
0644
JsonDeserializationStrictVisitor.php
4085 bytes
0644
JsonDeserializationVisitor.php
6617 bytes
0644
JsonSerializationVisitor.php
5062 bytes
0644
NullAwareVisitorInterface.php
346 bytes
0644
SerializationContext.php
3475 bytes
0644
Serializer.php
9062 bytes
0644
SerializerBuilder.php
21521 bytes
0644
SerializerInterface.php
768 bytes
0644
VisitorInterface.php
938 bytes
0644
XmlDeserializationVisitor.php
19740 bytes
0644
XmlSerializationVisitor.php
20830 bytes
0644
N4ST4R_ID | Naxtarrr