WordPress plugin constructors seem to be more and more a topic of debate when it comes to what they should define. I’ve talked about it before but it’s okay to revisit a topic like this from time-to-time, right?

After all, there are things we learn and things that we change as we gain more experience.

It’s not at all uncommon to see plugins defining hooks and other behavior, but I’m not a fan of this approach. Instead, I think handling hook registration should be done in its own function or, even more drastically, handled by a set of classes.

But before getting into that, I want to explain what should go in a WordPress plugin constructor, why it should go in a constructor, and how this can be handled when working on your plugins.

WordPress Plugin Constructors

From the outset, I think that constructors should be used for one thing:

  • Initializing the state of an object.

What defines an object’s initial state might depend on if it’s created “from scratch” or if it’s being loaded with information from a previous set (like a session being serialized). The way I see it:

  • attributes are nouns that describe an object,
  • functions are verbs that describe what the object can do.

The functions, of course, do the work that object is capable of doing. They may modify the state of the object when called, or they may do work on the arguments passed into the functions.

What Should Go in a Constructor?

When an object is constructed it should simply be set in such a way that its attributes are set and its functions are ready to do work.

If there’s something in the constructor that does not impact an object’s initial state, it should not be there.

Why Should Attributes Be in a Constructor?

Perhaps a better way to ask this question is: 

Why shouldn’t hooks be defined in the constructor?

WordPress’ hook system is part of the event-driven design pattern (which I’m a fan of), but registering hooks doesn’t describe the state of the object. Instead, at the most fundamental level, it’s something that creates a relationship with the object and WordPress.

WordPress Plugin Constructors

The object’s initial state does not need to know about WordPress, have any of its functions set to be coupled with WordPress, or need to do any processing with WordPress.

Remember, attributes are initialized in a constructor. WordPress is not an attribute. It’s a dependency. To create a dependency is to take an action which is the definition of a verb.

Thus, all hook registration should be done in a function.

How Can We Handle Hook Registration?

This is one of those topics that can be a post or a series of posts all its own.

  • It’s possible to create a class that maintains a registry of objects and the hooks with WordPress.
  • It’s also possible to define hook registration within a function in the class.
  • We can also do a number of things with dependency inversion.

All of the above are things that are beyond the scope of this post but for the sake of simplicity, I’ll show can example of how a class can register its functions with WordPress in an init function:

This way, we’re able to instantiate the object, test it, use it, etc., but we don’t have to deal with anything related to WordPress without explicitly calling the init function.

Once that’s called, the dependency is created, WordPress is needed, and things get more complicated.

Oh, And That Testing Thing

I want to mention one more point that’s a bit beyond the scope and point of this post but is still relevant: When it comes to testing a class, we should be able to:

  1. create an instance of the class,
  2. testing its logic by calling functions,
  3. passing it parameters and evaluating its return values.

And we should be able to do as much of this as possible in isolation. If hooks are defined in the constructor, it creates an immediate dependency on WordPress which should not be needed.

WordPress does not describe the state of an object. It’s a dependency of the object.

Anyway, the point I’m trying to make is that WordPress plugin constructors should not handle the registration of hooks because hooks do not describe its state. They are related to something the class does and they prevent us from being able to test an object in isolation.

So they have their place, but it’s not in the constructor.

 

Category:
Articles
Tags:

