private function doUnmarshal(XMLReader $cursor)
{
$allMappedXmlNodes = $this->classMetadataFactory->getAllXmlNodes();
$knownMappedNodes = array_keys($allMappedXmlNodes);
if ($cursor->nodeType !== XMLReader::ELEMENT && $cursor->nodeType !== XMLReader::CDATA) {
throw MarshallerException::invalidMarshallerState($cursor);
}
$elementName = $cursor->localName;
if (!in_array($elementName, $knownMappedNodes)) {
throw MappingException::invalidMapping($elementName);
}
$classMetadata = $this->classMetadataFactory->getMetadataFor($allMappedXmlNodes[$elementName]);
$mappedObject = $classMetadata->newInstance();
// Pre Unmarshal hook
if ($classMetadata->hasLifecycleCallbacks(Events::preUnmarshal)) {
$classMetadata->invokeLifecycleCallbacks(Events::preUnmarshal, $mappedObject);
}
if ($cursor->hasAttributes) {
while ($cursor->moveToNextAttribute()) {
if ($classMetadata->hasXmlField($cursor->name)) {
$fieldName = $classMetadata->getFieldName($cursor->name);
$fieldMapping = $classMetadata->getFieldMapping($fieldName);
$type = Type::getType($fieldMapping['type']);
if ($classMetadata->isRequired($fieldName) && $cursor->value === null) {
throw MappingException::fieldRequired($classMetadata->name, $fieldName);
}
if ($classMetadata->isCollection($fieldName)) {
$convertedValues = array();
foreach (explode(" ", $cursor->value) as $value) {
$convertedValues[] = $type->convertToPHPValue($value);
}
$classMetadata->setFieldValue($mappedObject, $fieldName, $convertedValues);
} else {
$classMetadata->setFieldValue($mappedObject, $fieldName, $type->convertToPHPValue($cursor->value));
}
}
}
$cursor->moveToElement();
}
if (!$cursor->isEmptyElement) {
$collectionElements = array();
while ($cursor->read()) {
if ($cursor->nodeType === XMLReader::END_ELEMENT && $cursor->name === $elementName) {
// we're at the original element closing node, bug out
break;
}
if ($cursor->nodeType !== XMLReader::ELEMENT && $cursor->nodeType !== XMLReader::TEXT && $cursor->nodeType !== XMLReader::CDATA) {
// skip insignificant elements
continue;
}
if ($classMetadata->hasXmlField($cursor->localName)) {
$fieldName = $classMetadata->getFieldName($cursor->localName);
// Check for mapped entity as child, add recursively
$fieldMapping = $classMetadata->getFieldMapping($fieldName);
if ($this->classMetadataFactory->hasMetadataFor($fieldMapping['type'])) {
if ($classMetadata->isCollection($fieldName)) {
$collectionElements[$fieldName][] = $this->doUnmarshal($cursor);
} else {
$classMetadata->setFieldValue($mappedObject, $fieldName, $this->doUnmarshal($cursor));
}
} else {
// assume text element (dangerous?)
$cursor->read();
if (!$cursor->isEmptyElement && $cursor->nodeType !== XMLReader::END_ELEMENT) {
if ($cursor->nodeType !== XMLReader::TEXT && $cursor->nodeType !== XMLReader::CDATA) {
throw MarshallerException::invalidMarshallerState($cursor);
}
$type = Type::getType($fieldMapping['type']);
if ($classMetadata->isCollection($fieldName)) {
$collectionElements[$fieldName][] = $type->convertToPHPValue($cursor->value);
} else {
$classMetadata->setFieldValue($mappedObject, $fieldName, $type->convertToPHPValue($cursor->value));
}
$cursor->read();
}
}
} elseif ($cursor->nodeType === XMLReader::TEXT || $cursor->nodeType === XMLReader::CDATA) {
foreach ($classMetadata->getFieldNames() as $fieldName) {
if (ClassMetadata::XML_VALUE === $classMetadata->getFieldXmlNode($fieldName)) {
$fieldMapping = $classMetadata->getFieldMapping($fieldName);
$type = Type::getType($fieldMapping['type']);
$classMetadata->setFieldValue($mappedObject, $fieldName, $type->convertToPHPValue($cursor->value));
}
}
} elseif (in_array($cursor->name, $knownMappedNodes)) {
// look for inherited child directly
$childClassMetadata = $this->classMetadataFactory->getMetadataFor($allMappedXmlNodes[$cursor->name]);
// todo: ensure this potential child inherits from parent correctly
$fieldName = null;
foreach ($classMetadata->getFieldMappings() as $fieldMapping) {
if ($fieldMapping['type'] == $allMappedXmlNodes[$cursor->name]) {
$fieldName = $fieldMapping['fieldName'];
} else {
// Walk parent tree
foreach ($childClassMetadata->getParentClasses() as $parentClass) {
if ($fieldMapping['type'] == $parentClass) {
$fieldName = $fieldMapping['fieldName'];
}
}
}
}
if ($fieldName !== null) {
if ($classMetadata->isCollection($fieldName)) {
$collectionElements[$fieldName][] = $this->doUnmarshal($cursor);
} else {
$classMetadata->setFieldValue($mappedObject, $fieldName, $this->doUnmarshal($cursor));
}
}
}
}
if (!empty($collectionElements)) {
foreach ($collectionElements as $fieldName => $elements) {
$classMetadata->setFieldValue($mappedObject, $fieldName, $elements);
}
}
}
// PostUnmarshall hook
if ($classMetadata->hasLifecycleCallbacks(Events::postUnmarshal)) {
$classMetadata->invokeLifecycleCallbacks(Events::postUnmarshal, $mappedObject);
}
return $mappedObject;
}