Skip to content

Commit

Permalink
Merge pull request #96 from boesing/refactor/serialization
Browse files Browse the repository at this point in the history
Prevent double serialization when `Serializer` plugin is used
  • Loading branch information
boesing authored Jul 10, 2024
2 parents 8a1912f + d39361a commit e49d542
Showing 1 changed file with 52 additions and 8 deletions.
60 changes: 52 additions & 8 deletions src/Filesystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use Laminas\Cache\Storage\FlushableInterface;
use Laminas\Cache\Storage\IterableInterface;
use Laminas\Cache\Storage\OptimizableInterface;
use Laminas\Cache\Storage\Plugin\Serializer;
use Laminas\Cache\Storage\TaggableInterface;
use Laminas\Cache\Storage\TotalSpaceCapableInterface;
use Laminas\Stdlib\ErrorHandler;
Expand Down Expand Up @@ -84,6 +85,8 @@ final class Filesystem extends AbstractMetadataCapableAdapter implements
{
public const FILENAME_SUFFIX = 'cache';
public const TAG_FILENAME_SUFFIX = 'tag';
private const SERIALIZED_FALSE = 'b:0;';
private const SERIALIZED_NULL = 'N;';

/**
* Buffered total space in bytes
Expand Down Expand Up @@ -1087,15 +1090,13 @@ private function getCacheValue(string $file, bool $nonBlocking = false, ?bool &$
throw new Exception\RuntimeException('Malformed cache file contents.');
}

$options = $this->getOptions();
ErrorHandler::start(E_NOTICE | E_WARNING);
$cachedValue = unserialize($parts[1], ['allowed_classes' => $options->getUnserializableClasses()]);
$error = ErrorHandler::stop();
if ($error !== null) {
throw new Exception\RuntimeException('Cached value contains invalid data.', 0, $error);
$serializedCacheValue = $parts[1];

if ($this->isSerializerAttached()) {
return $serializedCacheValue;
}

return $cachedValue;
return $this->unserializeCacheValue($serializedCacheValue);
}

/**
Expand Down Expand Up @@ -1236,7 +1237,7 @@ private function getFirstLineOfFile(string $file): string
private function createCacheValue(mixed $value, int|float $ttl): string
{
$expiresAt = $this->calculateExpireTimestampBasedOnTtl($ttl);
return $expiresAt . "\n" . serialize($value);
return $expiresAt . "\n" . $this->normalizeCacheValue($value);
}

/**
Expand Down Expand Up @@ -1282,4 +1283,47 @@ private function getFileContents(string $file, bool $nonBlocking = false, ?bool

return $this->filesystem->read($file, $locking, $nonBlocking, $wouldblock);
}

private function normalizeCacheValue(mixed $value): string
{
if ($this->isSerializerAttached()) {
assert(
is_string($value),
'In case the serializer plugin is attached, the value should already be a string.',
);
return $value;
}

return serialize($value);
}

private function isSerializerAttached(): bool
{
foreach ($this->getPluginRegistry() as $plugin) {
if ($plugin instanceof Serializer) {
return true;
}
}

return false;
}

private function unserializeCacheValue(string $serializedCacheValue): mixed
{
$options = $this->getOptions();
ErrorHandler::start(E_NOTICE | E_WARNING);

$cachedValue = match ($serializedCacheValue) {
self::SERIALIZED_FALSE => false,
self::SERIALIZED_NULL => null,
default => unserialize($serializedCacheValue, ['allowed_classes' => $options->getUnserializableClasses()]),
};

$error = ErrorHandler::stop();
if ($error !== null) {
throw new Exception\RuntimeException('Cached value contains invalid data.', 0, $error);
}

return $cachedValue;
}
}

0 comments on commit e49d542

Please sign in to comment.