In various hosting environments, WordPress core-, plugin- and theme updates sometimes fail because of enabled opcode caches. Popular PHP opcode caches are OPcache, WinCache and APC. This little WordPress Must Use Plugin tries to flush opcode caches. Making your live a bit easier when updating WordPress Core, Plugins and Themes.

For this mu-plugin to function only on WordPress Core-, Plugin- and Theme-updates, it hooks into WordPress' upgrader_pre_install filter hook:

Filters the install response before the installation has started.

You can use the upgrader_pre_install hook to execute a function before an update is installed. For example a function to flush OPcache cache, easing the update process and hopefully preventing update failures due to this opcode cache. Furthermore, it also prevents a stale (cached) "Please update!" notice in your WordPress Dashboard.

Reset PHP OPcache cache MU-Plugin

This WordPress mu-plugin is meant as an example, to show you how easy it can be to flush PHP caches. It's provided "AS-IS". You are free to use and extend it, but please do credit me though ;-) . It currently supports PHP OPcache and WinCache, but you can easily extend it for Redis, Memcached, and APCu (for example).

Before trying to flush the OPcache cache, the plugin tries to determine whether or not the PHP extensions are loaded and active on your web server. The plugin logs success and failure through PHP's error_log() function, meaning: check your WordPress Debug log for results.

Save the following PHP code in a new file called clear-php-opcode-caches.php, and upload it to your wp-content/mu-plugins folder. It'll be activated automatically.

<?php
/**
 * Plugin Name: Clear PHP opcode caches
 * Plugin URI: https://www.saotn.org/wordpress-plugin-flush-php-opcache/
 * Donate Link: https://www.paypal.me/jreilink
 * Description: Clears out various PHP opcode and user caches. Currently it tries to clear, or flush, PHP OPcache and WinCache caches from memory. Easily extended to flush Redis, Memcached, and APCu cache. This should ease WordPress updates and plugin activation / deactivation.
 * Network: True
 * Version: 1.0
 * Author: Jan Reilink
 * Author URI: https://www.saotn.org
 * License: GPLv2
 */

function clear_iis_wincache() {
	if( ! function_exists( 'wincache_ucache_get' ) ) {
		return;
	}
	if( ! wincache_ucache_clear() ) {
		return false;
	}
	else {
		return true;
	}
}

function clear_php_opcache() {
	if( ! extension_loaded( 'Zend OPcache' ) ) {
		return;
	}
	$opcache_status = opcache_get_status();
	if( false === $opcache_status["opcache_enabled"] ) {
		// extension loaded but OPcache not enabled
		return;
	}
	if( ! opcache_reset() ) { 
		return false;
	}
	else {
		/**
		 * opcache_reset() is performed, now try to clear the 
		 * file cache.
		 * Please note: http://stackoverflow.com/a/23587079/1297898
		 *   "Opcache does not evict invalid items from memory - they 
		 *   stay there until the pool is full at which point the 
		 *   memory is completely cleared"
		 */
		foreach( $opcache_status['scripts'] as $key => $data ) {
			$dirs[dirname( $key )][basename( $key )] = $data;
			opcache_invalidate( $data['full_path'] , $force = true );
		}
		return true;
	}
}

function is_iis() {
	$software = strtolower( $_SERVER["SERVER_SOFTWARE"] );
	if( false !== strpos( $software, "microsoft-iis" ) )
		return true;
	else
		return false;
}

function clear_caches() {
	if( is_iis() ) {
		if ( clear_iis_wincache() ) {
			error_log( 'WinCache user cache cleared.' );
		}
		else {
			error_log( 'Clearing WinCache user cache opcode cache failed.' );
		}
	}
	if( clear_php_opcache() ) {
		error_log( 'PHP OPcache opcode cache cleared.' );
	}
	else {
		error_log( 'Clearing PHP OPcache opcode cache failed.' );
	}
}

add_filter( 'plugin_row_meta', 'saotn1_plugin_row_meta', 10, 2 );
function saotn1_plugin_row_meta( $links, $file ) {
	if ( !preg_match('/clear-opcode-caches.php$/', $file ) ) {
	  return $links;
	}
		
	$links[] = sprintf(
	  '<a href="https://www.paypal.me/jreilink">%s</a>',
	  __( 'Donate' )
	);
	return $links;
}
add_filter( 'upgrader_pre_install', 'clear_caches', 10, 2 );
?>

That's all! Now this mu-plugin's clear_caches() function is ran before every WordPress update, whether it is a Core-, Plugin- or Theme update.

Why is this plugin important?

With the OPcache module in PHP core, you might run into cache misses and errors when upgrading WordPress. For example, in WordPress 4.7, get_paged_template() was moved to wp-includes/deprecated.php. A fatal fatal error might be thrown when PHP's internal OPcache still points to wp-includes/template.php (source by @swissspidy).

A second problem is that WordPress sometimes is not able to install and apply updates. An error message "Could not copy file. index.php" followed up with "Installation failed" is displayed in the Dashboard. Stale opcode cache might be the cause of this, and currently there is a running Trac ticket on the subject: Call opcache_reset() after plug-in, theme or core update.

Do you wonder how to flush an opcode cache like Redis when you publish a new post? I wrote a plugin for it! Automatically flush Redis cache after publishing a WordPress post

Extend the plugin and flushAPC cache

As said, you can easily extend the functionality of this mu-plugin. For example to flush APC / APCu cache.

To flush APC caches, you can use the PHP function apc_clear_cache(). See the following example:

/**
 * Follow me on Twitter: @Jan_Reilink
 * Support & donate: https://www.paypal.me/jreilink
 */
function clear_apc_cache() {
	if( ! extension_loaded( 'apc' ) ) {
		// APC extension not loaded
		return;
	}
	else {
		if( apc_clear_cache( 'opcode' ) ) {
			error_log( 'APC opcode cache flushed' );
		}
		else {
			error_log(' Flushing APC opcode cache failed' );
		}
		if( apc_clear_cache( 'user' ) ) {
			error_log( 'APC user cache flushed' );
		}
		else {
			error_log( 'Flushing APC user cache failed' );
		}
	}

	/**
	 * APCu is the user cache part of the old APC, without 
	 * bytecode caching. OPcache provides opcode caching 
	 * for PHP, but no user caching.
	 */
	if( ! extension_loaded( 'apcu' ) ) {
		// APCu extension not loaded
		return;
	}
	else {
		if ( apc_clear_cache( 'user' ) ) {
			error_log( 'APC user cache flushed' );
		}
		else {
			error_log( 'Flushing APC user cache failed' );
		}
	}
}

Yes, this code is very verbose for example purposes.

Reset PHP opcode caches Readme.txt

Hooking into upgrader_pre_install() can only be done from a Must Use Plugin, not from a normal plugin.

Loaded by PHP, in alphabetical order, before normal plugins, meaning API hooks added in an mu-plugin apply to all other plugins even if they run hooked-functions in the global namespace.

WordPress Codex Must Use Plugins

Here is its current readme.txt.

=== Clear PHP opcode caches ===
Contributors: janr
Donate link: https://www.paypal.me/jreilink
Author: Jan Reilink
Author URI: https://www.saotn.org
Tags: php, opcache cache, purge, apc, wincache, opcache, flush
Requires at least: 4.6
Tested up to: 4.9.7
Stable tag: 1.0
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html

Easily purge various PHP opcode caches like OPcache, WinCache and APC/
APCu. Ease the WordPress updating process.

== License ==
Released under the terms of the GNU General Public License.

== Description ==
In various hosting environments, WordPress core-, plugin- and theme 
updates sometimes fail because of enabled opcode caches. Popular PHP 
opcode caches are OPcache, WinCache and APC/APCu. This mu-plugin tries to 
purge, or clear/flush/invalidate, some of those opcode caches.

This'll make your live a bit easier when updating WordPress Core, 
Plugins and Themes.

The "Clear PHP opcode caches" mu-plugin hooks into `upgrader_pre_install()` 
to execute a function prior to an update, flushing the available 
opcode caches. Furthermore, it also prevents a stale (cached) 
"Please update!" notice. This opcode cache purge mu-plugin is 
particularly handy if used in combination with the Vevida Optimizer 
(https://wordpress.org/plugins/vevida-optimizer/) plugin.

Use Vevida Optimizer to configure WordPress automatic updates.

Before trying to flush the cache, it tries to determine whether 
or not the PHP extensions are loaded and active on your web server. 
It logs success and failure through PHP's error_log() function, 
meaning: check your WordPress Debug log for results, when enabled.

== Installation ==

1. Download the .zip package and unzip
1. Upload the file `clear-opcode-caches.php` to the `wp-content/mu-plugins/` directory

That's it, the Must Use Plugin to flush opcode caches is automatically activated. There is no configuration.

== Frequently Asked Questions ==

None so far.

== Changelog ==
= 1.0 =
* Initial tested version and release.

Download Clear opcode caches (clear-opcode-caches.zip).

If you have a valuable tip, please let me know and drop me a comment.

Donate a cup of coffee
Donate a cup of coffee

Thank you very much! <3 ❤️

2 Comments

  1. Before upgrade?
    Why not after upgrade and call opcache_reset()?

    • Some cache modules aggressively lock files, so I needed to flush/reset the cache and release locks before the upgrade. But perhaps upgrader_post_install() is just as a good as hook.

      Work is done by WordPress contributors to add a opcache_reset() to the core: https://core.trac.wordpress.org/ticket/36455, a plugin like mine may soon be no longer needed.

Comments are closed