BLOG

Writing WordPress code that’s universal

WordCamp Belgrade 2015 has been a great experience for Presslabs. Many thanks to the organizers for accepting this year our presentation about “Writing WordPress code that’s universal”. It has been delivered by Marius, based on his experience while taking care of customer happiness in our support team.

Here is the transcript of the presentation, followed by the video recording:

My name is Marius. I come from Timișoara, Romania, and I’m a WordPress developer. I’ve been experimenting with WordPress for about 3 years now, and I can say I learn new stuff every day.

I work in the Support Team at Presslabs. Presslabs is a hosting company which is dedicated to WordPress publishers, and, on a daily basis, we come across a lot of plugins that are not really implemented as they should be and do not go by certain coding standards that us WordPress developers are used to.

This is why I chose the topic of universal coding for this meeting. I’ve structured the presentation in a series of ideas that, I believe, have to be taken into consideration when starting to write a plugin or a theme. I’ve structured my presentation into 3 main topics. We’ll talk about them when we get to them.

Presentation WordCamp Belgrade - 18.04.2015 (5)

For starters, what should we understand by writing WordPress code that’s universal?

Mainly, it all comes down to writing code that does not put a WordPress administrator into difficulty when he has to migrate a site from one hosting environment to another. For example, he might want to optimize the performance of the site, so he chooses to migrate the site from a regular hosting system to a hosting system that uses different caching layers and optimization techniques. Once he does this and the plugin is not compatible with this kind of behavior, he cannot really take advantage of what the new hosting environment has to offer. Also, plugins often times come into conflict with one another, and, at the same time, it might happen that plugins are implemented in such a manner that sometimes, if an article becomes super popular and you get a high traffic boost, your site might crash.

General DOs and DON’Ts

First I’d like to go through some general DOs and DON’Ts, when writing code, and I’ll start with something we’ve often come across when debugging our clients’ code.

In order to explain the idea of this slide, I’ll have to describe the environment at Presslabs. The WordPress Dashboard is decoupled from the frontend, it’s always served with HTTPS, and its domain differs from what the client would choose as the main domain of the site. This is why we have this area grayed out in the admin panel as the clients have to configure them from our own panel, and they can switch from one domain to another. What we actually do is we hook into the site and home URLs, and we filter out the values that should be displayed for the backend URL and the frontend URL.

Presentation WordCamp Belgrade - 18.04.2015 (7)

And we’ve noticed that lots of plugins just take the value of the options from the options table, mainly the site URL and the home options, which leads to a bunch of problems when trying to activate plugin licenses, since the plugins sends a certain URL to the server handling the plugin licenses, and then, for example, if the client chooses to migrate to our environment where the backend is decoupled and is served from a different domain, the license might appear as having already been validated, and, thus, the new request appears as an attempt to license a new copy of the plugin.

We’ve also noticed that when trying to register with different services, plugins tend to send a callback URL so that the client can later return to his site once he’s confirmed with the API of the service. That can also differ which leads to problems, and many teams often have nicely arranged administration panels, loading assets such as the CSS files, the Javascript, and the images, and since our backends always run on HTTPS while the frontend also might on HTTP, those assets are blocked by the browser since they will appear as being loaded from an insecure end point.

The idea would be that it’s better to use the site URL and home URL functions instead of choosing the unfiltered values.

Presentation WordCamp Belgrade - 18.04.2015 (8)

The second idea I would like to bring up concerns php calls to files other than, for example, admin-ajax.php. Often times, we see such includes the beginning of a php file, which can be loaded asynchronously to bring some CSS or Javascript code that is dynamically manipulated. The problem with such declarations is that the point at which the WordPress install resides, can vary.

Thus, ../../.. might not match from one WordPress setup to another, leaving the php file unable to load the entire WordPress environment and access its functions. What would be recommended to do here is to use admin-ajax.php and the other interfaces that WordPress has to offer. If the use case that we’re trying to implement allows it, we should try to implement some custom URLs by defining our own rewrite rules. For example, we have a CSS asset that we would like to dynamically modify. From the administrator’s configurations in the admin panel, we can just generate the code that’s associated with that CSS asset and store it at a certain URL. This way, we’re always serving it from that URL without having to run to the php code itself. This is especially helpful if we’re using an environment that uses caching in order to optimize performance of the site.

Presentation WordCamp Belgrade - 18.04.2015 (10)

I often encountered in plugins the $_REQUEST variable which can be quite confusing, because it’s not really clear from which type of request the data is received, so I personally consider that it’s best to just be straightforward and use the $_GET or $_POST variables in order to ease the debugging if the developer will get there.

