[FEATURE] Migrate all Extbase-related signals to PSR-14 events 28/62528/13
authorBenni Mack <benni@typo3.org>
Tue, 3 Dec 2019 14:39:29 +0000 (15:39 +0100)
committerSusanne Moog <look@susi.dev>
Tue, 14 Jan 2020 20:05:56 +0000 (21:05 +0100)
Nine new PSR-14 events are introduced which replace the existing
Extbase Signals in Persistence and MVC area.

Resolves: #89870
Releases: master
Change-Id: I6c56fc0562ba8c02899d5de07e39443167e6311a
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/62528
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Jörg Bösche <typo3@joergboesche.de>
Tested-by: Susanne Moog <look@susi.dev>
Reviewed-by: Jörg Bösche <typo3@joergboesche.de>
Reviewed-by: Susanne Moog <look@susi.dev>
22 files changed:
typo3/sysext/beuser/Classes/Controller/BackendUserController.php
typo3/sysext/core/Documentation/Changelog/master/Deprecation-89870-NewPSR-14EventsForExtbase-relatedSignals.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Feature-89870-NewPSR-14EventsForExtbase-relatedSignals.rst [new file with mode: 0644]
typo3/sysext/extbase/Classes/Compatibility/SlotReplacement.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Event/Mvc/AfterRequestDispatchedEvent.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Event/Mvc/BeforeActionCallEvent.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Event/Persistence/AfterObjectThawedEvent.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Event/Persistence/EntityAddedToPersistenceEvent.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Event/Persistence/EntityFinalizedAfterPersistenceEvent.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Event/Persistence/EntityPersistedEvent.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Event/Persistence/EntityRemovedFromPersistenceEvent.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Event/Persistence/EntityUpdatedInPersistenceEvent.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Event/Persistence/ModifyQueryBeforeFetchingObjectDataEvent.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Event/Persistence/ModifyResultAfterFetchingObjectDataEvent.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php
typo3/sysext/extbase/Classes/Mvc/Dispatcher.php
typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php
typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php
typo3/sysext/extbase/Classes/SignalSlot/Dispatcher.php
typo3/sysext/extbase/Configuration/Services.yaml
typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php
typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php

index 61a4dab..faeb8a0 100644 (file)
@@ -14,7 +14,6 @@ namespace TYPO3\CMS\Beuser\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
-use Psr\EventDispatcher\EventDispatcherInterface;
 use TYPO3\CMS\Backend\Authentication\Event\SwitchUserEvent;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Session\Backend\SessionBackendInterface;
@@ -62,11 +61,6 @@ class BackendUserController extends ActionController
     protected $backendUserSessionRepository;
 
     /**
-     * @var EventDispatcherInterface
-     */
-    protected $eventDispatcher;
-
-    /**
      * @param \TYPO3\CMS\Beuser\Service\ModuleDataStorageService $moduleDataStorageService
      */
     public function injectModuleDataStorageService(\TYPO3\CMS\Beuser\Service\ModuleDataStorageService $moduleDataStorageService)
@@ -98,11 +92,6 @@ class BackendUserController extends ActionController
         $this->backendUserSessionRepository = $backendUserSessionRepository;
     }
 
