Source for file Localfile.php

Documentation is available at Localfile.php

  1. <?php
  2. // vim: foldmethod=marker tabstop=4 shiftwidth=4 autoindent
  3. /**
  4.  *  Localfile.php
  5.  *
  6.  *  @author     Masaki Fujimoto <fujimoto@php.net>
  7.  *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
  8.  *  @package    Ethna
  9.  *  @version    $Id: 6f3c32a99ad7268ad0ac36e2682bab0564f9e95a $
  10.  */
  11.  
  12. /**
  13.  *  キャッシュマネージャクラス(ローカルファイルキャッシュ版)
  14.  *
  15.  *  @author     Masaki Fujimoto <fujimoto@php.net>
  16.  *  @access     public
  17.  *  @package    Ethna
  18.  */
  19. {
  20.     /**#@+  @access private */
  21.  
  22.     /**#@-*/
  23.  
  24.     /**
  25.      *  キャッシュに設定された値を取得する
  26.      *
  27.      *  キャッシュに値が設定されている場合はキャッシュ値
  28.      *  が戻り値となる。キャッシュに値が無い場合やlifetime
  29.      *  を過ぎている場合、エラーが発生した場合はEthna_Error
  30.      *  オブジェクトが戻り値となる。
  31.      *
  32.      *  @access public
  33.      *  @param  string  $key        キャッシュキー
  34.      *  @param  int     $lifetime   キャッシュ有効期間
  35.      *  @param  string  $namespace  キャッシュネームスペース
  36.      *  @return array   キャッシュ値
  37.      */
  38.     public function get($key, $lifetime = null, $namespace = null)
  39.     {
  40.         $namespace = $this->getNamespace($namespace);
  41.         $cache_file = $this->_getCacheFile($namespace, $key);
  42.  
  43.         // ライフタイムチェック
  44.         clearstatcache();
  45.         if (is_readable($cache_file) === false
  46.             || ($st = stat($cache_file)) === false) {
  47.             return Ethna::raiseError("No such cache (key=%s, file=%s)", E_CACHE_NO_VALUE, $key, $cache_file);
  48.         }
  49.         if (is_null($lifetime) == false) {
  50.             if (($st[9]+$lifetime) < time()) {
  51.                 return Ethna::raiseError("Cache expired (key=%s, file=%s)", E_CACHE_EXPIRED, $key, $cache_file);
  52.             }
  53.         }
  54.  
  55.         $fp = fopen($cache_file, "r");
  56.         if ($fp == false) {
  57.             return Ethna::raiseError('fopen failed', E_CACHE_NO_VALUE);
  58.         }
  59.         // ロック
  60.         $timeout = 3;
  61.         while ($timeout > 0) {
  62.             $r = flock($fp, LOCK_EX|LOCK_NB);
  63.             if ($r) {
  64.                 break;
  65.             }
  66.             $timeout--;
  67.             sleep(1);
  68.         }
  69.         if ($timeout <= 0) {
  70.             fclose($fp);
  71.             return Ethna::raiseError('fopen failed', E_CACHE_GENERAL);
  72.         }
  73.  
  74.         $n = 0;
  75.         while ($st[7] == 0) {
  76.             clearstatcache();
  77.             $st = stat($cache_file);
  78.             usleep(1000*1);
  79.             $n++;
  80.             if ($n > 5) {
  81.                 break;
  82.             }
  83.         }
  84.  
  85.         if ($st == false || $n > 5) {
  86.             fclose($fp);
  87.             return Ethna::raiseError('stat failed', E_CACHE_NO_VALUE);
  88.         }
  89.         $value = fread($fp, $st[7]);
  90.         fclose($fp);
  91.  
  92.         return unserialize($value);
  93.     }
  94.  
  95.     /**
  96.      *  キャッシュの最終更新日時を取得する
  97.      *
  98.      *  @access public
  99.      *  @param  string  $key        キャッシュキー
  100.      *  @param  string  $namespace  キャッシュネームスペース
  101.      *  @return int     最終更新日時(unixtime)
  102.      */
  103.     public function getLastModified($key, $namespace = null)
  104.     {
  105.         $namespace = $this->getNamespace($namespace);
  106.         $cache_file = $this->_getCacheFile($namespace, $key);
  107.  
  108.         clearstatcache();
  109.         if (is_readable($cache_file) === false
  110.             || ($st = stat($cache_file)) === false) {
  111.             return Ethna::raiseError('fopen failed', E_CACHE_NO_VALUE);
  112.         }
  113.         return $st[9];
  114.     }
  115.  
  116.     /**
  117.      *  値がキャッシュされているかどうかを取得する
  118.      *
  119.      *  @access public
  120.      *  @param  string  $key        キャッシュキー
  121.      *  @param  int     $lifetime   キャッシュ有効期間
  122.      *  @param  string  $namespace  キャッシュネームスペース
  123.      */
  124.     public function isCached($key, $lifetime = null, $namespace = null)
  125.     {
  126.         $namespace = $this->getNamespace($namespace);
  127.         $cache_file = $this->_getCacheFile($namespace, $key);
  128.  
  129.         // ライフタイムチェック
  130.         clearstatcache();
  131.         if (is_readable($cache_file) === false
  132.             || ($st = stat($cache_file)) === false) {
  133.             return false;
  134.         }
  135.         if (is_null($lifetime) == false) {
  136.             if (($st[9]+$lifetime) < time()) {
  137.                 return false;
  138.             }
  139.         }
  140.  
  141.         return true;
  142.     }
  143.  
  144.     /**
  145.      *  キャッシュに値を設定する
  146.      *
  147.      *  @access public
  148.      *  @param  string  $key        キャッシュキー
  149.      *  @param  mixed   $value      キャッシュ値
  150.      *  @param  int     $timestamp  キャッシュ最終更新時刻(unixtime)
  151.      *  @param  string  $namespace  キャッシュネームスペース
  152.      */
  153.     public function set($key, $value, $timestamp = null, $namespace = null)
  154.     {
  155.         $namespace = $this->getNamespace($namespace);
  156.         $dir = $this->_getCacheDir($namespace, $key);
  157.  
  158.         // キャッシュディレクトリチェック
  159.         $r = Ethna_Util::mkdir($dir, 0777);
  160.         if ($r == false && is_dir($dir) == false) {
  161.             return Ethna::raiseError('mkdir(%s) failed', E_USER_WARNING, $dir);
  162.         }
  163.  
  164.         $cache_file = $this->_getCacheFile($namespace, $key);
  165.         $fp = fopen($cache_file, "a+");
  166.         if ($fp == false) {
  167.             return Ethna::raiseError('fopen failed', E_CACHE_GENERAL);
  168.         }
  169.  
  170.         // ロック
  171.         $timeout = 3;
  172.         while ($timeout > 0) {
  173.             $r = flock($fp, LOCK_EX|LOCK_NB);
  174.             if ($r) {
  175.                 break;
  176.             }
  177.             $timeout--;
  178.             sleep(1);
  179.         }
  180.         if ($timeout <= 0) {
  181.             fclose($fp);
  182.             return Ethna::raiseError('fopen failed', E_CACHE_GENERAL);
  183.         }
  184.         rewind($fp);
  185.         ftruncate($fp, 0);
  186.         fwrite($fp, serialize($value));
  187.         fclose($fp);
  188.         Ethna_Util::chmod($cache_file, 0666);
  189.  
  190.         if (is_null($timestamp)) {
  191.             // this could suppress warning
  192.             touch($cache_file);
  193.         } else {
  194.             touch($cache_file, $timestamp);
  195.         }
  196.  
  197.         return 0;
  198.     }
  199.  
  200.     /**
  201.      *  キャッシュ値を削除する
  202.      *
  203.      *  @access public
  204.      *  @param  string  $key        キャッシュキー
  205.      *  @param  string  $namespace  キャッシュネームスペース
  206.      */
  207.     public function clear($key, $namespace = null)
  208.     {
  209.         $namespace = $this->getNamespace($namespace);
  210.         $cache_file = $this->_getCacheFile($namespace, $key);
  211.  
  212.         if (file_exists($cache_file)) {
  213.             unlink($cache_file);
  214.         }
  215.     }
  216.  
  217.     /**
  218.      *  キャッシュ対象ディレクトリを取得する
  219.      *
  220.      *  @access private
  221.      */
  222.     private function _getCacheDir($namespace, $key)
  223.     {
  224.         $safe_mode = ini_get('safe_mode');
  225.         if ($safe_mode) {
  226.             return sprintf("%s", $this->backend->getTmpdir());
  227.         }
  228.  
  229.         $len = strlen($key);
  230.         // intentionally avoid using -2 or -4
  231.         $dir1 = substr($key, $len-4, 2);
  232.         if ($len-4 < 0 || strlen($dir1) < 2) {
  233.             $dir1 = "__dir1";
  234.         }
  235.         $dir2 = substr($key, $len-2, 2);
  236.         if ($len-2 < 0 || strlen($dir2) < 2) {
  237.             $dir2 = "__dir2";
  238.         }
  239.  
  240.         //$map = $this->config->get('cachemanager_localfile');
  241.         $map = $this->config;
  242.         $tmp_key = $namespace . "::" . $key;
  243.         // PHP依存:)
  244.         $dir = "default";
  245.  
  246.         if (is_array($map)) {
  247.             foreach ($map as $key => $value) {
  248.                 if (strncmp($key, $tmp_key, strlen($key)) == 0) {
  249.                     $dir = $value;
  250.                     break;
  251.                 }
  252.             }
  253.         }
  254.         return sprintf("%s/cache/%s/cache_%s/%s/%s", $this->backend->getTmpdir(), $dir, $this->_escape($namespace), $this->_escape($dir1), $this->_escape($dir2));
  255.     }
  256.  
  257.     /**
  258.      *  キャッシュファイルを取得する
  259.      *
  260.      *  @access private
  261.      */
  262.     private function _getCacheFile($namespace, $key)
  263.     {
  264.         $safe_mode = ini_get('safe_mode');
  265.         if ($safe_mode) {
  266.             return sprintf("%s/cache_%s_%s", $this->_getCacheDir($namespace, $key), $this->_escape($namespace), $this->_escape($key));
  267.         }
  268.  
  269.         return sprintf("%s/%s", $this->_getCacheDir($namespace, $key), $this->_escape($key));
  270.     }
  271.  
  272.     /**
  273.      *  キーをファイルシステム用にエスケープする
  274.      *
  275.      *  @access private
  276.      */
  277.     private function _escape($string)
  278.     {
  279.         return preg_replace('/([^0-9A-Za-z_])/e', "sprintf('%%%02X', ord('\$1'))", $string);
  280.     }
  281. }

Documentation generated on Fri, 11 Nov 2011 03:58:29 +0900 by phpDocumentor 1.4.3