Skip to content

Commit f63e409

Browse files
committed
Disable pings/trackbacks in non-production environments
1 parent c37a60a commit f63e409

File tree

4 files changed

+356
-0
lines changed

4 files changed

+356
-0
lines changed

src/wp-includes/comment.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3163,6 +3163,89 @@ function generic_ping( $post_id = 0 ) {
31633163
return $post_id;
31643164
}
31653165

3166+
/**
3167+
* Determines whether pings should be disabled for the current environment.
3168+
*
3169+
* By default, all pings (outgoing pingbacks, trackbacks, and ping service
3170+
* notifications, as well as incoming pingbacks and trackbacks) are disabled
3171+
* for non-production environments ('local', 'development', 'staging').
3172+
*
3173+
* @since 6.9.0
3174+
*
3175+
* @return bool True if pings should be disabled, false otherwise.
3176+
*/
3177+
function wp_should_disable_pings_for_environment() {
3178+
$environment_type = wp_get_environment_type();
3179+
$should_disable = 'production' !== $environment_type;
3180+
3181+
/**
3182+
* Filters whether pings should be disabled for the current environment.
3183+
*
3184+
* Returning false re-enables pings in non-production environments.
3185+
* Returning true disables pings even in production.
3186+
*
3187+
* @since 6.9.0
3188+
*
3189+
* @param bool $should_disable Whether pings should be disabled. Default true
3190+
* for non-production environments, false for production.
3191+
* @param string $environment_type The current environment type as returned by
3192+
* wp_get_environment_type().
3193+
*/
3194+
return apply_filters( 'wp_should_disable_pings_for_environment', $should_disable, $environment_type );
3195+
}
3196+
3197+
/**
3198+
* Removes outgoing ping callbacks in non-production environments.
3199+
*
3200+
* Hooked to `do_all_pings` at priority 1 so it runs before the default
3201+
* priority 10 callbacks. Does not remove `do_all_enclosures`.
3202+
*
3203+
* @since 6.9.0
3204+
* @access private
3205+
*/
3206+
function wp_disable_outgoing_pings_for_environment() {
3207+
if ( wp_should_disable_pings_for_environment() ) {
3208+
remove_action( 'do_all_pings', 'do_all_pingbacks', 10 );
3209+
remove_action( 'do_all_pings', 'do_all_trackbacks', 10 );
3210+
remove_action( 'do_all_pings', 'generic_ping', 10 );
3211+
}
3212+
}
3213+
3214+
/**
3215+
* Closes pings for posts in non-production environments.
3216+
*
3217+
* @since 6.9.0
3218+
* @access private
3219+
*
3220+
* @param bool $pings_open Whether the post is open for pings.
3221+
* @param int $post_id The post ID.
3222+
* @return bool False if pings are disabled for the environment, otherwise the original value.
3223+
*/
3224+
function wp_disable_pings_open_for_environment( $pings_open, $post_id ) {
3225+
if ( wp_should_disable_pings_for_environment() ) {
3226+
return false;
3227+
}
3228+
3229+
return $pings_open;
3230+
}
3231+
3232+
/**
3233+
* Removes the pingback XML-RPC method in non-production environments.
3234+
*
3235+
* @since 6.9.0
3236+
* @access private
3237+
*
3238+
* @param array $methods Associative array of XML-RPC methods.
3239+
* @return array Modified associative array of XML-RPC methods.
3240+
*/
3241+
function wp_disable_xmlrpc_pingback_for_environment( $methods ) {
3242+
if ( wp_should_disable_pings_for_environment() ) {
3243+
unset( $methods['pingback.ping'] );
3244+
}
3245+
3246+
return $methods;
3247+
}
3248+
31663249
/**
31673250
* Pings back the links found in a post.
31683251
*

src/wp-includes/default-filters.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,12 @@
421421
add_action( 'do_all_pings', 'do_all_enclosures', 10, 0 );
422422
add_action( 'do_all_pings', 'do_all_trackbacks', 10, 0 );
423423
add_action( 'do_all_pings', 'generic_ping', 10, 0 );
424+
425+
// Disable pings (pingbacks, trackbacks, and ping service notifications) in non-production environments.
426+
add_action( 'do_all_pings', 'wp_disable_outgoing_pings_for_environment', 1, 0 );
427+
add_filter( 'pings_open', 'wp_disable_pings_open_for_environment', 10, 2 );
428+
add_filter( 'xmlrpc_methods', 'wp_disable_xmlrpc_pingback_for_environment' );
429+
424430
add_action( 'do_robots', 'do_robots' );
425431
add_action( 'do_favicon', 'do_favicon' );
426432
add_action( 'wp_before_include_template', 'wp_start_template_enhancement_output_buffer', 1000 ); // Late priority to let `wp_template_enhancement_output_buffer` filters and `wp_finalized_template_enhancement_output_buffer` actions be registered.
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
<?php
2+
3+
/**
4+
* Tests for disabling pings in non-production environments.
5+
*
6+
* @group comment
7+
* @covers ::wp_should_disable_pings_for_environment
8+
* @covers ::wp_disable_outgoing_pings_for_environment
9+
* @covers ::wp_disable_pings_open_for_environment
10+
* @covers ::wp_disable_xmlrpc_pingback_for_environment
11+
*
12+
* @ticket 64837
13+
*/
14+
class Tests_Comment_DisablePingsForEnvironment extends WP_UnitTestCase {
15+
16+
/**
17+
* Stores the original WP_ENVIRONMENT_TYPE env value for cleanup.
18+
*
19+
* @var string|false
20+
*/
21+
private $original_env;
22+
23+
public function set_up() {
24+
parent::set_up();
25+
$this->original_env = getenv( 'WP_ENVIRONMENT_TYPE' );
26+
}
27+
28+
public function tear_down() {
29+
if ( false === $this->original_env ) {
30+
putenv( 'WP_ENVIRONMENT_TYPE' );
31+
} else {
32+
putenv( 'WP_ENVIRONMENT_TYPE=' . $this->original_env );
33+
}
34+
parent::tear_down();
35+
}
36+
37+
/**
38+
* @ticket 64837
39+
*/
40+
public function test_should_disable_returns_true_for_local() {
41+
putenv( 'WP_ENVIRONMENT_TYPE=local' );
42+
$this->assertTrue( wp_should_disable_pings_for_environment() );
43+
}
44+
45+
/**
46+
* @ticket 64837
47+
*/
48+
public function test_should_disable_returns_true_for_development() {
49+
putenv( 'WP_ENVIRONMENT_TYPE=development' );
50+
$this->assertTrue( wp_should_disable_pings_for_environment() );
51+
}
52+
53+
/**
54+
* @ticket 64837
55+
*/
56+
public function test_should_disable_returns_true_for_staging() {
57+
putenv( 'WP_ENVIRONMENT_TYPE=staging' );
58+
$this->assertTrue( wp_should_disable_pings_for_environment() );
59+
}
60+
61+
/**
62+
* @ticket 64837
63+
*/
64+
public function test_should_disable_returns_false_for_production() {
65+
putenv( 'WP_ENVIRONMENT_TYPE=production' );
66+
$this->assertFalse( wp_should_disable_pings_for_environment() );
67+
}
68+
69+
/**
70+
* @ticket 64837
71+
*/
72+
public function test_filter_can_enable_pings_in_non_production() {
73+
putenv( 'WP_ENVIRONMENT_TYPE=local' );
74+
add_filter( 'wp_should_disable_pings_for_environment', '__return_false' );
75+
76+
$this->assertFalse( wp_should_disable_pings_for_environment() );
77+
}
78+
79+
/**
80+
* @ticket 64837
81+
*/
82+
public function test_filter_can_disable_pings_in_production() {
83+
putenv( 'WP_ENVIRONMENT_TYPE=production' );
84+
add_filter( 'wp_should_disable_pings_for_environment', '__return_true' );
85+
86+
$this->assertTrue( wp_should_disable_pings_for_environment() );
87+
}
88+
89+
/**
90+
* @ticket 64837
91+
*/
92+
public function test_filter_receives_environment_type() {
93+
putenv( 'WP_ENVIRONMENT_TYPE=staging' );
94+
95+
$received_type = null;
96+
add_filter(
97+
'wp_should_disable_pings_for_environment',
98+
function ( $should_disable, $environment_type ) use ( &$received_type ) {
99+
$received_type = $environment_type;
100+
return $should_disable;
101+
},
102+
10,
103+
2
104+
);
105+
106+
wp_should_disable_pings_for_environment();
107+
108+
$this->assertSame( 'staging', $received_type );
109+
}
110+
111+
/**
112+
* @ticket 64837
113+
*/
114+
public function test_outgoing_pingbacks_removed_in_non_production() {
115+
putenv( 'WP_ENVIRONMENT_TYPE=development' );
116+
117+
// Re-register the defaults to ensure a clean state.
118+
add_action( 'do_all_pings', 'do_all_pingbacks', 10, 0 );
119+
120+
// Fire the priority-1 callback.
121+
wp_disable_outgoing_pings_for_environment();
122+
123+
$this->assertFalse( has_action( 'do_all_pings', 'do_all_pingbacks' ) );
124+
}
125+
126+
/**
127+
* @ticket 64837
128+
*/
129+
public function test_outgoing_trackbacks_removed_in_non_production() {
130+
putenv( 'WP_ENVIRONMENT_TYPE=development' );
131+
132+
add_action( 'do_all_pings', 'do_all_trackbacks', 10, 0 );
133+
134+
wp_disable_outgoing_pings_for_environment();
135+
136+
$this->assertFalse( has_action( 'do_all_pings', 'do_all_trackbacks' ) );
137+
}
138+
139+
/**
140+
* @ticket 64837
141+
*/
142+
public function test_outgoing_generic_ping_removed_in_non_production() {
143+
putenv( 'WP_ENVIRONMENT_TYPE=development' );
144+
145+
add_action( 'do_all_pings', 'generic_ping', 10, 0 );
146+
147+
wp_disable_outgoing_pings_for_environment();
148+
149+
$this->assertFalse( has_action( 'do_all_pings', 'generic_ping' ) );
150+
}
151+
152+
/**
153+
* @ticket 64837
154+
*/
155+
public function test_enclosures_not_removed_in_non_production() {
156+
putenv( 'WP_ENVIRONMENT_TYPE=development' );
157+
158+
add_action( 'do_all_pings', 'do_all_enclosures', 10, 0 );
159+
160+
wp_disable_outgoing_pings_for_environment();
161+
162+
$this->assertSame( 10, has_action( 'do_all_pings', 'do_all_enclosures' ) );
163+
}
164+
165+
/**
166+
* @ticket 64837
167+
*/
168+
public function test_outgoing_pings_preserved_in_production() {
169+
putenv( 'WP_ENVIRONMENT_TYPE=production' );
170+
171+
add_action( 'do_all_pings', 'do_all_pingbacks', 10, 0 );
172+
add_action( 'do_all_pings', 'do_all_trackbacks', 10, 0 );
173+
add_action( 'do_all_pings', 'generic_ping', 10, 0 );
174+
175+
wp_disable_outgoing_pings_for_environment();
176+
177+
$this->assertSame( 10, has_action( 'do_all_pings', 'do_all_pingbacks' ) );
178+
$this->assertSame( 10, has_action( 'do_all_pings', 'do_all_trackbacks' ) );
179+
$this->assertSame( 10, has_action( 'do_all_pings', 'generic_ping' ) );
180+
}
181+
182+
/**
183+
* @ticket 64837
184+
*/
185+
public function test_pings_open_returns_false_in_non_production() {
186+
putenv( 'WP_ENVIRONMENT_TYPE=local' );
187+
188+
$post = self::factory()->post->create_and_get(
189+
array( 'ping_status' => 'open' )
190+
);
191+
192+
$this->assertFalse( wp_disable_pings_open_for_environment( true, $post->ID ) );
193+
}
194+
195+
/**
196+
* @ticket 64837
197+
*/
198+
public function test_pings_open_unchanged_in_production() {
199+
putenv( 'WP_ENVIRONMENT_TYPE=production' );
200+
201+
$post = self::factory()->post->create_and_get(
202+
array( 'ping_status' => 'open' )
203+
);
204+
205+
$this->assertTrue( wp_disable_pings_open_for_environment( true, $post->ID ) );
206+
}
207+
208+
/**
209+
* @ticket 64837
210+
*/
211+
public function test_xmlrpc_pingback_removed_in_non_production() {
212+
putenv( 'WP_ENVIRONMENT_TYPE=development' );
213+
214+
$methods = array(
215+
'pingback.ping' => 'this:pingback_ping',
216+
'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
217+
'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',
218+
);
219+
220+
$filtered = wp_disable_xmlrpc_pingback_for_environment( $methods );
221+
222+
$this->assertArrayNotHasKey( 'pingback.ping', $filtered );
223+
}
224+
225+
/**
226+
* @ticket 64837
227+
*/
228+
public function test_xmlrpc_pingback_preserved_in_production() {
229+
putenv( 'WP_ENVIRONMENT_TYPE=production' );
230+
231+
$methods = array(
232+
'pingback.ping' => 'this:pingback_ping',
233+
'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',
234+
);
235+
236+
$filtered = wp_disable_xmlrpc_pingback_for_environment( $methods );
237+
238+
$this->assertArrayHasKey( 'pingback.ping', $filtered );
239+
}
240+
241+
/**
242+
* @ticket 64837
243+
*/
244+
public function test_xmlrpc_other_methods_preserved_in_non_production() {
245+
putenv( 'WP_ENVIRONMENT_TYPE=development' );
246+
247+
$methods = array(
248+
'pingback.ping' => 'this:pingback_ping',
249+
'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
250+
'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',
251+
'wp.getPost' => 'this:wp_getPost',
252+
);
253+
254+
$filtered = wp_disable_xmlrpc_pingback_for_environment( $methods );
255+
256+
$this->assertArrayHasKey( 'pingback.extensions.getPingbacks', $filtered );
257+
$this->assertArrayHasKey( 'wp.getUsersBlogs', $filtered );
258+
$this->assertArrayHasKey( 'wp.getPost', $filtered );
259+
}
260+
}

tests/phpunit/tests/comment/pingsOpen.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
*/
77
class Tests_Comment_PingsOpen extends WP_UnitTestCase {
88

9+
public function set_up() {
10+
parent::set_up();
11+
12+
// Remove the environment-based filter to test core pings_open() behavior in isolation.
13+
remove_filter( 'pings_open', 'wp_disable_pings_open_for_environment' );
14+
}
15+
916
/**
1017
* @ticket 54159
1118
*/

0 commit comments

Comments
 (0)