Skip to content

Commit

Permalink
Merge pull request #616 from WordPress/fix/612-enhance-drop-in-interop
Browse files Browse the repository at this point in the history
Enhance object-cache.php drop-in interoperability with other plugins
  • Loading branch information
felixarntz authored Jan 9, 2023
2 parents a86bcdd + 6eb9a0e commit 245c9eb
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 18 deletions.
39 changes: 33 additions & 6 deletions load.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,35 @@ function perflab_maybe_set_object_cache_dropin() {
}

if ( $wp_filesystem || WP_Filesystem() ) {
// If there is an actual object-cache.php file, rename it.
$dropin_path = WP_CONTENT_DIR . '/object-cache.php';
$dropin_backup_path = WP_CONTENT_DIR . '/object-cache-plst-orig.php';

// If there is an actual object-cache.php file, rename it to effectively
// back it up.
// The Performance Lab object-cache.php will still load it, so the
// behavior does not change.
if ( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
$wp_filesystem->move( WP_CONTENT_DIR . '/object-cache.php', WP_CONTENT_DIR . '/object-cache-plst-orig.php' );
if ( $wp_filesystem->exists( $dropin_path ) ) {
// If even the backup file already exists, we should not do anything,
// except for the case where that file is the same as the main
// object-cache.php file. This can happen if another plugin is
// aggressively trying to re-add its own object-cache.php file,
// ignoring that the Performance Lab object-cache.php file still
// loads that other file. This is also outlined in the bug
// https://github.com/WordPress/performance/issues/612).
// In that case we can simply delete the main file since it is
// already backed up.
if ( $wp_filesystem->exists( $dropin_backup_path ) ) {
$oc_content = $wp_filesystem->get_contents( $dropin_path );
$oc_orig_content = $wp_filesystem->get_contents( $dropin_backup_path );
if ( ! $oc_content || $oc_content !== $oc_orig_content ) {
// Set timeout of 1 hour before retrying again (only in case of failure).
set_transient( 'perflab_set_object_cache_dropin', true, HOUR_IN_SECONDS );
return;
}
$wp_filesystem->delete( $dropin_path );
} else {
$wp_filesystem->move( $dropin_path, $dropin_backup_path );
}
}

$wp_filesystem->copy( PERFLAB_PLUGIN_DIR_PATH . 'server-timing/object-cache.copy.php', WP_CONTENT_DIR . '/object-cache.php' );
Expand Down Expand Up @@ -343,13 +367,16 @@ function perflab_maybe_remove_object_cache_dropin() {
}

if ( $wp_filesystem || WP_Filesystem() ) {
$dropin_path = WP_CONTENT_DIR . '/object-cache.php';
$dropin_backup_path = WP_CONTENT_DIR . '/object-cache-plst-orig.php';

// If there is an actual object-cache.php file, restore it
// and override the Performance Lab file.
// Otherwise just delete the Performance Lab file.
if ( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache-plst-orig.php' ) ) {
$wp_filesystem->move( WP_CONTENT_DIR . '/object-cache-plst-orig.php', WP_CONTENT_DIR . '/object-cache.php', true );
if ( $wp_filesystem->exists( $dropin_backup_path ) ) {
$wp_filesystem->move( $dropin_backup_path, $dropin_path, true );
} else {
$wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
$wp_filesystem->delete( $dropin_path );
}
}

Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<testsuites>
<testsuite name="default">
<directory suffix=".php">./tests</directory>
<exclude>./tests/utils</exclude>
</testsuite>
</testsuites>
<groups>
Expand Down
131 changes: 119 additions & 12 deletions tests/load-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -237,23 +237,130 @@ function( $module_settings, $module_dir ) {
);
}

public function test_perflab_maybe_set_object_cache_dropin() {
if ( ! $GLOBALS['wp_filesystem'] && ! WP_Filesystem() ) {
$this->markTestSkipped( 'Filesystem cannot be initialized.' );
}
public function test_perflab_maybe_set_object_cache_dropin_no_conflict() {
global $wp_filesystem;

if ( ! $GLOBALS['wp_filesystem']->is_writable( WP_CONTENT_DIR ) ) {
$this->markTestSkipped( 'This system does not allow file modifications within WP_CONTENT_DIR.' );
}
$this->set_up_mock_filesystem();

// Ensure PL object-cache.php drop-in is not present and constant is not set.
$this->assertFalse( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertFalse( PERFLAB_OBJECT_CACHE_DROPIN_VERSION );

// Run function to place drop-in and ensure it exists afterwards.
perflab_maybe_set_object_cache_dropin();
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertSame( file_get_contents( PERFLAB_PLUGIN_DIR_PATH . 'server-timing/object-cache.copy.php' ), $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
}

public function test_perflab_maybe_set_object_cache_dropin_no_conflict_but_failing() {
global $wp_filesystem;

$this->set_up_mock_filesystem();

// Ensure PL object-cache.php drop-in is not present and constant is not set.
$this->assertFalse( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertFalse( PERFLAB_OBJECT_CACHE_DROPIN_VERSION );

// Run function to place drop-in, but then delete file, effectively
// simulating that (for whatever reason) placing the file failed.
perflab_maybe_set_object_cache_dropin();
$wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );

// Running the function again should not place the file at this point,
// as there is a transient timeout present to avoid excessive retries.
perflab_maybe_set_object_cache_dropin();
$this->assertFalse( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
}

public function test_perflab_maybe_set_object_cache_dropin_with_conflict() {
global $wp_filesystem;

$this->set_up_mock_filesystem();

$dummy_file_content = '<?php /* Empty object-cache.php drop-in file. */';
$wp_filesystem->put_contents( WP_CONTENT_DIR . '/object-cache.php', $dummy_file_content );

// Ensure dummy object-cache.php drop-in is present and PL constant is not set.
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertFalse( PERFLAB_OBJECT_CACHE_DROPIN_VERSION );

// Run function to place drop-in and ensure it exists afterwards, and
// the dummy one is backed up to object-cache-plst-orig.php.
perflab_maybe_set_object_cache_dropin();
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertSame( file_get_contents( PERFLAB_PLUGIN_DIR_PATH . 'server-timing/object-cache.copy.php' ), $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache-plst-orig.php' ) );
$this->assertSame( $dummy_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache-plst-orig.php' ) );
}

public function test_perflab_maybe_set_object_cache_dropin_with_conflict_and_orig_same_files() {
global $wp_filesystem;

$this->set_up_mock_filesystem();

$dummy_file_content = '<?php /* Empty object-cache.php drop-in file. */';
$wp_filesystem->put_contents( WP_CONTENT_DIR . '/object-cache.php', $dummy_file_content );
$wp_filesystem->put_contents( WP_CONTENT_DIR . '/object-cache-plst-orig.php', $dummy_file_content );

// Ensure two dummy object-cache.php drop-ins are present and PL constant is not set.
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache-plst-orig.php' ) );
$this->assertFalse( PERFLAB_OBJECT_CACHE_DROPIN_VERSION );

// Run function to place drop-in and ensure it exists afterwards, and
// the dummy one remains backed up to object-cache-plst-orig.php. Since
// both files were the same, the original one could just be replaced.
perflab_maybe_set_object_cache_dropin();
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertSame( file_get_contents( PERFLAB_PLUGIN_DIR_PATH . 'server-timing/object-cache.copy.php' ), $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache-plst-orig.php' ) );
$this->assertSame( $dummy_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache-plst-orig.php' ) );
}

public function test_perflab_maybe_set_object_cache_dropin_with_conflict_and_orig_different_files() {
global $wp_filesystem;

$this->assertFalse( $GLOBALS['wp_filesystem']->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->set_up_mock_filesystem();

$dummy_file_content1 = '<?php /* Empty object-cache.php drop-in file 1. */';
$dummy_file_content2 = '<?php /* Empty object-cache.php drop-in file 2. */';
$wp_filesystem->put_contents( WP_CONTENT_DIR . '/object-cache.php', $dummy_file_content1 );
$wp_filesystem->put_contents( WP_CONTENT_DIR . '/object-cache-plst-orig.php', $dummy_file_content2 );

// Ensure two dummy object-cache.php drop-ins are present and PL constant is not set.
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache-plst-orig.php' ) );
$this->assertFalse( PERFLAB_OBJECT_CACHE_DROPIN_VERSION );

// Run function to place drop-in, but in this case it should not modify
// anything. There are already two different object-cache.php drop-ins,
// one main one, and another backup one, so it is better not to touch
// them, in order to avoid any risk of breakage.
perflab_maybe_set_object_cache_dropin();
$this->assertTrue( $GLOBALS['wp_filesystem']->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertSame( $dummy_file_content1, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) );
$this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache-plst-orig.php' ) );
$this->assertSame( $dummy_file_content2, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache-plst-orig.php' ) );
}

private function set_up_mock_filesystem() {
global $wp_filesystem;

add_filter(
'filesystem_method_file',
function() {
return __DIR__ . '/utils/Filesystem/WP_Filesystem_MockFilesystem.php';
}
);
add_filter(
'filesystem_method',
function() {
return 'MockFilesystem';
}
);
WP_Filesystem();

// Clean up. This is okay to be run after the assertion since otherwise
// the file does not exist anyway.
$GLOBALS['wp_filesystem']->delete( WP_CONTENT_DIR . '/object-cache.php' );
// Simulate that the original object-cache.copy.php file exists.
$wp_filesystem->put_contents( PERFLAB_PLUGIN_DIR_PATH . 'server-timing/object-cache.copy.php', file_get_contents( PERFLAB_PLUGIN_DIR_PATH . 'server-timing/object-cache.copy.php' ) );
}
}
1 change: 1 addition & 0 deletions tests/multisite.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<testsuites>
<testsuite name="default">
<directory suffix=".php">./</directory>
<exclude>./utils</exclude>
</testsuite>
</testsuites>
<groups>
Expand Down
139 changes: 139 additions & 0 deletions tests/utils/Filesystem/WP_Filesystem_MockFilesystem.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php
/**
* This file needs to be in the global namespace due to how WordPress requires loading it.
*
* @package performance-lab
*/

/**
* Simple mock filesystem, limited to working with concrete file paths.
* No support for hierarchy or parent directories etc.
*
* Could be expanded in the future if needed.
*/
class WP_Filesystem_MockFilesystem extends WP_Filesystem_Base {

private $file_contents = array();

public function get_contents( $file ) {
if ( isset( $this->file_contents[ $file ] ) ) {
return $this->file_contents[ $file ];
}
return false;
}

public function get_contents_array( $file ) {
if ( isset( $this->file_contents[ $file ] ) ) {
return array( $this->file_contents[ $file ] );
}
return false;
}

public function put_contents( $file, $contents, $mode = false ) {
$this->file_contents[ $file ] = $contents;
return true;
}

public function cwd() {
return false;
}

public function chdir( $dir ) {
return false;
}

public function chgrp( $file, $group, $recursive = false ) {
return false;
}

public function chmod( $file, $mode = false, $recursive = false ) {
return false;
}

public function owner( $file ) {
return false;
}

public function group( $file ) {
return false;
}

public function copy( $source, $destination, $overwrite = false, $mode = false ) {
if ( ! isset( $this->file_contents[ $source ] ) ) {
return false;
}
if ( ! $overwrite && isset( $this->file_contents[ $destination ] ) ) {
return false;
}
$this->file_contents[ $destination ] = $this->file_contents[ $source ];
return true;
}

public function move( $source, $destination, $overwrite = false ) {
if ( $this->copy( $source, $destination, $overwrite, false ) ) {
return $this->delete( $source );
}
return false;
}

public function delete( $file, $recursive = false, $type = false ) {
if ( isset( $this->file_contents[ $file ] ) ) {
unset( $this->file_contents[ $file ] );
}
return true;
}

public function exists( $path ) {
return isset( $this->file_contents[ $path ] );
}

public function is_file( $file ) {
return isset( $this->file_contents[ $file ] );
}

public function is_dir( $path ) {
return false;
}

public function is_readable( $file ) {
return isset( $this->file_contents[ $file ] );
}

public function is_writable( $path ) {
return true;
}

public function atime( $file ) {
return false;
}

public function mtime( $file ) {
return false;
}

public function size( $file ) {
if ( isset( $this->file_contents[ $file ] ) ) {
return strlen( $this->file_contents[ $file ] );
}
return false;
}

public function touch( $file, $time = 0, $atime = 0 ) {
if ( ! isset( $this->file_contents[ $file ] ) ) {
$this->file_contents[ $file ] = '';
}
return true;
}

public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) {
return false;
}

public function rmdir( $path, $recursive = false ) {
return false;
}

public function dirlist( $path, $include_hidden = true, $recursive = false ) {
return false;
}
}

0 comments on commit 245c9eb

Please sign in to comment.