1<?php
2namespace DALMP\Cache;
3
4/**
5 * Disk
6 *
7 * @author Nicolas Embriz <nbari@dalmp.com>
8 * @package DALMP
9 * @license BSD License
10 * @version 3.0.3
11 */
12class Disk implements CacheInterface
13{
14    public $cache_dir;
15
16    /**
17     * Constructor
18     *
19     * @param string $dir
20     */
21    public function __construct()
22    {
23        $args = func_get_args();
24
25        if (!$args || !isset($args[0])) {
26            $cache_dir = defined('DALMP_CACHE_DIR') ? DALMP_CACHE_DIR : '/tmp/dalmp_cache';
27        } else {
28            $cache_dir = $args[0];
29        }
30
31        if (!is_writable($cache_dir)) {
32            if (!is_dir($cache_dir) && !mkdir($cache_dir, 0700, true)) {
33                throw new \InvalidArgumentException("$cache_dir  not accessible");
34            }
35        }
36
37        $this->cache_dir = $cache_dir;
38    }
39
40    /**
41     * Store data at the server
42     *
43     * @param string $key
44     * @param string $value
45     * @param int    $expire time in seconds(default is 2592000 '30 days')
46     */
47    public function set($key, $value, $expire = 2592000)
48    {
49        $key = sha1($key);
50        $expire = ($expire == 0) ? 2592000 : $expire;
51
52        $cache_path = sprintf('%s/%s/%s/%s', $this->cache_dir, substr($key, 0, 2), substr($key, 2, 2),  substr($key, 4, 2));
53
54        if (!file_exists($cache_path)) {
55            if (!mkdir($cache_path, 0750, true)) {
56                throw new \Exception("Can't create cache directory tree : $cache_path");
57            }
58        }
59
60        $cache_file = sprintf('%s/%s', $cache_path, "dalmp_{$key}.cache");
61
62        if (!($fp = fopen($cache_file, 'w'))) {
63            throw new \Exception(__METHOD__ . ": Cannot create cache file $cache_file");
64        }
65
66        if (flock($fp, LOCK_EX) && ftruncate($fp, 0)) {
67            if (fwrite($fp, serialize($value))) {
68                flock($fp, LOCK_UN);
69                fclose($fp);
70                $time = time() + (int) $expire;
71
72                return touch($cache_file, $time);
73            } else {
74                return false;
75            }
76        } else {
77            throw new \Exception(__METHOD__ . ": Cannot lock/truncate the cache file: $cache_file");
78        }
79    }
80
81    /**
82     * Retrieve item from the server
83     *
84     * @param string $key
85     */
86    public function Get($key)
87    {
88        $key = sha1($key);
89
90        $cache_file = sprintf('%s/%s/%s/%s/%s', $this->cache_dir, substr($key, 0, 2), substr($key, 2, 2),  substr($key, 4, 2), "dalmp_{$key}.cache");
91
92        if (file_exists($cache_file)) {
93            if (filemtime($cache_file) > time()) {
94                return unserialize(file_get_contents($cache_file));
95            } else {
96                unlink($cache_file);
97
98                return false;
99            }
100        } else {
101            return false;
102        }
103    }
104
105    /**
106     * Delete item from the server
107     *
108     * @param string $key
109     */
110    public function Delete($key)
111    {
112        $key = sha1($key);
113        $cache_file = sprintf('%s/%s/%s/%s/%s', $this->cache_dir, substr($key, 0, 2), substr($key, 2, 2),  substr($key, 4, 2), "dalmp_{$key}.cache");
114
115        return file_exists($cache_file) ? unlink($cache_file) : true;
116    }
117
118    /**
119     * Flush cache
120     */
121    public function Flush()
122    {
123        $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->cache_dir, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST);
124
125        foreach ($files as $fileinfo) {
126            if ($fileinfo->isDir()) {
127                rmdir($fileinfo->getRealPath());
128            } else {
129                unlink($fileinfo->getRealPath());
130            }
131        }
132
133        return rmdir($this->cache_dir);
134    }
135
136    /**
137     * Get cache stats
138     */
139    public function Stats()
140    {
141        $total_bytes = 0;
142        $total_files = 0;
143
144        $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->cache_dir, \RecursiveDirectoryIterator::SKIP_DOTS));
145        foreach ($files as $fileinfo) {
146            $total_bytes += $fileinfo->getSize();
147            $total_files++;
148        }
149
150        return array('Total Bytes' => number_format($total_bytes),
151            'Files' => $total_files);
152    }
153
154    /**
155     * X execute/call custom methods
156     *
157     * @return cache object
158     */
159    public function X()
160    {
161        return $this;
162    }
163
164}
165