Skip to content

Commit bc0f0f8

Browse files
committed
minor #18348 [DependencyInjection] Add support for casting callables into single-method interfaces (alexandre-daubois)
This PR was merged into the 6.3 branch. Discussion ---------- [DependencyInjection] Add support for casting callables into single-method interfaces Resolves #18132 Commits ------- 6957aee [DependencyInjection] Add support for casting callables into single-method interfaces
2 parents 0ed29b2 + 6957aee commit bc0f0f8

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
@@ -1294,6 +1294,130 @@ 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+
Now, you can define a service implementing this method, among other util ones::
1317+
1318+
// src/Service/MessageFormatterInterface.php
1319+
namespace App\Service;
1320+
1321+
class MessageUtils
1322+
{
1323+
// other utils methods...
1324+
1325+
public function format($string $message, array $parameters): string
1326+
{
1327+
// ...
1328+
}
1329+
}
1330+
1331+
We can now use ``#[AutowireCallable]`` with our ``MessageUtils`` service
1332+
to inject our functional interface implementation::
1333+
1334+
namespace App\Service\Mail;
1335+
1336+
use App\Service\MessageFormatterInterface;
1337+
use App\Service\MessageUtils;
1338+
use Symfony\Component\DependencyInjection\Attribute\AutowireCallable;
1339+
1340+
class Mailer
1341+
{
1342+
public function __construct(
1343+
#[AutowireCallable(service: MessageUtils::class, method: 'formatMessage')]
1344+
private MessageFormatterInterface $formatter
1345+
) {
1346+
}
1347+
1348+
public function sendMail($string $message, array $parameters): string
1349+
{
1350+
$formattedMessage = $this->formatter->format($message, $parameters);
1351+
1352+
// ...
1353+
}
1354+
}
1355+
1356+
.. versionadded:: 6.3
1357+
1358+
The :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireCallable`
1359+
attribute was introduced in Symfony 6.3.
1360+
1361+
Alternatively, generating an adapter for a functional interface can also
1362+
be done through configuration:
1363+
1364+
.. configuration-block::
1365+
1366+
.. code-block:: yaml
1367+
1368+
# config/services.yaml
1369+
services:
1370+
1371+
# ...
1372+
1373+
app.message_formatter:
1374+
class: App\Service\MessageFormatterInterface
1375+
from_callable: [!service {class: 'App\Service\MessageUtils'}, 'formatMessage']
1376+
1377+
.. code-block:: xml
1378+
1379+
<!-- config/services.xml -->
1380+
<?xml version="1.0" encoding="UTF-8" ?>
1381+
<container xmlns="http://symfony.com/schema/dic/services"
1382+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1383+
xsi:schemaLocation="http://symfony.com/schema/dic/services
1384+
https://symfony.com/schema/dic/services/services-1.0.xsd">
1385+
1386+
<services>
1387+
<!-- ... -->
1388+
1389+
<service id="app.message_formatter" class="App\Service\MessageFormatterInterface">
1390+
<from-callable method="formatMessage">
1391+
<service class="App\Service\MessageUtils"/>
1392+
</from-callable>
1393+
</service>
1394+
1395+
</services>
1396+
</container>
1397+
1398+
.. code-block:: php
1399+
1400+
// config/services.php
1401+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1402+
1403+
use App\Service\MessageFormatterInterface;
1404+
use App\Service\MessageUtils;
1405+
1406+
return function(ContainerConfigurator $container) {
1407+
// ...
1408+
1409+
$container
1410+
->set('app.message_formatter', MessageFormatterInterface::class)
1411+
->fromCallable([inline_service(MessageUtils::class), 'formatMessage'])
1412+
->alias(MessageFormatterInterface::class, 'app.message_formatter')
1413+
;
1414+
};
1415+
1416+
By doing so, Symfony will generate a class (also called an *adapter*)
1417+
implementing ``MessageFormatterInterface`` that will forward calls of
1418+
``MessageFormatterInterface::format()`` to your underlying service's method
1419+
``MessageUtils::format()``, with all its arguments.
1420+
12971421
Learn more
12981422
----------
12991423

0 commit comments

Comments
 (0)