Magento Full Page Cache (FPC) is a very useful technique or mechanism that allows us to copy web content by storing the output of a given URL to a temporary container (caching) to help reduce bandwidth usage, cpu load, memory comsuption, database stress, perceived lag among other benefits.

We need to understand what the run function in Mage_Core_Model_App does and how it is architected:

/**
* Run application. Run process responsible for request processing and sending response.
* List of supported parameters: * scope_code - code of default scope (website/store_group/store code)
* scope_type - type of default scope (website/group/store)
* options - configuration options
*
* @param array $params application run parameters
* @return Mage_Core_Model_App
*/
public function run($params)
{
$options = isset($params['options']) ? $params['options'] : array();
$this->baseInit($options);
Mage::register('application_params', $params);

if ($this->_cache->processRequest()) {
$this->getResponse()->sendResponse();
} else { $this->_initModules();
$this->loadAreaPart(Mage_Core_Model_App_Area::AREA_GLOBAL, Mage_Core_Model_App_Area::PART_EVENTS);
if ($this->_config->isLocalConfigLoaded()) {
$scopeCode = isset($params['scope_code']) ? $params['scope_code'] : '';
$scopeType = isset($params['scope_type']) ? $params['scope_type'] : 'store';
$this->_initCurrentStore($scopeCode, $scopeType);
$this->_initRequest();
Mage_Core_Model_Resource_Setup::applyAllDataUpdates();
}

$this->getFrontController()->dispatch();
}
return $this;
}

The most important part in that function is:

$this->_cache->processRequest()

What that line does is that checks if you have defined a caching node like this under app/etc/cache.xml:

cache.xml is just an arbitrary name I chose for this blog post (It’s not really arbitrary as you will see later).

The request processor node gets checked whenever the cache model gets instantiated:

/**
* Class constructor. Initialize cache instance based on options
*
* @param array $options
*/
public function __construct(array $options = array())
{

$this->_defaultBackendOptions['cache_dir'] = Mage::getBaseDir('cache');
/**
* Initialize id prefix
*/
$this->_idPrefix = isset($options['id_prefix']) ? $options['id_prefix'] : '';
if (!$this->_idPrefix && isset($options['prefix'])) { $this->_idPrefix = $options['prefix'];
}
if (empty($this->_idPrefix)) {
$this->_idPrefix = substr(md5(Mage::getConfig()->getOptions()->getEtcDir()), 0, 3).'_';
}

$backend = $this->_getBackendOptions($options);
$frontend = $this->_getFrontendOptions($options);

$this->_frontend = Zend_Cache::factory('Varien_Cache_Core', $backend['type'], $frontend, $backend['options'],
true, true, true );
if (isset($options['request_processors'])) {
$this->_requestProcessors = $options['request_processors']; } if (isset($options['disallow_save']))
{ $this->_disallowSave = $options['disallow_save'];
}
}


This piece of code:

if (isset($options['request_processors'])) {
$this->_requestProcessors = $options['request_processors'];
}

Is what matters most.

The next thing that magento does is to find / initialize the class you have defined and it expects that you have an extractContent function defined in your model. How you do that is totally up to you but look at Magento’s implementation and get a hint or two.

Magento Full Page Cache has its own config model that loads your module’s (see no arbitrary) cache.xml file which gets initialized whenever you dispatch this event core_block_abstract_to_html_after and do you know where that event gets dispatched? If you thought in the toHtml method in Mage_Core_Block_Abstract then you should be writing this article not me.


Anyhow, the Enterprise_PageCache_Model_Observer::renderBlockPlaceholder observes that event and initializes the Enterprise config model. It has a method called _initPlaceholders which iterates through all of the cache.xml nodes and finds the definition of the holes and fillers. This model is the one that basically takes control of filling the holes you defined in the cache.xml which has a syntax similar to this:



So now we know how Magento finds the cache, config, events and adds the container name in the page. However we don’t know what the containers are? Essentially they are the ones responsible for filling the holes you have defined. Each container has two important methods applyWithoutApp and applyInApp that Vinai has explained exceptionally well here. But it will be awesome if you go take a look and be amazed because trust me YOU will need to, to fully understand it.

The function that will probably will matter most for you is:

/**
* Render block content from placeholder
*
* @return string|false
*/
protected function _renderBlock()
{
}


As that is the one that will get your dynamic content (read holes).