comparison vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php @ 14:1fec387a4317

Update Drupal core to 8.5.2 via Composer
author Chris Cannam
date Mon, 23 Apr 2018 09:46:53 +0100
parents 7a779792577d
children c2387f117808
comparison
equal deleted inserted replaced
13:5fb285c0d0e3 14:1fec387a4317
10 */ 10 */
11 11
12 namespace Symfony\Component\HttpFoundation\Session\Storage; 12 namespace Symfony\Component\HttpFoundation\Session\Storage;
13 13
14 use Symfony\Component\HttpFoundation\Session\SessionBagInterface; 14 use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
15 use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler; 15 use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
16 use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; 16 use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
17 use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; 17 use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
18 18
19 /** 19 /**
20 * This provides a base class for session attribute storage. 20 * This provides a base class for session attribute storage.
22 * @author Drak <drak@zikula.org> 22 * @author Drak <drak@zikula.org>
23 */ 23 */
24 class NativeSessionStorage implements SessionStorageInterface 24 class NativeSessionStorage implements SessionStorageInterface
25 { 25 {
26 /** 26 /**
27 * Array of SessionBagInterface.
28 *
29 * @var SessionBagInterface[] 27 * @var SessionBagInterface[]
30 */ 28 */
31 protected $bags; 29 protected $bags = array();
32 30
33 /** 31 /**
34 * @var bool 32 * @var bool
35 */ 33 */
36 protected $started = false; 34 protected $started = false;
39 * @var bool 37 * @var bool
40 */ 38 */
41 protected $closed = false; 39 protected $closed = false;
42 40
43 /** 41 /**
44 * @var AbstractProxy 42 * @var AbstractProxy|\SessionHandlerInterface
45 */ 43 */
46 protected $saveHandler; 44 protected $saveHandler;
47 45
48 /** 46 /**
49 * @var MetadataBag 47 * @var MetadataBag
50 */ 48 */
51 protected $metadataBag; 49 protected $metadataBag;
52 50
53 /** 51 /**
54 * Constructor.
55 *
56 * Depending on how you want the storage driver to behave you probably 52 * Depending on how you want the storage driver to behave you probably
57 * want to override this constructor entirely. 53 * want to override this constructor entirely.
58 * 54 *
59 * List of options for $options array with their defaults. 55 * List of options for $options array with their defaults.
60 * 56 *
63 * 59 *
64 * ("auto_start", is not supported as it tells PHP to start a session before 60 * ("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). 61 * PHP starts to execute user-land code. Setting during runtime has no effect).
66 * 62 *
67 * cache_limiter, "" (use "0" to prevent headers from being sent entirely). 63 * cache_limiter, "" (use "0" to prevent headers from being sent entirely).
64 * cache_expire, "0"
68 * cookie_domain, "" 65 * cookie_domain, ""
69 * cookie_httponly, "" 66 * cookie_httponly, ""
70 * cookie_lifetime, "0" 67 * cookie_lifetime, "0"
71 * cookie_path, "/" 68 * cookie_path, "/"
72 * cookie_secure, "" 69 * cookie_secure, ""
75 * gc_divisor, "100" 72 * gc_divisor, "100"
76 * gc_maxlifetime, "1440" 73 * gc_maxlifetime, "1440"
77 * gc_probability, "1" 74 * gc_probability, "1"
78 * hash_bits_per_character, "4" 75 * hash_bits_per_character, "4"
79 * hash_function, "0" 76 * hash_function, "0"
77 * lazy_write, "1"
80 * name, "PHPSESSID" 78 * name, "PHPSESSID"
81 * referer_check, "" 79 * referer_check, ""
82 * serialize_handler, "php" 80 * serialize_handler, "php"
83 * use_strict_mode, "0" 81 * use_strict_mode, "0"
84 * use_cookies, "1" 82 * use_cookies, "1"
94 * sid_length, "32" 92 * sid_length, "32"
95 * sid_bits_per_character, "5" 93 * sid_bits_per_character, "5"
96 * trans_sid_hosts, $_SERVER['HTTP_HOST'] 94 * trans_sid_hosts, $_SERVER['HTTP_HOST']
97 * trans_sid_tags, "a=href,area=href,frame=src,form=" 95 * trans_sid_tags, "a=href,area=href,frame=src,form="
98 * 96 *
99 * @param array $options Session configuration options 97 * @param array $options Session configuration options
100 * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler 98 * @param \SessionHandlerInterface|null $handler
101 * @param MetadataBag $metaBag MetadataBag 99 * @param MetadataBag $metaBag MetadataBag
102 */ 100 */
103 public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null) 101 public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
104 { 102 {
105 session_cache_limiter(''); // disable by default because it's managed by HeaderBag (if used) 103 $options += array(
106 ini_set('session.use_cookies', 1); 104 'cache_limiter' => '',
105 'cache_expire' => 0,
106 'use_cookies' => 1,
107 'lazy_write' => 1,
108 );
107 109
108 session_register_shutdown(); 110 session_register_shutdown();
109 111
110 $this->setMetadataBag($metaBag); 112 $this->setMetadataBag($metaBag);
111 $this->setOptions($options); 113 $this->setOptions($options);
113 } 115 }
114 116
115 /** 117 /**
116 * Gets the save handler instance. 118 * Gets the save handler instance.
117 * 119 *
118 * @return AbstractProxy 120 * @return AbstractProxy|\SessionHandlerInterface
119 */ 121 */
120 public function getSaveHandler() 122 public function getSaveHandler()
121 { 123 {
122 return $this->saveHandler; 124 return $this->saveHandler;
123 } 125 }
189 // Cannot regenerate the session ID for non-active sessions. 191 // Cannot regenerate the session ID for non-active sessions.
190 if (\PHP_SESSION_ACTIVE !== session_status()) { 192 if (\PHP_SESSION_ACTIVE !== session_status()) {
191 return false; 193 return false;
192 } 194 }
193 195
196 if (headers_sent()) {
197 return false;
198 }
199
194 if (null !== $lifetime) { 200 if (null !== $lifetime) {
195 ini_set('session.cookie_lifetime', $lifetime); 201 ini_set('session.cookie_lifetime', $lifetime);
196 } 202 }
197 203
198 if ($destroy) { 204 if ($destroy) {
211 /** 217 /**
212 * {@inheritdoc} 218 * {@inheritdoc}
213 */ 219 */
214 public function save() 220 public function save()
215 { 221 {
216 session_write_close(); 222 $session = $_SESSION;
223
224 foreach ($this->bags as $bag) {
225 if (empty($_SESSION[$key = $bag->getStorageKey()])) {
226 unset($_SESSION[$key]);
227 }
228 }
229 if (array($key = $this->metadataBag->getStorageKey()) === array_keys($_SESSION)) {
230 unset($_SESSION[$key]);
231 }
232
233 // Register custom error handler to catch a possible failure warning during session write
234 set_error_handler(function ($errno, $errstr, $errfile, $errline) {
235 throw new \ErrorException($errstr, $errno, E_WARNING, $errfile, $errline);
236 }, E_WARNING);
237
238 try {
239 $e = null;
240 session_write_close();
241 } catch (\ErrorException $e) {
242 } finally {
243 restore_error_handler();
244 $_SESSION = $session;
245 }
246 if (null !== $e) {
247 // The default PHP error message is not very helpful, as it does not give any information on the current save handler.
248 // Therefore, we catch this error and trigger a warning with a better error message
249 $handler = $this->getSaveHandler();
250 if ($handler instanceof SessionHandlerProxy) {
251 $handler = $handler->getHandler();
252 }
253
254 trigger_error(sprintf('session_write_close(): Failed to write session data with %s handler', get_class($handler)), E_USER_WARNING);
255 }
217 256
218 $this->closed = true; 257 $this->closed = true;
219 $this->started = false; 258 $this->started = false;
220 } 259 }
221 260
255 { 294 {
256 if (!isset($this->bags[$name])) { 295 if (!isset($this->bags[$name])) {
257 throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name)); 296 throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
258 } 297 }
259 298
260 if ($this->saveHandler->isActive() && !$this->started) { 299 if (!$this->started && $this->saveHandler->isActive()) {
261 $this->loadSession(); 300 $this->loadSession();
262 } elseif (!$this->started) { 301 } elseif (!$this->started) {
263 $this->start(); 302 $this->start();
264 } 303 }
265 304
266 return $this->bags[$name]; 305 return $this->bags[$name];
267 } 306 }
268 307
269 /**
270 * Sets the MetadataBag.
271 *
272 * @param MetadataBag $metaBag
273 */
274 public function setMetadataBag(MetadataBag $metaBag = null) 308 public function setMetadataBag(MetadataBag $metaBag = null)
275 { 309 {
276 if (null === $metaBag) { 310 if (null === $metaBag) {
277 $metaBag = new MetadataBag(); 311 $metaBag = new MetadataBag();
278 } 312 }
308 * 342 *
309 * @see http://php.net/session.configuration 343 * @see http://php.net/session.configuration
310 */ 344 */
311 public function setOptions(array $options) 345 public function setOptions(array $options)
312 { 346 {
347 if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
348 return;
349 }
350
313 $validOptions = array_flip(array( 351 $validOptions = array_flip(array(
314 'cache_limiter', 'cookie_domain', 'cookie_httponly', 352 'cache_limiter', 'cache_expire', 'cookie_domain', 'cookie_httponly',
315 'cookie_lifetime', 'cookie_path', 'cookie_secure', 353 'cookie_lifetime', 'cookie_path', 'cookie_secure',
316 'entropy_file', 'entropy_length', 'gc_divisor', 354 'entropy_file', 'entropy_length', 'gc_divisor',
317 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character', 355 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
318 'hash_function', 'name', 'referer_check', 356 'hash_function', 'lazy_write', 'name', 'referer_check',
319 'serialize_handler', 'use_strict_mode', 'use_cookies', 357 'serialize_handler', 'use_strict_mode', 'use_cookies',
320 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', 358 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
321 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', 359 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
322 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags', 360 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags',
323 'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags', 361 'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
337 * session.save_handler and session.save_path e.g. 375 * session.save_handler and session.save_path e.g.
338 * 376 *
339 * ini_set('session.save_handler', 'files'); 377 * ini_set('session.save_handler', 'files');
340 * ini_set('session.save_path', '/tmp'); 378 * ini_set('session.save_path', '/tmp');
341 * 379 *
342 * or pass in a NativeSessionHandler instance which configures session.save_handler in the 380 * or pass in a \SessionHandler instance which configures session.save_handler in the
343 * constructor, for a template see NativeFileSessionHandler or use handlers in 381 * constructor, for a template see NativeFileSessionHandler or use handlers in
344 * composer package drak/native-session 382 * composer package drak/native-session
345 * 383 *
346 * @see http://php.net/session-set-save-handler 384 * @see http://php.net/session-set-save-handler
347 * @see http://php.net/sessionhandlerinterface 385 * @see http://php.net/sessionhandlerinterface
348 * @see http://php.net/sessionhandler 386 * @see http://php.net/sessionhandler
349 * @see http://github.com/drak/NativeSession 387 * @see http://github.com/drak/NativeSession
350 * 388 *
351 * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $saveHandler 389 * @param \SessionHandlerInterface|null $saveHandler
352 * 390 *
353 * @throws \InvalidArgumentException 391 * @throws \InvalidArgumentException
354 */ 392 */
355 public function setSaveHandler($saveHandler = null) 393 public function setSaveHandler($saveHandler = null)
356 { 394 {
357 if (!$saveHandler instanceof AbstractProxy && 395 if (!$saveHandler instanceof AbstractProxy &&
358 !$saveHandler instanceof NativeSessionHandler &&
359 !$saveHandler instanceof \SessionHandlerInterface && 396 !$saveHandler instanceof \SessionHandlerInterface &&
360 null !== $saveHandler) { 397 null !== $saveHandler) {
361 throw new \InvalidArgumentException('Must be instance of AbstractProxy or NativeSessionHandler; implement \SessionHandlerInterface; or be null.'); 398 throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.');
362 } 399 }
363 400
364 // Wrap $saveHandler in proxy and prevent double wrapping of proxy 401 // Wrap $saveHandler in proxy and prevent double wrapping of proxy
365 if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) { 402 if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
366 $saveHandler = new SessionHandlerProxy($saveHandler); 403 $saveHandler = new SessionHandlerProxy($saveHandler);
367 } elseif (!$saveHandler instanceof AbstractProxy) { 404 } elseif (!$saveHandler instanceof AbstractProxy) {
368 $saveHandler = new SessionHandlerProxy(new \SessionHandler()); 405 $saveHandler = new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler()));
369 } 406 }
370 $this->saveHandler = $saveHandler; 407 $this->saveHandler = $saveHandler;
371 408
372 if ($this->saveHandler instanceof \SessionHandlerInterface) { 409 if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
410 return;
411 }
412
413 if ($this->saveHandler instanceof SessionHandlerProxy) {
414 session_set_save_handler($this->saveHandler->getHandler(), false);
415 } elseif ($this->saveHandler instanceof \SessionHandlerInterface) {
373 session_set_save_handler($this->saveHandler, false); 416 session_set_save_handler($this->saveHandler, false);
374 } 417 }
375 } 418 }
376 419
377 /** 420 /**
379 * 422 *
380 * After starting the session, PHP retrieves the session from whatever handlers 423 * After starting the session, PHP retrieves the session from whatever handlers
381 * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()). 424 * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()).
382 * PHP takes the return value from the read() handler, unserializes it 425 * PHP takes the return value from the read() handler, unserializes it
383 * and populates $_SESSION with the result automatically. 426 * and populates $_SESSION with the result automatically.
384 *
385 * @param array|null $session
386 */ 427 */
387 protected function loadSession(array &$session = null) 428 protected function loadSession(array &$session = null)
388 { 429 {
389 if (null === $session) { 430 if (null === $session) {
390 $session = &$_SESSION; 431 $session = &$_SESSION;