From 11624f512bddf0075e7d7b75bc2621fcb86c4fb4 Mon Sep 17 00:00:00 2001 From: Dominique Fournier Date: Fri, 25 Jul 2014 09:59:33 +0000 Subject: [PATCH] First version of cacheFile with associated tests git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@1550 bf3deb0d-5f1a-0410-827f-c0cc1f45334c --- Tests/cacheFileTest.php | 82 +++++++++++++++++++++++ cacheFile.php | 143 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 Tests/cacheFileTest.php create mode 100644 cacheFile.php diff --git a/Tests/cacheFileTest.php b/Tests/cacheFileTest.php new file mode 100644 index 0000000..ad5648c --- /dev/null +++ b/Tests/cacheFileTest.php @@ -0,0 +1,82 @@ + */ + +/** Test the cache.php file */ +class test_cacheFile extends PHPUnit_Framework_TestCase +{ + public function __construct () + { + // Removing the cache file if it previously exists + $c = new cacheFile (); + $res = $c->delete ("id"); + } + + // Unknown cache file : return FALSE + public function testRead1 () + { + $c = new cacheFile (); + $res = $c->read ("id"); + $this->assertFalse ($res); + } + + // Write in cache file + public function testWrite1 () + { + $c = new cacheFile (); + $res = $c->write ("id","DATA_TO_STORE", 3); + $this->assertTrue ($res); + } + + // Previous cache file : return DATA_TO_STORE + public function testRead2 () + { + $c = new cacheFile (); + $res = $c->read ("id"); + $this->assertEquals ("DATA_TO_STORE", $res); + } + + // Sleep 3s to expire the cache + public function testWait1 () + { + sleep (4); + } + + // Previous cache file but expired : return false + public function testRead3 () + { + $c = new cacheFile (); + $res = $c->read ("id"); + $this->assertFalse ($res); + } + + // Write in cache file + public function testWrite2 () + { + $c = new cacheFile (); + $res = $c->write ("id","DATA_TO_STORE", 30); + $this->assertTrue ($res); + } + + // Create stale lock + public function testLock1 () + { + touch ("cache/".sha1 ("id").".lock"); + } + + // Previous cache in time file but lock : return content after lock timeout + public function testRead4 () + { + $c = new cacheFile (); + $res = $c->read ("id"); + $this->assertEquals ("DATA_TO_STORE", $res); + } + + public function testDel1 () + { + $c = new cacheFile (); + $res = $c->delete ("id"); + } +} + diff --git a/cacheFile.php b/cacheFile.php new file mode 100644 index 0000000..9180b5d --- /dev/null +++ b/cacheFile.php @@ -0,0 +1,143 @@ +directory) || $this->directory === "") + throw new Exception (_("No cache directory defined"), 500); + if (! file_exists ($this->directory)) + { + // Need to create the cache dir + $parent = realpath (dirname ($this->directory)); + if (! is_writeable (dirname ($this->directory))) + throw new Exception (sprintf (_( + "Directory %s is not writable : can not create cache directory"), + $parent), 500); + if (!mkdir ($this->directory)) + throw new Exception (sprintf (_("Can not create cache directory %s"), + $this->directory), 500); + file_put_contents ($this->directory."/.htaccess", "deny from all\n"); + } + if (! is_writable ($this->directory)) + throw new Exception (sprintf (_("Cache directory %s is not writable"), + $this->directory), 500); + if (! is_readable ($this->directory)) + throw new Exception (sprintf (_("Cache directory %s is not readable"), + $this->directory), 500); + return true; + } + + /** This function write data in cache + The cache system can not save FALSE value + @param string $id Cache identifier (add the authentication, the METHOD...) + @param string $data The data to save + @param integer|null $ttl The cache Time to Leave in seconds (3600s by + default)*/ + public function write ($id, $data, $ttl = 3600) + { + if ($this->nocache !== false) + return false; + if ($data === false) + throw new Exception (_("Can not store FALSE in cache"), 500); + try + { + $this->cachedir (); + } + catch (Exception $e) + { + throw new Exception ($e->getMessage(), $e->getCode()); + } + + $fileCache = $this->directory."/".sha1 ($id); + touch ($fileCache.".lock"); + $datas = array ("ttl"=>$ttl, + "createTime"=>time(), + "data"=>$data); + file_put_contents ($fileCache, serialize ($datas)); + unlink ($fileCache.".lock"); + return true; + } + + /** This function read data from cache. Return FALSE in case of empty or too + older cache + @param string $id Cache identifier (add the authentication, the METHOD) */ + public function read ($id) + { + if ($this->nocache !== false) + return false; + try + { + $this->cachedir (); + } + catch (Exception $e) + { + throw new Exception ($e->getMessage(), $e->getCode()); + } + + $fileCache = $this->directory."/".sha1 ($id); + if (!file_exists ($fileCache)) + return false; + if (!is_readable ($fileCache)) + throw new Exception (sprintf (_("File cache %s is not readable"), + $fileCache), 500); + if (!is_writable ($fileCache)) + throw new Exception (sprintf (_("File cache %s is not writable"), + $fileCache), 500); + // Lock : waiting the reconstruction of the cache by another process + // Waiting 10s maximum to re-create the cache file + $startTime = time (); + while (file_exists ($fileCache.".lock") && time () < $startTime + 10) + { + usleep (100000); + } + + // The lock is pending 10s (stale) : removing it to read the datas quicker + // next time + if (file_exists ($fileCache.".lock")) + unlink ($fileCache.".lock"); + + $datas = file_get_contents ($fileCache); + if ($datas === false) + { + unlink ($fileCache); + return false; + } + + $datas = unserialize ($datas); + if (! isset ($datas["ttl"]) || ! isset ($datas["data"]) || + ! isset ($datas["createTime"])) + { + unlink ($fileCache); + return false; + } + + if (($datas["createTime"] + $datas["ttl"]) >= time ()) + { + if (file_exists ($fileCache.".lock")) + unlink ($fileCache.".lock"); + return $datas["data"]; + } + + return false; + } + + /** This function delete an id in cache + @param string $id Cache identifier (add the authentication, the METHOD...) + */ + public function delete ($id) + { + $fileCache = $this->directory."/".sha1 ($id); + if (!file_exists ($fileCache)) + return false; + unlink ($fileCache); + return true; + } +}