Movatterモバイル変換


[0]ホーム

URL:


Skip to content
Version:

Defining Services Dependencies Automatically (Autowiring)

Edit this page

Autowiring allows you to manage services in the container with minimalconfiguration. It reads the type-hints on your constructor (or other methods)and automatically passes the correct services to each method. Symfony'sautowiring is designed to be predictable: if it is not absolutely clear whichdependency should be passed, you'll see an actionable exception.

Tip

Thanks to Symfony's compiled container, there is no runtime overhead for usingautowiring.

An Autowiring Example

Imagine you're building an API to publish statuses on a Twitter feed, obfuscatedwithROT13, a fun encoder that shifts all characters 13 letters forward inthe alphabet.

Start by creating a ROT13 transformer class:

12345678910
// src/Util/Rot13Transformer.phpnamespaceApp\Util;classRot13Transformer{publicfunctiontransform(string$value):string{returnstr_rot13($value);    }}

And now a Twitter client using this transformer:

1234567891011121314151617181920
// src/Service/TwitterClient.phpnamespaceApp\Service;useApp\Util\Rot13Transformer;// ...classTwitterClient{publicfunction__construct(private Rot13Transformer$transformer,    ){    }publicfunctiontweet(User$user,string$key,string$status):void{$transformedStatus =$this->transformer->transform($status);// ... connect to Twitter and send the encoded status    }}

If you're using thedefault services.yaml configuration,both classes are automatically registered as services and configured to be autowired.This means you can use them immediately withoutany configuration.

However, to understand autowiring better, the following examples explicitly configureboth services:

12345678910111213
# config/services.yamlservices:_defaults:autowire:trueautoconfigure:true# ...App\Service\TwitterClient:# redundant thanks to _defaults, but value is overridable on each serviceautowire:trueApp\Util\Rot13Transformer:autowire:true
12345678910111213141516
<!-- config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><containerxmlns="http://symfony.com/schema/dic/services"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd"><services><defaultsautowire="true"autoconfigure="true"/><!-- ... --><!-- autowire is redundant thanks to defaults, but value is overridable on each service --><serviceid="App\Service\TwitterClient"autowire="true"/><serviceid="App\Util\Rot13Transformer"autowire="true"/></services></container>
123456789101112131415
// config/services.phpreturnfunction(ContainerConfigurator$container):void{$services =$container->services()        ->defaults()            ->autowire()            ->autoconfigure()    ;$services->set(TwitterClient::class)// redundant thanks to defaults, but value is overridable on each service        ->autowire();$services->set(Rot13Transformer::class)        ->autowire();};

Now, you can use theTwitterClient service immediately in a controller:

123456789101112131415161718192021
// src/Controller/DefaultController.phpnamespaceApp\Controller;useApp\Service\TwitterClient;useSymfony\Bundle\FrameworkBundle\Controller\AbstractController;useSymfony\Component\HttpFoundation\Request;useSymfony\Component\HttpFoundation\Response;useSymfony\Component\Routing\Attribute\Route;classDefaultControllerextendsAbstractController{#[Route('/tweet')]publicfunctiontweet(TwitterClient$twitterClient, Request$request):Response{// fetch $user, $key, $status from the POST'ed data$twitterClient->tweet($user,$key,$status);// ...    }}

This works automatically! The container knows to pass theRot13Transformer serviceas the first argument when creating theTwitterClient service.

Autowiring Logic Explained

Autowiring works by reading theRot13Transformertype-hint inTwitterClient:

123456789101112131415
// src/Service/TwitterClient.phpnamespaceApp\Service;// ...useApp\Util\Rot13Transformer;classTwitterClient{// ...publicfunction__construct(private Rot13Transformer$transformer,    ){    }}

The autowiring systemlooks for a service whose id exactly matches the type-hint:soApp\Util\Rot13Transformer. In this case, that exists! When you configuredtheRot13Transformer service, you used its fully-qualified class name as itsid. Autowiring isn't magic: it looks for a service whose id matches the type-hint.If youload services automatically,each service's id is its class name.

