The DependencyInjection component allows you to standardize and centralize the way objects are constructed in your PHP applications. This component is used to create the service container, which is the biggest contributor to the extensibility of the Symfony framework. Symfony 2.7 improves this component with new features but it also removes some of its current features.
Added an auto_alias
compiler pass
This new feature emerged from the needs of the Drupal project. They define a series of related services like the following:
1 2 3 4 5 6
lock:
class: Drupal\Core\Lock\Lock...
mysql.lock:
class: Drupal\Core\Lock\MysqlLock...
sqlite.lock:
class: Drupal\Core\Lock\SqliteLock...
When the site administrator sets the default_backend
configuration option
to mysql
, the corresponding mysql.lock
service should be automatically
aliased to the generic lock
service.
In Symfony 2.7 a new auto_alias
compiler pass has been added to allow services
to define aliases automatically based on the value of container parameters. You
just need to tag your service with the new auto_alias
tag and define the format
of the alias (which can include any container parameter):
1 2 3 4
# app/config/services.yml
lock:
tags:
- { name: auto_alias, format: "%default_backend%.lock" }
Improved service inlining when using the XML dumper
In order to increase the application performance, the service container
inlines services which are injected just once in the application if they
are marked as private. When the cache gets built for an application's service container,
the container itself is also dumped as an XML file (see appDevDebugProjectContainer.xml
file in your application cache, for example).
However, this dump process failed in two cases: when you used a service configurator and when defining a private factory using the new factory syntax introduced in Symfony 2.6. These problems are now fixed in Symfony 2.7 and the XML dumper safely inlines any appropriate service.
Improved YAML service definition syntax
YAML is one of the three configuration formats that Symfony applications can use to define their services. In Symfony 2.6 and previous versions, a complex service definition using YAML format looked as follows:
1 2 3 4 5 6 7 8 9 10
# app/config/services.yml
services:
manager:
class: AppBundle\Manager\UserManager
arguments: [true]
calls:
- [setLogger, ["@logger"]]
- [setClass, ["User"]]
tags:
- { name: twig.extension, alias: user }
In Symfony 2.7, the YAML configuration syntax has been improved to allow using a more verbose and expressive service definition (you can of course keep using the previous concise format if you prefer it):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# app/config/services.yml
services:
manager:
class: AppBundle\Manager\UserManager
arguments:
- true
calls:
- method: setLogger
arguments:
- "@logger"
- method: setClass
arguments:
- User
tags:
- name: manager
alias: user
Deprecated synchronized services
In Symfony's service container, a service cannot depend on services from a narrower
scope. For example, if you create a service and try to inject the request
service,
you will see a ScopeWideningInjectionException
.
Symfony 2.3 introduced the concept of synchronized services as a way to solve
this problem. That's why the request
service is defined as synchronized
:
1 2 3 4 5
services:
request:
scope: request
synchronized: true
# ...
Then in your service you can use setter injection to safely use this kind of synchronized services:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
namespace AppBundle\Mail;
use Symfony\Component\HttpFoundation\Request;
class Mailer
{
protected $request;
public function setRequest(Request $request = null)
{
$this->request = $request;
}
// ...
}
1 2 3 4 5 6
# app/config/services.yml
services:
mailer:
class: AppBundle\Mail\Mailer
calls:
- [setRequest, ["@?request"]]
However, the real problem is that the request is not a service but a value object.
In Symfony 3.0, we will remove the request
service from the container to fix
the problem once and for all.
In addition, we have deprecated synchronized services in Symfony 2.7 because
this feature is quite complex for something which is not really needed and was
the wrong way to solve our problems. If your service needs the request
service,
use the request_stack
service instead.
There is a typo in the first examaple, it's "mysql.lock" not "myqsl.lock".
@Róbert thanks for reporting this typo. It's fixed now.
if a service is an alias to another service, why do we need to set the class parameter?
In this phrase there is a typo: 'user' should be 'use': "If your service needs the request service, user the request_stack service instead."
@Tito when using the auto_alias tag, the aliased service may not exist (keep in ind that you are creating the alias with a variable value, so this can definitely happen). In that case, the service will be a regular service that will use the configured class.
@Xavier good catch! It's fixed now. Thanks.
@Javier, interesting use case, the class parameter acts like a fallback then.. thanks for the explanation
Cool for auto_alias, thanks ! However, I'm a bit annoyed for synchronized services. They are used quite heavily in eZ for different things than request...