From 4b71afe7c28c22dd3da37ac44d46ebff9b5a5a78 Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Sun, 18 Apr 2010 22:47:03 +0200 Subject: [PATCH] Improving metadata caching performance by only serializing what is absolutely necessary. --- lib/Doctrine/Common/Annotations/Parser.php | 4 +- .../ORM/Mapping/AssociationMapping.php | 55 ++++++++++++++- lib/Doctrine/ORM/Mapping/ClassMetadata.php | 69 +++++++++++++------ .../ORM/Mapping/ManyToManyMapping.php | 23 ++++++- lib/Doctrine/ORM/Mapping/OneToManyMapping.php | 23 ++++++- lib/Doctrine/ORM/Mapping/OneToOneMapping.php | 24 ++++++- .../Tests/ORM/Mapping/ClassMetadataTest.php | 2 + 7 files changed, 170 insertions(+), 30 deletions(-) diff --git a/lib/Doctrine/Common/Annotations/Parser.php b/lib/Doctrine/Common/Annotations/Parser.php index bde253d58..de373c3f8 100644 --- a/lib/Doctrine/Common/Annotations/Parser.php +++ b/lib/Doctrine/Common/Annotations/Parser.php @@ -1,7 +1,5 @@ _isNestedAnnotation && $this->_lexer->lookahead != null && + ( ! $this->_isNestedAnnotation && $this->_lexer->lookahead != null && ! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) && ! $this->_lexer->isNextToken(Lexer::T_AT)) || ! class_exists($name, false) diff --git a/lib/Doctrine/ORM/Mapping/AssociationMapping.php b/lib/Doctrine/ORM/Mapping/AssociationMapping.php index 18915ba3c..881f878fe 100644 --- a/lib/Doctrine/ORM/Mapping/AssociationMapping.php +++ b/lib/Doctrine/ORM/Mapping/AssociationMapping.php @@ -340,5 +340,58 @@ abstract class AssociationMapping ? $platform->quoteIdentifier($this->joinTable['name']) : $this->joinTable['name']; } - + + /** + * Determines which fields get serialized. + * + * It is only serialized what is necessary for best unserialization performance. + * That means any metadata properties that are not set or empty or simply have + * their default value are NOT serialized. + * + * @return array The names of all the fields that should be serialized. + */ + public function __sleep() + { + $serialized = array( + 'sourceEntityName', + 'targetEntityName', + 'sourceFieldName' + ); + + if ($this->isCascadeDetach) { + $serialized[] = 'isCascadeDetach'; + } + if ($this->isCascadeMerge) { + $serialized[] = 'isCascadeMerge'; + } + if ($this->isCascadePersist) { + $serialized[] = 'isCascadePersist'; + } + if ($this->isCascadeRefresh) { + $serialized[] = 'isCascadeRefresh'; + } + if ($this->isCascadeRemove) { + $serialized[] = 'isCascadeRemove'; + } + if ( ! $this->isOwningSide) { + $serialized[] = 'isOwningSide'; + } + if ($this->mappedBy) { + $serialized[] = 'mappedBy'; + } + if ($this->inversedBy) { + $serialized[] = 'inversedBy'; + } + if ($this->joinTable) { + $serialized[] = 'joinTable'; + } + if ($this->inherited) { + $serialized[] = 'inherited'; + } + if ($this->declared) { + $serialized[] = 'declared'; + } + + return $serialized; + } } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index c358cc007..a659e8235 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -264,8 +264,12 @@ class ClassMetadata extends ClassMetadataInfo /** * Determines which fields get serialized. + * + * It is only serialized what is necessary for best unserialization performance. + * That means any metadata properties that are not set or empty or simply have + * their default value are NOT serialized. * - * Parts that are NOT serialized because they can not be properly unserialized: + * Parts that are also NOT serialized because they can not be properly unserialized: * - reflClass (ReflectionClass) * - reflFields (ReflectionProperty array) * @@ -273,31 +277,56 @@ class ClassMetadata extends ClassMetadataInfo */ public function __sleep() { - return array( - 'associationMappings', // unserialization "bottleneck" with many associations - 'changeTrackingPolicy', + // This metadata is always serialized/cached. + $serialized = array( + 'associationMappings', 'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName'] - 'customRepositoryClassName', - 'discriminatorColumn', - 'discriminatorValue', - 'discriminatorMap', - 'fieldMappings',//TODO: Not all of this stuff needs to be serialized. Only type, columnName and fieldName. - 'fieldNames', - 'generatorType', + 'fieldMappings', + 'fieldNames', 'identifier', - 'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime. - 'inheritanceType', - 'isIdentifierComposite', - 'isMappedSuperclass', - 'isVersioned', - 'lifecycleCallbacks', + 'isIdentifierComposite', // TODO: REMOVE 'name', - 'parentClasses', 'table', 'rootEntityName', - 'subClasses', - 'versionField' + 'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime. ); + + // The rest of the metadata is only serialized if necessary. + if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) { + $serialized[] = 'changeTrackingPolicy'; + } + + if ($this->customRepositoryClassName) { + $serialized[] = 'customRepositoryClassName'; + } + + if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) { + $serialized[] = 'inheritanceType'; + $serialized[] = 'discriminatorColumn'; + $serialized[] = 'discriminatorValue'; + $serialized[] = 'discriminatorMap'; + $serialized[] = 'parentClasses'; + $serialized[] = 'subClasses'; + } + + if ($this->generatorType != self::GENERATOR_TYPE_NONE) { + $serialized[] = 'generatorType'; + } + + if ($this->isMappedSuperclass) { + $serialized[] = 'isMappedSuperclass'; + } + + if ($this->isVersioned) { + $serialized[] = 'isVersioned'; + $serialized[] = 'versionField'; + } + + if ($this->lifecycleCallbacks) { + $serialized[] = 'lifecycleCallbacks'; + } + + return $serialized; } /** diff --git a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php index 95aad2b41..35f7e451f 100644 --- a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php +++ b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php @@ -1,7 +1,5 @@ orderBy) { + $serialized[] = 'orderBy'; + } + return $serialized; + } } diff --git a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php index 0c4a2bb4f..5860c625e 100644 --- a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php @@ -1,7 +1,5 @@ loadOneToManyCollection($this, $conditions, $targetCollection); } + + /** + * Determines which fields get serialized. + * + * It is only serialized what is necessary for best unserialization performance. + * That means any metadata properties that are not set or empty or simply have + * their default value are NOT serialized. + * + * @return array The names of all the fields that should be serialized. + */ + public function __sleep() + { + $serialized = parent::__sleep(); + if ($this->orderBy) { + $serialized[] = 'orderBy'; + } + if ($this->orphanRemoval) { + $serialized[] = 'orphanRemoval'; + } + return $serialized; + } } diff --git a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php index c82558fb4..89a97449e 100644 --- a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php @@ -1,7 +1,5 @@ orphanRemoval) { + $serialized[] = 'orphanRemoval'; + } + return $serialized; + } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index b31f5077c..92231ae8e 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -20,8 +20,10 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $cm->rootEntityName); $this->assertEquals(array(), $cm->subClasses); $this->assertEquals(array(), $cm->parentClasses); + $this->assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm->inheritanceType); // Customize state + $cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE); $cm->setSubclasses(array("One", "Two", "Three")); $cm->setParentClasses(array("UserParent")); $cm->setCustomRepositoryClass("UserRepository");