<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marcus Kober</title>
    <description>The latest articles on DEV Community by Marcus Kober (@marcuskober).</description>
    <link>https://dev.to/marcuskober</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F344983%2F1e6813ab-8bba-4a5b-8db3-79bc3e99c9b4.jpeg</url>
      <title>DEV Community: Marcus Kober</title>
      <link>https://dev.to/marcuskober</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcuskober"/>
    <language>en</language>
    <item>
      <title>Elevate Your Plugin Development with Hook-Driven-Development</title>
      <dc:creator>Marcus Kober</dc:creator>
      <pubDate>Thu, 25 May 2023 06:45:30 +0000</pubDate>
      <link>https://dev.to/marcuskober/elevate-your-plugin-development-with-hook-driven-development-37i1</link>
      <guid>https://dev.to/marcuskober/elevate-your-plugin-development-with-hook-driven-development-37i1</guid>
      <description>&lt;p&gt;&lt;strong&gt;After the &lt;a href="https://marcuskober.com/registering-wordpress-hooks-effectively-with-php-attributes/"&gt;previous article&lt;/a&gt; in this series was very technical, today we'll take a breather and indulge in a slightly more theoretical piece. Our topic is Hook-Driven-Development and why you too should implement it in your plugins.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In WordPress plugin development, hooks are a fundamental and indispensable tool. Hooks ensure that a plugin is extendable, scalable, and remains cleanly structured. Hook-Driven-Development is an approach in plugin development that not only involves using the existing hooks of WordPress and other plugins but also incorporating your own hooks and using them within the plugin itself.&lt;/p&gt;

&lt;p&gt;In this article, I assume a basic understanding of hooks and their use in developing WordPress plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages of Hook-Driven-Development
&lt;/h2&gt;

&lt;p&gt;We don't want to use Hook-Driven-Development just for fun – its application also has some compelling advantages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantage 1: Modularity and Flexibility
&lt;/h3&gt;

&lt;p&gt;By consistently using hooks in your plugin, you enhance its modularity and flexibility.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ease of Modification and Expansion&lt;/strong&gt; &lt;br&gt;
Hooks allow for easier code adaptation to individual or new needs. They also facilitate the addition of extra functions without changing existing code. New functions, when using hooks, can either be defined in the actual plugin code, or added via additional plugins (e.g., addons). This enables you to offer a plugin in a free and a paid version. You develop the free version as a complete plugin, and the additional functions of the paid version are then integrated as a paid addon plugin. &lt;small&gt;However, if you want to implement such a model of a free and paid version, please ensure the free version provides real added value and avoid angering your users with an inadequate free version that wants to sell the paid version at every turn! There are already far too many such plugins.&lt;/small&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replacement of Components&lt;/strong&gt; &lt;br&gt;
The modular nature of plugins developed via Hook-Driven-Development allows individual components of the plugin to be easily replaced without affecting the rest of the code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Maintainability and Scalability
&lt;/h3&gt;

&lt;p&gt;The maintainability and scalability of your plugin also benefit from hook-based development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Better Structuring of Large Projects&lt;/strong&gt; Here we are back to my favorite topic! By strictly using hooks as a fundamental element of code structuring, it becomes much easier to implement well-designed plugins that can naturally grow and remain transparent. The question of where to put newly implemented code will rarely (if ever) arise.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Easier Updating and Debugging&lt;/strong&gt; With clearly defined hooks and the corresponding callbacks in small classes, updating individual components and troubleshooting becomes significantly easier – all without affecting the entire system.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Interoperability and Collaboration
&lt;/h3&gt;

&lt;p&gt;The use of hooks facilitates interoperability between plugins and cooperation among developers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Interoperability&lt;/strong&gt; 
As a clear interface, hooks facilitate communication between plugins. To reflect the adaptability of WordPress and many plugins in your plugin, you should offer hooks at important points in your plugin. Of course, it's absolutely permitted to leave certain things &lt;strong&gt;without&lt;/strong&gt; hooks if these things should not be changeable for security reasons.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Easier Cooperation through Shared Standards&lt;/strong&gt; 
Hooks form the common basis in plugin development, which is familiar to all plugin developers. This makes it easier to win developers for collaborative development. But your plugin will also gain respect in the developer community when you define enough hooks so that your plugin can be customized to individual needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Principles of Hook-Driven-Development
&lt;/h2&gt;

&lt;p&gt;All this sounds great, but what should it look like in practice? I will outline that here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Actions and Filters as the Central Concept in Plugin Development
&lt;/h3&gt;

