• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 12673986434

08 Jan 2025 03:42PM UTC coverage: 84.455% (+0.001%) from 84.454%
12673986434

Pull #9385

github

web-flow
Merge 06e47f0ee into e475fd8fa
Pull Request #9385: refactor: Fix phpstan expr.resultUnused

20699 of 24509 relevant lines covered (84.45%)

190.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.53
/system/Session/Session.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\Session;
15

16
use CodeIgniter\Cookie\Cookie;
17
use CodeIgniter\I18n\Time;
18
use Config\Cookie as CookieConfig;
19
use Config\Session as SessionConfig;
20
use Psr\Log\LoggerAwareTrait;
21
use SessionHandlerInterface;
22

23
/**
24
 * Implementation of CodeIgniter session container.
25
 *
26
 * Session configuration is done through session variables and cookie related
27
 * variables in app/config/App.php
28
 *
29
 * @property string $session_id
30
 * @see \CodeIgniter\Session\SessionTest
31
 */
32
class Session implements SessionInterface
33
{
34
    use LoggerAwareTrait;
35

36
    /**
37
     * Instance of the driver to use.
38
     *
39
     * @var SessionHandlerInterface
40
     */
41
    protected $driver;
42

43
    /**
44
     * The storage driver to use: files, database, redis, memcached
45
     *
46
     * @var string
47
     *
48
     * @deprecated Use $this->config->driver.
49
     */
50
    protected $sessionDriverName;
51

52
    /**
53
     * The session cookie name, must contain only [0-9a-z_-] characters.
54
     *
55
     * @var string
56
     *
57
     * @deprecated Use $this->config->cookieName.
58
     */
59
    protected $sessionCookieName = 'ci_session';
60

61
    /**
62
     * The number of SECONDS you want the session to last.
63
     * Setting it to 0 (zero) means expire when the browser is closed.
64
     *
65
     * @var int
66
     *
67
     * @deprecated Use $this->config->expiration.
68
     */
69
    protected $sessionExpiration = 7200;
70

71
    /**
72
     * The location to save sessions to, driver dependent.
73
     *
74
     * For the 'files' driver, it's a path to a writable directory.
75
     * WARNING: Only absolute paths are supported!
76
     *
77
     * For the 'database' driver, it's a table name.
78
     *
79
     * @todo address memcache & redis needs
80
     *
81
     * IMPORTANT: You are REQUIRED to set a valid save path!
82
     *
83
     * @var string
84
     *
85
     * @deprecated Use $this->config->savePath.
86
     */
87
    protected $sessionSavePath;
88

89
    /**
90
     * Whether to match the user's IP address when reading the session data.
91
     *
92
     * WARNING: If you're using the database driver, don't forget to update
93
     * your session table's PRIMARY KEY when changing this setting.
94
     *
95
     * @var bool
96
     *
97
     * @deprecated Use $this->config->matchIP.
98
     */
99
    protected $sessionMatchIP = false;
100

101
    /**
102
     * How many seconds between CI regenerating the session ID.
103
     *
104
     * @var int
105
     *
106
     * @deprecated Use $this->config->timeToUpdate.
107
     */
108
    protected $sessionTimeToUpdate = 300;
109

110
    /**
111
     * Whether to destroy session data associated with the old session ID
112
     * when auto-regenerating the session ID. When set to FALSE, the data
113
     * will be later deleted by the garbage collector.
114
     *
115
     * @var bool
116
     *
117
     * @deprecated Use $this->config->regenerateDestroy.
118
     */
119
    protected $sessionRegenerateDestroy = false;
120

121
    /**
122
     * The session cookie instance.
123
     *
124
     * @var Cookie
125
     */
126
    protected $cookie;
127

128
    /**
129
     * The domain name to use for cookies.
130
     * Set to .your-domain.com for site-wide cookies.
131
     *
132
     * @var string
133
     *
134
     * @deprecated No longer used.
135
     */
136
    protected $cookieDomain = '';
137

138
    /**
139
     * Path used for storing cookies.
140
     * Typically will be a forward slash.
141
     *
142
     * @var string
143
     *
144
     * @deprecated No longer used.
145
     */
146
    protected $cookiePath = '/';
147

148
    /**
149
     * Cookie will only be set if a secure HTTPS connection exists.
150
     *
151
     * @var bool
152
     *
153
     * @deprecated No longer used.
154
     */
155
    protected $cookieSecure = false;
156

157
    /**
158
     * Cookie SameSite setting as described in RFC6265
159
     * Must be 'None', 'Lax' or 'Strict'.
160
     *
161
     * @var string
162
     *
163
     * @deprecated No longer used.
164
     */
165
    protected $cookieSameSite = Cookie::SAMESITE_LAX;
166

167
    /**
168
     * sid regex expression
169
     *
170
     * @var string
171
     */
172
    protected $sidRegexp;
173

174
    /**
175
     * Session Config
176
     */
177
    protected SessionConfig $config;
178

179
    /**
180
     * Constructor.
181
     *
182
     * Extract configuration settings and save them here.
183
     */
184
    public function __construct(SessionHandlerInterface $driver, SessionConfig $config)
185
    {
186
        $this->driver = $driver;
6,525✔
187

188
        $this->config = $config;
6,525✔
189

190
        $cookie = config(CookieConfig::class);
6,525✔
191

192
        $this->cookie = (new Cookie($this->config->cookieName, '', [
6,525✔
193
            'expires'  => $this->config->expiration === 0 ? 0 : Time::now()->getTimestamp() + $this->config->expiration,
6,525✔
194
            'path'     => $cookie->path,
6,525✔
195
            'domain'   => $cookie->domain,
6,525✔
196
            'secure'   => $cookie->secure,
6,525✔
197
            'httponly' => true, // for security
6,525✔
198
            'samesite' => $cookie->samesite ?? Cookie::SAMESITE_LAX,
6,525✔
199
            'raw'      => $cookie->raw ?? false,
6,525✔
200
        ]))->withPrefix(''); // Cookie prefix should be ignored.
6,525✔
201

202
        helper('array');
6,525✔
203
    }
204

205
    /**
206
     * Initialize the session container and starts up the session.
207
     *
208
     * @return $this|null
209
     */
210
    public function start()
211
    {
212
        if (is_cli() && ENVIRONMENT !== 'testing') {
81✔
213
            // @codeCoverageIgnoreStart
214
            $this->logger->debug('Session: Initialization under CLI aborted.');
×
215

216
            return null;
×
217
            // @codeCoverageIgnoreEnd
218
        }
219

220
        if ((bool) ini_get('session.auto_start')) {
81✔
221
            $this->logger->error('Session: session.auto_start is enabled in php.ini. Aborting.');
×
222

223
            return null;
×
224
        }
225

226
        if (session_status() === PHP_SESSION_ACTIVE) {
81✔
227
            $this->logger->warning('Session: Sessions is enabled, and one exists. Please don\'t $session->start();');
×
228

229
            return null;
×
230
        }
231

232
        $this->configure();
81✔
233
        $this->setSaveHandler();
81✔
234

235
        // Sanitize the cookie, because apparently PHP doesn't do that for userspace handlers
236
        if (isset($_COOKIE[$this->config->cookieName])
81✔
237
            && (! is_string($_COOKIE[$this->config->cookieName]) || preg_match('#\A' . $this->sidRegexp . '\z#', $_COOKIE[$this->config->cookieName]) !== 1)
81✔
238
        ) {
239
            unset($_COOKIE[$this->config->cookieName]);
×
240
        }
241

242
        $this->startSession();
81✔
243

244
        // Is session ID auto-regeneration configured? (ignoring ajax requests)
245
        if ((! isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest')
81✔
246
            && ($regenerateTime = $this->config->timeToUpdate) > 0
81✔
247
        ) {
248
            if (! isset($_SESSION['__ci_last_regenerate'])) {
79✔
249
                $_SESSION['__ci_last_regenerate'] = Time::now()->getTimestamp();
78✔
250
            } elseif ($_SESSION['__ci_last_regenerate'] < (Time::now()->getTimestamp() - $regenerateTime)) {
3✔
251
                $this->regenerate($this->config->regenerateDestroy);
1✔
252
            }
253
        }
254
        // Another work-around ... PHP doesn't seem to send the session cookie
255
        // unless it is being currently created or regenerated
256
        elseif (isset($_COOKIE[$this->config->cookieName]) && $_COOKIE[$this->config->cookieName] === session_id()) {
2✔
257
            $this->setCookie();
×
258
        }
259

260
        $this->initVars();
81✔
261
        $this->logger->debug("Session: Class initialized using '" . $this->config->driver . "' driver.");
81✔
262

263
        return $this;
81✔
264
    }
265

266
    /**
267
     * Destroys the current session.
268
     *
269
     * @deprecated Use destroy() instead.
270
     */
271
    public function stop()
272
    {
273
        $this->destroy();
×
274
    }
275

276
    /**
277
     * Configuration.
278
     *
279
     * Handle input binds and configuration defaults.
280
     */
281
    protected function configure()
282
    {
283
        ini_set('session.name', $this->config->cookieName);
81✔
284

285
        $sameSite = $this->cookie->getSameSite() ?: ucfirst(Cookie::SAMESITE_LAX);
81✔
286

287
        $params = [
81✔
288
            'lifetime' => $this->config->expiration,
81✔
289
            'path'     => $this->cookie->getPath(),
81✔
290
            'domain'   => $this->cookie->getDomain(),
81✔
291
            'secure'   => $this->cookie->isSecure(),
81✔
292
            'httponly' => true, // HTTP only; Yes, this is intentional and not configurable for security reasons.
81✔
293
            'samesite' => $sameSite,
81✔
294
        ];
81✔
295

296
        ini_set('session.cookie_samesite', $sameSite);
81✔
297
        session_set_cookie_params($params);
81✔
298

299
        if ($this->config->expiration > 0) {
81✔
300
            ini_set('session.gc_maxlifetime', (string) $this->config->expiration);
80✔
301
        }
302

303
        if ($this->config->savePath !== '') {
81✔
304
            ini_set('session.save_path', $this->config->savePath);
40✔
305
        }
306

307
        // Security is king
308
        ini_set('session.use_trans_sid', '0');
81✔
309
        ini_set('session.use_strict_mode', '1');
81✔
310
        ini_set('session.use_cookies', '1');
81✔
311
        ini_set('session.use_only_cookies', '1');
81✔
312

313
        $this->configureSidLength();
81✔
314
    }
315

316
    /**
317
     * Configure session ID length
318
     *
319
     * To make life easier, we force the PHP defaults. Because PHP9 forces them.
320
     * See https://wiki.php.net/rfc/deprecations_php_8_4#sessionsid_length_and_sessionsid_bits_per_character
321
     */
322
    protected function configureSidLength()
323
    {
324
        $bitsPerCharacter = (int) ini_get('session.sid_bits_per_character');
81✔
325
        $sidLength        = (int) ini_get('session.sid_length');
81✔
326

327
        // We force the PHP defaults.
328
        if (PHP_VERSION_ID < 90000) {
81✔
329
            if ($bitsPerCharacter !== 4) {
81✔
330
                ini_set('session.sid_bits_per_character', '4');
×
331
            }
332
            if ($sidLength !== 32) {
81✔
333
                ini_set('session.sid_length', '32');
×
334
            }
335
        }
336

337
        $this->sidRegexp = '[0-9a-f]{32}';
81✔
338
    }
339

340
    /**
341
     * Handle temporary variables
342
     *
343
     * Clears old "flash" data, marks the new one for deletion and handles
344
     * "temp" data deletion.
345
     */
346
    protected function initVars()
347
    {
348
        if (! isset($_SESSION['__ci_vars'])) {
81✔
349
            return;
81✔
350
        }
351

352
        $currentTime = Time::now()->getTimestamp();
2✔
353

354
        foreach ($_SESSION['__ci_vars'] as $key => &$value) {
2✔
355
            if ($value === 'new') {
2✔
356
                $_SESSION['__ci_vars'][$key] = 'old';
2✔
357
            }
358
            // DO NOT move this above the 'new' check!
359
            elseif ($value === 'old' || $value < $currentTime) {
1✔
360
                unset($_SESSION[$key], $_SESSION['__ci_vars'][$key]);
1✔
361
            }
362
        }
363

364
        if ($_SESSION['__ci_vars'] === []) {
2✔
365
            unset($_SESSION['__ci_vars']);
1✔
366
        }
367
    }
368

369
    /**
370
     * Regenerates the session ID.
371
     *
372
     * @param bool $destroy Should old session data be destroyed?
373
     */
374
    public function regenerate(bool $destroy = false)
375
    {
376
        $_SESSION['__ci_last_regenerate'] = Time::now()->getTimestamp();
×
377
        session_regenerate_id($destroy);
×
378

379
        $this->removeOldSessionCookie();
×
380
    }
381

382
    private function removeOldSessionCookie(): void
383
    {
384
        $response              = service('response');
×
385
        $cookieStoreInResponse = $response->getCookieStore();
×
386

387
        if (! $cookieStoreInResponse->has($this->config->cookieName)) {
×
388
            return;
×
389
        }
390

391
        // CookieStore is immutable.
392
        $newCookieStore = $cookieStoreInResponse->remove($this->config->cookieName);
×
393

394
        // But clear() method clears cookies in the object (not immutable).
395
        $cookieStoreInResponse->clear();
×
396

397
        foreach ($newCookieStore as $cookie) {
×
398
            $response->setCookie($cookie);
×
399
        }
400
    }
401

402
    /**
403
     * Destroys the current session.
404
     */
405
    public function destroy()
406
    {
407
        if (ENVIRONMENT === 'testing') {
×
408
            return;
×
409
        }
410

411
        session_destroy();
×
412
    }
413

414
    /**
415
     * Writes session data and close the current session.
416
     *
417
     * @return void
418
     */
419
    public function close()
420
    {
421
        if (ENVIRONMENT === 'testing') {
×
422
            return;
×
423
        }
424

425
        session_write_close();
×
426
    }
427

428
    /**
429
     * Sets user data into the session.
430
     *
431
     * If $data is a string, then it is interpreted as a session property
432
     * key, and  $value is expected to be non-null.
433
     *
434
     * If $data is an array, it is expected to be an array of key/value pairs
435
     * to be set as session properties.
436
     *
437
     * @param array|string                            $data  Property name or associative array of properties
438
     * @param array|bool|float|int|object|string|null $value Property value if single key provided
439
     */
440
    public function set($data, $value = null)
441
    {
442
        if (is_array($data)) {
114✔
443
            foreach ($data as $key => &$value) {
12✔
444
                if (is_int($key)) {
12✔
445
                    $_SESSION[$value] = null;
1✔
446
                } else {
447
                    $_SESSION[$key] = $value;
11✔
448
                }
449
            }
450

451
            return;
12✔
452
        }
453

454
        $_SESSION[$data] = $value;
104✔
455
    }
456

457
    /**
458
     * Get user data that has been set in the session.
459
     *
460
     * If the property exists as "normal", returns it.
461
     * Otherwise, returns an array of any temp or flash data values with the
462
     * property key.
463
     *
464
     * Replaces the legacy method $session->userdata();
465
     *
466
     * @param non-empty-string|null $key Identifier of the session property to retrieve
467
     *
468
     * @return array|bool|float|int|object|string|null The property value(s)
469
     */
470
    public function get(?string $key = null)
471
    {
472
        if ($key !== null && $key !== '' && (null !== ($value = $_SESSION[$key] ?? null) || null !== ($value = dot_array_search($key, $_SESSION ?? [])))) {
67✔
473
            return $value;
51✔
474
        }
475

476
        if (! isset($_SESSION) || $_SESSION === []) {
18✔
477
            return $key === null ? [] : null;
12✔
478
        }
479

480
        if ($key !== null && $key !== '') {
6✔
481
            return null;
5✔
482
        }
483

484
        $userdata = [];
1✔
485
        $_exclude = array_merge(['__ci_vars'], $this->getFlashKeys(), $this->getTempKeys());
1✔
486

487
        $keys = array_keys($_SESSION);
1✔
488

489
        foreach ($keys as $key) {
1✔
490
            if (! in_array($key, $_exclude, true)) {
1✔
491
                $userdata[$key] = $_SESSION[$key];
1✔
492
            }
493
        }
494

495
        return $userdata;
1✔
496
    }
497

498
    /**
499
     * Returns whether an index exists in the session array.
500
     *
501
     * @param string $key Identifier of the session property we are interested in.
502
     */
503
    public function has(string $key): bool
504
    {
505
        return isset($_SESSION[$key]);
38✔
506
    }
507

508
    /**
509
     * Push new value onto session value that is array.
510
     *
511
     * @param string $key  Identifier of the session property we are interested in.
512
     * @param array  $data value to be pushed to existing session key.
513
     */
514
    public function push(string $key, array $data)
515
    {
516
        if ($this->has($key) && is_array($value = $this->get($key))) {
1✔
517
            $this->set($key, array_merge($value, $data));
1✔
518
        }
519
    }
520

521
    /**
522
     * Remove one or more session properties.
523
     *
524
     * If $key is an array, it is interpreted as an array of string property
525
     * identifiers to remove. Otherwise, it is interpreted as the identifier
526
     * of a specific session property to remove.
527
     *
528
     * @param array|string $key Identifier of the session property or properties to remove.
529
     */
530
    public function remove($key)
531
    {
532
        if (is_array($key)) {
2✔
533
            foreach ($key as $k) {
1✔
534
                unset($_SESSION[$k]);
1✔
535
            }
536

537
            return;
1✔
538
        }
539

540
        unset($_SESSION[$key]);
1✔
541
    }
542

543
    /**
544
     * Magic method to set variables in the session by simply calling
545
     *  $session->foo = bar;
546
     *
547
     * @param string       $key   Identifier of the session property to set.
548
     * @param array|string $value
549
     */
550
    public function __set(string $key, $value)
551
    {
552
        $_SESSION[$key] = $value;
1✔
553
    }
554

555
    /**
556
     * Magic method to get session variables by simply calling
557
     *  $foo = $session->foo;
558
     *
559
     * @param string $key Identifier of the session property to remove.
560
     *
561
     * @return string|null
562
     */
563
    public function __get(string $key)
564
    {
565
        // Note: Keep this order the same, just in case somebody wants to
566
        // use 'session_id' as a session data key, for whatever reason
567
        if (isset($_SESSION[$key])) {
1✔
568
            return $_SESSION[$key];
1✔
569
        }
570

571
        if ($key === 'session_id') {
×
572
            return session_id();
×
573
        }
574

575
        return null;
×
576
    }
577

578
    /**
579
     * Magic method to check for session variables.
580
     * Different from has() in that it will validate 'session_id' as well.
581
     * Mostly used by internal PHP functions, users should stick to has()
582
     *
583
     * @param string $key Identifier of the session property to remove.
584
     */
585
    public function __isset(string $key): bool
586
    {
587
        return isset($_SESSION[$key]) || ($key === 'session_id');
2✔
588
    }
589

590
    /**
591
     * Sets data into the session that will only last for a single request.
592
     * Perfect for use with single-use status update messages.
593
     *
594
     * If $data is an array, it is interpreted as an associative array of
595
     * key/value pairs for flashdata properties.
596
     * Otherwise, it is interpreted as the identifier of a specific
597
     * flashdata property, with $value containing the property value.
598
     *
599
     * @param array|string                            $data  Property identifier or associative array of properties
600
     * @param array|bool|float|int|object|string|null $value Property value if $data is a scalar
601
     */
602
    public function setFlashdata($data, $value = null)
603
    {
604
        $this->set($data, $value);
11✔
605
        $this->markAsFlashdata(is_array($data) ? array_keys($data) : $data);
11✔
606
    }
607

608
    /**
609
     * Retrieve one or more items of flash data from the session.
610
     *
611
     * If the item key is null, return all flashdata.
612
     *
613
     * @param string $key Property identifier
614
     *
615
     * @return array|null The requested property value, or an associative array  of them
616
     */
617
    public function getFlashdata(?string $key = null)
618
    {
619
        if (isset($key)) {
×
620
            return (isset($_SESSION['__ci_vars'], $_SESSION['__ci_vars'][$key], $_SESSION[$key])
×
621
                && ! is_int($_SESSION['__ci_vars'][$key])) ? $_SESSION[$key] : null;
×
622
        }
623

624
        $flashdata = [];
×
625

626
        if (isset($_SESSION['__ci_vars'])) {
×
627
            foreach ($_SESSION['__ci_vars'] as $key => &$value) {
×
628
                if (! is_int($value)) {
×
629
                    $flashdata[$key] = $_SESSION[$key];
×
630
                }
631
            }
632
        }
633

634
        return $flashdata;
×
635
    }
636

637
    /**
638
     * Keeps a single piece of flash data alive for one more request.
639
     *
640
     * @param array|string $key Property identifier or array of them
641
     */
642
    public function keepFlashdata($key)
643
    {
644
        $this->markAsFlashdata($key);
1✔
645
    }
646

647
    /**
648
     * Mark a session property or properties as flashdata.
649
     *
650
     * @param array|string $key Property identifier or array of them
651
     *
652
     * @return bool False if any of the properties are not already set
653
     */
654
    public function markAsFlashdata($key): bool
655
    {
656
        if (is_array($key)) {
11✔
657
            foreach ($key as $sessionKey) {
1✔
658
                if (! isset($_SESSION[$sessionKey])) {
1✔
659
                    return false;
×
660
                }
661
            }
662

663
            $new = array_fill_keys($key, 'new');
1✔
664

665
            $_SESSION['__ci_vars'] = isset($_SESSION['__ci_vars']) ? array_merge($_SESSION['__ci_vars'], $new) : $new;
1✔
666

667
            return true;
1✔
668
        }
669

670
        if (! isset($_SESSION[$key])) {
10✔
671
            return false;
×
672
        }
673

674
        $_SESSION['__ci_vars'][$key] = 'new';
10✔
675

676
        return true;
10✔
677
    }
678

679
    /**
680
     * Unmark data in the session as flashdata.
681
     *
682
     * @param array|string $key Property identifier or array of them
683
     */
684
    public function unmarkFlashdata($key)
685
    {
686
        if (! isset($_SESSION['__ci_vars'])) {
1✔
687
            return;
×
688
        }
689

690
        if (! is_array($key)) {
1✔
691
            $key = [$key];
1✔
692
        }
693

694
        foreach ($key as $k) {
1✔
695
            if (isset($_SESSION['__ci_vars'][$k]) && ! is_int($_SESSION['__ci_vars'][$k])) {
1✔
696
                unset($_SESSION['__ci_vars'][$k]);
1✔
697
            }
698
        }
699

700
        if ($_SESSION['__ci_vars'] === []) {
1✔
701
            unset($_SESSION['__ci_vars']);
1✔
702
        }
703
    }
704

705
    /**
706
     * Retrieve all of the keys for session data marked as flashdata.
707
     *
708
     * @return array The property names of all flashdata
709
     */
710
    public function getFlashKeys(): array
711
    {
712
        if (! isset($_SESSION['__ci_vars'])) {
2✔
713
            return [];
1✔
714
        }
715

716
        $keys = [];
1✔
717

718
        foreach (array_keys($_SESSION['__ci_vars']) as $key) {
1✔
719
            if (! is_int($_SESSION['__ci_vars'][$key])) {
1✔
720
                $keys[] = $key;
1✔
721
            }
722
        }
723

724
        return $keys;
1✔
725
    }
726

727
    /**
728
     * Sets new data into the session, and marks it as temporary data
729
     * with a set lifespan.
730
     *
731
     * @param array|string                            $data  Session data key or associative array of items
732
     * @param array|bool|float|int|object|string|null $value Value to store
733
     * @param int                                     $ttl   Time-to-live in seconds
734
     */
735
    public function setTempdata($data, $value = null, int $ttl = 300)
736
    {
737
        $this->set($data, $value);
9✔
738
        $this->markAsTempdata($data, $ttl);
9✔
739
    }
740

741
    /**
742
     * Returns either a single piece of tempdata, or all temp data currently
743
     * in the session.
744
     *
745
     * @param string $key Session data key
746
     *
747
     * @return array|bool|float|int|object|string|null Session data value or null if not found.
748
     */
749
    public function getTempdata(?string $key = null)
750
    {
751
        if (isset($key)) {
5✔
752
            return (isset($_SESSION['__ci_vars'], $_SESSION['__ci_vars'][$key], $_SESSION[$key])
1✔
753
                    && is_int($_SESSION['__ci_vars'][$key])) ? $_SESSION[$key] : null;
1✔
754
        }
755

756
        $tempdata = [];
4✔
757

758
        if (isset($_SESSION['__ci_vars'])) {
4✔
759
            foreach ($_SESSION['__ci_vars'] as $key => &$value) {
3✔
760
                if (is_int($value)) {
3✔
761
                    $tempdata[$key] = $_SESSION[$key];
3✔
762
                }
763
            }
764
        }
765

766
        return $tempdata;
4✔
767
    }
768

769
    /**
770
     * Removes a single piece of temporary data from the session.
771
     *
772
     * @param string $key Session data key
773
     */
774
    public function removeTempdata(string $key)
775
    {
776
        $this->unmarkTempdata($key);
1✔
777
        unset($_SESSION[$key]);
1✔
778
    }
779

780
    /**
781
     * Mark one of more pieces of data as being temporary, meaning that
782
     * it has a set lifespan within the session.
783
     *
784
     * @param array|string $key Property identifier or array of them
785
     * @param int          $ttl Time to live, in seconds
786
     *
787
     * @return bool False if any of the properties were not set
788
     */
789
    public function markAsTempdata($key, int $ttl = 300): bool
790
    {
791
        $ttl += Time::now()->getTimestamp();
9✔
792

793
        if (is_array($key)) {
9✔
794
            $temp = [];
8✔
795

796
            foreach ($key as $k => $v) {
8✔
797
                // Do we have a key => ttl pair, or just a key?
798
                if (is_int($k)) {
8✔
799
                    $k = $v;
1✔
800
                    $v = $ttl;
1✔
801
                } elseif (is_string($v)) {
7✔
802
                    $v = Time::now()->getTimestamp() + $ttl;
6✔
803
                } else {
804
                    $v += Time::now()->getTimestamp();
1✔
805
                }
806

807
                if (! array_key_exists($k, $_SESSION)) {
8✔
808
                    return false;
×
809
                }
810

811
                $temp[$k] = $v;
8✔
812
            }
813

814
            $_SESSION['__ci_vars'] = isset($_SESSION['__ci_vars']) ? array_merge($_SESSION['__ci_vars'], $temp) : $temp;
8✔
815

816
            return true;
8✔
817
        }
818

819
        if (! isset($_SESSION[$key])) {
1✔
820
            return false;
×
821
        }
822

823
        $_SESSION['__ci_vars'][$key] = $ttl;
1✔
824

825
        return true;
1✔
826
    }
827

828
    /**
829
     * Unmarks temporary data in the session, effectively removing its
830
     * lifespan and allowing it to live as long as the session does.
831
     *
832
     * @param array|string $key Property identifier or array of them
833
     */
834
    public function unmarkTempdata($key)
835
    {
836
        if (! isset($_SESSION['__ci_vars'])) {
3✔
837
            return;
×
838
        }
839

840
        if (! is_array($key)) {
3✔
841
            $key = [$key];
2✔
842
        }
843

844
        foreach ($key as $k) {
3✔
845
            if (isset($_SESSION['__ci_vars'][$k]) && is_int($_SESSION['__ci_vars'][$k])) {
3✔
846
                unset($_SESSION['__ci_vars'][$k]);
3✔
847
            }
848
        }
849

850
        if ($_SESSION['__ci_vars'] === []) {
3✔
851
            unset($_SESSION['__ci_vars']);
1✔
852
        }
853
    }
854

855
    /**
856
     * Retrieve the keys of all session data that have been marked as temporary data.
857
     */
858
    public function getTempKeys(): array
859
    {
860
        if (! isset($_SESSION['__ci_vars'])) {
2✔
861
            return [];
1✔
862
        }
863

864
        $keys = [];
1✔
865

866
        foreach (array_keys($_SESSION['__ci_vars']) as $key) {
1✔
867
            if (is_int($_SESSION['__ci_vars'][$key])) {
1✔
868
                $keys[] = $key;
1✔
869
            }
870
        }
871

872
        return $keys;
1✔
873
    }
874

875
    /**
876
     * Sets the driver as the session handler in PHP.
877
     * Extracted for easier testing.
878
     */
879
    protected function setSaveHandler()
880
    {
881
        session_set_save_handler($this->driver, true);
40✔
882
    }
883

884
    /**
885
     * Starts the session.
886
     * Extracted for testing reasons.
887
     */
888
    protected function startSession()
889
    {
890
        if (ENVIRONMENT === 'testing') {
40✔
891
            $_SESSION = [];
40✔
892

893
            return;
40✔
894
        }
895

896
        session_start(); // @codeCoverageIgnore
×
897
    }
898

899
    /**
900
     * Takes care of setting the cookie on the client side.
901
     *
902
     * @codeCoverageIgnore
903
     */
904
    protected function setCookie()
905
    {
906
        $expiration   = $this->config->expiration === 0 ? 0 : Time::now()->getTimestamp() + $this->config->expiration;
×
907
        $this->cookie = $this->cookie->withValue(session_id())->withExpires($expiration);
×
908

909
        $response = service('response');
×
910
        $response->setCookie($this->cookie);
×
911
    }
912
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc