Skip to content

Commit

Permalink
Add session assertions in LifecycleEventManager
Browse files Browse the repository at this point in the history
  • Loading branch information
alcaeus committed Jan 8, 2024
1 parent 701dde2 commit 84e4eb2
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 12 deletions.
5 changes: 5 additions & 0 deletions lib/Doctrine/ODM/MongoDB/MongoDBException.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,9 @@ public static function cannotCreateRepository(string $className): self
{
return new self(sprintf('Cannot create repository for class "%s".', $className));
}

public static function transactionalSessionMismatch(): self
{
return new self('The transactional operation cannot be executed because it was started in a different session.');
}
}
6 changes: 4 additions & 2 deletions lib/Doctrine/ODM/MongoDB/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -456,10 +456,12 @@ public function commit(array $options = []): void
$this->evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->dm));

if ($this->useTransaction($options)) {
$this->lifecycleEventManager->enableTransactionalMode();
$session = $this->dm->getClient()->startSession();

$this->lifecycleEventManager->enableTransactionalMode($session);

with_transaction(
$this->dm->getClient()->startSession(),
$session,
function (Session $session) use ($options): void {
$this->doCommit(['session' => $session] + $this->stripTransactionOptions($options));
},
Expand Down
29 changes: 19 additions & 10 deletions lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Doctrine\ODM\MongoDB\Event\PreUpdateEventArgs;
use Doctrine\ODM\MongoDB\Events;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use Doctrine\ODM\MongoDB\MongoDBException;
use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface;
use Doctrine\ODM\MongoDB\UnitOfWork;
use MongoDB\Driver\Session;
Expand All @@ -24,6 +25,8 @@ final class LifecycleEventManager
{
private bool $transactionalModeEnabled = false;

private ?Session $session = null;

/** @var array<string, array<string, true>> */
private array $transactionalEvents = [];

Expand All @@ -34,12 +37,14 @@ public function __construct(private DocumentManager $dm, private UnitOfWork $uow
public function clearTransactionalState(): void
{
$this->transactionalModeEnabled = false;
$this->session = null;
$this->transactionalEvents = [];
}

public function enableTransactionalMode(): void
public function enableTransactionalMode(Session $session): void
{
$this->transactionalModeEnabled = true;
$this->session = $session;
}

/**
Expand Down Expand Up @@ -76,7 +81,7 @@ public function postCollectionLoad(PersistentCollectionInterface $coll): void
*/
public function postPersist(ClassMetadata $class, object $document, ?Session $session = null): void
{
if (! $this->shouldDispatchEvent($document, Events::postPersist)) {
if (! $this->shouldDispatchEvent($document, Events::postPersist, $session)) {
return;
}

Expand All @@ -97,7 +102,7 @@ public function postPersist(ClassMetadata $class, object $document, ?Session $se
*/
public function postRemove(ClassMetadata $class, object $document, ?Session $session = null): void
{
if (! $this->shouldDispatchEvent($document, Events::postRemove)) {
if (! $this->shouldDispatchEvent($document, Events::postRemove, $session)) {
return;
}

Expand All @@ -118,7 +123,7 @@ public function postRemove(ClassMetadata $class, object $document, ?Session $ses
*/
public function postUpdate(ClassMetadata $class, object $document, ?Session $session = null): void
{
if (! $this->shouldDispatchEvent($document, Events::postUpdate)) {
if (! $this->shouldDispatchEvent($document, Events::postUpdate, $session)) {
return;
}

Expand All @@ -139,7 +144,7 @@ public function postUpdate(ClassMetadata $class, object $document, ?Session $ses
*/
public function prePersist(ClassMetadata $class, object $document): void
{
if (! $this->shouldDispatchEvent($document, Events::prePersist)) {
if (! $this->shouldDispatchEvent($document, Events::prePersist, null)) {
return;
}

Expand All @@ -159,7 +164,7 @@ public function prePersist(ClassMetadata $class, object $document): void
*/
public function preRemove(ClassMetadata $class, object $document): void
{
if (! $this->shouldDispatchEvent($document, Events::preRemove)) {
if (! $this->shouldDispatchEvent($document, Events::preRemove, null)) {
return;
}

Expand All @@ -179,7 +184,7 @@ public function preRemove(ClassMetadata $class, object $document): void
*/
public function preUpdate(ClassMetadata $class, object $document, ?Session $session = null): void
{
if (! $this->shouldDispatchEvent($document, Events::preUpdate)) {
if (! $this->shouldDispatchEvent($document, Events::preUpdate, $session)) {
return;
}

Expand Down Expand Up @@ -251,7 +256,7 @@ private function cascadePostUpdate(ClassMetadata $class, object $document, ?Sess
$entryClass = $this->dm->getClassMetadata($entry::class);
$event = $this->uow->isScheduledForInsert($entry) ? Events::postPersist : Events::postUpdate;

if (! $this->shouldDispatchEvent($entry, $event)) {
if (! $this->shouldDispatchEvent($entry, $event, $session)) {
continue;
}

Expand All @@ -260,7 +265,7 @@ private function cascadePostUpdate(ClassMetadata $class, object $document, ?Sess
$entryClass->invokeLifecycleCallbacks($event, $entry, [$eventArgs]);
$this->dispatchEvent($entryClass, $event, $eventArgs);

$this->cascadePostUpdate($entryClass, $entry);
$this->cascadePostUpdate($entryClass, $entry, $session);
}
}
}
Expand Down Expand Up @@ -298,12 +303,16 @@ private function dispatchEvent(ClassMetadata $class, string $eventName, ?EventAr
$this->evm->dispatchEvent($eventName, $eventArgs);
}

private function shouldDispatchEvent(object $document, string $eventName): bool
private function shouldDispatchEvent(object $document, string $eventName, ?Session $session): bool
{
if (! $this->transactionalModeEnabled) {
return true;
}

if ($session !== $this->session) {
throw MongoDBException::transactionalSessionMismatch();
}

// Check whether the event has already been dispatched.
$hasDispatched = isset($this->transactionalEvents[spl_object_hash($document)][$eventName]);

Expand Down

0 comments on commit 84e4eb2

Please sign in to comment.