00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
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
00102
00103
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
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);
00257
00258 if ($flags & 1)
00259 $value = unserialize($value);
00260 else
00261
00262
00263 $value = rtrim($value);
00264
00265 return $value;
00266 } else {
00267 fread($this->link, 2);
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
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 ?>