Presentation WordCamp Belgrade - 18.04.2015 (11)

Now, of course, a site can make use of a lot of plugins and themes, and WordPress itself has its own interface with lots of different functions, so it’s highly likely that function names will clash into each other. A piece of advice would be to use well defined name spaces and authentic prefixes in order to make sure that our functions and our classes are really unique in the way they are named. Also try to avoid wp_ because you never know if the function that you’ve implemented won’t actually get to be replicated within the WordPress core somewhere in the future.

Presentation WordCamp Belgrade - 18.04.2015 (12)

Good practice would be to just stuff all your functions inside a class, and then instantiate your class using a global variable. After that, you just have to make sure that the class and the global variable are very well prefixed to make them unique.

eval is one of the dangerously good things that php has to offer, as it depends very much on the way that the plugin and the theme is implemented, and the degree to which the eval function is exposed to user input. Using it might be the right choice when it comes to implementing a certain functionality, but we really have to make sure that we have total control of what we pass as an argument to the eval call itself. I don’t recommend using eval or create_functions, since debugging would be really difficult as you are never really certain what exactly is the code that executes there. create_function has some problems regarding performance, as well, when the function that it creates actually gets to be called.

Presentation WordCamp Belgrade - 18.04.2015 (13)

Custom tables in the database are not necessarily a bad thing since they might offer just what WordPress lacks, but, at the same time, when we decide to use an approach that creates tables associated to a plugin or to a theme install, we really have to make sure that we clean up after ourselves. If the user chooses not to use our plugin anymore, we just offer functionality which does sort of an install of the tables that we’ve created.

Presentation WordCamp Belgrade - 18.04.2015 (14)

In my team, we’ve run across several sites that have some history of publishing behind, and, when we had a look at their database in phpmyadmin, there were 5 pages of tables that were left over from different plugins that they had installed a long time. This is a lot of clutter that is really unnecessary, especially if you have a plugin which creates a lot of entries for each article. And if you write a lot of articles and stop using that plugin, you’ll be left with a table with a lot of needless content inside of it. Of course, when we are to write a piece of code, we have to make sure that, in case it doesn’t cover all use cases that a client or a user might come across, we make it extensible.

Presentation WordCamp Belgrade - 18.04.2015 (15)

We’ve often seen clients that wanted to make their site to look different, and, in order to do that, we had to go into a plugin and alter its functionality. We then realized that what we modified in the plugin would be useful and suggested its integration in the next release. But this doesn’t always happen, so we get to implement a fix, then the client relentlessly updates the plugin, and, since, the fix is not included in the update, he calls us and says “Hey, man, you just fixed something, and it doesn’t work.”. Then we have to go again, apply the fix, and so on. If we as plugin developers place different action hooks and filters in the right places in the code, a certain developer can just create a new plugin which hooks into this plugin provided that our plugin is activated. And this works inside his own functionality without messing with the code of the original plugin.

Presentation WordCamp Belgrade - 18.04.2015 (16)

As I was editing my presentation, I remembered this article that I read some time ago, from Andrew Nacin’s blog, and he suggested that we use hooks instead of template tags. For example, if we want to have a small widget which brings Tweets extracted from Twitter, one approach would be to just add a function verification to check if the function is actually defined, and get to use it in order to avoid having errors generated. A much cleaner and cooler way to do it would be to add the line of code below in the plugin and implement the function over here, and then instruct the user to just add the next line of code within the theme. Whether the plugin is activated or not, the hook will just try to look for any functions that are trying to hook into this hook call, and if there’s nothing there, nothing bad happens.

Presentation WordCamp Belgrade - 18.04.2015 (17)

Using external resources

The second part of my presentation will go into using external services or 3rd party libraries in order to extend the functionality that we are trying to implement.

And my first idea would be to be very careful in the way we handle our remote calls.

Presentation WordCamp Belgrade - 18.04.2015 (19)

If we do curl calls, or if we use the HTTP API of WordPress, we should make sure that the way we use them does not stall the page load.

Presentation WordCamp Belgrade - 18.04.2015 (20)

We’ve seen plugins which try to fetch data from Google, Facebook, Twitter, and all the social media channels in order to aggregate the total number of shares, and, of course, each server will have its own response time. Having a response time that’s too high might end up causing a php timeout which serves a blank page to the user. The recommendation here would be to actually use cron jobs to fetch data from external services, from other servers, store it within options or transients or any other approach that we consider adequate, and fetch it in the browser asynchronously so that the page loads fine.

