From 68c8b9cf0f5f2260894baa5906e61d0111bdb150 Mon Sep 17 00:00:00 2001 From: Andrew Nacin Date: Wed, 26 Feb 2014 18:43:59 +0000 Subject: [PATCH] Introduce doing_filter() and doing_action() to identify hooks in progress. did_action() returns true the moment a hook is initially run, leaving you no way to tell if the hook is still in progress. Hooks can be nested and this checks the entire stack, versus current_filter() which only identifies the final hook in the stack. This commit also introduces current_action() for parity. To tell if a hook has completed, one can use did_action() and ! doing_action() together. The functions do not require an argument. In that situation, they indicate whether the stack is empty. props ericmann for the initial unit tests. fixes #14994. git-svn-id: https://develop.svn.wordpress.org/trunk@27294 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/plugin.php | 55 ++++++++++++++++++ tests/phpunit/tests/actions.php | 100 ++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) diff --git a/src/wp-includes/plugin.php b/src/wp-includes/plugin.php index 6607440424..f5d213d356 100644 --- a/src/wp-includes/plugin.php +++ b/src/wp-includes/plugin.php @@ -324,6 +324,61 @@ function current_filter() { return end( $wp_current_filter ); } +/** + * Retrieve the name of the current action. + * + * @since 3.9.0 + * @uses current_filter() + * + * @return string Hook name of the current action. + */ +function current_action() { + return current_filter(); +} + +/** + * Retrieve the name of a filter currently being processed. + * + * The function current_filter() only returns the most recent filter or action + * being executed. did_action() returns true once the action is initially + * processed. This function allows detection for any filter currently being + * executed (despite not being the most recent filter to fire, in the case of + * hooks called from hook callbacks) to be verified. + * + * @since 3.9.0 + * @see current_filter() + * @see did_action() + * + * @param string $filter Optional. Filter to check. Defaults to null, which checks if any filter is currently being run. + * + * @global array $wp_current_filter + * + * @return bool Whether the filter is currently in the stack + */ +function doing_filter( $filter = null ) { + global $wp_current_filter; + + if ( null === $filter ) { + return ! empty( $wp_current_filter ); + } + + return in_array( $filter, $wp_current_filter ); +} + +/** + * Retrieve the name of an action currently being processed. + * + * @since 3.9.0 + * @uses doing_filter() + * + * @param string $action Optional. Action to check. Defaults to null, which checks if any action is currently being run. + * + * @return bool Whether the action is currently in the stack. + */ +function doing_action( $action = null ) { + return doing_filter( $action ); +} + /** * Hooks a function on to a specific action. * diff --git a/tests/phpunit/tests/actions.php b/tests/phpunit/tests/actions.php index a11416843c..583c8cea15 100644 --- a/tests/phpunit/tests/actions.php +++ b/tests/phpunit/tests/actions.php @@ -256,4 +256,104 @@ class Tests_Actions extends WP_UnitTestCase { function action_self_removal() { remove_action( 'test_action_self_removal', array( $this, 'action_self_removal' ) ); } + + /** + * Make sure current_action() behaves as current_filter() + * + * @ticket 14994 + */ + function test_current_action() { + global $wp_current_filter; + $wp_current_filter[] = 'first'; + $wp_current_filter[] = 'second'; // Let's say a second action was invoked. + + $this->assertEquals( 'second', current_action() ); + } + + /** + * @ticket 14994 + */ + function test_doing_filter() { + global $wp_current_filter; + $wp_current_filter = array(); // Set to an empty array first + + $this->assertFalse( doing_filter() ); // No filter is passed in, and no filter is being processed + $this->assertFalse( doing_filter( 'testing' ) ); // Filter is passed in but not being processed + + $wp_current_filter[] = 'testing'; + + $this->assertTrue( doing_filter() ); // No action is passed in, and a filter is being processed + $this->assertTrue( doing_filter( 'testing') ); // Filter is passed in and is being processed + $this->assertFalse( doing_filter( 'something_else' ) ); // Filter is passed in but not being processed + + $wp_current_filter = array(); + } + + /** + * @ticket 14994 + */ + function test_doing_action() { + global $wp_current_filter; + $wp_current_filter = array(); // Set to an empty array first + + $this->assertFalse( doing_action() ); // No action is passed in, and no filter is being processed + $this->assertFalse( doing_action( 'testing' ) ); // Action is passed in but not being processed + + $wp_current_filter[] = 'testing'; + + $this->assertTrue( doing_action() ); // No action is passed in, and a filter is being processed + $this->assertTrue( doing_action( 'testing') ); // Action is passed in and is being processed + $this->assertFalse( doing_action( 'something_else' ) ); // Action is passed in but not being processed + + $wp_current_filter = array(); + } + + /** + * @ticket 14994 + */ + function test_doing_filter_real() { + $this->assertFalse( doing_filter() ); // No filter is passed in, and no filter is being processed + $this->assertFalse( doing_filter( 'testing' ) ); // Filter is passed in but not being processed + + add_filter( 'testing', array( $this, 'apply_testing_filter' ) ); + $this->assertTrue( has_action( 'testing' ) ); + $this->assertEquals( 10, has_action( 'testing', array( $this, 'apply_testing_filter' ) ) ); + + apply_filters( 'testing', '' ); + + // Make sure it ran. + $this->assertTrue( $this->apply_testing_filter ); + + $this->assertFalse( doing_filter() ); // No longer doing any filters + $this->assertFalse( doing_filter( 'testing' ) ); // No longer doing this filter + } + + function apply_testing_filter() { + $this->apply_testing_filter = true; + + $this->assertTrue( doing_filter() ); + $this->assertTrue( doing_filter( 'testing' ) ); + $this->assertFalse( doing_filter( 'something_else' ) ); + $this->assertFalse( doing_filter( 'testing_nested' ) ); + + add_filter( 'testing_nested', array( $this, 'apply_testing_nested_filter' ) ); + $this->assertTrue( has_action( 'testing_nested' ) ); + $this->assertEquals( 10, has_action( 'testing_nested', array( $this, 'apply_testing_nested_filter' ) ) ); + + apply_filters( 'testing_nested', '' ); + + // Make sure it ran. + $this->assertTrue( $this->apply_testing_nested_filter ); + + $this->assertFalse( doing_filter( 'testing_nested' ) ); + $this->assertFalse( doing_filter( 'testing_nested' ) ); + } + + function apply_testing_nested_filter() { + $this->apply_testing_nested_filter = true; + $this->assertTrue( doing_filter() ); + $this->assertTrue( doing_filter( 'testing' ) ); + $this->assertTrue( doing_filter( 'testing_nested' ) ); + $this->assertFalse( doing_filter( 'something_else' ) ); + } }