Make plugins in a way that might let you retire on a beach one day.

The very first time you sit down to learn a programming language, you will likely learn how to output the words “Hello World” to the screen. With WordPress and PHP, that “Hello World” quickly turned into outputting HTML code. PHP’s ability to go “in and out” of PHP mode made WordPress theme template pages what they are today. You’ve probably seen this in your theme’s files somewhere:

<body <?php body_class(); ?>>

WordPress’s set of functions, filters, and actions lends itself to a certain way of creating output, both in the admin dashboard, and on the front end. For example, the Settings API outputs content using a callback function which returns HTML as a string. We really need to stop doing things this way.

Generating HTML in PHP is a thing of the past.

The days of generating HTML output in PHP should be something we all put behind us. It makes it almost impossible to have proper separation of concerns, blurring the line between server and client, PHP and HTML, and even more impossible to easily use a frontend library like React. Furthermore, someday we may move beyond React to something else entirely. If our screen output’s HTML code is generated from the server (PHP), it’s is totally rigid, inflexible, and prone to accidental scope overlap. But there’s another, better, less error-prone, more-easily-testable, less-headache-ridden, maybe-I-can-retire-one-day way to do it.

So how do we get stuff on the screen?

The answer is JSON. JSON is the great “middleman” between languages. PHP can output JSON, and Javascript can read JSON and do stuff with it, like generate HTML via React!

The only thing PHP should be outputting is JSON. PHP shouldn’t have to know anything about which HTML tags you want to use. JSON is just an array of non-code data; values like these:

  • The title of a post
  • The URL to the featured image
  • The date of the post

It should never need to contain any HTML tags, or any other “code” that only computers use. It should contain human-readable values.

Let’s talk about unit tests.

Unit tests are the saving grace of programming. They’ll help you sleep at night, and make your life at least 40 times better* (*not an actual stat). They will help prevent your code from turning into thin ice, cracking in-the-wild every time you make a minor tweak, and frustrating your users (and ultimately you).

Unit tests can either be the easiest thing you ever make, or completely impossible. It all depends on how you write your code.

From a WordPress plugin creation perspective, this will determine if you are taking the correct approach: write your unit tests before you have any cool (or uncool) output to the screen.

This is a great rule to follow, as it means your unit tests are the first thing to actually use your code; not the frontend user interface. It requires thinking about your server code in a more structured way. Instead of just having a plugin that “does some cool stuff”, you can break it into easily-understandable-components.

For example, say you are creating a simple contact form plugin. The main “component”, or “part” of your plugin would be:

  • A form component

That form component needs to be able to have these things happen with it:

  • Create a form
  • Read a form
  • Update a form
  • Delete a form

Regardless of your user interface, these 4 things are going to have to happen at one time or another. Since your PHP code is what will store that data in the database, you need a way to create, read, update, and delete forms. This is where the acronym CRUD comes from.

So before we even start thinking about HTML output to the screen, we need to write code that does those 4 things, and we want that code to be easily unit-tested. And really, that’s about ALL we need the PHP server code to do.

You may want to accomplish other things with your plugin as well. But I would challenge you to consider that everything can be thought of as a “component” which needs to be created, read, updated, or deleted. Try and view your other things through that lens, and you might realize that your PHP code doesn’t need to do anything but those 4 things. Any of the unique things your plugin does can likely happen exclusively while you are doing one of those 4 things.

That means everything happens through one of those 4 endpoints. The endpoints are the entry and exit points. They call your plugin’s unique functions, and you never call your unique functions outside of one of those endpoints.

Enter the WordPress REST API.

The concept of creating, reading, updating, and deleting is why the WordPress REST API became a thing. It acts as a guide through that process, giving you structured ways of doing that securely.

After all, while you might (and I stress might) want anyone to be able to read something, you don’t want just anybody to be able to create, update, or delete something. You need to validate that they have the right permission to do so, sanitize the data they’ve given you, and ensure they intended to do it.

Sidenote: Why custom WordPress REST API routes over a totally custom endpoint?

Ironically, as a WP developer of over 10 years, I tend to be the type of person who likes to avoid using WordPress-defined code patterns wherever possible. The reason why I could explore in a separate post. But my original approach was simply to create my own API endpoints that generated JSON output by hooking to “init”, conditionally output my own JSON, and calling exit or die().

While this worked great, I had to come to terms with the fact that I am still playing in the WordPress sandbox, which means that there are a plethora of caching plugins people might choose to use. My custom endpoints are not “known” to WordPress, and thus, 3rd party caching plugins would have no way of knowing that my custom endpoints shouldn’t be cached. and I definitely didn’t want to start including code for every caching plugin out there.

This is why using the WP REST API ends up being the better choice: your endpoints will be “known” to WordPress, and caching plugins (the good ones) will know to avoid caching your endpoints.

Leave a comment

Your email address will not be published. Required fields are marked *