A modern PHP application is full of objects. One object may facilitate the
delivery of email messages while another may allow you to persist information
into a database. In your application, you may create an object that manages
your product inventory, or another object that processes data from a third-party
API. The point is that a modern application does many things and is organized
into many objects that handle each task.

This chapter is about a special PHP object in Symfony2 that helps
you instantiate, organize and retrieve the many objects of your application.
This object, called a service container, will allow you to standardize and
centralize the way objects are constructed in your application. The container
makes your life easier, is super fast, and emphasizes an architecture that
promotes reusable and decoupled code. Since all core Symfony2 classes
use the container, you'll learn how to extend, configure and use any object
in Symfony2. In large part, the service container is the biggest contributor
to the speed and extensibility of Symfony2.

Finally, configuring and using the service container is easy. By the end
of this chapter, you'll be comfortable creating your own objects via the
container and customizing objects from any third-party bundle. You'll begin
writing code that is more reusable, testable and decoupled, simply because
the service container makes writing good code so easy.

Put simply, a Service is any PHP object that performs some sort of
"global" task. It's a purposefully-generic name used in computer science
to describe an object that's created for a specific purpose (e.g. delivering
emails). Each service is used throughout your application whenever you need
the specific functionality it provides. You don't have to do anything special
to make a service: simply write a PHP class with some code that accomplishes
a specific task. Congratulations, you've just created a service!

Note

As a rule, a PHP object is a service if it is used globally in your
application. A single Mailer service is used globally to send
email messages whereas the many Message objects that it delivers
are not services. Similarly, a Product object is not a service,
but an object that persists Product objects to a database is a service.

So what's the big deal then? The advantage of thinking about "services" is
that you begin to think about separating each piece of functionality in your
application into a series of services. Since each service does just one job,
you can easily access each service and use its functionality wherever you
need it. Each service can also be more easily tested and configured since
it's separated from the other functionality in your application. This idea
is called service-oriented architecture and is not unique to Symfony2
or even PHP. Structuring your application around a set of independent service
classes is a well-known and trusted object-oriented best-practice. These skills
are key to being a good developer in almost any language.

This is easy enough. The imaginary Mailer class allows you to configure
the method used to deliver the email messages (e.g. sendmail, smtp, etc).
But what if you wanted to use the mailer service somewhere else? You certainly
don't want to repeat the mailer configuration every time you need to use
the Mailer object. What if you needed to change the transport from
sendmail to smtp everywhere in the application? You'd need to hunt
down every place you create a Mailer service and change it.

A better answer is to let the service container create the Mailer object
for you. In order for this to work, you must teach the container how to
create the Mailer service. This is done via configuration, which can
be specified in YAML, XML or PHP:

When Symfony2 initializes, it builds the service container using the
application configuration (app/config/config.yml by default). The
exact file that's loaded is dictated by the AppKernel::registerContainerConfiguration()
method, which loads an environment-specific configuration file (e.g.
config_dev.yml for the dev environment or config_prod.yml
for prod).

An instance of the Acme\HelloBundle\Mailer object is now available via
the service container. The container is available in any traditional Symfony2
controller where you can access the services of the container via the get()
shortcut method:

When you ask for the my_mailer service from the container, the container
constructs the object and returns it. This is another major advantage of
using the service container. Namely, a service is never constructed until
it's needed. If you define a service and never use it on a request, the service
is never created. This saves memory and increases the speed of your application.
This also means that there's very little or no performance hit for defining
lots of services. Services that are never used are never constructed.

As an added bonus, the Mailer service is only created once and the same
instance is returned each time you ask for the service. This is almost always
the behavior you'll need (it's more flexible and powerful), but you'll learn
later how you can configure a service that has multiple instances in the
"How to work with Scopes" cookbook article.

The end result is exactly the same as before - the difference is only in
how you defined the service. By surrounding the my_mailer.class and
my_mailer.transport strings in percent (%) signs, the container knows
to look for parameters with those names. When the container is built, it
looks up the value of each parameter and uses it in the service definition.

New in version 2.1: Escaping the @ character in YAML parameter values is new in Symfony 2.1.9
and Symfony 2.2.1.

Note

