Login / Status
developer.Resource
Home . Documentation . Document Library . Extension Manuals
Sponsors
hosted by punkt.deTYPO3 and Open Source Magazine

1.5. Coding with enetcache

Wrapping an existing plugin

This is another way to write wrappers. Say we want to actually realize the above mentioned tag cloud example. We decide to not calculate the whole tag cloud handling on our own, but stumble upon vge_tagcloud which already does this nicely. This extension is pretty easy to feed with own data. Here is a solution to wrap vge_tagcloud in an own mini plugin, which just does configuration and caching. Our own plugin is called cloudwrap and has a pi1.

TS settings

plugin.tx_cloudwrap_pi1 {    # lifetime is 20 minutes  cachetime = 1200    # stuff ts settings from vge_tagcloud into own namespace  vge_tagcloudSettings < plugin.tx_vgetagcloud_pi1  vge_tagcloudSettings {    referenceTable= tx_mysearch_words    referenceName = name}

pi1 class

public function main($content, $conf) {    // Lazy use of $conf as identifier  $cacheIdentifier = $conf;    // Get from cache  $cachedContent = t3lib_div::makeInstance('tx_enetcache')->get($cacheIdentifier);    // Early return on cache hit  if ($cachedContent) {    return $cachedContent;  }    // Include tagCloud class  require_once(t3lib_extMgm::extPath('vge_tagcloud') . 'pi1/class.tx_vgetagcloud_pi1.php');    // Instantiate tagcloud and render content  $tagCloud = t3lib_div::makeInstance('tx_vgetagcloud_pi1');  $tagCloud->cObj = &$this->cObj;  $tagCloudConfig = $conf['vge_tagcloudSettings.'];  $content = $tagCloud->$tagCloud->main('', $tagCloudConfig);    // Add some cache tags  $cacheTags = array();  $cacheTags[] = 'tx_mysearch_words';    // Cache entry and return content  return t3lib_div::makeInstance('tx_enetcache')->set(    $cacheIdentifier,    $content,    $cacheTags,    $conf['cachetime']  );}

Discussion

First step is to calculate the identifier (which here consists just of the complete TS array), the early return on successful get(), else the content calculation and a set() at the end of the method. This example is pretty rough. In case we have a page browser we would probably add something like $this->piVars['page'] to the identifier to distinct between different pages. It's important that the current page ID and content-element ID are not included in the identifier, this would prevent re-using cache entries on other pages.

tx_enetcache is a singleton class, t3lib_div::makeInstance() will only instantiate it once during the frontend processing. Set() actually returns the content again, that's why we can do a direct return at the end.

All enetcache classes use the TYPO3 4.3 autoloader, so there is no need for a require_once(), we only need to require the vge_tagcloud class because it currently doesn't ship with an autoloader file.

Our cache entry for this plugin will only be tagged with the the table name, so if we change a record in the backend the cache entry will be dropped. Cache lifetime is configurable via TS. If the lifetime of this cache entry is that short, and things like search words will never be administrated in backend anyway, we could even give an empty array as tags (no manual dropping needed). For real world cases proper dropping is much more important for entries with a long lifetime than for “hotlists” like this one, we should then add tx_mysearch_words_123 to the cacheTag array and omit the table name tag.

In case we use an eID controller or something like that to insert new records to the words table, we might even decide to clear the cache entry in our eID controller with a line like that:t3lib_div::makeInstance('tx_enetcache')->drop(array('tx_mysearch_words'));

Using cache interface in more complex plugins

Enetcache comes with the interface tx_enetcache_cachable which can be used in own plugins. It helps to develop more complex extensions that consist of a dispatcher class and different controllers. Imagine a plugin with a list / detail view. A dispatcher class in pi1/ gets the list / detail controller selection from a flexform value, instatiates either the list or detail class and does the caching. If the controller classes implement the interface, the dispatcher could easily check this and call getIdentifier() and getTags() of those classes.

Injecting information to TSFE if plugins are cached

It's a problem if cached plugins need  to inject information to TSFE during runtime. This is frequently used by plugins, eg. to set a special site title or to add a Javascript to the header (via $GLOBALS['TSFE']->additionalHeaderData). These operations are not executed if the content of the plugin comes from cache.

There are various solutions to solve this:

  1. Don't set this information at runtime. This is often possible if plugins only add a static JS or CSS file to the header. It's easily possible to always load these files with TS.

  2. Add this handling before getting content from cache. This is not very elegant, and often costs some time.

  3. Cache those information in a separate array next to the cached content and set() those data right before returning content from cache.

Example for a combination of first and last option: wec_map usually sets two additional header information: It loads a local JS file and calculates a reference to an external google JS file, both are injected to TSFE using additionalHeaderData. If we have a wrapper for wec_map that implements enetcache, this is a possible solution:

EXT: wec_map hack out the local JS file and register in TS template:map_service/google/class.tx_wecmap_map_google.php// $GLOBALS['TSFE']->additionalHeaderData['wec_map'] = '<script src="' . $jsFile . '" type="text/javascript"></script>';

EXT: wec_map_enetcacheprivate function mapController () {    // Try to get from cache, set cached map js file and do an early return with cached content  $cacheIdentifier = $this->conf;  $cached = $this->cache->get($cacheIdentifier);  if ($cached) {    $GLOBALS['TSFE']->additionalHeaderData['wec_map_googleMaps'] = $cached['googleMapJS'];    return $cached['result'];  }    // Calculate wec_map content, instantiate class and handle things we need here. $content is filled with map content here  …    // Cached data is an array of content and the google js code link    // wec_map injects wec_map_googleMaps to additionalHeaderData    // We get it back from TSFE here and add it next to our content to the cache entry  $cacheData = array();  $cacheData['googleMapJS'] = $GLOBALS['TSFE']->additionalHeaderData['wec_map_googleMaps'];  $cacheData['result'] = $content;    // Write to cache and return  $this->cache->set(    $cacheIdentifier,    $cacheData,    $cacheTags,    $cacheTime  );}