comparison vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 7a779792577d
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\HttpFoundation\Session\Storage;
13
14 use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
15 use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
16 use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
17 use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
18
19 /**
20 * This provides a base class for session attribute storage.
21 *
22 * @author Drak <drak@zikula.org>
23 */
24 class NativeSessionStorage implements SessionStorageInterface
25 {
26 /**
27 * Array of SessionBagInterface.
28 *
29 * @var SessionBagInterface[]
30 */
31 protected $bags;
32
33 /**
34 * @var bool
35 */
36 protected $started = false;
37
38 /**
39 * @var bool
40 */
41 protected $closed = false;
42
43 /**
44 * @var AbstractProxy
45 */
46 protected $saveHandler;
47
48 /**
49 * @var MetadataBag
50 */
51 protected $metadataBag;
52
53 /**
54 * Constructor.
55 *
56 * Depending on how you want the storage driver to behave you probably
57 * want to override this constructor entirely.
58 *
59 * List of options for $options array with their defaults.
60 *
61 * @see http://php.net/session.configuration for options
62 * but we omit 'session.' from the beginning of the keys for convenience.
63 *
64 * ("auto_start", is not supported as it tells PHP to start a session before
65 * PHP starts to execute user-land code. Setting during runtime has no effect).
66 *
67 * cache_limiter, "" (use "0" to prevent headers from being sent entirely).
68 * cookie_domain, ""
69 * cookie_httponly, ""
70 * cookie_lifetime, "0"
71 * cookie_path, "/"
72 * cookie_secure, ""
73 * entropy_file, ""
74 * entropy_length, "0"
75 * gc_divisor, "100"
76 * gc_maxlifetime, "1440"
77 * gc_probability, "1"
78 * hash_bits_per_character, "4"
79 * hash_function, "0"
80 * name, "PHPSESSID"
81 * referer_check, ""
82 * serialize_handler, "php"
83 * use_strict_mode, "0"
84 * use_cookies, "1"
85 * use_only_cookies, "1"
86 * use_trans_sid, "0"
87 * upload_progress.enabled, "1"
88 * upload_progress.cleanup, "1"
89 * upload_progress.prefix, "upload_progress_"
90 * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS"
91 * upload_progress.freq, "1%"
92 * upload_progress.min-freq, "1"
93 * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
94 *
95 * @param array $options Session configuration options
96 * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
97 * @param MetadataBag $metaBag MetadataBag
98 */
99 public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
100 {
101 session_cache_limiter(''); // disable by default because it's managed by HeaderBag (if used)
102 ini_set('session.use_cookies', 1);
103
104 session_register_shutdown();
105
106 $this->setMetadataBag($metaBag);
107 $this->setOptions($options);
108 $this->setSaveHandler($handler);
109 }
110
111 /**
112 * Gets the save handler instance.
113 *
114 * @return AbstractProxy
115 */
116 public function getSaveHandler()
117 {
118 return $this->saveHandler;
119 }
120
121 /**
122 * {@inheritdoc}
123 */
124 public function start()
125 {
126 if ($this->started) {
127 return true;
128 }
129
130 if (\PHP_SESSION_ACTIVE === session_status()) {
131 throw new \RuntimeException('Failed to start the session: already started by PHP.');
132 }
133
134 if (ini_get('session.use_cookies') && headers_sent($file, $line)) {
135 throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
136 }
137
138 // ok to try and start the session
139 if (!session_start()) {
140 throw new \RuntimeException('Failed to start the session');
141 }
142
143 $this->loadSession();
144
145 return true;
146 }
147
148 /**
149 * {@inheritdoc}
150 */
151 public function getId()
152 {
153 return $this->saveHandler->getId();
154 }
155
156 /**
157 * {@inheritdoc}
158 */
159 public function setId($id)
160 {
161 $this->saveHandler->setId($id);
162 }
163
164 /**
165 * {@inheritdoc}
166 */
167 public function getName()
168 {
169 return $this->saveHandler->getName();
170 }
171
172 /**
173 * {@inheritdoc}
174 */
175 public function setName($name)
176 {
177 $this->saveHandler->setName($name);
178 }
179
180 /**
181 * {@inheritdoc}
182 */
183 public function regenerate($destroy = false, $lifetime = null)
184 {
185 // Cannot regenerate the session ID for non-active sessions.
186 if (\PHP_SESSION_ACTIVE !== session_status()) {
187 return false;
188 }
189
190 if (null !== $lifetime) {
191 ini_set('session.cookie_lifetime', $lifetime);
192 }
193
194 if ($destroy) {
195 $this->metadataBag->stampNew();
196 }
197
198 $isRegenerated = session_regenerate_id($destroy);
199
200 // The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it.
201 // @see https://bugs.php.net/bug.php?id=70013
202 $this->loadSession();
203
204 return $isRegenerated;
205 }
206
207 /**
208 * {@inheritdoc}
209 */
210 public function save()
211 {
212 session_write_close();
213
214 $this->closed = true;
215 $this->started = false;
216 }
217
218 /**
219 * {@inheritdoc}
220 */
221 public function clear()
222 {
223 // clear out the bags
224 foreach ($this->bags as $bag) {
225 $bag->clear();
226 }
227
228 // clear out the session
229 $_SESSION = array();
230
231 // reconnect the bags to the session
232 $this->loadSession();
233 }
234
235 /**
236 * {@inheritdoc}
237 */
238 public function registerBag(SessionBagInterface $bag)
239 {
240 if ($this->started) {
241 throw new \LogicException('Cannot register a bag when the session is already started.');
242 }
243
244 $this->bags[$bag->getName()] = $bag;
245 }
246
247 /**
248 * {@inheritdoc}
249 */
250 public function getBag($name)
251 {
252 if (!isset($this->bags[$name])) {
253 throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
254 }
255
256 if ($this->saveHandler->isActive() && !$this->started) {
257 $this->loadSession();
258 } elseif (!$this->started) {
259 $this->start();
260 }
261
262 return $this->bags[$name];
263 }
264
265 /**
266 * Sets the MetadataBag.
267 *
268 * @param MetadataBag $metaBag
269 */
270 public function setMetadataBag(MetadataBag $metaBag = null)
271 {
272 if (null === $metaBag) {
273 $metaBag = new MetadataBag();
274 }
275
276 $this->metadataBag = $metaBag;
277 }
278
279 /**
280 * Gets the MetadataBag.
281 *
282 * @return MetadataBag
283 */
284 public function getMetadataBag()
285 {
286 return $this->metadataBag;
287 }
288
289 /**
290 * {@inheritdoc}
291 */
292 public function isStarted()
293 {
294 return $this->started;
295 }
296
297 /**
298 * Sets session.* ini variables.
299 *
300 * For convenience we omit 'session.' from the beginning of the keys.
301 * Explicitly ignores other ini keys.
302 *
303 * @param array $options Session ini directives array(key => value)
304 *
305 * @see http://php.net/session.configuration
306 */
307 public function setOptions(array $options)
308 {
309 $validOptions = array_flip(array(
310 'cache_limiter', 'cookie_domain', 'cookie_httponly',
311 'cookie_lifetime', 'cookie_path', 'cookie_secure',
312 'entropy_file', 'entropy_length', 'gc_divisor',
313 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
314 'hash_function', 'name', 'referer_check',
315 'serialize_handler', 'use_strict_mode', 'use_cookies',
316 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
317 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
318 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags',
319 ));
320
321 foreach ($options as $key => $value) {
322 if (isset($validOptions[$key])) {
323 ini_set('session.'.$key, $value);
324 }
325 }
326 }
327
328 /**
329 * Registers session save handler as a PHP session handler.
330 *
331 * To use internal PHP session save handlers, override this method using ini_set with
332 * session.save_handler and session.save_path e.g.
333 *
334 * ini_set('session.save_handler', 'files');
335 * ini_set('session.save_path', '/tmp');
336 *
337 * or pass in a NativeSessionHandler instance which configures session.save_handler in the
338 * constructor, for a template see NativeFileSessionHandler or use handlers in
339 * composer package drak/native-session
340 *
341 * @see http://php.net/session-set-save-handler
342 * @see http://php.net/sessionhandlerinterface
343 * @see http://php.net/sessionhandler
344 * @see http://github.com/drak/NativeSession
345 *
346 * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $saveHandler
347 *
348 * @throws \InvalidArgumentException
349 */
350 public function setSaveHandler($saveHandler = null)
351 {
352 if (!$saveHandler instanceof AbstractProxy &&
353 !$saveHandler instanceof NativeSessionHandler &&
354 !$saveHandler instanceof \SessionHandlerInterface &&
355 null !== $saveHandler) {
356 throw new \InvalidArgumentException('Must be instance of AbstractProxy or NativeSessionHandler; implement \SessionHandlerInterface; or be null.');
357 }
358
359 // Wrap $saveHandler in proxy and prevent double wrapping of proxy
360 if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
361 $saveHandler = new SessionHandlerProxy($saveHandler);
362 } elseif (!$saveHandler instanceof AbstractProxy) {
363 $saveHandler = new SessionHandlerProxy(new \SessionHandler());
364 }
365 $this->saveHandler = $saveHandler;
366
367 if ($this->saveHandler instanceof \SessionHandlerInterface) {
368 session_set_save_handler($this->saveHandler, false);
369 }
370 }
371
372 /**
373 * Load the session with attributes.
374 *
375 * After starting the session, PHP retrieves the session from whatever handlers
376 * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()).
377 * PHP takes the return value from the read() handler, unserializes it
378 * and populates $_SESSION with the result automatically.
379 *
380 * @param array|null $session
381 */
382 protected function loadSession(array &$session = null)
383 {
384 if (null === $session) {
385 $session = &$_SESSION;
386 }
387
388 $bags = array_merge($this->bags, array($this->metadataBag));
389
390 foreach ($bags as $bag) {
391 $key = $bag->getStorageKey();
392 $session[$key] = isset($session[$key]) ? $session[$key] : array();
393 $bag->initialize($session[$key]);
394 }
395
396 $this->started = true;
397 $this->closed = false;
398 }
399 }