Source for file Memcache.php

Documentation is available at Memcache.php

  1. <?php
  2. // vim: foldmethod=marker tabstop=4 shiftwidth=4 autoindent
  3. /**
  4.  *  Memcache.php
  5.  *
  6.  *  - Point Cutしたいと思った!
  7.  *  - キャッシュキーには250文字までしか使用できないので注意して下さい
  8.  *
  9.  *  @todo   ネームスペース/キャッシュキー長のエラーハンドリング
  10.  *
  11.  *  @author     Masaki Fujimoto <fujimoto@php.net>
  12.  *  @package    Ethna
  13.  *  @version    $Id: 163b3ffd272f571d005b78745eadd0049f890cc0 $
  14.  */
  15.  
  16. /**
  17.  *  キャッシュマネージャクラス(memcache版)
  18.  *
  19.  *  @author     Masaki Fujimoto <fujimoto@php.net>
  20.  *  @access     public
  21.  *  @package    Ethna
  22.  */
  23. {
  24.     /**#@+  @access private */
  25.  
  26.     /** @var    object  Memcache    Memcacheオブジェクト */
  27.     private $memcache null;
  28.  
  29.     /** @var bool 圧縮フラグ */
  30.     protected $compress = true;
  31.  
  32.     /** @var    array   plugin configure */
  33.     protected $config_default = array(
  34.         'host' => 'localhost',
  35.         'port' => '11211',
  36.         'retry' => 3,
  37.         'timeout' => 3,
  38.     );
  39.  
  40.     /**#@-*/
  41.  
  42.     /**
  43.      *  _load
  44.      *
  45.      *  @access protected
  46.      */
  47.     protected function _load()
  48.     {
  49.         parent::_load();
  50.         $this->memcache_pool array();
  51.     }
  52.  
  53.     /**
  54.      *  memcacheキャッシュオブジェクトを生成、取得する
  55.      *
  56.      *  @access protected
  57.      */
  58.     protected function _getMemcache($cache_key$namespace null)
  59.     {
  60.         $retry $this->config['retry'];
  61.         $timeout $this->config['timeout'];
  62.  
  63.         $r false;
  64.  
  65.         list($host$port$this->_getMemcacheInfo($cache_key$namespace);
  66.         if (isset($this->memcache_pool["$host:$port"])) {
  67.             // activate
  68.             $this->memcache $this->memcache_pool["$host:$port"];
  69.             return $this->memcache;
  70.         }
  71.         $this->memcache_pool["$host:$port"new MemCache();
  72.  
  73.         while ($retry 0{
  74.             if ($this->config['use_pconnect']{
  75.                 $r $this->memcache_pool["$host:$port"]->pconnect($host$port$timeout);
  76.             else {
  77.                 $r $this->memcache_pool["$host:$port"]->connect($host$port$timeout);
  78.             }
  79.             if ($r{
  80.                 break;
  81.             }
  82.             sleep(1);
  83.             $retry--;
  84.         }
  85.         if ($r == false{
  86.             trigger_error("memcache: connection failed");
  87.             $this->memcache_pool["$host:$port"null;
  88.         }
  89.  
  90.         $this->memcache $this->memcache_pool["$host:$port"];
  91.         return $this->memcache;
  92.     }
  93.  
  94.     /**
  95.      *  memcache接続情報を取得する
  96.      *
  97.      *  @access protected
  98.      *  @return array   array(host, port)
  99.      *  @todo   $cache_keyから$indexを決める方法を変更できるようにする
  100.      */
  101.     protected function _getMemcacheInfo($cache_key$namespace)
  102.     {
  103.         $namespace $this->getNamespace($namespace);
  104.  
  105.         $memcache_info $this->config['info'];
  106.         $default_memcache_host $this->config['host'];
  107.         $default_memcache_port $this->config['port'];
  108.  
  109.         if ($memcache_info == null || isset($memcache_info[$namespace]== false{
  110.             return array($default_memcache_host$default_memcache_port);
  111.         }
  112.  
  113.         // namespace/cache_keyで接続先を決定
  114.         $n count($memcache_info[$namespace]);
  115.  
  116.         $index $cache_key $n;
  117.         return array(
  118.             isset($memcache_info[$namespace][$index]['host']?
  119.                 $memcache_info[$namespace][$index]['host':
  120.                 $this->config['host'],
  121.             isset($memcache_info[$namespace][$index]['port']?
  122.                 $memcache_info[$namespace][$index]['port':
  123.                 $this->config['port'],
  124.         );
  125.  
  126.         // for safe
  127.         return array($default_memcache_host$default_memcache_port);
  128.     }
  129.  
  130.     /**
  131.      *  キャッシュに設定された値を取得する
  132.      *
  133.      *  キャッシュに値が設定されている場合はキャッシュ値
  134.      *  が戻り値となる。キャッシュに値が無い場合やlifetime
  135.      *  を過ぎている場合、エラーが発生した場合はEthna_Error
  136.      *  オブジェクトが戻り値となる。
  137.      *
  138.      *  @access public
  139.      *  @param  string  $key        キャッシュキー
  140.      *  @param  int     $lifetime   キャッシュ有効期間
  141.      *  @param  string  $namespace  キャッシュネームスペース
  142.      *  @return array   キャッシュ値
  143.      */
  144.     public function get($key$lifetime null$namespace null)
  145.     {
  146.         $this->_getMemcache($key$namespace);
  147.         if ($this->memcache == null{
  148.             return Ethna::raiseError('memcache server not available'E_CACHE_NO_VALUE);
  149.         }
  150.  
  151.         $namespace $this->getNamespace($namespace);
  152.  
  153.         $cache_key $this->_getCacheKey($namespace$key);
  154.         if ($cache_key == null{
  155.             return Ethna::raiseError('invalid cache key (too long?)'E_CACHE_NO_VALUE);
  156.         }
  157.  
  158.         $value $this->memcache->get($cache_key);
  159.         if ($value == null{
  160.             return Ethna::raiseError('no such cache'E_CACHE_NO_VALUE);
  161.         }
  162.         $time $value['time'];
  163.         $data $value['data'];
  164.  
  165.         // ライフタイムチェック
  166.         if (is_null($lifetime== false{
  167.             if (($time+$lifetimetime()) {
  168.                 return Ethna::raiseError('lifetime expired'E_CACHE_EXPIRED);
  169.             }
  170.         }
  171.  
  172.         return $data;
  173.     }
  174.  
  175.     /**
  176.      *  キャッシュの最終更新日時を取得する
  177.      *
  178.      *  @access public
  179.      *  @param  string  $key        キャッシュキー
  180.      *  @param  string  $namespace  キャッシュネームスペース
  181.      *  @return int     最終更新日時(unixtime)
  182.      */
  183.     public function getLastModified($key$namespace null)
  184.     {
  185.         $this->_getMemcache($key$namespace);
  186.         if ($this->memcache == null{
  187.             return Ethna::raiseError('memcache server not available'E_CACHE_NO_VALUE);
  188.         }
  189.  
  190.         $namespace $this->getNamespace($namespace);
  191.  
  192.         $cache_key $this->_getCacheKey($namespace$key);
  193.         if ($cache_key == null{
  194.             return Ethna::raiseError('invalid cache key (too long?)'E_CACHE_NO_VALUE);
  195.         }
  196.  
  197.         $value $this->memcache->get($cache_key);
  198.  
  199.         return $value['time'];
  200.     }
  201.  
  202.     /**
  203.      *  値がキャッシュされているかどうかを取得する
  204.      *
  205.      *  @access public
  206.      *  @param  string  $key        キャッシュキー
  207.      *  @param  int     $lifetime   キャッシュ有効期間
  208.      *  @param  string  $namespace  キャッシュネームスペース
  209.      */
  210.     public function isCached($key$lifetime null$namespace null)
  211.     {
  212.         $r $this->get($key$lifetime$namespace);
  213.  
  214.         return Ethna::isError($rfalsetrue;
  215.     }
  216.  
  217.     /**
  218.      *  キャッシュに値を設定する
  219.      *
  220.      *  @access public
  221.      *  @param  string  $key        キャッシュキー
  222.      *  @param  mixed   $value      キャッシュ値
  223.      *  @param  int     $timestamp  キャッシュ最終更新時刻(unixtime)
  224.      *  @param  string  $namespace  キャッシュネームスペース
  225.      */
  226.     public  function set($key$value$timestamp null$namespace null)
  227.     {
  228.         $this->_getMemcache($key$namespace);
  229.         if ($this->memcache == null{
  230.             return Ethna::raiseError('memcache server not available'E_CACHE_NO_VALUE);
  231.         }
  232.  
  233.         $namespace $this->getNamespace($namespace);
  234.  
  235.         $cache_key $this->_getCacheKey($namespace$key);
  236.         if ($cache_key == null{
  237.             return Ethna::raiseError('invalid cache key (too long?)'E_CACHE_NO_VALUE);
  238.         }
  239.  
  240.         $time $timestamp $timestamp time();
  241.         $this->memcache->set($cache_keyarray('time' => $time'data' => $value)$this->compress ? MEMCACHE_COMPRESSED null);
  242.     }
  243.  
  244.     /**
  245.      *  キャッシュ値を削除する
  246.      *
  247.      *  @access public
  248.      *  @param  string  $key        キャッシュキー
  249.      *  @param  string  $namespace  キャッシュネームスペース
  250.      */
  251.     public function clear($key$namespace null)
  252.     {
  253.         $this->_getMemcache($key$namespace);
  254.         if ($this->memcache == null{
  255.             return Ethna::raiseError('memcache server not available'E_CACHE_NO_VALUE);
  256.         }
  257.  
  258.         $namespace $this->getNamespace($namespace);
  259.  
  260.         $cache_key $this->_getCacheKey($namespace$key);
  261.         if ($cache_key == null{
  262.             return Ethna::raiseError('invalid cache key (too long?)'E_CACHE_NO_VALUE);
  263.         }
  264.  
  265.         $this->memcache->delete($cache_key-1);
  266.     }
  267.  
  268.     /**
  269.      *  キャッシュデータをロックする
  270.      *
  271.      *  @access public
  272.      *  @param  string  $key        キャッシュキー
  273.      *  @param  int     $timeout    ロックタイムアウト
  274.      *  @param  string  $namespace  キャッシュネームスペース
  275.      *  @return bool    true:成功 false:失敗
  276.      */
  277.     public function lock($key$timeout 5$namespace null)
  278.     {
  279.         $this->_getMemcache($key$namespace);
  280.         if ($this->memcache == null{
  281.             return Ethna::raiseError('memcache server not available'E_CACHE_LOCK_ERROR);
  282.         }
  283.  
  284.         // ロック用キャッシュデータを利用する
  285.         $namespace $this->getNamespace($namespace);
  286.         $cache_key "lock::" $this->_getCacheKey($namespace$key);
  287.         $lock_lifetime 30;
  288.  
  289.         do {
  290.             $r $this->memcache->add($cache_keytruefalse$lock_lifetime);
  291.             if ($r != false{
  292.                 break;
  293.             }
  294.             sleep(1);
  295.             $timeout--;
  296.         while ($timeout 0);
  297.  
  298.         if ($r == false{
  299.             return Ethna::raiseError('lock timeout'E_CACHE_LOCK_TIMEOUT);
  300.         }
  301.  
  302.         return true;
  303.     }
  304.  
  305.     /**
  306.      *  キャッシュデータのロックを解除する
  307.      *
  308.      *  @access public
  309.      *  @param  string  $key        キャッシュキー
  310.      *  @param  string  $namespace  キャッシュネームスペース
  311.      *  @return bool    true:成功 false:失敗
  312.      */
  313.     public function unlock($key$namespace null)
  314.     {
  315.         $this->_getMemcache($key$namespace);
  316.         if ($this->memcache == null{
  317.             return Ethna::raiseError('memcache server not available'E_CACHE_LOCK_ERROR);
  318.         }
  319.  
  320.         $namespace $this->getNamespace($namespace);
  321.         $cache_key "lock::" $this->_getCacheKey($namespace$key);
  322.  
  323.         $this->memcache->delete($cache_key-1);
  324.     }
  325.  
  326.     /**
  327.      *  ネームスペースからキャッシュキーを生成する
  328.      *
  329.      *  @access private
  330.      */
  331.     private function _getCacheKey($namespace$key)
  332.     {
  333.         // 少し乱暴だけど...
  334.         $key str_replace(":""_"$key);
  335.         $cache_key $namespace "::" $key;
  336.         if (strlen($cache_key250{
  337.             return null;
  338.         }
  339.         return $cache_key;
  340.     }
  341.  
  342.     /**
  343.      * 圧縮フラグを立てる
  344.      *
  345.      * MySQLなどいくつかの子クラスで有効
  346.      * 
  347.      * @access public
  348.      * @param bool $flag フラグ
  349.      */
  350.     public function setCompress($flag{
  351.         $this->compress = $flag;
  352.     }
  353. }

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