Forum Replies Created

Viewing 7 replies - 1 through 7 (of 7 total)
  • Sadly, I am no longer able to reproduce this on my hosting account. If I find any more information I will be sure to follow up.

    Feel free to reach out on the wordpress slack @captin411 I would love to help out.

    Perhaps these lines:

    ...
    if (false === $languages = $this->cache->get('languages')) {
    			if ((defined('PLL_CACHE_LANGUAGES') && !PLL_CACHE_LANGUAGES) || false === ($languages = get_transient('pll_languages_list'))) {
    ...

    Could instead read something like this:

    ...
    $FORCE_NOCACHE = defined('PLL_CACHE_LANGUAGES') && !PLL_CACHE_LANGUAGES;
    
    if (($FORCE_NOCACHE || false === $languages = $this->cache->get('languages'))) {
    			if (($FORCE_NOCACHE || false === ($languages = get_transient('pll_languages_list'))) {
    ...

    I have not explored any of the side effects this change would have at all tho.

    As for why the cache option did not work. I believe it is a chicken and egg problem.

    I installed polylang and set it up. Then got the error — but the fastcgi or apache object caches now already have the objects in this odd state. (and I suspect $this->cache->get(…) runs, “succeeds” and bypasses the PLL_CACHE_LANGUAGE check altogether).

    Because it’s a shared hosting environment, I do not have the ability to reboot the web server processes.

    This fix should correct things for people running on shared hosts that still WANT to leverage an object cache for performance reasons.

    I am willing to work on some alternative code patches if you think there is a better approach.

    In this case, get transient does not return false – and it should not.

    The bug comes when an unprimed intermediary object cache doesn’t have the object in memory and in turn loads it from the database (via get transient).

    Maybe I can ASCII picture it out:

    Pretend each thread has no knowledge or shared data from the other threads

    [Thread A]
      -> WordPress loads
        -> APC object cache loads
          -> polylang is loaded
            -> PLL_Language include is loaded
              -> PLL save cache languages
                -> save transient as object
                  -> transient is saved in mysql
    [Thread B]
      -> WordPress loads
        -> APC object cache loads (it actually loads all transients and options from mysql and HERE is where the problem is, the object exists, but the polylang includes have not yet been loaded and thus deserialization fails)
          -> polylang is loaded (classes are loaded but too little too late)
            -> language loaded from object cache (incomplete deserialization)
              -> some code us run that tries to access a property of language object which is not "real"
                -> PHP failure

    I was not able to see a way for plugins to run any hooks that would berun BEFORE object caches are taken into consideration.

    Another option is a refactoring of how the transients are stored: namely don’t store object but associative arrays and move the array-to-object logic into the polylang plugin.

    My patch is written with the intent of avoiding a refactor.

    Hope this clears things up.

    I should note that the “no cache” option did not work for me either.

    I was running into this problem as well. It turns out it has to do with hosting providers using object caches.

    Here is how it breaks.

    1) polylang writes cached languages to cache as a transient
    2) another http request comes in to some apache/fastcgi (whatever) process that has no cache info
    2a) the cache is loaded from the db (before the poly lang plugin is loaded)
    2b) polylang tries to deserialize the object and it doesn’t quite work when it tries to access properties
    3) explosions

    My patch to fix this (diff against 1.7.2).

    --- model.php	2015-03-23 16:21:56.000000000 -0700
    +++ /Users/davidbartle/dbartle_model.php	2015-04-03 10:16:26.000000000 -0700
    @@ -5,6 +5,13 @@
      *
      * @since 1.2
      */
    +
    +function _pll_maybe_fix_object($object) {
    +    // Thanks google, stackoverflow and 23hush for the technique
    +    // http://stackoverflow.com/questions/965611/forcing-access-to-php-incomplete-class-object-properties
    +    return !is_object($object) && gettype($object) == 'object' ? unserialize(serialize($object)) : $object;
    +}
    +
     class PLL_Model {
     	public $options;
     	protected $cache; // our internal non persistent cache object
    @@ -173,6 +180,10 @@
    
     		$args = wp_parse_args($args, array('hide_empty' => false));
    
    +        foreach ($languages as $k => $v) {
    +            $languages[$k] = _pll_maybe_fix_object($v);
    +        }
    +
     		// remove empty languages if requested
     		$languages = array_filter($languages, create_function('$v', sprintf('return $v->count || !%d;', $args['hide_empty'])));
    
    @@ -190,6 +201,7 @@
     	public function _languages_list() {
     		if ((defined('PLL_CACHE_LANGUAGES') && !PLL_CACHE_LANGUAGES) || false === get_transient('pll_languages_list')) {
     			foreach ($languages = $this->cache->get('languages') as $language) {
    +                $language = _pll_maybe_fix_object($language);
     				$language->set_flag();
    
     				if (!defined('PLL_CACHE_HOME_URL') || PLL_CACHE_HOME_URL) {
    @@ -202,6 +214,7 @@
     		}
    
     		foreach ($this->cache->get('languages') as $language) {
    +            $language = _pll_maybe_fix_object($language);
     			if (defined('PLL_CACHE_HOME_URL') && !PLL_CACHE_HOME_URL)
     				$language->set_home_url();
    
    @@ -259,7 +272,7 @@
     			$return = $this->cache->get('language:' . $value);
     		}
    
    -		return $return;
    +		return _pll_maybe_fix_object($return);
     	}
    
     	/*

    Thing I’ve personally learned:
    – I’ll probably avoid storing objects in wp transients just to avoid this all together – and instead just use associative arrays

Viewing 7 replies - 1 through 7 (of 7 total)