If there isnot a service whose id exactly matches the type, a clear exceptionwill be thrown.

Autowiring is a great way to automate configuration, and Symfony tries to be aspredictable and as clear as possible.

Using Aliases to Enable Autowiring

The main way to configure autowiring is to create a service whose id exactly matchesits class. In the previous example, the service's id isApp\Util\Rot13Transformer,which allows us to autowire this type automatically.

This can also be accomplished using analias. Suppose thatfor some reason, the id of the service was insteadapp.rot13.transformer. Inthis case, any arguments type-hinted with the class name (App\Util\Rot13Transformer)can no longer be autowired.

No problem! To fix this, you cancreate a service whose id matches the class byadding a service alias:

12345678910111213
# config/services.yamlservices:# ...# the id is not a class, so it won't be used for autowiringapp.rot13.transformer:class:App\Util\Rot13Transformer# ...# but this fixes it!# the "app.rot13.transformer" service will be injected when# an App\Util\Rot13Transformer type-hint is detectedApp\Util\Rot13Transformer:'@app.rot13.transformer'
12345678910111213
<!-- config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><containerxmlns="http://symfony.com/schema/dic/services"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd"><services><!-- ... --><serviceid="app.rot13.transformer"class="App\Util\Rot13Transformer"autowire="true"/><serviceid="App\Util\Rot13Transformer"alias="app.rot13.transformer"/></services></container>
1234567891011121314151617
// config/services.phpnamespaceSymfony\Component\DependencyInjection\Loader\Configurator;useApp\Util\Rot13Transformer;returnfunction(ContainerConfigurator$container):void{// ...// the id is not a class, so it won't be used for autowiring$services->set('app.rot13.transformer', Rot13Transformer::class)        ->autowire();// but this fixes it!// the "app.rot13.transformer" service will be injected when// an App\Util\Rot13Transformer type-hint is detected$services->alias(Rot13Transformer::class,'app.rot13.transformer');};

This creates a service "alias", whose id isApp\Util\Rot13Transformer.Thanks to this, autowiring sees this and uses it whenever theRot13Transformerclass is type-hinted.

Tip

Aliases are used by the core bundles to allow services to be autowired. Forexample, MonologBundle creates a service whose id islogger. But it alsoadds an alias:Psr\Log\LoggerInterface that points to thelogger service.This is why arguments type-hinted withPsr\Log\LoggerInterface can be autowired.

Working with Interfaces

You might also find yourself type-hinting abstractions (e.g. interfaces) insteadof concrete classes as it replaces your dependencies with other objects.

To follow this best practice, suppose you decide to create aTransformerInterface:

1234567
// src/Util/TransformerInterface.phpnamespaceApp\Util;interfaceTransformerInterface{publicfunctiontransform(string$value):string;}

Then, you updateRot13Transformer to implement it:

12345
// ...classRot13TransformerimplementsTransformerInterface{// ...}

Now that you have an interface, you should use this as your type-hint:

