1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Jasny\Session; |
||
6 | |||
7 | use Jasny\Session\Flash\FlashBag; |
||
8 | use Jasny\Session\Flash\FlashTrait; |
||
9 | |||
10 | /** |
||
11 | * Wrapper round $_SESSION. |
||
12 | */ |
||
13 | class GlobalSession implements SessionInterface |
||
14 | { |
||
15 | use FlashTrait; |
||
16 | |||
17 | /** @var array<string,mixed> */ |
||
18 | protected array $options; |
||
19 | |||
20 | /** |
||
21 | * Session constructor. |
||
22 | * |
||
23 | * @param array<string,mixed> $options Passed to session_start() |
||
24 | * @param FlashBag|null $flashBag |
||
25 | */ |
||
26 | 8 | public function __construct(array $options = [], ?FlashBag $flashBag = null) |
|
27 | { |
||
28 | 8 | $this->options = $options; |
|
29 | 8 | $this->flashBag = $flashBag ?? new FlashBag(); |
|
30 | 8 | } |
|
31 | |||
32 | /** |
||
33 | * Start the session. |
||
34 | * @see session_start() |
||
35 | */ |
||
36 | 3 | public function start(): void |
|
37 | { |
||
38 | 3 | session_start($this->options); |
|
39 | 3 | } |
|
40 | |||
41 | /** |
||
42 | * Write session data and end session. |
||
43 | * @see session_write_close() |
||
44 | */ |
||
45 | 3 | public function stop(): void |
|
46 | { |
||
47 | 3 | session_write_close(); |
|
48 | 3 | } |
|
49 | |||
50 | /** |
||
51 | * Discard session array changes and finish session. |
||
52 | * @see session_abort() |
||
53 | * |
||
54 | * Only a shallow clone is done to save the original data. If the session data contains objects, make sure that |
||
55 | * `__clone()` is overwritten, so it does a deep clone. |
||
56 | */ |
||
57 | 2 | public function abort(): void |
|
58 | { |
||
59 | 2 | session_abort(); |
|
60 | 2 | } |
|
61 | |||
62 | |||
63 | /** |
||
64 | * Get the sessions status. |
||
65 | * @see session_status() |
||
66 | */ |
||
67 | 5 | public function status(): int |
|
68 | { |
||
69 | 5 | return session_status(); |
|
70 | } |
||
71 | |||
72 | |||
73 | /** |
||
74 | * Clear all data from the session. |
||
75 | */ |
||
76 | 1 | public function clear(): void |
|
77 | { |
||
78 | 1 | $this->assertStarted(); |
|
79 | |||
80 | 1 | $_SESSION = []; |
|
81 | 1 | } |
|
82 | |||
83 | /** |
||
84 | * @inheritDoc |
||
85 | */ |
||
86 | 1 | public function kill(): void |
|
87 | { |
||
88 | 1 | $this->assertStarted(); |
|
89 | |||
90 | 1 | $this->removeCookie(session_name()); |
|
91 | 1 | $_SESSION = []; |
|
92 | 1 | session_destroy(); |
|
93 | 1 | } |
|
94 | |||
95 | /** |
||
96 | * Remove the session cookie. |
||
97 | * @codeCoverageIgnore |
||
98 | */ |
||
99 | protected function removeCookie(string $name): void |
||
100 | { |
||
101 | if (!(bool)ini_get("session.use_cookies")) { |
||
102 | return; |
||
103 | } |
||
104 | |||
105 | $options = ['expires' => time() - 42000] + session_get_cookie_params(); |
||
106 | unset($options['lifetime']); |
||
107 | |||
108 | setcookie($name, '', $options); |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * @inheritDoc |
||
113 | */ |
||
114 | 1 | public function rotate(?callable $copy = null): void |
|
115 | { |
||
116 | 1 | $this->assertStarted(); |
|
117 | |||
118 | 1 | $data = isset($copy) ? $copy($_SESSION) : []; |
|
119 | |||
120 | 1 | $_SESSION = []; |
|
121 | 1 | $this->regenerateId(true); |
|
122 | |||
123 | 1 | $_SESSION = $data; |
|
124 | 1 | } |
|
125 | |||
126 | /** |
||
127 | * Wrapper around `session_regenerate_id()` |
||
128 | * @codeCoverageIgnore |
||
129 | */ |
||
130 | protected function regenerateId(bool $delete): void |
||
131 | { |
||
132 | session_regenerate_id($delete); |
||
133 | } |
||
134 | |||
135 | |||
136 | /** |
||
137 | * @param string $offset |
||
138 | * @return bool |
||
139 | */ |
||
140 | 1 | public function offsetExists($offset): bool |
|
141 | { |
||
142 | 1 | $this->assertStarted(); |
|
143 | |||
144 | 1 | return array_key_exists($offset, $_SESSION); |
|
145 | } |
||
146 | |||
147 | /** |
||
148 | * @param string $offset |
||
149 | * @return mixed |
||
150 | */ |
||
151 | 1 | public function offsetGet($offset) |
|
152 | { |
||
153 | 1 | $this->assertStarted(); |
|
154 | |||
155 | 1 | return $_SESSION[$offset]; |
|
156 | } |
||
157 | |||
158 | /** |
||
159 | * @param string $offset |
||
160 | * @param mixed $value |
||
161 | */ |
||
162 | 4 | public function offsetSet($offset, $value): void |
|
0 ignored issues
–
show
introduced
by
![]() |
|||
163 | { |
||
164 | 4 | $this->assertStarted(); |
|
165 | |||
166 | 3 | $_SESSION[$offset] = $value; |
|
167 | 3 | } |
|
168 | |||
169 | /** |
||
170 | * @param string $offset |
||
171 | */ |
||
172 | 1 | public function offsetUnset($offset): void |
|
173 | { |
||
174 | 1 | $this->assertStarted(); |
|
175 | |||
176 | 1 | unset($_SESSION[$offset]); |
|
177 | 1 | } |
|
178 | |||
179 | |||
180 | /** |
||
181 | * Assert that there is an active session. |
||
182 | * |
||
183 | * @throws NoSessionException |
||
184 | */ |
||
185 | 7 | protected function assertStarted(): void |
|
186 | { |
||
187 | 7 | if (session_status() !== \PHP_SESSION_ACTIVE) { |
|
188 | 1 | throw new NoSessionException("Session not started"); |
|
189 | } |
||
190 | 6 | } |
|
191 | } |
||
192 |