1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Twig\Cache;
13
14/**
15 * Implements a cache on the filesystem.
16 *
17 * @author Andrew Tch <andrew@noop.lv>
18 */
19class FilesystemCache implements CacheInterface
20{
21    public const FORCE_BYTECODE_INVALIDATION = 1;
22
23    private $directory;
24    private $options;
25
26    /**
27     * @param string $directory The root cache directory
28     * @param int    $options   A set of options
29     */
30    public function __construct($directory, $options = 0)
31    {
32        $this->directory = rtrim($directory, '\/').'/';
33        $this->options = $options;
34    }
35
36    public function generateKey($name, $className)
37    {
38        $hash = hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $className);
39
40        return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
41    }
42
43    public function load($key)
44    {
45        if (file_exists($key)) {
46            @include_once $key;
47        }
48    }
49
50    public function write($key, $content)
51    {
52        $dir = \dirname($key);
53        if (!is_dir($dir)) {
54            if (false === @mkdir($dir, 0777, true)) {
55                clearstatcache(true, $dir);
56                if (!is_dir($dir)) {
57                    throw new \RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir));
58                }
59            }
60        } elseif (!is_writable($dir)) {
61            throw new \RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir));
62        }
63
64        $tmpFile = tempnam($dir, basename($key));
65        if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $key)) {
66            @chmod($key, 0666 & ~umask());
67
68            if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) {
69                // Compile cached file into bytecode cache
70                if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) {
71                    @opcache_invalidate($key, true);
72                } elseif (\function_exists('apc_compile_file')) {
73                    apc_compile_file($key);
74                }
75            }
76
77            return;
78        }
79
80        throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $key));
81    }
82
83    public function getTimestamp($key)
84    {
85        if (!file_exists($key)) {
86            return 0;
87        }
88
89        return (int) @filemtime($key);
90    }
91}
92
93class_alias('Twig\Cache\FilesystemCache', 'Twig_Cache_Filesystem');
94