annotate vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php @ 2:92f882872392

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