includes/class-hooks.php
<?php
/**
* View Admin As - Class Hooks
*
* @author Jory Hogeveen <info@keraweb.nl>
* @package View_Admin_As
*/
if ( ! defined( 'VIEW_ADMIN_AS_DIR' ) ) {
die();
}
/**
* Hooks class that holds all registered actions and filters.
*
* @author Jory Hogeveen <info@keraweb.nl>
* @package View_Admin_As
* @link https://github.com/JoryHogeveen/view-admin-as/wiki/Actions-&-Filters
* @since 1.8.0
* @version 1.8.7
*/
class VAA_View_Admin_As_Hooks
{
/**
* The array of actions registered with WordPress.
*
* @since 1.8.0
* @access protected
* @var array $actions The actions registered with WordPress.
*/
protected $_actions = array();
/**
* The array of filters registered with WordPress.
*
* @since 1.8.0
* @access protected
* @var array $filters The filters registered with WordPress.
*/
protected $_filters = array();
/**
* Log of actions run through this instance.
*
* @since 1.8.7
* @access protected
* @var array $filters Actions run through this instance.
*/
protected $_logged_actions = array();
/**
* Calls the callback functions that have been added to the hook tag.
* This method will also log the initial call and store the params.
*
* @since 1.8.7
* @param string $tag The name of the hook.
* @param mixed ...$args Additional parameters to pass to the callback functions.
* @return mixed The filtered value after all hooked functions are applied to it.
*/
public function do_action( $tag ) {
$args = func_get_args();
$log = $args;
array_shift( $log ); // Remove tag.
if ( VAA_API::debug() ) {
$log['callstack'] = debug_backtrace();
}
if ( ! isset( $this->_logged_actions[ $tag ] ) ) {
$this->_logged_actions[ $tag ] = array();
}
$this->_logged_actions[ $tag ][] = $log;
return call_user_func_array( 'do_action', $args );
}
/**
* Retrieve the number of times an action is fired.
*
* @since 1.8.7
* @param string $tag The name of the action hook.
* @return int The number of times action hook $tag is fired.
*/
public function did_action( $tag ) {
return did_action( $tag );
}
/**
* Get the arguments from a specific action that has been fired.
*
* @since 1.8.7
* @param string $tag The name of the action hook.
* @param int $occurrence The # time it was fired.
* @param bool $objects Return the full object of a callback? Default: false, can cause PHP memory issues.
* @return array
*/
public function get_action_log( $tag = null, $occurrence = null, $objects = false ) {
$log = VAA_API::get_array_data( $this->_logged_actions, $tag );
if ( $log && is_int( $occurrence ) ) {
// Subtract one since the counter starts at 0;
$occurrence--;
$log = VAA_API::get_array_data( $log, $occurrence );
}
if ( ! $objects ) {
$log = $this->_convert_callback( $log );
}
return $log;
}
/**
* Convert callable into an identifier.
*
* @since 1.8.0
* @access protected
* @see _wp_filter_build_unique_id()
* @param string $hook The name of the WordPress hook (that is, actions or filters).
* @param callable $callback The callable.
* @param int $priority The priority at which the function would be fired. Default: 10.
* @return string
*/
protected function _get_identifier( $hook, $callback, $priority ) {
if ( function_exists( '_wp_filter_build_unique_id' ) ) {
return _wp_filter_build_unique_id( $hook, $callback, $priority );
}
// Fallback since `_wp_filter_build_unique_id()` is a private WP function.
return VAA_API::callable_to_string( $callback );
}
/**
* Add a new action to the collection to be registered with WordPress.
*
* @since 1.8.0
* @see add_action()
* @param string $hook The name of the WordPress action.
* @param callable $callback The callable.
* @param int $priority (optional) The priority at which the function should be fired. Default: 10.
* @param int $accepted_args (optional) The number of arguments that should be passed to the $callback. Default: 1.
*/
public function add_action( $hook, $callback, $priority = 10, $accepted_args = 1 ) {
add_action( $hook, $callback, $priority, $accepted_args );
$this->_actions = $this->_add( $this->_actions, $hook, $callback, $priority, $accepted_args );
}
/**
* Add a new filter to the collection to be registered with WordPress.
*
* @since 1.8.0
* @see add_filter()
* @param string $hook The name of the WordPress filter.
* @param callable $callback The callable.
* @param int $priority (optional) The priority at which the function should be fired. Default: 10.
* @param int $accepted_args (optional) The number of arguments that should be passed to the $callback. Default: 1.
*/
public function add_filter( $hook, $callback, $priority = 10, $accepted_args = 1 ) {
add_filter( $hook, $callback, $priority, $accepted_args );
$this->_filters = $this->_add( $this->_filters, $hook, $callback, $priority, $accepted_args );
}
/**
* A utility function that is used to register the hooks into a single collection.
*
* @since 1.8.0
* @access protected
* @param array[] $hooks The collection of hooks (that is, actions or filters).
* @param string $hook The name of the WordPress filter that is being registered.
* @param callable $callback The callable.
* @param int $priority The priority at which the function should be fired.
* @param int $accepted_args The number of arguments that should be passed to the $callback.
* @return array The collection of actions and filters registered with WordPress.
*/
protected function _add( $hooks, $hook, $callback, $priority, $accepted_args ) {
if ( ! isset( $hooks[ $hook ] ) ) {
$hooks[ $hook ] = array();
}
if ( ! isset( $hooks[ $hook ][ $priority ] ) ) {
$hooks[ $hook ][ $priority ] = array();
}
$hooks[ $hook ][ $priority ][ $this->_get_identifier( $hook, $callback, $priority ) ] = array(
'hook' => $hook,
'callback' => $callback,
'priority' => $priority,
'accepted_args' => $accepted_args,
);
ksort( $hooks[ $hook ] );
return $hooks;
}
/**
* Remove an action from the collection registered with WordPress.
*
* @since 1.8.0
* @see remove_action()
* @param string $hook The name of the WordPress action.
* @param callable $callback The callable.
* @param int $priority (optional) The priority at which the function would be fired. Default: 10.
* Pass `null` to let this class try to find the priority.
*/
public function remove_action( $hook, $callback, $priority = 10 ) {
$priority = $this->_validate_priority( $this->_actions, $hook, $callback, $priority );
remove_action( $hook, $callback, $priority );
$this->_actions = $this->_remove( $this->_actions, $hook, $callback, $priority );
}
/**
* Remove a filter from the collection registered with WordPress.
*
* @since 1.8.0
* @see remove_filter()
* @param string $hook The name of the WordPress filter.
* @param callable $callback The callable.
* @param int $priority (optional) The priority at which the function would be fired. Default: 10.
* Pass `null` to let this class try to find the priority.
*/
public function remove_filter( $hook, $callback, $priority = 10 ) {
$priority = $this->_validate_priority( $this->_filters, $hook, $callback, $priority );
remove_filter( $hook, $callback, $priority );
$this->_filters = $this->_remove( $this->_filters, $hook, $callback, $priority );
}
/**
* A utility function that is used to remove registered hooks from a single collection.
*
* @since 1.8.0
* @access protected
* @param array[] $hooks The collection of hooks (that is, actions or filters).
* @param string $hook The name of the WordPress filter.
* @param callable $callback The callable.
* @param int $priority The priority at which the function should be fired.
* @return array The collection of actions and filters registered with WordPress.
*/
protected function _remove( $hooks, $hook, $callback, $priority ) {
unset( $hooks[ $hook ][ $priority ][ $this->_get_identifier( $hook, $callback, $priority ) ] );
if ( empty( $hooks[ $hook ][ $priority ] ) ) {
unset( $hooks[ $hook ][ $priority ] );
}
return $hooks;
}
/**
* Remove all hooks from the collection registered with WordPress.
*
* @since 1.8.0
* @param string $hook The name of the WordPress action.
* @param int|bool $priority (optional) The priority at which the function would be fired. Default: false (all).
*/
public function remove_all_hooks( $hook, $priority = false ) {
$this->remove_all_actions( $hook, $priority );
$this->remove_all_filters( $hook, $priority );
}
/**
* Remove all actions from the collection registered with WordPress.
*
* @since 1.8.0
* @see remove_all_actions()
* @param string $hook The name of the WordPress action.
* @param int|bool $priority (optional) The priority at which the function would be fired. Default: false (all).
*/
public function remove_all_actions( $hook, $priority = false ) {
remove_all_actions( $hook, $priority );
$this->_actions = $this->_remove_all( $this->_actions, $hook, $priority );
}
/**
* Remove all filters from the collection registered with WordPress.
*
* @since 1.8.0
* @see remove_all_filters()
* @param string $hook The name of the WordPress filter.
* @param int|bool $priority (optional) The priority at which the function would be fired. Default: false (all).
*/
public function remove_all_filters( $hook, $priority = false ) {
remove_all_filters( $hook, $priority );
$this->_filters = $this->_remove_all( $this->_filters, $hook, $priority );
}
/**
* A utility function that is used to remove all registered hooks from a single collection.
*
* @since 1.8.0
* @access protected
* @param array[] $hooks The collection of hooks (that is, actions or filters).
* @param string $hook The name of the WordPress filter.
* @param int|bool $priority The priority at which the function should be fired.
* @return array The collection of actions and filters registered with WordPress.
*/
protected function _remove_all( $hooks, $hook, $priority ) {
if ( false !== $priority ) {
unset( $hooks[ $hook ][ $priority ] );
return $hooks;
}
unset( $hooks[ $hook ] );
return $hooks;
}
/**
* Remove all plugin hooks from the collection registered with WordPress.
*
* @since 1.8.0
* @param string $hook (optional) The name of the WordPress action.
* @param int|bool $priority (optional) The priority at which the function would be fired. Default: false (all).
* @param string $class (optional) Only remove filters from a specific class.
*/
public function remove_own_hooks( $hook = null, $priority = false, $class = '' ) {
$this->remove_own_actions( $hook, $priority, $class );
$this->remove_own_filters( $hook, $priority, $class );
}
/**
* Remove all plugin actions from the collection registered with WordPress.
*
* @since 1.8.0
* @param string $hook (optional) The name of the WordPress action.
* @param int|bool $priority (optional) The priority at which the function would be fired. Default: false (all).
* @param string $class (optional) Only remove filters from a specific class.
*/
public function remove_own_actions( $hook = null, $priority = false, $class = '' ) {
$this->_actions = $this->_remove_own( $this->_actions, $hook, $priority, 'remove_action', $class );
}
/**
* Remove all plugin filters from the collection registered with WordPress.
*
* @since 1.8.0
* @param string $hook (optional) The name of the WordPress filter.
* @param int|bool $priority (optional) The priority at which the function would be fired. Default: false (all).
* @param string $class (optional) Only remove filters from a specific class.
*/
public function remove_own_filters( $hook = null, $priority = false, $class = '' ) {
$this->_filters = $this->_remove_own( $this->_filters, $hook, $priority, 'remove_filter', $class );
}
/**
* A utility function that is used to remove all registered plugin hooks from a single collection.
*
* Disable some PHPMD checks for this method.
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @todo Refactor to enable above checks?
*
* @since 1.8.0
* @access protected
* @param array[] $hooks The collection of hooks (that is, actions or filters).
* @param string $hook The name of the WordPress filter.
* @param int|bool $priority The priority at which the function should be fired.
* @param callable $function The function to use for removal.
* @param string $class Only remove filters from a specific class.
* @return array The collection of actions and filters registered with WordPress.
*/
protected function _remove_own( $hooks, $hook, $priority, $function, $class ) {
// Remove specific priority from hook.
if ( false !== $priority ) {
if ( isset( $hooks[ $hook ][ $priority ] ) ) {
foreach ( (array) $hooks[ $hook ][ $priority ] as $id => $args ) {
if ( $class ) {
$class_compare = ( isset( $args['callback'][0] ) ) ? $args['callback'][0] : '';
if ( is_object( $class_compare ) ) {
$class_compare = get_class( $class_compare );
}
if ( $class !== $class_compare ) {
continue;
}
}
// Remove it from WordPress.
$this->$function( $hook, $args['callback'], $priority );
unset( $hooks[ $hook ][ $priority ][ $id ] );
}
if ( empty( $hooks[ $hook ][ $priority ] ) ) {
unset( $hooks[ $hook ][ $priority ] );
}
}
return $hooks;
}
// Remove specific hook.
if ( null !== $hook ) {
if ( isset( $hooks[ $hook ] ) ) {
foreach ( (array) $hooks[ $hook ] as $priority => $foo ) {
$hooks = $this->_remove_own( $hooks, $hook, $priority, $function, $class );
}
if ( empty( $hooks[ $hook ] ) ) {
unset( $hooks[ $hook ] );
}
}
return $hooks;
}
// Remove everything.
foreach ( (array) $hooks as $hook => $foo ) {
$hooks = $this->_remove_own( $hooks, $hook, false, $function, $class );
}
return $hooks;
}
/**
* Validates the priority value.
* If it's passed as `null` it will attempt to find it.
*
* @since 1.8.0
* @param array[] $hooks The collection of hooks (that is, actions or filters).
* @param string $hook The name of the WordPress filter.
* @param callable $callback The callable.
* @param int $priority The priority at which the function should be fired.
* @return int Default: 10.
*/
protected function _validate_priority( $hooks, $hook, $callback, $priority ) {
if ( ! is_numeric( $priority ) ) {
$priority = $this->_find_priority( $hooks, $hook, $callback );
if ( ! is_numeric( $priority ) ) {
return 10;
}
}
return (int) $priority;
}
/**
* Finds the priority of a hook if unknown.
*
* @since 1.8.0
* @param array[] $hooks The collection of hooks (that is, actions or filters).
* @param string $hook The name of the WordPress filter.
* @param callable $callback The callable.
* @return int
*/
protected function _find_priority( $hooks, $hook, $callback ) {
if ( ! isset( $hooks[ $hook ] ) ) {
return null;
}
foreach ( (array) $hooks[ $hook ] as $priority => $registered ) {
foreach ( $registered as $args ) {
if ( $callback === $args['callback'] ) {
return $priority;
}
}
}
return null;
}
/**
* Return all registered hooks data.
* Can be used for debugging.
*
* @since 1.8.0
* @param string|array $keys The hook array keys to look for. Each key stands for a level deeper in the array.
* Order: hook type >> hook name >> priority >> function id >> hook args.
* In case of a string it will stand for the hook type.
* @param bool $objects Return the full object of a callback? Default: false, can cause PHP memory issues.
* @return array[]|mixed
*/
public function _get_hooks( $keys = null, $objects = false ) {
$data = array(
'actions' => $this->_actions,
'filters' => $this->_filters,
);
if ( ! $objects ) {
// Don't return full objects.
$data = $this->_convert_callback( $data );
}
if ( $keys ) {
$keys = (array) $keys;
foreach ( $keys as $key ) {
$data = VAA_API::get_array_data( $data, $key );
}
}
return $data;
}
/**
* Return all registered actions.
* Can be used for debugging.
*
* @since 1.8.0
* @param string|array $keys The hook array keys to look for. Each key stands for a level deeper in the array.
* Order: hook name >> priority >> function id >> hook args.
* In case of a string it will stand for the hook name.
* @param bool $objects Return the full object of a callback? Default: false, can cause PHP memory issues.
* @return array[]|mixed
*/
public function _get_actions( $keys = null, $objects = false ) {
$keys = (array) $keys;
array_unshift( $keys, 'actions' );
return $this->_get_hooks( $keys, $objects );
}
/**
* Return all registered filters.
* Can be used for debugging.
*
* @since 1.8.0
* @param string|array $keys The hook array keys to look for. Each key stands for a level deeper in the array.
* Order: hook name >> priority >> function id >> hook args.
* In case of a string it will stand for the hook name.
* @param bool $objects Return the full object of a callback? Default: false, can cause PHP memory issues.
* @return array[]|mixed
*/
public function _get_filters( $keys = null, $objects = false ) {
$keys = (array) $keys;
array_unshift( $keys, 'filters' );
return $this->_get_hooks( $keys, $objects );
}
/**
* Convert object types into object class names instead of full object data.
* @since 1.8.0
* @param array $array The collection of arrays that might contain objects.
* @return array
*/
protected function _convert_callback( $array ) {
foreach ( (array) $array as $key => $val ) {
if ( is_object( $val ) ) {
$array[ $key ] = get_class( $val );
continue;
}
if ( is_array( $val ) ) {
$array[ $key ] = $this->_convert_callback( $val );
}
}
return $array;
}
} // End class VAA_View_Admin_As_Hooks.