1<?php
2/**
3 * Zend Framework (http://framework.zend.com/)
4 *
5 * @link      http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license   http://framework.zend.com/license/new-bsd New BSD License
8 */
9
10namespace Zend\Cache\Storage\Adapter;
11
12use Traversable;
13use Zend\Cache\Exception;
14
15/**
16 * These are options specific to the Filesystem adapter
17 */
18class FilesystemOptions extends AdapterOptions
19{
20    /**
21     * Directory to store cache files
22     *
23     * @var null|string The cache directory
24     *                  or NULL for the systems temporary directory
25     */
26    protected $cacheDir = null;
27
28    /**
29     * Call clearstatcache enabled?
30     *
31     * @var bool
32     */
33    protected $clearStatCache = true;
34
35    /**
36     * How much sub-directaries should be created?
37     *
38     * @var int
39     */
40    protected $dirLevel = 1;
41
42    /**
43     * Permission creating new directories
44     *
45     * @var false|int
46     */
47    protected $dirPermission = 0700;
48
49    /**
50     * Lock files on writing
51     *
52     * @var bool
53     */
54    protected $fileLocking = true;
55
56    /**
57     * Permission creating new files
58     *
59     * @var false|int
60     */
61    protected $filePermission = 0600;
62
63    /**
64     * Overwrite default key pattern
65     *
66     * Defined in AdapterOptions
67     *
68     * @var string
69     */
70    protected $keyPattern = '/^[a-z0-9_\+\-]*$/Di';
71
72    /**
73     * Namespace separator
74     *
75     * @var string
76     */
77    protected $namespaceSeparator = '-';
78
79    /**
80     * Don't get 'fileatime' as 'atime' on metadata
81     *
82     * @var bool
83     */
84    protected $noAtime = true;
85
86    /**
87     * Don't get 'filectime' as 'ctime' on metadata
88     *
89     * @var bool
90     */
91    protected $noCtime = true;
92
93    /**
94     * Umask to create files and directories
95     *
96     * @var false|int
97     */
98    protected $umask = false;
99
100    /**
101     * Constructor
102     *
103     * @param  array|Traversable|null $options
104     * @return FilesystemOptions
105     * @throws Exception\InvalidArgumentException
106     */
107    public function __construct($options = null)
108    {
109        // disable file/directory permissions by default on windows systems
110        if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
111            $this->filePermission = false;
112            $this->dirPermission = false;
113        }
114
115        parent::__construct($options);
116    }
117
118    /**
119     * Set cache dir
120     *
121     * @param  string $cacheDir
122     * @return FilesystemOptions
123     * @throws Exception\InvalidArgumentException
124     */
125    public function setCacheDir($cacheDir)
126    {
127        if ($cacheDir !== null) {
128            if (!is_dir($cacheDir)) {
129                throw new Exception\InvalidArgumentException(
130                    "Cache directory '{$cacheDir}' not found or not a directory"
131                );
132            } elseif (!is_writable($cacheDir)) {
133                throw new Exception\InvalidArgumentException(
134                    "Cache directory '{$cacheDir}' not writable"
135                );
136            } elseif (!is_readable($cacheDir)) {
137                throw new Exception\InvalidArgumentException(
138                    "Cache directory '{$cacheDir}' not readable"
139                );
140            }
141
142            $cacheDir = rtrim(realpath($cacheDir), DIRECTORY_SEPARATOR);
143        } else {
144            $cacheDir = sys_get_temp_dir();
145        }
146
147        $this->triggerOptionEvent('cache_dir', $cacheDir);
148        $this->cacheDir = $cacheDir;
149        return $this;
150    }
151
152    /**
153     * Get cache dir
154     *
155     * @return null|string
156     */
157    public function getCacheDir()
158    {
159        if ($this->cacheDir === null) {
160            $this->setCacheDir(null);
161        }
162
163        return $this->cacheDir;
164    }
165
166    /**
167     * Set clear stat cache
168     *
169     * @param  bool $clearStatCache
170     * @return FilesystemOptions
171     */
172    public function setClearStatCache($clearStatCache)
173    {
174        $clearStatCache = (bool) $clearStatCache;
175        $this->triggerOptionEvent('clear_stat_cache', $clearStatCache);
176        $this->clearStatCache = $clearStatCache;
177        return $this;
178    }
179
180    /**
181     * Get clear stat cache
182     *
183     * @return bool
184     */
185    public function getClearStatCache()
186    {
187        return $this->clearStatCache;
188    }
189
190    /**
191     * Set dir level
192     *
193     * @param  int $dirLevel
194     * @return FilesystemOptions
195     * @throws Exception\InvalidArgumentException
196     */
197    public function setDirLevel($dirLevel)
198    {
199        $dirLevel = (int) $dirLevel;
200        if ($dirLevel < 0 || $dirLevel > 16) {
201            throw new Exception\InvalidArgumentException(
202                "Directory level '{$dirLevel}' must be between 0 and 16"
203            );
204        }
205        $this->triggerOptionEvent('dir_level', $dirLevel);
206        $this->dirLevel = $dirLevel;
207        return $this;
208    }
209
210    /**
211     * Get dir level
212     *
213     * @return int
214     */
215    public function getDirLevel()
216    {
217        return $this->dirLevel;
218    }
219
220    /**
221     * Set permission to create directories on unix systems
222     *
223     * @param false|string|int $dirPermission FALSE to disable explicit permission or an octal number
224     * @return FilesystemOptions
225     * @see setUmask
226     * @see setFilePermission
227     * @link http://php.net/manual/function.chmod.php
228     */
229    public function setDirPermission($dirPermission)
230    {
231        if ($dirPermission !== false) {
232            if (is_string($dirPermission)) {
233                $dirPermission = octdec($dirPermission);
234            } else {
235                $dirPermission = (int) $dirPermission;
236            }
237
238            // validate
239            if (($dirPermission & 0700) != 0700) {
240                throw new Exception\InvalidArgumentException(
241                    'Invalid directory permission: need permission to execute, read and write by owner'
242                );
243            }
244        }
245
246        if ($this->dirPermission !== $dirPermission) {
247            $this->triggerOptionEvent('dir_permission', $dirPermission);
248            $this->dirPermission = $dirPermission;
249        }
250
251        return $this;
252    }
253
254    /**
255     * Get permission to create directories on unix systems
256     *
257     * @return false|int
258     */
259    public function getDirPermission()
260    {
261        return $this->dirPermission;
262    }
263
264    /**
265     * Set file locking
266     *
267     * @param  bool $fileLocking
268     * @return FilesystemOptions
269     */
270    public function setFileLocking($fileLocking)
271    {
272        $fileLocking = (bool) $fileLocking;
273        $this->triggerOptionEvent('file_locking', $fileLocking);
274        $this->fileLocking = $fileLocking;
275        return $this;
276    }
277
278    /**
279     * Get file locking
280     *
281     * @return bool
282     */
283    public function getFileLocking()
284    {
285        return $this->fileLocking;
286    }
287
288    /**
289     * Set permission to create files on unix systems
290     *
291     * @param false|string|int $filePermission FALSE to disable explicit permission or an octal number
292     * @return FilesystemOptions
293     * @see setUmask
294     * @see setDirPermission
295     * @link http://php.net/manual/function.chmod.php
296     */
297    public function setFilePermission($filePermission)
298    {
299        if ($filePermission !== false) {
300            if (is_string($filePermission)) {
301                $filePermission = octdec($filePermission);
302            } else {
303                $filePermission = (int) $filePermission;
304            }
305
306            // validate
307            if (($filePermission & 0600) != 0600) {
308                throw new Exception\InvalidArgumentException(
309                    'Invalid file permission: need permission to read and write by owner'
310                );
311            } elseif ($filePermission & 0111) {
312                throw new Exception\InvalidArgumentException(
313                    "Invalid file permission: Cache files shoudn't be executable"
314                );
315            }
316        }
317
318        if ($this->filePermission !== $filePermission) {
319            $this->triggerOptionEvent('file_permission', $filePermission);
320            $this->filePermission = $filePermission;
321        }
322
323        return $this;
324    }
325
326    /**
327     * Get permission to create files on unix systems
328     *
329     * @return false|int
330     */
331    public function getFilePermission()
332    {
333        return $this->filePermission;
334    }
335
336    /**
337     * Set namespace separator
338     *
339     * @param  string $namespaceSeparator
340     * @return FilesystemOptions
341     */
342    public function setNamespaceSeparator($namespaceSeparator)
343    {
344        $namespaceSeparator = (string) $namespaceSeparator;
345        $this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
346        $this->namespaceSeparator = $namespaceSeparator;
347        return $this;
348    }
349
350    /**
351     * Get namespace separator
352     *
353     * @return string
354     */
355    public function getNamespaceSeparator()
356    {
357        return $this->namespaceSeparator;
358    }
359
360    /**
361     * Set no atime
362     *
363     * @param  bool $noAtime
364     * @return FilesystemOptions
365     */
366    public function setNoAtime($noAtime)
367    {
368        $noAtime = (bool) $noAtime;
369        $this->triggerOptionEvent('no_atime', $noAtime);
370        $this->noAtime = $noAtime;
371        return $this;
372    }
373
374    /**
375     * Get no atime
376     *
377     * @return bool
378     */
379    public function getNoAtime()
380    {
381        return $this->noAtime;
382    }
383
384    /**
385     * Set no ctime
386     *
387     * @param  bool $noCtime
388     * @return FilesystemOptions
389     */
390    public function setNoCtime($noCtime)
391    {
392        $noCtime = (bool) $noCtime;
393        $this->triggerOptionEvent('no_ctime', $noCtime);
394        $this->noCtime = $noCtime;
395        return $this;
396    }
397
398    /**
399     * Get no ctime
400     *
401     * @return bool
402     */
403    public function getNoCtime()
404    {
405        return $this->noCtime;
406    }
407
408    /**
409     * Set the umask to create files and directories on unix systems
410     *
411     * Note: On multithreaded webservers it's better to explicit set file and dir permission.
412     *
413     * @param false|string|int $umask FALSE to disable umask or an octal number
414     * @return FilesystemOptions
415     * @see setFilePermission
416     * @see setDirPermission
417     * @link http://php.net/manual/function.umask.php
418     * @link http://en.wikipedia.org/wiki/Umask
419     */
420    public function setUmask($umask)
421    {
422        if ($umask !== false) {
423            if (is_string($umask)) {
424                $umask = octdec($umask);
425            } else {
426                $umask = (int) $umask;
427            }
428
429            // validate
430            if ($umask & 0700) {
431                throw new Exception\InvalidArgumentException(
432                    'Invalid umask: need permission to execute, read and write by owner'
433                );
434            }
435
436            // normalize
437            $umask = $umask & ~0002;
438        }
439
440        if ($this->umask !== $umask) {
441            $this->triggerOptionEvent('umask', $umask);
442            $this->umask = $umask;
443        }
444
445        return $this;
446    }
447
448    /**
449     * Get the umask to create files and directories on unix systems
450     *
451     * @return false|int
452     */
453    public function getUmask()
454    {
455        return $this->umask;
456    }
457}
458