Below you will find pages that utilize the taxonomy term “Service Container”
Hand-written service containers
You say "convention over configuration;" I hear "ambient information stuck in someone's head." You say "configuration over hardcoding;" I hear "information in a different language that must be parsed, can be malformed, or not exist."
— Paul Snively (@paul_snively) March 2, 2019
Dependency injection is very important. Dependency injection containers are too. The trouble is with the tools, that let us define services in a meta-language, and rely on conventions to work well. This extra layer requires the “ambient information” Paul speaks about in his tweet, and easily lets us make mistakes that we wouldn’t make if we’d just write out the code for instantiating our services.
Decoupling from a service locator
Decoupling from a service locator - shouldn’t that be: don’t use a service locator?
Well, not really, since there are lots of valid use cases for using a service locator. The main use case is for making things lazy-loading (yes, you can also use some kind of proxy mechanism for that, but let’s assume you need something simpler). Say we have this EventDispatcher
class:
class EventDispatcher
{
public function __construct(array $listeners)
{
$this->listeners = $listeners;
}
public function dispatch($event)
{
foreach ($this->listeners[$event] as $listener) {
$listener->notify();
}
}
}
Now it appears that some event listeners are rather expensive to instantiate. And, even though it may never be notified (because it listens to a rare event), in order to register any event listener, we need to instantiate it:
Backwards compatible bundle releases
The problem
With a new bundle release you may want to rename services or parameters, make a service private, change some constructor arguments, change the structure of the bundle configuration, etc. Some of these changes may acually be backwards incompatible changes for the users of that bundle. Luckily, the Symfony DependenyInjection component and Config component both provide you with some options to prevent such backwards compatibility (BC) breaks. If you want to know more about backwards compatibility and bundle versioning, please read my previous article on this subject.
Symfony2: Some things I don't like about Bundles
This article could have been titled “Ten things I hate about bundles”, but that would be too harsh. The things I am going to describe, are not things I hate, but things I don’t like. Besides, when I count them, I don’t come to ten things…
Bundle extension discovery
A Symfony2 bundle can have an Extension
class, which allows you to load service definitions from configuration files or define services for the application in a more dynamic way. From the Symfony documentation:
Symfony2: Console Commands as Services - Why?
As you may have read on the Symfony blog: as of Symfony 2.4 you can register console commands using the service tag console.command
.
What is the big deal, you would say. Well, let’s discover it now. This is how the documentation tells you to write your console commands:
namespace Matthias\ConsoleBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class MyCommand extends ContainerAwareCommand
{
protected function configure()
{
$this->setName('my:action');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// do something
}
}
How to make sure your command will be noticed?
Until Symfony 2.4 there were two requirements for your command to be picked up by the console application itself:
PHPUnit & Pimple: Integration Tests with a Simple DI Container
Unit tests are tests on a micro-scale. When unit testing, you are testing little units of your code to make sure that, given a certain input, they produce the output you expected. When your unit of code makes calls to other objects, you can “mock” or “stub” these objects and verify that a method is called a specific number of times, or to make sure the unit of code you’re testing will receive the correct data from the other objects.
Dependency injection smells
The Symfony2 DependencyInjection Component has made my life and work as a developer a lot easier. Choosing the right way to use it however can be a bit difficult sometimes. Knowing what a/the service container can do, helps a lot, and also thinking about how you would do it with just PHP can put you back on track. To be able to recognize some problems related to dependency injection in your own code, I will describe a few “dependency injection smells” below (a term derived from “code smells”, used by Kent Beck, Martin Fowler and the likes).
Symfony2 Security: Using advanced Request matchers to activate firewalls
In the Symfony2 security documentation both the firewalls and the access control rules are demonstrated using the “path” option, which is used to determine if a firewall or rule is applicable to the current URL. Also the “ip” option is demonstrated. The fact of the matter is, the string based configuration options in security.yml
are transformed into objects of class RequestMatcher. This is a curious class in the HttpFoundation component which allows you to match a given Request object. The Security component uses it to determine if it should activate a certain firewall for the current request (usually only by checking the request’s path info).
Symfony2 & JMSSerializerBundle: Vendor MIME types and API versioning
The JMSSerializerBundle has a VersionExclusionStrategy
, which allows you to serialize/deserialize objects for a specific version of your API. You can mark the properties that are available for different versions using the @Since
and @Until
annotations:
use JMS\SerializerBundle\Annotation\Type;
use JMS\SerializerBundle\Annotation\Since;
use JMS\SerializerBundle\Annotation\Until;
class Comment
{
/**
* @Type("DateTime")
* @Since("1.2.0")
*/
private $createdAt;
/**
* @Type("DateTime")
* @Until("2.1.3")
*/
private $updatedAt;
}
The only thing you have to do is tell the serializer which version to use, before you start using it:
Symfony2: Creating a Validator with dependencies? Make it a service!
One of the ugly things about Symfony 1 validators was that their dependencies were generally fetched from far away, mainly by calling sfContext::getInstance()
. With Symfony2 and it’s service container, this isn’t necessary anymore. Validators can be services. The example below is quite simple, but given you can inject anything you want, you can make it quite complicated. I show you how to create a validator that checks with the router of the value-under-validation is a route.
Symfony2: define your bundle's configuration values using the TreeBuilder
After reading the Cookbook article How to expose a Semantic Configuration for a Bundle I became interested in the possibilities for defining a configuration tree using the TreeBuilder
from the Symfony component Config
. This
TreeBuilderallows you to build a schema against which the configuration values (or short: config) from
/app/config.yml` and the like are to be validated.
Create an Extension
First make sure that your bundle has a /DependencyInjection/[BundleNameWithoutBundle]Extension.php
containing the extension class:
Symfony2: create a response filter and set extra response headers
Sometimes you need to make changes to the Response object, after it is returned by your controller, but before it is rendered as output to the client (e.g. the browser). You may want to set some extra response headers, or “completely mess up the content” of the response. You can accomplish this by creating an event listener that listens to the kernel.response
event. I give you a sample event listener which changes the Content-Type
header in case the requested format is “json” and the browser’s accepted response format contains “text/html”; in that case, at least in my experience, the browser doesn’t render the JSON string as plain text when the status code is 4xx or 5xx. So in these situations, the event listener changes the “Content-Type” to “text/plain”, to be sure you always get decent output in the browser.
Symfony2 service container: how to make your service use tags
First of all: dependency injection is GREAT!
Several of Symfony2’s core services depend on tags to recognize which user-defined services should be loaded, notified of events, etc. For example, Twig uses the tag twig.extension
to load extra extensions.
It would also be great to use tags in case your service implements some kind of “chain”, in which several alternative strategies are tried until one of them is successful. In this post I use the example of a so-called “TransportChain”. This chain consists of a set of classes which implement the Swift_Transport
interface. Using the chain, the mailer may try several ways of transport, until one succeeds. This post focuses on the “dependency injection” part of the story. The implementation of the real TransportChain
is left to the reader (as an exercise ;)).