# Memcached.class.php

Go to the documentation of this file.

    00001 <?php
    00002 /***************************************************************************
    00003  *   Copyright (C) 2004-2008 by Konstantin V. Arkhipov                     *
    00004  *                                                                         *
    00005  *   This program is free software; you can redistribute it and/or modify  *
    00006  *   it under the terms of the GNU Lesser General Public License as        *
    00007  *   published by the Free Software Foundation; either version 3 of the    *
    00008  *   License, or (at your option) any later version.                       *
    00009  *                                                                         *
    00010  *   Inspired by the work of Ryan Gilfether <hotrodder@rocketmail.com>     *
    00011  *   Copyright (c) 2003, under the GNU GPL license                         *
    00012  *                                                                         *
    00013  ***************************************************************************/
    00014 /* $Id$ */
    00015
    00023     final class Memcached extends CachePeer
    00024     {
    00025         const DEFAULT_PORT      = 11211;
    00026         const DEFAULT_HOST      = '127.0.0.1';
    00027         const DEFAULT_BUFFER    = 16384;
    00028
    00029         private $link       = null;
    00030
    00031         private $buffer     = Memcached::DEFAULT_BUFFER;
    00032
    00036         public static function create(
    00037             $host = Memcached::DEFAULT_HOST,
    00038             $port = Memcached::DEFAULT_PORT,
    00039             $buffer = Memcached::DEFAULT_BUFFER
    00040         )
    00041         {
    00042             return new Memcached($host, $port, $buffer);
    00043         }
    00044
    00045         public function __construct(
    00046             $host = Memcached::DEFAULT_HOST,
    00047             $port = Memcached::DEFAULT_PORT,
    00048             $buffer = Memcached::DEFAULT_BUFFER
    00049         )
    00050         {
    00051             $errno = $errstr = null;
    00052
    00053             try {
    00054                 if ($this->link = @fsockopen($host, $port, $errno, $errstr, 1)) {
    00055                     $this->alive = true;
    00056
    00057                     $this->buffer = $buffer;
    00058
    00059                     stream_set_blocking($this->link, true);
    00060                 }
    00061             } catch (BaseException $e) {/*_*/}
    00062         }
    00063
    00064         public function __destruct()
    00065         {
    00066             try {
    00067                 fclose($this->link);
    00068             } catch (BaseException $e) {/*_*/}
    00069         }
    00070
    00074         public function clean()
    00075         {
    00076             if (!$this->link) {
    00077                 $this->alive = false;
    00078                 return null;
    00079             }
    00080
    00081             $this->sendRequest("flush_all\r\n");
    00082
    00083             // flushing obligatory response - "OK\r\n"
    00084             fread($this->link, 4);
    00085
    00086             return parent::clean();
    00087         }
    00088
    00089         public function getList($indexes)
    00090         {
    00091             if (!$this->link) {
    00092                 $this->alive = false;
    00093                 return null;
    00094             }
    00095
    00096             $command = 'get '.implode(' ', $indexes)."\r\n";
    00097
    00098             if (!$this->sendRequest($command))
    00099                 return null;
    00100
    00101             // we can't deserialize objects inside parseGetRequest,
    00102             // because of possibility further requests to memcached
    00103             // during deserialization - in __wakeup(), for example
    00104             return unserialize($this->parseGetRequest(false));
    00105         }
    00106
    00107         public function increment($key, $value)
    00108         {
    00109             return $this->changeInteger('incr', $key, $value);
    00110         }
    00111
    00112         public function decrement($key, $value)
    00113         {
    00114             return $this->changeInteger('decr', $key, $value);
    00115         }
    00116
    00117         public function get($index)
    00118         {
    00119             if (!$this->link) {
    00120                 $this->alive = false;
    00121                 return null;
    00122             }
    00123
    00124             $command = "get {$index}\r\n";
    00125
    00126             if (!$this->sendRequest($command))
    00127                 return null;
    00128
    00129             return $this->parseGetRequest(true);
    00130         }
    00131
    00132         public function delete($index, $time = null)
    00133         {
    00134             $command =
    00135                 $time
    00136                     ? "delete {$index} {$time}\r\n"
    00137                     : "delete {$index}\r\n";
    00138
    00139             if (!$this->sendRequest($command))
    00140                 return false;
    00141
    00142             try {
    00143                 $response = fread($this->link, $this->buffer);
    00144             } catch (BaseException $e) {
    00145                 return false;
    00146             }
    00147
    00148             if ($response === "DELETED\r\n")
    00149                 return true;
    00150             else
    00151                 return false;
    00152         }
    00153
    00154         public function append($key, $data)
    00155         {
    00156             $packed = serialize($data);
    00157
    00158             $length = strlen($packed);
    00159
    00160             // flags and exptime are ignored
    00161             $command = "append {$key} 0 0 {$length}\r\n{$packed}\r\n";
    00162
    00163             if (!$this->sendRequest($command))
    00164                 return false;
    00165
    00166             $response = fread($this->link, $this->buffer);
    00167
    00168             if ($response === "STORED\r\n")
    00169                 return true;
    00170
    00171             return false;
    00172         }
    00173
    00174         protected function store(
    00175             $method, $index, $value, $expires = Cache::EXPIRES_MINIMUM
    00176         )
    00177         {
    00178             if ($expires === Cache::DO_NOT_CACHE)
    00179                 return false;
    00180
    00181             $flags = 0;
    00182
    00183             if (!is_numeric($value)) {
    00184                 if (is_string($value))
    00185                     $packed = $value;
    00186                 else {
    00187                     $packed = serialize($value);
    00188
    00189                     $flags |= 1;
    00190                 }
    00191
    00192                 if ($this->compress) {
    00193                     $compressed = gzcompress($packed);
    00194
    00195                     if (strlen($compressed) < strlen($packed)) {
    00196                         $packed = $compressed;
    00197                         $flags |= 2;
    00198                         unset($compressed);
    00199                     }
    00200                 }
    00201             } elseif (
    00202                 Assert::checkFloat($value)
    00203                 && ((int) $value != (float) $value)
    00204             ) {
    00205                 $packed = serialize($value);
    00206
    00207                 $flags |= 1;
    00208             } else
    00209                 $packed = $value;
    00210
    00211             $lenght = strlen($packed);
    00212
    00213             $command = "{$method} {$index} {$flags} {$expires} {$lenght}\r\n{$packed}\r\n";
    00214
    00215             if (!$this->sendRequest($command))
    00216                 return false;
    00217
    00218             $response = fread($this->link, $this->buffer);
    00219
    00220             if ($response === "STORED\r\n")
    00221                 return true;
    00222
    00223             return false;
    00224         }
    00225
    00226         private function parseGetRequest($single)
    00227         {
    00228             $result = null;
    00229             $index = 0;
    00230
    00231             while ($header = fgets($this->link, 8192)) {
    00232                 if (
    00233                     ($header === "END\r\n")
    00234                     || ($header === "ERROR\r\n")
    00235                 )
    00236                     break;
    00237
    00238                 $array = explode(' ', rtrim($header, "\r\n"), 4);
    00239
    00240                 if (count($array) <> 4)
    00241                     continue;
    00242                 else
    00243                     list(, $key, $flags, $bytes) = $array;
    00244
    00245                 if (
    00246                     is_string($key)
    00247                     && is_numeric($flags)
    00248                     && is_numeric($bytes)
    00249                 ) {
    00250                     $value = stream_get_contents($this->link, $bytes);
    00251
    00252                     if ($flags & 2)
    00253                         $value = gzuncompress($value);
    00254
    00255                     if ($single) {
    00256                         fread($this->link, 7); // skip "\r\nEND\r\n"
    00257
    00258                         if ($flags & 1)
    00259                             $value = unserialize($value);
    00260                         else
    00261                             // help in case when 100 was decreased to 99
    00262                             // memcached will not honor output lenght then
    00263                             $value = rtrim($value);
    00264
    00265                         return $value;
    00266                     } else {
    00267                         fread($this->link, 2); // skip "\r\n"
    00268
    00269                         $index++;
    00270
    00271                         if (is_numeric($key)) {
    00272                             $result .= 'i:'.$key.';';
    00273                         } else {
    00274                             $result .= 's:'.strlen($key).':"'.$key.'";';
    00275                         }
    00276
    00277                         if ($flags & 1)
    00278                             $result .= $value;
    00279                         elseif (is_numeric($value))
    00280                             $result .= 'i:'.$value.';';
    00281                         else // string
    00282                             $result .= 's:'.$bytes.':"'.$value.'";';
    00283                     }
    00284                 } else
    00285                     break;
    00286             }
    00287
    00288             if ($single)
    00289                 return $result;
    00290             else
    00291                 return 'a:'.$index.':{'.$result.'}';
    00292         }
    00293
    00294         private function changeInteger($command, $key, $value)
    00295         {
    00296             if (!$this->link)
    00297                 return null;
    00298
    00299             $command = "{$command} {$key} {$value}\r\n";
    00300
    00301             if (!$this->sendRequest($command))
    00302                 return null;
    00303
    00304             try {
    00305                 $response = rtrim(fread($this->link, $this->buffer));
    00306             } catch (BaseException $e) {
    00307                 return null;
    00308             }
    00309
    00310             if (is_numeric($response))
    00311                 return (int) $response;
    00312
    00313             return null;
    00314         }
    00315
    00316         private function sendRequest($command)
    00317         {
    00318             $commandLenght = strlen($command);
    00319
    00320             if ($commandLenght > $this->buffer) {
    00321                 $offset = 0;
    00322                 while ($offset < $commandLenght) {
    00323                     try {
    00324                         $result = fwrite(
    00325                             $this->link,
    00326                             substr($command, $offset, $this->buffer)
    00327                         );
    00328                     } catch (BaseException $e) {
    00329                         return $this->alive = false;
    00330                     }
    00331
    00332                     if ($result !== false)
    00333                         $offset += $result;
    00334                     else
    00335                         return false;
    00336                 }
    00337             } else {
    00338                 try {
    00339                     return
    00340                         fwrite($this->link, $command, $commandLenght) !== false;
    00341                 } catch (BaseException $e) {
    00342                     return $this->alive = false;
    00343                 }
    00344             }
    00345
    00346             return true;
    00347         }
    00348     }
    00349 ?>