If you want to use a string that starts with an @ sign as a parameter
value (i.e. a very safe mailer password) in a yaml file, you need to escape
it by adding another @ sign (This only applies to the YAML format):

1
2
3
4

# app/config/parameters.ymlparameters:# This will be parsed as string "@securepass"mailer_password:"@@securepass"

Note

The percent sign inside a parameter or argument, as part of the string, must
be escaped with another percent sign:

The purpose of parameters is to feed information into services. Of course
there was nothing wrong with defining the service without using any parameters.
Parameters, however, have several advantages:

separation and organization of all service "options" under a single
parameters key;

parameter values can be used in multiple service definitions;

when creating a service in a bundle (this follows shortly), using parameters
allows the service to be easily customized in your application.

The choice of using or not using parameters is up to you. High-quality
third-party bundles will always use parameters as they make the service
stored in the container more configurable. For the services in your application,
however, you may not need the flexibility of parameters.

In this section, service configuration files are referred to as resources.
This is to highlight the fact that, while most configuration resources
will be files (e.g. YAML, XML, PHP), Symfony2 is so flexible that configuration
could be loaded from anywhere (e.g. a database or even via an external
web service).

The service container is built using a single configuration resource
(app/config/config.yml by default). All other service configuration
(including the core Symfony2 and third-party bundle configuration) must
be imported from inside this file in one way or another. This gives you absolute
flexibility over the services in your application.

External service configuration can be imported in two different ways. The
first - and most common method - is via the imports directive. Later, you'll
learn about the second method, which is the flexible and preferred method
for importing service configuration from third-party bundles.

So far, you've placed your my_mailer service container definition directly
in the application configuration file (e.g. app/config/config.yml). Of
course, since the Mailer class itself lives inside the AcmeHelloBundle,
it makes more sense to put the my_mailer container definition inside the
bundle as well.

The definition itself hasn't changed, only its location. Of course the service
container doesn't know about the new resource file. Fortunately, you can
easily import the resource file using the imports key in the application
configuration.

The imports directive allows your application to include service container
configuration resources from any other location (most commonly from bundles).
The resource location, for files, is the absolute path to the resource
file. The special @AcmeHello syntax resolves the directory path of
the AcmeHelloBundle bundle. This helps you specify the path to the resource
without worrying later if you move the AcmeHelloBundle to a different
directory.

When developing in Symfony2, you'll most commonly use the imports directive
to import container configuration from the bundles you've created specifically
for your application. Third-party bundle container configuration, including
Symfony2 core services, are usually loaded using another method that's more
flexible and easy to configure in your application.

Here's how it works. Internally, each bundle defines its services very much
like you've seen so far. Namely, a bundle uses one or more configuration
resource files (usually XML) to specify the parameters and services for that
bundle. However, instead of importing each of these resources directly from
your application configuration using the imports directive, you can simply
invoke a service container extension inside the bundle that does the work for
you. A service container extension is a PHP class created by the bundle author
to accomplish two things:

import all service container resources needed to configure the services for
the bundle;

provide semantic, straightforward configuration so that the bundle can
be configured without interacting with the flat parameters of the bundle's
service container configuration.

In other words, a service container extension configures the services for
a bundle on your behalf. And as you'll see in a moment, the extension provides
a sensible, high-level interface for configuring the bundle.

Take the FrameworkBundle - the core Symfony2 framework bundle - as an
example. The presence of the following code in your application configuration
invokes the service container extension inside the FrameworkBundle:

When the configuration is parsed, the container looks for an extension that
can handle the framework configuration directive. The extension in question,
which lives in the FrameworkBundle, is invoked and the service configuration
for the FrameworkBundle is loaded. If you remove the framework key
from your application configuration file entirely, the core Symfony2 services
won't be loaded. The point is that you're in control: the Symfony2 framework
doesn't contain any magic or perform any actions that you don't have control
over.

Of course you can do much more than simply "activate" the service container
extension of the FrameworkBundle. Each extension allows you to easily
customize the bundle, without worrying about how the internal services are
defined.

In this case, the extension allows you to customize the error_handler,
csrf_protection, router configuration and much more. Internally,
the FrameworkBundle uses the options specified here to define and configure
the services specific to it. The bundle takes care of creating all the necessary
parameters and services for the service container, while still allowing
much of the configuration to be easily customized. As an added bonus, most
service container extensions are also smart enough to perform validation -
notifying you of options that are missing or the wrong data type.

When installing or configuring a bundle, see the bundle's documentation for
how the services for the bundle should be installed and configured. The options
available for the core bundles can be found inside the Reference Guide.

Note

Natively, the service container only recognizes the parameters,
services, and imports directives. Any other directives
are handled by a service container extension.

So far, the original my_mailer service is simple: it takes just one argument
in its constructor, which is easily configurable. As you'll see, the real
power of the container is realized when you need to create a service that
depends on one or more other services in the container.

As an example, suppose you have a new service, NewsletterManager,
that helps to manage the preparation and delivery of an email message to
a collection of addresses. Of course the my_mailer service is already
really good at delivering email messages, so you'll use it inside NewsletterManager
to handle the actual delivery of the messages. This pretend class might look
something like this:

This approach is fine, but what if you decide later that the NewsletterManager
class needs a second or third constructor argument? What if you decide to
refactor your code and rename the class? In both cases, you'd need to find every
place where the NewsletterManager is instantiated and modify it. Of course,
the service container gives you a much more appealing option:

In YAML, the special @my_mailer syntax tells the container to look for
a service named my_mailer and to pass that object into the constructor
of NewsletterManager. In this case, however, the specified service my_mailer
must exist. If it does not, an exception will be thrown. You can mark your
dependencies as optional - this will be discussed in the next section.

Using references is a very powerful tool that allows you to create independent service
classes with well-defined dependencies. In this example, the newsletter_manager
service needs the my_mailer service in order to function. When you define
this dependency in the service container, the container takes care of all
the work of instantiating the objects.

Injecting dependencies into the constructor in this manner is an excellent
way of ensuring that the dependency is available to use. If you have optional
dependencies for a class, then "setter injection" may be a better option. This
means injecting the dependency using a method call rather than through the
constructor. The class would look like this:

Sometimes, one of your services may have an optional dependency, meaning
that the dependency is not required for your service to work properly. In
the example above, the my_mailer service must exist, otherwise an exception
will be thrown. By modifying the newsletter_manager service definition,
you can make this reference optional. The container will then inject it if
it exists and do nothing if it doesn't:

Since Symfony2 and all third-party bundles configure and retrieve their services
via the container, you can easily access them or even use them in your own
services. To keep things simple, Symfony2 by default does not require that
controllers be defined as services. Furthermore Symfony2 injects the entire
service container into your controller. For example, to handle the storage of
information on a user's session, Symfony2 provides a session service,
which you can access inside a standard controller as follows:

In Symfony2, you'll constantly use services provided by the Symfony core or
other third-party bundles to perform tasks such as rendering templates (templating),
sending emails (mailer), or accessing information on the request (request).

You can take this a step further by using these services inside services that
you've created for your application. Beginning by modifying the NewsletterManager
to use the real Symfony2 mailer service (instead of the pretend my_mailer).
Also pass the templating engine service to the NewsletterManager
so that it can generate the email content via a template:

The newsletter_manager service now has access to the core mailer
and templating services. This is a common way to create services specific
to your application that leverage the power of different services within
the framework.

Tip

Be sure that the swiftmailer entry appears in your application
configuration. As was mentioned in Importing Configuration via Container Extensions,
the swiftmailer key invokes the service extension from the
SwiftmailerBundle, which registers the mailer service.

In the same way that a blog post on the Web might be tagged with things such
as "Symfony" or "PHP", services configured in your container can also be
tagged. In the service container, a tag implies that the service is meant
to be used for a specific purpose. Take the following example:

The twig.extension tag is a special tag that the TwigBundle uses
during configuration. By giving the service this twig.extension tag,
the bundle knows that the foo.twig.extension service should be registered
as a Twig extension with Twig. In other words, Twig finds all services tagged
with twig.extension and automatically registers them as extensions.

Tags, then, are a way to tell Symfony2 or other third-party bundles that
your service should be registered or used in some special way by the bundle.

The following is a list of tags available with the core Symfony2 bundles.
Each of these has a different effect on your service and many tags require
additional arguments (beyond just the name parameter).