diff --git a/UPGRADE_TO_2_1 b/UPGRADE_TO_2_1 index 8b289ecd4..f42244493 100644 --- a/UPGRADE_TO_2_1 +++ b/UPGRADE_TO_2_1 @@ -9,6 +9,7 @@ The EntityRepository now has an interface Doctrine\Common\Persistence\ObjectRepo The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory the operation of the new reader should be backwards compatible, but it has to be setup differently to work that way: + // new call to the AnnotationRegistry \Doctrine\Common\Annotations\AnnotationRegistry::registerFile('/doctrine-src/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'); $reader = new \Doctrine\Common\Annotations\AnnotationReader(); diff --git a/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php b/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php new file mode 100644 index 000000000..5475d998f --- /dev/null +++ b/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php @@ -0,0 +1,151 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Event\OnFlushEventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\UnitOfWork; + +/** + * Use this logger to dump the identity map during the onFlush event. This is useful for debugging + * weird UnitOfWork behavior with complex operations. + */ +class DebugUnitOfWorkListener +{ + private $file; + private $context; + + /** + * Pass a stream and contet information for the debugging session. + * + * The stream can be php://output to print to the screen. + * + * @param string $file + * @param string $context + */ + public function __construct($file = 'php://output', $context = '') + { + $this->file = $file; + $this->context = $context; + } + + public function onFlush(OnFlushEventArgs $args) + { + $this->dumpIdentityMap($args->getEntityManager()); + } + + /** + * Dump the contents of the identity map into a stream. + * + * @param EntityManager $em + * @return void + */ + public function dumpIdentityMap(EntityManager $em) + { + $uow = $em->getUnitOfWork(); + $identityMap = $uow->getIdentityMap(); + + $fh = fopen($this->file, "x+"); + if (count($identityMap) == 0) { + fwrite($fh, "Flush Operation [".$this->context."] - Empty identity map.\n"); + return; + } + + fwrite($fh, "Flush Operation [".$this->context."] - Dumping identity map:\n"); + foreach ($identityMap AS $className => $map) { + fwrite($fh, "Class: ". $className . "\n"); + foreach ($map AS $idHash => $entity) { + fwrite($fh, " Entity: " . $this->getIdString($entity, $uow) . " " . spl_object_hash($entity)."\n"); + fwrite($fh, " Associations:\n"); + + $cm = $em->getClassMetadata($className); + foreach ($cm->associationMappings AS $field => $assoc) { + fwrite($fh, " " . $field . " "); + $value = $cm->reflFields[$field]->getValue($entity); + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($value === null) { + fwrite($fh, " NULL\n"); + } else { + if ($value instanceof Proxy && !$value->__isInitialized__) { + fwrite($fh, "[PROXY] "); + } + + fwrite($fh, $this->getIdString($value, $uow) . " " . spl_object_hash($value) . "\n"); + } + } else { + $initialized = !($value instanceof PersistentCollection) || $value->isInitialized(); + if ($value === null) { + fwrite($fh, " NULL\n"); + } else if ($initialized) { + fwrite($fh, "[INITIALIZED] " . $this->getType($value). " " . count($value) . " elements\n"); + foreach ($value AS $obj) { + fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n"); + } + } else { + fwrite($fh, "[PROXY] " . $this->getType($value) . " unknown element size\n"); + foreach ($value->unwrap() AS $obj) { + fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n"); + } + } + } + } + } + } + fclose($fh); + } + + private function getType($var) + { + if (is_object($var)) { + $refl = new \ReflectionObject($var); + return $refl->getShortname(); + } else { + return gettype($var); + } + } + + private function getIdString($entity, $uow) + { + if ($uow->isInIdentityMap($entity)) { + $ids = $uow->getEntityIdentifier($entity); + $idstring = ""; + foreach ($ids AS $k => $v) { + $idstring .= $k."=".$v; + } + } else { + $idstring = "NEWOBJECT "; + } + + $state = $uow->getEntityState($entity); + if ($state == UnitOfWork::STATE_NEW) { + $idstring .= " [NEW]"; + } else if ($state == UnitOfWork::STATE_REMOVED) { + $idstring .= " [REMOVED]"; + } else if ($state == UnitOfWork::STATE_MANAGED) { + $idstring .= " [MANAGED]"; + } else if ($state == UnitOfwork::STATE_DETACHED) { + $idstring .= " [DETACHED]"; + } + + return $idstring; + } +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Version.php b/lib/Doctrine/ORM/Version.php index 5e811edd0..183887147 100644 --- a/lib/Doctrine/ORM/Version.php +++ b/lib/Doctrine/ORM/Version.php @@ -36,7 +36,7 @@ class Version /** * Current Doctrine Version */ - const VERSION = '2.1.0RC4-DEV'; + const VERSION = '2.2.0-DEV'; /** * Compares a Doctrine version with the current one. diff --git a/lib/vendor/doctrine-common b/lib/vendor/doctrine-common index 74a2c924c..40f1bf16e 160000 --- a/lib/vendor/doctrine-common +++ b/lib/vendor/doctrine-common @@ -1 +1 @@ -Subproject commit 74a2c924cd08b30785877808b1fb519b4b2e60b1 +Subproject commit 40f1bf16e84ddc5291a6a63aa00b9879c40e3500 diff --git a/lib/vendor/doctrine-dbal b/lib/vendor/doctrine-dbal index be3790059..0127ee98a 160000 --- a/lib/vendor/doctrine-dbal +++ b/lib/vendor/doctrine-dbal @@ -1 +1 @@ -Subproject commit be3790059cc43b674a55548eb42d5d25846ea6a9 +Subproject commit 0127ee98a4301f2f6e3463c824adc3a3687f901f diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 92320e3a4..1f8032aeb 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -303,6 +303,10 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $evm->addEventSubscriber($subscriberInstance); } } + + if (isset($GLOBALS['debug_uow_listener'])) { + $evm->addEventListener(array('onFlush'), new \Doctrine\ORM\Tools\DebugUnitOfWorkListener()); + } return \Doctrine\ORM\EntityManager::create($conn, $config); }