 We'll be talking about Drupal 8 plug-in system. People here, how many have actually worked in Drupal 8? Like, dabbled with the code, and maybe updated their modules. Awesome. So hopefully a lot of this will be familiar with you, and I'll be exposing some internal information that might help you out. And if not, maybe it'll be too much information, and you'll confuse a lot of it. Like, this is so awkward. I have to lean forward. I'll do my best. Cool. So this is me. I'm Hellyer. You guys haven't met me before. I have been working in Drupal for maybe about five or six years. I currently work at MEC Universal. And my handler is pretty easy at Hellyer, at most social media sites in Drupal as well. I haven't actually contributed anything meaningful to Drupal 8. I just kind of dabbled in it maybe a couple of months ago. And am I finding those some pretty cool stuff to share with you. But it is fairly technical, I guess, just by its nature. Plugins are itself just PHP classes. And so there's a lot of dry, technical information involved in that. So just a fair bit of warning. When I prepared these slides, I had in mind that everyone here is very nerdy and can handle a lot of the PHP talk. So if anything, that is the disclaimer here. And the prerequisite that hopefully you guys understand at least the basics of object-oriented PHP and PHP 5. So yeah, just dropped in a quick overview so you guys know what to expect here. I'll explain what plugins are at least in the scope of Drupal 8. And I'll talk about why it's so important to discuss the conversation of why creating plugins as opposed to just creating custom hooks or just going to town with whatever kind of mechanism. Why it's all important and what the benefits are. And also, I'll drop in and talk about some critical Drupal 8 concepts that are in itself requirements to understand the plugin system. A quick preview to that is I'll be talking about PSR4, annotations, service containers, and dependency injection. And after we get through with that, I'll do a quick little demo of what it is to actually write some plugins. And I'll dabble in with a few different plugin systems and show you some other concepts that are baked into what it is to define plugins and then create derivations of them, et cetera, et cetera. And then the most important part. Well, if you don't intend on creating your own plugin system, maybe you can drop off at that point after step 4. But step 5 here in this case is going to be talking about the internals of the plugin system, how it actually works internally, what are the classes that are used to bake together the discoverability of plugins, what we're using to map certain instances to this or that, and how to basically set up your own thing from the ground up. And I'll do another demo of creating your own plugin type. Now, I think with creating plugin types, you see a lot of them in Drupal. And they're usually wrapped around user interfaces. And those are baked in with configuration entities. And it's very similar to config bags and then also just having plugins on the side. So it's kind of confusing. The lines are pretty thin. So I try my best to avoid talking about configuration entities, because they will get confusing. And they're basically a talk within themselves. But I'll create a plugin type. And it's going to be basically headless. And you'll see a few lines of code there of how to define your plugin type, where to discover other plugins that are implemented, and then how to actually use it. But the business logic that you decided to do with them, I guess the business logic system itself, that would be an exercise for you guys to do on your end. So moving on, what is a plugin? The way I define plugins, at least within the scope of Drupal 8, is a discrete class that executes an operation within the context of a given scope as a means to extend Drupal's functionality. So we think of extending Drupal's functionality in many ways with, hey, I'm going to invoke a hook, or I'm going to implement a hook. Sorry. Sorry. So yeah, a plugin is a discrete class that executes an operation within the context of a given scope. When I say within a given scope, there's always the evils of within Drupal. There's anything you can do in almost any place. And that can have some confusion with developers that are not really sure with where to put in their code. So oftentimes, you put in some crazy stuff inside of hook and it, or a hook page build, or whatnot. But the idea with the plugin is that it kind of enforces the best practice of only doing a thing within a small scope, maybe within a method. And you're only given a certain amount of parameters to work with. And if there's anything else that's outside of that, a global, for instance, you might start to feel a little uncomfortable with doing that, because now you're pulling in objects out of thin air, and you start to feel kind of a strange, cringy feeling. But within plugins, just the whole good behavior here or the best practice is that you have a limited scope and limited to what you can actually do. And moving on to that, plugins themselves, they should really only do one thing and do it well. Kind of like a UNIX philosophy where you have one utility that just does this one operation, gets a simple input, sends out a simple output, and that's it. And it sends it out to the next thing that does its job really well. You should kind of treat plugins in such a way as well, where you're not trying to achieve too much within some sort of an implementation, or you have all your logic within a method as if it was a controller that has a lot of application specific stuff. Plugin should be abstracted. Plugin should be something that can take in configuration and then do stuff with it, but not too much. And then also, I mean, since you can pass in plugins, so you can pass in configurations within plugins, you have this idea of every instance could have different config. It could have different states. And regardless of what the states are, the plugin can handle the operations within them. And so you have a sense of reusability. And you can have a single class that you've created, a single plugin. And you reuse it across different places on a site, or even better, reuse it across different sites within all of your projects. Because you're just passing in different configurations to do things with it. Yeah. I'm kind of a visual guy. So I like to see what plugins are, at least in a practical sense, where you've seen them. So I'll show you a few screenshots of a Drupal 8 interface to kind of get you familiar with what plugins are and how you could identify them. And keep in mind those three principles about plugins, that they're instantiable, configurable, reusable. They do one thing, one thing well, and that they're limited within scope. So here you see the blocks UI, where everything on the right well is essentially an instance of a plugin, search forum, user login. Each one of these are implementing a block type, or just a block in itself. In Drupal 8, you can configure these and instantiate them. It's not like Drupal 7 where you can only use one block per region. You can pull these in and add state to them, basically adding configurations to it. And they're very narrow in scope. You're not supposed to have content within a single block. Another example of plugins are field types themselves. Field types prior to Drupal 8 were implemented by several different hooks, but still conceptually, it's supposed to be a single thing. You're describing a schema, validation, et cetera, et cetera. And each one of these plugins or field types can be used across different sites. It's not like it's application specific. Likewise, with field widgets and field formatters, actions themselves. So take for account a limited scope. Each one of these actions are supposed to be expecting just a single user or just a single node. It's only supposed to operate one single thing. So again, it's small in scope. It does one thing and one thing well, and can be used across different projects. And here we have image effects, also implemented as plugins. Input filters. They process text, and they do a very specific thing. And they pass it on to the next input filter. And this is kind of similar to input formats. However, input formats are configuration bags, configuration entity, which basically houses all the configuration that you would pass into input filters. So that itself is kind of like a container. But the input filter itself is the plugin in which it uses to process some text. And now in Drupal 8, we have a CKE Editor, or CKE Editor buttons. So each one of those are implemented as plugins. And entity types, et cetera, et cetera. So you see that plugins are everywhere. And if you can see this with your binoculars, you can see that there's a lot of plugins in Drupal 8. And you have a little bubble off into the top left. Those are actually view plugins. So since they're baked in as classes, your plugin types can be extended and build other sub-plugin types with them. So you'd be familiar with all the different views plugins that are available. That's essentially it. And almost everything here that you see in Drupal 8 interfaces are baked in as plugins. And you might be thinking to yourself, all this seems really, really familiar to me. And rightly so. A lot of these in the past have been implemented before with some sort of an info hook, whatever info. But now essentially that has been deprecated for the most part in Drupal 8. And we have different means of discovering these different instances, discovering these different plugins. And that's all things to the plugin system. So yeah. So before we move on to the actual how to create plugins, I'll basically talk about the benefits of them. One thing I really like about Drupal 8 is that the definition of a plugin and the implementation are all baked in together. If you had ever implemented a block, you know that you have to have a hook block info and then a hook block view and then a hook block, et cetera, et cetera. And all these different procedural functions describe this one thing. And it gets kind of hairy. And it's a little more difficult to work with, especially if you're providing more blocks within those hooks. Because then you have to do switch cases, and it gets pretty ugly. But in Drupal 8, since every plugin is defined as a class, we can use actually annotations to describe and declare what each one of our plugin types do without having to have a mess of code all over the place. So I'll talk more about annotations than why they're so cool. Secondly, plugins are lazy loaded. And this is one thing I never really liked is that if I had to do some sort of operation or needed to have some globally accessible service or whatnot, I would have to include it inside of the module file. And that made me feel uncomfortable because that's a lot of PHP code that gets cached in and has to get loaded up. But as far as Drupal 8 is concerned, your plugins won't exist. They'll know about them as far as where the definitions are and where to find it, but won't actually load the code until it actually uses it. So they are lazy loaded. And the more plugins you add in, there's no performance costs at all because they're just dormant in your code, which is really cool. And also code is unified. I mentioned already the hook block info and hook block, whatever. Fields were also a huge pain in that sense whenever you wanted to create a field type or a field widget or a field formatter, you'd have to write seven or eight different plugins to describe just this one thing. And again, if your module provided multiple types of these, you'd have to mess with cases, switch cases, and so that gets kind of hairy as well. But in this case, all the code is unified inside PHP class methods. So you don't have that separation and you don't have to do weird stuff to pass in state across all of these different methods or all these different hooks that you would apply. And just by the fact that plugins are implemented as classes, you get the object oriented aspect of it where all your plugins are extendable. I like some of the widgets and the field types that are provided by Drupal Core. But it's really difficult to say, I like this image type or this image widget, but I would like to add just one little thing to it. It's not a thing to just extend it and apply your own button to it or apply an extra validation to it. You literally have to re-implement the entire field type or re-implement the entire widget and pass in all the different validation and all the different hooks, and it's just god-awful. But with Drupal 8, you could extend that class and just provide in your own extra things with it and still get the benefit of all the existing validation and all the existing formatters that exist for it. So there's a big one there with the reusability. Because of course, they are classes. And also because they're PHP classes, we can now leverage interfaces where we can say, hey, this plugin type that's provided is supposed to implement said number of methods and that's a contract essentially. So if you have any kind of plugin that are implementing a plugin type, you can rest assured that they're gonna have the methods that are promised within the interfaces. So there's no guessing of like, hey, does this plugin support this operation? Do I have to check for this if method exists? It's going to be there. So a little bit of developer love there. That's a great thing. And because everything is using similar interfaces, this implies the fact that you can actually swap these classes around because they all, basically provide the same inputs and they give out the same outputs. They have the unified interface. So what's to keep us from actually swapping out these operations? So yeah, plugins are now swappable and you can move them around interchangeably. So that's a great thing. And reusable across different projects. This is very true because of course, all of this logic is baked into plugins and shouldn't necessarily be tied into application logic. You can use them within different sites. It doesn't have application logic baked into them. If anything, you'd be passing in configurations to them. And I feel that the plugin system kind of enforces or encourages that kind of pattern. So kudos to you. But you could have always done this in Drupal 7 and in some cases in Drupal 6 as well using T-Tools. Actually, I was very involved with T-Tools and just its mechanics with having plugins and having classes as ways of implementing these plugins. And of course, if you're careful with the way you're implementing these things, you could always capture these plugin types into your own custom module that can be reused across different sites in your team or even within just different projects and basically passing in the configuration state into these plugins to do different stuff. So moving on, core concepts that are baked into Drupal 8 that are kind of a necessity to understand when we're looking into how to implement these plugins. So we're talking about dependency injection, service containers, annotations, and PSR4. I was in an assumption at first that I had to learn symphony in order to understand Drupal 8. And so I spent some time in there for a while and realized there's a lot of stuff in here that's not necessarily applicable. It's really good stuff to know, but it boils down to really these four concepts that are required. And then everything else just kind of becomes Drupalisms that are using these concepts as helpers. So the first one we'll talk about is PSR4, which is an auto-loading standard. If you're not already familiar with what PSR is, it's a PHP specification request. Essentially, representatives of all the PHP frameworks get together and they discuss what are some patterns that we can use across the board where we can work interoperably and foster these standards. PSR0 was the first auto-loading standard. And up to maybe a week ago, I think Drupal 8 converted at least all the module code into using PSR4. What that is is basically a way for auto-loaders to know where to find your classes. So there has to be some sort of a structure in which everything follows. Part one of this is having a fully qualified namespace. And it should be in the format of your vendor namespace and then a sub namespace and then finally the class name. And secondly, your directory structure is supposed to match at least the sub namespace for this plugin. So given an example, say we have a class called my block and the fully qualified namespace for this is Drupal my module, plugin block my block. And within this class name, it's just called my block, but it's encapsulated within the namespace of Drupal my module, plugin block. Previously in the Drupal 7 and below, we had to prefix all of our classes and all of our modules with the module name. And that worked out pretty well. It prevented namespace clashes, but this is a much more elegant way of achieving the same thing. So given this class, how can the auto-loader find it? It depends on where you put it. So here would be the directory structure that's baked into it. Within your modules directory here, my module exists. And you have a source directory. And in there, that's where the sub namespace is kind of mapped with the directory structure there. So we have plugin, block, and finally, myblock.php. There's different variants of this, and of course there's a lot more explanation to this. I have a couple links at the bottom for phpfig.org. And you can see all the different PSRs. That's specifically this PSR4, which describes it. And more germane to Drupal actually is another link that talks about how Drupal uses it. All the pros and cons, and possibly links to other issues where there's a lot of drama and why we should or shouldn't use it at all. And also as a reminder, we have Sprint on Friday. So there's a lot of issues in the plugin system, about two pages full. So if you wanna jump in, a follow Drupal Mentoring on Twitter, check out Austin2014.drupal.org slash Sprints and see what you can do to help out. Make this all possible for everyone. Anyway, annotations. So annotations is essentially just metadata inside of doc blocks. And a lot of people call it doc blocks, just multi-line comments. But actually they're not. A regular comment is in a sense a single line comment so you have your double slashes in front. And so that's cool. And that's actually ignored by opt-code cache. And we could also have multi-line comments with the forward slash and the splat. But again, that's still ignored by opt-code cache. But sometime around, I think PHP 5.1, a reflector class had the ability to actually introspect and read its own doc blocks, which is what we have to describe a lot of our functions. And so that kind of opens up the ability to have something that's parsable inside of the doc blocks and pull things in. And you've seen this a lot before, these markers where you have an at symbol. So like at deprecated, at to do, at param, at return. Those are all markers. And now with the PHP 5.1, with the reflector class, we can actually read those in and parse them. And essentially that's what annotations are. Not necessarily code. It is just basically declaring certain values so you're parameterizing your markers. And this is a custom marker, so at my plugin name. And I can pass an arbitrary list of values and keys. And so yeah, the annotations can find that within the code. And this is essentially how a lot of the classes can define themselves. You don't have to have a separate info hook. You can just have that all baked into the comments. So very easy, very simple. There's a very interesting slide deck. Have a link at the bottom on slideshare.net that describes the history of annotations, all the different annotation engines that exist in PHP. And why they're so cool. So yeah, that's it, annotations. Moving on to dependency injection. People describe this as an inversion of control and I really didn't get what that meant for a long time. So here's an example. And this is actually a problem that I struggled with for a while with some of my own previous projects. Say I have a class named sum class. And inside of the constructor, I'm using another class to kind of help me do some stuff. And so inside of the constructor, I initialize a handler. So this handler is a new handler. And so there you go. I instantiate my sum class and then inside of it it does some stuff with this handler. But say for whatever reason, someone else needs to use this sum class and extend it and do some other stuff. And within extending it, they need to modify the handler that's instantiated. But the problem is that this handler is actually instantiated in a lot of different places within this class. And so now in order for them to do a simple extension, they have to look for all the different methods that are instantiating these classes, override those and implement their own or instantiate their own handler. And this gets very frustrating and very difficult to basically manage. The inversion of control here is basically described that the calling user is instead defining what the dependencies are. So in my sum class, if I had written in such way where in the constructor, I'm just accepting the handler object and then I just simply assign it within, then that means that the calling user can just invoke their own or instantiate their own handler and pass that into my sum class. So now who has control? It's not my class anymore, it's the developer. So that's all of what dependency injection is in and out. There's nothing more to it. And there's a link posted at the bottom that kind of describes this with using better examples. But yeah, that's essentially what dependency injection is if this ever confused you. Now you know. Moving on to this though, there's something called service containers. Service containers, they do something pretty cool where they auto instantiate these classes for you, but they have to be service oriented, implying that they don't have states, they don't have configuration baked in. They're just classes that do things and they do things in a global scope. And the thing with service containers is that you can register all the dependent classes within them. So for instance, the example that we had with the handler and the sum class, you would basically set up like this, handler equals new handler, instance equals sum class of whatever passing in the handler into that. But instead of having to provide all the classes that are dependents, because keep in mind, new handler probably had dependencies of its own. And those classes that it depends on probably had dependencies of its own. So you could imagine you would have to instantiate a lot of classes and pass them all in until you could actually have the instance that you want. So the idea here is that within service containers, we can define what those dependencies are. So in my case with the sum class, inside of my module.services.yaml file is where I can basically describe my service. And I'm providing a machine name, mymodule.sumclass, and I'm defining where the class can be found, what the namespace is, and I'm passing in the arguments. And my arguments are pointing to something called at handler, which is the machine name of the dependent service container. And the thing with this is that somewhere out there, there is a handler service that's being described that it will in turn describe its own dependencies. And it's just like daisy chaining across the board. And when I want to instantiate a sum class, all I have to do now is say, Drupal, give me the service, mymodule.sumclass, and it will go ahead and do the instantiation for me. In fact, reading off the services files, Drupal will generate PHP code that has all these instantiators inside of an array. So it's pretty tricky how it does it. It's pretty cool, actually. And there's a link at the bottom, basically talking about Pimple, which is a library of PHP that does the service container stuff, and it kind of talks about how the mechanisms are, like what it's actually doing internally. But yeah, so instead of you having to instantiate all your classes on your own, you can just register them and then call it in at the very end using Drupal.services. And this is okay because defining what classes you're going to depend on isn't necessarily a runtime decision. It's something that you can have baked in and registered. And if you ever had to have the same class but have different dependencies, you can just register another service itself. So yeah, that's how you can instantiate things with service containers. Very cool. So, quick review. Talk about what plugins are, basically what their benefits of using them, and what the use cases are for having the different instances, configurations, and whatnot. Brief through some of the core concepts using Drupal 8, and now it's time for some demos. So we've been looking at some source code. Let me mirror this real quick. Oh, that's cool, awesome. Alrighty, so here I have a demo module called Pilot. And as we mentioned before, per PSR4, we have all of our plugin implementations or all of our classes, so basically the auto-loaders can find them inside of the source directory. You notice inside of the module, I don't have any info hooks at all, and everything is just found within source, plugin directory, and then all the different plugin types that are out there. So, what differentiates a class from a plugin? Essentially, it's a class that's within a certain namespace, and that implements a specific interface. So I'll start off with the image effects plugin type. So this is what's desaturating or resizing your images. And we have something called pixelize. So, first off, it's inside of the directory, source, plugin, image effect, and then my plugin name is called pixelize, so therefore there's my class. It's extending the image effect base, which is implementing configurable image effect interface, which just means that, hey, I have this image effect, but I also have a way of basically defining configurations for it. So that means I can implement stuff like default configuration and get form and validate form, or et cetera, et cetera. What's very important is that I'm defining this class within a certain namespace. Is that large enough, by the way? Uh-huh, it's on GitHub. Postaling to it, in the comments of the talk. So yeah, the namespace is Drupal pilot, so that's the vendor namespace. The sub namespace would be plugin image effect, because that's what the image module defined it to be. For every class that I use, I have to go ahead and say, hey, use this one, so that way I can basically shortcut. So again, I'm assuming PHP experience with you guys. And the info hook that would have been is actually now within an annotation. Sure I have my description, but the most important part is I have an at image effect marker that describes, hey, this is my identifier, this is my ID, pilot pixelize, and this is the label that I want. And we're even using a translation marker that says this string right here is translatable, so pass it through the t function, do your thing. So ID, label, description, bam. And just with this, I should have it available in the UI. Of course, the thing is with the contract that I'm implementing a specific interface, I have to provide the apply effect. And I could have extended something like desaturate or resize and do some additional stuff to it. But this is just extending off the image effect base, which essentially does nothing. In here, I'm just going to be pixelizing an image. And I'm saying, how much do I want to pixelize it by? And do I want to use advanced controls for this pixelization? And here is a quick form API insertation, so we have get form. So that's essentially it. And I will show you what that looks like in the UI. So we have the image style. Let's add a style. Call that blah. Because I have this information inside of the annotation, I should be able to see it here, pixelize. Awesome, so let's add this. Since I am implementing the configurable, what was it, configurable image effect interface, I have the opportunity to add in a form. So say pixel size 40 would be fine. I don't want to use advanced pixelization effects. Go ahead and add it, boom. So that's saved into a config entity. And let's go over to the article display image and say we're going to use that image effect. Blah is in there, go ahead and update that. And then say, and it does some content, some article content. Stuff's adding an image, Super Mario. So this Super Mario image is actually a very realistic rendition of Mario where Yoshi is a dinosaur and Mario's a pudgy fat guy. Let's go ahead and save and publish this and see what it looks like through our pixelization. Hey, it's what we're used to. Pixelize Mario. Pretty cool, huh? And all I had to do is apply effect method, essentially. And this is all contained within a single file. So no hooks elsewhere. It's not cluttering up any of the module file. It's just by fiat, just because it exists inside this name space, it would be picked up. So this is essentially a simple plugin that just does this one thing. However, if you guys are familiar with C-tools, there was an opportunity to create child's plugins. For instance, there was one class or one plugin type that handled all of the blocks. Or there was one plugin type that handled all of the relationships between entities. But it had a mechanism to basically look through everything inside of the schema, find relationships between things, and it can create spawn child plugins based on what it found through some sort of a traversable set of data. And so now we have child plugins, and there's several of them, with their own instances. However, it's still using one single class to do all the operation. Injurably, we call that derivations or derivatives. So I have another image effect called Colorize. And this is super simple. Basically just applying a tint based off a list of colors that I had to find prior. So, say for instance, and I'll jump into the module file, here's an array of colors. So I have lime and hot pink. Imagine if this was a configuration that you built yourself, where you can just add another, add another color. And then with that data, our plugin for colorization can just basically pull that in and apply the effect that you provided with the state. So the thing that defines this plugin from being a derivative is that we have a derivative right here, a parameter, in which we're defining where our class is to do the derivatives itself. And that can be found inside a plugin derivative. And this is what's basically, it's a glorified for each. And for each one of the data that we have stored in the database, or as you can see, I just had it as an array, it's going to derive the annotation data that we would have had inside of each one of these classes and auto-generated for us. And all we're really doing is passing in an ID, a label, and then the color, and going ahead and returning that is here are all my derivatives. And going back to the original plugin, it's just reading that data in through the plugin definition. So my derivative class is providing the values for the plugin definition of all these child plugins. So just with this one class, I'm able to do multiple things. I'll hop back over to the image style blah and add in a colorized to hot pink. So here, again, we have colorized to lime, colorized to hot pink. And this is coming through the derivatives, which is, again, a glorified for each loop. And I'll say, I want this hot pink. And there we have now a hot pink pixelized image. So I'll take styles, come back over here. Now I have a pinkish pixelized Mario. Cool. So jumping into something simpler, something very common, is how to define a block. And many of you have defined blocks before with hook block info, hook block view, and hook block, et cetera, et cetera. This is all the code that's necessary to define a block. And again, following the same patterns. It uses annotations to discover where all these plugins are and what they do. All it needs is an ID, an admin label. And then, again, really just extending the block base and then whatever it is that you're doing, your, I guess, your custom logic, you can just override the methods that are necessary. In this case, in the method build, I'm just providing a little snowman in the markup. And so if we jump into the block layout and say, I'm going to drop the, where is the snowman? There you go. Add in an instance of the snowman. If I had implemented some configuration, I can go ahead and drop that in. I did not. I'll go ahead and save this and put it inside of the contents of the page. So save that, and there's my block. Think I saved, not sure. If I come back over here, boom. We have an indifference snowman on the page now. So very, very easy to implement a block. Cool stuff. So yeah, those are some examples of how you can implement plugins. We did this already. Yes, so now for the even drier section of that. Plug-in system internals. Okay, so if you are a developer who wants you to find some new systems, this would be important to you. Basically everything that is managing a plugin system is a plugin manager. And the responsibilities of this plugin manager are basically it's a twofold. First off, you want to find where all your plugins exist, where all the other modules are providing these plugins and then you want to instantiate them. So essentially the discovery phase and the factory phase. And then there's also an optional phase called the mapper, which would be if for whatever reason at runtime, you don't know what you want to call. So you need some sort of mechanism that says, hey, dynamic data, come here. Let me figure out what you wanna do. And then I can find what class I'm supposed to be instantiating and go ahead and pass that to you. And that is what the mapper does. So first off, the discovery classes that are provided in Drupal for the plugin manager. All it's really implementing is get definition and get definitions. So in a sense, this is finding all of the plugins that exist and it has very different mechanisms to do this. And I'll talk about each one of those. Again, the factory is just, it's a way of instantiating. So the only method that it implements is the create instance where we provide what the plugin ID is and then we can provide a state as well, the configuration. And then the mapper is get instance, whereas I don't know what the instance is. But here's some configuration, here's some options. You figure it out. And then come back to me and give it to the factory so then I could create the instance and go to town with it. So the plugin manager, all encompassing, is really just implementing each one of these interfaces. So now you have to just deal with one class, not necessarily three different classes. So for instance, you instantiated a mapper, or sorry, you instantiated a manager and you're saying, hey, get all the definitions. The plugin manager itself doesn't have to re-implements what get definitions means. Instead you can just assign the discovery property within it to a discovery class and just kind of proxy the get definitions itself. So when I say manager get definitions, really what it's doing is calling the discovery class to get the definitions for it and just passing that along. The discovery classes that are available are static discovery, which is, hey, I'm just gonna give you straight up all the different plugins that I want you to have and this is kind of cool for testing. So you don't have to implement plugins, just say here's an array of data, go to town. YAML discovery, which is what you often see with basically every YAML file that is in Drupal. Menu links, menu routers, contextual menus, services themselves, et cetera, et cetera. Those are essentially providing data for plugins and the way the systems will find them is by looking through all the YAML files. And hook discovery, which is what we're all used to, is something hook info. And at this point, I'm not even sure if that's available anymore. Haven't checked. That might have been deprecated, not sure, but hey. But the most common one is the annotation discovery, which is where we put in these annotations baked into our classes and that's what most people are doing and it looks really cool. So maybe that's the method that you wanna try. So what does it look like when you're actually pulling in this data? Inside of the constructor for your plugin manager, you can do this. Say the discovery property, it's going to be an annotated class discovery. How do I find this stuff? Well, the first parameter is here's the sub directory that you should be looking in for the namespace and here's a list of all the namespaces you should be looking at. Essentially this is all the modules that are enabled. And then how do I know which marker I should be looking for? Well, that's where you would have to implement an annotation class and you just specify what the namespace is for that. So Drupal core annotations actions in this case will be looking for anything with an at action marker inside of the dock blocks and assuming it's within a certain namespace, bam, it finds all the plugins. So again, this is what differentiates the standard class as being a standard class or a plugin that can be used. Hook discovery, this is basically invoking a module invoke. So if I'm saying, hey, here are all the modules that exist and I'm invoking element info, it's basically calling out hook element info or looking for everything that implements that. So that's something that you're very familiar with. YAML discovery, this is the file name that you want to look for and again, these are all the modules that are enabled. So look through all those directories and grab all the stuff. And then static directory, static discovery which is again just passing in an array, only really useful for testing. And on top of the existing discovery classes, there's something called decorators which is a lot like alter hooks. We'll jump into each one of these. So derivative discovery decorator which is supposed to be trying to remember. For every plugin type that you define, if your plugins are providing a derivative class within the annotations, I'll go ahead and pull in those classes and find all the sub or child plugins that exist for that plugin. So if you needed that or wanted to provide that functionality inside the constructor of the plugin manager, you would pass this in. Alter decorator, again, this is invoking something that's very familiar to most of us which is calling out hook action info alter or whatever hook that you pass in for the discovery. And then a process decorator would be something like, hey, I have all the plugins that I want even through the discovery decorators. Now I want to process each one of these hooks to provide some defaults or maybe massage the data a little bit and I want you to call this callback to be responsible for doing that. This is where you would provide that. And then a cache decorator which is a kind of interesting way of thinking of this. If we're passing in all the plugins that have been discovered, how do we cache them? We can patch that in through this discovery or decorator, which is a cache. And instead of pulling in plugins directly from Drupal source code, it would just pull it in directly from cache. So here you define what your cache key is and it would be in charge of just caching that on the fly as it's discovering it. So all of these, I guess, processes to find plugins or even to find things that are defined in info hooks. Something that you've all been familiar with but now we have classes that basically walk us through the process without us having to do much. So that's the discovery phase. Now the factory classes, what we have available are just the default factory, container factory, reflection and widget factory. And in a sense, what this looks like when you're implementing it is you call the plugin manager and say, hey, create the instance. Here's the plugin ID that I want because now that I've discovered all the plugins, I have machine names and I'm gonna pass this plugin ID and pass in some states based on whatever my application is doing. I look at it in terms of this. When you instantiate a class, it kind of, it's broken down into two pieces. How you instantiate something and then the arguments they pass into it. Given that kind of context, now all the plugin factories that exist or factory plugins that exist kind of make more sense. For instance, the default factory, this is literally just calling new whatever the class name is. So that's a pretty straightforward operation and passing in the fixed arguments that you require in your plugin type. So that's very simple. But if you wanna do something different like instantiate your plugins with the container factory, just like we talked about with the service containers, there's a class for that. And internally, this is what it would be calling in. The plugin class create method and just pulling in the Drupal container object in which we can then grab in the ID that we want and passing some state into that. So yet again, a different way of instantiating this plugin. The widget factory is something that's used with the field widgets in a sense. If you ever needed to have an instantiator with custom parameters, this would be I guess a nice way to follow suit. Where in this case, it's not just pulling in the config plugin and the plugin definition. Instead, it's passing in the field definitions and then the config settings. So it's doing its own thing here. Not necessarily useful, probably for your own custom code to extend upon it. But it is a good example to see how you can, I guess, have a different way of instantiating with the custom parameters. And the reflection factory, which is actually pretty interesting. The dynamic part here is that instead of having a fixed set of arguments, it's a dynamic set. So a reflection class would basically pull in the class and find out what arguments exist for it in the function signature. And based on what you pass in as configuration, it would go ahead and pull in these IDs into the parameter or into the plugin itself dynamically. And this is kind of helpful for any kind of plugin that you have an X number of parameters that can be coming in. This is especially used in something like autocomplete where you can have N number of parameters being passed in or even in, I think, entity relations where you can have, again, different parameters to define what these relationships are. So reflection factory is really good for that, dynamic arguments. And then the mapper class, which is the optional part, getting the instance. The way this works is that you're not really defining which class you're instantiating, nor are you defining what arguments to pass in, but you do have a set of options based on, I don't know, the state of your application. What it would do is it'll grab in some options and it'll go through some logic phase that say, hey, figure out what class would be most appropriate to do the process that I wanna do. One example would be the archiver where there's many different mechanisms to archive something, ZIP or TARGZ. How do we know which one to use? Based on configurations that we're passing in, it would decide what class is most appropriate to handle such thing. So for instance, looking at the extension of a file, then it would know, oh, this is a .zip file, so I'm gonna use the ZIP on archiver. Another example of this would be, say for instance, you have a REST server and you wanna process basically the response of a request. At runtime, you don't know what class is most appropriate, but you can look at the content type that the requester is accepting and you can say, hey, this guy wants to have JSON format, so let's pull in the JSON class and this will happen on runtime, so you don't necessarily have to have strange if else conditions inside your application. The mapper class should be able to handle that instead. So once it has the logic and figures out what it needs, it figures out what the plugin ID is and then you can just pass that into the factory. So this create instance, now I have that ID and again, you can pass in the options to define whatever configurations you need thereafter. So that is in a nut, all you really need, ah, I went back too far, concepts. Okay, that is all plugin manager is. But again, keep in mind this is headless. You're basically pulling in plugin data, so once you have that data, it's up to you if you wanted to have some sort of interface that goes along with it and again, that's the fancy part that you can decide what you want to do with. And then of course the instantiation, which is a very boring and simple thing, but again, there's different mechanisms of doing it based on certain use cases and whatever's part of a Drupal, pretty much helps you get there, at least half of the way. So I'll go ahead and create a demo of doing a plugin manager. In a sense, this is what the relationships look like. We have a person plugin manager, which is the fictional plugin type that I've created, which is a really lame example. It's extending the default plugin manager and that itself is implementing a lot of interfaces in which you can see in the green dotted lines. It implements the mapper interface, factory interface and discovery interface, of course. And then it also implements the plugin manager base itself. So it has now a contract in which there's a certain number of methods that the plugin manager is supposed to implement. Luckily the default plugin manager handles almost all of them and the person plugin manager really only needs to add in additional things that the default plugin manager does not provide, such as a cache backend if I wanted to or alter hooks if that's what I wanted to do as well. So when you define your own plugin types, there's really not a whole lot of custom code you have to write, but there is a bit of boilerplate stuff that you'll need to do. So let's drop into code again and we'll talk about each one of these. So it starts off with a person plugin manager. Actually let's take a step back. I'm defining a system in which I have a plugin type that's supposed to do certain operations. In this case, it's just something simple like get a person and there's really only three parameters in which I play with. A person's name, their age, and then how they do their greeting, assuming they're like multilingual or whatever. So this is the interface in which all the plugins would basically extend. I will provide a person base, which is a class. And again, these classes don't necessarily have to exist in any specific place. You define what these namespaces are. But in here in this case, I'm just using Drupal pilot plugin because that's a bit intuitive. My class person base is extending the plugin base, which again provides a lot of other stuff for us, but I'm implementing the person interface. So again, this is what differentiates a class from a plugin type is that it's implementing a specific interface. And here I'm just providing the defaults. Get name, it's just gonna grab the definition and grab the name property. Get age, I'm grabbing it from the definition, grabbing the age property. And then the greeting, simple. Hello, my name is this and I am this many years old. So very, very straightforward. So that defines, I guess, the skeleton of what my plugins would look like. Now I need to define a manager to basically handle doing the discoverability of these plugins and all the instantiating of these plugins. So now I jump into the person plugin manager PHP file. So this is now three files that we're working with. Again, I'm providing the namespace Drupal pilot plugin because that's intuitive. All of this used stuff is just as a shortcut for not having to define the classes over and over again. But in my person plugin manager, I'm extending the default plugin manager, which itself is in charge of really pulling in or implementing a lot of the methods that are necessary since we are extending or implementing a certain interface. So that handles a lot of it. I'll go ahead and move this over so we can see more of the code as possible. Normally, all you wouldn't want to do is just call the parent constructor, which is a default plugin manager, which does a lot of stuff to it. We passed in what sub namespace we are expecting our plugins to exist in. So in this case, plugin person. The namespace that's passed in, which is all the different modules that are enabled at the time, where all the different places where I can find plugins. Module handler, which is just a bullet plate, describes all the different modules that exist. And in this case, something that is special. I am using annotations to find all my plugin types. So I'm saying, hey, where's my annotation classes? Internally, so it's not so opaque as to what's happening, the default plugin manager is doing this. It's defining a discovery property and it's setting that to the annotation discovery. And so this is where the sub namespace and the annotation path actually goes to work. Go find all of the markers that have at person under this directory using that class. And then I'm again, reassigning discovery, but this time passing it through a derivative discovery decorator. In this case, it's a container derivative discovery decorator and which if I had any person classes that defined a derivative class in itself, it can go ahead and spawn child plugins. And then I'm defining the factory, which is just a default container factory. You don't have to do anything special. And then also just providing the module handler. But this is what the default plugin manager does just for visibility. But in a sense, this is all I really had to do. The only custom parts that I wanted for myself is I wanted to provide an ability to have a hook pilot person info to be invoked. So I can go ahead and change up the info if I had another module that wanted to do that. So this is where I would invoke that. And since I wanted this stuff cached in the backend, I'll go ahead and implement this set cache backend and provide a cache key in there itself. So that is a plugin type that is a manager and that's essentially on all I need to do. All the heavy lifting is really provided through the default plugin manager to get all the definitions to instantiate the definitions or instantiate the plugins rather. But I did mention something about annotations, a custom annotation. So there's one extra thing that we have to do. Inside of our source directory, we have annotations and then a person.php class. And this is very, very simple. Where we're extending the plugin annotation. And this is where we define what our default parameters would be for I guess the hook info, what we're all used to. And in this case, I'm just looking for an ID, a name, and an age. And providing some defaults also in here. And with this, now I can use doctrine to basically find all of the classes or all my plugins looking for that app person marker inside of the code. And one last thing is that since we've provided a plugin manager and this is itself a global service in which we can use globally, we basically register it as a service. So I give it the machine name of plugin.manager.pilot.person, right? I say where the class is and I define basically what my arguments are, but I don't necessarily have to redefine what my arguments are. I can just reference the parents, which is the default plugin manager. And so we'll use whatever it defined as its arguments. So I don't have to do anything other than just say, hey, here's my parents. And whatever arguments you pass to it, I want you to pass it to me. So those are all the four components that we need to actually generate this. In this module, I'm providing person plugins. So this is what other modules would be doing. Essentially, the annotation portion of this at person. I'm defining the ID, the name and the age, and I'm not necessarily doing anything within the class itself because the defaults are fine. The defaults that I'm putting in the person base are just completely fine, for Bob at least. For Dave, Dave is gonna say something a little different. Say, hey, my name is this. But again, the whole idea of having your plugins parameterized and having all your info baked in together is all right here through the annotations. So we have those two plugins. Other modules can provide their own plugins as well. And to actually pull this in, we'll look at form that I have, which does the collection of these things. So we'll get this stuff out of the way. All right, so given that now we have the class for the plugin manager and all the additional boilerplate stuff that was necessary, we can now actually use the plugin manager using a service container. So I'm saying, hey, I want the manager and grab that from the Drupal service container. Here's the machine name that I defined within the services.yaml file. With that, it would figure out all the different classes that it needs to instantiate and do its whole dependency injection thing, and in which case it would basically instantiate the class for me. So now this is a manager class, the plugin manager that I've created. I want to collect all of the plugins, so I say, hey, manager, get all the definitions, and here I'm just printing R all of these onto the page. And I want to pull in an instance specifically. So in this case, you would probably want to do something fancy, like a UI, where a user can click through things or whatnot, and you have a list of all these plugins in which you could probably attach configurations to them or whatever. But once your logic defines which plugin you want to load in, you can go ahead and just create the instance and pass in the machine name that you want. And now that we have the instance, the actual plugin type with optional configurations within it, we can do stuff with it. In this case, I'm just calling out the greeting, so it's gonna say, hello, my name is whatever. And that is all. So let's navigate to the page that's providing that. And that is, all right. So in this second, I was able to discover where all my plugins are through annotations. I was also given the opportunity to have derivatives, child plugins for everything that's instantiated or everything that's defined as a plugin of person. And in the end, I'm really just collecting it and caching it, and now I'm just displaying it right here. And which I've only found two plugins, Bob and Dave. And you have all the plugin definitions in here. Secondly, if you remember, I pulled in an instance, a specific instance, Bob, and I'm just saying, get the greeting from Bob. So you come back over here and I'm printing that out itself. Hello, my name is Bob and I'm 26 years old. And I pulled in that data from the annotations that I defined. So again, this is very, very simple and boiled down to its simplicity in which now you can define what your UI would look like or what your business logic or what the purpose of your system is to pull in this data. But now you're pulling in these classes, figuring out a way of which to pull in which specific plugin ID and then doing stuff with it. So it's up to you to decide what it is you're gonna do. So very simple stuff, three lines. And I was able to have a plugin system or at least actually working with it. So yeah, cool way. So my final thoughts. There's a lot of implication that comes in with plugin types or just building plugins in general. And a lot of it is, at least through my eyes, a way of segmenting out operations, functionalities into their own little silos in which they belong. It's really easy to have any different opportunity to drop in code and do stuff with. For instance, hook page, preprocess page or hook page alter or whatnot. And it gets pretty crazy in which all the opportunities you have to do some really wild stuff. And it becomes unmanageable and becomes very far away from, I guess, the metaphor of life and how we think of things. When you think of how certain operations in life can be separated out into smaller segments into their own little silos, you can swap out actual mechanisms to do things. And your code should actually follow suit as much as possible to real life metaphors. I think that would keep you more aligned with the reality of writing code and doing things that are more, I guess, friendly for the product owners or just the way a system works. And again, plugins just kind of follow a certain best practice that encourage you to do better code and not necessarily just like match things together. I envision a lot of the development moving forward to be not just using like a big blob of custom code in your site, but really just abstracting as much as possible very specific operations into plugin types in which then you can nurture in which you can then extend upon because you'll have these different instances, the same code used over and over again. And now you have more eyes and more use cases on it in which you can discover bugs and really put more care into the code that you put in. So I feel this is a good kind of segue into a happy way of coding or at least a better work-life balance in the sense when you think about it. So anyway, that was me. Please evaluate the session. There's a link at the very bottom for you to do so. And if there's any time, I'll go ahead and take in questions now. Thank you. This might be a little rudimentary given the depth that you just covered in this subject. I'm honestly not, I'm still not sure I get quite what an interface is or what it's meant to accomplish. Yeah, so in Drupal 7, it's all functional, which means that there's no contract in which a certain function has to do something. There's no guarantee is what the input or what the output is other than the arguments that are passed in. But with an interface, a PHP class interface, you define what are required methods that a class needs to implement. So anything that is implementing an interface, if you don't have those methods within it, PHP will fail and it'll say, no, there's an error. We cannot process because the method that is supposed to be implementing this interface is not doing its contractual agreements. So this would include the parameters that you pass in, the type of parameters you pass in, and then also just the existence of the methods. So that is how a method or a class can implement an interface by providing the methods and also ensuring that the arguments that it's accepting are the same that the interface had to find. Thank you. Thanks for this wonderful talk. It was really great to get a in-depth discussion of plugins and see how they operate. Thank you so much for that. I feel like I'm still trying to wrap my head around what the architectural reason for using annotations is. It seems like we're taking comments and now using them almost like properties on objects or like static variables. I'm just trying to understand why we would start using comments in what seemed to be almost like we're doing the programming in the comments. Yes. So if you could elaborate on that, I appreciate it. Yes, so it's not code inside of the annotation. It's kind of like saying, oh, we have YAML files now and we have code inside of YAML files, which is not necessarily the case. It's all declarative. Basically provide a value and a key and that's all annotations they're doing. Why use those? Because it's baked into the class. It's a convenience, but maybe it does make it harder to debug because where are these values coming from and I have to dig deep into like what doctrine is doing to grab that data. But you don't have to use annotations. It's completely optional. You could continue using info hooks. Hopefully they're not deprecated at this point. I haven't checked. But there are different mechanisms of discovery. Annotations is just one of them and one of the more popular ones. The slide link or the link that's in that slide in particular has another slide deck that describes why annotations are useful and kind of defends both sides of the argument. So looking into that would probably lighten things up a bit. Thank you. Hello, Helior. Thank you for giving us talk. I watched a YouTube video. You've given a talk at a different camp and this is a much better version of the presentation. That was great. I was gonna use the word talking actually. I'm conflicted though. I could see plugins as a solution being a great solution for plugging into an existing bit of architecture. Some things like file for field for matters and blocks and things like that. What I'm not getting is the cross section between using a plugin for the solution and using an entity for that same kind, solving that same kind of problem. Can you explain where it conflicts with entities and how to resolve that problem? There's a lot of overlap actually with config entities. And entities in a sense are basically a collection of configuration and it would use plugins to do things with it. So think of plugins as being the operational part of things to do singular actions, to process data, to do stuff but doesn't actually hold data. If anything these plugins could have configuration that modify how it does things but doesn't necessarily store a node. It doesn't persist any kind of entity. And you can think of entities as being basically like if it has a table record, right? If there's a row inside the database then it would be an entity. But plugins don't necessarily operate that way. So for me that is the distinction between where plugins work, processing data and entities would be basically persisting data and having like load functions and the whole crud stack that's baked into that and everything else that goes along with it. Yeah, that helped a bit but that makes blocks more confusing. Yes it does. I can kind of help clarify that if that helps a little bit with an example. Is that okay? So think about views. In the views UI, you assemble, you take a bunch of different filters in a bunch of different fields and you have several displays in the view and you put them all together to make one thing, right? So each of those individual little things, each kind of filter that you add is defined by a plugin. The plugin contains the metadata about it, a definition of what the thing is and then an API for how to do the thing. But what it doesn't contain is what you just configured in your view. And so what happens when you configure a view is that the specific instances, the specific configuration data for many different plugins is saved in one configuration entity. So the entity is essentially just providing the storage for the thing that you just configured. Similarly with blocks. If I have like a, you know, his snowman block, I thought that was a very cute example by the way. If I have a snowman block, that doesn't actually create a block on my site. What that does is it says, hey block API, I have this block available, use it how you see fit as the block API. And then where the entity part of that comes in is when you are configuring a site and you place an instance of that block somewhere. So you're not actually, that the instance of the block is, you can have many of them right in triple light. That's one of the new pieces of functionality that's available. So you can take your snowman block and you can put it in the header and you can put it in the footer and you can put five of them in the sidebar if you want to and configure specific things with each of those. Then those block instances would be stored as configuration entities and the configuration system to be deployed to other sites or whatever. So that's where the distinction is entities are about data, whereas the plugin is about defining an API for something in the way in which you can create things. It's not necessarily coupled, but that's how the two interact frequently in core. Does that help? So would it be fair to say that the only responsibility a plugin has in regards to entities is creating their instances? And then however that entity stores its configuration, if it's a content entity or a configuration entity, it does what it does. We need to take this conversation outside so we can go ahead. Okay, okay, go ahead. Yeah, that's why specifically I didn't want to talk about configuration entities. Hi, thank you for your talk. I had a question about when you are instantiating plugin type, the annotation, is there a, does something tell you if your syntax isn't correct or does it just fail silently? Specifically where? Like when your annotation is incorrect, like with the curly braces or within the comments about it. Instead of like a HookedBotGambo, you have your annotation. Yeah, because a doctrine does its own processing. It's parsing the string and it does its own thing. So in there it has its own validators and you'll get errors saying, hey, this is not formatted correctly. You're missing a comma or maybe you have this other thing that's not closed out. So it will do that for you through all the errors. Okay, thank you. Yeah. Cool, thank you guys.