1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
session.php
См. документацию.
1<?php
2
3namespace Bitrix\Main\Session;
4
5use Bitrix\Main\Application;
6use Bitrix\Main\Session\Handlers\AbstractSessionHandler;
7use Bitrix\Main\Session\Handlers\NullSessionHandler;
8use Bitrix\Main\Web\Cookie;
9
10class Session implements SessionInterface, \ArrayAccess
11{
13
14 protected bool $started = false;
15 protected ?\SessionHandlerInterface $sessionHandler;
16 protected bool $lazyStartEnabled = false;
17 protected bool $debug = false;
19 protected bool $ignoringSessionStartErrors = false;
20 protected bool $useStrictMode = true;
21
25 public function __construct(\SessionHandlerInterface $sessionHandler = null)
26 {
27 $this->sessionHandler = $sessionHandler;
28 $this->debugger = new Debugger();
29
30 session_register_shutdown();
31 if ($this->sessionHandler)
32 {
33 session_set_save_handler($this->sessionHandler, false);
34 }
35 }
36
37 public function enableLazyStart(): self
38 {
39 $this->lazyStartEnabled = true;
40
41 return $this;
42 }
43
44 public function disableLazyStart(): self
45 {
46 $this->lazyStartEnabled = false;
47
48 return $this;
49 }
50
51 public function enableDebug(): self
52 {
53 $this->debug = true;
54
55 return $this;
56 }
57
58 public function disableDebug(): self
59 {
60 $this->debug = false;
61
62 return $this;
63 }
64
65 public function enableIgnoringSessionStartErrors(): self
66 {
67 $this->ignoringSessionStartErrors = true;
68
69 return $this;
70 }
71
72 public function disableIgnoringSessionStartErrors(): self
73 {
74 $this->ignoringSessionStartErrors = false;
75
76 return $this;
77 }
78
79 public function isActive(): bool
80 {
81 return session_status() === \PHP_SESSION_ACTIVE;
82 }
83
84 private function isHeadersSent(&$file, &$line): bool
85 {
86 return filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN) && headers_sent($file, $line);
87 }
88
89 public function isAccessible(): bool
90 {
91 return !$this->isHeadersSent($file, $line);
92 }
93
94 public function getId(): string
95 {
96 return session_id();
97 }
98
99 public function setId($id): void
100 {
101 if ($this->isActive())
102 {
103 throw new \RuntimeException('Could not change the ID of an active session');
104 }
105
106 session_id($id);
107 }
108
109 public function getName(): string
110 {
111 return session_name();
112 }
113
114 public function setName($name): void
115 {
116 if ($this->isActive())
117 {
118 throw new \RuntimeException('Could not change the name of an active session');
119 }
120
121 session_name($name);
122 }
123
124 public function getSessionHandler(): ?\SessionHandlerInterface
125 {
126 return $this->sessionHandler;
127 }
128
129 public function start(): bool
130 {
131 if ($this->isStarted())
132 {
133 return true;
134 }
135
136 if ($this->isActive())
137 {
138 throw new \RuntimeException('Could not start session by PHP because session is active.');
139 }
140
141 if ($this->isHeadersSent($file, $line))
142 {
143 throw new \RuntimeException(
144 "Could not start session because headers have already been sent. \"{$file}\":{$line}."
145 );
146 }
147
148 $this->debug('Session tries to start at');
149 $this->detectFirstUsage();
150
151 try
152 {
154 if (!session_start() && !$this->ignoringSessionStartErrors)
155 {
156 throw new \RuntimeException('Could not start session by PHP.');
157 }
158 }
159 catch (\Error $error)
160 {
161 if ($this->shouldLogError($error))
162 {
163 $this->writeToLogError($error);
164 }
165
166 if (!$this->ignoringSessionStartErrors)
167 {
168 throw $error->getPrevious() ?: $error;
169 }
170 }
171 $this->debug('Session started at');
172
173 $this->sessionData = &$_SESSION;
174 $this->started = true;
175
176 if ($this->has('destroyed'))
177 {
178 //todo 100? why?
179 if ($this->get('destroyed') < time() - 100)
180 {
181 $this->clear();
182 }
183 else
184 {
185 $newSessionId = $this->get('newSid');
186 $this->save();
187
188 $this->setId($newSessionId);
189
190 return $this->start();
191 }
192 }
193
194 return true;
195 }
196
197 protected function getSessionStartOptions(): array
198 {
199 $useStrictModeValue = $this->useStrictMode? 1 : 0;
200
201 if ($this->sessionHandler instanceof NullSessionHandler)
202 {
203 return [
204 'use_cookies' => 0,
205 'use_strict_mode' => $useStrictModeValue,
206 ];
207 }
208
209 $options = [
210 'cookie_httponly' => 1,
211 'use_strict_mode' => $useStrictModeValue,
212 ];
213
214 $domain = Cookie::getCookieDomain();
215 if ($domain)
216 {
217 $options['cookie_domain'] = $domain;
218 }
219
220 return $options;
221 }
222
224 {
225 foreach ($settings as $name => $value)
226 {
227 ini_set("session.{$name}", $value);
228 }
229 }
230
231 private function writeToLogError(\Error $error): void
232 {
233 $exceptionHandler = Application::getInstance()->getExceptionHandler();
234 $exceptionHandler->writeToLog($error);
235
236 if ($error->getPrevious())
237 {
238 $exceptionHandler->writeToLog($error->getPrevious());
239 }
240 }
241
242 private function shouldLogError(\Error $error): bool
243 {
244 if (!$error->getPrevious())
245 {
246 return true;
247 }
248
249 if (str_starts_with($error->getPrevious()->getMessage(), AbstractSessionHandler::LOCK_ERROR_MESSAGE))
250 {
251 return false;
252 }
253
254 return true;
255 }
256
257 public function regenerateId(): bool
258 {
259 if (!$this->isStarted())
260 {
261 return false;
262 }
263
264 $newSessionId = session_create_id();
265
266 $this->set('newSid', $newSessionId);
267 $this->set('destroyed', time());
268
269 $backup = $this->sessionData;
270 $this->saveWithoutReleaseLock();
271
272 $this->disableStrictMode();
273 $this->setId($newSessionId);
274// Idea to switch on strict mode after setId is good. But in that case
275// session_start will invoke validateId for one time more. So behavior in
276// 7.1, 7.2 & 7.4 is different and now it's way to avoid that.
277// if ($prevStrictMode !== false)
278// {
279// ini_set('session.use_strict_mode', $prevStrictMode);
280// }
281
282 $this->start();
283 $this->enableStrictMode();
284
285 $_SESSION = $backup;
286
287 $this->remove('newSid');
288 $this->remove('destroyed');
289
290 return true;
291 }
292
293 public function destroy(): void
294 {
295 if ($this->isActive())
296 {
297 session_destroy();
298 $this->started = false;
299 }
300 }
301
302 protected function saveWithoutReleaseLock(): void
303 {
304 if ($this->sessionHandler instanceof AbstractSessionHandler)
305 {
306 $this->sessionHandler->turnOffReleaseLockAfterCloseSession();
307 $this->save();
308 $this->sessionHandler->turnOnReleaseLockAfterCloseSession();
309 }
310 else
311 {
312 $this->save();
313 }
314 }
315
316 public function save(): void
317 {
318 $session = $_SESSION;
319
320 $previousHandler = set_error_handler(
321 function($type, $msg, $file, $line) use (&$previousHandler) {
322 if ($type === E_WARNING && str_starts_with($msg, 'session_write_close():'))
323 {
324 $handler = $this->sessionHandler;
325 $msg = sprintf(
326 "session_write_close(): Failed to write session data with \"%s\" handler",
327 $handler? \get_class($handler) : ''
328 );
329 }
330
331 return $previousHandler ? $previousHandler($type, $msg, $file, $line) : false;
332 }
333 );
334
335 try
336 {
338 session_write_close();
339 }
340 finally
341 {
342 restore_error_handler();
343 if ($_SESSION)
344 {
345 $_SESSION = $session;
346 }
347 }
348
349 $this->started = false;
350 }
351
352 protected function processLazyStart(): bool
353 {
354 if (!$this->lazyStartEnabled)
355 {
356 return false;
357 }
358 if ($this->isStarted())
359 {
360 return false;
361 }
362
363 return $this->start();
364 }
365
366 public function clear(): void
367 {
368 $_SESSION = [];
369 $this->nullPointers = [];
370 }
371
372 public function isStarted(): bool
373 {
374 return $this->started;
375 }
376
380 public function getDebugger(): Debugger
381 {
382 return $this->debugger;
383 }
384
385 private function debug(string $text): void
386 {
387 if (!$this->debug)
388 {
389 return;
390 }
391
392 $this->getDebugger()->logToFile($text);
393 }
394
395 private function detectFirstUsage(): void
396 {
397 if (!$this->debug)
398 {
399 return;
400 }
401
402 $this->getDebugger()->detectFirstUsage();
403 }
404
405 private function enableStrictMode(): self
406 {
407 $this->useStrictMode = true;
408
409 return $this;
410 }
411
412 private function disableStrictMode(): self
413 {
414 ini_set('session.use_strict_mode', 0);
415 $this->useStrictMode = false;
416
417 return $this;
418 }
419}
$type
Определения options.php:106
static getInstance()
Определения application.php:98
Определения error.php:15
getSessionStartOptions()
Определения session.php:197
disableLazyStart()
Определения session.php:44
bool $debug
Определения session.php:17
setName($name)
Определения session.php:114
disableIgnoringSessionStartErrors()
Определения session.php:72
bool $started
Определения session.php:14
bool $ignoringSessionStartErrors
Определения session.php:19
regenerateId()
Определения session.php:257
bool $useStrictMode
Определения session.php:20
getSessionHandler()
Определения session.php:124
__construct(\SessionHandlerInterface $sessionHandler=null)
Определения session.php:25
setId($id)
Определения session.php:99
enableIgnoringSessionStartErrors()
Определения session.php:65
enableDebug()
Определения session.php:51
getDebugger()
Определения session.php:380
applySessionStartIniSettings(array $settings)
Определения session.php:223
bool $lazyStartEnabled
Определения session.php:16
Debugger $debugger
Определения session.php:18
enableLazyStart()
Определения session.php:37
SessionHandlerInterface $sessionHandler
Определения session.php:15
isAccessible()
Определения session.php:89
disableDebug()
Определения session.php:58
processLazyStart()
Определения session.php:352
saveWithoutReleaseLock()
Определения session.php:302
$options
Определения commerceml2.php:49
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$GLOBALS[ 'DB'] debug
Определения start.php:56
$name
Определения menu_edit.php:35
trait ArrayAccessWithReferences
Определения arrayaccesswithreferences.php:8
$settings
Определения product_settings.php:43
$text
Определения template_pdf.php:79
$error
Определения subscription_card_product.php:20