A library to allow the use of PHP attributes for WordPress hooks

Overview

WordPress Hook Attributes

Build Status

This library should be considered experimental and not production ready.

Installation

composer require boxuk/wp-hook-attributes

Until this is on packagist or similar, you will need to add this repository to your composer.json repositories section, see below for an example:

{
  "repositories": [
    {
      "type": "vcs",
      "url": "https://github.com/boxuk/wp-hook-attributes"
    }
  ]
}

Enable caching (optional, recommended for production)

Basic array based caching is enabled as standard but in production you may wish to bring in a more optimal adapter. Below is an example using memcache, but any PSR-6 adapter is supported.

composer require cache/memcache-adapter

use Doctrine\Common\Annotations\Reader;
use BoxUk\WpHookAttributes\PsrCachedAnnotationReader;
use Cache\Adapter\Memcache\MemcacheCachePool;
use Psr\Cache\CacheItemPoolInterface;

if ( wp_get_environment_type() === 'production' ) {
	add_filter(
		'wp_hook_attributes_cache_adapter',
		function ( CacheItemPoolInterface $cache_adapter ): CacheItemPoolInterface {
			global $wp_object_cache;
			if ( $wp_object_cache->get_mc( 'default' ) instanceof \Memcache ) {
				$client = $wp_object_cache->get_mc( 'default' );

				return new MemcacheCachePool( $client );
			}

			return $cache_adapter;
		}
	);
}

Usage

Now you can annotate functions and methods with attributes to attach them to a hook.

use BoxUk\WpHookAttributes\Hook\Attributes\Action;
use BoxUk\WpHookAttributes\Hook\Attributes\Filter;

// Example of using an action hook
#[Action('init')]
function basic_action(): string {
	return 'something...';
}

// Example of using a filter hook
#[Filter('the_content')]
function basic_filter(): string {
	return 'something...';
}

// You can also attach a priority and args
#[Action('init', priority: 20, args: 4)]
function advanced_action( string $arg1, int $arg2, bool $arg3, array $arg4 ): string
	return 'something...';
}

Not on PHP8 yet? You can use annotations instead

use BoxUk\WpHookAttributes\Hook\Annotations\Action;
use BoxUk\WpHookAttributes\Hook\Annotations\Filter;

// Example of using an action hook
/**
 * @Action("init")
 */
function basic_action(): string {
	return 'something...';
}

// Example of using a filter hook
/**
 * @Filter("the_content") 
 */
function basic_filter(): string {
	return 'something...';
}

// You can also attach a priority and args
/**
 * @Action("init", priority="20", args="4")
 */
function advanced_action( string $arg1, int $arg2, bool $arg3, array $arg4 ): string
	return 'something...';
}

Note: Anything lower than PHP 7.4 is not supported.

Registering a namespace or prefix (highly recommended)

You likely want to register a namespace or prefix to ensure it only looks for attributes/annotations for your code. You can do so via the following hook:

If you're using annotations and don't do this it will likely be extremely slow

// Namespace
add_filter( 'wp_hook_attributes_registered_namespaces', function() {
	return [
		'BoxUk\Mu\Plugins',
	];
});

// Prefix
add_filter( 'wp_hook_attributes_registered_prefixes', function() {
	return [
		'boxuk_',
	];
});

It does a stripos() comparison, so you can just put the first part of the namespace/prefix.

Registering files and classes

Currently only works with defined functions and declared classes that are registered before the init hook. To get around this you can register function files or classes manually using the following hooks. This will need to be done prior to init though, or the resolver will need to be called manually (details below).

add_filter( 'wp_hook_attributes_registered_function_files', function() {
	return [
		'path/to/my/file/with/functions.php',
	];
});

add_filter( 'wp_hook_attributes_registered_classes', function() {
	return [
		'Fqcn\Of\My\Class',
	];
});

Ignoring existing annotation names

Sometimes you may get errors when using annotations that an existing annotation hasn't been imported. This is because sometimes you find non-standard annotations or docblock parameters that we need to ignore.

Some common WordPress and related libraries are ignored by default, but it won't cover everything.

You can ignore any custom annotations you need to with the following hook:

add_filter( 
	'wp_hook_attributes_annotation_ignores',
	function( array $existing_ignores ): array {
		$existing_ignores[] = 'my-custom-annotation';
		return $existing_ignores;
	}
);