&lt;p&gt;We define Actions and Filters as the central concept in plugin development. You are probably familiar with the MVC pattern (&lt;a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller"&gt;Model, View, Controller&lt;/a&gt;). In a way, one could say that in hook-driven development, the hooks (or the classes whose methods serve as callbacks for hooks) take on the role of the &lt;a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller#Controller"&gt;controllers&lt;/a&gt; from the MVC approach. In the MVC pattern, the controller is the central control unit between the &lt;a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller#Model"&gt;model&lt;/a&gt; (i.e., the business logic) and the &lt;a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller#View"&gt;view&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In our hook-driven approach, the hooks, or the classes containing hooks, form the central control unit.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Designing Plugin Architecture Around Hooks
&lt;/h3&gt;

&lt;p&gt;An effective hook-driven development strategy requires that the plugin architecture be designed from the ground up to integrate hooks. This means structuring the code in a way that it is broken down into smaller, reusable modules and these modules then communicate via hooks.&lt;/p&gt;

&lt;p&gt;This promotes the efficient structuring of the plugin into reusable modules, a clear separation of functions and responsibilities. As a result, the plugin can be easily and quickly extended in the future and is simple to maintain and test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Definition and Use of Own Hooks
&lt;/h3&gt;

&lt;p&gt;If your plugin already uses hooks from WordPress and/or other plugins (which it should), it probably still does not automatically already rely on hook-driven development. Only the definition and use of own hooks in sufficiently extensive plugins forms the basis for well-implemented and successful hook-driven development.&lt;/p&gt;

&lt;p&gt;By using your own hooks, you open up your plugin to the outside world and allow other developers to build upon your plugin or modify individual aspects while simultaneously ensuring a good structure for your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Hook-Driven-Development
&lt;/h2&gt;

&lt;p&gt;For your hook-driven approach to be truly successful and to facilitate the structuring of your plugin on the one hand, and to meaningfully open up your plugin to the outside on the other hand, you should consider a few best practices. Only in this way is effective and sustainable implementation possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Naming Conventions
&lt;/h3&gt;

&lt;p&gt;Choosing unique and meaningful names for hooks is crucial for the readability and understandability of the code. Here are some recommendations for effective naming conventions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use of a Prefix&lt;/strong&gt;
So that the hooks of your plugin are directly recognizable as such, they should consistently use their own prefix. This also makes it less likely that identical hook names will be defined by WordPress or other plugins. The prefix could be the name of your plugin, if it is short enough, or you could use an abbreviation of the plugin name. The &lt;a href="https://woocommerce.github.io/code-reference/hooks/hooks.html"&gt;hooks of WooCommerce&lt;/a&gt;, for example, all start with &lt;code&gt;woocommerce_&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Descriptive Names&lt;/strong&gt;
Choose descriptive names for your hooks. It should be as clear as possible what your hook is used for. It should be clear that a name like &lt;code&gt;myplugin_hook_1&lt;/code&gt; is of little use, while &lt;code&gt;myplugin_menu_items&lt;/code&gt; suggests that it could be a filter for menu entries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Notation&lt;/strong&gt;
Maintain a consistent notation of your hook names. How you write the hooks is not very relevant - the important thing is not to mix different notations. WordPress and therefore many plugins use the underscore (&lt;code&gt;myplugin_menu_items&lt;/code&gt;), while others also use &lt;em&gt;camelCase&lt;/em&gt; (&lt;code&gt;mypluginMenuItems&lt;/code&gt;). For particularly extensive plugins with many hooks, the use of slashes (&lt;code&gt;myplugin/navigation/menuItems&lt;/code&gt;) could lead to more clarity, and there are some examples of this in the plugin world, such as the well-known plugin &lt;a href="https://wordpress.org/plugins/elementor/"&gt;Elementor&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;p&gt;Not only developers of other plugins need good documentation of your plugin's hooks. You yourself (or especially your team, if you have one) also need this documentation. After some time, you will be very happy to be able to look up what a certain hook does and where it is used.&lt;/p&gt;

&lt;p&gt;There are two approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inline Comments&lt;/strong&gt;
For you and other developers, it is especially important to rely on inline comments when there are functions and dependencies that are not directly evident from the hook name and the underlying code. The general rule of PHP comments applies here: use as few inline comments as possible, but as much as necessary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External Documentation&lt;/strong&gt;
Especially for extensive plugins, it can be advantageous to create external documentation. You can create this on your website or plugin website, or you can use services that are already designed for this purpose, such as &lt;a href="https://www.gitbook.com/"&gt;GitBook&lt;/a&gt; or &lt;a href="https://readthedocs.org/"&gt;Read the Docs&lt;/a&gt;. There is also an interesting project that automatically documents your hooks: the &lt;a href="https://github.com/pronamic/wp-documentor"&gt;Pronamic WordPress Documentor&lt;/a&gt;. It automatically generates documentation in various formats based on your hook declarations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing the Hooks
&lt;/h3&gt;

&lt;p&gt;Since hooks often serve as interfaces to other plugins or parts of WordPress, it is important to ensure their stability and reliability. This requires careful testing, for example, with PHP Unit, which we will cover in a later article. It is important to consider the following points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test Coverage&lt;/strong&gt;
Make sure that all hooks, or their corresponding callbacks, are covered by tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing Dependencies on External Plugins&lt;/strong&gt;
Dependencies on other plugins should be avoided as much as possible during plugin development. However, if your plugin still requires another plugin, it is important to clearly communicate the dependencies, and your plugin should check if the required plugin is installed and activated during activation using appropriate methods. All dependencies must be tested, and when external plugins are updated, it must be ensured that the communication still works smoothly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing Incompatibilities with Other Plugins&lt;/strong&gt;
Test your plugin in a WordPress installation that has popular plugins installed and activated. Do problems with your hooks occur here, such as name collisions? All of this must be tested to ensure smooth operation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Backward Compatibility When Renaming Hooks
&lt;/h3&gt;

&lt;p&gt;It may happen that you need or want to rename hooks. Perhaps because you just read this article and have been thinking about your naming conventions.&lt;/p&gt;

&lt;p&gt;As long as your plugin is still in development, you only need to make sure to change all occurrences of the hook accordingly.&lt;/p&gt;

&lt;p&gt;However, the situation is different if your plugin is already being used on websites. In that case, other plugins (or themes) may be using your hooks. If you were to simply rename your hooks and the new version of your plugin is updated on the websites, the hooks used by other plugins would no longer work. And this could potentially affect the users' websites who use your plugins.&lt;/p&gt;

&lt;p&gt;To avoid such conflicts, you need to respond to hook renaming in an appropriate manner.&lt;/p&gt;

&lt;p&gt;Let's imagine that you noticed that a hook in your plugin from the early stages of development still uses a hook with the not-so-nice name &lt;code&gt;myplugin_hook_01&lt;/code&gt;. While creating your external documentation, you get annoyed by it and decide that the hook should have the new name &lt;code&gt;myplugin_menu_items&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is a perfect way to achieve backward compatibility in case other developers have already been using &lt;code&gt;myplugin_hook_01&lt;/code&gt; in their plugins (and have already been frustrated by the name)!&lt;/p&gt;

&lt;p&gt;For this example, let's assume that the call with the old hook name looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$menuItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply_filters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_hook_01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the first step, you rename the hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$menuItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply_filters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_menu_items'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the second step, you create a file in your plugin called &lt;em&gt;DeprecatedHooks.php&lt;/em&gt;. In this file, you register a callback for the &lt;strong&gt;new(!)&lt;/strong&gt; hook using our PHP attribute method from &lt;a href="https://marcuskober.com/registering-wordpress-hooks-effectively-with-php-attributes/"&gt;previous article&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Main&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\Attributes\Filter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeprecatedHooks&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;#[Filter('myplugin_menu_items', 0, 3)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;menuItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$menuItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$menuId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nv"&gt;$menuObject&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can simply fire the old hook and we're done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;#[Filter('myplugin_menu_items', 0, 3)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;menuItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$menuItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$menuId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nv"&gt;$menuObject&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;apply_filters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_hook_01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this would silently fire the old hook, and other developers would only become aware of the renaming if they read your changelogs.&lt;/p&gt;

&lt;p&gt;That's why we have the functions &lt;code&gt;apply_filters_deprecated()&lt;/code&gt; and &lt;code&gt;do_action_deprecated&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In our example, we are focusing on filters, but the same applies to actions. Let's take a look at &lt;code&gt;apply_filters_deprecated()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;apply_filters_deprecated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$hook_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="k"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$replacement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; 
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! We can pass the old hook name to the function, all the arguments in the &lt;code&gt;$args&lt;/code&gt; array, the version from which the hook was deprecated in &lt;code&gt;$version&lt;/code&gt;, and the new hook name in &lt;code&gt;$replacement&lt;/code&gt;. Additionally, we can even provide a message regarding the renaming in &lt;code&gt;$message&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In our case, it would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;#[Filter('myplugin_menu_items', 0, 3)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;menuItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$menuItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$menuId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nv"&gt;$menuObject&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;apply_filters_deprecated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'myplugin_hook_01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Old hook name&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$menuItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$menuObject&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Arguments as an array&lt;/span&gt;
        &lt;span class="s1"&gt;'1.1.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Version of our plugin where the renaming took place&lt;/span&gt;
        &lt;span class="s1"&gt;'myplugin_menu_items'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// New hook name&lt;/span&gt;
        &lt;span class="s1"&gt;'Changed the hook name.'&lt;/span&gt; &lt;span class="c1"&gt;// Message&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the old hook still works as if nothing has changed. However, WordPress will throw a deprecation notice that will be displayed if the &lt;code&gt;WP_DEBUG&lt;/code&gt; constant in the &lt;strong&gt;wp-config.php&lt;/strong&gt; file is set to &lt;code&gt;true&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Deprecated: Hook myplugin_hook_01 is deprecated since version 1.1.0! Use myplugin_menu_items instead. Changed the hook name.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The same concept applies to actions using the &lt;code&gt;do_action_deprecated()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;By following this approach, we have achieved everything that is important when renaming a hook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The hook has been renamed.&lt;/li&gt;
&lt;li&gt;The old hook continues to function for backward compatibility.&lt;/li&gt;
&lt;li&gt;Other developers or our development team will be warned when using old hooks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Examples of Successful Hook-Driven Plugins
&lt;/h2&gt;

&lt;p&gt;There are already many excellent plugins that utilize hook-driven development. The extent to which a plugin is entirely based on hooks may vary. The following plugins can serve as good examples, although they may not be suitable examples in all aspects of architectural decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  WooCommerce
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://woocommerce.com/"&gt;WooCommerce&lt;/a&gt; is &lt;strong&gt;the&lt;/strong&gt; E-Commerce plugin for WordPress, developed by &lt;a href="https://automattic.com/"&gt;Automattic&lt;/a&gt;, the company founded by Matt Mullenweg, the creator of WordPress. WooCommerce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Offers a wide range of hooks that allow for extensive extension and customization of WooCommerce.&lt;/li&gt;
&lt;li&gt;Utilizes the hooks defined within the plugin extensively for its own functionality.&lt;/li&gt;
&lt;li&gt;Serves as a great example of the possibility for extension through standalone add-on plugins, which WooCommerce distributes as modular paid plugins.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Advanced Custom Fields
&lt;/h3&gt;

&lt;p&gt;The well-known plugin &lt;a href="https://wordpress.org/plugins/advanced-custom-fields/"&gt;Advanced Custom Fields&lt;/a&gt;, commonly loved by developers, especially during the pre-Gutenberg era, is another excellent example of hook-driven development. Originally developed by &lt;a href="https://profiles.wordpress.org/elliotcondon/"&gt;Elliot Condon&lt;/a&gt;, acquired by &lt;a href="https://deliciousbrains.com/"&gt;Delicious Brains&lt;/a&gt;, and now driven by &lt;a href="https://wpengine.com/"&gt;WP Engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Even today, the plugin remains powerful and essential and can serve as a good example of hook-driven development. ACF provides many hooks for developing great extensions.&lt;/p&gt;

&lt;p&gt;These are just a few examples of successful hook-driven plugins. While the level of implementation may vary, they demonstrate the benefits and possibilities of hook-driven development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Example
&lt;/h2&gt;

&lt;p&gt;While we can't develop a complete plugin using hook-driven development in this text, I can provide a small example to illustrate the concept. Please note that due to space limitations, the example may be simplified.&lt;/p&gt;

&lt;p&gt;Let's assume you have developed a plugin that needs to add menu items to a specific menu with the slug &lt;code&gt;main-menu&lt;/code&gt;. You're already using the hook &lt;code&gt;"wp_nav_menu_{$menu-&amp;gt;slug}_items"&lt;/code&gt; to achieve this (&lt;a href="https://developer.wordpress.org/reference/hooks/wp_nav_menu_menu-slug_items/"&gt;documentation&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FrontendMenu&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;#[Filter('wp_nav_menu_main-menu_items', 10, 2)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addMenuItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$newItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'dashboard'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;esc_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_permalink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_dashboard_page_id'&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Dashboard'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="s1"&gt;'faq'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;esc_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_permalink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_faq_page_id'&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'FAQ'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nv"&gt;$newItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menuItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'&amp;lt;li&amp;gt;&amp;lt;a href="%s"&amp;gt;%s&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nv"&gt;$menuItem&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="nv"&gt;$menuItem&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;$newItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$newItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code, we register the &lt;code&gt;addMenuItems()&lt;/code&gt; method as a callback for the &lt;code&gt;wp_nav_menu_main-menu_items&lt;/code&gt; hook. Inside the method, we define an array &lt;code&gt;$newItems&lt;/code&gt; that contains the new menu items. We use &lt;code&gt;array_map()&lt;/code&gt; to transform the menu item arrays into HTML list item strings. Finally, we concatenate the new items to the &lt;code&gt;$items&lt;/code&gt; string and return it.&lt;/p&gt;

&lt;p&gt;Now, let's take a step towards hook-driven development and expose the new menu items to the outside so that we and other developers can manipulate the items. We introduce a new filter called &lt;code&gt;myplugin_menu_items&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;#[Filter('wp_nav_menu_main-menu_items', 10, 2)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addMenuItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$newItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply_filters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_menu_items'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="s1"&gt;'main-menu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$newItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menuItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'&amp;lt;li&amp;gt;&amp;lt;a href="%s"&amp;gt;%s&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;$menuItem&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nv"&gt;$menuItem&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;$newItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$newItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We pass an empty array as the default value to our new filter, along with the menu slug and the &lt;code&gt;$args&lt;/code&gt; object of the original filter, in case we or other developers need it. In the original class, we can then populate the menu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FrontendMenu&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;#[Filter('wp_nav_menu_main-menu_items', 10, 2)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addMenuItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$newItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply_filters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_menu_items'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="s1"&gt;'main-menu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$newItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menuItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'&amp;lt;li&amp;gt;&amp;lt;a href="%s"&amp;gt;%s&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nv"&gt;$menuItem&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="nv"&gt;$menuItem&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;$newItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$items&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$newItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;#[Filter('myplugin_menu_items', 10, 3)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;generateMenuItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$menuSlug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'main-menu'&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nv"&gt;$menuSlug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$newItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'dashboard'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;esc_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_permalink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_dashoboard_page_id'&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Dashboard'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="s1"&gt;'faq'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;esc_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_permalink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_faw_page_id'&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'FAQ'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;array_merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$newItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, first, we apply the filter using &lt;code&gt;apply_filters&lt;/code&gt; with an empty array to populate it in the &lt;code&gt;generateMenuItems()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;What have we achieved with this approach?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flexibility:

&lt;ul&gt;
&lt;li&gt;We can now add new menu items from anywhere in our code by using the &lt;code&gt;myplugin_menu_items&lt;/code&gt; hook. For example, we can conditionally display a login or logout link based on whether a user is logged in. &lt;/li&gt;
&lt;li&gt;By decoupling the addition of menu items from the &lt;code&gt;wp_nav_menu_main-menu_items&lt;/code&gt; hook, we have the flexibility to insert new menu items before the Dashboard link by using the &lt;code&gt;myplugin_menu_items&lt;/code&gt; hook with a priority of &lt;code&gt;9&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Extensibility:

&lt;ul&gt;
&lt;li&gt;We allow other plugins to modify the list of new menu items. Other plugins can add or remove menu items to our plugin's menu.&lt;/li&gt;
&lt;li&gt;Our own code or add-on plugins can also influence the menu by utilizing the &lt;code&gt;myplugin_menu_items&lt;/code&gt; hook.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Testability:

&lt;ul&gt;
&lt;li&gt;The array containing the new menu items can now be the subject of tests.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was just a small and perhaps somewhat contrived example, but it demonstrates the approach and benefits of hook-driven development. In the remaining articles, the concept will become even clearer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Outlook
&lt;/h2&gt;

&lt;p&gt;In this article, we learned about what Hook-Driven Development is, the benefits it brings to WordPress plugin development, and the best practices involved. We also saw a small code example that demonstrated how this approach can be implemented in real-world scenarios.&lt;/p&gt;

&lt;p&gt;In the next article, we will learn about using Dependency Injection in WordPress plugins. We will explore why Dependency Injection may be necessary, especially when utilizing hook registration via PHP attributes.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>php</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to register WordPress hooks with PHP attributes: A step-by-step guide</title>
      <dc:creator>Marcus Kober</dc:creator>
      <pubDate>Fri, 19 May 2023 07:11:47 +0000</pubDate>
      <link>https://dev.to/marcuskober/how-to-register-wordpress-hooks-with-php-attributes-a-step-by-step-guide-1722</link>
      <guid>https://dev.to/marcuskober/how-to-register-wordpress-hooks-with-php-attributes-a-step-by-step-guide-1722</guid>
      <description>&lt;p&gt;&lt;strong&gt;In this article, I will discuss how and why WordPress hooks are used in plugins, and how we will use PHP attributes for registering hooks in classes effectively and clearly.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are hooks and how do they work?
&lt;/h2&gt;

&lt;p&gt;Since I assume a certain level of knowledge about WordPress plugin development for this article series, there is a good chance you are already familiar with hooks. If not, you should &lt;a href="https://developer.wordpress.org/plugins/hooks/"&gt;learn about hooks&lt;/a&gt; now. However, I still want to provide a brief overview as an introduction.&lt;/p&gt;

&lt;h2&gt;
  
  
  A brief introduction to hooks
&lt;/h2&gt;

&lt;p&gt;Hooks are the most important tool for plugin developers. With hooks, code can be executed at specific predefined points, and data can be modified. Hooks are somewhat similar to events that are fired at certain points in the code, and they are what allow plugins to extend and modify WordPress in the first place!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please NEVER use methods other than hooks if you want to create a clean and secure plugin!&lt;/strong&gt; Other methods here refer to modifying core files or any other types of hacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There are two types of hooks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Filters&lt;/strong&gt;, which allow you to modify data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actions&lt;/strong&gt;, which allow you to execute code at specific points&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hooks consist of a hook name and a callback function that can be attached to the hook.&lt;/p&gt;

&lt;p&gt;The callback functions of filters always have a return statement, while the callbacks of actions usually do not require one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filters
&lt;/h3&gt;

&lt;p&gt;Let’s briefly take a look at the function &lt;code&gt;apply_filters()&lt;/code&gt; (&lt;a href="https://developer.wordpress.org/reference/functions/apply_filters/"&gt;documentation&lt;/a&gt;). This function allows us to execute the callbacks that have been added to a filter name. Any number of callbacks can be added to a filter name.&lt;/p&gt;

&lt;p&gt;Suppose you want to allow another plugin to modify your plugin’s data. Let’s say you have developed a book management system as a WordPress plugin, and you have decided that the book management system should only allow science fiction books and horror thrillers. However, you want other developers to be able to create their own plugins that extend your book management system.&lt;/p&gt;

&lt;p&gt;In your plugin, you use a method called isGenreValid() to check if the book belongs to the correct genre. This could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;isGenreValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$genre&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$genres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'Science Fiction'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'Horror thriller'&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$genre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, both genres are hard-coded inside the function and can’t be changed from outside. To allow another plugin to modify your genre list, you can work with &lt;code&gt;apply_filters()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;isGenreValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$genre&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$genres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'Science Fiction'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'Horror thriller'&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nv"&gt;$genres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply_filters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_book_genres'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$genre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, another plugin (or even your own code, as we’ll see later) can hook into the &lt;code&gt;myplugin_book_genres&lt;/code&gt; filter and extend or completely replace the list. If there is no callback for this filter, the original list will be returned, which you specify as the second argument in &lt;code&gt;apply_filters()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The other plugin can then register a callback using &lt;code&gt;add_filter()&lt;/code&gt; that modifies the genre array. This could look like this, using an anonymous function, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_book_genres'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Drama'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if the genre drama is passed to the &lt;code&gt;isGenreValid()&lt;/code&gt; method, it will return &lt;code&gt;true&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Let’s say, in your book management plugin, it’s possible to create virtual bookshelves. Perhaps it’s important for the code hooking into the filter to know which virtual bookshelf is currently selected. The ID of the bookshelf is stored in the class variable &lt;code&gt;$shelfId&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can add any number of additional arguments to the &lt;code&gt;apply_filters()&lt;/code&gt; function to provide more information to the callback that hooks into the hook. This is where we can pass the ID of the bookshelf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$genres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply_filters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_book_genres'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;shelfId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the callback function of this hook, we are now able to modify the genre list depending on the shelf ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin_book_genres'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$shelfId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$shelfId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Drama'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Comedy'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$genres&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are added two additional arguments to &lt;code&gt;add_filter()&lt;/code&gt;: &lt;code&gt;10&lt;/code&gt; and &lt;code&gt;2&lt;/code&gt;. &lt;code&gt;10&lt;/code&gt; is the priority of the callback in the list of callbacks registered to the hook, and &lt;code&gt;2&lt;/code&gt; is the number of arguments passed to the callback. The number of passed arguments has to be &lt;code&gt;2&lt;/code&gt; here, because we need to get &lt;code&gt;$genres&lt;/code&gt; and &lt;code&gt;$shelfId&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The priority specifies, in which order the callbacks get called if multiple callbacks are registered for a hook. In case you want your code to run earlier than the other callbacks, you have to choose values lower than the default value of 10. Should the code run later, choose values greater than 10. Callbacks registered with the same priority are called in the order they were registered.&lt;/p&gt;

&lt;p&gt;Let’s look at the syntax of &lt;code&gt;add_filter()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$hook_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callable&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$accepted_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the default value for &lt;code&gt;$priority&lt;/code&gt; is &lt;code&gt;10&lt;/code&gt;, and the default for &lt;code&gt;$accepted_args&lt;/code&gt; &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Actions
&lt;/h3&gt;

&lt;p&gt;While filters allow data to be modified in specific ways with their callbacks returning the corresponding value, actions interrupt the flow of the code to allow for other additional (external) code to be executed.&lt;/p&gt;

&lt;p&gt;The function to run an action is named &lt;code&gt;do_action()&lt;/code&gt;. Let’s compare it to its counterpart &lt;code&gt;apply_filters()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;apply_filters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$hook_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mixed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;do_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$hook_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$arg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, &lt;code&gt;do_action()&lt;/code&gt; lacks the &lt;code&gt;$value&lt;/code&gt; argument, which contains the value that gets filtered, and there’s no return value. However, like its filter counterpart, &lt;code&gt;do_action()&lt;/code&gt; can also be given required arguments.&lt;/p&gt;

&lt;p&gt;The callback, that gets registered via &lt;code&gt;add_action()&lt;/code&gt;, runs immediately where &lt;code&gt;do_action()&lt;/code&gt; is called.&lt;/p&gt;

&lt;p&gt;The function &lt;code&gt;add_action()&lt;/code&gt; has the same syntax as &lt;code&gt;add_filter()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$hook_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callable&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$accepted_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s assume, you want to load a specific CSS file in frontend. In this case you have to use the action hook &lt;code&gt;wp_enqueue_scripts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;wp_enqueue_style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'myplugin-style'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;plugins_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'assets/dist/css/my-plugin.css'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that the &lt;code&gt;wp_enqueue_style()&lt;/code&gt; function is called exactly where it is needed for working properly!&lt;/p&gt;

&lt;h3&gt;
  
  
  Actions are actually filters
&lt;/h3&gt;

&lt;p&gt;Actions are essentially a special case of filters. This becomes clearer, when we take a closer look at the source code of the &lt;code&gt;add_action()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$hook_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$accepted_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$hook_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$accepted_args&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes, &lt;code&gt;add_action()&lt;/code&gt; simply calls &lt;code&gt;add_filter()&lt;/code&gt;, passing all arguments – we will use this fact to our advantage below!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But please don’t use &lt;code&gt;add_filter()&lt;/code&gt; only&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nevertheless, you should not simply start using only &lt;code&gt;add_filter()&lt;/code&gt; from now on – after all, there is a significant difference between actions and filters, and it should always be clear from the code whether a callback is being assigned to an action or a filter.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I find out which hooks exist?
&lt;/h2&gt;

&lt;p&gt;As previously mentioned, hooks are essential for WordPress plugin development. The WordPress core has a wealth of hooks to modify almost every aspect of WordPress. Additionally, many plugins leverage hooks, allowing other developers to change the flow and data.&lt;/p&gt;

&lt;p&gt;There are basically four different ways to find out which hooks exist. Each method has different advantages and disadvantages and use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Google it:
&lt;/h3&gt;

&lt;p&gt;We can search for hooks on Google. This is useful when we wonder if there is a hook for the problem we want to solve. A Google query could look like this: &lt;em&gt;“wordpress hook for adding a class to the body tag”&lt;/em&gt;. In this way, the desired hook can usually be found quickly and easily.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--knyIDedq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtxv81wuzlo0u73wmhkt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--knyIDedq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtxv81wuzlo0u73wmhkt.png" alt="Google search result: wordpress hook for adding a class to the body tag" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. ChatGPT
&lt;/h3&gt;

&lt;p&gt;We can ask ChatGPT:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6Urgk-xC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2jnxcbo586csqd7hhecn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Urgk-xC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2jnxcbo586csqd7hhecn.png" alt="ChatGPT result" width="800" height="802"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Version: ChatGPT 4&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here, we notice how extensively ChatGPT responds, not only providing a code example but also pointing out that the theme must support adding a CSS class to the body tag by using the function &lt;code&gt;body_class()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As always, caution is advised with ChatGPT: firstly, the tool’s knowledge base currently ends in September 2021 (so it cannot know any recent changes), and secondly, it is always better to check and test the provided results!&lt;/p&gt;

&lt;p&gt;Nevertheless, this type of hook research is superior to a Google search.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Documentation and hook directories
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;WordPress itself has a list of hooks, and the hooks themselves have their own subpages explaining their usage: &lt;a href="https://developer.wordpress.org/reference/hooks/"&gt;Hook Reference&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Plugins that use hooks often provide a corresponding directory as well. For example, &lt;a href="https://woocommerce.github.io/code-reference/hooks/hooks.html"&gt;WooCommerce&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;There used to be a service called hookr.io, a website that listed Core and Plugin hooks. However, I just discovered that this site no longer exists. &lt;a href="https://web.archive.org/web/20200328143351/http://hookr.io/"&gt;This is what it used to look like.&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Documentations are often easy to search and provide a good overview of the use of specific hooks. However, they are less suitable for searching for the right hook to solve a particular problem.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Searching in the source code of WordPress or plugins
&lt;/h3&gt;

&lt;p&gt;The method that brings you the most from a didactic point of view is the direct search in, or reading of, the source code. &lt;strong&gt;As a developer, you should spend a significant amount of time reading and studying other people’s code.&lt;/strong&gt; And, of course, it’s important to know at least the parts of the code that are relevant to you, which you want to change with hooks. So, open the WordPress core (usually, this will be the files in &lt;em&gt;/wp-includes/&lt;/em&gt;) in the code editor of your choice (I recommend &lt;a href="https://code.visualstudio.com/"&gt;VS Code&lt;/a&gt;) and search for &lt;code&gt;apply_filters&lt;/code&gt; and &lt;code&gt;do_action&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uDkznye9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zsmeypjcqtrcocadeejj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uDkznye9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zsmeypjcqtrcocadeejj.png" alt="Search result: apply_filters" width="724" height="454"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;1.681 times &lt;code&gt;apply_filters&lt;/code&gt; is found&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this way, you are able to find all filter hooks WordPress is using, and you are learning, how WordPress uses hooks internally. Search for &lt;code&gt;add_filter&lt;/code&gt; and &lt;code&gt;add_action&lt;/code&gt; too and you will see, that WordPress uses its Hook itself. A method we will talk about later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EemAiA0p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fw5vmsor1tge2d0hc4l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EemAiA0p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fw5vmsor1tge2d0hc4l.png" alt="Search result: add_filter" width="724" height="454"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;405 times &lt;code&gt;add_filter&lt;/code&gt; is found&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Of course, you should also know the code of a plugin whose hooks you want to use, at least partially, or have it read once. Here you can also search for the used hooks.&lt;/p&gt;
&lt;h2&gt;
  
  
  Registering hooks inside a class
&lt;/h2&gt;

&lt;p&gt;Now we have found the hook we’ve been looking for and we want to use it in our WordPress plugin. Let’s follow the basic example from above: we have to add a class to the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag using the &lt;code&gt;body_class&lt;/code&gt; filter. We assume that this is the first step in the development of a comprehensive plugin, where it will be worthwhile to use advanced programming methods.&lt;/p&gt;

&lt;p&gt;We want to define hook registrations inside a class by using class methods for callbacks.There are three classic approaches to this.&lt;/p&gt;
&lt;h3&gt;
  
  
  Approach 1: Use the class constructor
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Frontend&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'body_class'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'addBodyClass'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addBodyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'my-added-class'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At an appropriate place, such as in the main file of the plugin or the main class (we’ll learn more about this in a later article), the class is then instantiated in some way. To keep things simple here, as this step is irrelevant for our discussion, let’s imagine that in the main file of the plugin, the instantiation takes place: &lt;code&gt;$frontend = new Frontend();&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the class is instantiated, the constructor is executed, and thus the &lt;code&gt;add_filter&lt;/code&gt; function is called. Our callback in the array notation &lt;code&gt;[$this, 'addBodyClass']&lt;/code&gt; is added to the &lt;code&gt;body_class&lt;/code&gt; filter with the default priority of &lt;code&gt;10&lt;/code&gt; and the default number of arguments of &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In a lenient view, this is a valid approach to register a filter callback. However, this approach has some drawbacks, especially in large plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We are misusing the class constructor to execute the &lt;code&gt;add_filter&lt;/code&gt; call. Strictly speaking, the constructor of a class should be used to assign the correct values to its parameters at the time of instantiation, in other words, to initialize the object.&lt;/li&gt;
&lt;li&gt;When we learn about unit testing later in this series, we will see that calling &lt;code&gt;add_filter()&lt;/code&gt; in the constructor makes testing the class more difficult. We haven’t covered unit testing yet, but I would like to briefly mention here the points that make using &lt;code&gt;add_filter()&lt;/code&gt; in the constructor challenging for testing:

&lt;ol&gt;
&lt;li&gt;By calling &lt;code&gt;add_filter()&lt;/code&gt;, the class becomes tightly coupled with WordPress. This makes it harder to test the class in isolation from WordPress, and we would have to work with elaborate mocks just to be able to test it properly.&lt;/li&gt;
&lt;li&gt;We violate the rule stating that class constructors should not have side effects but should only be responsible for object initialization. The side effect of adding the callback to the filter makes testing more complicated.
Therefore, we need an approach that allows us to avoid calling add_filter() from the constructor.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Approach 2: Register from outside the class
&lt;/h3&gt;

&lt;p&gt;In the second approach, we will remove the &lt;code&gt;add_filter()&lt;/code&gt; call from the class context and perform the call after the class is instantiated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Frontend&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addBodyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'my-added-class'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$frontend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Frontend&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'body_class'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$frontend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'addBodyClass'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a significant improvement for the class itself. The class is now better suited for unit testing, and we no longer misuse the class constructor. In our example, the class no longer needs a constructor.&lt;/p&gt;

&lt;p&gt;However, there are also drawbacks to this approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drawback 1: Separation of &lt;code&gt;add_filter()&lt;/code&gt; call and class/method&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When considering the overall structure of our plugin, contemplating its architecture, the question quickly arises as to where the class should be instantiated. Since we want to rely entirely on &lt;a href="https://marcuskober.com/autoloading-coding-standards-and-file-structure-in-wordpress-plugin-development/"&gt;autoloading&lt;/a&gt; (it wouldn’t be ideal to use autoloading for some classes while loading others with &lt;code&gt;require_once()&lt;/code&gt;), the instantiation of the class cannot happen in the PHP file of the class itself.&lt;/p&gt;

&lt;p&gt;We have the class file &lt;em&gt;src/Main/Frontend.php&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Main&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Frontend&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addBodyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'my-added-class'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The call of &lt;code&gt;add_filter()&lt;/code&gt; is then done, however, e.g. in the main file &lt;em&gt;my-plugin.php&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: My Plugin
*/&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Frontend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$frontend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Frontend&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'body_class'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$frontend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'addBodyClass'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the registration of the &lt;code&gt;addBodyClass()&lt;/code&gt; method of the Frontend class using &lt;code&gt;add_filter()&lt;/code&gt; is located in a different file from the class itself. This can lead to a state of unmanageable complexity and that makes changes more difficult, especially in large plugins.&lt;/p&gt;

&lt;p&gt;For example, if we realize that the number of arguments needs to be adjusted for a filter, we are forced to edit two files: we have to pass the number of arguments to the &lt;code&gt;add_filter()&lt;/code&gt; call in the main plugin file and then add the arguments themselves to the method in the class file.&lt;/p&gt;

&lt;p&gt;Furthermore, just by looking at the &lt;code&gt;Frontend&lt;/code&gt; class in our example, we cannot tell that the &lt;code&gt;addBodyClass()&lt;/code&gt; method is a callback for a hook. We would need to indicate this through a suitable PHP comment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cd"&gt;/**
 * Callback for Filter body_class
 */&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addBodyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we would need to establish a standard for such comments because if a team is working on the plugin, the hook comments should always have a consistent structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drawback 2: Complexity of registration code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This structure quickly leads to an ever-growing list of registration calls and class instantiations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: My Plugin
*/&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Frontend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Admin\Dashboard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$frontend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Frontend&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$backendDashboard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Dashboard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'body_class'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$frontend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'addBodyClass'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$frontend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'enqueueAssets'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'admin_init'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$backendDashboard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'registerDashboard'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The long lists of class instantiations and hook registrations quickly become unwieldy. More questions arise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How should the calls be organized – alphabetically, by class, by hook?&lt;/li&gt;
&lt;li&gt;How do we maintain an overview and create a quick reference in the code to determine which methods belong to which class?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Approach 3: Back to the class
&lt;/h3&gt;

&lt;p&gt;Approach 3 is the version I have used in my plugins for a long time. In this approach, we bring the hook registration back into the class to have the registrations closer to the methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Frontend&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'body_class'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'addBodyClass'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addBodyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'my-added-class'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The class can then be instantiated with just one call, for example, in the main plugin file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: My Plugin
*/&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Frontend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Frontend&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we have many classes, the list of &lt;code&gt;::register()&lt;/code&gt; calls can become long, and we will need to consider how to organize it differently. However, at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We are not using the constructor for hook registration with &lt;code&gt;add_filter()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The hook registration is happening within the class that also contains the callback method.&lt;/li&gt;
&lt;li&gt;This construction is somewhat better for unit testing, as the aforementioned side effect is easier to control through the static method, making the class more suitable for unit testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Nevertheless, this method also has its drawbacks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If multiple hooks are registered within a class, the static method may contain long lists of &lt;code&gt;add_filter()&lt;/code&gt; and &lt;code&gt;add_action()&lt;/code&gt; calls.&lt;/li&gt;
&lt;li&gt;With multiple hooks, the code becomes less organized as the registration calls in the static method are relatively far from their associated methods. Again, we would need to use appropriate comments to indicate which method belongs to which hook.&lt;/li&gt;
&lt;li&gt;For unit testing, this method is more suitable, but it would still be nice if the static method could be eliminated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Registering hooks using PHP attributes
&lt;/h2&gt;

&lt;p&gt;I occasionally enjoy reading the source code of non-WordPress projects and I’m interested in the structure of large frameworks like &lt;a href="https://symfony.com/"&gt;Symfony&lt;/a&gt; and &lt;a href="https://laravel.com/"&gt;Laravel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In Symfony’s &lt;a href="https://symfony.com/doc/current/routing.html"&gt;routing&lt;/a&gt;, I came across the use of PHP attributes for defining routes (&lt;a href="https://symfony.com/doc/current/routing.html#matching-http-methods"&gt;see here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;It looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;#[Route('/api/posts/{id}', methods: ['GET', 'HEAD'])]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I found that very elegant and immediately saw the resemblance to the registration of hooks in WordPress plugins!&lt;/p&gt;

&lt;h3&gt;
  
  
  What are PHP attributes?
&lt;/h3&gt;

&lt;p&gt;PHP attributes are metadata used to provide additional information about declarations such as classes, methods, properties, or functions. They allow you to write declarative code that influences the behavior of the corresponding elements in a clean and organized way.&lt;/p&gt;

&lt;h3&gt;
  
  
  How are PHP attributes defined?
&lt;/h3&gt;

&lt;p&gt;PHP attributes serve as metadata for classes, methods, functions, parameters, properties, and constants, and they are declared directly on the line above the element that should be adorned with an attribute.&lt;/p&gt;

&lt;p&gt;An attribute begins with the hash sign (&lt;code&gt;#&lt;/code&gt;), while the attribute itself is enclosed in square brackets. The simplest declaration looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;#[AttributName]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attributes can also have parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;#[AttributName('value')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We mainly use attributes on methods in this article, so the examples provided focus on that usage.&lt;/p&gt;

&lt;p&gt;What is important for us is also the ability to use attributes multiple times since it may be necessary to define two different hooks for a method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;#[Attribut1('value')]&lt;/span&gt;
&lt;span class="c1"&gt;#[Attribut2('value')]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;someMethod&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Attributes for hook registration
&lt;/h3&gt;

&lt;p&gt;Now, we want to achieve the ability to bind hook declarations directly to the method, so that our class looks like the following example. I have expanded the class with an additional hook to make it more illustrative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Frontend&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;#[Filter('body_class')]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addBodyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'my-added-class'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;#[Action('wp_enqueue_scripts')]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;enqueueAssets&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;wp_enqueue_style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'my-plugin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'style.css'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At a glance, we can see the advantages that PHP attributes offer compared to the other methods!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No more need for a static method.&lt;/li&gt;
&lt;li&gt;By using PHP attributes, the declaration of the hook is in direct proximity to the respective method. This is clear and concise, and we immediately know that the method is a callback for a hook. We can make changes in direct correlation to the method.&lt;/li&gt;
&lt;li&gt;The class is not only easier to cover with unit tests, but we can even establish tests to verify that the correct attributes, i.e., the correct hook registrations, are used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, we still have two major issues to address:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do PHP and WordPress know what to do with these attributes?&lt;/li&gt;
&lt;li&gt;How and where should classes with hooks be instantiated?
Fortunately, these questions are answered by each other, as we will see shortly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Defining Attributes
&lt;/h3&gt;

&lt;p&gt;First, we need to inform PHP that we want to use two attributes named &lt;code&gt;Filter&lt;/code&gt; and &lt;code&gt;Action&lt;/code&gt;, which accept the hook name, priority, and number of arguments as parameters.&lt;/p&gt;

&lt;p&gt;To do this, we create the folder &lt;em&gt;Attributes&lt;/em&gt; inside &lt;em&gt;src/&lt;/em&gt; and create the file &lt;em&gt;Filter.php&lt;/em&gt; inside it. With this addition, our sample plugin now has the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-plugin
├── src
│   ├── Attributes
│   │   └── Filter.php
│   └── Main
│       └── Frontend.php
└── my-plugin.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An attribute in PHP is nothing more than a simple class with its constructor setting the parameters for the attribute. We use &lt;a href="https://php.watch/versions/8.0/constructor-property-promotion"&gt;constructor property promotion&lt;/a&gt; to define the parameters:&lt;/p&gt;

&lt;p&gt;File &lt;em&gt;src/Attributes/Filter&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Attributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$hookName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$acceptedArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To inform PHP that our simple class is an attribute definition, we can use, ironically, an attribute itself. The attribute declares our &lt;code&gt;Filter&lt;/code&gt; class as an attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Attributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;#[Attribute]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$hookName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$acceptedArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, we have successfully defined our Filter attribute. We can further specify that the Filter attribute can only be used on methods and that we can use the attribute multiple times on a single method. We do this by specifying the &lt;a href="https://www.php.net/manual/en/language.attributes.classes.php"&gt;corresponding flags&lt;/a&gt; for the attribute: &lt;code&gt;Attribute::TARGET_METHOD&lt;/code&gt; and &lt;code&gt;Attribute::IS_REPEATABLE&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;#[Attribute(Attribute::TARGET_METHOD|Attribute::IS_REPEATABLE)]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.php.net/manual/en/language.attributes.classes.php"&gt;Here you can find more about attribute declaration in PHP.&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, we have established the &lt;code&gt;Filter&lt;/code&gt; attribute, but it doesn’t do anything yet. We now have three steps ahead of us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find a method for instantiating classes with hooks.&lt;/li&gt;
&lt;li&gt;Find a way to recognize that a class uses hooks.&lt;/li&gt;
&lt;li&gt;Actually register the hooks using &lt;code&gt;add_filter()&lt;/code&gt; and &lt;code&gt;add_action()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Loading, parsing, and instantiating classes with hooks
&lt;/h3&gt;

&lt;p&gt;We will now take care of steps 1 and 2 from the previous section. To do this, we need to tell our plugin where to find the classes that we want to check for hooks.&lt;/p&gt;

&lt;p&gt;For the overall architecture, it is important that the classes using hooks should not be instantiated elsewhere. They should only be instantiated through the code we are about to develop. Since classes should generally do only one thing and not have too much responsibility, this should not be a problem.&lt;/p&gt;

&lt;p&gt;There are different ways to locate the classes that use hooks. We could search through all classes, but that wouldn’t be very performant.&lt;/p&gt;

&lt;p&gt;First, we create a directory in the root folder of our plugin, which we can name config, and create a file inside it, which can be named &lt;em&gt;hooked-classes.php&lt;/em&gt;, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-plugin
├── config
│   └── hooked-classes.php
├── src
│   ├── Attributes
│   │   └── Filter.php
│   └── Main
│       └── Frontend.php
└── my-plugin.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the PHP file, we now list our only class that uses hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Frontend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * List of classes with hooks
 */&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Frontend&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the file simply returns an array with the fully qualified class name of Frontend (&lt;code&gt;::class&lt;/code&gt; is a static constant that contains the fully qualified class name, more &lt;a href="https://www.php.net/manual/de/language.oop5.basic.php#language.oop5.basic.class.class"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Next, we want to remove the main code from the plugin file &lt;em&gt;my-plugin.php&lt;/em&gt; and establish a plugin main class called &lt;code&gt;App&lt;/code&gt;, which we include under &lt;code&gt;Main&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-plugin
├── config
│   └── hooked-classes.php
├── src
│   ├── Attributes
│   │   └── Filter.php
│   └── Main
│       ├─── App.php
│       └── Frontend.php
└── my-plugin.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the App class in the _App.php _file is lean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Main&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the &lt;em&gt;my-plugin.php&lt;/em&gt; file is now very clean as well, but we need to define a constant that contains the path of the plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: My Plugin
 */&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'MYPLUGIN_DIR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;plugin_dir_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the main class, we now establish a private method that will handle our classes with hooks, and in the constructor of the class, we load the list of class names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Main&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$classNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;classNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="no"&gt;MYPLUGIN_DIR&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'config/hooked-classes.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;registerHooks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;registerHooks&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;classNames&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Code for registering hooks&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;registerHooks()&lt;/code&gt; method, we process the class names in a &lt;code&gt;foreach&lt;/code&gt; loop. But what exactly do we do with the class names we obtain in this way? How can we find out if and which hooks are registered here?&lt;/p&gt;

&lt;p&gt;We make use of PHP’s &lt;a href="https://www.php.net/manual/en/intro.reflection.php"&gt;Reflection API&lt;/a&gt; for this purpose. With this API, we can examine classes at runtime of the script.&lt;/p&gt;

&lt;p&gt;In our loop, we first create a reflection class of the original class using &lt;code&gt;new ReflectionClass()&lt;/code&gt; (&lt;a href="https://www.php.net/manual/de/class.reflectionclass.php"&gt;more about ReflectionClass&lt;/a&gt;). The reflection class can then provide us with all the methods of the class to be examined using the &lt;code&gt;getMethods()&lt;/code&gt; method (&lt;a href="https://www.php.net/manual/de/reflectionclass.getmethods.php"&gt;more about this method&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;classNames&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$reflectionClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReflectionClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$reflectionClass&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMethods&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Check methods for attributes&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we check if the method has attributes. The &lt;code&gt;getAttributes()&lt;/code&gt; method (&lt;a href="https://www.php.net/manual/de/reflectionfunctionabstract.getattributes.php"&gt;more info&lt;/a&gt;) can return all &lt;code&gt;Filter&lt;/code&gt; attributes in an array by passing the class name of our attribute to the method. If filter attributes are defined, we can then iterate over them with another &lt;code&gt;foreach&lt;/code&gt; loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$reflectionClass&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMethods&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Filter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$attributes&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$attribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Check attributes&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this final loop, we finally instantiate our class containing hooks and then need to figure out how to register the callback using &lt;code&gt;add_filter()&lt;/code&gt;. To do this, it would be good if we could first instantiate the attribute class &lt;code&gt;Filter&lt;/code&gt;. Fortunately, we can do this using the &lt;code&gt;newInstance()&lt;/code&gt; method of &lt;code&gt;$attribute&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$attributes&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$attribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Instantiate class with hooks&lt;/span&gt;
    &lt;span class="nv"&gt;$hookedClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Instantiate filter class&lt;/span&gt;
    &lt;span class="nv"&gt;$filterClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$attribute&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;newInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have everything we need in one place: we have an instance of the &lt;code&gt;Frontend&lt;/code&gt; class, an instance of the attribute class &lt;code&gt;Filter&lt;/code&gt;, and the method adorned with a filter attribute. We want to define the registration process within the &lt;code&gt;Filter&lt;/code&gt; class itself. To do this, we establish a &lt;code&gt;register()&lt;/code&gt; method that passes the callback as an array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;#[Attribute]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$hookName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$acceptedArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;callable&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hookName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;acceptedArgs&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the &lt;code&gt;register&lt;/code&gt; method is established, we can call it within the loop in the registerHooks method in the &lt;em&gt;App.php&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$attributes&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$attribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Instantiate class with hooks (Frontend in our case)&lt;/span&gt;
    &lt;span class="nv"&gt;$hookedClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Instantiate filter class&lt;/span&gt;
    &lt;span class="nv"&gt;$filterClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$attribute&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;newInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$filterClass&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nv"&gt;$hookedClass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are almost finished with the filter. We just need to ensure that each class with hooks is instantiated only once in &lt;code&gt;registerHooks&lt;/code&gt;. Multiple instantiation is not necessary here. To achieve this, we create an instance array and check whether the class has already been instantiated. For clarity, here is the complete method again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;registerHooks&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$instances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; 

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;classNames&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$reflectionClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReflectionClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$reflectionClass&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMethods&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Filter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$attributes&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$attribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Instantiate class if not instantiated&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;array_key_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$instances&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nv"&gt;$instances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="c1"&gt;// Instantiate filter class&lt;/span&gt;
                &lt;span class="nv"&gt;$filterClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$attribute&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;newInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nv"&gt;$filterClass&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="nv"&gt;$instances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                        &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indeed, we have now completed the registration of filters! We would now need to do the same for actions. But of course, this can be done more easily.&lt;/p&gt;

&lt;p&gt;Let’s take another look at this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Filter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now let’s check what we can do with &lt;code&gt;getAttributes()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;ReflectionFunctionAbstract&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interesting, so we can provide flags! A look at the &lt;a href="https://www.php.net/manual/de/reflectionfunctionabstract.getattributes.php"&gt;documentation&lt;/a&gt; shows that we can set the flag &lt;code&gt;ReflectionAttribute::IS_INSTANCEOF&lt;/code&gt;, which means that filtering is no longer done by the exact name of the attribute class, but instead &lt;code&gt;instanceof&lt;/code&gt; is used for filtering.&lt;/p&gt;

&lt;p&gt;This naturally leads us to the idea of using an &lt;a href="https://www.php.net/manual/de/language.oop5.interfaces.php"&gt;interface&lt;/a&gt; for the attributes. We create two new files in our Attributes folder: &lt;em&gt;Action.php&lt;/em&gt; and &lt;em&gt;HookInterface.php&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-plugin
├── config
│   └── hooked-classes.php
├── src
│   ├── Attributes
│   │   ├── Action.php
│   │   ├── Filter.php
│   │   └── HookInterface.php
│   └── Main
│       ├── App.php
│       └── Frontend.php
└── my-plugin.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;em&gt;HookInterface.php&lt;/em&gt; file, we establish our interface that requires our hook attributes to implement a &lt;code&gt;register()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Attributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;HookInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;callable&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;em&gt;Filter.php&lt;/em&gt; file, we now need to declare the Filter class as an implementation of &lt;code&gt;HookInterface&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;#[Attribute(Attribute::TARGET_METHOD|Attribute::IS_REPEATABLE)]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;HookInterface&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, we are indeed completely done with the filter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Action Attribute
&lt;/h3&gt;

&lt;p&gt;Creating the Action attribute class is now the easiest task. As we saw earlier, &lt;code&gt;add_action()&lt;/code&gt; internally just calls &lt;code&gt;add_filter()&lt;/code&gt;, which is why we can work with inheritance here. We open the &lt;em&gt;Action.php&lt;/em&gt; file and create the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Attributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;#[Attribute(Attribute::TARGET_METHOD|Attribute::IS_REPEATABLE)]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Action&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we only create a new class that completely extends the &lt;code&gt;Filter&lt;/code&gt; class. All properties from &lt;code&gt;Filter&lt;/code&gt; are inherited in &lt;code&gt;Action&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As the last step, we now need to adjust the &lt;code&gt;getAttributes()&lt;/code&gt; method in the &lt;code&gt;App::registerHooks()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HookInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ReflectionAttribute&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IS_INSTANCEOF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thus, &lt;code&gt;getAttributes()&lt;/code&gt; returns all attributes that implement the &lt;code&gt;HookInterface&lt;/code&gt;, which in our case are the attribute classes &lt;code&gt;Filter&lt;/code&gt; and &lt;code&gt;Action&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Outlook
&lt;/h2&gt;

&lt;p&gt;In this article, you have learned what hooks in WordPress are and how to register callbacks for hooks. We have seen the methods for registering hooks in classes and how to use autoloading, namespaces, and PHP attributes to implement hook registration in a simple, clear, and unit-test-compatible way.&lt;/p&gt;

&lt;p&gt;The solution we have developed is complex but can now be used in any of your plugins that require it. All you have to do is drag the Attributes folder into your new plugin and adjust the namespaces.&lt;/p&gt;

&lt;p&gt;In my opinion, we have created a good foundation that defines hooks exactly where the callback is located and ensures that everything is loaded almost magically through &lt;a href="https://marcuskober.com/autoloading-coding-standards-and-file-structure-in-wordpress-plugin-development/"&gt;autoloading&lt;/a&gt; without endless require and instantiation lists. In a later step, we should optimize the &lt;code&gt;registerHooks()&lt;/code&gt; method, or create a separate class for it. This will be covered in one of the upcoming articles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the next article, we will focus on structuring your plugin in such a way that you can rely solely on hooks. This will make your plugins virtually infinitely extensible and even more maintainable. A nice side effect is that you open your plugin for modifications and adjustments by other plugins.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a further article, we will return to the techniques described here and learn why &lt;strong&gt;dependency injection&lt;/strong&gt; is a must in this architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugin code
&lt;/h2&gt;

&lt;p&gt;You can find the entire plugin code &lt;a href="https://github.com/marcuskober/wp-articles-code"&gt;here on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Usually, you would add the &lt;em&gt;vendor&lt;/em&gt; directory to the &lt;em&gt;.gitignore&lt;/em&gt; file, as it has no place in the repository. For the sake of clarity, however, I made an exception here so you can see which code is generated by Composer.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>php</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Coding standards part 2: .editorconfig, documentation and strict types</title>
      <dc:creator>Marcus Kober</dc:creator>
      <pubDate>Tue, 16 May 2023 09:23:59 +0000</pubDate>
      <link>https://dev.to/marcuskober/coding-standards-part-2-editorconfig-documentation-and-strict-types-2jk2</link>
      <guid>https://dev.to/marcuskober/coding-standards-part-2-editorconfig-documentation-and-strict-types-2jk2</guid>
      <description>&lt;p&gt;&lt;strong&gt;In the &lt;a href="https://marcuskober.com/autoloading-coding-standards-and-file-structure-in-wordpress-plugin-development/"&gt;previous article&lt;/a&gt;, we already learned a lot about coding standards, and today I like to discuss three more points: the file &lt;em&gt;.editorconfig&lt;/em&gt;, the documentation of your code, and the declaration of strict types.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The file .editorconfig
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;.editorconfig&lt;/em&gt; file is a text file you create in the root folder of your plugin. This file ensures, that you and your team use the same standards for line breaks, indentations, and character sets throughout the project. Editors like Visual Studio Code can read this file and apply the formatting and settings automatically. &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt; requires &lt;a href="https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig"&gt;this extension&lt;/a&gt; for that purpose.&lt;/p&gt;

&lt;p&gt;The extension for Visual Studio code supports these settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;indent_style&lt;/code&gt;: Indentation style: spaces or tabs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;indent_size&lt;/code&gt;: Size of indentation when spaces are used (soft tabs)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tab_width&lt;/code&gt;: Size of indentation when tabs are used&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;end_of_line&lt;/code&gt; (applied when saving): Type of line breaks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;insert_final_newline&lt;/code&gt; (applied when saving): Specifies whether an empty line should be added to the end of the file (see PSR-2)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;trim_trailing_whitespace&lt;/code&gt; (applied when saving): Removes any whitespace after the actual code line&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the &lt;em&gt;.editorconfig&lt;/em&gt;, you can set the styles for each programming language or file extension.&lt;/p&gt;

&lt;p&gt;My file usually looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# http://editorconfig.org&lt;/span&gt;

&lt;span class="s"&gt;root = &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;charset = utf-8&lt;/span&gt;
&lt;span class="s"&gt;end_of_line = lf&lt;/span&gt;
&lt;span class="s"&gt;indent_size = &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="s"&gt;indent_style = space&lt;/span&gt;
&lt;span class="s"&gt;trim_trailing_whitespace = &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;.php&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;indent_size = &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
&lt;span class="s"&gt;insert_final_newline = &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;.scss&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;insert_final_newline = &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;.md&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;trim_trailing_whitespace = &lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;root = true&lt;/code&gt;, it is specified that the file is located in the root directory of your plugin. Text editors like VS Code initially search for a &lt;em&gt;.editorconfig&lt;/em&gt; file in the folder of the currently opened code file. If none is found, it searches one folder above, and so on. The line &lt;code&gt;root = true&lt;/code&gt; means that the search can end here and the file can be used since root has been reached.&lt;/p&gt;

&lt;p&gt;With the declarations under &lt;code&gt;[*]&lt;/code&gt;, the general settings are made, and below the settings for specific file extensions are overwritten. I want the final newline only in PHP and SCSS files, for example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Even if you don’t work in a team, I recommend using a &lt;em&gt;.editorconfig&lt;/em&gt; file. This ensures that you use the same styles in every project and on every device.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;More infos: &lt;a href="https://editorconfig.org/"&gt;editorconfig.org&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;Hated by many developers, it is still an important tool for you and your team. I would like to emphasize here that documenting your code is also important when developing your plugin alone. I promise you will love your documentation when you have to go through your code again after two to three months! Code quickly becomes distant, and after just a few months, you won’t remember specific details, like why you implemented something in a particular way and not differently.&lt;/p&gt;

&lt;p&gt;When it comes to documentation, we generally distinguish between two types of documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation in the code itself.&lt;/li&gt;
&lt;li&gt;External Documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Documentation inside your code
&lt;/h3&gt;

&lt;p&gt;In PHP, we distinguish not only single-line documentation with a preceding double slash (&lt;code&gt;//&lt;/code&gt;) and multi-line documentation in the following form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* This is a comment, 
   that spans
   over multiple lines
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also distinguish between documentation at the file, class, and function/method level. I recommend documenting in the &lt;a href="https://de.wikipedia.org/wiki/PHPDoc"&gt;PHPDoc format&lt;/a&gt;, as used, for example, by &lt;a href="https://docs.phpdoc.org/guide/getting-started/what-is-a-docblock.html#what-is-a-docblock"&gt;phpDocumentor&lt;/a&gt;. A PHPDoc block starts not with &lt;code&gt;/*&lt;/code&gt;, but with two asterisk symbols: &lt;code&gt;/**&lt;/code&gt;. Text editors like Visual Studio Code can quickly create these blocks when typing &lt;code&gt;/**&lt;/code&gt;, which reduces typing work.&lt;/p&gt;

&lt;p&gt;Here is an example of the various doc blocks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * At the very top appears the file-level documentation. Here you can discuss
 * what exactly is happening in this file. As with all documentation:
 * 
 * Document as much as necessary and as little as possible!
 * 
 * What this means, you can read further below.
 * 
 * Here you can, for example, specify the author and the version with which
 * this file was created:
 * 
 * @author: Marcus Kober
 * @since: 1.2.1
 */&lt;/span&gt; 

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\Test\Main&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\Test\Attributes\Filter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * DocBlock at the class level
 * 
 * The DocBlock is created directly before the class to be documented and indicates
 * what the class is for.
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Frontend&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * DocBlock at the variable level
     * 
     * Here you can explain what this variable does
     *
     * @var array
     */&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$addedClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * DocBlock at the method level
     * 
     * Here you can explain what the method does.
     * 
     * The DocBlock comes before the declaration of attributes, if
     * any are used.
     *
     * @param array $classes
     * @return array
     */&lt;/span&gt;
    &lt;span class="c1"&gt;#[Filter('body_class')]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;addClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Single-line comment, very briefly, explaining something specific&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addedClasses&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'my-class'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;array_merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$classes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addedClasses&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The statements starting with &lt;code&gt;@&lt;/code&gt;, such as &lt;code&gt;@author&lt;/code&gt; or &lt;code&gt;@since&lt;/code&gt;, are called “tags” of the DocBlock syntax. Here you can find a list of possible tags. For variables and methods, you can additionally specify types with &lt;code&gt;@var&lt;/code&gt;, or &lt;code&gt;@param&lt;/code&gt; and &lt;code&gt;@return&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Commenting your code with DocBlocks not only makes your code more transparent for you and your team. You can also use phpDocumentor to create &lt;a href="https://docs.phpdoc.org/guide/getting-started/generating-documentation.html#generating-documentation"&gt;external API documentation&lt;/a&gt; from sufficiently well-documented code. This can be added to your external documentation (if you need it) or even replace it.&lt;/p&gt;

&lt;h3&gt;
  
  
  External Documentation
&lt;/h3&gt;

&lt;p&gt;For very extensive plugins, you can create external documentation either publicly or just for yourself and/or your team. External documentation takes place outside of your code, for example, on a website, or you can use services designed for it, such as &lt;a href="https://www.gitbook.com/"&gt;GitBook&lt;/a&gt; or &lt;a href="https://readthedocs.org/"&gt;Read the Docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;External documentation is particularly important for documenting your own hooks. There is also an interesting project that automatically documents your hooks: the &lt;a href="https://github.com/pronamic/wp-documentor"&gt;Pronamic WordPress Documentor&lt;/a&gt;. It automatically generates documentation in various formats from your hook declarations. You can find more about hooks in the upcoming article on &lt;strong&gt;Hook-Driven Development.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Declare strict types
&lt;/h2&gt;

&lt;p&gt;Although the corresponding declaration is missing in most examples in the early articles of this series due to space constraints, I strongly recommend enabling PHP’s strict types check.&lt;/p&gt;

&lt;p&gt;The declaration is made with &lt;code&gt;declare(strict_types=1)&lt;/code&gt;. This should be done in every file of your plugin, and it must be this way to ensure that you fundamentally activate strict type checking. The &lt;code&gt;declare&lt;/code&gt; statement is placed immediately after the opening PHP tag at the beginning of the file and before the namespace declaration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MKMyPlugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// use statements&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refer to this as well: &lt;a href="https://www.php-fig.org/psr/psr-12/#3-declare-statements-namespace-and-import-statements"&gt;PSR-12: Declare Statements, Namespace, and Import Statements&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What does declare(strict_types=1) do exactly?
&lt;/h3&gt;

&lt;p&gt;The statement &lt;code&gt;declare(strict_types=1)&lt;/code&gt; enables strict type-checking in PHP. This leads to improved code quality and stability, as using strict type-checking allows errors to be detected more quickly and seen during development in the text editor.&lt;/p&gt;

&lt;p&gt;This checks the so-called scalar types, which are &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;, and &lt;code&gt;bool&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s take a look at the following function as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without enabling strict type checking, we can run the following without any issues and error messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1.2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// -&amp;gt; 3.6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PHP silently converts the &lt;code&gt;string('1.2')&lt;/code&gt; to &lt;code&gt;float(1.2)&lt;/code&gt; and then returns the calculated &lt;code&gt;float(3.6)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since this is a potential source of errors that can be difficult to find, we enable strict type checking in every PHP file with &lt;code&gt;declare(strict_types=1)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s see this in the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1.2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This leads to the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fatal error: Uncaught TypeError: Argument 2 passed to add() must be of the type float, string given
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, this also applies to the return value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we correctly pass two floats to the &lt;code&gt;add()&lt;/code&gt; function. However, since return type hinting is enabled and we specify that the function should return a &lt;code&gt;float&lt;/code&gt;, but at the same time cast the result of the addition as a &lt;code&gt;string&lt;/code&gt;, we will encounter the following error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fatal error: Uncaught TypeError: Return value of add() must be of the type float, string returned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; In order to use type checking correctly, your functions and methods must, of course, use type hinting! Type hinting refers to the type declarations, as seen, for example, in the declaration of the function above. There, we explicitly tell &lt;code&gt;$a&lt;/code&gt; and &lt;code&gt;$b&lt;/code&gt; that they should be of type &lt;code&gt;float&lt;/code&gt;, and we indicate to the function that the return value should also be of type &lt;code&gt;float&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;I strongly recommend equipping all PHP files of your plugin with &lt;code&gt;declare(strict_type=1)&lt;/code&gt;!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, you have learned about three ways to make your code cleaner and more maintainable. Two methods deal with the outer form of the code (&lt;em&gt;.editorconfig&lt;/em&gt; and documentation), and one with the quality and security of the actual code (Strict Types).&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>php</category>
    </item>
    <item>
      <title>Autoloading, coding standards and file structure in WordPress plugin development</title>
      <dc:creator>Marcus Kober</dc:creator>
      <pubDate>Wed, 10 May 2023 09:47:13 +0000</pubDate>
      <link>https://dev.to/marcuskober/autoloading-coding-standards-and-file-structure-in-wordpress-plugin-development-1fab</link>
      <guid>https://dev.to/marcuskober/autoloading-coding-standards-and-file-structure-in-wordpress-plugin-development-1fab</guid>
      <description>&lt;p&gt;&lt;strong&gt;In the &lt;a href="https://marcuskober.com/using-namespaces-in-wordpress-plugin-development/"&gt;last article&lt;/a&gt;, I explained the use of namespaces, which we need today to establish autoloading, which simultaneously defines our file and folder structure.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Declare classes in their own PHP files
&lt;/h2&gt;

&lt;p&gt;To keep our code clear, we distribute the code sensibly across several PHP files. Each class should be declared in its own file. This file must then be loaded into the main code (e.g. via &lt;code&gt;require_once()&lt;/code&gt;) before the class can be instantiated.&lt;/p&gt;

&lt;p&gt;Let’s assume the following folder structure of an example plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-plugin
├── class-01.php
├── class-02.php
└── my-plugin.php
Content class-01.php:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;class01&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'class01 instantiated.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we now want to instantiate the class01 in the plugin file &lt;em&gt;my-plugin.php&lt;/em&gt;, we must first include the file and then we can instantiate the class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * Plugin Name: MyPlugin
 */&lt;/span&gt;

&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/class-01.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$klasse01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;class01&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we also need &lt;code&gt;class02&lt;/code&gt; in this script, we also have to include the corresponding file first and then we can instantiate the class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * Plugin Name: MyPlugin
 */&lt;/span&gt;

&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/class-01.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/class-02.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$klasse01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;class01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$klasse02&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;class02&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can quickly become confusing even with a small number of classes and lead to long lists of &lt;code&gt;require_once()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now a requirement for our plugin changes and we have to introduce a service that must be used by &lt;code&gt;class02&lt;/code&gt;, for example.&lt;/p&gt;

&lt;p&gt;We add our service as a new class in the &lt;em&gt;service-01.php&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-plugin
├── class-01.php
├── class-02.php
├── my-plugin.php
└── service-01.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;service01&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'service01 instantiated.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And change &lt;code&gt;class02&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;class01&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;service01&lt;/span&gt; &lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;service01&lt;/span&gt; &lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;service01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'class02 and service01 instantiated.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have to include the file &lt;em&gt;service-01.png&lt;/em&gt; in the main file of the plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
* Plugin Name: MyPlugin
*/&lt;/span&gt;

&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/class-01.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/class-02.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/service-01.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$service01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;service01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$klasse01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;class01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$klasse02&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;class02&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And at this point, at the latest, it becomes confusing. First, there is a long list of &lt;code&gt;require_once()&lt;/code&gt;, and with each new class we need, this list becomes even longer. Moreover, our folder structure no longer looks very clear. In the next step, an inc folder could be introduced, containing all the files we want to include using &lt;code&gt;require_once()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-plugin
├── inc
│   ├── class-01.php
│   ├── class-02.php
│   └── servie-01.php
└── my-plugin.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In principle, there is nothing wrong with such a setup at this stage. However, as the plugin becomes more extensive and we have different classes like models, services, and classes with hooks, we also need a folder structure that organizes all the new files in a sensible and clear manner.&lt;/p&gt;

&lt;p&gt;In addition, we want to move away from the very long list of &lt;code&gt;require_once()&lt;/code&gt; calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing for Autoloading and code standards
&lt;/h2&gt;

&lt;p&gt;Autoloading refers to the automatic loading of required files, and to reliably use this, we need to equip our existing code with namespaces and follow certain coding standards. Coding standards affect both the code itself and the folder structure we will use. These standards help bring some order to the chaos that PHP generally allows. There are standards for indenting code (the eternal war: tabs or spaces?), naming variables, functions, classes, and methods, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why are standards important?
&lt;/h3&gt;

&lt;p&gt;Coding standards lead to more clarity, and they generally provide a schema for how something should look so that developers don’t have to worry about it, and so that everyone can quickly and easily find their way into unfamiliar code, especially in a team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coding standards
&lt;/h3&gt;

&lt;p&gt;Because it would be too easy otherwise, there are different standards. 😅 Coding standards are defined by different sources. There are standards set by WordPress itself, standards set by the PHP Framework Interoperability Group (PHP-FIG), and individual companies and agencies can also define their own standards, of course.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At this point, we are primarily interested in two definitions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The standards that &lt;a href="https://developer.wordpress.org/coding-standards/wordpress-coding-standards/"&gt;WordPress itself defines&lt;/a&gt;, with the &lt;a href="https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/"&gt;standards for PHP&lt;/a&gt; being important in this article.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.php-fig.org/psr/"&gt;PHP Standard Recommendation&lt;/a&gt;, or PSR for short, from the aforementioned &lt;a href="https://www.php-fig.org/"&gt;PHP-FIG&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;So which standards should we follow?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer you won’t like: &lt;em&gt;it depends&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It depends on your personal taste, and there is no right or wrong here. You can also define your own standards (which I do not recommend). The important thing is to actually stick to one thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which standards will we follow in this series?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I don’t want to dictate which standards you should follow, but I stick to the PSR, as they are also followed by other PHP frameworks like &lt;a href="https://symfony.com/"&gt;Symfony&lt;/a&gt; and &lt;a href="https://laravel.com/"&gt;Laravel&lt;/a&gt;. If you rely on PSR, you are not trapped in the WordPress universe and can seamlessly work on Symfony or Laravel projects as well. Opinions differ here, though; there are numerous advocates of the WordPress standard when programming for WordPress. If you ever want to become a core developer, you should definitely be aware that different coding standards exist.&lt;/p&gt;

&lt;p&gt;Please take the time, if you are not familiar with PSR, to review the following PSRs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.php-fig.org/psr/psr-1/"&gt;PSR-1&lt;/a&gt;: Basic Coding Standard&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.php-fig.org/psr/psr-2/"&gt;PSR-2&lt;/a&gt;: Coding Style Guide&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.php-fig.org/psr/psr-12/"&gt;PSR-12&lt;/a&gt;: Extended Coding Style&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;IMPORTANT:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PHP files that contain either pure PHP code or end with a PHP code block must not contain a closing PHP tag (&lt;code&gt;?&amp;gt;&lt;/code&gt;) (see PSR-2, 2.2 Files)! This can especially lead to a “Headers already sent” error in plugin development if whitespace or a blank line follows the &lt;code&gt;?&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Standard for folder structure
&lt;/h3&gt;

&lt;p&gt;Since we have to follow the namespaces in folder naming for autoloading, I don’t want to define fixed standards for the folder structure. This is ultimately more a matter of taste, especially in WordPress plugin development. Frameworks, however, are much stricter.&lt;/p&gt;

&lt;p&gt;For later (and also for PSR-4 autoloading, see below), it is important that all PHP files, except for the main plugin file, are located within a src folder. This becomes especially important when we get to unit tests, as the actual source code is in src and all associated tests are in tests. But also for autoloading, as we will see shortly, this is important.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our previous example code is now standardized
&lt;/h3&gt;

&lt;p&gt;To establish autoloading well, we now fully align our code with the standards and introduce namespaces. We make the following changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Class names are written in &lt;a href="https://www.php-fig.org/psr/psr-1/#3-namespace-and-class-names"&gt;StudlyCaps&lt;/a&gt;, so &lt;code&gt;Class01&lt;/code&gt; instead of &lt;code&gt;class01&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The file names are identical to the class names, so &lt;em&gt;Class01.php&lt;/em&gt; instead of &lt;em&gt;class-01.php&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;All files in src have namespaces, with the base namespace &lt;code&gt;MK\MyPlugin&lt;/code&gt; following the &lt;code&gt;Manufacturer\Plugin&lt;/code&gt; pattern, and our classes 1 and 2 are in the &lt;code&gt;Main&lt;/code&gt; sub-namespace, while the service is in &lt;code&gt;Service&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Folder structure
&lt;/h3&gt;

&lt;p&gt;We now establish the folder structure that follows the naming of the namespaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-plugin
├── src
│   ├── Main
│   │   ├── Class01.php
│   │   └── Class02.php
│   └── Service
│       └── Service01.php
└── my-plugin.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The files
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;src/Main/Class01.php&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Main&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Class01&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Class01 instantiated.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;src/Main/Class02.php&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Main&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Service\Service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Class02&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;Service01&lt;/span&gt; &lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Service01&lt;/span&gt; &lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;$service01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Class02 instantiated.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;src/Service/Service01.php&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\MyPlugin\Main&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Service01&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Service01 instantiated.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;my-plugin.php&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
* Plugin Name: MyPlugin
*/&lt;/span&gt;

&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/src/Main/Class01.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/src/Main/Class02.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/src/Service/Service01.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$service01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Service01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$class01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$class02&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class02&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using autoloading in WordPress plugins
&lt;/h2&gt;

&lt;p&gt;Basically, there are two ways to use autoloading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Self-configured autoloading via &lt;code&gt;spl_autoload_register()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;PSR-4 autoloading via Composer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I generally use autoloading via Composer and highly recommend it. However, to cover the most important things here, I also deal with &lt;code&gt;spl_autoload_register()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Autoloading with spl_autoload_register()
&lt;/h3&gt;

&lt;p&gt;With the PHP function &lt;code&gt;spl_autoload_register()&lt;/code&gt;, we can implement our own autoloading. To do this, let’s look at the &lt;a href="https://www.php.net/manual/function.spl-autoload-register.php"&gt;function call&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;spl_autoload_register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;callable&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$throw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$prepend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the function expects at least one callback function, and this function is given the name of the class to be loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now comment out the &lt;code&gt;require_once()&lt;/code&gt; calls in our main file my-plugin.php and insert &lt;code&gt;spl_autoload_register()&lt;/code&gt; for testing, using an &lt;a href="https://www.php.net/manual/en/functions.anonymous.php"&gt;anonymous function&lt;/a&gt; as the callback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
* Plugin Name: MyPlugin
*/&lt;/span&gt;

&lt;span class="c1"&gt;// require_once __DIR__ . '/src/Main/Class01.php';&lt;/span&gt;
&lt;span class="c1"&gt;// require_once __DIR__ . '/src/Main/Class02.php';&lt;/span&gt;
&lt;span class="c1"&gt;// require_once __DIR__ . '/src/Service/Service01.php';&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;spl_autoload_register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;var_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;$service01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Service01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$class01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$class02&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class02&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By disabling the &lt;code&gt;require_once()&lt;/code&gt; calls and integrating &lt;code&gt;spl_autoload_register()&lt;/code&gt;, PHP tries to get the appropriate PHP file via the autoloader when calling &lt;code&gt;new Service01()&lt;/code&gt;. To do this, PHP passes the fully qualified class name to the callback function that we passed to &lt;code&gt;spl_autoload_register()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All our callback does at the moment is to output the class name using &lt;code&gt;var_dump()&lt;/code&gt;. So our above code generates the following message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string(29) "MK\MyPlugin\Service\Service01"
Fatal error: Uncaught Error: Class "MK\MyPlugin\Service\Service01" not found in 
[(...)/wp-content/plugins]/autoload-plugin-02/autoload-plugin-01.php:18 
Stack trace: #0 (...)/wp-settings.php(453): include_once() #1 (...)/wp-config.php(93): 
require_once '...' #2 (...)/wp-load.php(50): require_once '...' #3 (...)/wp-admin/admin.php(34): 
require_once '...' #4 (...)/wp-admin/plugins.php(10): require_once '...' #5 {main} 
thrown in (...)/wp-content/plugins/autoload-plugin-02/autoload-plugin-01.php on line 18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code, I have replaced my local paths with (…) and added manual line breaks to avoid making the message too long.&lt;/p&gt;

&lt;p&gt;First, the output of &lt;code&gt;var_dump()&lt;/code&gt; appears:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s2"&gt;"MK\MyPlugin\Service\Service01"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the fully qualified name of the class that is instantiated first.&lt;/p&gt;

&lt;p&gt;Then follows the Fatal Error, as the class could not be found. We now need to ensure in our callback that the appropriate file is loaded.&lt;/p&gt;

&lt;p&gt;Since we have made it very easy for ourselves with the namespaces and the corresponding folder structure, we can get the filename with a few simple adjustments to the fully qualified class name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
* Plugin Name: MyPlugin
*/&lt;/span&gt;

&lt;span class="c1"&gt;// require_once __DIR__ . '/src/Main/Class01.php';&lt;/span&gt;
&lt;span class="c1"&gt;// require_once __DIR__ . '/src/Main/Class02.php';&lt;/span&gt;
&lt;span class="c1"&gt;// require_once __DIR__ . '/src/Service/Service01.php';&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;spl_autoload_register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// MKMyPlugin vom Klassennamen durch den Pfad zu src ersetzen:&lt;/span&gt;
    &lt;span class="nv"&gt;$className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'MK\\MyPlugin\\'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/src/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Die restlichen Backslashes durch Verzeichnis-Trenner (Slashes) ersetzen und .php anhängen&lt;/span&gt;
    &lt;span class="nv"&gt;$classFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'\\'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Klassen-Datei laden&lt;/span&gt;
    &lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="nv"&gt;$classFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;$service01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Service01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$class01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$class02&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class02&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example of the &lt;code&gt;Service01&lt;/code&gt; class, the class name is &lt;code&gt;MK\MyPlugin\Service\Service01&lt;/code&gt;. The part &lt;code&gt;MK\MyPlugin&lt;/code&gt; can be seen as an alias to the src directory of our plugin.&lt;/p&gt;

&lt;p&gt;So, to get the path for the class file from this, in the first step we only need to replace the part &lt;code&gt;MK\MyPlugin&lt;/code&gt; with &lt;code&gt;__DIR__ . '/src/&lt;/code&gt;. Afterward, we exchange all backslashes (&lt;code&gt;\&lt;/code&gt;) for the directory separator (slash, &lt;code&gt;/&lt;/code&gt;) and add the extension &lt;code&gt;.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, &lt;code&gt;MK\MyPlugin\Service\Service01&lt;/code&gt; becomes &lt;code&gt;(…)/wp-content/plugins/my-plugin/src/Service/Service01.php&lt;/code&gt;, which exactly matches our file. The part (…) corresponds to your local path to the WordPress installation.&lt;/p&gt;

&lt;p&gt;With the above code, our plugin should now automatically reload all class files. So now we just need to remove our redundant, commented out code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
* Plugin Name: MyPlugin
*/&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;spl_autoload_register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Replace MK\MyPlugin in the class name with the path to src:&lt;/span&gt;
    &lt;span class="nv"&gt;$className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'MK\\MyPlugin\\'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/src/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Replace the remaining backslashes with directory separators (slashes) and append .php&lt;/span&gt;
    &lt;span class="nv"&gt;$classFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'\\'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Load class file&lt;/span&gt;
    &lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="nv"&gt;$classFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;$service01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Service01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$class01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$class02&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class02&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if we now implement this code and reload the page in our WordPress installation, we will be confronted with a new error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warning: require_once(WP_Site_Health.php): Failed to open stream: No such file or directory 
in (...)/wp-content/plugins/autoload-plugin-02/autoload-plugin-01.php on line 27

Fatal error: Uncaught Error: Failed opening required 'WP_Site_Health.php' (include_path='.:/usr/share/php:/www/wp-content/pear') 
in (...)/wp-content/plugins/autoload-plugin-02/autoload-plugin-01.php:27 
Stack trace: #0 [internal function]: {closure}('WP_Site_Health') 
#1 (...)/wp-settings.php(604): class_exists('WP_Site_Health') #2 (...)/wp-config.php(93): 
require_once('...') #3 (...)/wp-load.php(50): require_once('...') 
#4 (...)/wp-admin/admin.php(34): require_once('...') #5 (...)/wp-admin/plugins.php(10): 
require_once('...') #6 {main} thrown in (...)/wp-content/plugins/autoload-plugin-02/autoload-plugin-01.php on line 27
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What’s happening here?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PHP tries to load the WordPress core class WP_Site_Health, from which our callback code makes the file WP_Site_Health.php, and of course, this cannot be found.&lt;/p&gt;

&lt;p&gt;The autoloader callback that we register with &lt;code&gt;spl_autoload_register()&lt;/code&gt; is declared in the global namespace. So, we need to ensure that we only process class names that are actually present in our namespace, as PHP uses the callback for every loaded class that is loaded after registration. This includes WordPress core classes.&lt;/p&gt;

&lt;p&gt;To ensure that only class names in our namespace are handled in our autoloader, we add the following check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
* Plugin Name: MyPlugin
*/&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;spl_autoload_register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nb"&gt;strpos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'MK\\MyPlugin'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Replace MK\MyPlugin in the class name with the path to src:&lt;/span&gt;
    &lt;span class="nv"&gt;$className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'MK\\MyPlugin\\'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/src/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Replace the remaining backslashes with directory separators &lt;/span&gt;
    &lt;span class="nv"&gt;$classFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'\\'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Load class file&lt;/span&gt;
    &lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="nv"&gt;$classFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;$service01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Service01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$class01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$class02&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class02&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;if (false === strpos($className, 'MK\MyPlugin'))&lt;/code&gt;, we check if the beginning of our namespace is present in the class name. If not (&lt;code&gt;strpos&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;), we terminate the callback with &lt;code&gt;return&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we have a working autoloader that only handles the class names of our plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Autoloading with Composer
&lt;/h2&gt;

&lt;p&gt;The manual transformation of the class name in the callback of &lt;code&gt;spl_autoload_register()&lt;/code&gt; and the exception handling of class names outside the namespace of the plugin are both cumbersome and prone to errors.&lt;/p&gt;

&lt;p&gt;In terms of code reusability, this version of autoloading doesn’t score well, as we always have to manually adjust the namespace in at least two places.&lt;/p&gt;

&lt;p&gt;It would be much nicer if we could use an established solution here. That’s where &lt;a href="https://getcomposer.org/"&gt;Composer&lt;/a&gt; comes in. Composer is a cross-platform dependency management tool for PHP that helps developers easily manage and automatically download required libraries and packages for their projects. At the same time, Composer also offers a &lt;a href="https://www.php-fig.org/psr/psr-4/"&gt;PSR-4&lt;/a&gt;-based autoloading that we want to use for our plugin from now on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up Composer
&lt;/h3&gt;

&lt;p&gt;To use Composer in the plugin, we first need to set up Composer. Composer must already be installed on your device; you can follow &lt;a href="https://getcomposer.org/doc/00-intro.md"&gt;this tutorial&lt;/a&gt; to install it.&lt;/p&gt;

&lt;p&gt;Once Composer is installed, please open a terminal (I recommend &lt;a href="https://www.warp.dev/"&gt;Warp&lt;/a&gt; if you’re on a Mac, or &lt;a href="https://hyper.is/#installation"&gt;hyper.js&lt;/a&gt; otherwise) and switch to your plugin directory.&lt;/p&gt;

&lt;p&gt;Then enter the following command: &lt;code&gt;composer init&lt;/code&gt;. The Composer config generator will guide you through the setup. In most cases, you can use the default settings.&lt;/p&gt;

&lt;p&gt;Since we don’t want to define any dependencies yet, you can answer the questions &lt;code&gt;“Would you like to define your dependencies (require) interactively”&lt;/code&gt; and &lt;code&gt;“Would you like to define your dev dependencies (require-dev) interactively”&lt;/code&gt; with no.&lt;/p&gt;

&lt;p&gt;Then comes the question we need:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Add PSR-4 autoload mapping? Maps namespace "Marcuskober\MyPlugin" to the entered relative path. [src/, n to skip]:&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here, Composer automatically generates a namespace from the initially specified package name, which by default consists of the name you chose when setting up Composer and the directory name. We confirm the selection here with enter to choose the name and the suggested src directory.&lt;/p&gt;

&lt;p&gt;The setup must then be confirmed with a yes.&lt;/p&gt;

&lt;p&gt;Composer now creates a vendor directory for your plugin and the config file &lt;em&gt;composer.json&lt;/em&gt;. The vendor directory already contains the files that Composer needs for autoloading, and packages will be placed in this directory later if you install them via Composer.&lt;/p&gt;

&lt;p&gt;My config file now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"marcuskober/my-plugin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"autoload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"psr-4"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Marcuskober\\MyPlugin\\"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"authors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Marcus Kober"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"marcus.kober@gmail.com"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, I need to correct the namespace under &lt;code&gt;"psr-4”&lt;/code&gt; so that it matches our chosen namespace. Depending on your choice of name, directory, and namespaces, you may also need to do this.&lt;/p&gt;

&lt;p&gt;So, we’ll change this (from &lt;code&gt;Marcuskober&lt;/code&gt; to &lt;code&gt;MK&lt;/code&gt;) accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"psr-4"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"MK\\MyPlugin\\"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After changing the namespace in composer.json, we need to inform Composer to adjust its autoload files accordingly. To do this, we enter the following command in the terminal: &lt;code&gt;composer dump-autoload&lt;/code&gt;. If Composer returns &lt;code&gt;Generated autoload files&lt;/code&gt; in the terminal, we have correctly set up Composer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using composer autoload in our plugin
&lt;/h2&gt;

&lt;p&gt;To use Composer autoloading in our plugin, all we need to do is remove the &lt;code&gt;require_once()&lt;/code&gt; calls and load the file &lt;em&gt;vendor/autoload.php&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
* Plugin Name: MyPlugin
*/&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Class02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;MK\MyPlugin\Main\Service01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$service01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Service01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$class01&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class01&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$class02&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Class02&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$service01&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And simply by adding the line &lt;code&gt;require __DIR__ . '/vendor/autoload.php';&lt;/code&gt;, the autoloading with Composer works directly out of the box!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Outlook
&lt;/h2&gt;

&lt;p&gt;As you can see, autoloading with Composer is not rocket science and, unlike &lt;code&gt;spl_autoload_register()&lt;/code&gt;, is quickly and easily implemented.&lt;/p&gt;

&lt;p&gt;Once your plugins have reached a certain size, I recommend using autoloading with Composer. Even though you can freely adjust the settings, autoloading still forces you to use clean class naming, a logical folder structure, and the use of a single src directory for your PHP files. And believe me, the constraint is purely positive at this point.&lt;/p&gt;

&lt;p&gt;In the upcoming articles in this series, we will delve deeper into the development and structuring of complex plugins, and the code will become more tangible as we will see usable code after these rather theoretical first articles!&lt;/p&gt;

&lt;p&gt;In the next article, we will focus on hooks and their registration from your plugin.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>wordpress</category>
      <category>php</category>
    </item>
    <item>
      <title>Namespaces in WordPress plugin development</title>
      <dc:creator>Marcus Kober</dc:creator>
      <pubDate>Wed, 03 May 2023 10:34:03 +0000</pubDate>
      <link>https://dev.to/marcuskober/namespaces-in-wordpress-plugin-development-3d62</link>
      <guid>https://dev.to/marcuskober/namespaces-in-wordpress-plugin-development-3d62</guid>
      <description>&lt;p&gt;&lt;strong&gt;In this article I'll point out, what's the reason for using namespaces in our WordPress plugins and what namespaces are. We will learn, that namespaces are important for the upcoming topics in this series.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with naming constants, functions, and classes in WordPress plugins
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.wordpress.org/plugins/plugin-basics/best-practices/#avoid-naming-collisions"&gt;Avoiding name collisions&lt;/a&gt; is of paramount importance in plugin development. If you use constants, functions, and classes directly in your plugin file, these are registered in the global namespace. Therefore, there is a risk that you will overwrite constants, functions, or classes from other plugins (or from WordPress itself), or vice versa. As a result, your plugin (or other plugins) may not function correctly, or a PHP error may be thrown.&lt;/p&gt;

&lt;p&gt;So, let’s assume that there are two plugins installed on our system, each declaring a function called &lt;code&gt;plugin_init()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Plugin file &lt;em&gt;plugin-01/plugin-01.php&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: Plugin #1
 */&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;plugin_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// some code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plugin file &lt;em&gt;plugin-02/plugin-02.php&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: Plugin #2
 */&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;plugin_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// some code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will lead to the following error: &lt;code&gt;PHP Fatal error: Cannot redeclare plugin_init() (previously declared in /../plugin-01/plugin-01.php)&lt;/code&gt;. The reason behind this is that PHP does not allow the declaration of two functions with the same name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a prefix to solve this problem
&lt;/h2&gt;

&lt;p&gt;We can now come up with a prefix for our plugin, which we will then use consistently throughout our plugin. A prefix is just a string of characters that we put in front of our function and class names, usually followed by an underscore.&lt;/p&gt;

&lt;p&gt;Please keep in mind that the prefix must be as unique as possible. If we only use our plugin name, it is possible that another plugin also has that name, which then leads to naming conflicts again. A short combination of your name and the name of your plugin should generally be safe.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: Prefixed plugin
 */&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;mk_pp_plugin_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// some code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;mk_pp_get_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// some code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we have chosen &lt;em&gt;mk_pp&lt;/em&gt; as a prefix (a short form of Marcus Kober and an abbreviation for the plugin) and now use this for all our constants and functions. As you can see, this leads to long names and surely doesn’t contribute to clarity and readability. However, such a prefix is relatively safe. But can’t we do it even more elegantly?&lt;/p&gt;

&lt;h2&gt;
  
  
  What are namespaces?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.php.net/manual/en/language.namespaces.rationale.php"&gt;Namespaces&lt;/a&gt; offer the ability to encapsulate elements of your code so as not to cause name collisions. Essentially, namespaces are a mechanism for organizing functions, classes, and constants. A namespace defines a unique area in which the names of functions and classes are encapsulated. This makes it possible to use identical names within different namespaces.&lt;/p&gt;

&lt;p&gt;You can also imagine namespaces like a folder system. The “subfolders” are separated with backslashes (&lt;code&gt;/&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For the root namespace, I recommend using your name or that of your company, or an abbreviation thereof. In my case, that would be &lt;code&gt;MK&lt;/code&gt;. Then follows the name of your plugin (or an abbreviation thereof): &lt;code&gt;MK\MyPlugin&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  We use namespaces
&lt;/h2&gt;

&lt;p&gt;The namespace is defined at the beginning of each PHP file in your plugin. Of course, you should again make sure not to use too generic namespaces like simply Plugin, as this could also lead to a name collision of the namespace.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: Namespaced plugin
 */&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\NamespacePlugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;plugin_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// some code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// some code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://www.php.net/manual/en/language.namespaces.faq.php#language.namespaces.faq.full"&gt;fully qualified name&lt;/a&gt; of the function &lt;code&gt;plugin_init()&lt;/code&gt; is now: &lt;code&gt;MK\NamespacePlugin\plugin_init()&lt;/code&gt;. This resolves the potential name conflict with a possibly existing function &lt;code&gt;plugin_init()&lt;/code&gt; in another plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Namespaces in WordPress plugins
&lt;/h2&gt;

&lt;p&gt;Now let’s try to register a function inside a namespace as a callback for a hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: Namespaced plugin
 */&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\NamespacePlugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;plugin_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// some code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'init'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'plugin_init'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will lead to a Fatal error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fatal error: Uncaught TypeError: call_user_func_array(): Argument #1 ($callback) must be a valid callback, function "plugin_init" not found or invalid function name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why does this error message appear? After all, the function &lt;code&gt;plugin_init()&lt;/code&gt; does exist in our plugin. To understand, let’s look at the file where the error occurs. In the PHP error description, we see below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;thrown in /.../wp-includes/class-wp-hook.php on line 308
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the error occurs in the WordPress core file &lt;em&gt;class-wp-hook.php&lt;/em&gt;, where the core function &lt;code&gt;apply_filters()&lt;/code&gt; is defined, which calls the callback that we defined in &lt;code&gt;add_action()&lt;/code&gt;. However, the file &lt;em&gt;class-wp-hook.php&lt;/em&gt; is outside of the namespace &lt;code&gt;MK\NamespacePlugin&lt;/code&gt; that we defined, which is why we need to give the function &lt;code&gt;add_action()&lt;/code&gt; the fully qualified name of the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: Namespaced plugin
 */&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MK\NamespacePlugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;plugin_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// some code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'init'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'MK\NamespacePlugin\plugin_init'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So that we don’t have to manually type in the full name everywhere, we can also provide the call with the &lt;a href="https://www.php.net/manual/en/language.namespaces.nsconstants.php"&gt;magic constant&lt;/a&gt; &lt;code&gt;__NAMESPACE__&lt;/code&gt;, which contains the name of the current namespace as a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'init'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;__NAMESPACE__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'plugin_init'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using namespaces is a clean and efficient way to encapsulate our constants, functions, and classes from the global namespace and thus prevent name collisions.&lt;/p&gt;

&lt;p&gt;In the next article of this series, we will also see that we need namespaces for autoloading and the folder structure of plugins.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>wordpress</category>
      <category>php</category>
    </item>
    <item>
      <title>You might not be using object-oriented programming</title>
      <dc:creator>Marcus Kober</dc:creator>
      <pubDate>Fri, 28 Apr 2023 12:56:36 +0000</pubDate>
      <link>https://dev.to/marcuskober/you-might-not-be-using-object-oriented-programming-36ab</link>
      <guid>https://dev.to/marcuskober/you-might-not-be-using-object-oriented-programming-36ab</guid>
      <description>&lt;p&gt;&lt;strong&gt;What lies behind this somewhat provocative headline? Over time, I have noticed something about certain WordPress plugins. What exactly, I will describe here.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, I must admit, that I did not only notice that with plugins from other developers, but first and foremost with older plugins by myself. When I was developing my first plugins, I proclaimed with the utmost conviction at some point: "I now use object-oriented programming in my plugins!". Unfortunately, this was not the truth. My code looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: Not object oriented
 * Description: I'm not object oriented
 * Author: Marcus Kober
 */&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MK_MyPlugin&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'init'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'init'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'assets'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getResults&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Some more code&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// wp_enqueue...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getResults&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Some code&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// More methods&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MK_MyPlugin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what did I do? I have packed all functionality of the plugin inside one large class and then instantiated it directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complete code inside a huge single class does not make it object-oriented programming
&lt;/h2&gt;

&lt;p&gt;If all the code of your plugin resides within a single class, and this class is instantiated at runtime, this is some sort of programming inside a class, but not yet object-oriented programming.&lt;/p&gt;

&lt;p&gt;All that is achieved with the code above is merely a better encapsulation of the code so that there is no need for prefixing your functions and variables to avoid name conflicts (more on that in the upcoming article about namespaces). Unfortunately, it's nothing more like that and certainly not object-oriented programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, what type of programming do we have here?
&lt;/h2&gt;

&lt;p&gt;Next to &lt;a href="https://en.wikipedia.org/wiki/Object-oriented_programming"&gt;object-oriented programming&lt;/a&gt; is &lt;a href="https://en.wikipedia.org/wiki/Procedural_programming"&gt;procedural programming&lt;/a&gt;. In procedural programming the code is organized in steps, to get a clear structure. Code fragments are often organized in functions and every step is represented by a code line. One step is, for example, a variable declaration, a function call, an if-query, and so on.&lt;/p&gt;

&lt;p&gt;The code of our little plugin introduced above would look something like that in procedural programming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Plugin Name: Not object oriented
 * Description: I'm not object oriented
 * Author: Marcus Kober
 */&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;mk_myplugin_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mk_myplugin_getResults&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Some more code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;mk_myplugin_assets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// wp_enqueue...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;mk_myplugin_getResults&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Some code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'init'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'mk_myplugin_init'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'mk_myplugin_assets'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only difference to the first code example is that the various functions of the plugin were implemented as methods of a single class. Essentially, the problem is solved in the same procedural way in both versions of the code, only the arrangement of the code is different.&lt;/p&gt;

&lt;p&gt;In the world of WordPress, you'll find many such examples of procedural programming. Many plugins and even large parts of the WordPress core are based or were based on procedural programming, and that is absolutely fine. Even though many parts of WordPress are now built as classes, these are obscured in the WordPress API by functions such as get_posts().&lt;/p&gt;

&lt;p&gt;In the first article of this series, I already wrote: it is not absolutely necessary to use object-oriented programming and modern PHP for every plugin. However, especially for more extensive plugins, object-oriented programming helps enormously in structuring the code (alongside other advantages that we will get to know).&lt;/p&gt;

&lt;h2&gt;
  
  
  What does object-oriented programming solve?
&lt;/h2&gt;

&lt;p&gt;Let's assume you are developing a plugin. At first, it's not very complicated. Procedural programming would not be a problem here and should be the way to go.&lt;/p&gt;

&lt;p&gt;Over time, however, several things happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;new functions need to be implemented&lt;/li&gt;
&lt;li&gt;bugs need to be fixed&lt;/li&gt;
&lt;li&gt;perhaps there are problems with other plugins and a workaround needs to be implemented&lt;/li&gt;
&lt;li&gt;new WordPress versions are leading to problems that need to be addressed&lt;/li&gt;
&lt;li&gt;and so on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where procedural programming quickly reaches its limits. Such issues indicate that something should be done about the general structure of the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where should the new code be integrated?&lt;/li&gt;
&lt;li&gt;Does the new code lead to conflicts with existing code?&lt;/li&gt;
&lt;li&gt;Should the code be split into multiple files?&lt;/li&gt;
&lt;li&gt;How should the new files be named?&lt;/li&gt;
&lt;li&gt;How much code and which code should the files contain?&lt;/li&gt;
&lt;li&gt;In what order should these files be loaded?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's assume, we solved all those issues with procedural programming. We now have a large codebase, distributed over many files with more or less speaking file names. The code gets more and more complex and tasks such as bug fixing or feature integration become increasingly difficult.&lt;/p&gt;

&lt;p&gt;But now a new problem arrives: a client is calling and wants you to develop a new plugin. You recognize that the new plugin needs some parts of your last plugin. The reusability of code that is so deeply entangled in your codebase is certainly not very high. You can indeed copy certain parts of the code, but you probably have to untangle many tight wirings first to really integrate the code into the new plugin.&lt;/p&gt;

&lt;p&gt;That costs you time - and therefore either you or the client money!&lt;/p&gt;

&lt;p&gt;That's where object-oriented programming enters the stage. In object-oriented programming, as small classes as possible work together to solve a problem in its entirety, while each participating class solves only a narrowly defined sub-problem. This not only increases the readability of the code and leads to a more orderly structure, but also facilitates the reuse of the code. In this way, certain classes can be used in different plugins without having to complicatedly extract them from the existing code.&lt;/p&gt;

&lt;p&gt;Done right, object-oriented programming increases the readability, maintainability, reusability, and testability of your code, leading to cleaner code and - hopefully - lesser bugs. 😊&lt;/p&gt;

&lt;p&gt;What exactly does a WordPress plugin look like, that is based on object-oriented programming? We will work that out together in this series of articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Of course, object-oriented programming brings its own problems, but it is precisely these problems that I want to address in this series and hopefully solve to some extent. By using object-oriented programming, as a developer, you have a wealth of often proven solutions for recurring problems at your disposal, the so-called design patterns. Autoloading relieves you of the agony of choice of when to load which file, and dependency injection takes away the question of which class to instantiate when. All of this we will discuss in this article series.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>php</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Modern object-oriented PHP in WordPress plugin development</title>
      <dc:creator>Marcus Kober</dc:creator>
      <pubDate>Thu, 27 Apr 2023 13:16:59 +0000</pubDate>
      <link>https://dev.to/marcuskober/modern-object-oriented-php-in-wordpress-plugin-development-4emk</link>
      <guid>https://dev.to/marcuskober/modern-object-oriented-php-in-wordpress-plugin-development-4emk</guid>
      <description>&lt;p&gt;&lt;strong&gt;In this article series, I will present methods for building WordPress plugins in a modern and object-oriented way. The focus will be on code quality, reusability, maintainability, and extensibility of plugins and plugin code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this first article, I would like to briefly introduce myself and explain why I'm writing this series. I also want to highlight when these approaches and methods are unsuitable and provide additional considerations when considering modern PHP and WordPress plugins.&lt;/p&gt;

&lt;p&gt;My Name is &lt;a href="https://marcuskober.com/about-me/"&gt;Marcus&lt;/a&gt; and at the time of publication, I'm 47 years old, living in Cologne with my wife and two kids. I have been professionally developing websites and web apps using PHP since 1999, and since 2005 using WordPress too. During this time, I have worked on large-scale web application projects using Yii2, Symfony, and Laravel. Furthermore, I have developed over 150 custom WordPress themes and more than 50 different plugins during this time. These themes and plugins were mainly commissioned by clients, including some very extensive plugins (with over 100 PHP files) that are installed on many client sites. Since then, I have enjoyed thinking and learning about plugin architecture and software architecture in general, always striving to improve my code.&lt;/p&gt;

&lt;p&gt;There are many good tutorials and articles on plugin development available on the internet, but most of them end just where it becomes interesting for developing more complex and larger plugins. Unfortunately, there are very few resources available on code organization, file structure, and advanced PHP programming in WordPress plugins.&lt;/p&gt;

&lt;p&gt;That's why I developed the desire to gather all the knowledge I have acquired so far. In one place and as comprehensively as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern PHP and WordPress - do they even fit together?
&lt;/h2&gt;

&lt;p&gt;WordPress itself is not really built with object-oriented programming and does not use modern PHP due to backward compatibility. However, this does not mean that new plugins must follow this philosophy. If you are developing plugins for the WordPress Plugin Repository and want to reach the widest possible audience of users, it may be advisable to avoid certain PHP features that were introduced later. It is also essential to explicitly specify the PHP version in the plugin header so that plugin activation is prevented on WordPress installations with incompatible PHP versions. For example: Requires PHP: 8.1.&lt;/p&gt;

&lt;p&gt;So yes, if you know what you're doing, you can use modern PHP in your plugins. Especially if you have control over who receives your plugins (for example, in commissioned work) or even manage the hosting for your clients, there is absolutely nothing against it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Brillant idea, from now on I will use these advanced techniques and object orientation for all my plugins!
&lt;/h2&gt;

&lt;p&gt;No, please don't. Not every plugin needs those things. Especially smaller plugins with a few lines of code and just a few functions are better developed without these advanced methods. It's not necessary to generate more overhead as needed for a project. Plugins that register only two or three hooks, haven't to use autoloading, PHP attributes, and dependency injection (more on this in the following articles).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the focus on PHP when WordPress is moving more and more towards JavaScript / React?
&lt;/h2&gt;

&lt;p&gt;Although I am a big fan of Gutenberg and block themes, and developing custom blocks is one of my favorite topics, PHP remains relevant for extensive plugins. Therefore, this article series is also of long-term importance.&lt;/p&gt;

&lt;p&gt;I may also write some articles on block development after this series. However, for now, my focus is on how modern PHP techniques can be effectively used in WordPress plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preview
&lt;/h2&gt;

&lt;p&gt;In this article series, the focus is on efficiently using hooks and implementing them with modern PHP. Through many practical code examples, we will explore topics such as dependency injection, its importance, and implementation in WordPress plugins, as well as methods for unit testing and its implementation in the WordPress context.&lt;/p&gt;

&lt;p&gt;I will strive to regularly publish new articles and - as far as my job and family allow - to already have many articles completed before the first article goes online. 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is this article series for?
&lt;/h2&gt;

&lt;p&gt;You should already have some experience with WordPress development and want to take the next step. You should also have heard of object-oriented programming before.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://marcuskober.com/modern-object-oriented-php-in-wordpress-plugin-development/"&gt;Source&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>wordpress</category>
      <category>php</category>
    </item>
  </channel>
</rss>
