Magento Core Cache Bug

Thanks very much to my colleagues from iWelt who found this bug and allow me to publish it here to get all the reputation ;-)

Magento can overwrite a cached block with a different one.

Magento Cache Key

Magento generates the cache key by default using the array returned by getCacheKeyInfo:

\Mage_Core_Block_Abstract::getCacheKey
public function getCacheKey()
{
    if ($this->hasData('cache_key')) {
        return $this->getData('cache_key');
    }
    $key = $this->getCacheKeyInfo();
    ...
}

The default implementation of getCacheKeyInfo only takes the name in the layout in consideration:

\Mage_Core_Block_Abstract::getCacheKeyInfo
public function getCacheKeyInfo()
{
    return array(
        $this->getNameInLayout()
    );
}

So far so good.

What is the name of a block, which is created WITHOUT a name?

Block name if no name is given

public function createBlock($type, $name='', array $attributes = array())
{
    ...  
    $block = $this->_getBlockInstance($type, $attributes);
    ...
    $name = 'ANONYMOUS_'.sizeof($this->_blocks);
    ...
    $block->setNameInLayout($name);
    ...
    $this->_blocks[$name] = $block;
    ...
    }
}

As you can see, the name of the block is set automatically. In case you don't know sizeof: it's an alias for count.

So the raising number is the current block count of the whole page.

This means, the name of the block is ANONYMOUS_1, ANONYMOUS_2, etc.

This means, the cache key is generated from [ANONYMOUS_1].

The problem

Now imagine, you have two different pages, which have a block, without a name. These two blocks accidentally have the same sequential number during generation.

Same cache key means: one entry

So in the end you'll get the same cache key and this means: Magento treats it as the same block.

In our case the homepage content block was overwritten by a block of the sidebar.

Solution

My colleague came up with this solution:

We add the current action to the block name:

$name = 'ANONYMOUS_'.sizeof($this->_blocks).Mage::app()->getFrontController()->getRequest()->getPathInfo();

We gave our best to fix the problem itself (which is a wrong generated cache key), but there is no event somewhere in the context of name generation and cache key generation. And because the key is generated in abstract class Mage_Core_Block_Abstract the only solution would be to copy the class to local/Mage which is no solution by definition.

So the rewrite of Mage_Core_Model_Layout is our best bet.