# RubberFileSystem.class.php

Go to the documentation of this file.

    00001 <?php
    00002 /***************************************************************************
    00003  *   Copyright (C) 2005-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  ***************************************************************************/
    00011
    00017     final class RubberFileSystem extends CachePeer
    00018     {
    00019         private $directory  = null;
    00020
    00024         public static function create($directory = 'cache/')
    00025         {
    00026             return new self($directory);
    00027         }
    00028
    00029         public function __construct($directory = 'cache/')
    00030         {
    00031             $directory = ONPHP_TEMP_PATH.$directory;
    00032
    00033             if (!is_writable($directory)) {
    00034                 if (!mkdir($directory, 0700, true)) {
    00035                     throw new WrongArgumentException(
    00036                         "can not write to '{$directory}'"
    00037                     );
    00038                 }
    00039             }
    00040
    00041             if ($directory[strlen($directory) - 1] != DIRECTORY_SEPARATOR)
    00042                 $directory .= DIRECTORY_SEPARATOR;
    00043
    00044             $this->directory = $directory;
    00045         }
    00046
    00047         public function isAlive()
    00048         {
    00049             if (!is_writable($this->directory))
    00050                 return mkdir($this->directory, 0700, true);
    00051             else
    00052                 return true;
    00053         }
    00054
    00058         public function clean()
    00059         {
    00060             // just to return 'true'
    00061             FileUtils::removeDirectory($this->directory, true);
    00062
    00063             return parent::clean();
    00064         }
    00065
    00066         public function increment($key, $value)
    00067         {
    00068             $path = $this->makePath($key);
    00069
    00070             if (null !== ($current = $this->operate($path))) {
    00071                 $this->operate($path, $current += $value);
    00072
    00073                 return $current;
    00074             }
    00075
    00076             return null;
    00077         }
    00078
    00079         public function decrement($key, $value)
    00080         {
    00081             $path = $this->makePath($key);
    00082
    00083             if (null !== ($current = $this->operate($path))) {
    00084                 $this->operate($path, $current -= $value);
    00085
    00086                 return $current;
    00087             }
    00088
    00089             return null;
    00090         }
    00091
    00092         public function get($key)
    00093         {
    00094             $path = $this->makePath($key);
    00095
    00096             if (is_readable($path)) {
    00097
    00098                 if (filemtime($path) <= time()) {
    00099                     try {
    00100                         unlink($path);
    00101                     } catch (BaseException $e) {
    00102                         // we're in race with unexpected clean()
    00103                     }
    00104                     return null;
    00105                 }
    00106
    00107                 return $this->operate($path);
    00108             }
    00109
    00110             return null;
    00111         }
    00112
    00113         public function delete($key)
    00114         {
    00115             try {
    00116                 unlink($this->makePath($key));
    00117             } catch (BaseException $e) {
    00118                 return false;
    00119             }
    00120
    00121             return true;
    00122         }
    00123
    00124         public function append($key, $data)
    00125         {
    00126             $path = $this->makePath($key);
    00127
    00128             $directory = dirname($path);
    00129
    00130             if (!file_exists($directory)) {
    00131                 try {
    00132                     mkdir($directory);
    00133                 } catch (BaseException $e) {
    00134                     // we're in race
    00135                 }
    00136             }
    00137
    00138             if (!is_writable($path))
    00139                 return false;
    00140
    00141             try {
    00142                 $fp = fopen($path, 'ab');
    00143             } catch (BaseException $e) {
    00144                 return false;
    00145             }
    00146
    00147             fwrite($fp, $data);
    00148
    00149             fclose($fp);
    00150
    00151             return true;
    00152         }
    00153
    00154         protected function store($action, $key, $value, $expires = 0)
    00155         {
    00156             $path = $this->makePath($key);
    00157             $time = time();
    00158
    00159             $directory = dirname($path);
    00160
    00161             if (!file_exists($directory)) {
    00162                 try {
    00163                     mkdir($directory);
    00164                 } catch (BaseException $e) {
    00165                     // we're in race
    00166                 }
    00167             }
    00168
    00169             // do not add, if file exist and not expired
    00170             if (
    00171                 $action == 'add'
    00172                 && is_readable($path)
    00173                 && filemtime($path) > $time
    00174             )
    00175                 return true;
    00176
    00177             // do not replace, when file not exist or expired
    00178             if ($action == 'replace') {
    00179
    00180                 if (!is_readable($path)) {
    00181                     return false;
    00182                 } elseif (filemtime($path) <= $time) {
    00183                     $this->delete($key);
    00184                     return false;
    00185                 }
    00186             }
    00187
    00188             $this->operate($path, $value, $expires);
    00189
    00190             return true;
    00191         }
    00192
    00193         private function operate($path, $value = null, $expires = null)
    00194         {
    00195             $key = hexdec(substr(md5($path), 3, 2)) + 1;
    00196
    00197             $pool = SemaphorePool::me();
    00198
    00199             if (!$pool->get($key))
    00200                 return null;
    00201
    00202             try {
    00203                 $old = umask(0077);
    00204                 $fp = fopen($path, $value !== null ? 'wb' : 'rb');
    00205                 umask($old);
    00206             } catch (BaseException $e) {
    00207                 $pool->drop($key);
    00208                 return null;
    00209             }
    00210
    00211             if ($value !== null) {
    00212                 fwrite($fp, $this->prepareData($value));
    00213                 fclose($fp);
    00214
    00215                 if ($expires < parent::TIME_SWITCH)
    00216                     $expires += time();
    00217
    00218                 try {
    00219                     touch($path, $expires);
    00220                 } catch (BaseException $e) {
    00221                     // race-removed
    00222                 }
    00223
    00224                 return $pool->drop($key);
    00225             } else {
    00226                 if (($size = filesize($path)) > 0)
    00227                     $data = fread($fp, $size);
    00228                 else
    00229                     $data = null;
    00230
    00231                 fclose($fp);
    00232
    00233                 $pool->drop($key);
    00234
    00235                 return $data ? $this->restoreData($data) : null;
    00236             }
    00237
    00238             Assert::isUnreachable();
    00239         }
    00240
    00241         private function makePath($key)
    00242         {
    00243             return
    00244                 $this->directory
    00245                 .$key[0].$key[1]
    00246                 .DIRECTORY_SEPARATOR
    00247                 .substr($key, 2);
    00248         }
    00249     }
    00250 ?>