Limitations

Attributes on hooks prior to init require a bit more work

If you wish to use hooks prior to the init hook, for example muplugins_loaded you will not be able to use attributes for these without a bit more effort. As they would have already been called by the point the hook resolver is called, you will need to call the hook resolver yourself manually. For example, let's say you have a hook on muplugins_loaded which is a pre-init hook.

/**
 * @ActionAnnotation("muplugins_loaded")
 */
#[Action('muplugins_loaded')]
function muplugins_loaded_action(): void
{
    echo 'on muplugins_loaded action';
}

The muplugins_loaded hook would have already been called by the time our init hook is called which calls the hook resolver. So in these scenarios, you'll need to call the hook resolver manually, e.g.

/**
 * @ActionAnnotation("muplugins_loaded")
 */
#[Action('muplugins_loaded')]
function muplugins_loaded_action(): void
{
    echo 'on muplugins_loaded action';
}

// Some place earlier than `muplugins_loaded`, maybe a `000-my-project.php` or something within `mu-plugins`.
( new WordPressHookAttributes() )();

See the next limitation as to why we don't load the resolver this early by default.

The main hooks that this applies to is (but not limited to):

muplugins_loaded
registered_taxonomy
registered_post_type
plugins_loaded
sanitize_comment_cookies
setup_theme
unload_textdomain
load_textdomain
after_setup_theme
auth_cookie_malformed
auth_cookie_valid
set_current_user

Source: http://rachievee.com/the-wordpress-hooks-firing-sequence/

Functions/methods must be registered before the init hook

Attributes should work for any function/method registered before the init hook is called. Any function/method that is registered as part of an mu-plugin or a theme should work as the hooks to load these are called prior to init.

What won't work is any function/method that is registered after the init hook, for example the following won't work because wp_loadded is called after init and thus the functions within my-functions.php won't be registered in time:

// This will not work.
add_action( 'wp_loaded', function() {
    require_once 'my-functions.php';
});

You can register files manually, but again, this must be done before init, so to fix the above you can do:

add_action( 'muplugins-loaded', function() {
    add_filter( 'wp_hook_attributes_registered_function_files', function() {
        return [
            'my-functions.php';
        ];
    });
});

Similarly, simply requiring it will work also:

add_action( 'muplugins-loaded', function() {
    require_once 'my-functions.php';
});

Non-static methods are not supported

If you have a method which relies on an instance of the current object, for examples:

class Example {
    private $foo = 'world';
    
    public function hello(): string {
        return 'Hello ' . $this->foo;
    }
}

You are able to set up a callback using an instance of Example, e.g.

$example = new Example();
$callback = [ $example, 'hello' ];

However, this isn't supported with this library because it cannot make an assumption on how it instantiates the class. Therefore, only static methods will work. It also requires methods are marked as static even if they are implicitly static. This is good practice anyway as using a method as static if not explicitly declared will raise a PHP Deprecated on PHP 7.4 and a Fatal Error on PHP 8.

You might also like...
A custom WordPress nav walker class to fully implement the Twitter Bootstrap 4.0+ navigation style (v3-branch available for Bootstrap 3) in a custom theme using the WordPress built in menu manager.

WP Bootstrap Navwalker This code in the main repo branch is undergoing a big shakeup to bring it in line with recent standards and to merge and test t

A curated list of Awesome WordPress Theme, Plugins and Framework development Resources and WordPress Communities.

Awesome WordPress A curated list of Awesome WordPress Theme, Plugins and Framework development Resources and WordPress Communities. Inspired by bayand

The Pronamic WordPress Basecone plugin allows you to connect your WordPress installation to Basecone.

Pronamic WordPress Basecone The Pronamic WordPress Basecone plugin allows you to connect your WordPress installation to Basecone. Table of contents Au

A WordPress plugin to suspend WordPress sites automagically. Simple and lightweight, no annoying ads and fancy settings.
A WordPress plugin to suspend WordPress sites automagically. Simple and lightweight, no annoying ads and fancy settings.

