Want to get more articles like this one? Join my newsletter

Thoughts on type safety with WordPress

As a WordPress developer, you might be familiar with type hints. These are the elements of your php docblock that let other developers know about the type that you expect a function or method parameter to be. Here’s an example:

/**
 * Return the first element in an array.
 *
 * @param array $array
 */
function first($array)
{
    foreach ($array as $value) {
        return $value
    }
}

@param array $array in the first function above is the type hint for the array parameter. It tells other developers that we expect the parameter to be an array. But it doesn’t prevent them from passing you an integer or something else that wouldn’t work in a foreach loop.

Preventing these types of errors is what we call type safety. Any time that you write code, you’re making assumptions about the type of the variable you’re working with. That’s why type safety is an important to aspect of programming.

As a developer, you have to make a choice about how you want to deal with type safety. You have three overall approaches to deal with type errors.
You can:

  • Do nothing and let other developers deal with type errors from not respecting your type hints.
  • Program your function or method defensively to deal with developers not passing you the correct parameter type.
  • Have PHP enforce the parameter types of your functions or methods.

Since PHP 7, PHP developers favour the third approach of PHP enforcing type. But, as WordPress developers, we don’t always have that luxury. So it’s important to understand the tradeoffs between these different approaches to type safety.

WordPress still supports older versions of PHP

The most of common reason why a WordPress developer can’t have PHP enforce type is backwards compatibility. If you’re working on a plugin or theme, you might have to support older versions of PHP. So you have to decide on another way to deal with type safety.

You can ignore type safety

If you’re only working by yourself, it might be tempting to ignore type safety. It’s also possible that it’s not something the development team that you’re on cares much about. That’s fine if you’re ok with the possibility that you might have to deal with bugs related to it.

This scenario is a bit like weighing the benefits of unit testing. It’s hard to estimate how much time you’d save or lose by doing it. It can be a bit of an investment upfront that pays off in the long run.

You can use defensive programming

The other way to deal with type safety before PHP 7 is by coding defensively. When coding defensively, it’s your role to guard your code against type errors. Here’s an example of what that means:

/**
 * Change the title of a post
 *
 * @param int $post_id
 * @param string $title
 */
function my_plugin_change_post_title($post_id, $title)
{
    if (!is_int($post_id) || !is_string($title)) {
        return;
    }

    $post = get_post($post_id);
    
    if (!is_object($post)) {
        return;
    }
    
    $post->post_title = $title;
    
    wp_update_post($post);
}

Above is the my_plugin_change_post_title function. It has two parameters: post_id and title. And according to the PHPDoc, post_id needs to be an integer and title a string.

When coding defensively for type safety, you have to be able to deal with unexpected parameters passed to your function or method. There are different ways to do that. But with the my_plugin_change_post_title function, using a guard clause when entering the function makes a lot of sense.

We can’t change the title of a post if we’re not given an integer. We also can’t do it if we’re not given a string for the title. So we use is_int and is_string to check the type of post_id and title. If either fails their type check, we leave the function right away since there’s no way we could change the post title.

The advantage of doing this over doing nothing is that you don’t have to worry about the type of your parameters. Once passed the guard clause, you can use post_id and title knowing that their type is correct. This is great for preventing type errors inside the my_plugin_change_post_title function.

That said, it doesn’t prevent all issues. If someone sends the wrong types to the my_plugin_change_post_title function, nothing happens. Is that a good or bad developer experience? Well, it depends on who you ask.

Thinking about developer experience

This gets to the essence of what you should think about when deciding on how to handle type safety. You need to ask yourself who is going to work with the code that you’re writing. That’s because type safety is also part of the developer experience.

Are they developers comfortable with code breaking if they make a type error? Are they expecting the code to break if they make a type error? Then it makes sense to handle type safety by either enforcing it (if you’re using PHP 7) or coding offensively.

But what if it’s less experienced developers using your code? Then maybe it makes sense to be less strict about type errors. Coding defensively and trying to handle mistakes for them makes their life easier. It also makes learning about WordPress and PHP less overwhelming.

This is a lot of what WordPress core does. And many people argue (I believe correctly) that it’s been one of the pillars of WordPress’s success. So if you’re working on a plugin or theme used by the community, it might be more beneficial to be flexible with the types you accept.

On the flip side, if you’re not catering to a large segment of the WordPress community, you can do what you want. You can be more rigid about type or code offensively. The impact on other developers won’t be as significant.

Example: Passwords Evolved plugin

This has been my overall philosophy when writing WordPress code. For example, there’s a small security plugin that I’ve built called “Passwords Evolved“. It only has a few hundred installs according to the plugin repository.

In the context of the number of installs, it makes little sense to worry about other developers. It’s better that I focus on what allows me to write better code. And that’s by using type safety features of PHP to prevent type errors.

But let’s say that I had a plugin with hundreds of thousands of installs. (I don’t and never have.) I would expect the choice to be different. It’s not just about me as a developer. It’s about the team working on the plugin and the developer ecosystem building on top of it.

Defensive coding makes a lot more sense when thinking on that scale. You lose some type safety. But you can replace a lot of it with a good testing suite.

Type safety is a moving objective

The point of this article and the example was to show that type safety isn’t a binary decision in the WordPress world. It’s easy to try to go in one direction and just be super strict about it. But it’s not always the ideal path to take.

Instead, you have to make a choice between your needs and those of the other developers that work with your code. It could just be you and another developer. It could also be a large segment of the WordPress community.

But which ever path you take, it benefits you to care about type safety. It makes your code more resilient. And that’s always a good thing.

Creative Commons License