Created
March 18, 2021 20:54
-
-
Save tdlm/2fd858ad9e00300a331cd11565cd5814 to your computer and use it in GitHub Desktop.
Hookable Plugins with singleton action
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 | |
/** | |
* Trait Hookable | |
*/ | |
trait Hookable | |
{ | |
/** | |
* Add doc hooks. | |
*/ | |
public function addDocHooks() | |
{ | |
// Get instanced class to relate the callback to. | |
$object = static::$instances[static::class]; | |
// Start a reflector. | |
$reflector = new \ReflectionObject($object); | |
foreach ($reflector->getMethods() as $method) { | |
$phpdoc = $method->getDocComment(); | |
$arg_count = $method->getNumberOfParameters(); | |
if (preg_match_all( | |
'#\* @(?P<type>filter|action|shortcode)\s+(?P<name>[a-z0-9\/\=\-\._]+)(?:,\s+(?P<priority>\d+))?#', | |
$phpdoc, | |
$matches, | |
PREG_SET_ORDER | |
)) { | |
foreach ($matches as $match) { | |
$type = $match['type']; | |
$name = $match['name']; | |
$priority = empty($match['priority']) ? 11 : intval($match['priority']); | |
$callback = [$this, $method->getName()]; | |
call_user_func( | |
[ | |
self::class, | |
'add' . ucfirst($type), | |
], | |
$name, | |
$callback, | |
compact('priority', 'arg_count') | |
); | |
} | |
} | |
} | |
} | |
/** | |
* Hooks a function on to a specific action. | |
* | |
* @param string $name The hook name. | |
* @param array $callback The class object and method. | |
* @param array $args An array with priority and arg_count. | |
* | |
* @return mixed | |
*/ | |
public function addAction( | |
$name, | |
$callback, | |
$args = [] | |
) { | |
// Merge defaults. | |
$args = array_merge( | |
[ | |
'priority' => 10, | |
'arg_count' => PHP_INT_MAX, | |
], | |
$args | |
); | |
return $this->addHook('action', $name, $callback, $args); | |
} | |
/** | |
* Hooks a function on to a specific filter. | |
* | |
* @param string $name The hook name. | |
* @param array $callback The class object and method. | |
* @param array $args An array with priority and arg_count. | |
* | |
* @return mixed | |
*/ | |
public function addFilter( | |
$name, | |
$callback, | |
$args = [] | |
) { | |
// Merge defaults. | |
$args = array_merge( | |
[ | |
'priority' => 10, | |
'arg_count' => PHP_INT_MAX, | |
], | |
$args | |
); | |
return $this->addHook('filter', $name, $callback, $args); | |
} | |
/** | |
* Hooks a function on to a specific shortcode. | |
* | |
* @param string $name The shortcode name. | |
* @param array $callback The class object and method. | |
* | |
* @return mixed | |
*/ | |
public function addShortcode( | |
$name, | |
$callback | |
) { | |
return $this->addHook('shortcode', $name, $callback); | |
} | |
/** | |
* Hooks a function on to a specific action/filter. | |
* | |
* @param string $type The hook type. Options are action/filter. | |
* @param string $name The hook name. | |
* @param array $callback The class object and method. | |
* @param array $args An array with priority and arg_count. | |
* | |
* @return mixed | |
*/ | |
protected function addHook( | |
$type, | |
$name, | |
$callback, | |
$args = [] | |
) { | |
$priority = $args['priority'] ?? 10; | |
$arg_count = $args['arg_count'] ?? PHP_INT_MAX; | |
$fn = sprintf('\add_%s', $type); | |
return \call_user_func($fn, $name, $callback, $priority, $arg_count); | |
} | |
} |
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 | |
/** | |
* Bootstrap class, for kicking things off. | |
*/ | |
class Loader | |
{ | |
/** | |
* Loader constructor. | |
*/ | |
public function __construct() | |
{ | |
\add_action('muplugins_loaded', [__CLASS__, 'bootstrap']); | |
} | |
/** | |
* Fire up our items. | |
*/ | |
public static function bootstrap() | |
{ | |
Menu::instance(); // Create instance and load up all the hooks! | |
} | |
} | |
// Now just create a new instance of the Loader! | |
new Loader(); |
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 | |
/** | |
* Plugin abstract class. | |
*/ | |
abstract class Plugin extends Singleton | |
{ | |
use Hookable; | |
/** | |
* Gets the plugin directory path. | |
* | |
* @return string Plugin directory path. | |
*/ | |
public static function dir() | |
{ | |
return plugin_dir_path(__FILE__); | |
} | |
/** | |
* Gets the plugin file path. | |
* | |
* @return string Plugin file path. | |
*/ | |
public static function file() | |
{ | |
return __FILE__; | |
} | |
/** | |
* Gets the plugin url. | |
* | |
* @return string Plugin url. | |
*/ | |
public static function url() | |
{ | |
return plugin_dir_url(__FILE__); | |
} | |
/** | |
* Gets the plugin version. | |
* | |
* @return string Plugin version. | |
*/ | |
public static function version() | |
{ | |
return get_file_data(self::file(), ['Version' => 'Version'], 'plugin')['Version']; | |
} | |
} |
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 | |
/** | |
* Trait Singleton | |
*/ | |
abstract class Singleton | |
{ | |
/** | |
* @var self Reference to singleton instance. | |
*/ | |
protected static $instances = []; | |
/** | |
* Creates a new instance of a singleton class (via late static binding), accepting a variable-length argument list. | |
* | |
* @param mixed ...$params | |
* | |
* @return self | |
*/ | |
final public static function instance(...$params): Singleton | |
{ | |
if (!isset(static::$instances[static::class])) { | |
static::$instances[static::class] = new static(); | |
// Call 'addDocHooks' to parse and fire object doc actions/filters. | |
if (method_exists(self::$instances[static::class], 'addDocHooks')) { | |
call_user_func_array([self::$instances[static::class], 'addDocHooks'], []); | |
} | |
// Call 'init' bootstrap method if it's defined in the inheriting class. | |
if (method_exists(self::$instances[static::class], 'init')) { | |
call_user_func_array([self::$instances[static::class], 'init'], func_get_args()); | |
} | |
} | |
return static::$instances[static::class]; | |
} | |
/** | |
* Prevents direct instantiation. | |
* | |
* @return void | |
*/ | |
final private function __construct() | |
{ | |
} | |
/** | |
* Prevents cloning the singleton instance. | |
* | |
* @return void | |
*/ | |
final public function __clone() | |
{ | |
} | |
/** | |
* Prevents unserializing the singleton instance. | |
* | |
* @return void | |
*/ | |
final public function __wakeup() | |
{ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment