<?php

  /**
   * APC cache backend
   *
   * This backend saves cache data using APC
   * 
   * @package angie.library.cache
   * @subpackage backend
   */
  class APCCacheBackend extends CacheBackend {
    
    /**
     * key that describes location of list of caches
     *
     * @var string
     */
    var $stored_caches_key = '_stored_caches';
    
    /**
     * List of existing caches
     *
     * @var array
     */
    var $stored_caches = array();
    
    /**
     * Storage server name
     *
     * @var string
     */
    var $storage_server = null;
    
    /**
     * Prefix of all stored cache keys
     * 
     * @var string
     */
    var $instance_prefix = null;
    
    /**
     * Internal cache - why not bypass APC if we already have that value in memory
     *
     * @var array
     */
    var $data = array();
      
    /**
     * Constructor
     *
     * @param array $params
     */
    function __construct($params = null) {
      parent::__construct($params);
      
      $this->instance_prefix = substr(md5(ROOT), 0, 15);
      $this->stored_caches = (array) apc_fetch($this->getFullKeyName($this->stored_caches_key));
    } // __construct
    
    /**
     * Get the real key name
     * 
     * @param string $key_name
     */
    function getFullKeyName($key_name) {
    	return $this->instance_prefix . '-' . $key_name;
    } // getKeyName
    
    /**
     * Get value for a given variable from cache and return it
     *
     * @param string $name
     * @return mixed
     */
    function get($name) {
      if (isset($this->data[$name])) {
      	$result = $this->data[$name];
      } else {
        $result = apc_fetch($this->getFullKeyName($name));
        if (!in_array($name, $this->stored_caches)) {
          $this->stored_caches[] = $name;
          $this->saveCachesList();
        } // if
        $this->data[$name] = $result;
      } // if
			return $result === false ? null : $result;
    } // get
    
    /**
     * Set value for a given variable
     * 
     * @param string $name
     * @param mixed $value
     * @return boolean
     */
    function set($name, $value) {
      $existing_value = $this->get($name);
      
      if ($existing_value != $value) {
        apc_store($this->getFullKeyName($name), $value, $this->lifetime);
        
        if (!in_array($name, $this->stored_caches)) {
          $this->stored_caches[] = $name;
          $this->saveCachesList();
        } // if
        
        $this->data[$name] = $value;
      } // if
      return true;
    } // set
    
    /**
     * Remove variable from cache
     *
     * @param string $name
     * @return boolean
     */
    function remove($name) {
      apc_delete($this->getFullKeyName($name));
      
      if (in_array($name, $this->stored_caches)) {
        $key = array_search($name, $this->stored_caches);
        unset($this->stored_caches[$key]);
        $this->saveCachesList();
      } // if
      
      // remove cache from internal cache
      if (isset($this->data[$name])) {
        unset($this->data[$name]);
      } // if
      
      return true;
    } // remove
    
    /**
     * Remove config options by pattern
     *
     * @param string $pattern
     * @return boolean
     */
    function removeByPattern($pattern) {
      $reg_expression = $this->preparePattern($pattern);
      
      $this->stored_caches = array_values($this->stored_caches);
      if (is_foreachable($this->stored_caches)) {
        foreach ($this->stored_caches as $key => $cache_name) {
          if(preg_match($reg_expression, $cache_name)) {            
            unset($this->stored_caches[$key]);
            unset($this->data[$cache_name]);
            apc_delete($this->getFullKeyName($cache_name));
          } // if
        } // foreach
        $this->saveCachesList();
      } // if
      return true;
    } // removeByPattern
    
    /**
     * Save data to persistant storage
     */
    function save() {
      return true;
    } // save
    
    /**
     * Clear data from cache - drop everything
     */
    function clear() {
      if (is_foreachable($this->stored_caches)) {
        foreach ($this->stored_caches as $key => $cache_name) {
          apc_delete($this->getFullKeyName($cache_name));
        } // foreach
      } // if
      
      unset($this->stored_caches);
      $this->stored_caches = array();
      $this->saveCachesList();
      
      unset($this->data);
    } // clear
    
    /**
     * Save list of caches
     */
    function saveCachesList() {
      apc_store($this->getFullKeyName($this->stored_caches_key), $this->stored_caches);
    } // saveCachesList
    
    /**
     * Prepare cache name
     *
     * @param string $name
     * @return string
     */
    function prepareCacheName($name) {
      return $this->storage_server.'_'.$name;
    } // prepareCacheName
  
  }