/
Session.php
233 lines (204 loc) · 6.73 KB
/
Session.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
<?php
/*
* This file is part of the Stash package.
*
* (c) Robert Hafner <tedivm@tedivm.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Stash;
use Stash\Interfaces\PoolInterface;
use Stash\Session\SessionHandlerInterface as SessionHandlerInterface;
/**
* Stash\Session lets developers use Stash's Pool class to back session storage.
* By injecting a Pool class into a Session object, and registering that Session
* with PHP, developers can utilize any of Stash's drivers (including the
* composite driver) and special features.
*
* @package Stash
* @author Robert Hafner <tedivm@tedivm.com>
*/
class Session implements \SessionHandlerInterface
{
/**
* The Stash\Pool generates the individual cache items corresponding to each
* session. Basically all persistence is handled by this object.
*
* @var Stash\Pool
*/
protected $pool;
/**
* PHP passes a "save_path", which is not really relevant to most session
* systems. This class uses it as a namespace instead.
*
* @var string
*/
protected $path = '__empty_save_path';
/**
* The name of the current session, used as part of the cache namespace.
*
* @var string
*/
protected $name = '__empty_session_name';
/**
* Some options (such as the ttl of a session) can be set by the developers.
*
* @var array
*/
protected $options = array();
/**
* Registers a Session object with PHP as the session handler. This
* eliminates some boilerplate code from projects while also helping with
* the differences in php versions.
*
* @param \Stash\Session $handler
* @return bool
*/
public static function registerHandler(Session $handler): bool
{
// this isn't possible to test with the CLI phpunit test
// @codeCoverageIgnoreStart
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
return session_set_save_handler($handler, true);
} else {
$results = session_set_save_handler(
array($handler, 'open'),
array($handler, 'close'),
array($handler, 'read'),
array($handler, 'write'),
array($handler, 'destroy'),
array($handler, 'gc')
);
if (!$results) {
return false;
}
// the following prevents unexpected effects when using objects as save handlers
register_shutdown_function('session_write_close');
return true;
}
// @codeCoverageIgnoreEnd
}
/**
* The constructor expects an initialized Pool object. The creation of this
* object is up to the developer, but it should contain it's own unique
* drivers or be appropriately namespaced to avoid conflicts with other
* libraries.
*
* @param Interfaces\PoolInterface|Pool $pool
*/
public function __construct(PoolInterface $pool)
{
$this->pool = $pool;
$this->options['ttl'] = (int) ini_get('session.gc_maxlifetime');
}
/**
* Options can be set using an associative array. The only current option is
* a "ttl" value, which represents the amount of time (in seconds) that each
* session should last.
*
* @param array $options
* @return bool
*/
public function setOptions($options = array()) : void
{
$this->options = array_merge($this->options, $options);
}
/*
* The functions below are all implemented according to the
* SessionHandlerInterface interface.
*/
/**
* This function is defined by the SessionHandlerInterface and is for PHP's
* internal use. It takes the saved session path and turns it into a
* namespace.
*
* @param string $save_path
* @param string $session_name
* @return bool
*/
public function open($save_path, $session_name) : bool
{
if (isset($save_path) && $save_path !== '') {
$this->path = $save_path;
}
if (isset($session_name) || $session_name == '') {
$this->name = $session_name;
}
return true;
}
protected function getCache($session_id) : \Stash\Item
{
$path = '/' .
base64_encode($this->path) . '/' .
base64_encode($this->name) . '/' .
base64_encode($session_id);
return $this->pool->getItem($path);
}
/**
* This function is defined by the SessionHandlerInterface and is for PHP's
* internal use. It reads the session data from the caching system.
*
* @param string $session_id
* @return string
*/
public function read($session_id) : string
{
$cache = $this->getCache($session_id);
$data = $cache->get();
return $cache->isMiss() ? '' : $data;
}
/**
* This function is defined by the SessionHandlerInterface and is for PHP's
* internal use. It writes the session data to the caching system.
*
* @param string $session_id
* @param string $session_data
* @return bool
*/
public function write($session_id, $session_data) : bool
{
$cache = $this->getCache($session_id);
return $cache->set($session_data)->expiresAfter($this->options['ttl'])->save();
}
/**
* This function is defined by the SessionHandlerInterface and is for PHP's
* internal use. It currently does nothing important, as there is no need to
* take special action.
*
* @return bool
*/
public function close() : bool
{
return true;
}
/**
* This function is defined by the SessionHandlerInterface and is for PHP's
* internal use. It clears the current session.
*
* @param string $session_id
* @return bool
*/
public function destroy($session_id) : bool
{
$cache = $this->getCache($session_id);
return $cache->clear();
}
/**
* This function is defined by the SessionHandlerInterface and is for PHP's
* internal use. It is called randomly based on the session.gc_divisor,
* session.gc_probability and session.gc_lifetime settings, which should be
* set according to the drivers used. Those with built in eviction
* mechanisms will not need this functionality, while those without it will.
* It is also possible to disable the built in garbage collection (place
* gc_probability as zero) and call the "purge" function on the Stash\Pool
* class directly.
*
* @param int $maxlifetime
* @return bool
*/
public function gc($maxlifetime) : int|false
{
return $this->pool->purge();
}
}