Subversion Repositories web.creative

Rev

Blame | Last modification | View Log | Download

<?php namespace ProcessWire;

/**
 * ProcessWire Markup Cache module
 *
 * A simple way to cache segments of markup in your templates. 
 * A simpler front end to ProcessWire's CacheFile class. 
 *
 * Example usage:
 * 
 * $mycache = $modules->get("MarkupCache"); 
 * if(!$data = $mycache->get("cityOptions")) {
 *  foreach($pages->find("template=city, sort=name") as $city) {
 *    $data .= "<option value='{$city->id}'>{$city->title}</option>";
 *  }
 *  $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"); 
  }

  
}