1<?php
2namespace DALMP\Sessions;
3
4/**
5 * Sessions\Redis
6 *
7 * @author Nicolas Embriz <nbari@dalmp.com>
8 * @package DALMP
9 * @license BSD License
10 * @version 3.0.3
11 */
12class Redis implements \SessionHandlerInterface
13{
14    /**
15     * DALMP\Cache\Redis instance
16     *
17     * @access protected
18     * @var DALMP\Cache\Redis
19     */
20    protected $cache;
21
22    /**
23     * cache_ref_key key used to store the reference on the cache engine
24     *
25     * @access private
26     * @var string
27     */
28    private $cache_ref_key;
29
30    /**
31     * REF - field used for storing references
32     *
33     * @access private
34     * @var mixed
35     */
36    private $dalmp_sessions_ref;
37
38    /**
39     * key used for creating more entropy when storing the sessions on a
40     * key/value cache engine, useful when serving multiple sites
41     *
42     * @access private
43     * @var mixed
44     */
45    private $dalmp_sessions_key;
46
47    /**
48     * constructor
49     *
50     * @param DALMP\Cache\Redis $cache        instance
51     * @param string            $sessions_ref global variable to be stored as reference
52     */
53    public function __construct(\DALMP\Cache\Redis $cache, $sessions_ref = 'UID')
54    {
55        $this->cache = $cache;
56        $this->dalmp_sessions_ref = defined('DALMP_SESSIONS_REF') ? DALMP_SESSIONS_REF : $sessions_ref;
57        $this->dalmp_sessions_key = defined('DALMP_SESSIONS_KEY') ? DALMP_SESSIONS_KEY : __FILE__;
58        $this->cache_ref_key = sprintf('DALMP_REF_%s', sha1($this->dalmp_sessions_ref . $this->dalmp_sessions_key));
59    }
60
61    public function close()
62    {
63        return true;
64    }
65
66    public function destroy($session_id)
67    {
68        $key = sprintf('DALMP_%s', sha1($this->dalmp_sessions_ref . $session_id));
69        if ($rs = $this->cache->Delete($key)) {
70
71            /**
72             * destroy REF on cache
73             */
74            if (isset($GLOBALS[$this->dalmp_sessions_ref]) && !empty($GLOBALS[$this->dalmp_sessions_ref])) {
75                $this->cache->X()->HDEL($this->cache_ref_key, $key);
76                $this->cache->X()->EXPIRE($this->cache_ref_key, 3600);
77            }
78
79            return true;
80        } else {
81            return false;
82        }
83    }
84
85    public function gc($maxlifetime)
86    {
87        $refs = $this->cache->X()->HGETALL($this->cache_ref_key);
88
89        $keys = array($this->cache_ref_key);
90
91        if (is_array($refs)) {
92            foreach ($refs as $key => $sref) {
93                $data = explode('|', $sref);
94                if (current($data) < time()) {
95                    $keys[] = $key;
96                }
97            }
98
99            if (count($keys) > 1) {
100                $redis = $this->cache->X();
101                call_user_func_array(array($redis, 'HDEL'), $keys);
102                $this->cache->X()->EXPIRE($this->cache_ref_key, 3600);
103            }
104        }
105
106        return true;
107    }
108
109    public function open($save_path, $name)
110    {
111        return true;
112    }
113
114    public function read($session_id)
115    {
116        $key = sprintf('DALMP_%s', sha1($this->dalmp_sessions_ref . $session_id));
117
118        return $this->cache->Get($key);
119    }
120
121    public function write($session_id, $session_data)
122    {
123        $ref = (isset($GLOBALS[$this->dalmp_sessions_ref]) && !empty($GLOBALS[$this->dalmp_sessions_ref])) ? $GLOBALS[$this->dalmp_sessions_ref] : null;
124        $timeout = ini_get('session.gc_maxlifetime');
125        $expiry = time() + $timeout;
126
127        $key = sprintf('DALMP_%s', sha1($this->dalmp_sessions_ref . $session_id));
128        $rs = (bool) $this->cache->Set($key, $session_data, $timeout);
129
130        /**
131         * store REF on cache
132         */
133        if ($rs && $ref) {
134            return (bool) ($this->cache->X()->HSET($this->cache_ref_key, $key, sprintf('%s|%s', $ref, $expiry)) ? $this->cache->X()->EXPIRE($this->cache_ref_key, 3600) : false);
135        } else {
136            return $rs;
137        }
138    }
139
140    /**
141     * getSessionsRefs
142     *
143     * @return array of sessions containing any reference
144     */
145    public function getSessionsRefs()
146    {
147        $refs = $this->cache->X()->HGetALL($this->cache_ref_key);
148        $rs = array();
149
150        foreach ($refs as $key => $data) {
151            list($reference, $expiry) = explode('|', $data);
152            $rs[$key] = array($reference => $expiry);
153        }
154
155        return $rs;
156    }
157
158    /**
159     * getSessionRef
160     *
161     * @param  string $ref
162     * @return array  of session containing a specific reference
163     */
164    public function getSessionRef($ref)
165    {
166        $refs = $this->cache->X()->HGetALL($this->cache_ref_key);
167        $rs = array();
168
169        foreach ($refs as $key => $data) {
170            list($reference, $expiry) = explode('|', $data);
171            if ($reference == $ref) {
172                $rs[$key] = array($reference => $expiry);
173            }
174        }
175
176        return $rs;
177    }
178
179    /**
180     * delSessionRef - delete sessions containing a specific reference
181     *
182     * @param  string  $ref
183     * @return boolean
184     */
185    public function delSessionRef($ref)
186    {
187        $refs = $this->cache->X()->HGETALL($this->cache_ref_key);
188
189        $keys = array($this->cache_ref_key);
190
191        if (is_array($refs)) {
192            foreach ($refs as $key => $data) {
193                list($reference, $expiry) = explode('|', $data);
194                if ($reference == $ref) {
195                    $keys[] = $key;
196                }
197            }
198        }
199
200        if (count($keys) > 1) {
201            $redis = $this->cache->X();
202            call_user_func_array(array($redis, 'HDEL'), $keys);
203            array_shift($keys);
204
205            return (bool) $this->cache->delete($keys);
206        } else {
207            return false;
208        }
209    }
210
211}
212