Suspend WP A WordPress plugin to suspend WordPress sites automagically. Simple and lightweight, no annoying ads and fancy settings. 😎 Demo (coming so

Twenty Twenty-Two, the default WordPress theme that will launch with WordPress 5.9.
Twenty Twenty-Two, the default WordPress theme that will launch with WordPress 5.9.

Twenty Twenty-Two Welcome to the development repository for the default theme that will launch with WordPress 5.9. About Twenty Twenty-Two is designed

Easy handle APlayer on WordPress. A shortcode for WordPress to using APlayer.

Description Easy handle APlayer on WordPress. A shortcode for WordPress to using APlayer. Support [audio] tag, compatible with AMP. Requirement WordPr

WordPress & TypeScript. Simple starter template for WordPress projects

WordPress & TypeScript. Simple starter template for WordPress projects that want to use TypeScript in combination with @wordpress/scripts

WordPress entities creation library (CPT, CT, native option page, ACF option page, user role, block pattern category, block category…)

WordPress entities creation library (CPT, CT, native option page, ACF option page, user role, block pattern category, block category…)

A PHP Class for creating Wordpress Custom Post Types easily

N.B I've released an updated version of the project to a new repository, PostTypes. WP Custom Post Type Class v1.4 A single class to help you build mo

Comments
  • Add cache warmer

    Add cache warmer

    Caching with a more persistent cache like memcache or redis is a good idea in production. It's likely you'd want to warm the cache before (or probably during) deployment though so that x number of users don't get a slow experience.

    There's 2 ways I can think of doing this. One is a WP CLI command, the other is a scheduled task.

    enhancement 
    opened by jenkoian 0
Releases(0.0.2)
  • 0.0.2(Mar 3, 2022)

    What's Changed

    • [FEATURE] PHP8.1 support. by @jenkoian in https://github.com/boxuk/wp-hook-attributes/pull/5

    Full Changelog: https://github.com/boxuk/wp-hook-attributes/compare/0.0.1...0.0.2

    Source code(tar.gz)
    Source code(zip)
  • 0.0.1(Jan 17, 2022)

Owner
Box UK
Box UK
Simple WordPress plugin to learn how to understand WordPress Crons and the Action Scheduler library.

Simple WordPress plugin to learn how to understand WordPress Crons and the Action Scheduler library. Import Jamendo playlists with tracks in WordPress posts.

Pierre Saikali 3 Dec 7, 2022
Use WordPress backend with Laravel or any PHP application

A collection of Model classes that allows you to get data directly from a WordPress database. Corcel is a collection of PHP classes built on top of El

Corcel PHP 3.9k Dec 29, 2022
Use WordPress backend with Laravel or any PHP application

A collection of Model classes that allows you to get data directly from a WordPress database. Corcel is a collection of PHP classes built on top of El

Corcel PHP 3.9k Jan 7, 2023
A PHP client for Wordpress websites that closely implement the XML-RPC WordPress API

Wordpress XML-RPC PHP Client A PHP client for Wordpress websites that closely implement the XML-RPC WordPress API Created by Hieu Le MIT licensed. Cur

Hieu Le 112 Nov 10, 2022
Automattic 10.7k Jan 2, 2023
The easiest to use WordPress option framework.

Titan Framework The easiest to use WordPress options framework. Titan Framework allows theme and plugin developers to create admin pages, options, met

Gambit Technologies 374 Nov 14, 2022
WordPress plugin people can use to embed to their website: a Mintbase NEAR NFT and Mintbase NEAR store

mintbase-embed WordPress plugin people can use to embed to their website: a Mintbase NEAR NFT and Mintbase NEAR store This is demo plugin that allows

null 3 Oct 1, 2021
This is code to create a new user as admin use for Wordpress FrontEnd Developer to prevent any scaming from clients

theme-setup This is code to create a new user as admin use for Wordpress FrontEnd Developer to prevent any scaming from clients How to use Just copy c

Abdul Razzaq 1 Nov 27, 2021
Beauty and simple Wordpress video player plugin. Powerfull and lite in use.

Sonic Spectre Video Player Beauty and simple Wordpress video player plugin. Powerfull and lite in use. Quick Start: Download plugin from this repo and

Halcyon 1 Jan 4, 2022
A WordPress plugin to re-use the same domain name for both your website and your Rebrandly links

By installing this plugin and configuring it to connect with your Rebrandly account, you will be able to create branded links using the same domain yo

null 3 Jan 19, 2022