<?php /* * This file is part of the Cache package. * * Copyright (c) Daniel González * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @author Daniel González <daniel@desarrolla2.com> * @author Arnold Daniels <arnold@jasny.net> */ declare(strict_types=1); namespace Desarrolla2\Cache; use Desarrolla2\Cache\Exception\InvalidArgumentException; use Desarrolla2\Cache\Exception\UnexpectedValueException; use Desarrolla2\Cache\Packer\PackerInterface; use Desarrolla2\Cache\Packer\SerializePacker; /** * Cache file. */ class File extends AbstractFile { /** * @var string 'embed', 'file', 'mtime' */ protected $ttlStrategy = 'embed'; /** * Create the default packer for this cache implementation * * @return PackerInterface */ protected static function createDefaultPacker(): PackerInterface { return new SerializePacker(); } /** * Set TTL strategy * * @param string $strategy */ protected function setTtlStrategyOption($strategy) { if (!in_array($strategy, ['embed', 'file', 'mtime'])) { throw new InvalidArgumentException("Unknown strategy '$strategy', should be 'embed', 'file' or 'mtime'"); } $this->ttlStrategy = $strategy; } /** * Get TTL strategy * * @return string */ protected function getTtlStrategyOption(): string { return $this->ttlStrategy; } /** * Get the TTL using one of the strategies * * @param string $cacheFile * @return int */ protected function getTtl(string $cacheFile) { switch ($this->ttlStrategy) { case 'embed': return (int)$this->readLine($cacheFile); case 'file': return file_exists("$cacheFile.ttl") ? (int)file_get_contents("$cacheFile.ttl") : PHP_INT_MAX; case 'mtime': return $this->getTtl($cacheFile) > 0 ? filemtime($cacheFile) + $this->ttl : PHP_INT_MAX; } throw new \InvalidArgumentException("Invalid TTL strategy '{$this->ttlStrategy}'"); } /** * Set the TTL using one of the strategies * * @param int|null $expiration * @param string $contents * @param string $cacheFile * @return string The (modified) contents */ protected function setTtl($expiration, $contents, $cacheFile) { switch ($this->ttlStrategy) { case 'embed': $contents = ($expiration ?? PHP_INT_MAX) . "\n" . $contents; break; case 'file': if ($expiration !== null) { file_put_contents("$cacheFile.ttl", $expiration); } break; case 'mtime': // nothing break; } return $contents; } /** * {@inheritdoc} */ public function get($key, $default = null) { if (!$this->has($key)) { return $default; } $cacheFile = $this->getFilename($key); $packed = $this->readFile($cacheFile); if ($this->ttlStrategy === 'embed') { $packed = substr($packed, strpos($packed, "\n") + 1); } return $this->unpack($packed); } /** * {@inheritdoc} */ public function has($key) { $cacheFile = $this->getFilename($key); if (!file_exists($cacheFile)) { return false; } $ttl = $this->getTtl($cacheFile); if ($ttl <= time()) { $this->deleteFile($cacheFile); return false; } return true; } /** * {@inheritdoc} */ public function set($key, $value, $ttl = null) { $cacheFile = $this->getFilename($key); $packed = $this->pack($value); if (!is_string($packed)) { throw new UnexpectedValueException("Packer must create a string for the data to be cached to file"); } $contents = $this->setTtl($this->ttlToTimestamp($ttl), $packed, $cacheFile); return $this->writeFile($cacheFile, $contents); } }