Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge branch 'feature/120-custom-file-suffixes' into develop
Browse files Browse the repository at this point in the history
Close #120
  • Loading branch information
weierophinney committed Apr 17, 2018
2 parents 50a756e + f5c79a9 commit fa4d311
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 46 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ All notable changes to this project will be documented in this file, in reverse

### Added

- [#120](https://github.com/zendframework/zend-cache/pull/120) adds the ability to configure alternate file suffixes for both
cache and tag cache files within the Filesystem adapter. Use the `suffix` and `tag_suffix`
options to set them; they will default to `dat` and `tag`, respectively.

- [#116](https://github.com/zendframework/zend-cache/pull/116)
docblock method chaining consistency
- [#46](https://github.com/zendframework/zend-cache/issues/46)
Add wrapper for PSR-6
- [#79](https://github.com/zendframework/zend-cache/issues/79)
Add capability for the "lock-on-expire" feature (úsed by Zend Data Cache)

### Changed

- Nothing.

### Deprecated

- [#101](https://github.com/zendframework/zend-cache/pull/101)
Expand Down
5 changes: 5 additions & 0 deletions doc/book/storage/adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,11 @@ Name | Data Type | Default Value | Description
`no_atime` | `boolean` | `true` | Don’t get ‘fileatime’ as ‘atime’ on metadata.
`no_ctime` | `boolean` | `true` | Don’t get ‘filectime’ as ‘ctime’ on metadata.
`umask` | `integer|false` | `false` | Use [umask](http://wikipedia.org/wiki/Umask) to set file and directory permissions.
`suffix` | `string` | `dat` | Suffix for cache files
`tag_suffix` | `string` | `tag` | Suffix for tag files

Note: the `suffix` and `tag_suffix` options will be escaped in order to be safe
for glob operations.

## The Memcached Adapter

Expand Down
120 changes: 84 additions & 36 deletions src/Storage/Adapter/Filesystem.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Cache\Storage\Adapter;
Expand Down Expand Up @@ -155,7 +153,8 @@ public function clearExpired()
$flags = GlobIterator::SKIP_DOTS | GlobIterator::CURRENT_AS_PATHNAME;
$path = $options->getCacheDir()
. str_repeat(DIRECTORY_SEPARATOR . $prefix . '*', $options->getDirLevel())
. DIRECTORY_SEPARATOR . $prefix . '*.dat';
. DIRECTORY_SEPARATOR . $prefix
. '*.' . $this->escapeSuffixForGlob($this->getOptions()->getSuffix());
$glob = new GlobIterator($path, $flags);
$time = time();
$ttl = $options->getTtl();
Expand All @@ -178,7 +177,7 @@ public function clearExpired()
if ($err && file_exists($pathname)) {
ErrorHandler::addError($err->getSeverity(), $err->getMessage(), $err->getFile(), $err->getLine());
} else {
$tagPathname = substr($pathname, 0, -4) . '.tag';
$tagPathname = $this->formatTagFilename(substr($pathname, 0, -4));
ErrorHandler::start();
unlink($tagPathname);
$err = ErrorHandler::stop();
Expand Down Expand Up @@ -328,11 +327,14 @@ public function setTags($key, array $tags)
$filespec = $this->getFileSpec($key);

if (! $tags) {
$this->unlink($filespec . '.tag');
$this->unlink($this->formatTagFilename($filespac));
return true;
}

$this->putFileContent($filespec . '.tag', implode("\n", $tags));
$this->putFileContent(
$this->formatTagFilename($filespec),
implode("\n", $tags)
);
return true;
}

Expand All @@ -349,10 +351,10 @@ public function getTags($key)
return false;
}

$filespec = $this->getFileSpec($key);
$filespec = $this->formatTagFilename($this->getFileSpec($key));
$tags = [];
if (file_exists($filespec . '.tag')) {
$tags = explode("\n", $this->getFileContent($filespec . '.tag'));
if (file_exists($filespec)) {
$tags = explode("\n", $this->getFileContent($filespec));
}

return $tags;
Expand Down Expand Up @@ -382,7 +384,8 @@ public function clearByTags(array $tags, $disjunction = false)
$flags = GlobIterator::SKIP_DOTS | GlobIterator::CURRENT_AS_PATHNAME;
$path = $options->getCacheDir()
. str_repeat(DIRECTORY_SEPARATOR . $prefix . '*', $options->getDirLevel())
. DIRECTORY_SEPARATOR . $prefix . '*.tag';
. DIRECTORY_SEPARATOR . $prefix
. '*.' . $this->escapeSuffixForGlob($this->getOptions()->getTagSuffix());
$glob = new GlobIterator($path, $flags);

foreach ($glob as $pathname) {
Expand All @@ -407,7 +410,7 @@ public function clearByTags(array $tags, $disjunction = false)
if ($rem) {
unlink($pathname);

$datPathname = substr($pathname, 0, -4) . '.dat';
$datPathname = $this->formatFilename(substr($pathname, 0, -4));
if (file_exists($datPathname)) {
unlink($datPathname);
}
Expand All @@ -431,7 +434,8 @@ public function getIterator()
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$path = $options->getCacheDir()
. str_repeat(DIRECTORY_SEPARATOR . $prefix . '*', $options->getDirLevel())
. DIRECTORY_SEPARATOR . $prefix . '*.dat';
. DIRECTORY_SEPARATOR . $prefix
. '*.' . $this->escapeSuffixForGlob($this->getOptions()->getSuffix());
return new FilesystemIterator($this, $path, $prefix);
}

Expand Down Expand Up @@ -587,12 +591,12 @@ protected function internalGetItem(& $normalizedKey, & $success = null, & $casTo
}

try {
$filespec = $this->getFileSpec($normalizedKey);
$data = $this->getFileContent($filespec . '.dat');
$filespec = $this->formatFilename($this->getFileSpec($normalizedKey));
$data = $this->getFileContent($filespec);

// use filemtime + filesize as CAS token
if (func_num_args() > 2) {
$casToken = filemtime($filespec . '.dat') . filesize($filespec . '.dat');
$casToken = filemtime($filespec) . filesize($filespec);
}
$success = true;
return $data;
Expand Down Expand Up @@ -625,8 +629,8 @@ protected function internalGetItems(array & $normalizedKeys)
continue;
}

$filespec = $this->getFileSpec($key);
$data = $this->getFileContent($filespec . '.dat', $nonBlocking, $wouldblock);
$filespec = $this->formatFilename($this->getFileSpec($key));
$data = $this->getFileContent($filespec, $nonBlocking, $wouldblock);
if ($nonBlocking && $wouldblock) {
continue;
} else {
Expand Down Expand Up @@ -694,7 +698,7 @@ public function hasItems(array $keys)
*/
protected function internalHasItem(& $normalizedKey)
{
$file = $this->getFileSpec($normalizedKey) . '.dat';
$file = $this->formatFilename($this->getFileSpec($normalizedKey));
if (! file_exists($file)) {
return false;
}
Expand Down Expand Up @@ -763,7 +767,7 @@ protected function internalGetMetadata(& $normalizedKey)

$options = $this->getOptions();
$filespec = $this->getFileSpec($normalizedKey);
$file = $filespec . '.dat';
$file = $this->formatFilename($filespec);

$metadata = [
'filespec' => $filespec,
Expand Down Expand Up @@ -795,7 +799,7 @@ protected function internalGetMetadatas(array & $normalizedKeys)

foreach ($normalizedKeys as $normalizedKey) {
$filespec = $this->getFileSpec($normalizedKey);
$file = $filespec . '.dat';
$file = $this->formatFilename($filespec);

$metadata = [
'filespec' => $filespec,
Expand Down Expand Up @@ -957,18 +961,19 @@ public function replaceItems(array $keyValuePairs)
protected function internalSetItem(& $normalizedKey, & $value)
{
$filespec = $this->getFileSpec($normalizedKey);
$file = $this->formatFilename($filespec);
$this->prepareDirectoryStructure($filespec);

// write data in non-blocking mode
$wouldblock = null;
$this->putFileContent($filespec . '.dat', $value, true, $wouldblock);
$this->putFileContent($file, $value, true, $wouldblock);

// delete related tag file (if present)
$this->unlink($filespec . '.tag');
$this->unlink($this->formatTagFilename($filespec));

// Retry writing data in blocking mode if it was blocked before
if ($wouldblock) {
$this->putFileContent($filespec . '.dat', $value);
$this->putFileContent($file, $value);
}

return true;
Expand All @@ -990,10 +995,10 @@ protected function internalSetItems(array & $normalizedKeyValuePairs)
$this->prepareDirectoryStructure($filespec);

// *.dat file
$contents[$filespec . '.dat'] = & $value;
$contents[$this->formatFilename($filespec)] = & $value;

// *.tag file
$this->unlink($filespec . '.tag');
$this->unlink($this->formatTagFilename($filespec));
}

// write to disk
Expand Down Expand Up @@ -1055,7 +1060,7 @@ protected function internalCheckAndSetItem(& $token, & $normalizedKey, & $value)
}

// use filemtime + filesize as CAS token
$file = $this->getFileSpec($normalizedKey) . '.dat';
$file = $this->formatFilename($this->getFileSpec($normalizedKey));
$check = filemtime($file) . filesize($file);
if ($token !== $check) {
return false;
Expand Down Expand Up @@ -1120,12 +1125,13 @@ protected function internalTouchItem(& $normalizedKey)
}

$filespec = $this->getFileSpec($normalizedKey);
$file = $this->formatFilename($filespec);

ErrorHandler::start();
$touch = touch($filespec . '.dat');
$touch = touch($file);
$error = ErrorHandler::stop();
if (! $touch) {
throw new Exception\RuntimeException("Error touching file '{$filespec}.dat'", 0, $error);
throw new Exception\RuntimeException("Error touching file '{$file}'", 0, $error);
}

return true;
Expand Down Expand Up @@ -1183,12 +1189,13 @@ public function removeItems(array $keys)
protected function internalRemoveItem(& $normalizedKey)
{
$filespec = $this->getFileSpec($normalizedKey);
if (! file_exists($filespec . '.dat')) {
$file = $this->formatFilename($filespec);
if (! file_exists($file)) {
return false;
} else {
$this->unlink($filespec . '.dat');
$this->unlink($filespec . '.tag');
}

$this->unlink($file);
$this->unlink($this->formatTagFilename($filespec));
return true;
}

Expand All @@ -1214,6 +1221,12 @@ protected function internalGetCapabilities()
$metadata[] = 'ctime';
}

// Calculate max key length: 255 - strlen(.dat | .tag)
$maxKeyLength = 254 - max([
strlen($this->getOptions()->getSuffix()),
strlen($this->getOptions()->getTagSuffix()),
]);

$capabilities = new Capabilities(
$this,
$marker,
Expand All @@ -1233,7 +1246,7 @@ protected function internalGetCapabilities()
'maxTtl' => 0,
'staticTtl' => false,
'ttlPrecision' => 1,
'maxKeyLength' => 251, // 255 - strlen(.dat | .tag)
'maxKeyLength' => $maxKeyLength,
'namespaceIsPrefix' => true,
'namespaceSeparator' => $options->getNamespaceSeparator(),
]
Expand Down Expand Up @@ -1653,4 +1666,39 @@ protected function unlink($file)
);
}
}

/**
* Formats the filename, appending the suffix option
*
* @param string $filename
* @return string
*/
private function formatFilename($filename)
{
return sprintf('%s.%s', $filename, $this->getOptions()->getSuffix());
}

/**
* Formats the filename, appending the tag suffix option
*
* @param string $filename
* @return string
*/
private function formatTagFilename($filename)
{
return sprintf('%s.%s', $filename, $this->getOptions()->getTagSuffix());
}

/**
* Escapes a filename suffix to be safe for glob operations
*
* Wraps any of *, ?, or [ characters within [] brackets.
*
* @param string $suffix
* @return string
*/
private function escapeSuffixForGlob($suffix)
{
return preg_replace('#([*?\[])#', '[$1]', $suffix);
}
}
Loading

0 comments on commit fa4d311

Please sign in to comment.