Skip to content

Commit 6957aee

Browse files
[DependencyInjection] Add support for casting callables into single-method interfaces
1 parent 4f56d81 commit 6957aee

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

service_container.rst

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,130 @@ the closure::
13341334
// services depending on which environment you're on
13351335
};
13361336

1337+
Generating Adapters for Functional Interfaces
1338+
---------------------------------------------
1339+
1340+
Functional interfaces are interfaces with a single method.
1341+
They are conceptually very similar to a closure except that their only method
1342+
has a name. Moreover, they can be used as type-hints across your code.
1343+
1344+
The :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireCallable`
1345+
attribute can be used to generate an adapter for a functional interface.
1346+
Let's say you have the following functional interface::
1347+
1348+
// src/Service/MessageFormatterInterface.php
1349+
namespace App\Service;
1350+
1351+
interface MessageFormatterInterface
1352+
{
1353+
public function format(string $message, array $parameters): string;
1354+
}
1355+
1356+
Now, you can define a service implementing this method, among other util ones::
1357+
1358+
// src/Service/MessageFormatterInterface.php
1359+
namespace App\Service;
1360+
1361+
class MessageUtils
1362+
{
1363+
// other utils methods...
1364+
1365+
public function format($string $message, array $parameters): string
1366+
{
1367+
// ...
1368+
}
1369+
}
1370+
1371+
We can now use ``#[AutowireCallable]`` with our ``MessageUtils`` service
1372+
to inject our functional interface implementation::
1373+
1374+
namespace App\Service\Mail;
1375+
1376+
use App\Service\MessageFormatterInterface;
1377+
use App\Service\MessageUtils;
1378+
use Symfony\Component\DependencyInjection\Attribute\AutowireCallable;
1379+
1380+
class Mailer
1381+
{
1382+
public function __construct(
1383+
#[AutowireCallable(service: MessageUtils::class, method: 'formatMessage')]
1384+
private MessageFormatterInterface $formatter
1385+
) {
1386+
}
1387+
1388+
public function sendMail($string $message, array $parameters): string
1389+
{
1390+
$formattedMessage = $this->formatter->format($message, $parameters);
1391+
1392+
// ...
1393+
}
1394+
}
1395+
1396+
.. versionadded:: 6.3
1397+
1398+
The :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireCallable`
1399+
attribute was introduced in Symfony 6.3.
1400+
1401+
Alternatively, generating an adapter for a functional interface can also
1402+
be done through configuration:
1403+
1404+
.. configuration-block::
1405+
1406+
.. code-block:: yaml
1407+
1408+
# config/services.yaml
1409+
services:
1410+
1411+
# ...
1412+
1413+
app.message_formatter:
1414+
class: App\Service\MessageFormatterInterface
1415+
from_callable: [!service {class: 'App\Service\MessageUtils'}, 'formatMessage']
1416+
1417+
.. code-block:: xml
1418+
1419+
<!-- config/services.xml -->
1420+
<?xml version="1.0" encoding="UTF-8" ?>
1421+
<container xmlns="http://symfony.com/schema/dic/services"
1422+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1423+
xsi:schemaLocation="http://symfony.com/schema/dic/services
1424+
https://symfony.com/schema/dic/services/services-1.0.xsd">
1425+
1426+
<services>
1427+
<!-- ... -->
1428+
1429+
<service id="app.message_formatter" class="App\Service\MessageFormatterInterface">
1430+
<from-callable method="formatMessage">
1431+
<service class="App\Service\MessageUtils"/>
1432+
</from-callable>
1433+
</service>
1434+
1435+
</services>
1436+
</container>
1437+
1438+
.. code-block:: php
1439+
1440+
// config/services.php
1441+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1442+
1443+
use App\Service\MessageFormatterInterface;
1444+
use App\Service\MessageUtils;
1445+
1446+
return function(ContainerConfigurator $container) {
1447+
// ...
1448+
1449+
$container
1450+
->set('app.message_formatter', MessageFormatterInterface::class)
1451+
->fromCallable([inline_service(MessageUtils::class), 'formatMessage'])
1452+
->alias(MessageFormatterInterface::class, 'app.message_formatter')
1453+
;
1454+
};
1455+
1456+
By doing so, Symfony will generate a class (also called an *adapter*)
1457+
implementing ``MessageFormatterInterface`` that will forward calls of
1458+
``MessageFormatterInterface::format()`` to your underlying service's method
1459+
``MessageUtils::format()``, with all its arguments.
1460+
13371461
Learn more
13381462
----------
13391463

0 commit comments

Comments
 (0)