Skip to content

Commit

Permalink
Recording edits and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanbossenger committed Nov 5, 2024
1 parent d27f4db commit 60c75be
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 134 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ What makes a web server a little different is that it has software installed and

WordPress runs on a tech stack called LAMP. LAMP stands for Linux, Apache, MySQL, and PHP.

Linux is the operating system that manages the hardware and software resources of the server. Popular Linux distributions include Debian, Ubuntu, RedHat, and CentOS. These operating systems made up of the Linux kernel and a collection of software packages that are installed on top of the kernel.
Linux is the operating system that manages the hardware and software resources of the server. Popular Linux distributions include Debian, Ubuntu, RedHat, and AlmaLinux.

These operating systems are made up of the Linux kernel and a collection of software packages that are installed on top of the kernel.

Apache, MySQL, and PHP are all installed via the package manager of the specific Linux distribution on the server.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,57 @@

At the time of creating this lesson, WordPress Core has more than 3000 available hooks.

As you might guess, there is a hook for everything, so one problem you may often encounter is which hook you should use.
As you might guess, there is a hook for everything, so one problem you may often encounter is which is the correct hook to use for a given set of functionality.

In that lesson, we will learn how to find the right hook for your needs using the WordPress documentation as well as some of the more important hooks.
In this lesson, you will learn how to find the right hook for your needs as well as some of the more important hooks to be aware of.

## Finding the correct hook type

The first choice to make is whether you're going to need an action or a filter hook.

As a reminder, actions allow you to perform some action at a specific point during the execution of a request, while filters allow you to modify, or filter, some data at a specific point, which will be used later on.

## Finding a hook inside WordPress documentation
You will need to determine whether you just need to trigger something, or if you need to modify some data.

There are a couple of places you can find a list of available hooks.
## Finding a hook in the WordPress documentation

There is a section dedicated to hooks in the Common APIs section of the WordPress developer documentation.
There are a couple of places you can find a list of available hooks in the WordPress documentation.

