diff --git a/components/http_kernel.rst b/components/http_kernel.rst index a68d2470501..4971d246655 100644 --- a/components/http_kernel.rst +++ b/components/http_kernel.rst @@ -281,6 +281,16 @@ Another typical use-case for this event is to retrieve the attributes from the controller using the :method:`Symfony\\Component\\HttpKernel\\Event\\ControllerEvent::getAttributes` method. See the Symfony section below for some examples. +Controller attributes are stored in the ``_controller_attributes`` request +attribute. This decouples them from the controller source code, allowing +listeners to override attributes at runtime (e.g. to change caching behavior +for specific requests without modifying the controller). + +.. versionadded:: 8.1 + + Storing controller attributes in the ``_controller_attributes`` request + attribute was introduced in Symfony 8.1. + Listeners to this event can also change the controller callable completely by calling :method:`ControllerEvent::setController ` on the event object that's passed to listeners on this event. diff --git a/logging/monolog_console.rst b/logging/monolog_console.rst index 2033c971097..baa21f74062 100644 --- a/logging/monolog_console.rst +++ b/logging/monolog_console.rst @@ -119,11 +119,17 @@ The Monolog console handler is enabled by default: .. note:: - The handler name (``console``) and the excluded channel (``!console``) are - independent concepts. The handler name is an arbitrary identifier for this - handler configuration, while ``!console`` excludes log messages from the - ``console`` :doc:`logging channel `, which is - used internally by the console component itself. + In this configuration, ``console`` is an arbitrary handler name and can be + any string. The ``type: console`` option selects the + :class:`Symfony\\Bridge\\Monolog\\Handler\\ConsoleHandler`, which writes log + messages to the command output. + + The ``channels`` option uses the ``!`` prefix to exclude specific + :doc:`channels `. The ``console`` channel is + excluded to reduce noise. This is the channel where Symfony logs command + lifecycle events (e.g. *Command "{command}" exited with code "{code}"*). + Excluding this channel does **not** affect any log messages you write yourself + inside your commands, which use different channels and will still appear normally. Now, log messages will be shown on the console based on the log levels and verbosity. By default (normal verbosity level), warnings and higher will be shown. But in diff --git a/reference/events.rst b/reference/events.rst index 57806ee8f8d..0dfe6da7a99 100644 --- a/reference/events.rst +++ b/reference/events.rst @@ -69,6 +69,34 @@ even to change the controller entirely:: $event->setController($myCustomController); } +You can also use ``getAttributes()`` to retrieve PHP attributes declared on the +controller: + +.. code-block:: php-attributes + + use Symfony\Component\HttpKernel\Event\ControllerEvent; + + public function onKernelController(ControllerEvent $event): void + { + // get all attributes, grouped by class name + $allAttributes = $event->getAttributes(); + + // get all attributes as a flat list (not grouped) + $flatAttributes = $event->getAttributes('*'); + + // get attributes of a specific class + $cacheAttributes = $event->getAttributes(Cache::class); + } + +Controller attributes are stored in the ``_controller_attributes`` request +attribute, which means they can be overridden at runtime to change the behavior +of attribute-based listeners without modifying the controller source code. + +.. versionadded:: 8.1 + + Storing controller attributes in the ``_controller_attributes`` request + attribute was introduced in Symfony 8.1. + .. seealso:: Read more on the :ref:`kernel.controller event `. @@ -164,6 +192,30 @@ before sending it back (e.g. add/modify HTTP headers, add cookies, etc.):: // ... modify the response object } +You can use ``getControllerAttributes()`` to retrieve PHP attributes declared +on the controller that handled the request: + +.. code-block:: php-attributes + + use Symfony\Component\HttpKernel\Event\ResponseEvent; + + public function onKernelResponse(ResponseEvent $event): void + { + // get all controller attributes, grouped by class name + $allAttributes = $event->getControllerAttributes(); + + // get all controller attributes as a flat list (not grouped) + $flatAttributes = $event->getControllerAttributes('*'); + + // get attributes of a specific class + $cacheAttributes = $event->getControllerAttributes(Cache::class); + } + +.. versionadded:: 8.1 + + The ``ResponseEvent::getControllerAttributes()`` method was introduced in + Symfony 8.1. + .. seealso:: Read more on the :ref:`kernel.response event `. diff --git a/service_container.rst b/service_container.rst index f230cfe1f5a..da5d7d0a455 100644 --- a/service_container.rst +++ b/service_container.rst @@ -271,19 +271,6 @@ You can limit service registration to specific environments as follows: services: App\Service\AnotherClass: ~ - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php // config/services.php diff --git a/testing.rst b/testing.rst index d373c5b28af..50525adcc21 100644 --- a/testing.rst +++ b/testing.rst @@ -314,27 +314,31 @@ concrete one:: No further configuration is required, as the test service container is a special one that allows you to interact with private services and aliases. -Mocking non-shared services +Mocking Non-Shared Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Non-shared services can be mocked in tests when using the test service container. +:doc:`Non-shared services ` are instantiated every time +they are retrieved from the container. For this reason, in tests you must mock +them using a ``Closure`` that acts as a factory, rather than a concrete instance:: -Because non-shared services are instantiated every time they are retrieved, -they must be replaced by a *factory* instead of a concrete instance. For this -reason, non-shared services must be mocked using a ``Closure``:: - - static::bootKernel(); + self::bootKernel(); $container = static::getContainer(); - $services = [new \stdClass(), new \stdClass()]; + $container->set(Mailer::class, function (): Mailer { + $mailer = $this->createMock(Mailer::class); + $mailer->method('send')->willReturn(true); - $container->set('non_shared_service', static function () use (&$services) { - return array_pop($services); + return $mailer; }); -The closure acts as a factory and is called each time the service is requested. + $newsletterGenerator = $container->get(NewsletterGenerator::class); + +In this example, each time the container resolves ``Mailer``, the closure is +called and a new mock instance is returned + +.. versionadded:: 8.1 -Replacing a non-shared service with a non-callable value will throw an exception. + Support for mocking non-shared services was introduced in Symfony 8.1. .. _testing-databases: