Deregister Anonymous Functions in WordPress

[ad_1]

When I first started writing about anonymous functions in WordPress back in January, I didn’t anticipate it spanning over three articles reaching into the fourth month of the year. But here we are.

That said, this final article in the series aims to help provide a short introduction to a number of technical ideas both in PHP and WordPress to explain why deregistering anonymous functions is nearly impossible.

And it provides a way for us to actually get contextual information about every single hook and callback in an instance of WordPress so we can handle the anonymous functions as we see fit all through the use of a plugin.


Recall from the first article, the reason I started writing about anonymous functions in WordPress all stemmed from a tweet (or post or whatever they are currently called) that stated:

I do wish developers would stop using WordPress hooks with (what I think are called) anonymous functions […].

They are very hard if not impossible to unhook.

And the short of it is that it’s true: Anonymous functions are easy to register against WordPress hooks and are difficult to deregister.

But it’s not impossible.

Deregister Anonymous Functions in WordPress

This is not what it means to remove a function from a hook.

If you’ve stumbled across this article, here’s this is what the first two articles covered (should you be interested in reading them):

In the latter, I provided some examples for how to programmatically identify the anonymous functions that exist in the instance of WordPress in which said code is running. Further, I provided an example function to include in the code so you can easily trace what the aforementioned code was doing.

A Brief Summary of Anonymous Functions in WordPress

Though I obviously recommend reading those articles (1, 2), here is a brief summary of what’s covered between the two of them:

  1. Anonymous functions (also called closures in PHP) are useful when you have a small, one-time callback that doesn’t need to be referenced elsewhere in your code.
  2. When an anonymous function is registered with a WordPress hook, PHP generates a unique ID for the closure using the spl_object_hash() function, and associates this ID with the callback function.
  3. This allows WordPress to track and manage callbacks even when they are anonymous functions.
  4. I show how to get the closure ID generated by PHP for a specific anonymous function callback.
  5. I talk about the global $wp_filter object and explain how it holds information on all registered actions and filters in WordPress.
  6. I share information for how to easily list all functions/callbacks hooked to a specific hook like wp_enqueue_scripts.
  7. I provide an example for how to create your own anonymous function hooked to wp_enqueue_scripts for testing purposes.
  8. The debug_backtrace() PHP function is used to render information about the call stack, including the object representing the anonymous function.
  9. Through the debug_backtrace() function, I show how to obtain the internal ID generated by PHP for a given anonymous function that’s registered with a specific hook.

And at the end of the second article, I wrote:

In the next article, I’ll walk through a process – regardless of how manual it is – for how to deregister the functions.

How To Identify Anonymous Functions in WordPress

And that’s what I intend to do in this article. Further, I’ll share a utility that I’ve written that helps give context to each function – anonymous or not – and where it’s located within the instance of WordPress where it’s running

A Brief Survey of Technical Details

Though you can skip to the end of the article to see the practical aspect of locating anonymous functions and deregistering them, there are technical concepts worth reviewing that are helpful in understanding why dealing with anonymous functions are problematic and the strategies we can use to locate them.

Is this how you picture anonymous functions?

Closures and Anonymous Functions

The terms ‘anonymous functions’ and ‘closures’ are often used interchangeably in WordPress. Though they are similar, there are also differences worth noting.

Consider the definitions of each:

  • A closure is a special type of anonymous function that has access to variables from the outer scope even after the outer function has returned. It’s like having a function within a function and the inner function has access to the variables in the function that contains it.
  • An anonymous function is a general term for any function defined without a name. In PHP, anonymous functions can be used just like named functions, but they are typically used as callback functions or for short, one-off tasks.

They are similar in the following ways:

  • Both are defined without a name, hence the term “anonymous.” They are typically used when you need a small, one-time-use function without the need to give it a specific name.
  • Both can access variables from the enclosing scope through variable capturing. This means they can “close over” variables and maintain their values even after the enclosing function has finished executing.

While both closures and anonymous functions are anonymous and can capture variables from the surrounding scope, closures have additional capabilities related to variable scope and lifetime management; however, in common use, the term “closure” is often used to refer to any anonymous function, leading to the confusion between the two terms.

So when discussing anonymous functions in the context of WordPress hooks, it’s absolutely more accurate to state ‘anonymous functions.’

In short, perhaps it’s easy to remember it by saying “all closures are anonymous functions but not all anonymous functions are closures.”

Garbage Collection in PHP

Collecting garbage computers is not the same as garbage collection in a computer.

When variables or objects are created in PHP, memory is allocated to store their values; however, when these variables or objects are no longer needed, they occupy memory space unnecessarily. Because of that, these values are referred to as garbage.

Periodically, PHP will run an internal mechanism looking for values in memory that are no longer needed and free up the memory.

This is important as it relates to trying to manually clear values registered with WordPress. Whenever an anonymous function is registered with a WordPress hook, the function will be given an ID that’s maintained by PHP internals. WordPress will also track a reference to the same function using the discussed $wp_filter object.

Given this, it’s possible to iterate through the data structures maintained by the $wp_filter object and it’s even possible to call PHP’s unset on the value referenced by the $wp_filter object leading one of think that it may be possible to brute force the process of deregistering a function.

unset() destroys the specified variables.

The behavior of unset() inside of a function can vary depending on what type of variable you are attempting to destroy.

If a globalized variable is unset() inside of a function, only the local variable is destroyed. The variable in the calling environment will retain the same value as before unset() was called.

unset, via the PHP Manual

There’s catch, though: The garbage collector. More specifically, when we destroy a specific variable maintained in $wp_filter, we are at the mercy of the garbage collector.

So it’s possible to unset a reference but the associated function may not actually be deregistered with WordPress until the garbage collector runs. And since it runs non-deterministically, we can never really know if the function is still in memory.

What are we to do?

A Short Introduction to Reflection

If we could personify reflection of our code, it’d look like this. Maybe.

This is where PHP’s Reflection classes come in handy.

PHP comes with a complete reflection API that adds the ability to introspect classes, interfaces, functions, methods and extensions. Additionally, the reflection API offers ways to retrieve doc comments for functions, classes and methods.

Reflection via the PHP Manual

This is where we can start making headway in locating anonymous functions and how we can manage them. Further, we can even determine what kind of function is being registered with a given hook.

By that, I mean we can determine if the associated functions are object-based functions, static functions, functions part of the global namespace, or even anonymous functions.

  • object-based functions are functions that are associated with an object,
  • static functions are functions that belong to a class rather than to instances of the class,
  • global functions are accessible from anywhere within PHP,
  • anonymous function are functions without a specified name and are often created as a callback.

Through the use of Reflection and an understanding of the aforementioned function types, it’s possible to get a lot of contextual information about every single type of function registered with every single WordPress hook in the instance of WordPress you’re running.

In other words, we can use PHP to iterate through all of the hooks that exist in an instance of WordPress, all of the functions and their types that exist in the installation, and even what file and where the functions are located within the file.

And since WordPress plugins, themes, core, and associated tools are open source, we can refactor the ways in which prove useful to us, it allows us to modify the code, open issues with theme or plugin authors, and more.

Ultimately, this allows us to actually manually deregister anonymous functions.

Callback Info, a WordPress Plugin

When working through the first two articles of this series, I wrote sample code that I shared throughout each of the blog posts. And the more work I did and the more I researched to put content into all of these articles, I more I was writing small utilities in my local development environment.

And given that:

  • we can easily get contextual information about all of the types of functions and to which hook they are registered in WordPress,
  • and there is a need for others to locate and alter these functions so they can deregister them (or at least reach out to the developers maintaining the software so they can refactor them),

I wrote a small utility to help provide contextual information about every function registered with all WordPress hooks.

Specifically, the plugin iterates through each hook and lists the type of callback function (object, static, global, or anonymous), and where it’s located:

It also has a sample anonymous function that you can manually invoke so you can trace it in the data that you see above and then see how to use the data to locate and manipulate the function as you see fit.

Callback Info

This plugin renders contextual information about every function registered with all WordPress hooks.

Callback Info is currently only available on GitHub. At the time of this writing, it’s in the pending queue for the WordPress Plugin Repository.

If you’re comfortable downloading and installing plugins from GitHub then head to the releases page and the first release of the plugin. It’s extremely important to read the README if for not other reason that these two parts:

What The Plugin Does

Callback Info looks at each hook used in the current WordPress instance including the active themes and plugins and renders:

  • the name of the hook,
  • the type of method (object, static, standard function, or anonymous),
  • the file in which it’s registered,
  • the line on which the function starts,
  • the line on which the function ends.

This gives you the ability to manipulate the functions as you see fit. Even if it means locating an anonymous function, naming it, and then deregistering it (or allowing it to be deregistered through the use of other core functionality).

Usage Instructions

Assuming that your domain is https://plugin.test, append the query string ?show-callback-info=true to your URL such that the address is https://plugin.test?show-callback-info=true.

This will render all of the hooks along with each function that fires during that hook. Further, you’ll see which file in which the function belongs as well as the starting line number and ending line number for the callback.

And More

There is more information about using the callback information as well as the sample anonymous function included with the plugin.

I’ll also have additional information about this plugin in the next few weeks.

Conclusion

In the course of the three articles that have covered a variety of topics from how anonymous functions are managed within WordPress and PHP, how to get contextual information about each of them, and how we can use built-in PHP functions to get as much contextual information about them.

All of the above helps to get a step closer to writing – or working with – code that can be more maintainable, communicating with developers to make it more so, or to get more contextual information about the environment in which all of our code is running.

And finally, all of this resulted in a plugin that I hope helps developers achieve all of the above as easily as possible. I do have a roadmap of ideas for Callback Info but it’s contingent on how well it’s received or deemed useful by those who use it.

[ad_2]

Source link