Last active
April 2, 2016 17:55
-
-
Save westonruter/bdab58392c6a6837d918 to your computer and use it in GitHub Desktop.
Some code we're using at XWP to make plugins easier and cleaner to write.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once __DIR__ . '/pluginception.php' | |
class ShoutPlugin { | |
use Pluginception; | |
function __construct() { | |
/* | |
* This method comes from the Pluginception trait, | |
* and will parse out @action/@filter tags from this | |
* object's methods' PhpDoc blocks, and then call | |
* add_action() and add_filter(). Note that the | |
* arg count is not passed, as the PHP_MAX_INT is | |
* used instead. | |
*/ | |
$this->add_hooks_from_object_method_docblocks(); | |
} | |
/** | |
* Make all post titles ¡SHOUT! | |
* | |
* @filter the_title | |
* | |
* @param string $title | |
* @return string | |
*/ | |
function shoutify_title( $title ) { | |
$title = '¡' . strtoupper( $title ) . '!'; | |
return $title; | |
} | |
/** | |
* This will get added to the very bottom (priority 100) of the footer. | |
* | |
* @action wp_footer, 100 | |
*/ | |
function show_props() { | |
?> | |
<p>Props Shady Sharaf for idea to use Reflection API to parse out the | |
<code>@filter</code>/<code>@action</code> tags from method PhpDoc.</p> | |
<?php | |
} | |
} | |
$shout_plugin = new ShoutPlugin(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Add improved add_filter/add_action methods to any class, and support for declaring | |
* filter/action callbacks via tags in PhpDoc comments. Props Shady Sharaf (@shadyvb) | |
* for this application of the Reflection API. | |
*/ | |
trait Pluginception { | |
/** | |
* @var array | |
*/ | |
protected $added_hooks = array(); | |
/** | |
* | |
*/ | |
function __destruct() { | |
$this->remove_all_hooks(); | |
} | |
/** | |
* @param $name | |
* @param $callback | |
* @param array $args | |
* | |
* @return mixed | |
*/ | |
function add_filter( $name, $callback, $args = array( 'priority' => 10, 'arg_count' => PHP_INT_MAX ) ) { | |
return $this->_add_hook( 'filter', $name, $callback, $args ); | |
} | |
/** | |
* @param $name | |
* @param $callback | |
* @param array $args | |
* | |
* @return mixed | |
*/ | |
function add_action( $name, $callback, $args = array( 'priority' => 10, 'arg_count' => PHP_INT_MAX ) ) { | |
return $this->_add_hook( 'action', $name, $callback, $args ); | |
} | |
/** | |
* @param $type | |
* @param $name | |
* @param $callback | |
* @param array $args | |
* | |
* @return mixed | |
*/ | |
protected function _add_hook( $type, $name, $callback, $args = array() ) { | |
$priority = isset( $args['priority'] ) ? $args['priority'] : 10; | |
$arg_count = isset( $args['arg_count'] ) ? $args['arg_count'] : PHP_INT_MAX; | |
$fn = sprintf( '\add_%s', $type ); | |
$retval = \call_user_func( $fn, $name, $callback, $priority, $arg_count ); | |
$this->added_hooks[] = compact( 'type', 'name', 'callback', 'priority' ); | |
return $retval; | |
} | |
/** | |
* Add actions/filters from the methods of this class based on doc blocks | |
*/ | |
function add_hooks_from_object_method_docblocks() { | |
$reflector = new \ReflectionObject( $this ); | |
foreach ( $reflector->getMethods() as $method ) { | |
$doc = $method->getDocComment(); | |
$arg_count = $method->getNumberOfParameters(); | |
if ( preg_match_all( '#\* @(?P<type>filter|action)\s+(?P<name>\w+)(?:,\s+(?P<priority>\d+))?#', $doc, $matches, PREG_SET_ORDER ) ) { | |
foreach ( $matches as $match ) { | |
$type = $match['type']; | |
$name = $match['name']; | |
$priority = is_null( $match['priority'] ) ? 10 : intval( $match['priority'] ); | |
$callback = array( $this, $method->getName() ); | |
call_user_func( array( $this, "add_{$type}" ), $name, $callback, $priority, $arg_count ); | |
} | |
} | |
} | |
} | |
/** | |
* | |
*/ | |
function remove_all_hooks() { | |
foreach ( $this->added_hooks as $added_hook ) { | |
$fn = sprintf( 'remove_%s', $added_hook['type'] ); | |
call_user_func( $fn, $added_hook['name'], $added_hook['callback'], $added_hook['priority'] ); | |
} | |
} | |
} |
BTW, Using the pattern [A-Za-z0-9_\/]
makes it work for filters/actions with slashes in their names, unlike the current usage of \w
, ie:
if ( preg_match_all( '#\* @(?P<type>filter|action)\s+(?P<name>[A-Za-z0-9_\/]+)(?:,\s+(?P<priority>\d+))?#', $doc, $matches, PREG_SET_ORDER ) ) {
This is specially helpful while working with ACF, most of their hooks employ the slash character.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Could the annotation follow the draft PSR-5 suggestion of being prefixed?