12345678910
classTwitterClient{publicfunction__construct(private TransformerInterface$transformer,    ){// ...    }// ...}

But now, the type-hint (App\Util\TransformerInterface) no longer matchesthe id of the service (App\Util\Rot13Transformer). This means that theargument can no longer be autowired.

To fix that, add analias:

123456789
# config/services.yamlservices:# ...App\Util\Rot13Transformer:~# the App\Util\Rot13Transformer service will be injected when# an App\Util\TransformerInterface type-hint is detectedApp\Util\TransformerInterface:'@App\Util\Rot13Transformer'
12345678910111213
<!-- config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><containerxmlns="http://symfony.com/schema/dic/services"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd"><services><!-- ... --><serviceid="App\Util\Rot13Transformer"/><serviceid="App\Util\TransformerInterface"alias="App\Util\Rot13Transformer"/></services></container>
123456789101112131415
// config/services.phpnamespaceSymfony\Component\DependencyInjection\Loader\Configurator;useApp\Util\Rot13Transformer;useApp\Util\TransformerInterface;returnfunction(ContainerConfigurator$container):void{// ...$services->set(Rot13Transformer::class);// the App\Util\Rot13Transformer service will be injected when// an App\Util\TransformerInterface type-hint is detected$services->alias(TransformerInterface::class, Rot13Transformer::class);};

Thanks to theApp\Util\TransformerInterface alias, the autowiring subsystemknows that theApp\Util\Rot13Transformer service should be injected whendealing with theTransformerInterface.

Tip

When using aservice definition prototype, if only one service isdiscovered that implements an interface, configuring the alias is not mandatoryand Symfony will automatically create one.

Tip

Autowiring is powerful enough to guess which service to inject even when usingunion and intersection types. This means you're able to type-hint argument withcomplex types like this:

1234567891011121314
useSymfony\Component\Serializer\Normalizer\DenormalizerInterface;useSymfony\Component\Serializer\Normalizer\NormalizerInterface;useSymfony\Component\Serializer\SerializerInterface;classDataFormatter{publicfunction__construct(private(NormalizerInterface&DenormalizerInterface)|SerializerInterface$transformer,    ){// ...    }// ...}

Dealing with Multiple Implementations of the Same Type

Suppose you create a second class -UppercaseTransformer that implementsTransformerInterface:

12345678910
// src/Util/UppercaseTransformer.phpnamespaceApp\Util;classUppercaseTransformerimplementsTransformerInterface{publicfunctiontransform(string$value):string{returnstrtoupper($value);    }}

If you register this as a service, you now havetwo services that implement theApp\Util\TransformerInterface type. Autowiring subsystem can not decidewhich one to use. Remember, autowiring isn't magic; it looks for a servicewhose id matches the type-hint. So you need to choose one bycreating an alias from the type to the correct service id.Additionally, you can define several named autowiring aliases if you want to useone implementation in some cases, and another implementation in someother cases.

For instance, you may want to use theRot13Transformerimplementation by default when theTransformerInterface interface istype hinted, but use theUppercaseTransformer implementation in somespecific cases. To do so, you can create a normal alias from theTransformerInterface interface toRot13Transformer, and thencreate anamed autowiring alias from a special string containing theinterface followed by an argument name matching the one you use when doingthe injection:

12345678910111213141516171819
// src/Service/MastodonClient.phpnamespaceApp\Service;useApp\Util\TransformerInterface;classMastodonClient{publicfunction__construct(private TransformerInterface$shoutyTransformer,    ){    }publicfunctiontoot(User$user,string$key,string$status):void{$transformedStatus =$this->transformer->transform($status);// ... connect to Mastodon and send the transformed status    }}
1234567891011121314151617181920212223242526
# config/services.yamlservices:# ...App\Util\Rot13Transformer:~App\Util\UppercaseTransformer:~# the App\Util\UppercaseTransformer service will be# injected when an App\Util\TransformerInterface# type-hint for a $shoutyTransformer argument is detectedApp\Util\TransformerInterface$shoutyTransformer:'@App\Util\UppercaseTransformer'# If the argument used for injection does not match, but the# type-hint still matches, the App\Util\Rot13Transformer# service will be injected.App\Util\TransformerInterface:'@App\Util\Rot13Transformer'App\Service\TwitterClient:# the Rot13Transformer will be passed as the $transformer argumentautowire:true# If you wanted to choose the non-default service and do not# want to use a named autowiring alias, wire it manually:# arguments:#     $transformer: '@App\Util\UppercaseTransformer'# ...
123456789101112131415161718192021
<!-- config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><containerxmlns="http://symfony.com/schema/dic/services"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd"><services><!-- ... --><serviceid="App\Util\Rot13Transformer"/><serviceid="App\Util\UppercaseTransformer"/><serviceid="App\Util\TransformerInterface"alias="App\Util\Rot13Transformer"/><serviceid="App\Util\TransformerInterface $shoutyTransformer"alias="App\Util\UppercaseTransformer"/><serviceid="App\Service\TwitterClient"autowire="true"><!-- <argument key="$transformer" type="service"/> --></service></services></container>
1234567891011121314151617181920212223242526272829303132333435
// config/services.phpnamespaceSymfony\Component\DependencyInjection\Loader\Configurator;useApp\Service\MastodonClient;useApp\Service\TwitterClient;useApp\Util\Rot13Transformer;useApp\Util\TransformerInterface;useApp\Util\UppercaseTransformer;returnfunction(ContainerConfigurator$container):void{// ...$services->set(Rot13Transformer::class)->autowire();$services->set(UppercaseTransformer::class)->autowire();// the App\Util\UppercaseTransformer service will be// injected when an App\Util\TransformerInterface// type-hint for a $shoutyTransformer argument is detected$services->alias(TransformerInterface::class.' $shoutyTransformer', UppercaseTransformer::class);// If the argument used for injection does not match, but the// type-hint still matches, the App\Util\Rot13Transformer// service will be injected.$services->alias(TransformerInterface::class, Rot13Transformer::class);$services->set(TwitterClient::class)// the Rot13Transformer will be passed as the $transformer argument        ->autowire()// If you wanted to choose the non-default service and do not// want to use a named autowiring alias, wire it manually://     ->arg('$transformer', service(UppercaseTransformer::class))// ...    ;};

Thanks to theApp\Util\TransformerInterface alias, any argument type-hintedwith this interface will be passed theApp\Util\Rot13Transformer service.If the argument is named$shoutyTransformer,App\Util\UppercaseTransformer will be used instead.But, you can also manually wire anyother service by specifying the argumentunder the arguments key.

Another option is to use the#[Target] attribute. By adding this attributeto the argument you want to autowire, you can specify which service to inject bypassing the name of the argument used in the named alias. This way, you can havemultiple services implementing the same interface and keep the argument nameseparate from any implementation name (like shown in the example above). In addition,you'll get an exception in case you make any typo in the target name.

Warning

The#[Target] attribute only accepts the name of the argument used in thenamed alias; itdoes not accept service ids or service aliases.

You can get a list of named autowiring aliases by running thedebug:autowiring command:

12345678910111213141516
$php bin/console debug:autowiring LoggerInterfaceAutowirable Types================= The following classes & interfaces can be used astype-hints when autowiring: (only showing classes/interfaces matching LoggerInterface) Describes a logger instance. Psr\Log\LoggerInterface -alias:monolog.logger Psr\Log\LoggerInterface$assetMapperLogger - target:asset_mapperLogger -alias:monolog.logger.asset_mapper Psr\Log\LoggerInterface$cacheLogger -alias:monolog.logger.cache Psr\Log\LoggerInterface$httpClientLogger - target:http_clientLogger -alias:monolog.logger.http_client Psr\Log\LoggerInterface$mailerLogger -alias:monolog.logger.mailer [...]

Suppose you want to inject theApp\Util\UppercaseTransformer service. You would usethe#[Target] attribute by passing the name of the$shoutyTransformer argument:

1234567891011121314
// src/Service/MastodonClient.phpnamespaceApp\Service;useApp\Util\TransformerInterface;useSymfony\Component\DependencyInjection\Attribute\Target;classMastodonClient{publicfunction__construct(#[Target('shoutyTransformer')]private TransformerInterface$transformer,    ){    }}

Tip

Since the#[Target] attribute normalizes the string passed to it to itscamelCased form, name variations (e.g.shouty.transformer) also work.

Note

Some IDEs will show an error when using#[Target] as in the previous example:"Attribute cannot be applied to a property because it does not contain the 'Attribute::TARGET_PROPERTY' flag".The reason is that thanks toPHP constructor promotion this constructorargument is both a parameter and a class property. You can safely ignore this error message.

Fixing Non-Autowireable Arguments

Autowiring only works when your argument is anobject. But if you have a scalarargument (e.g. a string), this cannot be autowired: Symfony will throw a clearexception.

To fix this, you canmanually wire the problematic argumentin the service configuration. You wire up only the difficult arguments,Symfony takes care of the rest.

You can also use the#[Autowire] parameter attribute to instruct the autowiringlogic about those arguments:

123456789101112131415
// src/Service/MessageGenerator.phpnamespaceApp\Service;usePsr\Log\LoggerInterface;useSymfony\Component\DependencyInjection\Attribute\Autowire;classMessageGenerator{publicfunction__construct(#[Autowire(service:'monolog.logger.request')]private LoggerInterface$logger,    ){// ...    }}

The#[Autowire] attribute can also be used forparameters,complex expressions and evenenvironment variables ,including env variable processors:

1234567891011121314151617181920212223242526272829303132
// src/Service/MessageGenerator.phpnamespaceApp\Service;usePsr\Log\LoggerInterface;useSymfony\Component\DependencyInjection\Attribute\Autowire;classMessageGenerator{publicfunction__construct(        // use the %...% syntax for parameters#[Autowire('%kernel.project_dir%/data')]string$dataDir,        // or use argument"param"#[Autowire(param:'kernel.debug')]bool$debugMode,        // expressions#[Autowire(expression:'service("App\\\Mail\\\MailerConfiguration").getMailerMethod()')]string$mailerMethod,        // environment variables#[Autowire(env:'SOME_ENV_VAR')]string$senderName,        // environment variables with processors#[Autowire(env:'bool:SOME_BOOL_ENV_VAR')]bool$allowAttachments,    ){    }// ...}

Generate Closures With Autowiring

Aservice closure is an anonymous function that returns a service. This typeof instantiation is handy when you are dealing with lazy-loading. It is alsouseful for non-shared service dependencies.

Automatically creating a closure encapsulating the service instantiation can bedone with theAutowireServiceClosureattribute:

12345678910111213141516171819202122232425262728293031323334353637383940
// src/Service/Remote/MessageFormatter.phpnamespaceApp\Service\Remote;useSymfony\Component\DependencyInjection\Attribute\AsAlias;#[AsAlias('third_party.remote_message_formatter')]classMessageFormatter{publicfunction__construct(){// ...    }publicfunctionformat(string$message):string{// ...    }}// src/Service/MessageGenerator.phpnamespaceApp\Service;useApp\Service\Remote\MessageFormatter;useSymfony\Component\DependencyInjection\Attribute\AutowireServiceClosure;classMessageGenerator{publicfunction__construct(#[AutowireServiceClosure('third_party.remote_message_formatter')]private \Closure$messageFormatterResolver,    ){    }publicfunctiongenerate(string$message):void{$formattedMessage = ($this->messageFormatterResolver)()->format($message);// ...    }}

It is common that a service accepts a closure with a specific signature.In this case, you can use theAutowireCallable attributeto generate a closure with the same signature as a specific method of a service. Whenthis closure is called, it will pass all its arguments to the underlying servicefunction. If the closure needs to be called more than once, the service instanceis reused for repeated calls. Unlike a service closure, this will notcreate extra instances of a non-shared service:

1234567891011121314151617181920
// src/Service/MessageGenerator.phpnamespaceApp\Service;useSymfony\Component\DependencyInjection\Attribute\AutowireCallable;classMessageGenerator{publicfunction__construct(#[AutowireCallable(service:'third_party.remote_message_formatter',method:'format')]private \Closure$formatCallable,    ){    }publicfunctiongenerate(string$message):void{$formattedMessage = ($this->formatCallable)($message);// ...    }}

Finally, you can pass thelazy: true option to theAutowireCallableattribute. By doing so, the callable will automatically be lazy, which meansthat the encapsulated service will be instantiatedonly at theclosure's first call.

TheAutowireMethodOfattribute provides a simpler way of specifying the name of the service methodby using the property name as method name:

1234567891011121314151617181920
// src/Service/MessageGenerator.phpnamespaceApp\Service;useSymfony\Component\DependencyInjection\Attribute\AutowireMethodOf;classMessageGenerator{publicfunction__construct(#[AutowireMethodOf('third_party.remote_message_formatter')]private \Closure$format,    ){    }publicfunctiongenerate(string$message):void{$formattedMessage = ($this->format)($message);// ...    }}

7.1

TheAutowireMethodOfattribute was introduced in Symfony 7.1.

Autowiring other Methods (e.g. Setters and Public Typed Properties)

When autowiring is enabled for a service, you canalso configure the containerto call methods on your class when it's instantiated. For example, suppose you wantto inject thelogger service, and decide to use setter-injection:

123456789101112131415161718192021
// src/Util/Rot13Transformer.phpnamespaceApp\Util;useSymfony\Contracts\Service\Attribute\Required;classRot13Transformer{private LoggerInterface$logger;#[Required]publicfunctionsetLogger(LoggerInterface$logger):void{$this->logger =$logger;    }publicfunctiontransform($value):string{$this->logger->info('Transforming '.$value);// ...    }}

Autowiring will automatically callany method with the#[Required] attributeabove it, autowiring each argument. If you need to manually wire some of the argumentsto a method, you can always explicitlyconfigure the method call.

Despite property injection having somedrawbacks,autowiring with#[Required] can also be applied to publictyped properties:

123456789101112131415
namespaceApp\Util;useSymfony\Contracts\Service\Attribute\Required;classRot13Transformer{#[Required]public LoggerInterface$logger;publicfunctiontransform($value):void{$this->logger->info('Transforming '.$value);// ...    }}

Autowiring Anonymous Services Inline

7.1

The#[AutowireInline] attribute was added in Symfony 7.1.

Similar to how anonymous services can be defined inline in configuration files,theAutowireInlineattribute allows you to declare anonymous services inline, directly next to theircorresponding arguments:

12345678910111213
publicfunction__construct(#[AutowireInline(factory: [ScopingHttpClient::class,'forBaseUri'],arguments: ['$baseUri' =>'https://api.example.com','$defaultOptions' => ['auth_bearer' =>'%env(EXAMPLE_TOKEN)%',            ],        ]    )]private HttpClientInterface$client,){}

This example tells Symfony to inject an object created by calling theScopingHttpClient::forBaseUri() factory with the specified base URI anddefault options. This is just one example: you can use the#[AutowireInline]attribute to define any kind of anonymous service.

While this approach is convenient for simple service definitions, consider movingcomplex or heavily configured services to a configuration file to ease maintenance.

Autowiring Controller Action Methods

If you're using the Symfony Framework, you can also autowire arguments to your controlleraction methods. This is a special case for autowiring, which exists for convenience.SeeController for more details.

Performance Consequences

Thanks to Symfony's compiled container, there isno performance penalty for usingautowiring. However, there is a small performance penalty in thedev environment,as the container may be rebuilt more often as you modify classes. If rebuildingyour container is slow (possible on very large projects), you may not be able touse autowiring.

Public and Reusable Bundles

Public bundles should explicitly configure their services and not rely on autowiring.Autowiring depends on the services that are available in the container and bundles haveno control over the service container of applications they are included in. You can useautowiring when building reusable bundles within your company, as you have full controlover all code.

This work, including the code samples, is licensed under aCreative Commons BY-SA 3.0 license.
TOC
    Version

    Symfony 7.3backers


    [8]ページ先頭

    ©2009-2025 Movatter.jp