Take advantage of the WordPress hook API

For a WordPress developer actions and filters are the base of everything. Every logic the programmer write is initiated by a hook to adapt the logic from the WordPress core or another plugin.

This is why as a developer you should be mindful to use them inside your own code to keep your own plugin easily extendable without forcing your future users to modify the code you wrote.

Prefix your hooks

WordPress is a CMS and users often use plugins to make it feat their needs.
Considering that it is not a bad consideration to not consider our plugin will not be alone on the WordPress environment from our user.

Due to that hooks present inside that plugin will have to prevent clashing with the one from others plugins installed.
For that there is a simple rule that everyone applies that fixes that issue and which is called namespacing.
When a hook is from a plugin it will have to prefix the name from its plugin first. In that way it will be impossible for plugins hooks to clash one another.

Filters

Filters are in first place an excellent way to make your plugin more flexible and easier to adapt for your users by leaving them the choice to change some configurations values or block some data flows on purpose.

Never trust filters output

On an internet website when a user submit values to a form would you trust it?
As a professional web developer the answer to that question should be no.
We never trust the user as it can be something making a mistake or someone entering invalid values on purpose.

For an filter the same philosophy applies as filters are not something that is 100% under control.

Any user can add his callback with an invalid value that would break the plugin code.
Due to that it is important to verify the output of a filter and cast or replace the value returned when this one is invalid.

This seems at the beginning a bit overkill but the more the plugin will be used the more complain about broken website will arrived due to simple errors as this one.
Being cautious to that will save a lot of time and efforts debugging what’s going on a client website.

Get rid of magic constants

Magic constant are often values that the developer set in his code without apparent reason.
That can be for example a value like 3.14 inside a calculus or 24 inside an interval as you can see in the following code:

$radius = 3.14 * $perimeter;
$period = 24 * 60 * 60;

As you can see even in this case it is simple we have two main problems with that code.

First it is up the programmer to guess what theses values are. This is a code smell in the fact it increase the mental mapping from the programmer forcing him to remember that value.
The second issue we have this with that code is the problem of the non flexibility of the code.
In the case the value could be modified without impacting the validity of the code then the user will be fixed the developer value without anyway to modify it.

To address that issue we have two solution.
In the case of the magic value that can’t be modified like in this example:

$radius = 3.14 * $perimeter;

Then we can use a constant that we will share between each of its usages like this:

define(‘PI’, 3.14);
$radius = PI * $perimeter;

In the the case of the magic value that could be modified like in this example:

$period = 24 * 60 * 60;

Then we can use a filter to make the value easily modifiable by the user later by also
provide more information on what it is:

/**
* Interval for the period which is by default 24hrs.
* @param int $interval Interval for period in seconds.
**/
$period = apply_filter(‘period_interval’, 24 * 60 * 60);

Control data flows

Often plugins are a solution which is not perfect to the user and they often need to hack a way to make the plugin match their needs.
In most plugins as the developer doesn’t offer them a way to make it, they often finish overriding the code of the plugin preventing any potential update from the code.
This is a bad pattern that we would like to avoid by providing your user an easy way to control the flow within the plugin with filters.

A good example of that is the following code where we are sending some data to an API to create an address for a payment:

$billingAddress = new BillingAddressResource(
[
‘addressLine1’ => $order->get_billing_address_1(),
‘city’ => $order->get_billing_city(),
‘postalCode’ => $order->get_billing_postcode(),
‘country’ => $order->get_billing_country(),
]
);

As you can see this code is working.
However if we have a user that would like to only have clients in France and so would like to hide the country field then we would be sending an empty value to the API which gonna create an error inside the process.

The easiest way to solve this issue is to add a filter around the data we pass to that API:


$billingAddress = new BillingAddressResource(
apply_filters(
‘billing_address_data’,
[
‘addressLine1’ => $order->get_billing_address_1(),
‘city’ => $order->get_billing_city(),
‘postalCode’ => $order->get_billing_postcode(),
‘country’ => $order->get_billing_country(),
]
)
);

This would allow callback to be attached by users modifying data as they need and preventing them to have to make a dirty fix into the code:

add_filter(‘billing_address_data’, function ($data) {
$data[‘country’] = ‘FR’;
return $data;
});

Actions

Where filters are here to provide users a better control over the data flows inside the plugin, actions are better to use to signal an event that happened.
Another way to use them is to decouple different parts of the plugin to make it easier to make a switch to another solution.

Signal an event

Plugin users often try to make workflows between plugins thus it is a good practice to let them a way to attach their actions to the plugin logic.
There is multiple ways to do that but the best way would be to provide a way for the user to execute its logic before and after the action you are doing.
For example, with the following code:

