1<?php
2/**
3 * Copyright 2011-2017 Horde LLC (http://www.horde.org/)
4 *
5 * See the enclosed file COPYING for license information (LGPL). If you
6 * did not receive this file, see http://www.horde.org/licenses/lgpl21.
7 *
8 * @author   Jan Schneider <jan@horde.org>
9 * @author   Michael Slusarz <slusarz@horde.org>
10 * @category Horde
11 * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
12 * @package  Autoloader_Cache
13 */
14
15/**
16 * Bootstrap cache storage driver.
17 *
18 * Used for caching autoloader data before the full autoloader environment is
19 * setup. Transparently compresses the data if possible also.
20 *
21 * @author    Jan Schneider <jan@horde.org>
22 * @author    Michael Slusarz <slusarz@horde.org>
23 * @category  Horde
24 * @copyright 2011-2017 Horde LLC
25 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
26 * @package   Autoloader_Cache
27 * @since     2.1.0
28 */
29class Horde_Autoloader_Cache_Bootstrap
30{
31    /* Cache types. */
32    const APC = 1;
33    const XCACHE = 2;
34    const EACCELERATOR = 4;
35    const TEMPFILE = 8;
36
37    /* Compress types. */
38    const LZ4 = 16;
39    const LZF = 32;
40
41    /* Serialize types. */
42    const MSGPACK = 64;
43    const JSON = 128;
44
45    /**
46     * The storage parameters mask.
47     *
48     * @var array
49     */
50    protected $_mask = 0;
51
52    /**
53     * Temporary directory.
54     *
55     * @var string
56     */
57    protected $_tempdir;
58
59    /**
60     * Constructor.
61     *
62     * @param array $opts  Options:
63     *   - tempdir: (string) Use this path as the temporary directory.
64     */
65    public function __construct(array $opts = array())
66    {
67        if (extension_loaded('apc')) {
68            $this->_mask |= self::APC;
69        } elseif (extension_loaded('xcache')) {
70            $this->_mask |= self::XCACHE;
71        } elseif (extension_loaded('eaccelerator')) {
72            $this->_mask |= self::EACCELERATOR;
73        } else{
74            $tempdir = isset($opts['tempdir'])
75                ? $opts['tempdir']
76                : sys_get_temp_dir();
77            if (is_readable($tempdir)) {
78                $this->_tempdir = $tempdir;
79                $this->_mask |= self::TEMPFILE;
80            }
81        }
82
83        if (extension_loaded('horde_lz4')) {
84            $this->_mask |= self::LZ4;
85        } elseif (extension_loaded('lzf')) {
86            $this->_mask |= self::LZF;
87        }
88
89        $this->_mask |= extension_loaded('msgpack')
90            ? self::MSGPACK
91            : self::JSON;
92    }
93
94    /**
95     * Return cached data.
96     *
97     * @param string $key  Cache key.
98     *
99     * @return mixed  Cache data, or false if not found.
100     */
101    public function get($key)
102    {
103        if ($this->_mask & self::APC) {
104            $data = apc_fetch($key);
105        } elseif ($this->_mask & self::XCACHE) {
106            $data = xcache_get($key);
107        } elseif ($this->_mask & self::EACCELERATOR) {
108            $data = eaccelerator_get($key);
109        } elseif ($this->_mask & self::TEMPFILE) {
110            $data = @file_get_contents($this->_tempdir . '/' . $key);
111            if ($data === false) {
112                unlink($this->_tempdir . '/' . $key);
113            }
114        } else {
115            return false;
116        }
117
118        if ($data) {
119            if ($this->_mask & self::LZ4) {
120                $data = @horde_lz4_uncompress($data);
121            } elseif ($this->_mask & self::LZF) {
122                $data = @lzf_decompress($data);
123            }
124        }
125
126        return ($data === false)
127            ? false
128            : ($this->_mask & self::MSGPACK)
129                  ? msgpack_unpack($data)
130                  : @json_decode($data, true);
131    }
132
133    /**
134     * Set cached data.
135     *
136     * @param string $key  Cache key.
137     * @param mixed $data  Data to store.
138     *
139     * @return boolean  True on success, false on failure.
140     */
141    public function set($key, $data)
142    {
143        $data = ($this->_mask & self::MSGPACK)
144            ? msgpack_pack($data)
145            : json_encode($data);
146
147        if ($this->_mask & self::LZ4) {
148            $data = @horde_lz4_compress($data);
149        } elseif ($this->_mask & self::LZF) {
150            $data = lzf_compress($data);
151        }
152
153        if ($this->_mask & self::APC) {
154            return apc_store($key, $data);
155        } elseif ($this->_mask & self::XCACHE) {
156            return xcache_set($key, $data);
157        } elseif ($this->_mask & self::EACCELERATOR) {
158            eaccelerator_put($key, $data);
159            return true;
160        } elseif ($this->_mask & self::TEMPFILE) {
161            return file_put_contents($this->_tempdir . '/' . $key, $data);
162        }
163
164        return false;
165    }
166
167    /**
168     * Delete a key.
169     *
170     * @param string $key  Cache key.
171     */
172    public function delete($key)
173    {
174        if ($this->_mask & self::APC) {
175            apc_delete($key);
176        } elseif ($this->_mask & self::XCACHE) {
177            xcache_unset($key);
178        } elseif ($this->_mask & self::EACCELERATOR) {
179            eaccelerator_rm($key);
180        } elseif ($this->_mask & self::TEMPFILE) {
181            @unlink($this->_tempdir . '/' . $key);
182        }
183    }
184
185}
186