WordPress Development – Filters (tutorial)

In the first part of this series of WordPress development introduction articles, we learn to customize a theme through action hooks. These hooks allow us to associate our code with specific events in the system, such as the request for a page or the publishing of a post or commentary. All without interfering with the core of WordPress, which should be free of custom code to facilitate the updates.
Filters
We have seen in the first part that exists a second class of hooks, called filters. They are very similar to actions, with a slight difference: every filter function receives an argument and returns, transformed or not, the first received argument.
For this motif, and as the name implies, they are perfect for information process and transformation, or to apply in validations, before being stored in a database or displayed to the visiter. Let us try some examples.
My first filter
We will keep our modifications inside the functions.php theme file, located at /wp-content/themes/theme folder/functions.php, and only afterwards we will apply our knowledge in a new extension development.
The creation and register process for a filter is similar to the procedure of registering an action, so it is necessary to write the filtering function in PHP and associate it with a WordPress filter usingadd_filter()
.
For our first example, we will customize the listings display, by transforming the “More…” hyperlink text in every article separated by one More… button by something more elaborate.

Figura 1: O que pretendemos modificar.
function tutorial_the_content_more_link ( $output, $more_link_text ) { $more_link_url = get_permalink() . '#more-' . get_the_ID(); $new_more_link_text = "Continue a ler, modificado pelo meu primeiro filtro →"; $output = ' ' . $new_more_link_text . ''; return $output; }
The same as actions, it is mandatory to register the filter. Let’s use the add_filter()
function to associate our function to the excerpt_more
filter:
add_filter( 'the_content_more_link', 'tutorial_the_content_more_link', 10, 2 );
Let’s take small steps at a time.
First and foremost, the filter we are going to use (the_content_more_link
), receives two arguments: the first is the already existing hyperlink, and the second the simple text for that hyperlink. The number 2
inside the add_filter()
function indicate it, the same way that we saw with actions. The number 10
indicate the filter’s priority, once again, similar with what we already know. The bigger, the later it will run inside the the_content_more_link
filter’s sequence.
In this particular case, we want to rebuild the complete hyperlink, throwing away what comes from behind. As such,, we need to build the destination url using the get_permalink()
and get_the_ID()
functions, and storing it in the $more_link_url
variable.
At the end, we will redefine the $output
variable with the hyperlink HTML that we want to display and will return that same value. If it isn’t modified by another filter, this should be the HTML the visitor will see at the site.

Figura 2: A hiperligação modificada.
Our tutorial_the_content_more_link()
function may contain more elaborated code, for instance, to present the total word count of that particular article.
function tutorial_the_content_more_link_wordcount( $output, $more_link_text ) { global $post; $word_count = str_word_count( strip_tags( $post->post_content ) ); $more_link_text = "Continue a ler ($word_count palavras) →"; $more_link_url = get_permalink() . '#more-' . get_the_ID(); $output = ' ' . $more_link_text . ''; return $output; } add_filter( 'the_content_more_link', 'tutorial_the_content_more_link_wordcount', 20, 2 );
For that, we will need to access data from that article whose aren’t available in the arguments. Declaring the global variable $post
inside our function, so we may access the necessary data using $post->get_content
.
Now, with the entire text on our side, we may count the total words using str_word_count
, with the additional step to clean all the existing HTML markup with strip_tags
. All rest is similar to the first function we saw, with the sole difference the printing of total words.

Figura 3: The hyperlink with the words counter.
Filter chaining
In some particular uses it might be necessary to execute several functions with the same filter. There is no problem with that: like actions, all filters run in sequence, using the defined priority (the same as actions) and the registration order.
To exemplify that mecanism, we will change the article title with the filter the_title
, the purpose is to present the total word count, and to add an HTML tag around the text.
function tutorial_the_title_em ( $title, $id ) { return "$title"; } function tutorial_the_title_wordcount ( $title, $id ) { if ($id) { $post = &get_post( $id ); $word_count = str_word_count( strip_tags( $post->post_content ) ); return $title . " ($word_count palavras)"; } else { return $title; } } add_filter( 'the_title', 'tutorial_the_title_em', 10, 2 ); // Priority 10 add_filter( 'the_title', 'tutorial_the_title_wordcount', 20, 2 ); // Priority 20
Take notice that the filter the_title
accepts among its arguments the article identifier, and since we don’t have garanties that this function will run inside the WordPress presentation loop, we will have to take that cover and access the content without the global variable $post
. Let’s use the get_post()
function for that purpose.

Figura 4: Title with italic applied first and the total words added afterwards.
In the above filters sequence, the italic is applied to the title first and afterwards it is added the total word count. However, if we invert priorities…
add_filter( 'the_title', 'tutorial_the_title_em', 20, 2 ); // Priority 20 add_filter( 'the_title', 'tutorial_the_title_wordcount', 10, 2 ); // Priority 10
…then the counter is added first and the italic will affect everything.

Figure 5: Title with total word count added first and italic applied afterwards.
the_title
and the_content_more_link
are only two of an huge list of available filters. There is a list in the Codex with all the available WordPress filters, and we may add our own personal filters and, use filters from other themes and plugins. It is important to give credit to Adam Brown for assembling the WordPress Hook Database, with more detailed information about all hooks supported by WordPress in each specific version.
Remove filters
In the same way we can add filters, as well we may remove an association. We have seen in the first article how this process works using the remove_action()
function.
The same goes here, with the sole difference is the function’s name remove_filter()
. For instance, running the tutorial_the_title_wordcount()
function would prevent the association with the filter the_title
.
// Remove filter with priority 10: remove_filter( 'the_title', 'tutorial_the_title_wordcount', 10 ); // Remove filter with priority 20: remove_filter( 'the_title', 'tutorial_the_title_wordcount', 20 );
However, the remove_all_filters()
function would prevent all functions associated with a given filter to run. For instance, all the declared filters the_title
prior to this line will be disabled:
remove_all_filters( 'the_title' ); // But this one, associated after the cleanup, will run: add_filter( 'the_title', 'tutorial_the_title_em', 10, 2 );
New filters
Similar to do_action()
presented in the last article, there is an analogous function for filters called apply_filters()
.
We may, for instance, apply all WordPress’ title filters (including the above mention) to an arbitrary character chain:
// Associate the function to a new filter: add_filter( 'custom_the_title', 'tutorial_the_title_em', 10, 2 ); // Running the filter... $filtered_title = apply_filters( 'custom_the_title', 'Hello, world!', 0 ) echo $filtered_title; // ...or even an already existing filter. $filtered_title = apply_filters( 'the_title', 'Hello, world!', 0 ) echo $filtered_title;
In the next tutorial, we will work with WordPress’ shortcodes API, a mechanism with special tags that may be included inside the articles content with difficult presentation functionalities or even impossible to reproduce through an ordinary visual editor.