Join the conversation! 19 Comments

  1. Perhaps this is more of a code aesthetics thing, but if you define your hooks in the constructor, you can result in the “bootstrap” step being simply creating the object and ignoring it. So you get kind of odd looking code like this:

    new MyPlugin();

    And that’s all it does. Creating a object shouldn’t produce side effects like that, and it’s weird to me when I see objects created and not assigned to anything like that. It’s a “code smell”, which this blog post does a good job of explaining why.

  2. Very happy to see some discussion on a topic I always struggle with: class architecture in plugins.

    That said, fretting about a plugin class being dependent on WordPress strikes me as very much a first world problem.

    I agree with your basic premise that adding hooks in the constructor is dangerous, but for a different reason. I find that it can set you up to add the hooks multiple times, if you end up instantiating the class multiple tiles.

    • Very happy to see some discussion on a topic I always struggle with: class architecture in plugins.

      I think it’s something a lot of think about but, at the end of the day, we sometimes have to push through and get things delivered so we aren’t always able to spend the time we’d like building things we’d like.

      That said, fretting about a plugin class being dependent on WordPress strikes me as very much a first world problem.

      I think it depends on the nature of the project. If I’m building something for a customer who I know will need maintenance on a project over time (which will likely be true because WordPress is updated quite frequently, all things considered) then doing it in a well-engineered way is worth it.

      I find that it can set you up to add the hooks multiple times, if you end up instantiating the class multiple tiles.

      Depending on how you’re organizing your plugin, this shouldn’t be a problem. First, if you take a class that’s dependent on WordPress and set it up properly – similar to your first comment – then the instantiation problem you mention won’t be an issue.

      Secondly, classes are often instantiated multiple times because of WordPress, in its basic form (without additional plugin), is stateless. So with each page load, it’s going to have to instantiate the class.

      If you’re concerned about an action being hooked twice (which, IIRC, WordPress will only hook the first object – not the first instance, necessarily because it will need that with the next page load because of the whole stateless thing :), then I’d question first what the action is doing that’s problematic and secondly I may look to see if it needs to be unhooked and then rehooked.

      That gets into some weird stuff but there’s also some weird stuff out there :).

  3. Hey Tom,

    I did have one thought about using “init()” as a function name in your class. Its often confusing if you use the “init” name outside of the scope of the init action inside WordPress. If the assets “init” is called when the plugin is loaded, I’d change it to “load” or “onLoad” depending on how you queue up the function to be run.

    Loving the series you are writing about how you interpret best “ways to do things in WordPress”. I already think about this every week, and its nice to see you documenting whats been going on in my head! Your thoughts on confusion between the init function name and it not actually running “during init”?

    • Its often confusing if you use the “init” name outside of the scope of the init action inside WordPress.

      Fair point – and I actually like naming my functions within classes like the hooks to which their bound.

      If the assets “init” is called when the plugin is loaded, I’d change it to “load” or “onLoad” depending on how you queue up the function to be run.

      Any of the above are good – even a setup or start or anything like this. I also think it’s important to make sure that the functions are well-documented so people perusing the code in the future know what it’s doing.

      I already think about this every week, and its nice to see you documenting whats been going on in my head!

      Thanks! It’s not always accurate or the best way, but it’s an attempt and having comments like this help keep things honest :).

      Your thoughts on confusion between the init function name and it not actually running “during init”?

      Got it covered above :).

  4. I’ve also gotten away from using class instances and instead use static methods for my plugins. If another piece of code wants to unhook one of my calls, it’s challenging to do so without a reference to the class object, vs something simple like remove_action( ‘init’, ‘MyPlugin::init’ ).

    • I’ve also gotten away from using class instances and instead use static methods for my plugins.

      Why is that? I mean, classes with static methods undermine much of what OOP affords and it’s almost not really worth using OOP if much of your class is static.

      something simple like remove_action( ‘init’, ‘MyPlugin::init’ ).

      This works, but it’s not really jiving with the OOP paradigm. Not that I’m trying to be all preachy about it at all.

      But if you do opt to go that route, there are a number of patterns and strategies that allow for you to access instances of objects with class functions.

      My thing is that if you’re going to use OOP, then use OOP. If not, then stick with functional. That way, you just prefix the functions with what you need and then can unhook them as needed.

      • I had primarily done that to have 5.2 compatible code for my plugins in the WordPress repo and using static classes as a form of namespacing, but I use actual namespaces in newer projects. The static classes were updated in existing plugins.

        • Ah! That makes sense. Yeah, older versions of PHP that don’t support namespacing sometimes force us to use weird ways of doing things.

          I still wonder if having a standard object instance wouldn’t be better, but I don’t know the constraints and requirements of the project ;).

    • Also, at some point your going to want to test your class in isolation and at that point, static functions often bites back in a kind of bad and hurtful way. I’m kind of a code coverage nerd and for that reason alone I tend to avoid static functions.

      This is kind of an interesting work around for the specific issue of removing actions and filters that are declared as class instance functions:

      https://gist.github.com/tripflex/c6518efc1753cf2392559866b4bd1a53

      I could argue that WordPress should include these functions into core.

  5. Another way of talking about this is that a constructor should only ever have an impact on state that is encapsulated within the constructed instance. Hooks break that rule since they affect global data structures. I don’t have too much of a problem with calling methods that “do” things in a constructor, as long as they don’t break encapsulation (for example, instantiating instances of other classes).

    • Another way of talking about this is that a constructor should only ever have an impact on state that is encapsulated within the constructed instance.

      This is a good point.

      I don’t have too much of a problem with calling methods that “do” things in a constructor, as long as they don’t break encapsulation (for example, instantiating instances of other classes).

      Yeah – that’s not a bad note. I think have private or protected methods that help setup or prepare an object’s state are fine. This might deal with instance data and it might have to do with dependencies, like you mentioned. I think we should still aim to keep methods short so if constructors need units of work to set up an object, then moving that logic into smaller functions make sense.

  6. re: “WordPress is not an attribute. It’s a dependency.”

    Can I play devil’s advocate for a second and ask…Is it?

    Or is the plugin the dependency? That is, WP is for all intents and purposes calling a function (blindly) and it’s up to the plugin to be there (or not). Put another way, the plugin isn’t reaching out to WP, WP is reaching out to the plugin, yes?

    At best, perhaps the relationship – given the nature of the WP architecture (as well as it’s general lack of OOPness) – isn’t so binary? Perhaps it’s more symbiotic (and not a good fit for strict OOP thinking)?

    p.s. I’m not disputing your line of thinking as presented. Not at all. Makes a lot of sense. I’m simply wanting to tip it over on its side and see if the logic holds up, or not.

    • Can I play devil’s advocate for a second and ask…Is it?

      Absolutely. I think it’s important to think through this kind of stuff.

      Or is the plugin the dependency? That is, WP is for all intents and purposes calling a function (blindly) and it’s up to the plugin to be there (or not). Put another way, the plugin isn’t reaching out to WP, WP is reaching out to the plugin, yes?

      WordPress can function without a plugin, so I don’t see the plugin being a dependency. The plugin can’t function without WordPress, so I see WordPress has being the dependency.

      Put another way, the plugin isn’t reaching out to WP, WP is reaching out to the plugin, yes?

      WordPress will reach out to the plugin only if the plugin has made itself known to WordPress. Generally speaking, WordPress will still work even if said plugin does not, though if the plugin is poorly built or has a bug in it, it may halt executation.

      This isn’t WordPress’ fault. It’s just going through the code that registered itself with WordPress. That code should work and it requires certain aspects of WordPress to be present to work.

      At best, perhaps the relationship – given the nature of the WP architecture (as well as it’s general lack of OOPness) – isn’t so binary? Perhaps it’s more symbiotic (and not a good fit for strict OOP thinking)?

      I don’t disagree. Though I still tend to think of WordPress has being a dependency in some cases, I don’t mean to imply it as a hard and fast rule. More of a guide, if nothing else.

      There are certainly times where a plugin is going to be more tightly coupled with WordPress. In those cases, it might be a bit tougher to distinguish when it’s a dependency and when it isn’t. That is, it may be symbiotic more than dependent (as you mentioned)

      Makes a lot of sense. I’m simply wanting to tip it over on its side and see if the logic holds up, or not.

      Sure! I like doing this kind of things too, though I’d say that the best way to see if the logic holds up in the purest since would be to evaluate the negation. Something like this, perhaps…

      1. A dependency is defined as something that is required to run another piece of software.
      2. A plugin is a piece of software.
      3. A plugin requires WordPress to run.
      4. Thus, WordPress is a dependency.

      So the converse or tip it over would be something like:

      1. A plugin is defined as a dependency because WordPress can’t function without it.
      2. A plugin is a piece of software.
      3. WordPress can function without a plugin.
      4. Thus WordPress is not dependent on the plugin.

      This isn’t exactly the best syllogism but I think you get my point.

      Ultimately, I think it’s still more or less a guideline that WordPress is a dependency (which seems weird given the sheer size of the software versus the size of a plugin) but conceptually, this is how I tend to view it simply because a plugin can’t function without.

      Regardless, this was a good comment. I like thinking through this kind of stuff.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.