 Hi, good morning Not as full as last year last year there was a the other room was full as well, so I Have to step up my game now Please tweet about this session with the hashtag PHP UK and my Twitter handle. It's quite long my notifications are off So you won't bother me So thanks for introducing me my name is Hannes I I can describe myself with a couple of emoji characters I am a Belgian. I like to swim cycle and run I Work as a software engineer slash developer advocate for a company called get stream.io. I joined the company six months ago We built scalable newsfeeds and activity streams Can you show me some hands who has ever set up a follower relationship pivot table, right? You probably know that When your user base grows the pivot table gets enormously large And sometimes for some people like Twitter in the in the early days when they had a lot of fail whales Their pivot table was basically so big they couldn't handle Joining it with any other table because that would it would go out of memory And it would be super hard to fetch a timeline for some user So that's what we do We built scalable newsfeeds and timelines and notification feeds and activity streams So everything you see in some social app where you can follow people You can basically use our tool to build that feature and you don't have to think about pivot tables or memory or whatever So we have a page called Get streamed.io slash try the API It's super easy. I'll remember you after the talk as well You can sign up with github or register with github. It's totally free and you can browse to our API and see how it works But that's not what you came for you came for this you came for IOC container It's quite tricky Let me first preface this by saying what this talk is not about IOC when I talk about IOC I'm not using it as an acronym for International Olympic Committee That's not what it's about You might think IOC container Well, it's about Docker. No, that's not true either It's about inversion of control But to understand inversion of control we need to understand a couple of design patterns first So this is a quick quick quick five-minute lesson on design patterns And there's no better way to show off design patterns by showing some code So this is the first class. I want to show you it's a super simple service class and it has one method Calculates something it gets something from storage Calculates it does some calculations and returns the result. It's super simple. We all have dozens of classes like these But to get something from storage you actually need a connection with FTP so we do a first Design pattern here. We inject FTP into the service class Basically, you want something that's already configured and set up and inject that into the constructor So that's the second design pattern first. I talked about Dependency injection which is injecting a dependency something you depend on so this service class depends on FTP connection to work And the way we injected is constructor injection. So that's two design patterns just in the first few slides So the project rolls along and some some people come in and say well, we were hitting quite a lot of FTP traffic here Maybe once you want to add some caching. So developer comes in and says, okay, let's get this job done super quick Let's add a few lines in this service class where we actually do the FTP fetching So we cash some stuff and prevent the class from hitting the FTP over and over again now the QA engineer or whoever jumps into the room again and says well, we want to know how much of How much we actually hit this FTP server? So we add some logging Again a few lines to this simple service class, but now the service class does five different things It gets something from the FTP caches it Logs it to monologue or whatever this does some calculations and returns the result That's a lot of stuff going into one simple class. That's kind of a code smell, but okay, it works. So We'll just deal with it But later on we see that we depend a lot on FTP here We did depend on FTP to get some stuff. We cash what we get from the FTP We log what the what we get from the FTP That's that's a super easy refactor, right? Just get everything that has to do with FTP the storage thing and just extract it to a different class That's the first step. That's the first simple refactoring. We're going to do So we create a new FTP storage class and we just copy paste some code We give it a new method name Which is get and then a key or a file name, whatever you want to call it And we refactor the service class to inject the FTP storage class instead of the FTP Right, that's a super simple refactoring and now the service class is super clean It does one thing only and that's getting something from storage and return the calculation on that value The next step we can introduce is Introducing interfaces, right the service class depends still on the super detailed FTP storage class, which is super low level It's super. Yeah, super specific to this use case We might want to depend on something more abstract. Maybe a sort of storage interface, right? This is a super simple refactoring step again just introduce an interface Make the FTP storage implement the interface and refactor the service class to type in the storage instead of the FTP storage Right what we are left with is two classes and one interface the service class and FTP storage Class which implements the storage interface The next step is we can go back and refactor the FTP storage, which still does a lot of things It's fetch it's fetching from the FTP. It's cashing it and it's logging it. That's still three things in one class So if you're striving for single responsibility, we can refactor that By using this storage interface we can start Introducing some decorators This is the first one the cashing decorator implements the same storage interface It's super simple And it's basically going to wrap another storage class Doesn't have to be an FTP storage could be a login decorator as well So we inject the login decorator which wraps an FTP storage in here We also take a cashing implementation so we can actually remember what it's what it has returned in the past The same thing is true for the login decorator. It just wraps any kind of storage Yeah interface Typed thing could be the cash Decorator could be the FTP storage decorator could be an s3 Amazon s3 implementation So if you now look at the FTP storage, it's it only has one responsibility left and that's to get something from the FTP So I showed you a lot of code But now I want to show you how the different classes and interfaces work together in the beginning We started with this we had a service class which dependent on an FTP storage, which is dependent on an FTP class If you look at this graph all the arrows go up But the class at the top is like the most specific class that's the most Low-level implementation that you have in this entire graph, which is Completely the opposite of what you want You want the low-level implementation to be at the bottom of your graph and to go to have all the arrows go upwards so after The refactoring we end up with this graph We end up with a service class which depends on an interface which is high level and And then we have an FTP storage class which is low level at the bottom of the graph which in In itself depends on FTP which is still lower level than the service class the service class that does no longer Care about what the search is is it an s3 search interface? Is it the is it the caching decorator? Is it the login decorator? It doesn't matter So all of these low-level implementations and decorators They're all lower level than the service class which depends on any kind of these implementations So if we put them side by side you see that the FTP class has become Has moved from the top of the graph to the bottom of the graph So yeah, the design patterns I was talking about are dependency inversion instead of depending on a Low-level implementation you're going to depend on a high-level abstraction So all arrows are going up and your low-level implementations are going down in the dependency graph The second Design pattern is dependency injection. You're going to inject whatever you depend on for example the storage interface Through a constructor injection. So that's that's a form of dependency injection Some sometimes people use DI as the acronym for dependency Inversion and it's actually the same acronym as dependency injection But they're actually two different design patterns. So keep that in mind And then the last one actually works together with the top two and that's inversion of control Inversion of control IOC basically means You're no longer going to Let the service class new up a FTP storage like this And the beginning we had this We created a new service and we inject an FTP and at the end we had this Which is pretty much a lot of code Which we don't want to write and that's where the IOC container comes in, right? So instead of calling all of this or newing up all of this inside the service class We're going to reverse that and say well, whatever it is. You're going to call me You're I'm good. I'm going to pass off control to the container and I'm going to say well container you're up It's it's your task to tell me what is going to be injected inside the service class So I'm going to reverse the the method the object creation to let the IOC container itself do the object creation for me So that's inversion of control When we use a container and say well get me a service class object It's going to look up what it what needs to be done to create this service class object First it's going to create the FTP Storage class then it's going to wrap it with the log decorator then the cache decorator and then inject all of that Well the cache decorator instance, which is wrapping all the other instances into the service class note that the object that the service class is Being injected with is actually the cache decorator and not the FTP storage But because the cache decorator implements the storage interface the service class doesn't matter It doesn't care what's being injected. So that's IOC inversion of control Helps you with composing all of these objects that the IOC container help you with composition Now enough with a gang of four Design patterns here Let's talk about the the actual implementations we can find on packages So there's a ton of frameworks and all of these frameworks come with their IOC container There's symphony dependency injection Illuminate, which is a Laravel container RRDI is a Zen framework pimple leak container and most of these implement Some kind of interoperable interface. So there's container interrupt slash container interrupt Which is now deprecated in favor of the psr11 psr slash container package or interface so last year on Valentine's Day They basically deprecated container interrupt in favor of psr11 So that's some love for the phpfig this is what the the interrupt container looks like right now it just Extends the psr container. So it doesn't add anything to it And this is what the psr container looks like it has two simple methods The first one is the get method which returns the result resolved object or should return the resolved object And the second method is just a Boolean method. So it just returns. Yes or no I can I can resolve this or I cannot resolve this right Now let's let's talk about how this container works right the internals That's what you are all Interested in I hope so The first way a container could work is by and doing by doing something called Automatic object resolution That's just some fancy words for saying it's going to look at the At the constructor and see what needs to be injected. So to be able to create this send invoice class You need to look at the constructor because that's how you knew up a new object so the constructor has a mailer argument which is type in that mailer now We're going to do some construction injection here I use a container to do that and if we use the php leak container we can tell it to use a Reflection container and this reflection container is using a php native feature called reflection And the reflection API goes like this You pass in the class name to a reflection class object You get the constructor and you get the constructor parameters And then you can loop those parameters and get those to get the type hints the type hinted classes of those constructor arguments and The container is going to do that recursively. So if you go back to this This sent invoice class the sent invoice class needs a mailer object But if the mailer object needs something else in in that constructor It's going to resolve those objects as well So it's going to crawl down the dependency graph and find all the objects or all the type hints resolve them from the container by doing automatic Resolution and then it's going to inject it back into the sent invoice class so it's going to do it recursively and Basically all of the all of the ISC containers have it The symphony IC container has it the Zen Zen framework container has it Laravel leak container So that's the super easiest way Just tell it to do automatic resolution and you don't have to help it Just give me the send invoice class It's going to look at the constructor arguments and recursively resolve those objects Obviously it only ends when there's one constructor without any arguments so it can you up that object and roll Go back up the dependency graph to inject everything into the send invoice container But I just basically told you to Depend on an interface instead of a low-level implementation, so we're going to need to help the container sometimes So if we would have type in that the sent invoice class with mailer interface We're going to need to tell the container which interface or which implementation to inject into the send invoice class so The first situation is where we need to help the container is when we type in an interface and we actually need some concrete class to be To be resolved to inject there the first way to solve this is to use aliasing Right, we're going to tell the container. Well, whenever This an object is needed with this interface Use this concrete class In Laravel it looks like this container alias mailer interface uses concrete mailer Implementation and symphony. We can do the same thing with set alias and in most other Most other Containers we can use something called a anonymous function which is used to resolve this So we can say well leak container I'm gonna add a anonymous function here Which you can call whenever someone needs a mailer interface object and Whenever someone needs a mailer interface object call this anonymous function and I will return you the result Which you need to inject So we basically ask We basically tell the container Here's another anonymous function and trust me. I'm going to return a mailer interface type object Um, so we can create a super simple one-line anonymous function that says container get mailer class It just returns the Returns back to the container to say well, here's the actual implementation. You want to in you want to be injecting the second situation is When a constructor has arguments and one or more of those arguments are not type-handed for example You won't yeah, you have a constructor with two arguments and one is type-handed one is not then basically the Reflection container or the reflection class doesn't have any type hints the container doesn't know what to inject into that constructor So we're gonna have to define those dependencies and simply we can do this We can say register mailer at argument We can call that at argument method multiple times to say well This is the order of the arguments you want to inject into that constructor whenever you instantiate the mailer class for basically any any other container you can again use these This struct of anonymous functions So for the pimple and the leak container you can say well whenever mailer interface is resolved Call this anonymous function. So it's the same principle here it's kind of a factory method or a Anonymous function closure or whatever you want to call it and you basically tell the container well, trust me If you call this function, I'm going to return you an object with this interface The next situation is When a class is type-handed, I want to return I want to inject an object But the next time this I want I want something to be resolved. I want to return the exact same object This is useful for injecting a translator something with a global state translator has the the locale Which is global for the entire application Or an entity manager? You only want one entity manager to be used into into your entire application or an event dispatcher Something that you want to share all over your application and you want to have the exact same object. So sharing an instance In Laraville, it looks like this you do a container share and No, you do you do a container singleton and in the leak container you do container share And the symphony it says Register and then set shared to true And basically any container has this feature Some have this Sharing feature as the default some have it Disabled by default so it depends on the container. You're actually using but you can define per dependency Whether you want to reuse the same resolved object or not the next situation is When you have Different classes and they basically have the same type hand for example file system interface But you want to inject different implementations or different configured objects So For example, you have a file system interface and you have a bunch of controllers a photos controller a PDF controller But you want them to have a different configured file system interface object To do this we can use something called contextual binding I found I found this feature in Laravel. I don't know if any other containers have this but I'm I'm I'm pretty sure there's some other containers which have this but in Laravel. It's super easy Basically, I do this I Go to the container and I tell it there's different file system interface objects that you can resolve And I'm going to name them The first one I'm going to name filesystem.photos filesystem.files filesystem.pdf filesystem.avatars And I give them different names so When there's a photos controller, I can say When photos controller needs file system interface give it filesystem.photos and I do the same thing for different controllers So depending on the context in this case the photos controller I'm going to inject a different item from the container based on The name I gave it so this name is the same name I gave it when I registered it on the container, so I said container add filesystem.photos Whenever someone resolves filesystem.photos give it this this implementation or this this instance and That's how you are you basically register different items and You tag them or you give it the name filesystem.photos and you inject it in the right place You can still do this in in different containers as well. For example in the leak container. I just use a default Anonymous function and I say well when filesystem Well when photos controllers is resolved I want to get container get filesystem.photos and I want to inject it as a first argument It's as simple as that the next situation is When you are resolving something for example an event dispatcher or a Entity manager from doctrine you want to do something right before you inject it somewhere For example, you want to resolve some you want to register some some listeners for the entity manager in doctrine I Still want to resolve it in a normal way, but right before I'm injecting it I want to register some stuff for this we can use a solution called container events Some some implementations call it inflectors some call it hooks you can call it a Post resolving or pre-resolving hook or event And I like it the most in the leak Implementation and the leak container has something like this container inflector and then you give it a class name And whenever an entity manager is being resolved Right before injecting it into where it needs to be injected It's going to call this class this anonymous function first and it's passing in the entity manager as the first argument The first and only argument. I can then call some methods on it For example get configuration get the entity listener resolver and registers register some entity listeners And this is being done right before the entity manager is being injected somewhere Of course when I share this entity manager, this is only going to be resolved This is only going to be called once otherwise. I'll be registering the same model listener over and over again Which is not useful In larval it looks like this. You say container resolving and Then you give it in an anonymous function and it's going to use the same reflection API found in PHP Where it's going to look at the first argument of this anonymous function and it's going to see oh well Whenever an entity manager is resolved. I want to call this anonymous function because it takes an entity manager I Use this feature a lot And I use it for different purposes the first one is to inject something that Would otherwise require a connection with the global state or the global container For example an environment if you want to know the environment where you're working like local or staging or production That's just that's such a super simple string That I actually created an interface and a trade which I just add to some classes that need the context of the of the environment and Then I tell the container well whenever something is being resolved with this interface Environment aware interface. I'm going to inject the environment into the into the object. This is just an example But you can use it for more more than one thing you can use it for Injecting a command bus or injecting an event listener just add an interface Event listener aware and a trade event listener aware or event dispatcher aware and whenever something is being resolved with this Either one of these interfaces. I'm going to actually inject the event dispatcher or the command bus That's for me. It's a super super simple way to define dependencies and to inject them with a well, this is actually a different kind of This is a different kind of dependency injection instead of the injecting it through the constructor I'm going to inject it through a method. So it's method injection. I know some people are against this, but I like it so The next Thing I like to use this for is for Injecting soft dependencies Now sometimes a class needs a dependency, but it doesn't always need it for example If you have a miller class Sometimes you want to queue a mail you want to just put it on the background job So to be able to do this you need a dependency on a queue class But it's not always that you need to queue class so You want to new up the mailer, but you don't want it to know about the queue class before it's actually going to use it So it's a soft dependency. You don't want to open up a connection with your with your message bus or something So what I'm going to do here is I'm going to inject a Anonymous function which can be called by the mailer whenever it needs a queue So it's a anonymous queue resolver which I'm going to inject with method injection with a Set queue resolver Method on the mailer. So whenever the mailer needs to queue it already has an anonymous function It can call to resolve the queue class from the container But it doesn't need to have a connection with the container because it has a anonymous function Which it can call to create a queue class so quickly quickly injecting stuff injecting stuff dependencies But I also use it to inject It's basically like the environment variable It's injecting configure it can configurable variables so a Lot of frameworks have something called a config class which it can use to To get some config variables So sometimes the people that Classes depend on the config class of the framework Which is basically injecting your framework in into your custom domain code And I think personally that that is a code smell. So what I like to do differently is I want to inject those config Things into the classes without actually injecting the config class as a whole so Whenever this is just an example whenever this validator is being resolved There's a typo here When the validator is resolved, I want to get something from the config and inject it through a method so this validator I would add a Set allowed file types method on it, which I can use to inject config variables So whenever the validator is being resolved, I'm going to use method injection To let the validator know. Hey, here are the file types that are valid Instead of injecting a complete config Object into the validator. So I get the config class from the container I get the allowed file types and I inject it through method injection so That's how to help the container but now I want to Yeah, where do you put all this when do you put all the aliasing and the contextual binding and the Resolving the callbacks and they and the container events were to put all this There's another container interrupt Project called service provider Which basically says well, you can put it in these service providers and they can tell the container What needs to be resolved and how it needs to be resolved? There's a similar thing called service provider in leak slash container and Laravel also has service providers in symphony You can do the same thing in configuration files or in factories and in zent you can do the same thing with Factories as well and config and all all these kind of things So every project has its own way of telling the container how to resolve something, right? So We just told the container how to resolve something, but when are we going to use it? What and how are we going to use it? Basically, I prefer to use the container only in a limited set of Classes or in a limited set of places in my entire framework or application. I Don't want to inject the container in every single class of my application. I don't want to make it global I just want to use it in a limited set of places for example in the factory or In an event dispatcher or in a router or command bus Basically in places where you need to call other code But you don't know up front what what what the code is going to be for example, I want to call a I want to call a controller an HTTP controller But I don't know yet how to resolve this controller So I'm going to inject the container and I'm going to say well container now give me an instant shaded Photos controller or an instant shaded send invoice listener So it's just in a limited set of places where I'm going to actually use this container so the first The first and simplest way of using this container is to get something from it So as I showed you before the psr 11 container interface has two methods And the first one is has the second one is get and the first one just it's a boolean Can you resolve something with FS? Our file system and second one is well now actually give it to me, please The second way and this is not something that's in the psr interface, but that's Getting an object, but I want to use some arguments there For example, I want to get the file system object But here's an array of arguments. It's like passing in CLI arguments if you do a bad bash command So give me the file system File system object, but here are some arguments I use this to to get a file system object, but Give me the images one. I use this to to quickly define the file system that images But always call the same file system Resolving and give it an argument. So I would redefine the same block For a fast system images fast system that photos fast system that PDFs fast system that files something like that. So Another way to use a container and this is something that only a limited set of containers have is calling functions and Using the container to call an anonymous function To look at to inspect the arguments of the anonymous function and to automatically resolve those so for example In this line, I have an anonymous function with one argument could be multiple So whenever I call this anonymous function I Want the container to automatically? Resolve a mailer object and use it to call this anonymous function So this is what it looks like in leak in the leak container and in the larval container and basically those three things here are all Anonymous functions, so this simple array with two items Is also an anonymous function you can use this like any other closure So the listener object has to be resolved Has to be an actual instance with a method handle so You can basically say well, this is a closure Which is a simple array with two items instance and a method name you have to call on it And then it's going to use a reflection API to find the arguments of the anonymous function And the bottom one is also an anonymous function given that listener is a class name which has a static method handle A lot of people don't know this but all three of these are Anonymous functions So it's going to use a reflection function Which is a class in and it's in native PHP You don't need to install anything for it. You can get the callback whether that's Listener colon colon handle or the simple array form or an actual defined anonymous function And then you can get the parameters loop over those Look at the type hints and resolve those from the container itself And then there's another way to do this in larval It's going to wrap an anonymous function This anonymous function can have multiple arguments. It doesn't matter. It's going to return a anonymous function without any arguments So you can inject this into any other Legacy code base and say well, I still want to use the container, but I still want to use my legacy code base So this is in intermediate form where I can inject Closures without any arguments and I still want to use container to resolve stuff And this is this is what it looks like. It's a public function wrap give it a callback with some arguments And just return an empty anonymous function, which will call the same container So this is the wrap function is a function of the container itself Call itself with this callback which will in then in turn Find all the all the all the arguments and the type hints of the callback and resolve it The next feature I want to talk about is tagging And I want to warn you first Tagging in symphony is not the same as tagging in larval Tagging in symphony is just a way of documenting stuff and in larval. It's actually adding tags To items in the container so you can resolve all those items at the same time For example, I use I I use this file system that images and fast is not PDF. So up and I Can use the container or at least a larval container to get all the fast system tagged items from the container and loop over those So if I have fast images, I want to tag it with file system If I have file system that photos I want to tag it with file system, too And now when I ask the container now give me all the instances that are tagged file system It's going to give me an array of all those resolved instances Then I can loop over all those instance instances Which I hope are the same interface, of course, and then I can do something with them I want to maybe clear them or I want to Copy them to S3 or whatever This with this tagging. It's a super neat feature I can basically get all the same typed in instances from a container in But again, I want to warn you if you Google it and you want to use the same feature in symphony tagging means something completely different in symphony Since I have some time left I want to show you some bonus lights I Want to show you how to resolve circular dependencies so I told you the container can do something recursively until it hits a Constructor with zero arguments and then it can roll back up to the dependency dependency graph and inject stuff But what if There's a mailer class which depends on the queue interface or the queue implementation and the queue implementation depends on a mail Implementation or with some more intermediary steps. So if there's a circular Yeah, a circular thing in your dependency graph, you might have a problem or your container might have a problem For example, the mailer depends on the queue and the queue depends on the mailer Your container will probably fail and say well I Looped over this many many times and I couldn't resolve it Because it's recursively it's going to keep a counter and stop at like 100 or something And the way to stop this from happening is to use something called. Well, I showed you before It's basically making one of those Implementations or one of those dependency that dependencies a soft dependency. So instead of making the Mailer have a hard dependency on the queue. I'm going to make the mailer have a soft dependency on the queue So I'm going to add another method on the mailer class, which is the set queue resolver Which we we can then We can then use to tell the container. Well, whenever the mailer is being resolved So this is a container event again or an inflector We're going to say well whenever the mailer is being resolved right before returning it Here's an anonymous function to tell the mailer whenever you need a queue Hey, you can use this anonymous function. And if you call it, I promise you I will give you a queue interface object so That's basically it. That's a that's a bonus light. I want to quickly recap here So if you use dependent if you use dependency injection and depend dependency inversion and IOC and all of those Design patterns you might want to use a IOC container to compose all those objects because all those objects or all those classes are super small They have one responsibility single responsibility principle And it's super hard to compose all of that and to do that you use the IOC container to To compose them and to call a new service class or to create a new service class and the container will know how to compose that But you want to help the container by using aliasing and container events and soft dependencies and method injection and all those things to be able to compose all of that And if you do that, you have super small usable unit testable classes and Everything depends on high-level abstractions and not on low-level implementations and when you When you draw the beautiful design graph or the Dependency graph you will see that you have a lot of interfaces on top and a lot of low-level implementations on the bottom You can also resolve circle circular dependencies with soft dependencies By making one dependency a soft dependency instead of two hard dependencies which make a circular Loop inside your dependency graph. So, yeah, thank you