[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |