113 lines
4.3 KiB
PHP
113 lines
4.3 KiB
PHP
<?php
|
|
|
|
/**
|
|
* WordPress installations with OPCache enabled might bootstrap in an
|
|
* incoherent state from PHP and the Filesystem, causing fatal errors if
|
|
* upgrading to a newer version where a file has been removed.
|
|
*
|
|
* This file clears OPCache in this plugin if needed.
|
|
*
|
|
* WordPress 5.5+ handles OPCache invalidation depending on
|
|
* whether \WP_Upgrader::run is called with the "clear_destination"
|
|
* parameter set to true.
|
|
*
|
|
* @var string $pluginFilePath
|
|
* @var string $wp_version
|
|
*/
|
|
global $wp_version, $pluginFilePath;
|
|
|
|
// Early bail: WordPress 5.5+ already handles OPCache invalidation on plugin updates.
|
|
if (version_compare($wp_version, '5.5', '>=')) {
|
|
return;
|
|
}
|
|
|
|
$filename = isset($_SERVER['SCRIPT_FILENAME']) ? sanitize_text_field($_SERVER['SCRIPT_FILENAME']) : '';
|
|
|
|
// Ported from WordPress 5.5 wp_opcache_invalidate
|
|
$canInvalidate = function_exists('opcache_invalidate')
|
|
&& (!ini_get('opcache.restrict_api') || stripos(realpath($filename), ini_get('opcache.restrict_api')) === 0);
|
|
|
|
// Early bail: OPCache not enabled, or we can't clear it.
|
|
if (!$canInvalidate) {
|
|
if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
|
|
error_log('WP STAGING: Can not clear OPCache.');
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* When a site has OPCache enabled, it will cache the compiled "opcode" of this PHP file and all other PHP files.
|
|
*
|
|
* It doesn't cache, however, the result returned by the functions.
|
|
*
|
|
* We leverage this to run a runtime check between the version in this PHP file and
|
|
* the filesystem. The only possible scenario they can be different, on a regular
|
|
* distributed plugin, is if the variable in PHP is opcached to a different version
|
|
* from what's in the filesystem.
|
|
*
|
|
* We use the "Version" from the headers of the main file of the plugin to compare.
|
|
*/
|
|
$runtimeVersionDifferentFromBuildVersion = get_file_data($pluginFilePath, ['Version' => 'Version'])['Version'] !== '4.0.0';
|
|
$lastCheckHappenedAfterInterval = current_time('timestamp') > (int)get_site_transient('wpstg.bootstrap.opcache.lastCleared') + 5 * MINUTE_IN_SECONDS;
|
|
|
|
$shouldClearOpCache = apply_filters('wpstg.bootstrap.opcache.shouldClear', $runtimeVersionDifferentFromBuildVersion && $lastCheckHappenedAfterInterval);
|
|
|
|
if ($shouldClearOpCache) {
|
|
set_site_transient('wpstg.bootstrap.opcache.lastCleared', current_time('timestamp'), 1 * HOUR_IN_SECONDS);
|
|
|
|
$start = microtime(true);
|
|
|
|
clearstatcache(true);
|
|
|
|
try {
|
|
$it = new RecursiveDirectoryIterator(dirname($pluginFilePath));
|
|
} catch (Exception $e) {
|
|
// DirectoryIterator will throw if this plugin folder is not readable.
|
|
if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
|
|
error_log('WPSTG failed to clear OPCache because the folder does not exist or is not readable. Exception: ' . $e->getMessage());
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
$it = new RecursiveIteratorIterator($it);
|
|
|
|
$success = 0;
|
|
$failures = 0;
|
|
|
|
/** @var SplFileInfo $fileInfo */
|
|
foreach ($it as $fileInfo) {
|
|
if (
|
|
$fileInfo->isFile()
|
|
&& !$fileInfo->isLink()
|
|
&& $fileInfo->getExtension() === 'php'
|
|
) {
|
|
if (opcache_invalidate($fileInfo->getRealPath(), false)) {
|
|
$success++;
|
|
} else {
|
|
$failures++;
|
|
}
|
|
}
|
|
}
|
|
|
|
add_action('admin_notices', function () use ($pluginFilePath, $start) {
|
|
echo '<div class="notice-warning notice is-dismissible">';
|
|
echo '<p style="font-weight: bold;">' . esc_html__('WP STAGING OPCache', 'wp-staging') . '</p>';
|
|
echo '<p>' . wp_kses_post(
|
|
sprintf(
|
|
__('WP STAGING detected that the OPCache was outdated and automatically cleared the OPCache for the <strong>%s</strong> folder to prevent issues. This operation took %s seconds.', 'wp-staging'),
|
|
plugin_basename($pluginFilePath),
|
|
number_format(microtime(true) - $start, 4)
|
|
)
|
|
) . '</p>';
|
|
echo '</div>';
|
|
});
|
|
|
|
if (defined('WPSTG_DEBUG') && WPSTG_DEBUG) {
|
|
error_log(sprintf('%s files were cleared from OPCache in %s seconds', $success, microtime(true) - $start));
|
|
if (!empty($failures)) {
|
|
error_log(sprintf('WP STAGING could not clear %s files from the OpCache cache upon activation. There may be inconsistencies.', $failures));
|
|
}
|
|
}
|
|
}
|