-    public function injectEventDispatcher(EventDispatcherInterface $eventDispatcher)
-    {
-        $this->eventDispatcher = $eventDispatcher;
-    }
-
     /**
      * Load and persist module data
      *
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-89870-NewPSR-14EventsForExtbase-relatedSignals.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-89870-NewPSR-14EventsForExtbase-relatedSignals.rst
new file mode 100644 (file)
index 0000000..c78dad3
--- /dev/null
@@ -0,0 +1,58 @@
+.. include:: ../../Includes.txt
+
+===================================================================
+Deprecation: #89870 - New PSR-14 Events for Extbase-related signals
+===================================================================
+
+See :issue:`89870`
+
+Description
+===========
+
+The following signals have been marked as deprecated in favor of new PSR-14 events:
+
+- :php:`TYPO3\CMS\Extbase\Mvc\Dispatcher::afterRequestDispatch`
+- :php:`TYPO3\CMS\Extbase\Mvc\Controller\ActionController::beforeCallActionMethod`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::afterMappingSingleRow`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::beforeGettingObjectData`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::afterGettingObjectData`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::endInsertObject`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::afterUpdateObject`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::afterPersistObject`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::afterRemoveObject`
+
+The method :php:`emitBeforeCallActionMethodSignal` in :php:`ActionController`
+has been deprecated and is not called by Extbase itself anymore.
+
+Impact
+======
+
+Using any of the signals will still work as expected, but will trigger
+a deprecation warning.
+
+Calling the method :php:`emitBeforeCallActionMethodSignal` will trigger a
+deprecation warning.
+
+Affected Installations
+======================
+
+TYPO3 installations with extensions using the Extbase framework and
+Extbase-internal hooks.
+
+
+Migration
+=========
+
+The following new PSR-14-based Events should be used instead:
+
+- :php:`TYPO3\CMS\Extbase\Event\Mvc\AfterRequestDispatchedEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Mvc\BeforeActionCallEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\AfterObjectThawedEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\ModifyQueryBeforeFetchingObjectDataEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\ModifyResultAfterFetchingObjectDataEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\EntityAddedToPersistenceEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\EntityUpdatedInPersistenceEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\EntityRemovedFromPersistenceEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\EntityPersistedEvent`
+
+.. index:: PHP-API, PartiallyScanned, ext:extbase
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-89870-NewPSR-14EventsForExtbase-relatedSignals.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-89870-NewPSR-14EventsForExtbase-relatedSignals.rst
new file mode 100644 (file)
index 0000000..ecac644
--- /dev/null
@@ -0,0 +1,44 @@
+.. include:: ../../Includes.txt
+
+===============================================================
+Feature: #89870 - New PSR-14 Events for Extbase-related signals
+===============================================================
+
+See :issue:`89870`
+
+Description
+===========
+
+The following new PSR-14-based Events are introduced which allow
+to modify various concerns in the MVC and persistence stacks of Extbase internals.
+
+- :php:`TYPO3\CMS\Extbase\Event\Mvc\AfterRequestDispatchedEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Mvc\BeforeActionCallEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\AfterObjectThawedEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\ModifyQueryBeforeFetchingObjectDataEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\ModifyResultAfterFetchingObjectDataEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\EntityAddedToPersistenceEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\EntityFinalizedAfterPersistenceEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\EntityUpdatedInPersistenceEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\EntityRemovedFromPersistenceEvent`
+- :php:`TYPO3\CMS\Extbase\Event\Persistence\EntityPersistedEvent`
+
+
+Impact
+======
+
+Existing signals are replaced and should not be used anymore, as PSR-14 event classes exactly specify what can be modified or listened to.
+
+The following signals should not be used anymore then:
+- :php:`TYPO3\CMS\Extbase\Mvc\Dispatcher::afterRequestDispatch`
+- :php:`TYPO3\CMS\Extbase\Mvc\Controller\ActionController::beforeCallActionMethod`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::afterMappingSingleRow`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::beforeGettingObjectData`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::afterGettingObjectData`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::afterInsertObject`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::endInsertObject`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::afterUpdateObject`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::afterPersistObject`
+- :php:`TYPO3\CMS\Extbase\Persistence\Generic\Backend::afterRemoveObject`
+
+.. index:: PHP-API, ext:extbase
diff --git a/typo3/sysext/extbase/Classes/Compatibility/SlotReplacement.php b/typo3/sysext/extbase/Classes/Compatibility/SlotReplacement.php
new file mode 100644 (file)
index 0000000..7f85165
--- /dev/null
@@ -0,0 +1,162 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Extbase\Compatibility;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\Event\Mvc\AfterRequestDispatchedEvent;
+use TYPO3\CMS\Extbase\Event\Mvc\BeforeActionCallEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\AfterObjectThawedEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityAddedToPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityFinalizedAfterPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityPersistedEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityRemovedFromPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityUpdatedInPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\ModifyQueryBeforeFetchingObjectDataEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\ModifyResultAfterFetchingObjectDataEvent;
+use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
+use TYPO3\CMS\Extbase\Mvc\Dispatcher;
+use TYPO3\CMS\Extbase\Persistence\Generic\Backend;
+use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
+use TYPO3\CMS\Extbase\SignalSlot\Dispatcher as SignalSlotDispatcher;
+
+/**
+ * This class provides a replacement for all existing signals in EXT:extbase of TYPO3 Core, which now act as a
+ * simple wrapper for PSR-14 events with a simple ("first prioritized") listener implementation.
+ *
+ * @internal Please note that this class will likely be removed in TYPO3 v11, and Extension Authors should
+ * switch to PSR-14 event listeners.
+ */
+class SlotReplacement
+{
+    /**
+     * @var SignalSlotDispatcher
+     */
+    protected $signalSlotDispatcher;
+
+    public function __construct(SignalSlotDispatcher $signalSlotDispatcher)
+    {
+        $this->signalSlotDispatcher = $signalSlotDispatcher;
+    }
+
+    public function afterRequestDispatched(AfterRequestDispatchedEvent $event): void
+    {
+        $this->signalSlotDispatcher->dispatch(
+            Dispatcher::class,
+            'afterRequestDispatch',
+            [$event->getRequest(), $event->getResponse()]
+        );
+    }
+
+    public function beforeCallActionMethod(BeforeActionCallEvent $event): void
+    {
+        $this->signalSlotDispatcher->dispatch(
+            ActionController::class,
+            'beforeCallActionMethod',
+            [
+                $event->getControllerClassName(),
+                $event->getActionMethodName(),
+                $event->getPreparedArguments()
+            ]
+        );
+    }
+
+    public function afterDataMappedForObject(AfterObjectThawedEvent $event): void
+    {
+        $this->signalSlotDispatcher->dispatch(
+            DataMapper::class,
+            'afterMappingSingleRow',
+            [
+                $event->getObject()
+            ]
+        );
+    }
+
+    public function emitBeforeGettingObjectDataSignal(ModifyQueryBeforeFetchingObjectDataEvent $event): void
+    {
+        $signalArguments = $this->signalSlotDispatcher->dispatch(
+            Backend::class,
+            'beforeGettingObjectData',
+            [
+                $event->getQuery()
+            ]
+        );
+        $event->setQuery($signalArguments[0]);
+    }
+
+    public function emitAfterGettingObjectDataSignal(ModifyResultAfterFetchingObjectDataEvent $event): void
+    {
+        $signalArguments = $this->signalSlotDispatcher->dispatch(
+            Backend::class,
+            'afterGettingObjectData',
+            [$event->getQuery(), $event->getResult()]
+        );
+        $event->setResult($signalArguments[1]);
+    }
+
+    public function emitEndInsertObjectSignal(EntityFinalizedAfterPersistenceEvent $event): void
+    {
+        $this->signalSlotDispatcher->dispatch(
+            Backend::class,
+            'endInsertObject',
+            [
+                $event->getObject()
+            ]
+        );
+    }
+
+    public function emitAfterInsertObjectSignal(EntityAddedToPersistenceEvent $event): void
+    {
+        $this->signalSlotDispatcher->dispatch(
+            Backend::class,
+            'afterInsertObject',
+            [
+                $event->getObject()
+            ]
+        );
+    }
+
+    public function emitAfterUpdateObjectSignal(EntityUpdatedInPersistenceEvent $event): void
+    {
+        $this->signalSlotDispatcher->dispatch(
+            Backend::class,
+            'afterUpdateObject',
+            [
+                $event->getObject()
+            ]
+        );
+    }
+
+    public function emitAfterPersistObjectSignal(EntityPersistedEvent $event): void
+    {
+        $this->signalSlotDispatcher->dispatch(
+            Backend::class,
+            'afterPersistObject',
+            [
+                $event->getObject()
+            ]
+        );
+    }
+
+    public function emitAfterRemoveObjectSignal(EntityRemovedFromPersistenceEvent $event): void
+    {
+        $this->signalSlotDispatcher->dispatch(
+            Backend::class,
+            'afterRemoveObject',
+            [
+                $event->getObject()
+            ]
+        );
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Event/Mvc/AfterRequestDispatchedEvent.php b/typo3/sysext/extbase/Classes/Event/Mvc/AfterRequestDispatchedEvent.php
new file mode 100644 (file)
index 0000000..ed54e9b
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Event\Mvc;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\Mvc\RequestInterface;
+use TYPO3\CMS\Extbase\Mvc\ResponseInterface;
+
+/**
+ * Event which is fired after the dispatcher has successfully dispatched a request to a controller/action.
+ */
+final class AfterRequestDispatchedEvent
+{
+    /**
+     * @var RequestInterface
+     */
+    private $request;
+
+    /**
+     * @var ResponseInterface
+     */
+    private $response;
+
+    public function __construct(RequestInterface $request, ResponseInterface $response)
+    {
+        $this->request = $request;
+        $this->response = $response;
+    }
+
+    /**
+     * @return RequestInterface
+     */
+    public function getRequest(): RequestInterface
+    {
+        return $this->request;
+    }
+
+    /**
+     * @return ResponseInterface
+     */
+    public function getResponse(): ResponseInterface
+    {
+        return $this->response;
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Event/Mvc/BeforeActionCallEvent.php b/typo3/sysext/extbase/Classes/Event/Mvc/BeforeActionCallEvent.php
new file mode 100644 (file)
index 0000000..28da527
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Event\Mvc;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Event that is triggered before any Extbase Action is called within the ActionController or one
+ * of its subclasses.
+ */
+final class BeforeActionCallEvent
+{
+    /**
+     * @var string
+     */
+    private $controllerClassName;
+
+    /**
+     * @var string
+     */
+    private $actionMethodName;
+
+    /**
+     * @var array
+     */
+    private $preparedArguments;
+
+    public function __construct(string $controllerClassName, string $actionMethodName, array $preparedArguments)
+    {
+        $this->controllerClassName = $controllerClassName;
+        $this->actionMethodName = $actionMethodName;
+        $this->preparedArguments = $preparedArguments;
+    }
+
+    public function getControllerClassName(): string
+    {
+        return $this->controllerClassName;
+    }
+
+    public function getActionMethodName(): string
+    {
+        return $this->actionMethodName;
+    }
+
+    public function getPreparedArguments(): array
+    {
+        return $this->preparedArguments;
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Event/Persistence/AfterObjectThawedEvent.php b/typo3/sysext/extbase/Classes/Event/Persistence/AfterObjectThawedEvent.php
new file mode 100644 (file)
index 0000000..ca18854
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Event\Persistence;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
+
+/**
+ * Allows to modify values when creating domain objects.
+ */
+final class AfterObjectThawedEvent
+{
+    /**
+     * @var DomainObjectInterface
+     */
+    private $mappedObject;
+
+    /**
+     * @var array
+     */
+    private $record;
+
+    public function __construct(DomainObjectInterface $mappedObject, array $record)
+    {
+        $this->mappedObject = $mappedObject;
+        $this->record = $record;
+    }
+
+    public function getObject(): DomainObjectInterface
+    {
+        return $this->mappedObject;
+    }
+
+    public function getRecord(): array
+    {
+        return $this->record;
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Event/Persistence/EntityAddedToPersistenceEvent.php b/typo3/sysext/extbase/Classes/Event/Persistence/EntityAddedToPersistenceEvent.php
new file mode 100644 (file)
index 0000000..cb4152e
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Event\Persistence;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
+
+/**
+ * Event which is fired after an object/entity was sent to persistence layer to be added,
+ * but before updating the reference index and current session.
+ */
+final class EntityAddedToPersistenceEvent
+{
+    /**
+     * @var DomainObjectInterface
+     */
+    private $persistedObject;
+
+    public function __construct(DomainObjectInterface $persistedObject)
+    {
+        $this->persistedObject = $persistedObject;
+    }
+
+    public function getObject(): DomainObjectInterface
+    {
+        return $this->persistedObject;
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Event/Persistence/EntityFinalizedAfterPersistenceEvent.php b/typo3/sysext/extbase/Classes/Event/Persistence/EntityFinalizedAfterPersistenceEvent.php
new file mode 100644 (file)
index 0000000..4f41d49
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Event\Persistence;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
+
+/**
+ * Event which is fired after an object/entity was sent to persistence layer to be added
+ * including update of reference index and current session.
+ */
+final class EntityFinalizedAfterPersistenceEvent
+{
+    /**
+     * @var DomainObjectInterface
+     */
+    private $persistedObject;
+
+    public function __construct(DomainObjectInterface $persistedObject)
+    {
+        $this->persistedObject = $persistedObject;
+    }
+
+    public function getObject(): DomainObjectInterface
+    {
+        return $this->persistedObject;
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Event/Persistence/EntityPersistedEvent.php b/typo3/sysext/extbase/Classes/Event/Persistence/EntityPersistedEvent.php
new file mode 100644 (file)
index 0000000..ee6f608
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Event\Persistence;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
+
+/**
+ * Event which is fired after an object was pushed to the storage backend
+ */
+final class EntityPersistedEvent
+{
+    /**
+     * @var DomainObjectInterface
+     */
+    private $persistedObject;
+
+    public function __construct(DomainObjectInterface $persistedObject)
+    {
+        $this->persistedObject = $persistedObject;
+    }
+
+    public function getObject(): DomainObjectInterface
+    {
+        return $this->persistedObject;
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Event/Persistence/EntityRemovedFromPersistenceEvent.php b/typo3/sysext/extbase/Classes/Event/Persistence/EntityRemovedFromPersistenceEvent.php
new file mode 100644 (file)
index 0000000..4c57b57
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Event\Persistence;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
+
+/**
+ * Event which is fired after an object/entity was sent to persistence layer to be removed.
+ */
+final class EntityRemovedFromPersistenceEvent
+{
+    /**
+     * @var DomainObjectInterface
+     */
+    private $persistedObject;
+
+    public function __construct(DomainObjectInterface $persistedObject)
+    {
+        $this->persistedObject = $persistedObject;
+    }
+
+    public function getObject(): DomainObjectInterface
+    {
+        return $this->persistedObject;
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Event/Persistence/EntityUpdatedInPersistenceEvent.php b/typo3/sysext/extbase/Classes/Event/Persistence/EntityUpdatedInPersistenceEvent.php
new file mode 100644 (file)
index 0000000..eb79bd3
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Event\Persistence;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
+
+/**
+ * Event which is fired after an object/entity was sent to persistence layer to be updated.
+ */
+final class EntityUpdatedInPersistenceEvent
+{
+    /**
+     * @var DomainObjectInterface
+     */
+    private $persistedObject;
+
+    public function __construct(DomainObjectInterface $persistedObject)
+    {
+        $this->persistedObject = $persistedObject;
+    }
+
+    public function getObject(): DomainObjectInterface
+    {
+        return $this->persistedObject;
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Event/Persistence/ModifyQueryBeforeFetchingObjectDataEvent.php b/typo3/sysext/extbase/Classes/Event/Persistence/ModifyQueryBeforeFetchingObjectDataEvent.php
new file mode 100644 (file)
index 0000000..c76fe41
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Event\Persistence;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\Persistence\QueryInterface;
+
+/**
+ * Event which is fired before the storage backend is asked for results from a given query.
+ */
+final class ModifyQueryBeforeFetchingObjectDataEvent
+{
+    /**
+     * @var QueryInterface
+     */
+    private $query;
+
+    public function __construct(QueryInterface $query)
+    {
+        $this->query = $query;
+    }
+
+    public function getQuery(): QueryInterface
+    {
+        return $this->query;
+    }
+
+    public function setQuery(QueryInterface $query): void
+    {
+        $this->query = $query;
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Event/Persistence/ModifyResultAfterFetchingObjectDataEvent.php b/typo3/sysext/extbase/Classes/Event/Persistence/ModifyResultAfterFetchingObjectDataEvent.php
new file mode 100644 (file)
index 0000000..7d587bf
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Event\Persistence;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\Persistence\QueryInterface;
+
+/**
+ * Event which is fired after the storage backend has pulled results from a given query.
+ */
+final class ModifyResultAfterFetchingObjectDataEvent
+{
+    /**
+     * @var QueryInterface
+     */
+    private $query;
+
+    /**
+     * @var array
+     */
+    private $result;
+
+    public function __construct(QueryInterface $query, array $result)
+    {
+        $this->query = $query;
+        $this->result = $result;
+    }
+
+    public function getQuery(): QueryInterface
+    {
+        return $this->query;
+    }
+
+    public function getResult(): array
+    {
+        return $this->result;
+    }
+
+    public function setResult(array $result): void
+    {
+        $this->result = $result;
+    }
+}
index 757040d..28b0643 100644 (file)
@@ -14,11 +14,13 @@ namespace TYPO3\CMS\Extbase\Mvc\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\EventDispatcher\EventDispatcherInterface;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
+use TYPO3\CMS\Extbase\Event\Mvc\BeforeActionCallEvent;
 use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
 use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
 use TYPO3\CMS\Extbase\Mvc\Web\ReferringRequest;
@@ -82,6 +84,11 @@ class ActionController implements ControllerInterface
     protected $mvcPropertyMappingConfigurationService;
 
     /**
+     * @var EventDispatcherInterface
+     */
+    protected $eventDispatcher;
+
+    /**
      * The current request.
      *
      * @var \TYPO3\CMS\Extbase\Mvc\Request
@@ -127,6 +134,11 @@ class ActionController implements ControllerInterface
         $this->mvcPropertyMappingConfigurationService = $mvcPropertyMappingConfigurationService;
     }
 
+    public function injectEventDispatcher(EventDispatcherInterface $eventDispatcher): void
+    {
+        $this->eventDispatcher = $eventDispatcher;
+    }
+
     /**
      * Handles a request. The result output is returned by altering the given response.
      *
@@ -321,7 +333,7 @@ class ActionController implements ControllerInterface
         }
         $validationResult = $this->arguments->validate();
         if (!$validationResult->hasErrors()) {
-            $this->emitBeforeCallActionMethodSignal($preparedArguments);
+            $this->eventDispatcher->dispatch(new BeforeActionCallEvent(static::class, $this->actionMethodName, $preparedArguments));
             $actionResult = $this->{$this->actionMethodName}(...$preparedArguments);
         } else {
             $actionResult = $this->{$this->errorMethodName}();
@@ -343,6 +355,10 @@ class ActionController implements ControllerInterface
      */
     protected function emitBeforeCallActionMethodSignal(array $preparedArguments)
     {
+        trigger_error(
+            __METHOD__ . ' is deprecated and will be removed in version 11.0 - use PSR-14 events instead.',
+            E_USER_DEPRECATED
+        );
         $this->signalSlotDispatcher->dispatch(__CLASS__, 'beforeCallActionMethod', [static::class, $this->actionMethodName, $preparedArguments]);
     }
 
index 684c997..f1bf0ce 100644 (file)
@@ -1,10 +1,6 @@
 <?php
 namespace TYPO3\CMS\Extbase\Mvc;
 
-use Psr\Container\ContainerInterface;
-use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
-use TYPO3\CMS\Extbase\SignalSlot\Dispatcher as SignalSlotDispatcher;
-
 /*
  * This file is part of the TYPO3 CMS project.
  *
@@ -18,12 +14,18 @@ use TYPO3\CMS\Extbase\SignalSlot\Dispatcher as SignalSlotDispatcher;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Container\ContainerInterface;
+use Psr\EventDispatcher\EventDispatcherInterface;
+use TYPO3\CMS\Core\SingletonInterface;
+use TYPO3\CMS\Extbase\Event\Mvc\AfterRequestDispatchedEvent;
+use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
+
 /**
  * Dispatches requests to the controller which was specified by the request and
  * returns the response the controller generated.
  * @internal only to be used within Extbase, not part of TYPO3 Core API.
  */
-class Dispatcher implements \TYPO3\CMS\Core\SingletonInterface
+class Dispatcher implements SingletonInterface
 {
     /**
      * @var ObjectManagerInterface A reference to the object manager
@@ -36,9 +38,9 @@ class Dispatcher implements \TYPO3\CMS\Core\SingletonInterface
     private $container;
 
     /**
-     * @var SignalSlotDispatcher
+     * @var EventDispatcherInterface
      */
-    protected $signalSlotDispatcher;
+    protected $eventDispatcher;
 
     /**
      * @var array
@@ -50,61 +52,51 @@ class Dispatcher implements \TYPO3\CMS\Core\SingletonInterface
      *
      * @param ObjectManagerInterface $objectManager A reference to the object manager
      * @param ContainerInterface $container
-     * @param SignalSlotDispatcher $signalSlotDispatcher
+     * @param EventDispatcherInterface $eventDispatcher
      */
     public function __construct(
         ObjectManagerInterface $objectManager,
         ContainerInterface $container,
-        SignalSlotDispatcher $signalSlotDispatcher
+        EventDispatcherInterface $eventDispatcher
     ) {
         $this->objectManager = $objectManager;
         $this->container = $container;
-        $this->signalSlotDispatcher = $signalSlotDispatcher;
+        $this->eventDispatcher = $eventDispatcher;
     }
 
     /**
      * Dispatches a request to a controller and initializes the security framework.
      *
-     * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request to dispatch
-     * @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response The response, to be modified by the controller
+     * @param RequestInterface $request The request to dispatch
+     * @param ResponseInterface $response The response, to be modified by the controller
      * @throws Exception\InfiniteLoopException
      */
-    public function dispatch(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
+    public function dispatch(RequestInterface $request, ResponseInterface $response)
     {
         $dispatchLoopCount = 0;
         while (!$request->isDispatched()) {
             if ($dispatchLoopCount++ > 99) {
-                throw new \TYPO3\CMS\Extbase\Mvc\Exception\InfiniteLoopException('Could not ultimately dispatch the request after ' . $dispatchLoopCount . ' iterations. Most probably, a @' . \TYPO3\CMS\Extbase\Annotation\IgnoreValidation::class . ' annotation is missing on re-displaying a form with validation errors.', 1217839467);
+                throw new Exception\InfiniteLoopException('Could not ultimately dispatch the request after ' . $dispatchLoopCount . ' iterations. Most probably, a @' . \TYPO3\CMS\Extbase\Annotation\IgnoreValidation::class . ' annotation is missing on re-displaying a form with validation errors.', 1217839467);
             }
             $controller = $this->resolveController($request);
             try {
                 $controller->processRequest($request, $response);
-            } catch (\TYPO3\CMS\Extbase\Mvc\Exception\StopActionException $ignoredException) {
+            } catch (Exception\StopActionException $ignoredException) {
             }
         }
-        $this->emitAfterRequestDispatchSignal($request, $response);
-    }
 
-    /**
-     * Emits a signal after a request was dispatched
-     *
-     * @param RequestInterface $request
-     * @param ResponseInterface $response
-     */
-    protected function emitAfterRequestDispatchSignal(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
-    {
-        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterRequestDispatch', [$request, $response]);
+        $this->eventDispatcher->dispatch(new AfterRequestDispatchedEvent($request, $response));
     }
 
     /**
      * Finds and instantiates a controller that matches the current request.
      * If no controller can be found, an instance of NotFoundControllerInterface is returned.
      *
-     * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request to dispatch
+     * @param RequestInterface $request The request to dispatch
+     * @return Controller\ControllerInterface
      * @throws Exception\InvalidControllerException
-     * @return \TYPO3\CMS\Extbase\Mvc\Controller\ControllerInterface
      */
-    protected function resolveController(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request)
+    protected function resolveController(RequestInterface $request)
     {
         $controllerObjectName = $request->getControllerObjectName();
         if ($this->container->has($controllerObjectName)) {
@@ -112,8 +104,8 @@ class Dispatcher implements \TYPO3\CMS\Core\SingletonInterface
         } else {
             $controller = $this->objectManager->get($controllerObjectName);
         }
-        if (!$controller instanceof \TYPO3\CMS\Extbase\Mvc\Controller\ControllerInterface) {
-            throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidControllerException(
+        if (!$controller instanceof Controller\ControllerInterface) {
+            throw new Exception\InvalidControllerException(
                 'Invalid controller "' . $request->getControllerObjectName() . '". The controller must implement the TYPO3\\CMS\\Extbase\\Mvc\\Controller\\ControllerInterface.',
                 1476109646
             );
index e95497d..7c5baa9 100644 (file)
@@ -14,8 +14,16 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\EventDispatcher\EventDispatcherInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityAddedToPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityFinalizedAfterPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityPersistedEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityRemovedFromPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityUpdatedInPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\ModifyQueryBeforeFetchingObjectDataEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\ModifyResultAfterFetchingObjectDataEvent;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
 use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
 use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
@@ -96,6 +104,11 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
     protected $signalSlotDispatcher;
 
     /**
+     * @var EventDispatcherInterface
+     */
+    protected $eventDispatcher;
+
+    /**
      * Constructs the backend
      *
      * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
@@ -104,7 +117,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
      * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory
      * @param \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface $storageBackend
      * @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory $dataMapFactory
-     * @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
+     * @param EventDispatcherInterface $eventDispatcher
      */
     public function __construct(
         \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager,
@@ -113,7 +126,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory,
         \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface $storageBackend,
         \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory $dataMapFactory,
-        \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
+        EventDispatcherInterface $eventDispatcher
     ) {
         $this->configurationManager = $configurationManager;
         $this->session = $session;
@@ -121,7 +134,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         $this->qomFactory = $qomFactory;
         $this->storageBackend = $storageBackend;
         $this->dataMapFactory = $dataMapFactory;
-        $this->signalSlotDispatcher = $signalSlotDispatcher;
+        $this->eventDispatcher = $eventDispatcher;
 
         $this->referenceIndex = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ReferenceIndex::class);
         $this->referenceIndex->enableRuntimeCache();
@@ -187,35 +200,13 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
      */
     public function getObjectDataByQuery(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query)
     {
-        $query = $this->emitBeforeGettingObjectDataSignal($query);
+        $event = new ModifyQueryBeforeFetchingObjectDataEvent($query);
+        $this->eventDispatcher->dispatch($event);
+        $query = $event->getQuery();
         $result = $this->storageBackend->getObjectDataByQuery($query);
-        $result = $this->emitAfterGettingObjectDataSignal($query, $result);
-        return $result;
-    }
-
-    /**
-     * Emits a signal before object data is fetched
-     *
-     * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
-     * @return \TYPO3\CMS\Extbase\Persistence\QueryInterface Modified query
-     */
-    protected function emitBeforeGettingObjectDataSignal(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query)
-    {
-        $signalArguments = $this->signalSlotDispatcher->dispatch(__CLASS__, 'beforeGettingObjectData', [$query]);
-        return $signalArguments[0];
-    }
-
-    /**
-     * Emits a signal after object data is fetched
-     *
-     * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
-     * @param array $result
-     * @return array Modified result
-     */
-    protected function emitAfterGettingObjectDataSignal(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query, array $result)
-    {
-        $signalArguments = $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterGettingObjectData', [$query, $result]);
-        return $signalArguments[1];
+        $event = new ModifyResultAfterFetchingObjectDataEvent($query, $result);
+        $this->eventDispatcher->dispatch($event);
+        return $event->getResult();
     }
 
     /**
@@ -378,7 +369,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         foreach ($queue as $queuedObject) {
             $this->persistObject($queuedObject);
         }
-        $this->emitAfterPersistObjectSignal($object);
+        $this->eventDispatcher->dispatch(new EntityPersistedEvent($object));
     }
 
     /**
@@ -671,7 +662,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         $object->_setProperty('uid', (int)$uid);
         $object->setPid((int)$row['pid']);
         if ((int)$uid >= 1) {
-            $this->emitAfterInsertObjectSignal($object);
+            $this->eventDispatcher->dispatch(new EntityAddedToPersistenceEvent($object));
         }
         $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
         if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
@@ -679,32 +670,11 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         }
         $this->session->registerObject($object, $uid);
         if ((int)$uid >= 1) {
-            $this->emitEndInsertObjectSignal($object);
+            $this->eventDispatcher->dispatch(new EntityFinalizedAfterPersistenceEvent($object));
         }
     }
 
     /**
-     * Emits a signal after an object was added to the storage
-     *
-     * @param DomainObjectInterface $object
-     */
-    protected function emitAfterInsertObjectSignal(DomainObjectInterface $object)
-    {
-        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterInsertObject', [$object]);
-    }
-
-    /**
-     * Emits a signal after an object was registered in persistence session
-     * This signal replaces the afterInsertObject signal which is now deprecated
-     *
-     * @param DomainObjectInterface $object
-     */
-    protected function emitEndInsertObjectSignal(DomainObjectInterface $object)
-    {
-        $this->signalSlotDispatcher->dispatch(__CLASS__, 'endInsertObject', [$object]);
-    }
-
-    /**
      * Tests, if the given Value Object already exists in the storage backend and if so, it returns the uid.
      *
      * @param \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object The object to be tested
@@ -906,7 +876,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
             }
         }
         $this->storageBackend->updateRow($dataMap->getTableName(), $row);
-        $this->emitAfterUpdateObjectSignal($object);
+        $this->eventDispatcher->dispatch(new EntityUpdatedInPersistenceEvent($object));
 
         $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
         if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
@@ -916,26 +886,6 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
     }
 
     /**
-     * Emits a signal after an object was updated in storage
-     *
-     * @param DomainObjectInterface $object
-     */
-    protected function emitAfterUpdateObjectSignal(DomainObjectInterface $object)
-    {
-        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterUpdateObject', [$object]);
-    }
-
-    /**
-     * Emits a signal after an object was persisted
-     *
-     * @param DomainObjectInterface $object
-     */
-    protected function emitAfterPersistObjectSignal(DomainObjectInterface $object)
-    {
-        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterPersistObject', [$object]);
-    }
-
-    /**
      * Adds common database fields to a row
      *
      * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
@@ -1006,7 +956,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         } else {
             $this->storageBackend->removeRow($tableName, ['uid' => $object->getUid()]);
         }
-        $this->emitAfterRemoveObjectSignal($object);
+        $this->eventDispatcher->dispatch(new EntityRemovedFromPersistenceEvent($object));
 
         $this->removeRelatedObjects($object);
         $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
@@ -1016,16 +966,6 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
     }
 
     /**
-     * Emits a signal after an object was removed from storage
-     *
-     * @param DomainObjectInterface $object
-     */
-    protected function emitAfterRemoveObjectSignal(DomainObjectInterface $object)
-    {
-        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterRemoveObject', [$object]);
-    }
-
-    /**
      * Remove related objects
      *
      * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to scanned for related objects
index 76ca311..cd00833 100644 (file)
@@ -14,8 +14,10 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic\Mapper;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\EventDispatcher\EventDispatcherInterface;
 use TYPO3\CMS\Core\Database\Query\QueryHelper;
 use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
+use TYPO3\CMS\Extbase\Event\Persistence\AfterObjectThawedEvent;
 use TYPO3\CMS\Extbase\Object\Exception\CannotReconstituteObjectException;
 use TYPO3\CMS\Extbase\Persistence;
 use TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException;
@@ -60,9 +62,9 @@ class DataMapper
     protected $objectManager;
 
     /**
-     * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
+     * @var EventDispatcherInterface
      */
-    protected $signalSlotDispatcher;
+    protected $eventDispatcher;
 
     /**
      * @var ?QueryInterface
@@ -77,7 +79,7 @@ class DataMapper
      * @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory $dataMapFactory
      * @param \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface $queryFactory
      * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
-     * @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
+     * @param EventDispatcherInterface $eventDispatcher
      * @param QueryInterface|null $query
      */
     public function __construct(
@@ -87,7 +89,7 @@ class DataMapper
         \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory $dataMapFactory,
         \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface $queryFactory,
         \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager,
-        \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher,
+        EventDispatcherInterface $eventDispatcher,
         ?QueryInterface $query = null
     ) {
         $this->query = $query;
@@ -97,7 +99,7 @@ class DataMapper
         $this->dataMapFactory = $dataMapFactory;
         $this->queryFactory = $queryFactory;
         $this->objectManager = $objectManager;
-        $this->signalSlotDispatcher = $signalSlotDispatcher;
+        $this->eventDispatcher = $eventDispatcher;
 
         if ($query !== null) {
             trigger_error(
@@ -170,7 +172,8 @@ class DataMapper
             $object = $this->createEmptyObject($className);
             $this->persistenceSession->registerObject($object, $row['uid']);
             $this->thawProperties($object, $row);
-            $this->emitAfterMappingSingleRow($object);
+            $event = new AfterObjectThawedEvent($object, $row);
+            $this->eventDispatcher->dispatch($event);
             $object->_memorizeCleanState();
             $this->persistenceSession->registerReconstitutedEntity($object);
         }
@@ -178,21 +181,11 @@ class DataMapper
     }
 
     /**
-     * Emits a signal after mapping a single row.
-     *
-     * @param DomainObjectInterface $object The mapped object
-     */
-    protected function emitAfterMappingSingleRow(DomainObjectInterface $object)
-    {
-        $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterMappingSingleRow', [$object]);
-    }
-
-    /**
      * Creates a skeleton of the specified object
      *
      * @param string $className Name of the class to create a skeleton for
      * @throws CannotReconstituteObjectException
-     * @return object The object skeleton
+     * @return DomainObjectInterface The object skeleton
      */
     protected function createEmptyObject($className)
     {
index 5651ca4..7010bbd 100644 (file)
@@ -85,7 +85,20 @@ use TYPO3\CMS\Core\Resource\Service\FileProcessingService;
 use TYPO3\CMS\Core\Tree\Event\ModifyTreeDataEvent;
 use TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeDataProvider;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
+use TYPO3\CMS\Extbase\Event\Mvc\AfterRequestDispatchedEvent;
+use TYPO3\CMS\Extbase\Event\Mvc\BeforeActionCallEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\AfterObjectThawedEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityAddedToPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityFinalizedAfterPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityPersistedEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityRemovedFromPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\EntityUpdatedInPersistenceEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\ModifyQueryBeforeFetchingObjectDataEvent;
+use TYPO3\CMS\Extbase\Event\Persistence\ModifyResultAfterFetchingObjectDataEvent;
+use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
 use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
+use TYPO3\CMS\Extbase\Persistence\Generic\Backend;
+use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
 
 /**
  * A dispatcher which dispatches signals by calling its registered slot methods
@@ -196,6 +209,24 @@ class Dispatcher implements \TYPO3\CMS\Core\SingletonInterface
         UsernamePasswordLoginProvider::class => [
             'getPageRenderer' => ModifyPageLayoutOnLoginProviderSelectionEvent::class
         ],
+        \TYPO3\CMS\Extbase\Mvc\Dispatcher::class => [
+          'afterRequestDispatch' => AfterRequestDispatchedEvent::class
+        ],
+        ActionController::class => [
+            'beforeCallActionMethod' => BeforeActionCallEvent::class
+        ],
+        DataMapper::class => [
+            'afterMappingSingleRow' => AfterObjectThawedEvent::class
+        ],
+        Backend::class => [
+            'beforeGettingObjectData' => ModifyQueryBeforeFetchingObjectDataEvent::class,
+            'afterGettingObjectData' => ModifyResultAfterFetchingObjectDataEvent::class,
+            'endInsertObject' => EntityFinalizedAfterPersistenceEvent::class,
+            'afterInsertObject' => EntityAddedToPersistenceEvent::class,
+            'afterUpdateObject' => EntityUpdatedInPersistenceEvent::class,
+            'afterPersistObject' => EntityPersistedEvent::class,
+            'afterRemoveObject' => EntityRemovedFromPersistenceEvent::class
+        ],
         // Strings are used here on purpose for all non-required system extensions. Do not change to
         // Fqn::class *unless* you also declare each and every extension whose classes are listed
         // here as explicit and mandatory dependencies of EXT:extbase.
index 691376f..e393761 100644 (file)
@@ -39,3 +39,47 @@ services:
     class: TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
     factory: ['@TYPO3\CMS\Core\Cache\CacheManager', 'getCache']
     arguments: ['extbase']
+
+  # Listener for old Signal Slots
+  TYPO3\CMS\Extbase\Compatibility\SlotReplacement:
+    tags:
+      - name: event.listener
+        identifier: 'legacy-slot'
+        method: 'afterRequestDispatched'
+        event: TYPO3\CMS\Extbase\Event\Mvc\AfterRequestDispatchedEvent
+      - name: event.listener
+        identifier: 'legacy-slot'
+        method: 'beforeCallActionMethod'
+        event: TYPO3\CMS\Extbase\Event\Mvc\BeforeActionCallEvent
+      - name: event.listener
+        identifier: 'legacy-slot'
+        method: 'afterDataMappedForObject'
+        event: TYPO3\CMS\Extbase\Event\Persistence\AfterObjectThawedEvent
+      - name: event.listener
+        identifier: 'legacy-slot'
+        method: 'emitBeforeGettingObjectDataSignal'
+        event: TYPO3\CMS\Extbase\Event\Persistence\ModifyQueryBeforeFetchingObjectDataEvent
+      - name: event.listener
+        identifier: 'legacy-slot'
+        method: 'emitAfterGettingObjectDataSignal'
+        event: TYPO3\CMS\Extbase\Event\Persistence\ModifyResultAfterFetchingObjectDataEvent
+      - name: event.listener
+        identifier: 'legacy-slot'
+        method: 'emitEndInsertObjectSignal'
+        event: TYPO3\CMS\Extbase\Event\Persistence\EntityFinalizedAfterPersistenceEvent
+      - name: event.listener
+        identifier: 'legacy-slot'
+        method: 'emitAfterInsertObjectSignal'
+        event: TYPO3\CMS\Extbase\Event\Persistence\EntityAddedToPersistenceEvent
+      - name: event.listener
+        identifier: 'legacy-slot'
+        method: 'emitAfterUpdateObjectSignal'
+        event: TYPO3\CMS\Extbase\Event\Persistence\EntityUpdatedInPersistenceEvent
+      - name: event.listener
+        identifier: 'legacy-slot'
+        method: 'emitAfterRemoveObjectSignal'
+        event: TYPO3\CMS\Extbase\Event\Persistence\EntityRemovedFromPersistenceEvent
+      - name: event.listener
+        identifier: 'legacy-slot'
+        method: 'emitAfterPersistObjectSignal'
+        event: TYPO3\CMS\Extbase\Event\Persistence\EntityPersistedEvent
index 7c0ff55..4d7b999 100644 (file)
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Mapper;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\EventDispatcher\EventDispatcherInterface;
 use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
 use TYPO3\CMS\Extbase\Object\Container\Container;
 use TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException;
@@ -50,7 +51,7 @@ class DataMapperTest extends UnitTestCase
                 $this->createMock(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::class),
                 $this->createMock(\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface::class),
                 $this->createMock(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface::class),
-                $this->createMock(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class),
+                $this->createMock(EventDispatcherInterface::class),
             ])
             ->setMethods(['mapSingleRow', 'getTargetType'])
             ->getMock();
@@ -86,7 +87,7 @@ class DataMapperTest extends UnitTestCase
                 $this->createMock(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::class),
                 $this->createMock(\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface::class),
                 $this->createMock(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface::class),
-                $this->createMock(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class),
+                $this->createMock(EventDispatcherInterface::class),
             ]
         );
 
@@ -142,7 +143,7 @@ class DataMapperTest extends UnitTestCase
                 $dataMapFactory,
                 $this->createMock(\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface::class),
                 $this->createMock(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface::class),
-                $this->createMock(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class),
+                $this->createMock(EventDispatcherInterface::class),
             ]
         );
         $dataMapper->_call('thawProperties', $object, $row);
@@ -278,7 +279,7 @@ class DataMapperTest extends UnitTestCase
                 $this->createMock(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::class),
                 $this->createMock(\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface::class),
                 $this->createMock(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface::class),
-                $this->createMock(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class),
+                $this->createMock(EventDispatcherInterface::class),
             ]
         );
         $dataMapper->expects(self::any())->method('getDataMap')->willReturn($dataMap);
index 2152ad4..060b33f 100644 (file)
@@ -4399,4 +4399,11 @@ return [
             'Important-90020-LegacyBasicFileUtilityAndExtendedFileUtilityClassesMarkedAsInternal.rst',
         ],
     ],
+    'TYPO3\CMS\Extbase\Mvc\Controller\ActionController->emitBeforeCallActionMethodSignal' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 1,
+        'restFiles' => [
+            'Deprecation-89870-NewPSR-14EventsForExtbase-relatedSignals.rst',
+        ],
+    ]
 ];