Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 45 additions & 10 deletions load.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
if ( ! defined( 'PERFLAB_OBJECT_CACHE_DROPIN_VERSION' ) ) {
define( 'PERFLAB_OBJECT_CACHE_DROPIN_VERSION', false );
}
define( 'PERFLAB_OBJECT_CACHE_DROPIN_LATEST_VERSION', 3 );

// Load server-timing API.
require_once PERFLAB_PLUGIN_DIR_PATH . 'includes/server-timing/class-perflab-server-timing-metric.php';
Expand Down Expand Up @@ -371,11 +372,6 @@ function perflab_maybe_set_object_cache_dropin() {
return;
}

// Bail if already placed.
if ( PERFLAB_OBJECT_CACHE_DROPIN_VERSION ) {
return;
}

/**
* Filters whether the Perflab server timing drop-in should be set.
*
Expand All @@ -387,6 +383,24 @@ function perflab_maybe_set_object_cache_dropin() {
return;
}

/**
* Filters the value of the `object-cache.php` drop-in constant.
*
* This filter should not be used outside of tests.
*
* @since n.e.x.t
* @internal
*
* @param int|bool $current_dropin_version The drop-in version as defined by the
* `PERFLAB_OBJECT_CACHE_DROPIN_VERSION` constant.
*/
$current_dropin_version = apply_filters( 'perflab_object_cache_dropin_version', PERFLAB_OBJECT_CACHE_DROPIN_VERSION );

// Bail if already placed in the latest version or newer.
if ( $current_dropin_version && $current_dropin_version >= PERFLAB_OBJECT_CACHE_DROPIN_LATEST_VERSION ) {
return;
}

// Bail if already attempted before timeout has been completed.
// This is present in case placing the file fails for some reason, to avoid
// excessively retrying to place it on every request.
Expand All @@ -398,8 +412,11 @@ function perflab_maybe_set_object_cache_dropin() {
if ( $wp_filesystem || WP_Filesystem() ) {
$dropin_path = WP_CONTENT_DIR . '/object-cache.php';

/**
* If there is an actual object-cache.php file, do not replace it.
/*
* If there is an actual object-cache.php file, it is most likely from
* a third party, or it may be an older version of the Performance Lab
* object-cache.php. If it's from a third party, do not replace it.
*
* Previous versions of the Performance Lab plugin were renaming the
* original object-cache.php file and then loading both. However, due
* to other plugins eagerly checking file headers, this caused too many
Expand All @@ -408,9 +425,27 @@ function perflab_maybe_set_object_cache_dropin() {
* safest solution.
*/
if ( $wp_filesystem->exists( $dropin_path ) ) {
// Set timeout of 1 day before retrying again (only in case the file already exists).
set_transient( 'perflab_set_object_cache_dropin', true, DAY_IN_SECONDS );
return;
// If this constant evaluates to `false`, the existing file is for sure from a third party.
if ( ! $current_dropin_version ) {
// Set timeout of 1 day before retrying again (only in case the file already exists).
set_transient( 'perflab_set_object_cache_dropin', true, DAY_IN_SECONDS );
return;
}

// Otherwise, verify that it's actually the Performance Lab drop-in.
$test_content = "<?php\n/**\n * Plugin Name: Performance Lab Server Timing Object Cache Drop-In\n";
if ( ! str_starts_with( $wp_filesystem->get_contents( $dropin_path ), $test_content ) ) {
// Set timeout of 1 day before retrying again (only in case the file already exists).
set_transient( 'perflab_set_object_cache_dropin', true, DAY_IN_SECONDS );
return;
}

/*
* If this logic is reached, the existing file is an older version
* of the Performance Lab drop-in, so it can be safely deleted, and
* then be replaced below.
*/
$wp_filesystem->delete( $dropin_path );
}

$wp_filesystem->copy( PERFLAB_PLUGIN_DIR_PATH . 'includes/server-timing/object-cache.copy.php', $dropin_path );
Expand Down
70 changes: 70 additions & 0 deletions tests/load-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,62 @@ public function test_perflab_maybe_set_object_cache_dropin_with_conflict() {
$this->assertSame( $dummy_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
}

public function test_perflab_maybe_set_object_cache_dropin_with_older_version() {
global $wp_filesystem;

$this->set_up_mock_filesystem();

$latest_file_content = file_get_contents( PERFLAB_PLUGIN_DIR_PATH . 'includes/server-timing/object-cache.copy.php' );
$older_file_content = preg_replace( '/define\( \'PERFLAB_OBJECT_CACHE_DROPIN_VERSION\', (\d+) \)\;/', "define( 'PERFLAB_OBJECT_CACHE_DROPIN_VERSION', 1 );", $latest_file_content );
$wp_filesystem->put_contents( WP_CONTENT_DIR . '/object-cache.php', $older_file_content );

// Simulate PL constant is set to the value from the older file.
add_filter(
'perflab_object_cache_dropin_version',
static function () {
return 1;
}
);

// Ensure older object-cache.php drop-in is present.
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertSame( $older_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );

// Run function to place drop-in and ensure it overrides the existing drop-in with the latest version.
perflab_maybe_set_object_cache_dropin();
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertSame( $latest_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
}

public function test_perflab_maybe_set_object_cache_dropin_with_latest_version() {
global $wp_filesystem;

$this->set_up_mock_filesystem();

$latest_file_content = file_get_contents( PERFLAB_PLUGIN_DIR_PATH . 'includes/server-timing/object-cache.copy.php' );
$wp_filesystem->put_contents( WP_CONTENT_DIR . '/object-cache.php', $latest_file_content );

// Simulate PL constant is set to the value from the current file.
$this->assertTrue( (bool) preg_match( '/define\( \'PERFLAB_OBJECT_CACHE_DROPIN_VERSION\', (\d+) \)\;/', $latest_file_content, $matches ) );
$latest_version = (int) $matches[1];
add_filter(
'perflab_object_cache_dropin_version',
static function () use ( $latest_version ) {
return $latest_version;
}
);

// Ensure latest object-cache.php drop-in is present.
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertSame( $latest_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );

// Run function to place drop-in and ensure it doesn't attempt to replace the file.
perflab_maybe_set_object_cache_dropin();
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertSame( $latest_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertFalse( get_transient( 'perflab_set_object_cache_dropin' ) );
}

public function test_perflab_object_cache_dropin_may_be_disabled_via_filter() {
global $wp_filesystem;

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

public function test_perflab_object_cache_dropin_version_matches_latest() {
$file_content = file_get_contents( PERFLAB_PLUGIN_DIR_PATH . 'includes/server-timing/object-cache.copy.php' );

// Get the version from the file header and the constant.
$this->assertTrue( (bool) preg_match( '/^ \* Version: (\d+)$/m', $file_content, $matches ) );
$file_header_version = (int) $matches[1];
$this->assertTrue( (bool) preg_match( '/define\( \'PERFLAB_OBJECT_CACHE_DROPIN_VERSION\', (\d+) \)\;/', $file_content, $matches ) );
$file_constant_version = (int) $matches[1];

// Assert the versions are in sync.
$this->assertSame( PERFLAB_OBJECT_CACHE_DROPIN_LATEST_VERSION, $file_header_version );
$this->assertSame( PERFLAB_OBJECT_CACHE_DROPIN_LATEST_VERSION, $file_constant_version );
}

private function set_up_mock_filesystem() {
global $wp_filesystem;

Expand Down