apply_testing_filter = false; $this->apply_testing_nested_filter = false; parent::tear_down(); } /** * @covers ::do_action */ public function test_simple_action() { $a = new MockAction(); $hook_name = __FUNCTION__; add_action( $hook_name, array( &$a, 'action' ) ); do_action( $hook_name ); // Only one event occurred for the hook, with empty args. $this->assertSame( 1, $a->get_call_count() ); // Only our hook was called. $this->assertSame( array( $hook_name ), $a->get_hook_names() ); $argsvar = $a->get_args(); $args = array_pop( $argsvar ); $this->assertSame( array( '' ), $args ); } /** * @covers ::remove_action */ public function test_remove_action() { $a = new MockAction(); $hook_name = __FUNCTION__; add_action( $hook_name, array( &$a, 'action' ) ); do_action( $hook_name ); // Make sure our hook was called correctly. $this->assertSame( 1, $a->get_call_count() ); $this->assertSame( array( $hook_name ), $a->get_hook_names() ); // Now remove the action, do it again, and make sure it's not called this time. remove_action( $hook_name, array( &$a, 'action' ) ); do_action( $hook_name ); $this->assertSame( 1, $a->get_call_count() ); $this->assertSame( array( $hook_name ), $a->get_hook_names() ); } /** * @covers ::has_action */ public function test_has_action() { $hook_name = __FUNCTION__; $callback = __FUNCTION__ . '_func'; $this->assertFalse( has_action( $hook_name, $callback ) ); $this->assertFalse( has_action( $hook_name ) ); add_action( $hook_name, $callback ); $this->assertSame( 10, has_action( $hook_name, $callback ) ); $this->assertTrue( has_action( $hook_name ) ); remove_action( $hook_name, $callback ); $this->assertFalse( has_action( $hook_name, $callback ) ); $this->assertFalse( has_action( $hook_name ) ); } /** * One tag with multiple actions. * * @covers ::do_action */ public function test_multiple_actions() { $a1 = new MockAction(); $a2 = new MockAction(); $hook_name = __FUNCTION__; // Add both actions to the hook. add_action( $hook_name, array( &$a1, 'action' ) ); add_action( $hook_name, array( &$a2, 'action' ) ); do_action( $hook_name ); // Both actions called once each. $this->assertSame( 1, $a1->get_call_count() ); $this->assertSame( 1, $a2->get_call_count() ); } /** * One tag with multiple actions. * * @covers ::do_action */ public function test_action_args_1() { $a = new MockAction(); $hook_name = __FUNCTION__; $val = __FUNCTION__ . '_val'; add_action( $hook_name, array( &$a, 'action' ) ); // Call the action with a single argument. do_action( $hook_name, $val ); $call_count = $a->get_call_count(); $this->assertSame( 1, $call_count ); $argsvar = $a->get_args(); $this->assertSame( array( $val ), array_pop( $argsvar ) ); } /** * One tag with multiple actions. * * @covers ::do_action */ public function test_action_args_2() { $a1 = new MockAction(); $a2 = new MockAction(); $hook_name = __FUNCTION__; $val1 = __FUNCTION__ . '_val1'; $val2 = __FUNCTION__ . '_val2'; // $a1 accepts two arguments, $a2 doesn't. add_action( $hook_name, array( &$a1, 'action' ), 10, 2 ); add_action( $hook_name, array( &$a2, 'action' ) ); // Call the action with two arguments. do_action( $hook_name, $val1, $val2 ); $call_count = $a1->get_call_count(); // $a1 should be called with both args. $this->assertSame( 1, $call_count ); $argsvar1 = $a1->get_args(); $this->assertSame( array( $val1, $val2 ), array_pop( $argsvar1 ) ); // $a2 should be called with one only. $this->assertSame( 1, $a2->get_call_count() ); $argsvar2 = $a2->get_args(); $this->assertSame( array( $val1 ), array_pop( $argsvar2 ) ); } /** * Test that multiple callbacks receive the correct number of args even when the number * is less than, or greater than previous hooks. * * @see https://core.trac.wordpress.org/ticket/17817#comment:72 * @ticket 17817 * * @covers ::do_action */ public function test_action_args_3() { $a1 = new MockAction(); $a2 = new MockAction(); $a3 = new MockAction(); $hook_name = __FUNCTION__; $val1 = __FUNCTION__ . '_val1'; $val2 = __FUNCTION__ . '_val2'; // $a1 accepts two arguments, $a2 doesn't, $a3 accepts two arguments. add_action( $hook_name, array( &$a1, 'action' ), 10, 2 ); add_action( $hook_name, array( &$a2, 'action' ) ); add_action( $hook_name, array( &$a3, 'action' ), 10, 2 ); // Call the action with two arguments. do_action( $hook_name, $val1, $val2 ); $call_count = $a1->get_call_count(); // $a1 should be called with both args. $this->assertSame( 1, $call_count ); $argsvar1 = $a1->get_args(); $this->assertSame( array( $val1, $val2 ), array_pop( $argsvar1 ) ); // $a2 should be called with one only. $this->assertSame( 1, $a2->get_call_count() ); $argsvar2 = $a2->get_args(); $this->assertSame( array( $val1 ), array_pop( $argsvar2 ) ); // $a3 should be called with both args. $this->assertSame( 1, $a3->get_call_count() ); $argsvar3 = $a3->get_args(); $this->assertSame( array( $val1, $val2 ), array_pop( $argsvar3 ) ); } /** * Tests PHP 4 notation for calling actions while passing in an object by reference. * * @ticket 48312 * * @covers ::do_action */ public function test_action_args_with_php4_syntax() { $a = new MockAction(); $hook_name = __FUNCTION__; $val = new stdClass(); add_action( $hook_name, array( &$a, 'action' ) ); // Call the action with PHP 4 notation for passing object by reference. do_action( $hook_name, array( &$val ) ); $call_count = $a->get_call_count(); $argsvar = $a->get_args(); $this->assertSame( array( $val ), array_pop( $argsvar ) ); } public function test_action_priority() { $a = new MockAction(); $hook_name = __FUNCTION__; add_action( $hook_name, array( &$a, 'action' ), 10 ); add_action( $hook_name, array( &$a, 'action2' ), 9 ); do_action( $hook_name ); // Two events, one per action. $this->assertSame( 2, $a->get_call_count() ); $expected = array( // 'action2' is called first because it has priority 9. array( 'action' => 'action2', 'hook_name' => $hook_name, 'tag' => $hook_name, // Back compat. 'args' => array( '' ), ), // 'action' is called second. array( 'action' => 'action', 'hook_name' => $hook_name, 'tag' => $hook_name, // Back compat. 'args' => array( '' ), ), ); $this->assertSame( $expected, $a->get_events() ); } /** * @covers ::did_action */ public function test_did_action() { $hook_name1 = 'action1'; $hook_name2 = 'action2'; // Do action $hook_name1 but not $hook_name2. do_action( $hook_name1 ); $this->assertSame( 1, did_action( $hook_name1 ) ); $this->assertSame( 0, did_action( $hook_name2 ) ); // Do action $hook_name2 10 times. $count = 10; for ( $i = 0; $i < $count; $i++ ) { do_action( $hook_name2 ); } // $hook_name1's count hasn't changed, $hook_name2 should be correct. $this->assertSame( 1, did_action( $hook_name1 ) ); $this->assertSame( $count, did_action( $hook_name2 ) ); } /** * @covers ::do_action */ public function test_all_action() { $a = new MockAction(); $hook_name1 = __FUNCTION__ . '_1'; $hook_name2 = __FUNCTION__ . '_2'; // Add an 'all' action. add_action( 'all', array( &$a, 'action' ) ); $this->assertSame( 10, has_filter( 'all', array( &$a, 'action' ) ) ); // Do some actions. do_action( $hook_name1 ); do_action( $hook_name2 ); do_action( $hook_name1 ); do_action( $hook_name1 ); // Our action should have been called once for each tag. $this->assertSame( 4, $a->get_call_count() ); // Only our hook was called. $this->assertSame( array( $hook_name1, $hook_name2, $hook_name1, $hook_name1 ), $a->get_hook_names() ); remove_action( 'all', array( &$a, 'action' ) ); $this->assertFalse( has_filter( 'all', array( &$a, 'action' ) ) ); } /** * @covers ::remove_action */ public function test_remove_all_action() { $a = new MockAction(); $hook_name = __FUNCTION__; add_action( 'all', array( &$a, 'action' ) ); $this->assertSame( 10, has_filter( 'all', array( &$a, 'action' ) ) ); do_action( $hook_name ); // Make sure our hook was called correctly. $this->assertSame( 1, $a->get_call_count() ); $this->assertSame( array( $hook_name ), $a->get_hook_names() ); // Now remove the action, do it again, and make sure it's not called this time. remove_action( 'all', array( &$a, 'action' ) ); $this->assertFalse( has_filter( 'all', array( &$a, 'action' ) ) ); do_action( $hook_name ); $this->assertSame( 1, $a->get_call_count() ); $this->assertSame( array( $hook_name ), $a->get_hook_names() ); } /** * @covers ::do_action_ref_array */ public function test_action_ref_array() { $obj = new stdClass(); $a = new MockAction(); $hook_name = __FUNCTION__; add_action( $hook_name, array( &$a, 'action' ) ); do_action_ref_array( $hook_name, array( &$obj ) ); $args = $a->get_args(); $this->assertSame( $args[0][0], $obj ); // Just in case we don't trust assertSame(). $obj->foo = true; $this->assertNotEmpty( $args[0][0]->foo ); } /** * @ticket 11241 * * @covers ::do_action */ public function test_action_keyed_array() { $a = new MockAction(); $hook_name = __FUNCTION__; add_action( $hook_name, array( &$a, 'action' ) ); $context = array( 'key1' => 'val1' ); do_action( $hook_name, $context ); $args = $a->get_args(); $this->assertSame( $args[0][0], $context ); $context2 = array( 'key2' => 'val2', 'key3' => 'val3', ); do_action( $hook_name, $context2 ); $args = $a->get_args(); $this->assertSame( $args[1][0], $context2 ); } /** * @ticket 10493 * * @covers ::add_action * @covers ::has_action * @covers ::do_action */ public function test_action_closure() { $hook_name = __FUNCTION__; $closure = static function( $a, $b ) { $GLOBALS[ $a ] = $b; }; add_action( $hook_name, $closure, 10, 2 ); $this->assertSame( 10, has_action( $hook_name, $closure ) ); $context = array( 'val1', 'val2' ); do_action( $hook_name, $context[0], $context[1] ); $this->assertSame( $GLOBALS[ $context[0] ], $context[1] ); $hook_name2 = __FUNCTION__ . '_2'; $closure2 = static function() { $GLOBALS['closure_no_args'] = true; }; add_action( $hook_name2, $closure2 ); $this->assertSame( 10, has_action( $hook_name2, $closure2 ) ); do_action( $hook_name2 ); $this->assertTrue( $GLOBALS['closure_no_args'] ); remove_action( $hook_name, $closure ); remove_action( $hook_name2, $closure2 ); } /** * @ticket 23265 * * @covers ::add_action */ public function test_action_callback_representations() { $hook_name = __FUNCTION__; $this->assertFalse( has_action( $hook_name ) ); add_action( $hook_name, array( 'Class', 'method' ) ); $this->assertSame( 10, has_action( $hook_name, array( 'Class', 'method' ) ) ); $this->assertSame( 10, has_action( $hook_name, 'Class::method' ) ); } /** * @covers ::remove_action */ public function test_action_self_removal() { add_action( 'test_action_self_removal', array( $this, 'action_self_removal' ) ); do_action( 'test_action_self_removal' ); $this->assertSame( 1, did_action( 'test_action_self_removal' ) ); } public function action_self_removal() { remove_action( 'test_action_self_removal', array( $this, 'action_self_removal' ) ); } /** * @ticket 17817 * * @covers ::do_action */ public function test_action_recursion() { $hook_name = __FUNCTION__; $a = new MockAction(); $b = new MockAction(); add_action( $hook_name, array( $a, 'action' ), 11, 1 ); add_action( $hook_name, array( $b, 'action' ), 13, 1 ); add_action( $hook_name, array( $this, 'action_that_causes_recursion' ), 12, 1 ); do_action( $hook_name, $hook_name ); $this->assertSame( 2, $a->get_call_count(), 'recursive actions should call all callbacks with earlier priority' ); $this->assertSame( 2, $b->get_call_count(), 'recursive actions should call callbacks with later priority' ); } /** * @covers ::do_action */ public function action_that_causes_recursion( $hook_name ) { static $recursing = false; if ( ! $recursing ) { $recursing = true; do_action( $hook_name, $hook_name ); } $recursing = false; } /** * @ticket 9968 * @ticket 17817 * * @covers ::remove_action * @covers ::add_action */ public function test_action_callback_manipulation_while_running() { $hook_name = __FUNCTION__; $a = new MockAction(); $b = new MockAction(); $c = new MockAction(); $d = new MockAction(); $e = new MockAction(); add_action( $hook_name, array( $a, 'action' ), 11, 2 ); add_action( $hook_name, array( $this, 'action_that_manipulates_a_running_hook' ), 12, 2 ); add_action( $hook_name, array( $b, 'action' ), 12, 2 ); do_action( $hook_name, $hook_name, array( $a, $b, $c, $d, $e ) ); do_action( $hook_name, $hook_name, array( $a, $b, $c, $d, $e ) ); $this->assertSame( 2, $a->get_call_count(), 'callbacks should run unless otherwise instructed' ); $this->assertSame( 1, $b->get_call_count(), 'callback removed by same priority callback should still get called' ); $this->assertSame( 1, $c->get_call_count(), 'callback added by same priority callback should not get called' ); $this->assertSame( 2, $d->get_call_count(), 'callback added by earlier priority callback should get called' ); $this->assertSame( 1, $e->get_call_count(), 'callback added by later priority callback should not get called' ); } public function action_that_manipulates_a_running_hook( $hook_name, $mocks ) { remove_action( $hook_name, array( $mocks[1], 'action' ), 12, 2 ); add_action( $hook_name, array( $mocks[2], 'action' ), 12, 2 ); add_action( $hook_name, array( $mocks[3], 'action' ), 13, 2 ); add_action( $hook_name, array( $mocks[4], 'action' ), 10, 2 ); } /** * @ticket 17817 * * This specificaly addresses the concern raised at * https://core.trac.wordpress.org/ticket/17817#comment:52 * * @covers ::remove_filter */ public function test_remove_anonymous_callback() { $hook_name = __FUNCTION__; $a = new MockAction(); add_action( $hook_name, array( $a, 'action' ), 12, 1 ); $this->assertTrue( has_action( $hook_name ) ); $hook = $GLOBALS['wp_filter'][ $hook_name ]; // From http://wordpress.stackexchange.com/a/57088/6445 foreach ( $hook as $priority => $filter ) { foreach ( $filter as $identifier => $function ) { if ( is_array( $function ) && is_a( $function['function'][0], 'MockAction' ) && 'action' === $function['function'][1] ) { remove_filter( $hook_name, array( $function['function'][0], 'action' ), $priority ); } } } $this->assertFalse( has_action( $hook_name ) ); } /** * Test the ArrayAccess methods of WP_Hook * * @ticket 17817 * * @covers WP_Hook::offsetGet * @covers WP_Hook::offsetSet * @covers WP_Hook::offsetUnset */ public function test_array_access_of_wp_filter_global() { global $wp_filter; $hook_name = __FUNCTION__; add_action( $hook_name, '__return_null', 11, 1 ); $this->assertArrayHasKey( 11, $wp_filter[ $hook_name ] ); $this->assertArrayHasKey( '__return_null', $wp_filter[ $hook_name ][11] ); unset( $wp_filter[ $hook_name ][11] ); $this->assertFalse( has_action( $hook_name, '__return_null' ) ); $wp_filter[ $hook_name ][11] = array( '__return_null' => array( 'function' => '__return_null', 'accepted_args' => 1, ), ); $this->assertSame( 11, has_action( $hook_name, '__return_null' ) ); } /** * Make sure current_action() behaves as current_filter() * * @ticket 14994 * * @covers ::current_action */ public 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->assertSame( 'second', current_action() ); } /** * @ticket 14994 * * @covers ::doing_filter */ public 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 * * @covers ::doing_filter */ public 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 * * @covers ::doing_filter */ public 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->assertSame( 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. } public 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->assertSame( 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' ) ); } public 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' ) ); } /** * @ticket 10441 * @expectedDeprecated tests_do_action_deprecated * * @covers ::do_action_deprecated */ public function test_do_action_deprecated() { $p = new WP_Post( (object) array( 'post_title' => 'Foo' ) ); add_action( 'tests_do_action_deprecated', array( __CLASS__, 'deprecated_action_callback' ) ); do_action_deprecated( 'tests_do_action_deprecated', array( $p ), '4.6.0' ); remove_action( 'tests_do_action_deprecated', array( __CLASS__, 'deprecated_action_callback' ) ); $this->assertSame( 'Bar', $p->post_title ); } public static function deprecated_action_callback( $p ) { $p->post_title = 'Bar'; } /** * @ticket 10441 * @expectedDeprecated tests_do_action_deprecated * * @covers ::do_action_deprecated */ public function test_do_action_deprecated_with_multiple_params() { $p1 = new WP_Post( (object) array( 'post_title' => 'Foo1' ) ); $p2 = new WP_Post( (object) array( 'post_title' => 'Foo2' ) ); add_action( 'tests_do_action_deprecated', array( __CLASS__, 'deprecated_action_callback_multiple_params' ), 10, 2 ); do_action_deprecated( 'tests_do_action_deprecated', array( $p1, $p2 ), '4.6.0' ); remove_action( 'tests_do_action_deprecated', array( __CLASS__, 'deprecated_action_callback_multiple_params' ), 10, 2 ); $this->assertSame( 'Bar1', $p1->post_title ); $this->assertSame( 'Bar2', $p2->post_title ); } public static function deprecated_action_callback_multiple_params( $p1, $p2 ) { $p1->post_title = 'Bar1'; $p2->post_title = 'Bar2'; } }