 All right. The watch says it's 345. So hey, everyone. I am Jack Appelbee from Vancouver, British Columbia, Canada. I do a bunch of Drupal things. Those are some of my modules. I'd appreciate people checking them out and seeing if they work for them. If you want to find me online, Drupal.org and Twitter. So I have my notifications off, I hope. So none of those will pop up if anyone heckles me during the presentation. I work at My Planet as a developer. It's a cool place. So today, I'm here to bring back very old memes. You can tell by the JPEG pixelation and talk about the service container. So start with a little bit of foundation. Defining services. Every module core itself has a services.yaml file that starts with a mapping of giving your service a name. So here, it's in my module namespace. Give it a more specific name for what it does. You're mapping it to a class that has the code that actually does the functionality. And you're defining what other services it probably relies on. So here, very simple service just relies on some configuration. So we need to pull in the configuration factory so that we can do our own functionality. So when we start with our codes, we have a class. And in the constructor, we define that we're going to take something. We're going to add an object that we know exactly what it does because it implements that interface. We might not know what class it's going to be, but it's probably going to be the core class. That allows us to reuse this, know what we get, and operate against it kind of without knowing exactly what core does. If we want to access that service from procedural codes in our module files or anywhere that we're not using classes, we have the service container. So there's this static method, Drupal double colon service. And we provide it with the service that we're trying to get, again, our service from our module. And we can use that class within our code. There's a few pretty big benefits to this. The first one is testing. One of the big detriments to Drupal 7 with everything being a global function. If you want to test your code and you rely on some function that's provided by Drupal Core, you have to test Drupal Core's function as well. So in a lot of core tests, they don't work unless you do it on a full site install that everything can go back to the database. If you want to test something that interacts with a node on the site, you need a fully built out site that has all the node information in the database so that every system underlying your code can retrieve it from the database, convert it into the format the code expects. Whereas if we're swapping out services, we can use mock objects. So we can provide instead of an actual implementation of the service, we provide a class that implements the same interface but provides code that we know what the response is. We get exactly a pre-built node object. We can act on it. And our test is never going to change, even if core does. We don't need that full site behind the scenes. So tests can run a lot faster but be a lot more focused. You're only testing your layer and you just kind of mock out what every other layer does. Couple benefits potentially for performance. In lazy loading, a module file still happens in Drupal 8 but happened in Drupal 7 as well is parsed, loaded and parsed on every request. So if the module files get really big, that becomes a lot of code for Drupal to handle just to bootstrap, let alone do anything on the page. So there's a few ways to get around this in Drupal 7 by moving page handlers out to include files, doing that manually. It gets a little messy in Drupal 8 now that everything is a class. It's not loaded until you need it. Unless you actually load a service, it's not going to load the class behind it, the code behind it. It's not going to load any of its dependencies. So if a module doesn't do anything on any particular page on the site, its code isn't loaded, parsed, rendered, speeds up things a little bit. There's actually another level to lazy loading as well. When a service is initialized, it initializes all its dependencies, goes down the chain. You can even get lazier if you have a really expensive service that your class might not use. You can declare it as lazy too so that until you actually tell that service to do something, it's not initialized. So that can additionally save time. And the benefit, especially talking about here today, is overriding. Since it's a map between a service name that should never change, it should be a dependency, to a class, you can swap out that class behind the scenes. And as long as everything is dependent on an interface, what code actually gets executed doesn't really matter. The code that depends on it just knows I need to be able to execute this method. And as long as the response is something that I expect, I don't care what class, what module actually implements that. So this is where altering services comes in. So to start with the basic, the documentation starts you off with, this is a special class. So it's the name of your module converted to camel case and then appended by a service provider. It's in the root name space of your class, so directly in that SRC folder of your module. Drupal magically searches for, tries to load this class. And it implements this service provider base class. It has two methods on it that it mocks out. They don't do anything by default. So one is register. If you have a class that has some kind of dynamic operation that when you set it up needs to be put in place, you can use that register class. The important one here is this alter. So similar to any other Drupal alter hook, Drupal goes through all of those services.yaml files, loads up all the definitions. And then it gives every module the option to make any changes that it needs. So here's a pretty simple example. It grabs the container. It grabs the my module one, or it grabs a core interface, says, hey, we don't want to use the one that core defined. We want to swap it out for our modules version. This is pretty simple. This is going to expect that your own service, like it expects the same arguments as the core service. So it basically has the same constructor signature. If you want to, you can rely on whatever other services that you need. So there's operations to change the arguments completely or just append an argument to say, I want to do something like the core service, but I need another service to do my operation, provide some extra information. You can append that on and have your service change any behavior based on what the core does. At this point, this alter function basically is interacting on those string definitions. Nothing's been initialized yet, so you can make any changes that you need to. So what that looks like, you have your class. It implements the interface. The one downside of this is if you're just implementing that same interface as the core service, is you have to reimplement every single method that that interface defines. So you're duplicating any code that the core does. You're not using any of the benefits of an object-oriented structure. So one shortcut to that, object-oriented code, is instead of implementing the interface, you can extend that core service. So you're using that class. You only need to define your methods on your service that you want to interact with differently. If you need to use any data from the parent class, maybe you're just changing some of the information that the parent class returns. You can make any small alterations to it and return that data. Otherwise, if you don't care what the parent class does, you want your full built up from scratch response, do it in your method, return it here. Any method that you don't override goes back to the parent class. You don't have to duplicate any code from core. If core fixes any bugs, your class gets those bugs fixed too because you're not re-implementing those methods from scratch. One big downside from that is that it's the last module wins. If a contrib service wants to make any changes to that core service, and then you want to make changes too, any of those changes from the contrib module are lost. Because your module specifically in code says anything I don't define, go to the core version of it. The contrib module, any other contrib module has no way to get into that. If you know that you have a contrib module on your site, you want to reuse it, you want to build on top of it, you could extend the contrib module, but then if you have two contrib modules who both want to make changes, you can only pick one. So this is controlled by module weights, and definitely that limitation that once things get more complex, ideally these services are very focused, you're not going to have these competitions, but it just doesn't handle if there are them. So that's where decorating services comes in. This is a little bit different. To change this, you go to your services.yaml file. We're giving our service a name. I'm explicitly naming it as a decorator just to define what it is as well in the service name as well as the class name. And the extra key here is this decorates where you define what you alter. So here I want to decorate the core service. I'm providing my own decorator service in the module. And then in the arguments you have an extra, a new special key that's available is there's the dot inner service. So the name of this is based on the service that you're defining, the decorator that you're defining, not the service that you're wrapping. So we have the my module core service decorator dot inner. And then we want to define any other additional services that we need. So here we want to rely on this core service, but we also want to do something else potentially based on configuration or state from the website. So passing those services in as well. So when we're defining our module, it's very simple, we've got a constructor. We're type hinting against the interface of the core service, not the actual implementation of it. And then we're pulling in any of our other dependencies as services. And so we've got the method that we want to override in our service and we're returning any data. If we want to get any information from the core service itself, so if we want to make any alterations, we can call that method because we have that service inside of our own class. We can call that method, pull out any information, make any alterations to it. One downside to this is that we only have access to public methods from the interface. Whereas if you're extending a class, you have access to private or protected properties as well as protected methods. So it's a little bit less flexible, but in certain ways it's going to be more reliable to go against the public interface rather than the protected that might not be guaranteed to be as reliable that they might want to change something in the protected methods behind the parent class that you're extending. So this should be safe. If core changes an interface and it breaks your code, that's probably a core bug. So it's a little bit more reliable. You know what code you're getting out of core, you know what alterations you want to make to it. And it should be pretty safe. The downside to this, similar to the other method, there's no way around it, but instead you have to proxy every method to that class. So you have to define in your class all of the methods that the interface defines. And then if you don't want to do anything in your class, you always have to hand them off to the one that you're decorating. I tried a couple methods with magic PHP methods and some other items, unfortunately PHP expects that those method names are there or the function names are there and defined. So it's a bunch of boilerplate that you need to have in place. Hopefully the services are very focused. So you only have a few methods to override. If there's 20 methods on an interface, it might be an indication that interface needs to be split up a little bit. Or that service does a little bit too much. But it's kind of very, very straightforward to do here. One thing I've seen in a previous article when trying to look at this method, you don't want to extend the core service if you're doing a decorator, because then you're basically removing the benefit of this decoration that it provides. You're gonna, you wouldn't need to re-element all the service or all of the methods in your own service. But then instead the object chain is gonna be in place and it's gonna go back to that core service again instead of going back up through the decorator. So instead of the stack last module wins, it's this onion layer of decorators up to whatever anything relies on. So what happens is any service pulls in, I wanna get the service from core based on its name. The service container goes, oh, I have all these decorators. Instead, I'm gonna hand you this kind of decoration service. You know what methods you can rely on. It's gonna pass it back down the chain. So if you have multiple, the customer decorator is going to wrap the contrib module, which is gonna wrap the core. You're gonna call the methods, any other service on the site calling these methods. It's gonna call the method on the custom module. The custom module can either return its response or defer back to one down the chain, eventually getting to the core or wherever it stops. So if you have multiple modules doing this decoration, you have multiple layers. And as long as they're not trying to make competing changes along that layer, which is probably a little bit safer, it doesn't matter how many want to kind of inject themselves in the chain. Whereas that complete override that's controlled by module awaits. This is another thing within Symphony is priority. So I haven't tested it, but without this priority, as far as I know, it basically stacks up as I showed in that previous slide that it's done by module waits, which is alphabetical or adjusted by waits. And then basically the last module, the heaviest module with the largest wait is the one that'll actually get called by code that relies on a service and then get passed up the chain. Whereas decoration priority in Symphony priority is the opposite. So higher waits come first, as opposed to in Drupal or higher priorities come first, whereas in Drupal, kind of negative weights are the ones that act first. So if we're to alter that, we define that our custom module has a priority of five. That moves it up the chain so that it first wraps whichever service and then any other ones that either haven't had a priority defined, which default to zero or you can do negative priorities as well, those will come later. So that's very abstract. Let's try and pull in a little bit more of a concrete example. So this is something that's gonna happen in Drupal. Dropping support for Internet Explorer. This is fine for the web 3.0 rock stars who can keep up with the latest and greatest and ignore everything else, but it's only two years ago that Microsoft stopped supporting these browsers and that's just too soon for the enterprise. So the one that's gonna affect us is this one. There's one problem with Internet Explorer 9 that it only allows 31 style sheets, at least defined in link takes. And if there's more than that, it ignores the Rask, which is a problem because when you have a lot of modules on your site and everyone has a CSS file, you're gonna have more than 31 style sheets. So if you're aggregating it, you're probably safe, but if you're not and you have unaggregated CSS and you're trying to test your site on IE9, things are gonna look weird. So this is still in needs work, needs review. I think it needs tests is what's left on it, but sometime in the future, Drupal's gonna change its behavior. And in this case, we wanna keep it the same. And we wanna be safe against that change. We know this is gonna happen. We don't quite like what Drupal Core is gonna do, so we wanna keep it in place. So we'll create an IE9 module. We'll decorate the core service. So the collection renderer is what translates an array of here's all the CSS that's supposed to be output on the page, translates it into the actual HTML markup that gets output. So we can see we're decorating the core class. We're passing in the core service with that dot inner as an argument. And basic service to kind of class here. Since if there's less than 31 style sheets, core functionality is great. We wanna make sure that if they fix any bugs that we're not stomping on that or replacing it. So we just do a very basic check. If this output, we know it's gonna be compatible, core is gonna do what exactly what we want with Internet Explorer 9. We'll just defer to the core service, but we have this special case that we know that in the future in Drupal Core is gonna change. If there's 32 style sheets, Drupal Core sometime is gonna stop doing the right thing. We wanna make sure we preserve that behavior. So instead we're gonna render it out, do inline CSS with import tags and return that. So as I mentioned in the description, this has the benefit of, we don't have to apply a patch to core. We don't have to say core's code is weird and wrong. It's not actually a bug. If it's a core bug, file is an issue, put it in your composer or your make file so that when you build up Drupal, that patch is applied. Hopefully in that future, that patch gets incorporated into Drupal and you don't have to keep applying that patch. This is the instance where a core or a module, the service does something that you don't want it to do. You know you wanna change it and you wanna keep that change in the future. Swapping out services using this decoration. This is a way that you can define your own class, self-contained service. You can test your service for what it does to make sure that it keeps doing what it does. And make sure that as core changes, if they do something like just fix a bug, your core changes over time, you're not having to constantly update your own patches to maintain a certain behavior that's a little bit different. As well, if you're building a contrib module, something that you want other people to be able to use and configure, this sort of flexibility is important because sites can't rely on some modules have to. But a module can't say, well you have to patch core in order to do what we wanna do. This allows modules to very narrowly say, we wanna change around the behavior of a single method in certain instances and how Drupal core operates, as opposed to needing a patch to swap things out or making big changes to Drupal core and being able to layer them up. So yeah, if you're looking for some documentation, hopefully I'll get around to improving some declaration documentation directly on Drupal.org. But there's a basic services and dependency injection which refers to symphony.com. Their documentation is really good as well, easy to read, so there's a quick article that'll give you some of the syntax on how to decorate services as well. Check out Contribution Sprints. I'll be out there on Friday working on my content security policy module. So appreciate help with that or anything else that you're interested in working on. Thank you very much. Please take surveys, let me know what you think. On my session and all the other sessions that you visited today. Any questions? Thank you.