Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
NativeSessionStorage.php
001 <?php
002
003 /*
004 * This file is part of the Symfony package.
005 *
006 * (c) Fabien Potencier <fabien@symfony.com>
007 *
008 * For the full copyright and license information, please view the LICENSE
009 * file that was distributed with this source code.
010 */
011
012 namespace Symfony\Component\HttpFoundation\Session\Storage;
013
014 use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
015 use Symfony\Component\HttpFoundation\Session\SessionUtils;
016 use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
017 use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
018 use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
019
020 /**
021 * This provides a base class for session attribute storage.
022 *
023 * @author Drak <drak@zikula.org>
024 */
025 class NativeSessionStorage implements SessionStorageInterface
026 {
027 /**
028 * @var SessionBagInterface[]
029 */
030 protected $bags = [];
031
032 /**
033 * @var bool
034 */
035 protected $started = false;
036
037 /**
038 * @var bool
039 */
040 protected $closed = false;
041
042 /**
043 * @var AbstractProxy|\SessionHandlerInterface
044 */
045 protected $saveHandler;
046
047 /**
048 * @var MetadataBag
049 */
050 protected $metadataBag;
051
052 /**
053 * @var string|null
054 */
055 private $emulateSameSite;
056
057 /**
058 * Depending on how you want the storage driver to behave you probably
059 * want to override this constructor entirely.
060 *
061 * List of options for $options array with their defaults.
062 *
063 * @see https://php.net/session.configuration for options
064 * but we omit 'session.' from the beginning of the keys for convenience.
065 *
066 * ("auto_start", is not supported as it tells PHP to start a session before
067 * PHP starts to execute user-land code. Setting during runtime has no effect).
068 *
069 * cache_limiter, "" (use "0" to prevent headers from being sent entirely).
070 * cache_expire, "0"
071 * cookie_domain, ""
072 * cookie_httponly, ""
073 * cookie_lifetime, "0"
074 * cookie_path, "/"
075 * cookie_secure, ""
076 * cookie_samesite, null
077 * entropy_file, ""
078 * entropy_length, "0"
079 * gc_divisor, "100"
080 * gc_maxlifetime, "1440"
081 * gc_probability, "1"
082 * hash_bits_per_character, "4"
083 * hash_function, "0"
084 * lazy_write, "1"
085 * name, "PHPSESSID"
086 * referer_check, ""
087 * serialize_handler, "php"
088 * use_strict_mode, "0"
089 * use_cookies, "1"
090 * use_only_cookies, "1"
091 * use_trans_sid, "0"
092 * upload_progress.enabled, "1"
093 * upload_progress.cleanup, "1"
094 * upload_progress.prefix, "upload_progress_"
095 * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS"
096 * upload_progress.freq, "1%"
097 * upload_progress.min-freq, "1"
098 * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
099 * 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 }
467