Feature: #102935 - PSR-14 event for package initialization functionality

See forge#102935

Description

A new PSR-14 event \TYPO3\CMS\Core\Package\Event\PackageInitializationEvent has been introduced. It allows listeners to execute custom functionality after a package has been activated. The event is therefore being dispatched at several places, where packages get activated. Those are e.g. on extension installation by the extension manager, or on calling the typo3 extension:setup command. The main component, dispatching the event however is the new PackageActivationService. The new service is a drop-in replacement for the InstallUtility->install() method, which is from now on just a wrapper around PackageActivationService->activate(). The wrapper is used to still pass the current instance to listeners of the AfterPackageActivationEvent.

TYPO3 already registers a couple of listeners to this event:

  • \TYPO3\CMS\Core\Package\Initialization\ImportExtensionDataOnPackageInitialization

  • \TYPO3\CMS\Core\Package\Initialization\ImportStaticSqlDataOnPackageInitialization

  • \TYPO3\CMS\Core\Package\Initialization\CheckForImportRequirements

  • \TYPO3\CMS\Impexp\Initialization\ImportContentOnPackageInitialization

  • \TYPO3\CMS\Impexp\Initialization\ImportSiteConfigurationsOnPackageInitialization

Developers are able to listen to the new event before or after TYPO3 Core listeners have been executed, using before and after in the listener registration. All listeners are able to store arbitrary data in the Event using the addStorageEntry() method. This is also used by the core listeners to store their result, which was previously passed to the removed EXT:extensionmanager PSR-14 events.

Listeners can access those information using corresponding getStorageEntry() method. Those entries are a PackageInitializationResult object, which features the following methods:

  • getIdentifier() - Returns the entry identifier, which is the listener service name for the TYPO3 Core listeners

  • getResult() - Returns the result data, added by the corresponding listener

Using the new Event, listeners are equipped with following methods:

  • getExtensionKey() - Returns the extension key for the activated package

  • getPackage() - Returns the PackageInterface object of the activated package

  • getContainer() - Returns the ContainerInterface, used on activating the package

  • getEmitter() - Returns the emitter / the service, which has dispatched the event

  • hasStorageEntry() - Whether a storage entry for a given identifier exists

  • getStorageEntry() - Returns a storage entry for a given identifier

  • addStorageEntry() - Adds a storage entry (PackageInitializationResult) to the event

  • removeStorageEntry() - Removes a storage entry by a given identifier

Note

In case you have previously called InstallUtility->processExtensionSetup() directly, you can now just dispatch the new event.

Example

The event listener class, using the PHP attribute #[AsEventListener] for registration, placing the listener after a specific core listener and adding a storage entry, using the listener class name as identifier (which is recommended and also done by TYPO3 Core):

use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Package\Event\PackageInitializationEvent;
use TYPO3\CMS\Core\Package\Initialization\ImportExtensionDataOnPackageInitialization;

final class PackageInitializationEventListener
{
    #[AsEventListener(after: ImportExtensionDataOnPackageInitialization::class)]
    public function __invoke(PackageInitializationEvent $event): void
    {
        if ($event->getExtensionKey() === 'my_ext') {
            $event->addStorageEntry(__CLASS__, 'my result');
        }
    }
}

Impact

Using the new PSR-14 event, it's now possible to execute custom functionality when a package has been activated. Since TYPO3 Core also uses listeners to this event, custom extensions can easily place their functionality in between and fetch necessary information directly from the event's storage, instead of registering dedicated listeners.