/** * Loads the metadata for the specified class into the provided container. * * @param string $className * @param ClassMetadataInfo $metadata */ public function loadMetadataForClass($className, ClassMetadataInfo $metadata) { foreach ($this->drivers as $namespace => $driver) { if (strpos($className, $namespace) === 0) { $driver->loadMetadataForClass($className, $metadata); return; } } throw MappingException::classIsNotAValidXmlEntityOrXmlMappedSuperClass($className); }
/** * {@inheritDoc} */ public function getAllClassNames() { if ($this->classNames !== null) { return $this->classNames; } if (!$this->paths) { throw MappingException::pathRequired(); } $classes = array(); $includedFiles = array(); foreach ($this->paths as $path) { if (!is_dir($path)) { throw MappingException::fileMappingDriversRequiresConfiguredDirectoryPath($path); } $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::LEAVES_ONLY); foreach ($iterator as $file) { if (($fileName = $file->getBasename($this->fileExtension)) == $file->getBasename()) { continue; } $sourceFile = realpath($file->getPathName()); require_once $sourceFile; $includedFiles[] = $sourceFile; } } $declared = get_declared_classes(); foreach ($declared as $className) { $rc = new \ReflectionClass($className); $sourceFile = $rc->getFileName(); if (in_array($sourceFile, $includedFiles) && !$this->isTransient($className)) { $classes[] = $className; } } $this->classNames = $classes; return $classes; }
/** * Complete and validate type mappings * * @param string $className * @param ClassMetadataInfo $class */ private function completeMappingTypeValidation($className, ClassMetadataInfo $class) { foreach ($class->fieldMappings as $fieldName => $mapping) { if (Type::hasType($mapping['type'])) { continue; } // Support type as a mapped class? if (!$this->hasMetadataFor($mapping['type']) && !$this->getMetadataFor($mapping['type'])) { throw MappingException::fieldTypeNotFound($className, $fieldName, $mapping['type']); } // Mapped classes must have binding node type XML_ELEMENT if ($mapping['node'] !== ClassMetadataInfo::XML_ELEMENT) { throw MappingException::customTypeWithoutNodeElement($className, $fieldName); } } }
/** * * INTERNAL: Performance sensitive method * * @throws \Doctrine\OXM\Mapping\MappingException * @param \XMLReader $cursor * @return object */ private function doUnmarshal(XMLReader $cursor) { $allMappedXmlNodes = $this->classMetadataFactory->getAllXmlNodes(); $knownMappedNodes = array_keys($allMappedXmlNodes); if ($cursor->nodeType !== XMLReader::ELEMENT) { 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 (true) { $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) { // 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->nodeType !== XMLReader::TEXT) { 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 (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; }
/** * Gets the specified field's value off the given entity. * * @param object $entity * @param string $fieldName */ public function getFieldValue($entity, $fieldName) { if ($this->fieldMappings[$fieldName]['direct']) { return $this->reflFields[$fieldName]->getValue($entity); } else { if (!array_key_exists('getMethod', $this->fieldMappings[$fieldName])) { $this->fieldMappings[$fieldName]['getMethod'] = $this->inferGetter($fieldName); } $getter = $this->fieldMappings[$fieldName]['getMethod']; if ($this->reflClass->hasMethod($getter)) { return call_user_func(array($entity, $getter)); } else { throw MappingException::fieldGetMethodDoesNotExist($this->name, $fieldName, $getter); } } }
/** * Gets the names of all mapped classes known to this driver. * * @return array The names of all mapped classes known to this driver. */ public function getAllClassNames() { $classes = array(); if ($this->paths) { foreach ((array) $this->paths as $path) { if (!is_dir($path)) { throw MappingException::fileMappingDriversRequiresConfiguredDirectoryPath($path); } $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::LEAVES_ONLY); foreach ($iterator as $file) { if (($fileName = $file->getBasename($this->fileExtension)) == $file->getBasename()) { continue; } // NOTE: All files found here means classes are not transient! $classes[] = str_replace('.', '\\', $fileName); } } } return $classes; }
/** * @param string $fieldName * @return boolean * */ public function isNullable($fieldName) { if (!isset($this->fieldMappings[$fieldName])) { throw MappingException::mappingNotFound($this->name, $fieldName); } return $this->fieldMappings[$fieldName]['nullable'] ? true : false; }
/** * * 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; }
/** * {@inheritdoc} */ public function loadMetadataForClass($className, ClassMetadataInfo $metadata) { $xmlRoot = $this->getElement($className); if ($xmlRoot->getName() == 'entity') { if (isset($xmlRoot['root']) && $xmlRoot['root'] == "true") { $metadata->isRoot = true; $metadata->setCustomRepositoryClass(isset($xmlRoot['repository-class']) ? (string) $xmlRoot['repository-class'] : null); } } else { if ($xmlRoot->getName() == 'mapped-superclass') { $metadata->isMappedSuperclass = true; } else { throw MappingException::classIsNotAValidXmlEntityOrXmlMappedSuperClass($className); } } $metadata->setName($className); // Evalute xml-name attribute if (isset($xmlRoot['xml-name'])) { $metadata->setXmlName((string) $xmlRoot['xml-name']); } else { $metadata->setXmlName(Inflector::xmlize($className)); } // Evaluate change-tracking-policy attribute if (isset($xmlRoot['change-tracking-policy'])) { $metadata->setChangeTrackingPolicy(constant('Doctrine\\OXM\\Mapping\\ClassMetadata::CHANGETRACKING_' . strtoupper((string) $xmlRoot['change-tracking-policy']))); } // Evaluate <namespaces...> if (isset($xmlRoot->namespaces)) { $namespaces = array(); foreach ($xmlRoot->namespaces->namespace as $namespace) { $namespaces[] = array('url' => (string) $namespace['url'], 'prefix' => (string) $namespace['prefix']); } $metadata->setXmlNamespaces($namespaces); } // Evaluate <field ...> mappings if (isset($xmlRoot->field)) { foreach ($xmlRoot->field as $fieldMapping) { $mapping = array('fieldName' => (string) $fieldMapping['name'], 'type' => (string) $fieldMapping['type'], 'node' => constant('Doctrine\\OXM\\Mapping\\ClassMetadata::XML_' . strtoupper((string) $fieldMapping['node']))); if (isset($fieldMapping['xml-name'])) { $mapping['name'] = (string) $fieldMapping['xml-name']; } if (isset($fieldMapping['identifier'])) { $mapping['id'] = (bool) $fieldMapping['identifier']; } if (isset($fieldMapping['direct'])) { $mapping['direct'] = (bool) $fieldMapping['direct']; } if (isset($fieldMapping['nulable'])) { $mapping['nullable'] = (bool) $fieldMapping['nullable']; } if (isset($fieldMapping['required'])) { $mapping['required'] = (bool) $fieldMapping['required']; } if (isset($fieldMapping['collection'])) { $mapping['collection'] = (bool) $fieldMapping['collection']; } if (isset($fieldMapping['get-method'])) { $mapping['getMethod'] = (string) $fieldMapping['get-method']; } if (isset($fieldMapping['set-method'])) { $mapping['setMethod'] = (string) $fieldMapping['set-method']; } if (isset($fieldMapping['prefix'])) { $mapping['prefix'] = (string) $fieldMapping['prefix']; } if (isset($fieldMapping['wrapper'])) { $mapping['wrapper'] = (string) $fieldMapping['wrapper']; } $metadata->mapField($mapping); } } // Evaluate <lifecycle-callbacks...> if (isset($xmlRoot->{'lifecycle-callbacks'})) { foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) { $metadata->addLifecycleCallback((string) $lifecycleCallback['method'], constant('Doctrine\\OXM\\Events::' . (string) $lifecycleCallback['type'])); } } }