Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
SessionManager.php
Go to the documentation of this file.
1 <?php
9 
11 
17 {
27  protected $defaultDestroyOptions = ['send_expire_cookie' => true, 'clear_storage' => true];
28 
34  protected static $urlHostCache = [];
35 
41  protected $validator;
42 
48  protected $request;
49 
55  protected $sidResolver;
56 
62  protected $sessionConfig;
63 
69  protected $saveHandler;
70 
76  protected $storage;
77 
83  protected $cookieManager;
84 
89 
93  private $appState;
94 
107  public function __construct(
108  \Magento\Framework\App\Request\Http $request,
114  \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager,
115  \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory,
116  \Magento\Framework\App\State $appState
117  ) {
118  $this->request = $request;
119  $this->sidResolver = $sidResolver;
120  $this->sessionConfig = $sessionConfig;
121  $this->saveHandler = $saveHandler;
122  $this->validator = $validator;
123  $this->storage = $storage;
124  $this->cookieManager = $cookieManager;
125  $this->cookieMetadataFactory = $cookieMetadataFactory;
126  $this->appState = $appState;
127  $this->start();
128  }
129 
134  public function writeClose()
135  {
136  session_write_close();
137  }
138 
147  public function __call($method, $args)
148  {
149  if (!in_array(substr($method, 0, 3), ['get', 'set', 'uns', 'has'])) {
150  throw new \InvalidArgumentException(
151  sprintf('Invalid method %s::%s(%s)', get_class($this), $method, print_r($args, 1))
152  );
153  }
154  $return = call_user_func_array([$this->storage, $method], $args);
155  return $return === $this->storage ? $this : $return;
156  }
157 
164  public function start()
165  {
166  if (!$this->isSessionExists()) {
167  \Magento\Framework\Profiler::start('session_start');
168 
169  try {
170  $this->appState->getAreaCode();
171  } catch (\Magento\Framework\Exception\LocalizedException $e) {
172  throw new \Magento\Framework\Exception\SessionException(
173  new \Magento\Framework\Phrase(
174  'Area code not set: Area code must be set before starting a session.'
175  ),
176  $e
177  );
178  }
179 
180  // Need to apply the config options so they can be ready by session_start
181  $this->initIniOptions();
182  $this->registerSaveHandler();
183  if (isset($_SESSION['new_session_id'])) {
184  // Not fully expired yet. Could be lost cookie by unstable network.
185  session_commit();
186  session_id($_SESSION['new_session_id']);
187  }
188  $sid = $this->sidResolver->getSid($this);
189  // potential custom logic for session id (ex. switching between hosts)
190  $this->setSessionId($sid);
191  session_start();
192  if (isset($_SESSION['destroyed'])
193  && $_SESSION['destroyed'] < time() - $this->sessionConfig->getCookieLifetime()
194  ) {
195  $this->destroy(['clear_storage' => true]);
196  }
197 
198  $this->validator->validate($this);
199  $this->renewCookie($sid);
200 
201  register_shutdown_function([$this, 'writeClose']);
202 
203  $this->_addHost();
204  \Magento\Framework\Profiler::stop('session_start');
205  }
206  $this->storage->init(isset($_SESSION) ? $_SESSION : []);
207  return $this;
208  }
209 
216  private function renewCookie($sid)
217  {
218  if (!$this->getCookieLifetime()) {
219  return $this;
220  }
221  //When we renew cookie, we should aware, that any other session client do not
222  //change cookie too
223  $cookieValue = $sid ?: $this->cookieManager->getCookie($this->getName());
224  if ($cookieValue) {
225  $metadata = $this->cookieMetadataFactory->createPublicCookieMetadata();
226  $metadata->setPath($this->sessionConfig->getCookiePath());
227  $metadata->setDomain($this->sessionConfig->getCookieDomain());
228  $metadata->setDuration($this->sessionConfig->getCookieLifetime());
229  $metadata->setSecure($this->sessionConfig->getCookieSecure());
230  $metadata->setHttpOnly($this->sessionConfig->getCookieHttpOnly());
231 
232  $this->cookieManager->setPublicCookie(
233  $this->getName(),
234  $cookieValue,
235  $metadata
236  );
237  }
238 
239  return $this;
240  }
241 
247  protected function registerSaveHandler()
248  {
250  [$this->saveHandler, 'open'],
251  [$this->saveHandler, 'close'],
252  [$this->saveHandler, 'read'],
253  [$this->saveHandler, 'write'],
254  [$this->saveHandler, 'destroy'],
255  [$this->saveHandler, 'gc']
256  );
257  }
258 
264  public function isSessionExists()
265  {
266  if (session_status() === PHP_SESSION_NONE && !headers_sent()) {
267  return false;
268  }
269  return true;
270  }
271 
279  public function getData($key = '', $clear = false)
280  {
281  $data = $this->storage->getData($key);
282  if ($clear && isset($data)) {
283  $this->storage->unsetData($key);
284  }
285  return $data;
286  }
287 
293  public function getSessionId()
294  {
295  return session_id();
296  }
297 
303  public function getName()
304  {
305  return session_name();
306  }
307 
314  public function setName($name)
315  {
316  session_name($name);
317  return $this;
318  }
319 
326  public function destroy(array $options = null)
327  {
328  $options = $options ?? [];
329  $options = array_merge($this->defaultDestroyOptions, $options);
330 
331  if ($options['clear_storage']) {
332  $this->clearStorage();
333  }
334 
335  if (session_status() !== PHP_SESSION_ACTIVE) {
336  return;
337  }
338 
339  session_regenerate_id(true);
340  session_destroy();
341  if ($options['send_expire_cookie']) {
342  $this->expireSessionCookie();
343  }
344  }
345 
351  public function clearStorage()
352  {
353  $this->storage->unsetData();
354  return $this;
355  }
356 
362  public function getCookieDomain()
363  {
364  return $this->sessionConfig->getCookieDomain();
365  }
366 
372  public function getCookiePath()
373  {
374  return $this->sessionConfig->getCookiePath();
375  }
376 
382  public function getCookieLifetime()
383  {
384  return $this->sessionConfig->getCookieLifetime();
385  }
386 
393  public function setSessionId($sessionId)
394  {
395  $this->_addHost();
396  if ($sessionId !== null && preg_match('#^[0-9a-zA-Z,-]+$#', $sessionId)) {
397  session_id($sessionId);
398  }
399  return $this;
400  }
401 
409  public function getSessionIdForHost($urlHost)
410  {
411  $httpHost = $this->request->getHttpHost();
412  if (!$httpHost) {
413  return '';
414  }
415 
416  $urlHostArr = explode('/', $urlHost, 4);
417  if (!empty($urlHostArr[2])) {
418  $urlHost = $urlHostArr[2];
419  }
420  $urlPath = empty($urlHostArr[3]) ? '' : $urlHostArr[3];
421 
422  if (!isset(self::$urlHostCache[$urlHost])) {
423  $urlHostArr = explode(':', $urlHost);
424  $urlHost = $urlHostArr[0];
425  $sessionId = $httpHost !== $urlHost && !$this->isValidForHost($urlHost) ? $this->getSessionId() : '';
426  self::$urlHostCache[$urlHost] = $sessionId;
427  }
428 
429  return $this->isValidForPath($urlPath) ? self::$urlHostCache[$urlHost] : $this->getSessionId();
430  }
431 
438  public function isValidForHost($host)
439  {
440  $hostArr = explode(':', $host);
441  $hosts = $this->_getHosts();
442  return !empty($hosts[$hostArr[0]]);
443  }
444 
451  public function isValidForPath($path)
452  {
453  $cookiePath = trim($this->getCookiePath(), '/') . '/';
454  if ($cookiePath == '/') {
455  return true;
456  }
457 
458  $urlPath = trim($path, '/') . '/';
459  return strpos($urlPath, $cookiePath) === 0;
460  }
461 
467  protected function _addHost()
468  {
469  $host = $this->request->getHttpHost();
470  if (!$host) {
471  return $this;
472  }
473 
474  $hosts = $this->_getHosts();
475  $hosts[$host] = true;
476  $_SESSION[self::HOST_KEY] = $hosts;
477  return $this;
478  }
479 
485  protected function _getHosts()
486  {
487  return $_SESSION[self::HOST_KEY] ?? [];
488  }
489 
495  protected function _cleanHosts()
496  {
497  unset($_SESSION[self::HOST_KEY]);
498  return $this;
499  }
500 
506  public function regenerateId()
507  {
508  if (headers_sent()) {
509  return $this;
510  }
511 
512  if ($this->isSessionExists()) {
513  // Regenerate the session
515  $newSessionId = session_id();
516  $_SESSION['new_session_id'] = $newSessionId;
517 
518  // Set destroy timestamp
519  $_SESSION['destroyed'] = time();
520 
521  // Write and close current session;
522  session_commit();
523 
524  // Called after destroy()
525  $oldSession = $_SESSION;
526 
527  // Start session with new session ID
528  session_id($newSessionId);
529  session_start();
530  $_SESSION = $oldSession;
531 
532  // New session does not need them
533  unset($_SESSION['destroyed']);
534  unset($_SESSION['new_session_id']);
535  } else {
536  session_start();
537  }
538 
539  $this->storage->init(isset($_SESSION) ? $_SESSION : []);
540 
541  if ($this->sessionConfig->getUseCookies()) {
543  }
544  return $this;
545  }
546 
552  protected function clearSubDomainSessionCookie()
553  {
554  foreach (array_keys($this->_getHosts()) as $host) {
555  // Delete cookies with the same name for parent domains
556  if ($this->sessionConfig->getCookieDomain() !== $host) {
557  $metadata = $this->cookieMetadataFactory->createPublicCookieMetadata();
558  $metadata->setPath($this->sessionConfig->getCookiePath());
559  $metadata->setDomain($host);
560  $metadata->setSecure($this->sessionConfig->getCookieSecure());
561  $metadata->setHttpOnly($this->sessionConfig->getCookieHttpOnly());
562  $this->cookieManager->deleteCookie($this->getName(), $metadata);
563  }
564  }
565  }
566 
574  public function expireSessionCookie()
575  {
576  if (!$this->sessionConfig->getUseCookies()) {
577  return;
578  }
579 
580  $metadata = $this->cookieMetadataFactory->createPublicCookieMetadata();
581  $metadata->setPath($this->sessionConfig->getCookiePath());
582  $metadata->setDomain($this->sessionConfig->getCookieDomain());
583  $metadata->setSecure($this->sessionConfig->getCookieSecure());
584  $metadata->setHttpOnly($this->sessionConfig->getCookieHttpOnly());
585  $this->cookieManager->deleteCookie($this->getName(), $metadata);
587  }
588 
594  private function initIniOptions()
595  {
596  $result = ini_set('session.use_only_cookies', '1');
597  if ($result === false) {
598  $error = error_get_last();
599  throw new \InvalidArgumentException(
600  sprintf('Failed to set ini option session.use_only_cookies to value 1. %s', $error['message'])
601  );
602  }
603 
604  foreach ($this->sessionConfig->getOptions() as $option => $value) {
605  if ($option=='session.save_handler') {
606  continue;
607  } else {
609  if ($result === false) {
610  $error = error_get_last();
611  throw new \InvalidArgumentException(
612  sprintf('Failed to set ini option "%s" to value "%s". %s', $option, $value, $error['message'])
613  );
614  }
615  }
616  }
617  }
618 }
ini_set($varName, $newValue)
$value
Definition: gender.phtml:16
__construct(\Magento\Framework\App\Request\Http $request, SidResolverInterface $sidResolver, ConfigInterface $sessionConfig, SaveHandlerInterface $saveHandler, ValidatorInterface $validator, StorageInterface $storage, \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, \Magento\Framework\App\State $appState)
$method
Definition: info.phtml:13
if(!isset($_GET['name'])) $name
Definition: log.php:14