$order = wc_get_order($response->reference);
$order->payment_complete();

It is possible to make it more extensible adding the following actions:

$order = wc_get_order($response->reference);
do_action(“process_request_order_before”, $response->reference);
$order->payment_complete();
do_action(“process_request_order_after”, $response->reference);

By adding an action before and after the payment_complete method is called the user is now able to attaching his logic to execute his own logic without adding it directly inside your code.
This allows for example for an automation plugin to report the sell on Slack.

Delegate operations

Action are literally subscriber at a really low complexity cost.
That is a great functionality that it is easy to take advantage from to reduce the core logic from the plugin.
Often while coding it arrives that the main logic has to handle some cases that need to be executed but doesn’t impact the core logic like notifying the user or logging the data.
This kind of cases can be externalized from the main logic using an action that will act like a subscriber pattern and will abstract the implementation from theses notification for the code and delegate that complexity to the subscriber in question.
This approach also has the advantage to make the code easier to adapt in the future as removing that notification, add a new one or modifying won’t modify the main logic but will just impact subscriber linked to that logic.

Decouple your plugin

When a plugin starts to grow often logic breaks into multiple parts that interact with one another.
The problem comes when you need to make the link between theses parts which forces you to hardly couple one of them which makes any future change complex.

In WordPress it is possible to prevent that using hooks.

For example in this class diagram corresponding to the code bellow, we can use an action to break the link between the Controller and the Renderer:


class Controller {
protected $renderer;
public function __constructor(Renderer $renderer) {
$this->renderer = $renderer;
}
public function process(): void {
$this->render('index');
}
}
class Renderer {
public function render(string $view, array $args = []): void {

}
}
$renderer = new Renderer();
add_action('render_view', [$renderer, 'render'])

For that we could add an action to make the link this way:


class Controller {
public function process(): void {
do_action(‘render_view’, ‘index’);
}
}

class Renderer {
public function render(string $view, array $args = []): void {

}
}
$renderer = new Renderer();

add_action('render_view', [$renderer, 'render']);

This would give the following updated UML:

We could use the same kind of logic with filter to decouple the parts of the code that needs a return value.


class AdminInterface {
protected $options;
public function __constuct(Options $options) {
$this->options = $options;
}
public function register(): void {
$this->options->get('my_option');
}
}
class Subscriber {

protected $options;
public function __constuct(Options $options) {
$this->options = $options;
}
public function process(): void {
$this->options->get('my_option');
}
}
class Options {
public function get(string $value, $default = false) {

}
}

For example, we can improve the following code by using a filter that will allows to have a single source of truth for the option:


class AdminInterface {
public function register(): void {
$value = apply_filters(‘my_option’, ‘’);
}
}

class Subscriber {

protected $options;

public function __constuct(Options $options) {
$this->options = $options;
}

public function process(): void {
$this->options->get(‘my_option’);
}
}

class Options {
public function get(string $value, $default = false) {

}
}

You can also use that method to extract side logic out of the core of the plugin.
Allowing you to keep the business logic clean by only having essential components inside it.
It is often use to delegate logic that doesn’t impact the main logic such as mail notification.

Make your hooks visible

Often when you create something you are proud of it and you expect the whole world will use it when it will be released.
However when it is published the real wold comes back and you discover no one use your brand new functionality.
This is due to a simple thing your code is not the center of the universe and you will have to find a way for people to know about your amazing code.
For your brand new hooks this is the same. If you leave them like that in the code no one will use them as no one will know that they exists.
Helping users knowing about your hooks is what we will focus in that part.

Document your hooks

The first thing you will have to make sure you are doing in order to make your hooks easily understandable and easy to use for your future users is to document them the same way would do it for a function or a method.
For that we will use the standard WordPress provides to document hooks:

/**
* Summary.
*
* Description.
*
* @since x.x.x
*
* @param type $var Description.
* @param array $args {
* Short description about this hash.
*
* @type type $var Description.
* @type type $var Description.
* }
* @param type $var Description.
*/

Each hook documentation got the following parts.
First there will be a quick description from the usage from the hook in two parts.
A summary often in one line will explain the principal usage of the hook.
A description will complete the summary explaining with more deep what the summary couldn’t details.

Then a technical description from the hook will be present declaring all characteristics.
This description will start with the @since tag that will indicate the version of the plugin where the hook has been added.
After this tag there will be one tag @param per parameter describing each one.
Each parameter will have three parts.
First it will have the type from the parameter that will use the PHP syntax.
Second there will be the name from the parameter including the $.
Finally a description that will detail more the parameter usage in one sentence.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *