diff --git a/src/Illuminate/Cache/FileStore.php b/src/Illuminate/Cache/FileStore.php index 4ce8111b125f..2f72d978b630 100755 --- a/src/Illuminate/Cache/FileStore.php +++ b/src/Illuminate/Cache/FileStore.php @@ -5,6 +5,7 @@ use Exception; use Illuminate\Contracts\Cache\LockProvider; use Illuminate\Contracts\Cache\Store; +use Illuminate\Contracts\Filesystem\LockTimeoutException; use Illuminate\Filesystem\Filesystem; use Illuminate\Support\InteractsWithTime; @@ -84,6 +85,45 @@ public function put($key, $value, $seconds) return false; } + /** + * Store an item in the cache if the key doesn't exist. + * + * @param string $key + * @param mixed $value + * @param int $seconds + * @return bool + */ + public function add($key, $value, $seconds) + { + $this->ensureCacheDirectoryExists($path = $this->path($key)); + + $file = $this->files->newFileForReadWrite($path); + + try { + $file->getExclusiveLock(); + } catch (LockTimeoutException $e) { + $file->close(); + + return false; + } + + $expire = $file->read(10); + + if (empty($expire) || $this->currentTime() >= $expire) { + $file->truncate() + ->write($this->expiration($seconds).serialize($value)) + ->close(); + + $this->ensureFileHasCorrectPermissions($path); + + return true; + } + + $file->close(); + + return false; + } + /** * Create the file cache directory if necessary. * diff --git a/src/Illuminate/Contracts/Filesystem/LockTimeoutException.php b/src/Illuminate/Contracts/Filesystem/LockTimeoutException.php new file mode 100644 index 000000000000..f03f5c4e6c1d --- /dev/null +++ b/src/Illuminate/Contracts/Filesystem/LockTimeoutException.php @@ -0,0 +1,10 @@ +files = $files; + $this->path = $path; + + $this->ensureDirectoryExists($path); + $this->createResource($path, $mode); + } + + /** + * Read the file contents. + * + * @param int|null $length + * @return string + */ + public function read($length = null) + { + clearstatcache(true, $this->path); + + return fread($this->handle, $length ?? ($this->size() ?: 1)); + } + + /** + * Write to the file. + * + * @param string $contents + * @return string + */ + public function write($contents) + { + fwrite($this->handle, $contents); + + fflush($this->handle); + + return $this; + } + + /** + * Truncate the file. + * + * @return $this + */ + public function truncate() + { + rewind($this->handle); + + ftruncate($this->handle, 0); + + return $this; + } + + /** + * Get a shared lock on the file. + * + * @return $this + */ + public function getSharedLock($block = false) + { + if (! flock($this->handle, LOCK_SH | ($block ? 0 : LOCK_NB))) { + throw new LockTimeoutException("Unable to acquire file lock at path {$path}."); + } + + $this->isLocked = true; + + return $this; + } + + /** + * Get an exclusive lock on the file. + * + * @return bool + */ + public function getExclusiveLock($block = false) + { + if (! flock($this->handle, LOCK_EX | ($block ? 0 : LOCK_NB))) { + throw new LockTimeoutException("Unable to acquire file lock at path {$path}."); + } + + $this->isLocked = true; + + return $this; + } + + /** + * Release the lock on the file. + * + * @return $this + */ + public function releaseLock() + { + flock($this->handle, LOCK_UN); + + $this->isLocked = false; + + return $this; + } + + /** + * Close the file. + * + * @return bool + */ + public function close() + { + if ($this->isLocked) { + $this->releaseLock(); + } + + return fclose($this->handle); + } + + /** + * Get the file size. + * + * @return int + */ + public function size() + { + return filesize($this->path); + } + + /** + * Create the file resource. + * + * @return void + */ + protected function createResource($path, $mode) + { + $this->handle = @fopen($path, $mode); + } + + /** + * Create the file directory if necessary. + * + * @param string $path + * @return void + */ + protected function ensureDirectoryExists($path) + { + if (! $this->files->exists(dirname($path))) { + $this->files->makeDirectory(dirname($path), 0777, true, true); + } + } +} diff --git a/src/Illuminate/Filesystem/Filesystem.php b/src/Illuminate/Filesystem/Filesystem.php index 2076f4ffe93e..e977a5f63197 100644 --- a/src/Illuminate/Filesystem/Filesystem.php +++ b/src/Illuminate/Filesystem/Filesystem.php @@ -185,6 +185,26 @@ public function put($path, $contents, $lock = false) return file_put_contents($path, $contents, $lock ? LOCK_EX : 0); } + /** + * Create a new File instance for read and write. + * + * @return \Illuminate\Filesystem\File + */ + public function newFileForReadWrite($path) + { + return new File($this, $path, 'c+'); + } + + /** + * Create a new File instance for read and write. + * + * @return \Illuminate\Filesystem\File + */ + public function newFileForRead($path) + { + return new File($this, $path, 'r'); + } + /** * Write the contents of a file, replacing it atomically if it already exists. *