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 ?>