From there you can navigate to the Action Reference or Filter Reference.
There is a section dedicated to [Hooks](https://developer.wordpress.org/apis/hooks/) in the Common APIs handbook of the WordPress developer documentation.

From there you can navigate to the Action Reference or Filter Reference, and then browse through the list of actions or filters run during a typical request.

While this list is not extensive, it is a good starting point to find the hook you need.

It is also possible to find a list of all WordPress hooks in the [WordPress code reference under Hooks](https://developer.wordpress.org/reference/hooks/).

Then from there, it is possible to either navigate manually or search for hooks using the search bar.

## Important hooks

Many of the available hooks are not regularly used (if it all) by plugin developers, while some are used more often than others.
Many of the available Core hooks are not regularly used (if it all) by plugin developers, while some are used more often than others.

In this section, you're going to pay learn about some of the most important hooks to remember.
Let's look at some of the most important hooks to remember.

### The plugins_loaded hook

[This hook](https://developer.wordpress.org/reference/hooks/plugins_loaded/) fires once all plugins are loaded.

This hook is the perfect one to use when you want to register any plugin initialization tasks,
as it will ensure all other plugin code will be loaded.
This hook is the perfect one to use when you want to register any plugin initialization tasks, as it will ensure all other plugin code will be loaded.

This helps if you need to check for third party compatibilities or if your plugin relies on another plugin's logic to execute.

### The init hook

[This hook](https://developer.wordpress.org/reference/hooks/init/) is executed when the core of WordPress is loaded.

This is usually the hook used to configure the core functionality of your plugin such as registering custom post-types, dashboard menus, or cron tasks.
This is usually the hook used to configure the core functionality of your plugin such as registering custom post types, dashboard menus, or cron tasks.

A [similar hook](https://developer.wordpress.org/reference/hooks/admin_init/) called `admin_init` exists to register core functionality, but only to WordPress admin pages.
A [similar hook](https://developer.wordpress.org/reference/hooks/admin_init/) called `admin_init` exists to register core functionality, but only for WordPress admin pages.

### The wp_enqueue_scripts hook

When you need to enqueue CSS styles files or JavaScript script files, this [is the hook to use](https://developer.wordpress.org/reference/hooks/wp_enqueue_scripts/).
When you need to enqueue CSS style files or JavaScript files, this [is the hook to use](https://developer.wordpress.org/reference/hooks/wp_enqueue_scripts/).

Inside the hook callback function you can use the `wp_enqueue_style` and `wp_enqueue_script` functions to enqueue styles and scripts.

Expand All @@ -69,8 +72,10 @@ add_action('wp_enqueue_scripts', 'enqueue_assets');

## Hooks registered outside WordPress core?

If you need to extend the functionality of another plugin or theme, you have to rely on what hooks the plugin or theme developer has put in place. In many cases, there will be a limited set of hooks to make use of, and they may not even be documented correctly.
If you need to extend the functionality of another plugin or theme, you have to rely on what hooks the plugin or theme developer has put in place.

In many cases, there will be a limited set of hooks to make use of, and they may not even be documented correctly.

A good place to start is to search for any instances of `do_action` and `apply_filters` inside their codebase to find any hooks available.

Additionally, some plugin developers will have documentation on their site which includes all the relevant hooks you can use.
Additionally, some developers will have documentation on their site which includes all the relevant hooks you can use.
180 changes: 62 additions & 118 deletions plugin-developer-learning-pathway/02-Advanced-Hooks/04-good-practices.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<!-- Original script by Cyrille C: https://github.com/CrochetFeve0251 -->

# Good practices
# Developing with hooks

As you dive deeper into using WordPress hooks there are some details that become valuable to understand in order to master them.
As you dive deeper into developing with WordPress hooks there are some details about how hooks work that are valuable to understand.

In this lesson, you'll learn some good practices to follow when working with hooks.
In this lesson, you'll learn some good practices to follow when developing with hooks.

## Naming hooks

Expand All @@ -20,181 +20,125 @@ For example, the WordPress core action hook that's triggered when a post is dele
do_action( 'delete_post' );
```

If you want to add an action before the actual change, the convention is to prefix it with `pre_`.
If you want to add an action before the actual change, you can prefix it with `pre_`.

```php
do_action( 'pre_delete_post' );
```

Finally, if you want to add an action after the actual event, The convention is to use a past tense in the name.
Finally, if you want to add an action after the actual event, the convention is to use a past tense in the name.

```php
do_action( 'deleted_post' );
```

### Filters

On the other side, a filter is a value and due to that it is advised to use nouns within its name.
Because filters are generally linked to a variable that can be modified, filter hooks are often named after the variable that can be modified.

For example, the filter hook that allows you to modify the content of a post is named `the_content`.

```php
apply_filters( 'sitemap_url', 'https://example.org/sitemap.xml' );
$content = apply_filters( 'the_content', get_the_content() );
```

## Handling the filter type mess
### Avoid naming collisions

When using a filter, the type from the value is never guaranteed even it is set in the docblock.
When naming your hooks, it's important to avoid naming collisions with other plugins or themes. To do this, you can prefix your hook names with your plugin's name or a unique identifier.

### The problem
There is a simple reason for that: It is the last callback that decides what is returned by the filter, and that also means the last callback decides the type from the value.
```php
do_action( 'wp_learn_delete_book' );

That might not seem an issue at first, but you need to remember when you are creating your custom hooks that you are not the one who will be writing the callbacks but your users.
apply_filters( 'wp_learn_lesson_url', 'https://example.org/lesson' );
```

![Illustration from the problem](./imgs/handling-filter-return.png)
## Handling filter types

This means that you have no control on what your users will return and if a wrong type is returned, it might make your plugin crash.
When using a filter, the type of the variable that's returned from the filter is not guaranteed, even if it's documented.

### The solution
### The problem

The solution for this comes in two parts:
The reason for this is that the last callback that runs on the filter determines what is returned by the filter.

#### Validating your filter output
That might not seem an issue at first, because as a developer you will make sure to return a value with the same type.

The best way to make sure the output value is the type we expect is to validate it.
However, if you recall from the lesson on Custom Hooks, just as you can hook into actions and filters, so can other developers.

For primitive types as integer, float or boolean, we can cast the output value as following:
- `boolean`: `$value = (bool) apply_filters('my_filter', true);`
- `integer`: `$value = (int) apply_filters('my_filter', 10);`
- `float`: `$value = (float) apply_filters('my_filter', 10.0);`
![Illustration from the problem](./imgs/handling-filter-return.png)

However, for more complex validation or other types, it is better to have a manual validation.
For example, for a string or an array,
it would be better to check if the returned value is the right type
rather than casting which could lead to a fatal error.
This means that if you add custom hooks to your plugin, you need to ensure that the data returned is the correct type otherwise it might break your plugin's functionality.

For a string that would give the following:
```php
$initial = 'my_value';
### The solution

$value = apply_filters('my_filter', $initial);
The solution for this comes in two parts:

if ( ! is_string( $value ) ) {
$value = $initial;
}
```
#### Validating your filter output

#### Assert your callbacks value
The best way to make sure the value returned from any filter callbacks stays the type you expect is to validate the data type in your code.

However, that incertitude on the type of the value inside filters does not only apply to their output, but it is also present on each callback.
For primitive types like integer, float or boolean, you can use [PHP's type casting](https://www.php.net/manual/en/language.types.type-juggling.php#language.types.typecasting) system, to ensure the return value is the correct type:

![Illustration from problem](./imgs/assert-callback-values.png)
- `boolean`: `$is_admin = (bool) apply_filters( 'wp_learn_is_admin', true );`
- `integer`: `$book_count = (int) apply_filters( 'wp_learn_book_count', 10 );`
- `float`: `$base_price = (float) apply_filters( 'wp_learn_base_price', 10.0 );`

However, for more complex validation or other types, it is better to implement a more manual validation check.

This is why it is important to never strongly type the value from a filter (the first parameter).
For example, for a string or an array, it would be better to check if the returned value is the right type rather than using casting, as this could lead to a PHP fatal error.

The same way it is also important to always make a check on the type from the value before performing any operation on it.
For example, you can use the PHP `is_string()` [function](https://www.php.net/manual/en/function.is-string.php) to check if the value returned from any hooked callbacks is a string:

```php
function my_callback($value) {
if(! is_string($value)) {
return $value;
}

return $value . "my_string";
$book_slug = apply_filters( 'wp_learn_book_slug', 'books' );

if ( ! is_string( $book_slug ) ) {
// either reset the value or throw an error
}
```

## Getting information on a hook

Another good thing to know about hooks is how to get information about them.
If not, you can either reset the value or throw an error.

### Determining the current hook

WordPress allows a callback function or method to be used on different events.

Due to that, it can be sometimes unclear on which hook the callback is actually running.
#### Assert your callbacks value

This is why WordPress introduced two functions.
The fact that the type of the variable returned from a filter can be modified also means that an incorrectly typed variable can be passed to any hooked callback.

#### current_filter
![Illustration from problem](./imgs/assert-callback-values.png)

If you are in a callback linked with filters,
it is possible to use the function [`current_filter`](https://developer.wordpress.org/reference/functions/current_filter/):
Therefore, if you hook a callback function into a filter, it is important to always check the type of the value you receive before performing any operation on it.

```php
function my_callback($value) {
if('the_content_feed' === current_filter()) {
return '';
add_filter( 'wp_learn_book_slug', 'jon_doe_edit_book_slug' );
function jon_doe_edit_book_slug( $book_slug ) {
if ( ! is_string( $book_slug ) ) {
// throw some error because the type is incorrect
}

return $value;
// continue with your functionality because the type is correct
return 'book';
}

add_filter('the_content_rss', 'my_callback');
add_filter('the_content_feed', 'my_callback');

```

#### current_action
You can use the same methods as before to validate the type of the variable you receive.

If you are in a callback linked with filters,
it is possible to use the function [`current_action`](https://developer.wordpress.org/reference/functions/current_action/):
For more information on PHP's type jugging, you can refer to the [PHP documentation](https://www.php.net/manual/en/language.types.type-juggling.php).

```php
function my_callback() {

if('init' === current_action()) {
return;
}

// my logic
}
There's also a section dedicated to the various [Variable handling functions](https://www.php.net/manual/en/ref.var.php).

add_filter('amin_init', 'my_callback');
add_filter('init', 'my_callback');
## Getting information on a hook

```
### Check how many times a hook as run
Another good thing to know about hooks is how to get information about them.

Sometimes it is important to know if a callback has already run to prevent it from running again.
### Determining the current hook

For that, it is possible to use two functions depending on if you want to check an action or a filter.
WordPress allows a callback function or method to be used on more than one hook.

#### did_filter
Due to this, it can be sometimes unclear on which hook the callback is actually running.

If you want to know how many times a filter has been fired,
you can use the [`did_filter`](https://developer.wordpress.org/reference/functions/did_filter/) function:
In any callback function it is possible to use the [`current_filter`](https://developer.wordpress.org/reference/functions/current_filter/) or [`current_action`](https://developer.wordpress.org/reference/functions/current_action/) functions to determine the current filter or action the callback is running on.

```php
function my_callback($value) {

if(0 < did_filter('pre_delete_attachment')) {
return $value;
}

// My logic

return $value;
}

add_filter('after_delete_post', 'my_callback');
```

#### did_action
### Check how many times a hook has run

If you want to know how many times ana action has been fired,
you can use the [`did_action`](https://developer.wordpress.org/reference/functions/did_action/) function:
Sometimes it is important to know if a hooked callback has already run to prevent it from running again.

```php
function my_callback() {

if(0 == did_action('amin_init')) {
return;
}

return ;

// my logic
}
Inside any callback function, you can use the [`did_filter`](https://developer.wordpress.org/reference/functions/did_filter/) function to check how many times a filter has been applied during the current request and the [`did_action`](https://developer.wordpress.org/reference/functions/did_action/) function to check how many times an action has been applied.

add_action('added_option', 'my_callback');
```

0 comments on commit 60c75be

Please sign in to comment.