The last point here would be useful in environments that use HTML caching, so that if we have social media stats that update regularly, we have to make sure that we don’t cache the social media shares at a certain moment in time. They should be displayed in real time, and we’re loading them asynchronously in the browser.

Presentation WordCamp Belgrade - 18.04.2015 (21)

Another a very good approach is to just let WordPress handle content, and also offer the functionality for image optimization, sending emails etc. because certain hosting environments don’t offer support for them or the hosting servers are not necessarily optimized for doing these kind of tasks and it might slow down the site, consuming resources that would otherwise be allocated to serving actual content to the users. Such services already exist, and, provided that we have the resources necessary to buy them, I really recommend using them.

Presentation WordCamp Belgrade - 18.04.2015 (22)

Another issue that we’ve come across a few weeks ago was that a plugin was using the re-captcha library from Google to add the new “I’m not a robot” tick box, and the theme was also using the Google re-captcha library, but it was the old one. These two versions of the library were loaded from different places, one from the plugin and one from theme, so we ended up having the same class being defined twice, which rendered the functionality of the theme unusable. The recommendation here would be, since we’re the one developing the plugin or the theme, we’re the one controlling the code, we’re the one dictating when to switch from one version of the library to another, to actually go inside the library and prefix everything that we consider reaches the interface of that library (classes, functions) in order to make that library unique to the plugin that we use.

Cache-friendly WordPress code

The third part of my presentation will analyze some techniques that would be useful if we have caching on the site. Using asynchronous calls in order to load content is very useful, and one approach we used is to implement a plugin called Toplytics which creates a list of the most popular articles from the blog by fetching data from Google Analytics. Based on the data it receives, it creates a list in an ordered fashion which we store at a certain URL implemented using the rewrite rule. Then, via ajax calls, we just fetch the data and make sure that, in case we have two articles and these articles are cached at two separate moments in time, one is cached one hour prior to the user reading the article, and one at the moment the user is actually reading the article, so the user navigates from one article to the next and then back. In case the list has changed during this period, he’ll notice a small glitch since the cached versions contain different versions of the ordered list of the most read articles. So, by loading these items asynchronously we make them available in real time.

Presentation WordCamp Belgrade - 18.04.2015 (24)

As useful as ajax calls can be, it would be really cool not to make abundant use of admin-ajax.php since these calls reach the part of the server that has to process all the php code directly. In case you have a caching layer, as we have at Presslabs, the caching layer serves HTML directly from the cache, without going to the level at which the php code is actually executed, not using resources that would otherwise not be needed.

Presentation WordCamp Belgrade - 18.04.2015 (25)

An alternative here would be to use custom rewrite rules and template files which we can include using the template include hook. This makes content available without the necessity of running through admin-ajax.php at every page load.

Presentation WordCamp Belgrade - 18.04.2015 (26)

A lot of sites nowadays use infinite scroll, and it’s a new way to look at user experience when the user is reading content on the site. We’ve come across certain plugins which implement infinite scroll, but instead of fetching data from URLs that are known to be standard in WordPress and would end up in certain cache layer, they make calls directly to either admin-ajax.php or use GET query variables which will end up as misses. What we would recommend here would be to stick to standard URLs. For example, if we want to implement infinite scroll on an archived page, just use the URL that would be regularly used for an archived page.

Presentation WordCamp Belgrade - 18.04.2015 (27)

And, of course, make sure your code is well integrated with data base caching so that you take advantage of the WP_Object_Cache, thus reducing the number of queries that reach the database, and also taking it one step further by using extensions such as Memcached.

Presentation WordCamp Belgrade - 18.04.2015 (28)

One more idea would be to limit the size of the options that we store in the database. There are themes and plugins which choose to stuff into one single array everything that has to do with the theme, including HTML code. This tends to occupy a lot of space, so the recommendation would be to either split the option into multiple options or use some custom post types in order to store that data which occupies a lot of space. The issue here is that large options might skip certain caching mechanisms and it also often happens that plugin developers don’t pay attention to making their options’ autoload requirement optional. So, by having such large options, the code from each request might end up flooding the server and bringing it down.

Conclusion

The basic idea behind all these aspects that I’ve presented to you, is that the cleaner the code that we write, the better the methods that we choose to use in our code, the less effort we have to use offering support to users that employ our functionality, and also the higher the trust that we get from the users that use our products.

Smart Managed WordPress Hosting

Presslabs provides high-performance hosting and business intelligence for the WordPress sites you care about.

Get Started