 So my session is about state of hooking into Drupal. There are talks that we are slightly, slowly moving from hooks, the procedural hook system, to object-oriented hook system that we have technically borrowed from symphony, I would say. We would be using symphony to event dispatch a component for hooks in Drupal. So we're already on the track. How soon we can get there would mainly depend on the effort and prioritization of this thing by the community. So there are many other more important and I would say, yeah, important issues that need to be taken care of right now. So this may not get that much of an importance, but it surely will get there. We would eventually have the traditional hook system replaced with the object-oriented event system. So brief intro about who I am. My name is Nidai Smileshya. I am a developer at Acquia. It's been two years now that I've been with Acquia. I left college and straight joined Acquia. This is my contact info. I come from a beautiful place. It's Kashmir in South Asia. So I encourage you to visit us, read about it. We have a Drupal community, Drupal Kashmir as well. Google it and find ways you can come and contribute. An overview into what I am going to be talking about. Everything should be pretty simple. We'd basically be looking into what hooks are and why they were used and when do we use hooks and what for. Then we'd move to a state where we are currently, what's up with hooks and what about events. Since we are using events for some things in Drupal 8, we'd be looking at how we can do that for our custom things and what all we can achieve with events right now and where we are still using hooks in D8. Then we'd also be exploring a bit about symphony event dispatcher. Basically the same thing, how we can use and explore the symphony event dispatcher component with D8. And then as I mentioned, we are not yet there. There are still hooks are still there in D8. Then if you really don't like symphony or you don't like object oriented for reasons I cannot imagine, then you can still use hooks in D8. We'd be looking at that as well. So mainly a transition from Drupal 7 to Drupal 8 has been interesting. There have been a lot of changes for developers basically. It is, I think this talk is the benefits of moving from hooks to event dispatcher component is more about developer experience. So there are not too many benefits on the user front. Not, I guess, we cannot think of any performance reasons or something like that. But certainly developer experience has enhanced in a way. So Drupal 7 to Drupal 8 journey has been procedural to object oriented way. We have imported or used symphony components, dependency injection, service container, and event dispatcher component. Code has been a lot more modular in a way. Drupal itself is a modular system. But now we have sort of moved from making Drupal modular to making our modules modular. And eventually we are now writing cleaner code and we have enhanced developer experience as a result. So some things that have changed are like we have dependency injection for core services. We have event subscribers for hooks. We can now write portable code with the introduction of services and with the introduction of dependency injection, we can use other people's code. We don't have to worry about writing our own stuff, complex stuff. There are a lot of non-Drupal stuff, non-Drupal people who have created so many awesome things that we can simply use with services now in our systems. We can, as I said, use code from somewhere else. And unlike Drupal hooks, we can now write events that can be unit tested. So we can have objects and we can have mocked objects, mock dependencies, which can help us write better unit tests, which certainly wasn't possible with Drupal 7. So we'd be starting with looking into hooks a bit where we have come from, what purpose did hooks serve, and how they were executed. So we could then compare it with event dispatcher component. So this is exactly I was, while researching about this talk, I was going through previous talks as well. So there was a talk by Kim Pepper in Ruppelkorn Asia. So he mentioned this particular comment by Dries in 2000 when he started this hook system, the concept of hook system. The idea, I think, is to decouple components to be able to sort of, through the lifecycle of the program, to be able to run our custom code, run certain snippets of code at certain stages, or at certain events, to be able to decouple our event triggering mechanism from the event listeners. So to make them separate components, that is the basic idea of the hook system that hook system was started with. So to extend your functionality, your custom module would simply need to implement a hook. And when Drupal wishes to allow intervention, like when core wishes to allow intervention, or when you, as a module developer, want other module developers to intervene into your code or to extend your functionality, you'd declare a hook, and then the other module developers can hook into your module, or we can hook into core by implementing those hooks. So hooks are basically discovered in the module file. They are triggered by a naming convention mechanism. You basically declare a name, and then prepend it with your module name. And then whenever whichever module calls your function, implements a function with that name, prepends it with their module name, Drupal will call it. We'll look into how that happens and how it's happening now. So basically, there are many types of hooks. There is not a clear classification of hooks done anywhere, but I've tried to generalize a bit. Info hooks, which are used to declare metadata, as you can remember, we used to have hook block info and hook block view like things. So those info hooks, I think now we have gotten rid of them in D8. We have sort of removed all the info hooks. And this talk is mainly going to be about alter hooks, which were a common way to sort of let module developers to extend your functionality, or in a way, alter your data, whatever data you had within your system. And then if you wanted other module developers to intervene into your code or change that data, you used to have alter hooks. One fine example could be a form alter, wherein you had a form array. And then you were able to hook into other people's forms, and you were able to let other module developers hook into your forms. So the way we can create hooks is we have to decide a hook name, and then we, in our modules, and this is what core is also doing. Module invoke all, this is a famous function. This is how hooks get executed. What we need to do is module invoke all name. So whenever we just, if we have a hook, we decide on something, some name, we have hook, whatever. So if we call module invoke all, it would find all the enabled modules in your system and call all of those implemented functions. Module invoke all would find all the enabled modules off your site, and then execute all those functions. But if you want to execute a particular hook from a particular module, then you would need to call module invoke, then module name, and name. This is basically what's going on in D7, or what I can say what used to go on in D7. There's also a Drupal alter that I was talking about. You would basically need to do the same thing, sort of decide your hook name. That would be myData, and then you would pass in an array of data that you would want the users or developers to alter. Then the users would implement hook myDataAlter, and then they would get this data array as an argument, and then they could change that. This is how the naming convention mechanism that I was talking about earlier. This is how D7 does it, the hook system. If you can see the return call user funk array. Basically, we are deciding, creating the function name here, module, prepending it with the module name, and then a hyphen, and then your hook name, and then Drupal is calling all those functions and providing it with arguments. Similarly, with Drupal alter as well, we are just dollar hook, and the dollar type is your module name, and then, sorry, dollar type is your hook name that you would pass in here, myData, and then alter would be appended to it, and then it would be passed in your data array. An interesting thing would be was to manage the order of execution of your hooks in D7. So again, what Drupal used to do is use its own hook system to decide the order of execution of its hooks. So we used to create a Drupal alter module implements, and then pass in the array of the hooks, or this implementation array has all the hooks mentioned in it, keyed by the module name. So basically, if you wanted to alter the execution, if you wanted your module to a hook from your module to run before another module, so you would go to this array, you would implement this hook module implements, and you would unset your module, unset the element keyed by your module name, and then you would append it to the last so that the hook from your module gets executed last. So the state, this would be mainly about where we are. All the info hooks are gone. Info hooks are replaced with either annotations or EML files. As you can see in hook block info, we have an annotation block that replaces the hook block info. Hook init, hook exit, hook boot are gone. Alter hooks are still there, form altars and other altars. They will be likely to be replaced with event listeners, I can say. There are some Dorotto issues that have these conversations going on to prioritize this transition from hook system to event system eventually. But then that would be up to the effort that community gives and the progress of those issue queues. So that way we can, I think, get an answer to when we would be able to finally replace the hook system with the event dispatcher component. What's wrong and why do we need to change from hooks? So basically, hook block info. Block hooks are a perfect example to analyze what's wrong. So we have four block hooks in four view, configure, and save. Whenever we want to declare a block, we have these two required hooks, hook block info, and hook block view. But then out of these four hooks, it's not clear as to what's really required to implement, to declare your own block, which is something that we have managed to do away with in D8. We have a block interface. Blocks have been basically managed by plugins in D8. We have a block interface, and then we have build array, if you want to, that would determine the output of the block. And if you need a configuration form of your block, then you have this function within your interface block form, within the block base object. And you have this submit function as well for your block form. Also, it's always preferred for a better developer experience to have object-oriented code. That way, your code, as I already said, remains highly modular. And we are able to write unit tests. We are able to better decouple our components, suppose if you have a form submit function. And if you want to do so many things with the collected data from your form, you want to email them to someone. You want to save them to database. You don't have to worry about how to get the database connection and how to store that value to the database, actually. And with object-oriented, with this decoupling possible now, you don't have to worry about managing your mailer or something. You don't have to worry about how we can email that to. Only thing you need to worry about is your form getting submitted. Your submit handler only needs to worry about submitting your form. So that basically gives us better code reuse as well. We don't have to duplicate code, basically. We have so many hooks that are eventually doing the same things. So once we have decided on some basic configuration, we can reuse the code by extending our objects and stuff. And also, with object-oriented, we can mock our desks. We can mock our objects and then give better room for unit desks as well. So there's some simple comparison between events and hooks. Events are obviously object-oriented, easy to write, extensible code. And one thing that was clearly not possible with D7 hooks was that, or D8 hooks as well, was to stop propagation of a particular event from going further. You had no control over deciding on when to stop hook implementation. So we were not able to fire a hook twice. For some reasons, I mean, if we ever need to fire a hook twice, we are not able to do that. Now with events, we can do that. And we have loosely coupled implementation of an event system now, the reusability and also services. With the inclusion of services and object-oriented code, we can now inject services into our event handlers, into our event dispatchers, and into our event listeners. And we could sort of explore the possibilities with about what we could achieve with that. Do we have any questions so far? Events. Moving to the event section, an event is an action or an occurrence recognized by a software and handled by a software. So this is a basic, I think, simpler definition of defining events. The concept is, I think, same as that of hook system. But as I said, with a better developer experience now, we have this event system. So events are part of the symphony framework, symphony two component. They allow for different components of the system to interact and communicate with each other. Object-oriented way of interaction with core and your custom modules, I would say. Basically, events are an implementation of events or hooks, I can say, as well, are an implementation of a mediator pattern that I would be explaining further. And Drupal basically uses the container-revere dispatcher. That means your event dispatcher are always seen through your container. So basically, you have access to all the core services that you can instantiate with the help of the container. So as I said, again, better code extensibility. And with the use of services within the events, we can do wonders with this event subscriber sort of event dispatcher component. So the mediator pattern, I think this is a perfect example of decoupling we could achieve with the events. Suppose we have this radar thing at the airports. And your airplanes continuously landing and taking off, they do not need to interact with each other. They do not need to check in which plane is landing and which plane is taking off and to avoid collisions and stuff. They only have to interact with the main radar. And then the radar decides, has a function to decide on which one to which plane could land and which plane can take off at a particular time. So the intent of mediator pattern is to encapsulate how a set of objects interact and to basically decouple them from each other. That a particular object has a defined functionality. That an airplane, I would say, has a functionality of taking off or landing. It doesn't have to worry about if it may collide with another plane or something. So now I guess look into creating and subscribing to events, how we can subscribe to already exposed or already fired events, and how we can create our own events that other modules can subscribe to. So what we need is the context. I think wherein we can decide that I have an array or I am at a particular stage in a module or in a program and I want other module developers to intervene into my system. So we need the basic logic wherein we can decide I need to fire an event for this. Then we basically dispatch the event and then other modules can listen to that event and do something about it. So how do we listen or subscribe to an event? Basically, in Drupal, we have the symphony event subscriber interface in your subscriber class. We basically have to implement that interface. Implement this interface in our subscriber class. The event subscriber interface forces our subscriber class to implement the get subscribed events method, which basically returns the array of all the events that this subscriber is subscribing to, and with the PHP callables that would execute once the event is fired. Then we need to write those callables, obviously, and to extend the functionality. Then we also need to declare the event subscriber as a service. So the container could instantiate it automatically. This is the event subscriber interface. This is straight from the core. So I have tried to maintain that thing. Basically, it forces the, if you're implementing your interface, it forces the subscriber class to implement this get subscribed event method. And this is from the config factory subscriber class from core as well. It is basically subscribing to events, events, config events, save and config events, delete. So we have this events array, keyed by the constant that represents the event, and basically we have the on save, on config save and on config delete callables that would get executed when this event is fired by the config factory. Then also we have this event subscriber. Basically, we have our event subscriber config factory that implements event subscriber interface. We now need to declare our config factory subscriber class as a service. So how we do that in D8 is we have to tag this class with a tag name event subscriber. That is how Drupal comes to know that this class is an event subscriber. We can also have using, exploring the dependency injection. We can also have services injected into our event subscribers. So this is how we do it. Suppose I have my custom module and I have declared a service that is subscribing to some event and I would declare this arguments array wherein I would inject the logger factory into my event subscriber. So then we need to obviously implement the callable to do something about the event that we are subscribing to. Suppose I have my own custom module. I have this class that is basically a subscriber class. Alter response that implements event subscriber. It is returning, it is subscribing to the kernel event request that is fired at every request. And it expects it to call the logging demo function whenever that event is fired. We need to give the implementation of that function, logging demo. This whatever the callable that we are implementing would receive the event object as a parameter by default. So as an argument by default. So this kernel event request is getResponseEvent. It is basically an object of this class. So our custom, you know, this callable gets the getResponseEvent object. As a argument. So the order of execution is simple in event dispatcher component, as compared to Drupal hooks. So what we require to do is this array, whatever this array that we return from the getSubscribed events takes in as the parameter, a priority parameter. So the higher the priority, the events that have a higher priority that get executed, sorry, callables that have subscribers that have a higher priority get executed first. And those that have lower priority get executed afterwards. So summing up the listening process, we define a service in our module, target with event subscriber. That is how Drupal comes to know about our event subscriber. We define a class for our subscriber or a subscriber service that has to implement event subscriber interface. Then we implement the getSubscribed events from the event subscriber interface and then return a list of all the events that we are subscribing to and what all methods and with what priorities they should get executed. And then we obviously write those methods, write those callables that we want to execute once the event is fired. Now we'd be moving to dispatching process, how we can create our own events and how based on some context, we can dispatch or fire that event. Static event class, this is from the symphony event dispatcher component. Basically the static event class does nothing. It only assigns a special constant for your events so that developers can better know what the event is all about. So it's not a mandatory thing to have this event class but for a better developer experience, it is always recommended. Suppose I have this kernel events class from symphony. It has constants for all the events that it exposes. Constant request is kernel request that basically fires on every request, similarly with exception and view and controller and others. And then our event class, this is the event class that symphony exposes. We don't have much here as well. So developers could extend this class and extend their functionality as required depending upon whatever the use case is. This event class has a property, propagation stop that decides, that determines if the event is currently running or not. And also is propagation stopped, this would be a getter function for that property and then the stop propagation. There are two more methods that belong to this class that are deprecated now that would be get dispatcher and set dispatcher. And the base event class is provided by event dispatcher component is deliberately empty to allow the module developers to extend it and depending upon whatever the case is implemented accordingly. So how we extend this generic event class, we have our own event class. Suppose I have to dispatch an event, I have to create my own event. I would be, first I would be creating the general event class to determine the constants for my events. Then I would be extending the event class with extending my, sorry, extending my own class with this event class. And I would have, suppose I have this event class, I have created this event class that could get a configuration object from Drupal 8. It would get, and be able to alter this configuration in particular context. Dispatching that event. So first getting the service event dispatcher from Drupal and then I would be creating instantiating my event object and passing in the config object that I would be getting in whatever context I am in. Then to dispatch the event, I have the, I have a method dispatch that is from the dispatcher, event dispatcher component. What I need to pass into this dispatcher is the event name or either the event name or the constant that I have to find in my event class and the event object. So then I can alter the data. Once I have subscribed to this event, I can alter the data in the subscriber of this event and then get the data back from the event object, from the subscriber object, that is by these methods. If we have setters and getters in our event class, we can get the configuration back. So the whole process of dispatching the event would be summed up like this. To dispatch an event, we have to basically call the dispatch function and pass into it the name of the event and our event object. So the first argument obviously is a unique event name or a constant that we should be defining in a separate static class. The second argument is obviously the event object. We would need to extend this class into our own custom event class so we can provide our own functionality. By which we can extend the functionality that the core provides. So basically this is a simple dispatcher. We have other dispatchers and listeners as well. We have the event object that has the dispatch function, most important function, dispatch. We also have generic event object from Symphony which has certain functionalities. We also have a container event, a container event dispatcher. Basically this allows us to use services within our events and event listeners. Event dispatcher, event dispatcher, where events and event dispatcher are where listeners. So basically this would be having an event dispatcher, having an event dispatcher and having event listeners which have access to the event dispatcher. So we'd be able to dispatch some more events within our dispatcher or we'd be able to dispatch more events within our listeners so to have better extensibility of our code. So what's happening currently in core? We don't have too many events that are fired for things. We have config events, delete, import, save and rename which are fired on when a configuration event, configuration object is deleted, imported, saved or renamed. We have entity type events, field storage, field storage, definition events, console events. We have kernel events, controller, exception request, response, terminate and view. The exception event would fire when we have an uncaught exception for a request. Request event fires when we have fires for every request when we have a response delivered to the browser. We have this response event fired when a request is terminated, terminate event fires. There's also one special event, kernel event view that is fired when the response is not a response object, when we can have responses to requests as render arrays as well. So when the response is not a response object, kernel event view gets fired. We also have migrate events, map, delete, whenever an entry from map is deleted, map, save, whenever an entry is made into the map, post import that runs after the import process is over, post rollback simply runs after the rollback, similarly post to delete and post to save. We also have routing events, routing event alter, lets module developers to alter the routes or controllers for a request. So with this being said, we still have hooks in D8 and we basically look into how D8 is doing it, how D8 is letting module developers expose their hooks and how D8 is exposing its hooks and how we can still expose our hooks if need be. So what about hooks in D8? Some are gone and some are still there. I think most of the alter hooks are still there, which we should be replacing with the event dispatcher component. Some hooks are gone, there is no hook in it, there is no hook boot, there is no hook exit. So one way we could implement hook in it in, we could achieve the same functionality in D8 would be to implement, would be to subscribe to kernel event request, I would say, and then that way we would be achieving the same functionality. Similarly, hook exit would be, we can subscribe to kernel event terminate as well. Hooks in D8, this is the code chunk I have pasted from the user module file, sorry, the .module file from the user module. We have a function invoke all simplest, just like what we had in D7, the invoke module invoke all file, we have a module handler class, module handler object, which has this invoke all function. So basically what we are doing here is, we are exposing a hook user login and passing in and passing the user account into it so that other developers could hook into it by implementing hook user login. So how this invoke function is doing it, it's basically the same thing. And the function naming mechanism, we have these magic names, you know. So whenever we have a hook invoke all user login and then we'd be passing in the user account. User login is the hook name that we have to decide, just like here, dollar module is the module that would be implementing this hook. The dollar hook is the name that we decide. So for this user login hook, the function name would be your module underscore the hook name, your module underscore user login. And it would be getting the user account as the argument. And if we are invoking our hook from all the modules, we'd be needing to call this function from the module handler class. This again does the same thing, function module underscore hook name. This is the event, this is the function regarding the alter hooks, alter function from the module handler object. So this again does the same thing. This again does the same thing from, you know, the same way we would be in B7, we would be needing to call the Drupal alter function. Herein we would be needing to call the alter function from the module handler object and then pass into it the array of data or context, whatever we want to alter. And regarding the order of execution of hooks in D8, it's again implementing the same, exposing the same hook module implements alter, which, you know, has the array of implementation of all the, which has the array that has all the module names as keys that are implementing our hook and then the hooks get executed in the same order. So to alter the execution of hooks in D8, we would simply be, we'd again be needing to implement this hook, hook module implements alter. So how to, you know, again, how to alter if we had an array of data and we wanted other module developers to alter our data, we'd be needing to call this alter function, pass in our, you know, the name of that hook that we'd want to, you know, expose and then pass in data as well. This is how we are exposing hook views data. Alter in D8, we basically have, we basically call the alter function of the module handler. So somebody, if you are, you know, the basic question is if we should be going with hooks or events, if you're writing your custom module, I think we should definitely be, if you need to expose something, if you need to allow other module developers to hook into your system, we should definitely fire an event and there's a saying that we should, you know, for better experiences, we should log everything and trigger an event for everything. So if you're interacting with or altering code, subscribe to an event if one is fired, I have already mentioned some of the events that are available. If not, I don't see too many options for us, we still have to go the hook way. For configuration and admin forms, just like I said, hook block info, hook block configure, hook block save and stuff like that. We have plugins and tag services as well. And now I think I'd be going for a simple demo. We'd be subscribing to a few events in D8 and we'd be, you know, creating an event for a form submit and then we'd be executing some silly code while we have subscribed to that event. So basically, I have a simple site. I have a simple controller written for this page. It returns the render today. I also have this config form. There are a few fields that I have. So in the code section, I have these two modules config form that is responsible for creating my config form and the form that is available that I just showed. And I also have this event demo module that would be subscribing to a few events. So first we can look into, you know, as I said, to subscribe to an event, we'd be needing to have our own service, the event subscriber class. How we can define it is here in the module.services.yml file. I have this alter response class declared as a service. It would be getting this logger factory as an argument. I have declared this as a dependency and it would dependency would be injected by the container into my class. And I have tagged it with the event subscriber tag. So this is my alter response class. Basically, implementing the event subscriber interface and then implementing the get subscribed events method from that interface and then returning an array of, you know, various events that we can subscribe to. I have, you know, currently commented out all these events. So we could, you know, uncomment one by one and check what happens. I have for kernel element response, sorry, kernel element request. I have a function logging demo that is here. We'd be subscribing to kernel event request. So this would be happening for every request that our site makes. I am currently fetching the path from the request object that we are visiting and I'd be, you know, I'd be logging that path into the Drupal logs. So how we would be logging that with the help of the dependency injection, the injected logger factory object would be, you know, helping us to log the path. So let me just clear cache and let me just refresh this page so we make a request and then let's check logs. You can see our config form was visited and then reports and then this current page DB logs was visited. We can actually change this method, change this to better check if it's executed and then I can refresh this to make another request and then check the logs. This page was visited just now. So I also have, we also have this, you know, we can also have config events save to do something. We can also, you know, determine the, we can also set the priority for various events from callables to be executed. We'd also be subscribing to the kernel event response that gets fired for every time when a response is displayed to the user. Demo response function would be, I don't think, yes, I have, would be, we'd be getting the response object and then we'd be setting content on our response object to this, basically removing all the content from the response object and passing in a simple string to it. I would need to clear cache again. So for every request, I should get a simple string. So we can actually change the string and check if it is basically coming from here only. So this is this. Now, as I said, you know, I have this configuration form. I can also, you know, to, I am currently, the config form is coming from this module. I have a form, I have a few fields declared in this form and I also have a submit handler for this form. I am storing the values from this, from these fields into the configuration. Before that, I'm exposing, I'm firing an event and I'm letting other modules to subscribe to my event and then alter the data that gets submitted to the configuration. So basically I have this event demo class that is already defined, the event demo class is here. Event demo class extends event. So it would basically have the dispatch, access to dispatch function by which it can dispatch the event and then I have some getter and setters for the configuration that it needs to alter, that I want other module developers to alter. So I have this my module events class here, which would be defining the constant for my event that I'd be exposing. The unique name for my event would be event demo dot form. So to subscribe to this event, I would need an event listener that would be subscribing to this event. So I have this alter response subscribed to this event as well. I have this alter config function with a priority of zero. This alter config function would be basically changing, getting the configuration, getting the value of the text field and then setting it to I won't show really. So I would need to clear cache since I'm subscribing to a new event. So I have this, I can change it. I was altered. I can change the video field and then save the configuration. And then it would basically override the value that I had submitted in the submit function and that I'm overriding in my custom module. I can change it here as well to check if it is coming from here only. I can submit my form. So this is the method, this is the value that I'm getting. Now to explain the priorities thing as well, I can have this another function. I can, yeah. So I can basically subscribe to an event from the unique name as well and also from the constant as well. So I have these two functions subscribing to the same event, alter config with a priority of zero. So I can just comment it out. I have these two functions, alter config again and config again, alter config and alter config again, subscribing to the same event with a different priority. So with the higher priority of 10, the alter config function would be executing first. This would be executing first and setting the value to this. And then the alter config again function would be executing and then it would be setting the value to this. So once I submit this form, we should get the previous value. So this explains the priority thing as well. And this information about the contributions prints evaluate my session if you think it was helpful. Questions if any, no questions? Yeah. Like I said, it really depends upon, I mean, because it would require some effort to replace all of the hooks with events. But then it really depends upon the effort that the community could provide to take that initiative forward. I mean, I don't, there are issue queues in Drupal D.O that have that wherein the conversations are going on whether to which what all hooks need to be replaced first and then with the plans further. So we can definitely check issue queue and figure that out. Yeah. So basically you don't, you would have to go to the event object, sorry, you would have to go to the event class to figure that out. Once you have, if your method is subscribing to multiple events, you'd have to somehow do a check about what event it is receiving and then plan your actions accordingly. Yeah. Any other questions? Well then, thank you for your time. Thank you for your time.