get("MarkupCache"); * if(!$data = $mycache->get("cityOptions")) { * foreach($pages->find("template=city, sort=name") as $city) { * $data .= ""; * } * $mycache->save($data); * } * echo $data; * * * ProcessWire 3.x, Copyright 2016 by Ryan Cramer * https://processwire.com * * * @todo add serialize/unserialize to support non-string data in MarkupCache * */ class MarkupCache extends Wire implements Module, ConfigurableModule { /** * getModuleInfo is a module required by all modules to tell ProcessWire about them * * @return array * */ public static function getModuleInfo() { return array( 'title' => 'Markup Cache', 'version' => 101, 'summary' => 'A simple way to cache segments of markup in your templates. ', 'href' => 'https://processwire.com/api/modules/markupcache/', 'singular' => true, 'autoload' => true, ); } /** * Instance of CacheFile * * @var CacheFile * */ protected $cache = null; /** * Boolean indicating whether we've already cleared the cache. * */ protected $cleared = false; /** * Path to cache files, as set by the init() method. * */ protected $path = ''; /** * Non zero when caches shouldn't expire on page save * */ protected $noExpire = 0; /** * Generate the module's path, static so it can be used by the static getModuleConfigInputfields function * */ public function path() { return $this->wire('config')->paths->cache . 'MarkupCache' . '/'; } /** * Initialize the module and add a hook after Pages::save * */ public function init() { $this->path = $this->path(); if(!$this->noExpire) $this->pages->addHookAfter('save', $this, 'expire'); } /** * Get cached data identified by 'uniqueName' or false if cache not available * * @param string $uniqueName A unique string or number to identify this cache, i.e. 'citiesList' * @param int $seconds The number of seconds the cache should live. * @return string|bool Returns the cache data, or FALSE if it has expired and needs to be re-created. * @throws WireException * */ public function get($uniqueName, $seconds = 3600) { $cache = $this->wire(new CacheFile($this->path, $uniqueName, $seconds)); if(!$cache) throw new WireException("Unable to create cache '{$this->path}/$uniqueName'"); $this->cache = $cache; return $this->cache->get(); } /** * Save the data to the cache * * Must be preceded by a call to get() so that you have set the cache unique name * * @param string $data Data to cache * @return int Number of bytes written to cache, or FALSE on failure. * @throws WireException * */ public function save($data) { if(!$this->cache) throw new WireException("You must attempt to retrieve a cache first, before you can save it."); $result = $this->cache->save($data); $this->cache = null; return $result; } /** * Expire the cache, automatically hooked to every $pages->save() call * */ public function expire($event = null) { /* * If already cleared during this session, don't do it again * that way if we're saving 100+ pages, we aren't clearing the cache 100+ times * */ if($this->cleared) return; if($this->cache) $cache = $this->cache; else $cache = $this->wire(new CacheFile($this->path, '', 0)); $cache->expireAll(); $this->cleared = true; } /** * Clears all MarkupCache files * * @return number of files/dirs deleted * */ public function removeAll() { $path = $this->path(); try { $num = CacheFile::removeAll($path, true); } catch(\Exception $e) { $num = 0; } return $num; } /** * For ConfigurableModule interface, even though we aren't currently using it * */ public function __set($key, $value) { if($key == 'noExpire') $this->noExpire = (int) $value; // intentionally left blank } /** * Provide cache clearing capability in the module's configuration * * @param array $data * @return InputfieldWrapper * */ public function getModuleConfigInputfields(array $data) { $inputfields = $this->wire(new InputfieldWrapper()); $clearNow = $this->wire('input')->post->_clearCache ? true : false; $message = ''; if($clearNow) { $numFiles = $this->removeAll(); $message = "Cleared $numFiles MarkupCache files and dirs"; $inputfields->message($message); } $name = "_clearCache"; // prefix with '_' tells ProcessModule not to save it in module's config data $f = $this->wire('modules')->get('InputfieldCheckbox'); $f->attr('name', $name); $f->attr('value', 1); $f->attr('checked', ''); $f->label = "Clear the MarkupCache?"; $f->notes = $message; $f->description = "This will remove all files and directories used by the MarkupCache"; $inputfields->append($f); $f = $this->wire('modules')->get('InputfieldRadios'); $f->attr('name', 'noExpire'); $f->attr('value', empty($data['noExpire']) ? 0 : 1); $f->label = "Expire markup caches when pages are saved?"; $f->description = "If you want to ensure stale data is never shown, you should choose: Yes. If you want to maximize performance, you should choose: No."; $f->addOption(0, "Yes - Expire markup caches"); $f->addOption(1, "No - Don't expire markup caches"); $inputfields->append($f); return $inputfields; } /** * Uninstall this module and remove it's files * */ public function ___uninstall() { $numFiles = $this->removeAll(); $this->message("Removed $numFiles MarkupCache files"); } }