Skip to content

Commit e8a06e8

Browse files
authored
Merge pull request #1047 from WordPress/enhance/server-timing-dropin-update-support
Enhance `object-cache.php` drop-in placement logic to support updating to a newer version of the file
2 parents b928061 + 598a35f commit e8a06e8

2 files changed

Lines changed: 115 additions & 10 deletions

File tree

‎load.php‎

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
if ( ! defined( 'PERFLAB_OBJECT_CACHE_DROPIN_VERSION' ) ) {
3030
define( 'PERFLAB_OBJECT_CACHE_DROPIN_VERSION', false );
3131
}
32+
define( 'PERFLAB_OBJECT_CACHE_DROPIN_LATEST_VERSION', 3 );
3233

3334
// Load server-timing API.
3435
require_once PERFLAB_PLUGIN_DIR_PATH . 'includes/server-timing/class-perflab-server-timing-metric.php';
@@ -371,11 +372,6 @@ function perflab_maybe_set_object_cache_dropin() {
371372
return;
372373
}
373374

374-
// Bail if already placed.
375-
if ( PERFLAB_OBJECT_CACHE_DROPIN_VERSION ) {
376-
return;
377-
}
378-
379375
/**
380376
* Filters whether the Perflab server timing drop-in should be set.
381377
*
@@ -387,6 +383,24 @@ function perflab_maybe_set_object_cache_dropin() {
387383
return;
388384
}
389385

386+
/**
387+
* Filters the value of the `object-cache.php` drop-in constant.
388+
*
389+
* This filter should not be used outside of tests.
390+
*
391+
* @since n.e.x.t
392+
* @internal
393+
*
394+
* @param int|bool $current_dropin_version The drop-in version as defined by the
395+
* `PERFLAB_OBJECT_CACHE_DROPIN_VERSION` constant.
396+
*/
397+
$current_dropin_version = apply_filters( 'perflab_object_cache_dropin_version', PERFLAB_OBJECT_CACHE_DROPIN_VERSION );
398+
399+
// Bail if already placed in the latest version or newer.
400+
if ( $current_dropin_version && $current_dropin_version >= PERFLAB_OBJECT_CACHE_DROPIN_LATEST_VERSION ) {
401+
return;
402+
}
403+
390404
// Bail if already attempted before timeout has been completed.
391405
// This is present in case placing the file fails for some reason, to avoid
392406
// excessively retrying to place it on every request.
@@ -398,8 +412,11 @@ function perflab_maybe_set_object_cache_dropin() {
398412
if ( $wp_filesystem || WP_Filesystem() ) {
399413
$dropin_path = WP_CONTENT_DIR . '/object-cache.php';
400414

401-
/**
402-
* If there is an actual object-cache.php file, do not replace it.
415+
/*
416+
* If there is an actual object-cache.php file, it is most likely from
417+
* a third party, or it may be an older version of the Performance Lab
418+
* object-cache.php. If it's from a third party, do not replace it.
419+
*
403420
* Previous versions of the Performance Lab plugin were renaming the
404421
* original object-cache.php file and then loading both. However, due
405422
* to other plugins eagerly checking file headers, this caused too many
@@ -408,9 +425,27 @@ function perflab_maybe_set_object_cache_dropin() {
408425
* safest solution.
409426
*/
410427
if ( $wp_filesystem->exists( $dropin_path ) ) {
411-
// Set timeout of 1 day before retrying again (only in case the file already exists).
412-
set_transient( 'perflab_set_object_cache_dropin', true, DAY_IN_SECONDS );
413-
return;
428+
// If this constant evaluates to `false`, the existing file is for sure from a third party.
429+
if ( ! $current_dropin_version ) {
430+
// Set timeout of 1 day before retrying again (only in case the file already exists).
431+
set_transient( 'perflab_set_object_cache_dropin', true, DAY_IN_SECONDS );
432+
return;
433+
}
434+
435+
// Otherwise, verify that it's actually the Performance Lab drop-in.
436+
$test_content = "<?php\n/**\n * Plugin Name: Performance Lab Server Timing Object Cache Drop-In\n";
437+
if ( ! str_starts_with( $wp_filesystem->get_contents( $dropin_path ), $test_content ) ) {
438+
// Set timeout of 1 day before retrying again (only in case the file already exists).
439+
set_transient( 'perflab_set_object_cache_dropin', true, DAY_IN_SECONDS );
440+
return;
441+
}
442+
443+
/*
444+
* If this logic is reached, the existing file is an older version
445+
* of the Performance Lab drop-in, so it can be safely deleted, and
446+
* then be replaced below.
447+
*/
448+
$wp_filesystem->delete( $dropin_path );
414449
}
415450

416451
$wp_filesystem->copy( PERFLAB_PLUGIN_DIR_PATH . 'includes/server-timing/object-cache.copy.php', $dropin_path );

‎tests/load-tests.php‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,62 @@ public function test_perflab_maybe_set_object_cache_dropin_with_conflict() {
214214
$this->assertSame( $dummy_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
215215
}
216216

217+
public function test_perflab_maybe_set_object_cache_dropin_with_older_version() {
218+
global $wp_filesystem;
219+
220+
$this->set_up_mock_filesystem();
221+
222+
$latest_file_content = file_get_contents( PERFLAB_PLUGIN_DIR_PATH . 'includes/server-timing/object-cache.copy.php' );
223+
$older_file_content = preg_replace( '/define\( \'PERFLAB_OBJECT_CACHE_DROPIN_VERSION\', (\d+) \)\;/', "define( 'PERFLAB_OBJECT_CACHE_DROPIN_VERSION', 1 );", $latest_file_content );
224+
$wp_filesystem->put_contents( WP_CONTENT_DIR . '/object-cache.php', $older_file_content );
225+
226+
// Simulate PL constant is set to the value from the older file.
227+
add_filter(
228+
'perflab_object_cache_dropin_version',
229+
static function () {
230+
return 1;
231+
}
232+
);
233+
234+
// Ensure older object-cache.php drop-in is present.
235+
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
236+
$this->assertSame( $older_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
237+
238+
// Run function to place drop-in and ensure it overrides the existing drop-in with the latest version.
239+
perflab_maybe_set_object_cache_dropin();
240+
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
241+
$this->assertSame( $latest_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
242+
}
243+
244+
public function test_perflab_maybe_set_object_cache_dropin_with_latest_version() {
245+
global $wp_filesystem;
246+
247+
$this->set_up_mock_filesystem();
248+
249+
$latest_file_content = file_get_contents( PERFLAB_PLUGIN_DIR_PATH . 'includes/server-timing/object-cache.copy.php' );
250+
$wp_filesystem->put_contents( WP_CONTENT_DIR . '/object-cache.php', $latest_file_content );
251+
252+
// Simulate PL constant is set to the value from the current file.
253+
$this->assertTrue( (bool) preg_match( '/define\( \'PERFLAB_OBJECT_CACHE_DROPIN_VERSION\', (\d+) \)\;/', $latest_file_content, $matches ) );
254+
$latest_version = (int) $matches[1];
255+
add_filter(
256+
'perflab_object_cache_dropin_version',
257+
static function () use ( $latest_version ) {
258+
return $latest_version;
259+
}
260+
);
261+
262+
// Ensure latest object-cache.php drop-in is present.
263+
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
264+
$this->assertSame( $latest_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
265+
266+
// Run function to place drop-in and ensure it doesn't attempt to replace the file.
267+
perflab_maybe_set_object_cache_dropin();
268+
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
269+
$this->assertSame( $latest_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
270+
$this->assertFalse( get_transient( 'perflab_set_object_cache_dropin' ) );
271+
}
272+
217273
public function test_perflab_object_cache_dropin_may_be_disabled_via_filter() {
218274
global $wp_filesystem;
219275

@@ -231,6 +287,20 @@ public function test_perflab_object_cache_dropin_may_be_disabled_via_filter() {
231287
$this->assertFalse( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
232288
}
233289

290+
public function test_perflab_object_cache_dropin_version_matches_latest() {
291+
$file_content = file_get_contents( PERFLAB_PLUGIN_DIR_PATH . 'includes/server-timing/object-cache.copy.php' );
292+
293+
// Get the version from the file header and the constant.
294+
$this->assertTrue( (bool) preg_match( '/^ \* Version: (\d+)$/m', $file_content, $matches ) );
295+
$file_header_version = (int) $matches[1];
296+
$this->assertTrue( (bool) preg_match( '/define\( \'PERFLAB_OBJECT_CACHE_DROPIN_VERSION\', (\d+) \)\;/', $file_content, $matches ) );
297+
$file_constant_version = (int) $matches[1];
298+
299+
// Assert the versions are in sync.
300+
$this->assertSame( PERFLAB_OBJECT_CACHE_DROPIN_LATEST_VERSION, $file_header_version );
301+
$this->assertSame( PERFLAB_OBJECT_CACHE_DROPIN_LATEST_VERSION, $file_constant_version );
302+
}
303+
234304
private function set_up_mock_filesystem() {
235305
global $wp_filesystem;
236306

0 commit comments

Comments
 (0)