Skip to content

Commit 928fdf9

Browse files
committed
Merge branch '6.3' into 6.4
* 6.3: Minor tweaks [DependencyInjection] Add support for casting callables into single-method interfaces
2 parents d7bd395 + 7fb55b4 commit 928fdf9

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed

service_container.rst

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,6 +1294,131 @@ the closure::
12941294
// services depending on which environment you're on
12951295
};
12961296

1297+
Generating Adapters for Functional Interfaces
1298+
---------------------------------------------
1299+
1300+
Functional interfaces are interfaces with a single method.
1301+
They are conceptually very similar to a closure except that their only method
1302+
has a name. Moreover, they can be used as type-hints across your code.
1303+
1304+
The :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireCallable`
1305+
attribute can be used to generate an adapter for a functional interface.
1306+
Let's say you have the following functional interface::
1307+
1308+
// src/Service/MessageFormatterInterface.php
1309+
namespace App\Service;
1310+
1311+
interface MessageFormatterInterface
1312+
{
1313+
public function format(string $message, array $parameters): string;
1314+
}
1315+
1316+
You also have a service that defines many methods and one of them is the same
1317+
``format()`` method of the previous interface::
1318+
1319+
// src/Service/MessageFormatterInterface.php
1320+
namespace App\Service;
1321+
1322+
class MessageUtils
1323+
{
1324+
// other methods...
1325+
1326+
public function format($string $message, array $parameters): string
1327+
{
1328+
// ...
1329+
}
1330+
}
1331+
1332+
Thanks to the ``#[AutowireCallable]`` attribute, you can now inject this
1333+
``MessageUtils`` service as a functional interface implementation::
1334+
1335+
namespace App\Service\Mail;
1336+
1337+
use App\Service\MessageFormatterInterface;
1338+
use App\Service\MessageUtils;
1339+
use Symfony\Component\DependencyInjection\Attribute\AutowireCallable;
1340+
1341+
class Mailer
1342+
{
1343+
public function __construct(
1344+
#[AutowireCallable(service: MessageUtils::class, method: 'formatMessage')]
1345+
private MessageFormatterInterface $formatter
1346+
) {
1347+
}
1348+
1349+
public function sendMail($string $message, array $parameters): string
1350+
{
1351+
$formattedMessage = $this->formatter->format($message, $parameters);
1352+
1353+
// ...
1354+
}
1355+
}
1356+
1357+
.. versionadded:: 6.3
1358+
1359+
The :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireCallable`
1360+
attribute was introduced in Symfony 6.3.
1361+
1362+
Instead of using the ``#[AutowireCallable]`` attribute, you can also generate
1363+
an adapter for a functional interface through configuration:
1364+
1365+
.. configuration-block::
1366+
1367+
.. code-block:: yaml
1368+
1369+
# config/services.yaml
1370+
services:
1371+
1372+
# ...
1373+
1374+
app.message_formatter:
1375+
class: App\Service\MessageFormatterInterface
1376+
from_callable: [!service {class: 'App\Service\MessageUtils'}, 'formatMessage']
1377+
1378+
.. code-block:: xml
1379+
1380+
<!-- config/services.xml -->
1381+
<?xml version="1.0" encoding="UTF-8" ?>
1382+
<container xmlns="http://symfony.com/schema/dic/services"
1383+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1384+
xsi:schemaLocation="http://symfony.com/schema/dic/services
1385+
https://symfony.com/schema/dic/services/services-1.0.xsd">
1386+
1387+
<services>
1388+
<!-- ... -->
1389+
1390+
<service id="app.message_formatter" class="App\Service\MessageFormatterInterface">
1391+
<from-callable method="formatMessage">
1392+
<service class="App\Service\MessageUtils"/>
1393+
</from-callable>
1394+
</service>
1395+
1396+
</services>
1397+
</container>
1398+
1399+
.. code-block:: php
1400+
1401+
// config/services.php
1402+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1403+
1404+
use App\Service\MessageFormatterInterface;
1405+
use App\Service\MessageUtils;
1406+
1407+
return function(ContainerConfigurator $container) {
1408+
// ...
1409+
1410+
$container
1411+
->set('app.message_formatter', MessageFormatterInterface::class)
1412+
->fromCallable([inline_service(MessageUtils::class), 'formatMessage'])
1413+
->alias(MessageFormatterInterface::class, 'app.message_formatter')
1414+
;
1415+
};
1416+
1417+
By doing so, Symfony will generate a class (also called an *adapter*)
1418+
implementing ``MessageFormatterInterface`` that will forward calls of
1419+
``MessageFormatterInterface::format()`` to your underlying service's method
1420+
``MessageUtils::format()``, with all its arguments.
1421+
12971422
Learn more
12981423
----------
12991424

0 commit comments

Comments
 (0)