.. include:: /Includes.rst.txt
.. _breaking-110277-1784812454:
======================================================================
Breaking: #110277 - File renderer registration and interface changed
======================================================================
See :issue:`110277`
Description
===========
File renderers, used for example by the :html:`<f:media>` ViewHelper to
render audio, video or online media files, are now registered as tagged
services via dependency injection (see
:ref:`feature-110277-1784812454`). This comes with the following breaking
changes:
- :php:`\TYPO3\CMS\Core\Resource\Rendering\RendererRegistry->registerRendererClass()`
is now a no-op. Calling the method has no effect anymore, but triggers
an :php:`E_USER_DEPRECATED` notice. The method will be removed in
TYPO3 v16.0.
- :php:`\TYPO3\CMS\Core\Resource\Rendering\FileRendererInterface` does
not extend :php:`\TYPO3\CMS\Core\SingletonInterface` anymore. File
renderers are shared services managed by the dependency injection
container.
- The method :php:`getPriority()` has been removed from
:php:`FileRendererInterface`. The renderer priority is now defined at
registration time via the :php:`#[AsFileRenderer]` attribute or the
:yaml:`fal.file_renderer` service tag. Note that the file renderers
shipped with TYPO3 Core previously returned a priority of :php:`1`
from :php:`getPriority()` and are now registered with the attribute's
default priority of :php:`0`. Custom renderers that relied on a
priority of :php:`0` to rank strictly below all Core renderers now
rank equally with them instead and should use a negative priority to
keep the previous ordering.
- Renderers registered with the same priority are no longer guaranteed
to be asked in the order they were added: previously, same-priority
renderers kept the order in which
:php:`RendererRegistry->registerRendererClass()` was called from
:file:`ext_localconf.php`. The order of same-priority tagged services
is now an implementation detail of the dependency injection container
and must not be relied upon. Extensions that depend on a specific
evaluation order between renderers should assign distinct priorities
instead.
- The remaining methods of :php:`FileRendererInterface` are now
strictly typed: :php:`canRender(FileInterface $file): bool` and
:php:`render(FileInterface $file, int|string $width, int|string $height, array $options = []): string`.
- The methods :php:`createRendererInstance()` and
:php:`compareRendererPriority()` have been removed from
:php:`RendererRegistry`, the method :php:`getRendererInstances()` has
been changed from public to protected visibility, and the remaining
methods are now strictly typed.
- :php:`RendererRegistry` is now marked as :php:`@internal`, since
registering file renderers does not require interacting with the
registry anymore. TYPO3 Core resolves the matching renderer via
:php:`getRenderer()` internally, for example in the
:html:`<f:media>` ViewHelper.
Impact
======
File renderers registered via
:php:`RendererRegistry->registerRendererClass()` in
:file:`ext_localconf.php` are no longer evaluated. The corresponding
files (audio, video or online media) are no longer rendered by the custom
renderer until it is registered as a tagged service.
Custom renderer classes implementing :php:`FileRendererInterface` without
the adapted method signatures will cause a fatal PHP error.
Affected installations
======================
All installations with custom extensions registering file renderers via
:php:`RendererRegistry->registerRendererClass()`, or providing custom
implementations of :php:`FileRendererInterface`. The extension scanner
reports usages of :php:`registerRendererClass()` as weak match.
Migration
=========
Remove the :php:`RendererRegistry->registerRendererClass()` call from
:file:`ext_localconf.php` and add the :php:`#[AsFileRenderer]` attribute
to the renderer class instead. Move the priority previously returned by
:php:`getPriority()` to the attribute and remove the method. Add the
native type declarations to :php:`canRender()` and :php:`render()`:
.. code-block:: php
:caption: EXT:my_extension/Classes/Resource/Rendering/MyVideoRenderer.php
use TYPO3\CMS\Core\Attribute\AsFileRenderer;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\Rendering\FileRendererInterface;
#[AsFileRenderer(priority: 10)]
final class MyVideoRenderer implements FileRendererInterface
{
public function canRender(FileInterface $file): bool
{
// ...
}
public function render(FileInterface $file, int|string $width, int|string $height, array $options = []): string
{
// ...
}
}
In case the extension supports both TYPO3 v14 and v15, keep the
:php:`getPriority()` method (it is simply unused in v15) and register the
renderer in both ways: the :file:`ext_localconf.php` registration is
evaluated in v14, the attribute in v15. Since PHP parameter types must
not be narrowed in implementations, keep the :php:`$width` and
:php:`$height` parameters untyped in this case — only the :php:`bool`
and :php:`string` return type declarations are compatible with both
versions.
Code that called :php:`RendererRegistry->getRendererInstances()` to
inspect all registered renderers should inject :php:`RendererRegistry`
and use :php:`getRenderer($file)` to retrieve the matching renderer for
a given file instead.
.. index:: FAL, PHP-API, PartiallyScanned, ext:core
ℹ️ View this commit on Github
👥 Authored by Benni Mack benni@typo3.org
✔️ Merged by Stefan Bürk stefan@buerk.tech
Commit message
[!!!][FEATURE] Register file renderers as tagged services
File renderers are now registered as tagged services via the new
PHP attribute #[AsFileRenderer] (or the service tag
"fal.file_renderer") instead of the programmatic registration
through RendererRegistry->registerRendererClass() in
ext_localconf.php. The renderer priority is defined at
registration time via the attribute, renderers with a higher
priority are asked first whether they can render a given file.
The registration is resolved once at container compile time
instead of executing registration code on every request: loading
and validating the renderer classes per request as well as
instantiating and sorting them by priority at runtime is gone,
the compiled container provides a pre-sorted lazy iterator.
Renderers become proper services with constructor injection,
their order no longer depends on the ext_localconf.php loading
order, and the new FileRendererPass validates at compile time
that every tagged service implements FileRendererInterface,
failing the container build instead of erroring at render time.
This comes with the following breaking changes:
and will be removed in TYPO3 v16.0.
anymore, renderers are shared services managed by the DI
container.
the remaining methods are now strictly typed.
API is getRenderer(), getRendererInstances() is protected
and the priority sorting is delegated to the container.
Resolves: #110277
Releases: main
Change-Id: I7b90fcedf1d81a671266dd402fbc28b680fba3ac
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/94910
Tested-by: core-ci typo3@b13.com
Reviewed-by: Oli Bartsch bo@cedev.de
Tested-by: Oli Bartsch bo@cedev.de
Tested-by: Stefan Bürk stefan@buerk.tech
Reviewed-by: Stefan Bürk stefan@buerk.tech
➕ Added files
15.0/Breaking-110277-FileRendererRegistrationAndInterfaceChanged.rst
15.0/Feature-110277-RegisterFileRenderersAsTaggedServices.rst