/** * * INTERNAL: Performance sensitive method * * @throws \Doctrine\OXM\Mapping\MappingException * @param \XMLReader $cursor * @return object */ private function doUnmarshal(XMLReader $cursor, $endElement = NULL, $classMetadata = NULL, $virtualNamespace = NULL) { /* $allMappedXmlNodes = $this->classMetadataFactory->getAllXmlNodes(); $allMappedWrapperXmlNodes = $this->classMetadataFactory->getAllWrapperXmlNodes(); */ static $allMappedXmlNodes, $allMappedWrapperXmlNodes, $alternativeClasses; if (empty($allMappedXmlNodes)) { list($allMappedXmlNodes, $allMappedWrapperXmlNodes, $alternativeClasses) = $this->classMetadataFactory->getAllMaps(); } if ($cursor->nodeType !== XMLReader::ELEMENT && $cursor->nodeType !== XMLReader::CDATA) { throw MarshallerException::invalidMarshallerState($cursor); } $elementName = $cursor->localName; if (!empty($classMetadata)) { $isInWrapper = !empty($endElement); } else { $isInWrapper = FALSE; if (!array_key_exists($elementName, $allMappedXmlNodes) && !array_key_exists($elementName, $allMappedWrapperXmlNodes) && !array_key_exists('*', $allMappedXmlNodes)) { if ($this->allowUnknownElements) { return $this->doUnmarshalUnknownElement($cursor, $endElement, $classMetadata, $virtualNamespace); } else { throw MappingException::invalidMapping($elementName); } } if (array_key_exists($elementName, $allMappedXmlNodes)) { $classes = $allMappedXmlNodes[$elementName]; } else { $classes = $allMappedXmlNodes['*']; } if (count($classes) == 1) { $namespaces = array_keys($classes); $className = $classes[array_shift($namespaces)]; } else { // Should try to work out the correct namespace version here $attributes = $this->getElementAttributes($cursor); $attributes['-name'] = $cursor->localName; foreach ($classes as $thisClass) { $className = $this->classMetadataFactory->getAlternativeClassForNamespace($thisClass, $virtualNamespace, FALSE); if (!$className) { $className = $this->classMetadataFactory->getAlternativeClassForAttributes($thisClass, $virtualNamespace, $attributes, FALSE); } if (!$className) { break; } } if (!$className) { $namespaces = array_keys($classes); $className = $classes[array_shift($namespaces)]; error_log('Scream: too many choices! Selected ${className} for ${elementName}'); error_log(print_r($classes, TRUE)); } } $classMetadata = $this->classMetadataFactory->getMetadataFor($className); } $mappedObject = $classMetadata->newInstance(); // Pre Unmarshal hook -- doesn't make sense on the wrapper if ($classMetadata->hasLifecycleCallbacks(Events::preUnmarshal)) { $classMetadata->invokeLifecycleCallbacks(Events::preUnmarshal, $mappedObject); } if (!$isInWrapper && $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) || $isInWrapper) { $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 || $isInWrapper && $cursor->name === $endElement)) { // 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) || $classMetadata->hasXmlField('*')) { $fieldName = $classMetadata->getFieldName($cursor->localName); $inAnyMode = FALSE; if (!$fieldName) { $fieldName = $classMetadata->getFieldName('*'); $inAnyMode = TRUE; } // Check for mapped entity as child, add recursively $fieldMapping = $classMetadata->getFieldMapping($fieldName); if ($this->classMetadataFactory->hasMetadataFor($fieldMapping['type'])) { $namespace = $cursor->namespaceURI ? $cursor->namespaceURI : $virtualNamespace; $childClass = NULL; if ($inAnyMode) { $childClass = $this->classMetadataFactory->getAlternativeClassForNamespace($fieldMapping['type'], $namespace, FALSE); if (!$childClass) { $childClass = $this->classMetadataFactory->getAlternativeClassForName($fieldMapping['type'], $cursor->localName, FALSE); } if (!$childClass) { // Check the global mapping if (!empty($allMappedXmlNodes[$cursor->localName])) { $childClass = $allMappedXmlNodes[$cursor->localName]; if (is_array($childClass)) { $childClass = array_shift($childClass); } } } } if (empty($childClass)) { $attributes = $this->getElementAttributes($cursor); $attributes['-name'] = $cursor->localName; $childClass = $this->classMetadataFactory->getAlternativeClassForAttributes($fieldMapping['type'], $namespace, $attributes, FALSE); if (!$childClass) { $childClass = $this->classMetadataFactory->getAlternativeClassForNamespace($fieldMapping['type'], $namespace); } } $childClassMetadata = $this->classMetadataFactory->getMetadataFor($childClass); if ($classMetadata->hasFieldWrapping($fieldName) && !$isInWrapper) { $cursor->moveToElement(); $collectionElements[$fieldName] = $this->doUnmarshal($cursor, $fieldName, $classMetadata, $namespace); } elseif ($classMetadata->isCollection($fieldName) || $isInWrapper) { $collectionElements[$fieldName][] = $this->doUnmarshal($cursor, NULL, $childClassMetadata, $namespace); } else { $classMetadata->setFieldValue($mappedObject, $fieldName, $this->doUnmarshal($cursor, NULL, $childClassMetadata, $namespace)); } } elseif ($cursor->isEmptyElement) { // Don't move on just yet! $type = Type::getType($fieldMapping['type']); if ($classMetadata->isCollection($fieldName) || $isInWrapper) { $collectionElements[$fieldName][] = $type->convertToPHPValue($cursor->value); } else { $classMetadata->setFieldValue($mappedObject, $fieldName, $type->convertToPHPValue($cursor->value)); } // Still don't move on as the loop will } 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) || $isInWrapper) { $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 (array_key_exists($cursor->name, $allMappedXmlNodes) || array_key_exists($cursor->name, $allMappedWrapperXmlNodes) && isset($allMappedWrapperXmlNodes[$cursor->name][$elementName])) { // look for inherited child directly if (array_key_exists($cursor->name, $allMappedWrapperXmlNodes) && isset($allMappedWrapperXmlNodes[$cursor->name][$elementName])) { if ($allMappedWrapperXmlNodes[$cursor->name][$elementName] === NULL) { $childClassMetadata = NULL; } else { $childClassMetadata = $this->classMetadataFactory->getMetadataFor($allMappedWrapperXmlNodes[$cursor->name][$elementName]); } } elseif (isset($allMappedXmlNodes[$cursor->name])) { if (is_array($allMappedXmlNodes[$cursor->name])) { // FIXME: This is fragile as it doesn't account for the same xml name being used more than once $childClasses = array_values($allMappedXmlNodes[$cursor->name]); $childClass = array_shift($childClasses); } else { $childClass = $allMappedXmlNodes[$cursor->name]; } $childClassMetadata = $this->classMetadataFactory->getMetadataFor($childClass); } // todo: ensure this potential child inherits from parent correctly $fieldName = null; $isWrapper = FALSE; foreach ($classMetadata->getFieldMappings() as $fieldMapping) { if (isset($allMappedXmlNodes[$cursor->name]) && $fieldMapping['type'] == $allMappedXmlNodes[$cursor->name]) { $fieldName = $fieldMapping['fieldName']; break; } elseif (isset($allMappedWrapperXmlNodes[$cursor->name][$elementName]) && $fieldMapping['type'] == $allMappedWrapperXmlNodes[$cursor->name][$elementName]) { $fieldName = $fieldMapping['fieldName']; $isWrapper = TRUE; break; } else { // Walk parent tree if (!isset($childClassMetadata)) { continue; } foreach ($childClassMetadata->getParentClasses() as $parentClass) { if ($fieldMapping['type'] == $parentClass) { $fieldName = $fieldMapping['fieldName']; break 2; } } } } if ($fieldName !== null) { $namespace = $cursor->namespaceURI ? $cursor->namespaceURI : $virtualNamespace; if ($isWrapper) { $thisFieldMapping = $classMetadata->getFieldMapping($fieldName); $childEndElement = $thisFieldMapping['wrapper']; $childElements = $this->doUnmarshal($cursor, $childEndElement, $classMetadata, $namespace); $collectionElements[$fieldName] = $childElements; } elseif ($classMetadata->isCollection($fieldName)) { $collectionElements[$fieldName][] = $this->doUnmarshal($cursor, NULL, NULL, $namespace); } else { $classMetadata->setFieldValue($mappedObject, $fieldName, $this->doUnmarshal($cursor, NULL, NULL, $namespace)); } } } elseif ($this->allowUnknownElements) { $elementName = $cursor->name; $mappedObject->{$elementName} = $this->doUnmarshalUnknownElement($cursor, $endElement, $classMetadata, $virtualNamespace); } } if (!empty($collectionElements)) { if (!$isInWrapper || !isset($fieldName)) { foreach ($collectionElements as $fieldName => $elements) { $classMetadata->setFieldValue($mappedObject, $fieldName, $elements); } } else { if (!empty($collectionElements[$fieldName]) && (is_array($collectionElements[$fieldName]) && !empty($collectionElements[$fieldName][0]))) { $mappedObject = $collectionElements[$fieldName]; } } } } if (!$isInWrapper) { // PostUnmarshall hook if ($classMetadata->hasLifecycleCallbacks(Events::postUnmarshal)) { $classMetadata->invokeLifecycleCallbacks(Events::postUnmarshal, $mappedObject); } } return $mappedObject; }