[ Index ]

PHP Cross Reference of phpBB-3.3.14-deutsch

title

Body

[close]

/vendor/symfony/http-foundation/Session/Storage/ -> NativeSessionStorage.php (source)

   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\SessionUtils;
  16  use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
  17  use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
  18  use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
  19  
  20  /**
  21   * This provides a base class for session attribute storage.
  22   *
  23   * @author Drak <drak@zikula.org>
  24   */
  25  class NativeSessionStorage implements SessionStorageInterface
  26  {
  27      /**
  28       * @var SessionBagInterface[]
  29       */
  30      protected $bags = [];
  31  
  32      /**
  33       * @var bool
  34       */
  35      protected $started = false;
  36  
  37      /**
  38       * @var bool
  39       */
  40      protected $closed = false;
  41  
  42      /**
  43       * @var AbstractProxy|\SessionHandlerInterface
  44       */
  45      protected $saveHandler;
  46  
  47      /**
  48       * @var MetadataBag
  49       */
  50      protected $metadataBag;
  51  
  52      /**
  53       * @var string|null
  54       */
  55      private $emulateSameSite;
  56  
  57      /**
  58       * Depending on how you want the storage driver to behave you probably
  59       * want to override this constructor entirely.
  60       *
  61       * List of options for $options array with their defaults.
  62       *
  63       * @see https://php.net/session.configuration for options
  64       * but we omit 'session.' from the beginning of the keys for convenience.
  65       *
  66       * ("auto_start", is not supported as it tells PHP to start a session before
  67       * PHP starts to execute user-land code. Setting during runtime has no effect).
  68       *
  69       * cache_limiter, "" (use "0" to prevent headers from being sent entirely).
  70       * cache_expire, "0"
  71       * cookie_domain, ""
  72       * cookie_httponly, ""
  73       * cookie_lifetime, "0"
  74       * cookie_path, "/"
  75       * cookie_secure, ""
  76       * cookie_samesite, null
  77       * entropy_file, ""
  78       * entropy_length, "0"
  79       * gc_divisor, "100"
  80       * gc_maxlifetime, "1440"
  81       * gc_probability, "1"
  82       * hash_bits_per_character, "4"
  83       * hash_function, "0"
  84       * lazy_write, "1"
  85       * name, "PHPSESSID"
  86       * referer_check, ""
  87       * serialize_handler, "php"
  88       * use_strict_mode, "0"
  89       * use_cookies, "1"
  90       * use_only_cookies, "1"
  91       * use_trans_sid, "0"
  92       * upload_progress.enabled, "1"
  93       * upload_progress.cleanup, "1"
  94       * upload_progress.prefix, "upload_progress_"
  95       * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS"
  96       * upload_progress.freq, "1%"
  97       * upload_progress.min-freq, "1"
  98       * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
  99       * sid_length, "32"
 100       * sid_bits_per_character, "5"
 101       * trans_sid_hosts, $_SERVER['HTTP_HOST']
 102       * trans_sid_tags, "a=href,area=href,frame=src,form="
 103       *
 104       * @param AbstractProxy|\SessionHandlerInterface|null $handler
 105       */
 106      public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null)
 107      {
 108          if (!\extension_loaded('session')) {
 109              throw new \LogicException('PHP extension "session" is required.');
 110          }
 111  
 112          $options += [
 113              'cache_limiter' => '',
 114              'cache_expire' => 0,
 115              'use_cookies' => 1,
 116              'lazy_write' => 1,
 117          ];
 118  
 119          session_register_shutdown();
 120  
 121          $this->setMetadataBag($metaBag);
 122          $this->setOptions($options);
 123          $this->setSaveHandler($handler);
 124      }
 125  
 126      /**
 127       * Gets the save handler instance.
 128       *
 129       * @return AbstractProxy|\SessionHandlerInterface
 130       */
 131      public function getSaveHandler()
 132      {
 133          return $this->saveHandler;
 134      }
 135  
 136      /**
 137       * {@inheritdoc}
 138       */
 139      public function start()
 140      {
 141          if ($this->started) {
 142              return true;
 143          }
 144  
 145          if (\PHP_SESSION_ACTIVE === session_status()) {
 146              throw new \RuntimeException('Failed to start the session: already started by PHP.');
 147          }
 148  
 149          if (filter_var(ini_get('session.use_cookies'), \FILTER_VALIDATE_BOOLEAN) && headers_sent($file, $line)) {
 150              throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
 151          }
 152  
 153          // ok to try and start the session
 154          if (!session_start()) {
 155              throw new \RuntimeException('Failed to start the session.');
 156          }
 157  
 158          if (null !== $this->emulateSameSite) {
 159              $originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
 160              if (null !== $originalCookie) {
 161                  header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false);
 162              }
 163          }
 164  
 165          $this->loadSession();
 166  
 167          return true;
 168      }
 169  
 170      /**
 171       * {@inheritdoc}
 172       */
 173      public function getId()
 174      {
 175          return $this->saveHandler->getId();
 176      }
 177  
 178      /**
 179       * {@inheritdoc}
 180       */
 181      public function setId($id)
 182      {
 183          $this->saveHandler->setId($id);
 184      }
 185  
 186      /**
 187       * {@inheritdoc}
 188       */
 189      public function getName()
 190      {
 191          return $this->saveHandler->getName();
 192      }
 193  
 194      /**
 195       * {@inheritdoc}
 196       */
 197      public function setName($name)
 198      {
 199          $this->saveHandler->setName($name);
 200      }
 201  
 202      /**
 203       * {@inheritdoc}
 204       */
 205      public function regenerate($destroy = false, $lifetime = null)
 206      {
 207          // Cannot regenerate the session ID for non-active sessions.
 208          if (\PHP_SESSION_ACTIVE !== session_status()) {
 209              return false;
 210          }
 211  
 212          if (headers_sent()) {
 213              return false;
 214          }
 215  
 216          if (null !== $lifetime && $lifetime != ini_get('session.cookie_lifetime')) {
 217              $this->save();
 218              ini_set('session.cookie_lifetime', $lifetime);
 219              $this->start();
 220          }
 221  
 222          if ($destroy) {
 223              $this->metadataBag->stampNew();
 224          }
 225  
 226          $isRegenerated = session_regenerate_id($destroy);
 227  
 228          if (null !== $this->emulateSameSite) {
 229              $originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
 230              if (null !== $originalCookie) {
 231                  header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false);
 232              }
 233          }
 234  
 235          return $isRegenerated;
 236      }
 237  
 238      /**
 239       * {@inheritdoc}
 240       */
 241      public function save()
 242      {
 243          // Store a copy so we can restore the bags in case the session was not left empty
 244          $session = $_SESSION;
 245  
 246          foreach ($this->bags as $bag) {
 247              if (empty($_SESSION[$key = $bag->getStorageKey()])) {
 248                  unset($_SESSION[$key]);
 249              }
 250          }
 251          if ([$key = $this->metadataBag->getStorageKey()] === array_keys($_SESSION)) {
 252              unset($_SESSION[$key]);
 253          }
 254  
 255          // Register error handler to add information about the current save handler
 256          $previousHandler = set_error_handler(function ($type, $msg, $file, $line) use (&$previousHandler) {
 257              if (\E_WARNING === $type && 0 === strpos($msg, 'session_write_close():')) {
 258                  $handler = $this->saveHandler instanceof SessionHandlerProxy ? $this->saveHandler->getHandler() : $this->saveHandler;
 259                  $msg = sprintf('session_write_close(): Failed to write session data with "%s" handler', \get_class($handler));
 260              }
 261  
 262              return $previousHandler ? $previousHandler($type, $msg, $file, $line) : false;
 263          });
 264  
 265          try {
 266              session_write_close();
 267          } finally {
 268              restore_error_handler();
 269  
 270              // Restore only if not empty
 271              if ($_SESSION) {
 272                  $_SESSION = $session;
 273              }
 274          }
 275  
 276          $this->closed = true;
 277          $this->started = false;
 278      }
 279  
 280      /**
 281       * {@inheritdoc}
 282       */
 283      public function clear()
 284      {
 285          // clear out the bags
 286          foreach ($this->bags as $bag) {
 287              $bag->clear();
 288          }
 289  
 290          // clear out the session
 291          $_SESSION = [];
 292  
 293          // reconnect the bags to the session
 294          $this->loadSession();
 295      }
 296  
 297      /**
 298       * {@inheritdoc}
 299       */
 300      public function registerBag(SessionBagInterface $bag)
 301      {
 302          if ($this->started) {
 303              throw new \LogicException('Cannot register a bag when the session is already started.');
 304          }
 305  
 306          $this->bags[$bag->getName()] = $bag;
 307      }
 308  
 309      /**
 310       * {@inheritdoc}
 311       */
 312      public function getBag($name)
 313      {
 314          if (!isset($this->bags[$name])) {
 315              throw new \InvalidArgumentException(sprintf('The SessionBagInterface "%s" is not registered.', $name));
 316          }
 317  
 318          if (!$this->started && $this->saveHandler->isActive()) {
 319              $this->loadSession();
 320          } elseif (!$this->started) {
 321              $this->start();
 322          }
 323  
 324          return $this->bags[$name];
 325      }
 326  
 327      public function setMetadataBag(MetadataBag $metaBag = null)
 328      {
 329          if (null === $metaBag) {
 330              $metaBag = new MetadataBag();
 331          }
 332  
 333          $this->metadataBag = $metaBag;
 334      }
 335  
 336      /**
 337       * Gets the MetadataBag.
 338       *
 339       * @return MetadataBag
 340       */
 341      public function getMetadataBag()
 342      {
 343          return $this->metadataBag;
 344      }
 345  
 346      /**
 347       * {@inheritdoc}
 348       */
 349      public function isStarted()
 350      {
 351          return $this->started;
 352      }
 353  
 354      /**
 355       * Sets session.* ini variables.
 356       *
 357       * For convenience we omit 'session.' from the beginning of the keys.
 358       * Explicitly ignores other ini keys.
 359       *
 360       * @param array $options Session ini directives [key => value]
 361       *
 362       * @see https://php.net/session.configuration
 363       */
 364      public function setOptions(array $options)
 365      {
 366          if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
 367              return;
 368          }
 369  
 370          $validOptions = array_flip([
 371              'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
 372              'cookie_lifetime', 'cookie_path', 'cookie_secure', 'cookie_samesite',
 373              'entropy_file', 'entropy_length', 'gc_divisor',
 374              'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
 375              'hash_function', 'lazy_write', 'name', 'referer_check',
 376              'serialize_handler', 'use_strict_mode', 'use_cookies',
 377              'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
 378              'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
 379              'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags',
 380              'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
 381          ]);
 382  
 383          foreach ($options as $key => $value) {
 384              if (isset($validOptions[$key])) {
 385                  if ('cookie_samesite' === $key && \PHP_VERSION_ID < 70300) {
 386                      // PHP < 7.3 does not support same_site cookies. We will emulate it in
 387                      // the start() method instead.
 388                      $this->emulateSameSite = $value;
 389                      continue;
 390                  }
 391                  ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value);
 392              }
 393          }
 394      }
 395  
 396      /**
 397       * Registers session save handler as a PHP session handler.
 398       *
 399       * To use internal PHP session save handlers, override this method using ini_set with
 400       * session.save_handler and session.save_path e.g.
 401       *
 402       *     ini_set('session.save_handler', 'files');
 403       *     ini_set('session.save_path', '/tmp');
 404       *
 405       * or pass in a \SessionHandler instance which configures session.save_handler in the
 406       * constructor, for a template see NativeFileSessionHandler.
 407       *
 408       * @see https://php.net/session-set-save-handler
 409       * @see https://php.net/sessionhandlerinterface
 410       * @see https://php.net/sessionhandler
 411       *
 412       * @param AbstractProxy|\SessionHandlerInterface|null $saveHandler
 413       *
 414       * @throws \InvalidArgumentException
 415       */
 416      public function setSaveHandler($saveHandler = null)
 417      {
 418          if (!$saveHandler instanceof AbstractProxy &&
 419              !$saveHandler instanceof \SessionHandlerInterface &&
 420              null !== $saveHandler) {
 421              throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.');
 422          }
 423  
 424          // Wrap $saveHandler in proxy and prevent double wrapping of proxy
 425          if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
 426              $saveHandler = new SessionHandlerProxy($saveHandler);
 427          } elseif (!$saveHandler instanceof AbstractProxy) {
 428              $saveHandler = new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler()));
 429          }
 430          $this->saveHandler = $saveHandler;
 431  
 432          if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
 433              return;
 434          }
 435  
 436          if ($this->saveHandler instanceof SessionHandlerProxy) {
 437              session_set_save_handler($this->saveHandler, false);
 438          }
 439      }
 440  
 441      /**
 442       * Load the session with attributes.
 443       *
 444       * After starting the session, PHP retrieves the session from whatever handlers
 445       * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()).
 446       * PHP takes the return value from the read() handler, unserializes it
 447       * and populates $_SESSION with the result automatically.
 448       */
 449      protected function loadSession(array &$session = null)
 450      {
 451          if (null === $session) {
 452              $session = &$_SESSION;
 453          }
 454  
 455          $bags = array_merge($this->bags, [$this->metadataBag]);
 456  
 457          foreach ($bags as $bag) {
 458              $key = $bag->getStorageKey();
 459              $session[$key] = isset($session[$key]) && \is_array($session[$key]) ? $session[$key] : [];
 460              $bag->initialize($session[$key]);
 461          }
 462  
 463          $this->started = true;
 464          $this->closed = false;
 465      }
 466  }


Generated: Mon Nov 25 19:05:08 2024 Cross-referenced by PHPXref 0.7.1