 Okay. Hi, everyone. Welcome to this session. So today I'm going to talk about dependence injection. Dependence injection is a design pattern. It's a very, very simple concept to understand, but it's very hard to explain, probably because you need to use it a lot to really understand the benefits of using dependence injection. So the first part of the talk is going to be about dependence injection, so that everyone really understands what is it about. And the second part of the talk is more about the dependence injection container that you get when you are using symphony or when you will be using Drupal 8. So first, when I'm talking about a container, that's something I'm going to introduce later on in this session, but the very first part is really about just what is dependence injection. The first thing is that dependence injection is a design pattern that you can use when you are doing object-oriented programming. So you can't use dependence injection if you are just using functions. So you need some classes. So to make things clear, hopefully, I'm going to talk about this user class. When you are managing a web application, you need a user at some point. So a user is something that you can implement to store some user preferences, like its language, for instance. And because the HTTP protocol is stateless, you need to also have a way to store those preferences, so you are the language of the user from one request to the next one. So we need two different objects, one to be able to represent the user preferences. This is this class, and the other one, which would be a way to store all the preferences from one request to the next one. So using this user class is pretty easy. You create a user, and you set a language, for instance. So to be able to store this information between HTTP requests, we can use PHP sessions. So here is the session storage. So this is a class that you can use to actually store things in a session, a very simple one. It takes one argument for the constructor, the cookie name with a default value. It starts the session when you create a session storage object, and then you can set things on the storage. And, of course, there is probably a get method to be able to get back things from the storage. So now, I have this problem. I have two different objects, two different responsibilities. One is about managing the user preferences, and the other one is about storing things into a session. Totally different, but the user needs a session storage to be able to work properly when used in a web application. So here is how I can use both objects into one convenient package. So here in the constructor of the user class, I create a session storage. And whenever I set the language, instead of storing it locally into the user property, I set it in the storage. From the user perspective, this is exactly the same as before. If you want to use the user, you create a new user, you set the language. Everything happens behind the scenes. So you are not aware that this user object is actually using a session storage. So it's very easy to use, but it's very hard to customize. So another way would be to actually inject the dependency into the user object. So instead of creating the storage object in the user constructor, here you can see that the storage is injected into the constructor. Now, if you have a look at the code, it's very easy to customize because I can inject any kind of storage. I don't care about which one you are going to inject, but it's slightly more difficult to use. Now, if I want to create a user, I need to understand that first I need to create a session storage, and then I need to inject the session storage into the user object before being able to set the language or get the language, whatever. So from a design perspective, it's much better to use this solution, but from a user perspective, the developer who is going to use your user object, it's slightly more difficult to use because now you need to be aware of all the dependencies that you need to be able to create a user object. This slide is dependency injection. So that's dependency injection. Nothing more. Very easy to understand. Very easy to actually use, if you want. The biggest problem being that it's a bit more difficult to use, and that's the biggest problem, and that's why at some point people don't really use dependency injection because it makes things harder for the end user. So again, the difference between the two different slides, the two different user classes is subtle, but very important. In one case, I create the storage object within the constructor when using the dependency injection pattern, I inject it into the constructor instead. So this is a design pattern. This is a good practice, but just understanding that this is a good practice is not enough. Why? Why is it really something that is useful and why it is much better to actually use dependency injection? I want to show you why this is really important so that you can see the benefits. So the first thing I want to do, so I'm going to take the first example, trying to figure out how I can customize the session storage without using dependency injection. So the first thing I want to be able to do is to change the session cookie name. Remember that a session storage class takes a session name, so this is the cookie name, really, as a first argument. So here, instead of using the default one, I want to name it session, right? So this is one way to do that. Can you see a problem here? Why is this not a very good idea? Yeah. So, yeah. So basically, it's very hard to customize that from the outside of the user class. So if I want to be able to configure the session name from the outside, so creating a configuration file or storing the name in a variable outside of the user object, that's not possible, so everything is outcoded. So the way you can configure the session storage is outcoded into the user class. That's not really flexible enough. So one way to fix that would be to create a constant, right? So a bit different from a variable, but that works. So now I can say, okay, there is this constant, and this is the session name, and now I can change the session name if I want from the outside of the user class. That's one way to do that, but this is a global constant. So if I want to be able to test my user class with different storage strategies with different cookie names, I can't. Wait, this is a constant, so you can't change the value anymore. So instead of doing that, you can inject the session name into the constructor of the user class. So here I pass the session string as an argument to the constructor class. That works. But what if I need to pass more variables? What if I have more things to configure on the session storage object? It's going to add more arguments to the constructor. And what if the user class itself has some arguments? I'm going to mix and match the arguments for the user class and the ones for the session storage class. So it's possible, but it's not optimal. Okay, so instead of passing all the arguments one after the other, you can also create an array. So here I've created an array, and this array is about all the options that you can pass to the session storage. It's a bit better because there is only one argument used to pass the session storage constructor arguments. But still, it's not good enough, right? The user class should not be aware of how you want to configure the session storage, right? I want the objects to be decoupled. I don't want the user class to know anything about the session storage. Okay, and to be able to fix all those problems, you can just use dependency injection. So the right way is to create the object first, the session storage object first. You configure it in any way. It's really up to you, and then you inject it into the user class. So it solves all the problems, except that it's a bit more complex to use. Okay, yeah. Okay, so now I want to talk about some other solutions that work, Canada, but are less interesting than dependency injection. So instead of using dependency injection, you can create a registered class. So you have this global object where you store all the dependencies, right? So you create your session storage. You set it into the registry, and then from the user constructor, you get it from the registry, which means that from the user perspective, you can just create the user and use it as before. You don't need to be aware that the user is actually depending on the session storage. It's bad for many reasons, but the main one is you are adding yet another layer for nothing. Just using dependency injection is much better. So it doesn't solve anything. This is not the right solution. It's more complex for no added benefit. So using dependency injection is much better in this case. So dependency injection is something that is really easy to understand. It solves many problems. And yeah, there is no need to actually reinvent the wheel, and the registry pattern is not one way to solve this specific problem. Okay, so if you are using dependency injection, everything becomes natural, very easy. If I want to change the session storage, let's say I want to store my session in memcached, for instance. I can just swap the dependency, create a memcached session storage, and then inject it into the user. It doesn't care about which kind of session storage you are actually injecting. For testing, you can create an array session storage, probably a mock, very easy to do. Of course, the next stage would be to create an interface. So instead of injecting any kind of object, you create a session storage interface. There is a simple one with a get method, a set method. So this is a contract between the user class and the session storage. So now, if I have a look at the constructor of the user class, you can see that I've added the session storage interface int. So this is the contract. So it means that now you can inject any class implementing this interface. So any class with a get method and a set method will work. And of course, the big benefit of using interfaces means that if you are using PHP Unit, for instance, you can get a mock. So instead of creating a mock yourself, you can ask PHP Unit to create one for you. So if you don't want to actually create a session, and if you want to unit test this user class, you don't have any session. You don't have any HTTP request. So you can ask PHP Unit to create a mock for you, and then you can easily test your user class without any dependency on a session storage, everyone. So it helps building clean code, really. Your code is going to be reusable, right? So the session storage is reusable without the user class, and the user class is reusable without using always the same session storage. Testable because you can rely on interfaces, plugable so you can change the dependency with whatever you want, and loosely coupled. I have one user object, its single responsibility is to actually manage the user preferences, and then there is the session storage. Its only responsibility is to manage the storage of things. So it's all about the separation of concerns. This is a definition of dependency injection. It comes from a Java library, actually. So dependency injection is where components, a component being an object, really, are given their dependency through their constructor. That's what I've shown you, but also methods are directly into fields properties. So there is three different ways to actually inject dependencies into your object. The first one is the constructor injection. This is probably the most useful injection, but you can also use some setters, or directly into properties. I don't have the last one, so that's probably not really a good practice in PHP. I really like the constructor argument injection because your objects are usable right away. So when you create a new user object, you need to inject a storage so you can use the user object right away. If you are using setter injection, you need to remember to actually call a setter before being able to use the object. So the setter injection is interesting for optional dependencies. So if, for instance, you want to log when a user changes his preferences, for instance, you can inject a logger. But as you don't want to log in the production environment, for instance, you can use a setter injection. So if there is one logger injected, you are using it. If not, you are just doing nothing. Better when you have a very long list of dependencies for one object instead of having, you know, tens of arguments for the constructor. But then again, it probably means that your object is doing too much. So it's probably a good sign that you need to actually decouple your code a bit more. It's also the only way to actually break circular references. So if object A depends on object B and B depends on A, you cannot use a constructor injection, so you can use setter injection. But that's a very edge case. So most of the time using constructor injection is the way to go. So this is dependency injection. So if you have any question at this point, if something is not clear, please ask any questions now. It's going to be a bit more complex in a minute. So it's clear for everyone? Yes? That's a yes or that's a no? That's a yes. Thank you. Okay. So as you might know, I'm the creator of Symphony, so I like to create frameworks. So let's create a small framework. A very small one. So when talking about a web, you have a request, you have a response, you have some kind of user, you have a session storage, you have some cache, you have some routing. So you have a bunch of objects, classes, and here is how I can create my small framework. So I create a request object, I create the response object, the user, the cache, the routing, and then I'm using them. So to be able to bootstrap a page, I need to create those one, two, three, four, five, six different objects. And as you can see, there's no rejection here. Yes, yes. So that's fine. It works. But I don't want to force my users, so developers using my framework to actually copy and paste this piece of code on every single page. So I need to abstract that into something that is easier to use. So what I've done is that I have abstracted that, and I've moved everything into an application class. So now it's much easier to bootstrap the process, just create an application, and then in the constructor, everything is actually created. So it's much easier for the developer using my framework to actually bootstrap the page instead of knowing all the details about all the objects that need to be created. I've done everything for them into this application class. Can you spot a problem here? So basically we are back to square one. If you have a look at the class, we are not using dependency injection anymore, right? Because the creation of all those objects are done into the constructor of my application class. So if I want to tweak the configuration of my framework, it's not possible anymore. If I want to change the session ID, if I want to change the path for the cache, it's not possible anymore, right? This is the exact problem solved by dependency injection. But here I'm stuck. I cannot fix that because the first version, it was not that easy for users to actually use my code, but it was very flexible. Now, by using this application class, it is much easier to use my code, but it's not flexible anymore. In this case, we need what we call a container. So a container, a service, a dependency injection container, is an object that knows how to create your objects, how to configure them. It knows about the dependencies of all your objects, and it knows how to create and configure them when you need them. So basically a container is an object able to create the graph of your objects that you need in a given application. So let's have a look at the previous example. So I have this user class. It depends on the session storage interface. One implementation is the session storage, for instance, and I want to be able to change the session storage class name and also the session name. So I have two classes, two objects, user and session storage, and two ways to actually configure those objects, the session name and the session class. So using a container, so this is a small example using Pimple. Pimple is a small dependency injection container written in PHP. It's about 100 lines of code, so it's not a big library. This is just one file, actually, and it's enough to actually manage everything that you have in your code. So that's not something that is used by Drupal, but that's yet another container that I wrote several years ago. So here I create a container. I create two parameters. The first one is a session class, the second one is a session name, and then I define two objects. The first one is the user class. So the definition is done via anonymous function, and the second one is the definition for the session storage. So above the line, this is about the creation of the container. So this is the description of my objects and how to configure them. And the second part, so below the line, is how you can use the container. So as you can see, if I want to get the user, I'm asking the user from the container. So from a user perspective, this is exactly the same as the very first slide. So instead of saying new user, I'm asking the container to give me a user. And I don't care about the dependencies anymore. I don't care if the user object has a session storage as dependency. I'm just asking for the user object from the container. So yeah, just to be clear, above the line, it is just about configuring the dependency injection container. So describing the relationships between all the objects. So above the line, there is nothing created. So there is no user object, there is no session storage. And the very first time, I'm asking for a user, then the container is actually creating a user object. It knows that to be able to create a user, it needs a session storage. So it creates a user session storage first, getting a session name, returning it, and then the user object can be created. So I have abstracted how to create all the objects that I have in my application. So some rules about containers. The first one, very important one, objects must not be aware of the container. So never, ever inject the container into your objects. If you are doing that, you are using the container the wrong way. So each object, each class, should be aware of their dependencies, but should not be aware of the container. That's easy to understand. If you are injecting the container, it's much more complex to actually replace the implementation of one of the dependencies when you are doing testing, for instance. And then if you are injecting the container, you do not really know which dependencies are going to be used within the object. And of course, you are coupling your domain objects to the container that you are using. So you will not be able to replace the container with another implementation. So object must not be aware of the container, which means that a container is able to manage any PHP objects. You don't need anything special to support a container. Of course, your classes should use dependency injection, but that's all. So never, ever inject the container into your objects. There are some exceptions. For instance, if you want to lazy load a dependency, so when I create a user object, I need a session storage, which means that to be able to create a user object, I need to create a session storage, even if I don't need it now, which means that if you don't use the user, a setter, for instance, if you don't set the language, you will have created a session storage without using it. So that's a waste of resources. And sometimes if we are talking about a connection to a database, we are talking about an object that calls a web service at construction time or whatever, it can be really heavy. In this case, it is a good... The only way to actually do that is to inject the container. So just inject the container, which is just about the description of the dependencies. And the very first time, you need to actually use the session storage. You get it from the container. So you need to inject the container from a lazy-load object if you need to. So I won't talk about that today, but if you are using the Symphony dependency injection container, there is another way to actually lazy-load the dependency, so you don't need to inject the container to do that with Symphony, the recent versions of Symphony. Okay. So something else. The container does not manage all your objects. Far from it. So a container manages global objects. So what we call services. So an object that is sending emails is a good case. So all the objects that you need only one instance of. So a database connection is a good example. A user, when we are talking about an HTTP request, you are going to have only one user. A request, for instance, a logger, for instance, is a good example. But the container is not something that you want to use for your model object. So a product, a blog post, an article, they cannot be managed by a container. So, okay. So remember, most of the time, you don't need a container to use dependency injection. You can use dependency injection today without any container. But as soon as you need to manage a large number of objects, then it's nice to be able to wrap everything with a container. Okay, so that's all for dependency injection and what a container is. Now I'm going to talk about the implementation in Symphony. And again, this is what Drupal version 8 is going to use for managing all the dependencies and all the objects, the global objects of Drupal 8. So I have some small examples here. So this is an example of how you can configure the container. So the Symphony dependency injection container can be configured via PHP or XML or YAML file. So here you have an example. This is a YAML file. And in Drupal, all the container description is done via YAML files. So the first line here is the name of the object that you want to store into the container. And there is only one required attribute, which is the class name. So you want to know that when you want to get the JSON summarization object, the container knows how to create. So it just instantiates the JSON class from this namespace. From this definition, the Symphony container is actually working in two different ways. The first one is by actually creating everything on the fly, it reads the YAML file and then it introspect all the arguments and it creates the object on the fly. The second one is by building a PHP class and optimizing how to create the services. So that's how it's done in Drupal. So based on all the configuration that you've done for the container, Symphony is actually going to dump a PHP class representing your configuration able to create your object. So this is how Symphony is going to dump or to convert your configuration file to PHP. So there is a product function method, which is going to be called if you call container get and name serverization.json. And as you can see, this is pretty straightforward. There is no overhead, you know, using the container. This way, there is no overhead. If you would have created the dependency yourself, you would have written the exact same code. So you return a new instant of the JSON object. Oh, there is something I forgot to mention. So as a container is mostly managing global objects, objects that you need only one instance of, by default, if you are not configuring it in any other way, by default, you always get the same instance of the object. So every time I'm calling this method, I'm going to have the same instance of the JSON class. So that's why we are actually storing the instance into this property. Of course, we can manage arguments, construct or arguments. So this is another example from Drupal 8. Here you can see that we take one argument, and the at convention means that we want to inject the key value dependency into the state object. So somewhere else in the configuration, there is another object with the key value name and the key value object is configured there. So if you want to get the state object from the container, the first thing that Symphony is going to do is to create the key value one to be able to inject it into the state constructor. As you can see, this is how it's done. So we create a new state instance, and then we get the key value object to be able to inject it. So again, this is exactly what you would have done yourself if you want to create a state object. Okay, so this is yet another example. So here the module handler constructor takes two arguments. The first one is a list of modules, and the second one is an instance of cache bootstrap. So the first one is actually the convention to be able to make it a parameter, which means that container.modules is actually a value that is configured that is set elsewhere in a Drupal code. So it's not this string that is going to be injected into the module handler constructor, but the value of this parameter on the container. So you can make things more complex. So here we have some class, we have some arguments, and the last two lines is how you can inject dependencies with setters methods. So here the URL generator is actually taking a bunch of arguments, and then after the object is created, Symphony is going to call the setRequest method and the setContext method with those other services. And this is how it's compiled by the Symphony container. Yeah, I'm not going to talk about that too much, but as you can see, we create the object and then we inject some other dependency, the Request1 and the RequestContext1 via methods. Actually the slides are a bit old, so I think I'm pretty sure it's very different now if you have a look at Drupal 8. There is another way. So sometimes instead of calling the constructor, you can have some kind of factories. So a factory is an object able to create other objects. So here to be able to create a database connection, we are not instantiating the connection class directly. Instead we are calling a static method, getConnection on a database class. So this is how you can do that with a factory. And yeah, this is how it's converted to plain PHP. And of course, so here you have a database slave. So this is the exact same definition as before. So using the same factory, but different arguments. So this connection class is probably going to return a different object based on the arguments. You can create an object via a factory that is actually a service. So instead of a factory class here, I'm using a factory service. So again, this is an example in Drupal. So we have a cache factory service, which is defined elsewhere in Drupal. And then we are calling the get method with the default argument. It's going to return a cache back in interface object. So that's the cache default object. Okay, we also have what we call aliases. So aliases is a way to rename a service to another name. So sometimes, like for the database connections, you have several different connections with names, but there is a default one. And you want to name the default one connection, a short name. So you can use an alias. So here, for instance, when you get the config.storage object, it actually gets the config.storage.active service. So from a user perspective, you are only using the config.storage, but behind the scene, we can easily switch from one storage strategy to another one. Okay. Yeah, so when using aliases like this, you don't want your user to use the config.storage.active. You want your user to actually use the config.storage service directly. So the config.storage.active should be private, and that's possible if I say public false. It means that the config.storage.active service is going to be created by the container, but you can't get it from the outside. So it's only available for injection into other services. And the last time I looked at this, I think that the public false was not there, so that's something that should be fixed in Drupal if it's not already done. It's kind of important because when you set public false, it means that we know that nobody is going to get it from the container, which means that Symphony is going to optimize the code even more than you can do when public is true. So whenever you can set public false, it's a great way to optimize the dumped container, the PHP one. Okay, we also have some abstract services. This one is not a very good example. It's a very bad one, actually, because you can't see this. So an abstract service is a service that you can use as a template for other services. So here, when I want to create a service that actually needs the service container as a dependency, which is really bad, I can just enter it from this container.trade abstract service. So the way you can do that is by using parent. So here I have a logger factory, and the parent is the container.trade, which means that it's going to call setContainer and inject the container into this object. So it should also probably be fixed in Drupal. Okay, if you have a look at Drupal, you can get services from the container via those different possibilities. So you can call Drupal getContainer to get the container and then get any service. Or there is the service method as well, or some shortcuts like URL generator. Again, please refrain from using those shortcuts. It's much better, if possible, to actually inject the dependency instead of relying on those shortcuts. They are really practical because it means that whenever you are in your code, you can just get some services from the container. But remember that it's always much better to be able to inject them instead of relying on the container. Okay, okay, okay. So I've just talked about the basics about the Symphony-independence injection container today. I won't talk about more advanced topics because if you are using Drupal, you're not going to use those on a regular basis. I think you will never, ever create a compiler past yourself, probably not. You will probably use tags. Tags is a way to inject... Okay. So, yeah, difficult. So what is a tag? You can tag some services. So a tag is just a name. For instance, we have a tag to say this is a kernel listener, for instance. Or this is a twig extension. And when... So let's say that we have some twig extensions. So we have the twig environment. The twig environment is the main object responsible for actually rendering the templates. And you can add some extensions. Extensions to add some filters, some tags, whatever. But as a twig environment is an object managed by the container, how can I inject some external third party or custom extensions? So you need a way to be able to do that. And that's what tags are for. So you create your service. The service is the extension that you want to register. And you add the twig tag. And just by adding a twig tag, Symphony is going to be able to get a listing of all the services tagged with the twig tag and inject those extensions into the twig environment. So that's how you can easily make the injection configurable from the outside. And lazy services, I've talked about lazy services before. So it's available as of Symphony 2.4, which means that it's available in Drupal. And that's a great way to avoid injecting the container if you have some performance problems. Okay, so that's all for today. If you have any questions about dependence injection, the container that we have in Symphony, or Drupal 8 usage of the container, feel free to ask. Yeah, so I think there is a microphone somewhere. So we use dynamic dependency injection a lot in Drupal because we are using plugins. I'm here, right here. Okay, okay. So, for example, we have the Actions API in Drupal and developers extend that by implementing an Action plugin. And of course, the Action API doesn't know what the plugin has to receive as dependencies. So what we are using in Drupal is having a static create method that receives the container and then picks out whatever services it needs. For example, that Action might want to send out an email, so pick out the email service from the container and then invoke the constructor with all those arguments it needs. I don't know where that pattern came from. Is it something you're doing in Symphony as well? So yeah, we have to do that in Drupal because we don't know beforehand what dependencies will be needed because we are so dynamic. Yeah. So yeah, I don't know that part of the code that well, so it's a bit difficult to answer. But tags and compiler presses are probably one way. I'm not sure it's going to work in your specific case. But the main idea would be, yeah, because what you are injecting is actually not... You are not injecting services, right? Because those objects have parameters that can have... You can have more than one instances of those objects. Is that correct? Yes, so the objects themselves are plugins. Actually, I saw the code. The first time I saw the code, I said, ah, that's ugly, it's not possible. And then I understood why you're doing that and honestly, I don't have any better solutions, so it looks good enough. Yeah. I mean, good enough is enough. That's something very important. Design patterns and best practices are great, but you can break them. You can, you know, escape from them. That's fine. So you said container is to store instances of objects. Yeah. So what if I want to, you know, keep an instance in multiple requests? If you want to? Keep an instance in multiple requests. Drupal use serialization for that, and there is a big problem with that. So when you are talking about several requests, you are talking about several HTTP requests or several requests within one HTTP request? Several requests. So you store, you serialize objects from one request to the next one? Yes. That's correct? Yes. And why? Well, in form submission, Drupal have a form component in which we store the original request and the original input by the user to the next request. Okay. So that's kind of independent from the container because the container is really about all the global objects, so objects that do not depend on what is submitted by the user. So it should not be, it should not be depending on the request, for instance. So that's something that we fixed in Symphony that was fixed also in Drupal 8. The request coming from the user is actually not a service, right? So that's why we have the request stack object now. So if you are serializing the object that represents what the user submitted, that's fine. I mean, that's a totally different concern. So it's not something that you can store in the container anyway. So any other questions? Okay. So just one more thing. If you are doing Drupal apps and you are probably developing Drupal apps, I've been working on a profiler for the last six months or so, and I've created Drupal extensions. So if you want to give it a try and give me some feedback, it would be really helpful for me. So this is a profiler like Xhpro for Xdbug, slightly different with a different UI. So if you want to give it a go, it's free. We are in private beta, so... Yeah, that's all. Thank you very much.