Resolved

Reported for: WPML Multilingual CMS 3.2.7

Resolved in: 3.3.2

Symptoms

When using the caching implementation of WPEngine or other custom implementations, a fatal error as such may appear:

PHP Fatal error: Call to undefined method WP_Object_Cache::__get() in /nas/wp/www/cluster-40022/spms/wp-content/plugins/wpml-string-translation/inc/package-translation/inc/wpml-package-translation-helper.class.php on line 384

This is caused by WPML trying to flush caches by group.

When using the standard caching provided by WordPress, this is not an issue, as it uses the `WP_Object_Cache::__get()` magic method.

However, this method is used to read the `cache` property, which is actually private.

Custom implementations of this class may not have the magic method available, hence the fatal error.

Workaround

As a temporary workaround you can follow these steps (please read the bottom note):

  • Open the file at `wp-content/plugins/wpml-string-translation/inc/package-translation/inc/wpml-package-translation-helper.class.php`
  • Look for the method `flush_cache` around line 369. You should see this code:
    final private function flush_cache() {
    	/** @var WP_Object_Cache $wp_object_cache */
    	global $wp_object_cache;
    	$cache = $wp_object_cache->__get( 'cache' );
    	if ( isset( $cache[ $this->cache_group ] ) ) {
    		foreach ( $cache[ $this->cache_group ] as $cache_key => $data ) {
    			wp_cache_delete( $cache_key, $this->cache_group );
    		}
    	}
    }
  • Replace these lines with:
    final private function flush_cache() {
    	/** @var WP_Object_Cache $wp_object_cache */
    	global $wp_object_cache;
    	$has_cache_property = method_exists( $wp_object_cache, '__get' );
    	if ( ! $has_cache_property && property_exists( $wp_object_cache, 'cache' ) ) {
    		$reflector          = new ReflectionClass( get_class( $wp_object_cache ) );
    		$cache_property     = $reflector->getProperty( 'cache' );
    		$has_cache_property = $cache_property->isPublic();
    	}
    	if ( $has_cache_property ) {
    		$cache = $wp_object_cache->cache;
    		if ( isset( $cache[ $this->cache_group ] ) ) {
    			foreach ( $cache[ $this->cache_group ] as $cache_key => $data ) {
    				wp_cache_delete( $cache_key, $this->cache_group );
    			}
    		}
    	} else {
    		wp_cache_flush();
    	}
    }

Note: This functionality is required to clear part of the cache, rather than the whole cache. By using this workaround, the flushing will happen globally, therefore performance might be affected. However, this particular code is only ran when deleting or updating strings packages (e.g. with Layouts, or Gravity Forms).

4 Responses to “Caching compatibility issues with WPEngine or custom caching implementation”

  1. The workaround provided doesn’t work.

    The fix should:

    * Bail out of the function if the $wp_object_cache->cache property is not accessible.
    * Stop using $wp_object_cache->__get( ‘cache’ ) explicitly and instead access $wp_object_cache->cache (which transparently uses __get() on those implementations that do support it).

  2. Florian, Luís you’re right.

    `isset( $wp_object_cache->cache )` is not enough to check if the field is public (that’s why we can’t use your suggested fix Luís, as this solution would still throw the error).

    I’ve updated the errata with a better workaround.

    This one first check if `WP_Object_Cache::__get()` exists.
    If not, it checks if `WP_Object_Cache::cache` exists and also check that is public.

    This should cover all cases.
    I’ve tested this fix by actually removing the `WP_Object_Cache::__get()` method from `WP_Object_Cache` class, by making `WP_Object_Cache::cache` and any combination of the two cases.

    This requires the use of the reflection class, which adds some more overhead, but considering that this happens only when updating/creating packages, it should be still acceptable.

    I’d appreciate some feedback to confirm that this works also for you.