diff --git a/service_container.rst b/service_container.rst index 12e0705ae32..11ea370ee29 100644 --- a/service_container.rst +++ b/service_container.rst @@ -10,15 +10,10 @@ send emails while another object might help you save things to the database. Almost *everything* that your app "does" is actually done by one of these objects. And each time you install a new bundle, you get access to even more! -In Symfony, these useful objects are called **services** and each service lives inside -a very special object called the **service container**. If you have the service container, -then you can fetch a service by using that service's id:: - - $logger = $container->get('logger'); - $entityManager = $container->get('doctrine.orm.entity_manager'); - -The container allows you to centralize the way objects are constructed. It makes -your life easier, promotes a strong architecture and is super fast! +In Symfony, these useful objects are called **services** and each service lives +inside a very special object called the **service container**. The container +allows you to centralize the way objects are constructed. It makes your life +easier, promotes a strong architecture and is super fast! Fetching and using Services --------------------------- @@ -36,7 +31,7 @@ service's class or interface name. Want to :doc:`log ` something? No p /** * @Route("/products") */ - public function listAction(LoggerInterface $logger) + public function list(LoggerInterface $logger) { $logger->info('Look! I just used a service'); @@ -83,7 +78,7 @@ You can also use the unique "Service ID" to access a service directly:: /** * @Route("/products") */ - public function listAction() + public function list() { $logger = $this->container->get('logger'); $logger->info('Look! I just used a service'); @@ -146,7 +141,7 @@ inside your controller:: use App\Service\MessageGenerator; - public function newAction(MessageGenerator $messageGenerator) + public function new(MessageGenerator $messageGenerator) { // thanks to the type-hint, the container will instantiate a // new MessageGenerator and pass it to you! @@ -167,9 +162,7 @@ each time you ask for it. .. sidebar:: Automatic Service Loading in services.yaml - The documentation assumes you're using - `Symfony Standard Edition (version 3.3) services.yaml`_ configuration. The most - important part is this: + The documentation assumes you're using the following service configuration: .. configuration-block:: @@ -185,10 +178,10 @@ each time you ask for it. # makes classes in src/ available to be used as services App\: - resource: '../../src/*' + resource: '../src/*' # you can exclude directories or files # but if a service is unused, it's removed anyway - exclude: '../../src/{Entity,Repository}' + exclude: '../src/{Entity,Repository}' .. code-block:: xml @@ -204,7 +197,7 @@ each time you ask for it. - + @@ -223,7 +216,7 @@ each time you ask for it. ; // $this is a reference to the current loader - $this->registerClasses($definition, 'App\\', '../../src/*', '../../src/{Entity,Repository}'); + $this->registerClasses($definition, 'App\\', '../src/*', '../src/{Entity,Repository}'); .. tip:: @@ -237,15 +230,16 @@ each time you ask for it. If you'd prefer to manually wire your service, that's totally possible: see :ref:`services-explicitly-configure-wire-services`. -You can also fetch a service directly from the container via its "id", which will -be its class name in this case:: +If the :ref:`service is public `, you can also fetch it +directly from the container via its "id". However, this practice is discouraged +and you should instead inject services via constructors:: use App\Service\MessageGenerator; // accessing services like this only works if you extend Controller class ProductController extends Controller { - public function newAction() + public function new() { // only works if your service is public $messageGenerator = $this->get(MessageGenerator::class); @@ -256,8 +250,6 @@ be its class name in this case:: } } -However, this only works if you make your service :ref:`public `. - .. _services-constructor-injection: Injecting Services/Config into a Service @@ -368,7 +360,7 @@ you can use the service immediately:: use App\Updates\SiteUpdateManager; - public function newAction(SiteUpdateManager $siteUpdateManager) + public function new(SiteUpdateManager $siteUpdateManager) { // ... @@ -438,8 +430,8 @@ pass here. No problem! In your configuration, you can explicitly set this argume # same as before App\: - resource: '../../src/*' - exclude: '../../src/{Entity,Repository}' + resource: '../src/*' + exclude: '../src/{Entity,Repository}' # explicitly configure the service App\Updates\SiteUpdateManager: @@ -459,7 +451,7 @@ pass here. No problem! In your configuration, you can explicitly set this argume - + @@ -483,7 +475,7 @@ pass here. No problem! In your configuration, you can explicitly set this argume ->setPublic(false) ; - $this->registerClasses($definition, 'App\\', '../../src/*', '../../src/{Entity,Repository}'); + $this->registerClasses($definition, 'App\\', '../src/*', '../src/{Entity,Repository}'); // Explicitly configure the service $container->getDefinition(SiteUpdateManager::class) @@ -553,9 +545,9 @@ and reference it with the ``%parameter_name%`` syntax: // ... ->setArgument('$adminEmail', '%admin_email%'); -Actually, once you define a parameter, it can be referenced via the ``%parameter_name%`` -syntax in *any* other service configuration file - like ``config.yml``. Many parameters -are defined in a :ref:`parameters.yml file `. +Actually, once you define a parameter, it can be referenced via the +``%parameter_name%`` syntax in *any* other configuration file. Many parameters +are defined in the ``config/services.yaml`` file. You can then fetch the parameter in the service:: @@ -573,11 +565,11 @@ You can then fetch the parameter in the service:: You can also fetch parameters directly from the container:: - public function newAction() + public function new() { // ... - // this ONLY works if you extend Controller + // this ONLY works if you extend the base Controller $adminEmail = $this->container->getParameter('admin_email'); // or a shorter way! @@ -741,7 +733,7 @@ as a service, and :doc:`tag ` it with ``twig.extension` ->addTag('twig.extension'); But, with ``autoconfigure: true``, you don't need the tag. In fact, if you're using -the :ref:`Symfony Standard Edition services.yaml config `, +the :ref:`default services.yaml config `, you don't need to do *anything*: the service will be automatically loaded. Then, ``autoconfigure`` will add the ``twig.extension`` tag *for* you, because your class implements ``Twig_ExtensionInterface``. And thanks to ``autowire``, you can even add @@ -789,7 +781,7 @@ from the container:: use App\Service\MessageGenerator; - public function newAction(MessageGenerator $messageGenerator) + public function new(MessageGenerator $messageGenerator) { // type-hinting it as an argument DOES work @@ -797,7 +789,7 @@ from the container:: $this->container->get(MessageGenerator::class); } -Usually, this is ok: there are better ways to access a service. But, if you *do* +Usually, this is OK: there are better ways to access a service. But, if you *do* need to make your service public, just override this setting: .. configuration-block:: @@ -848,13 +840,13 @@ key. For example, the default Symfony configuration contains this: # the namespace prefix for classes (must end in \) App\: # create services for all the classes found in this directory... - resource: '../../src/*' + resource: '../src/*' # ...except for the classes located in these directories - exclude: '../../src/{Entity,Repository}' + exclude: '../src/{Entity,Repository}' # these were imported above, but we want to add some extra config App\Controller\: - resource: '../../src/Controller' + resource: '../src/Controller' # apply some configuration to these services public: true tags: ['controller.service_arguments'] @@ -871,9 +863,9 @@ key. For example, the default Symfony configuration contains this: - + - + @@ -893,7 +885,7 @@ key. For example, the default Symfony configuration contains this: ->setPublic(false) ; - $this->registerClasses($definition, 'App\\', '../../src/*', '../../src/{Entity,Repository}'); + $this->registerClasses($definition, 'App\\', '../src/*', '../src/{Entity,Repository}'); // Changes default config $definition @@ -902,7 +894,7 @@ key. For example, the default Symfony configuration contains this: ; // $this is a reference to the current loader - $this->registerClasses($definition, 'App\\Controller\\', '../../src/Controller/*'); + $this->registerClasses($definition, 'App\\Controller\\', '../src/Controller/*'); .. tip:: diff --git a/service_container/3.3-di-changes.rst b/service_container/3.3-di-changes.rst index ba4445ad42c..2cc1afec728 100644 --- a/service_container/3.3-di-changes.rst +++ b/service_container/3.3-di-changes.rst @@ -49,15 +49,15 @@ Symfony Standard Edition: # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name App\: - resource: '../../src/*' + resource: '../src/*' # you can exclude directories or files # but if a service is unused, it's removed anyway - exclude: '../../src/{Entity,Repository}' + exclude: '../src/{Entity,Repository}' # controllers are imported separately to make sure they're public # and have a tag that allows actions to type-hint services App\Controller\: - resource: '../../src/Controller' + resource: '../src/Controller' tags: ['controller.service_arguments'] # add more services, or override services that need manual wiring @@ -77,9 +77,9 @@ Symfony Standard Edition: - + - + @@ -101,7 +101,7 @@ Symfony Standard Edition: ->setPublic(false) ; - $this->registerClasses($definition, 'App\\', '../../src/*', '../../src/{Entity,Repository}'); + $this->registerClasses($definition, 'App\\', '../src/*', '../src/{Entity,Repository}'); // Changes default config $definition @@ -109,7 +109,7 @@ Symfony Standard Edition: ; // $this is a reference to the current loader - $this->registerClasses($definition, 'App\\Controller\\', '../../src/Controller/*'); + $this->registerClasses($definition, 'App\\Controller\\', '../src/Controller/*'); // add more services, or override services that need manual wiring @@ -139,10 +139,10 @@ thanks to the following config: # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name App\: - resource: '../../src/*' + resource: '../src/*' # you can exclude directories or files # but if a service is unused, it's removed anyway - exclude: '../../src/{Entity,Repository}' + exclude: '../src/{Entity,Repository}' .. code-block:: xml @@ -156,7 +156,7 @@ thanks to the following config: - + @@ -174,7 +174,7 @@ thanks to the following config: ->setPublic(false) ; - $this->registerClasses($definition, 'App\\', '../../src/*', '../../src/{Entity,Repository}'); + $this->registerClasses($definition, 'App\\', '../src/*', '../src/{Entity,Repository}'); This means that every class in ``src/`` is *available* to be used as a service. And thanks to the ``_defaults`` section at the top of the file, all of @@ -345,7 +345,7 @@ The third big change is that, in a new Symfony 3.3 project, your controllers are # controllers are imported separately to make sure they're public # and have a tag that allows actions to type-hint services App\Controller\: - resource: '../../src/Controller' + resource: '../src/Controller' tags: ['controller.service_arguments'] .. code-block:: xml @@ -360,7 +360,7 @@ The third big change is that, in a new Symfony 3.3 project, your controllers are - + @@ -372,7 +372,7 @@ The third big change is that, in a new Symfony 3.3 project, your controllers are // ... - $this->registerClasses($definition, 'App\\Controller\\', '../../src/Controller/*'); + $this->registerClasses($definition, 'App\\Controller\\', '../src/Controller/*'); But, you might not even notice this. First, your controllers *can* still extend the same base ``Controller`` class or a new :ref:`AbstractController `. @@ -720,11 +720,11 @@ You're now ready to automatically register all services in ``src/`` # ... + App\: - + resource: '../../src/*' - + exclude: '../../src/{Entity,Repository}' + + resource: '../src/*' + + exclude: '../src/{Entity,Repository}' + + App\Controller\: - + resource: '../../src/Controller' + + resource: '../src/Controller' + tags: ['controller.service_arguments'] # ... @@ -809,11 +809,11 @@ can be autowired. The final configuration looks like this: public: false App\: - resource: '../../src/*' - exclude: '../../src/{Entity,Repository}' + resource: '../src/*' + exclude: '../src/{Entity,Repository}' App\Controller\: - resource: '../../src/Controller' + resource: '../src/Controller' tags: ['controller.service_arguments'] App\Service\GitHubNotifier: diff --git a/service_container/alias_private.rst b/service_container/alias_private.rst index b6bd077fa30..b57099960f9 100644 --- a/service_container/alias_private.rst +++ b/service_container/alias_private.rst @@ -32,6 +32,7 @@ You can also control the ``public`` option on a service-by-service basis: .. code-block:: yaml + # config/services.yaml services: # ... @@ -40,6 +41,7 @@ You can also control the ``public`` option on a service-by-service basis: .. code-block:: xml + register(Foo::class) @@ -80,11 +83,6 @@ to access it directly from your code. However, if a service has been marked as private, you can still alias it (see below) to access this service (via the alias). -.. note:: - - Services are by default public, but it's considered a good practice to mark - as many services private as possible. - .. _services-alias: Aliasing @@ -98,6 +96,7 @@ services. .. code-block:: yaml + # config/services.yaml services: # ... App\Mail\PhpMailer: @@ -109,6 +108,7 @@ services. .. code-block:: xml + register(PhpMailer::class) @@ -142,6 +143,7 @@ This means that when using the container directly, you can access the .. code-block:: yaml + # config/services.yaml services: # ... app.mailer: '@App\Mail\PhpMailer' @@ -156,11 +158,13 @@ or you decided not to maintain it anymore), you can deprecate its definition: .. code-block:: yaml - App\Service\OldService: - deprecated: The "%service_id%" service is deprecated since 2.8 and will be removed in 3.0. + # config/services.yaml + App\Service\OldService: + deprecated: The "%service_id%" service is deprecated since 2.8 and will be removed in 3.0. .. code-block:: xml + `. Suppose that for some reason, the id of the service was instead ``app.rot13.transformer``. In this case, any arguments type-hinted with the class name (``App\Util\Rot13Transformer``) -can no longer be autowired (actually, it :ref:`will work now, but not in Symfony 4.0 `). +can no longer be autowired. No problem! To fix this, you can *create* a service whose id matches the class by adding a service alias: @@ -212,6 +215,7 @@ adding a service alias: .. code-block:: yaml + # config/services.yaml services: # ... @@ -227,6 +231,7 @@ adding a service alias: .. code-block:: xml + `). +argument can no longer be autowired. To fix that, add an :ref:`alias `: @@ -310,6 +315,7 @@ To fix that, add an :ref:`alias `: .. code-block:: yaml + # config/services.yaml services: # ... @@ -321,6 +327,7 @@ To fix that, add an :ref:`alias `: .. code-block:: xml + `: .. code-block:: php + // config/services.php use App\Util\Rot13Transformer; use App\Util\TransformerInterface; @@ -375,6 +383,7 @@ that alias: .. code-block:: yaml + # config/services.yaml services: # ... @@ -396,6 +405,7 @@ that alias: .. code-block:: xml + `, -you need to register them in the ``build()`` method of the bundle class (this -is not needed when implementing the ``process()`` method in the extension):: +Compiler passes are registered in the ``build()`` method of the application kernel:: - // src/AppBundle.php - namespace AppBundle; + // src/Kernel.php + namespace App; + + use App\DependencyInjection\Compiler\CustomPass; + use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; + use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + use Symfony\Component\DependencyInjection\ContainerBuilder; + use Symfony\Component\HttpKernel\Kernel as BaseKernel; + + class Kernel extends BaseKernel implements CompilerPassInterface + { + use MicroKernelTrait; + + // ... + + protected function build(ContainerBuilder $container): void + { + $container->addCompilerPass(new CustomPass()); + } + } + +One of the most common use-cases of compiler passes is to work with :doc:`tagged +services `. In those cases, instead of creating a +compiler pass, you can make the kernel implement +:class:`Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface` +and process the services inside the ``process()`` method:: + + // src/Kernel.php + namespace App; + + use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; + use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + use Symfony\Component\DependencyInjection\ContainerBuilder; + use Symfony\Component\HttpKernel\Kernel as BaseKernel; + + class Kernel extends BaseKernel implements CompilerPassInterface + { + use MicroKernelTrait; + + // ... + + public function process(ContainerBuilder $container) + { + // in this method you can manipulate the service container: + // for example, changing some container service: + $container->getDefinition('app.some_private_service')->setPublic(true); + + // or processing tagged services: + foreach ($container->findTaggedServiceIds('some_tag') as $id => $tags) { + // ... + } + } + } + +Working with Compiler Passes in Bundles +--------------------------------------- + +`Bundles `_ can define compiler passes in the ``build()`` method of +the main bundle class (this is not needed when implementing the ``process()`` +method in the extension):: + + // src/MyBundle/MyBundle.php + namespace App\MyBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use App\DependencyInjection\Compiler\CustomPass; - class AppBundle extends Bundle + class MyBundle extends Bundle { public function build(ContainerBuilder $container) { @@ -31,10 +90,8 @@ is not needed when implementing the ``process()`` method in the extension):: } } -One of the most common use-cases of compiler passes is to work with tagged services -(read more about tags in ":doc:`/service_container/tags`"). If you are using -custom tags in a bundle then by convention, tag names consist of the name of -the bundle (lowercase, underscores as separators), followed by a dot, and -finally the "real" name. For example, if you want to introduce some sort of -"transport" tag in your AcmeMailerBundle, you should call it -``acme_mailer.transport``. +If you are using custom service tags in a bundle then by convention, tag names +consist of the name of the bundle (lowercase, underscores as separators), +followed by a dot, and finally the "real" name. For example, if you want to +introduce some sort of "transport" tag in your AcmeMailerBundle, you should call +it ``acme_mailer.transport``. diff --git a/service_container/configurators.rst b/service_container/configurators.rst index 20ceaeed18c..7f2817e0a56 100644 --- a/service_container/configurators.rst +++ b/service_container/configurators.rst @@ -131,7 +131,7 @@ all the classes are already loaded as services. All you need to do is specify th # Registers all 4 classes as services, including App\Mail\EmailConfigurator App\: - resource: '../../src/*' + resource: '../src/*' # ... # override the services to set the configurator @@ -151,7 +151,7 @@ all the classes are already loaded as services. All you need to do is specify th http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -176,7 +176,7 @@ all the classes are already loaded as services. All you need to do is specify th $definition->setAutowired(true); - $this->registerClasses($definition, 'App\\', '../../src/*'); + $this->registerClasses($definition, 'App\\', '../src/*'); $container->getDefinition(NewsletterManager::class) ->setConfigurator(array(new Reference(EmailConfigurator::class), 'configure')); diff --git a/service_container/expression_language.rst b/service_container/expression_language.rst index c6878e413e9..cbab6c2ffde 100644 --- a/service_container/expression_language.rst +++ b/service_container/expression_language.rst @@ -78,12 +78,14 @@ via a ``container`` variable. Here's another example: .. code-block:: yaml + # config/services.yaml services: App\Mailer: arguments: ["@=container.hasParameter('some_param') ? parameter('some_param') : 'default_value'"] .. code-block:: xml + - - - + `. Internally, each bundle defines its services in files like you've seen so far. However, these files aren't imported using the ``import`` directive. Instead, bundles @@ -123,10 +121,6 @@ use a *dependency injection extension* to load the files automatically. As soon as you enable a bundle, its extension is called, which is able to load service configuration files. -In fact, each configuration block in ``config.yml`` - e.g. ``framework`` or ``twig``- -is passed to the extension for that bundle - e.g. ``FrameworkBundle`` or ``TwigBundle`` - +In fact, each configuration file in ``config/packages/`` is passed to the +extension of its related bundle - e.g. ``FrameworkBundle`` or ``TwigBundle`` - and used to configure those services further. - -If you want to use dependency injection extensions in your own shared -bundles and provide user friendly configuration, take a look at the -:doc:`/bundles/extension` article. diff --git a/service_container/injection_types.rst b/service_container/injection_types.rst index e5c7fe78dbf..60478f66a5f 100644 --- a/service_container/injection_types.rst +++ b/service_container/injection_types.rst @@ -41,7 +41,8 @@ service container configuration: .. code-block:: yaml - services: + # config/services.yaml + services: # ... App\Mail\NewsletterManager: @@ -49,6 +50,7 @@ service container configuration: .. code-block:: xml + register(AppExtension::class) diff --git a/service_container/parameters.rst b/service_container/parameters.rst index 224684e84b9..046076a5fe5 100644 --- a/service_container/parameters.rst +++ b/service_container/parameters.rst @@ -17,11 +17,13 @@ Use the ``parameters`` section of a config file to set parameters: .. code-block:: yaml + # config/services.yaml parameters: mailer.transport: sendmail .. code-block:: xml + setParameter('mailer.transport', 'sendmail'); You can refer to parameters elsewhere in any config file by surrounding them @@ -50,6 +53,7 @@ and hidden with the service definition: .. code-block:: yaml + # config/services.yaml parameters: mailer.transport: sendmail @@ -59,6 +63,7 @@ and hidden with the service definition: .. code-block:: xml +