vendor/pimcore/pimcore/bundles/AdminBundle/Session/Handler/AdminSessionHandler.php line 103

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Bundle\AdminBundle\Session\Handler;
  15. use Pimcore\Bundle\CoreBundle\EventListener\Traits\PimcoreContextAwareTrait;
  16. use Pimcore\Http\Request\Resolver\PimcoreContextResolver;
  17. use Pimcore\Http\RequestHelper;
  18. use Pimcore\Session\Attribute\LockableAttributeBagInterface;
  19. use Psr\Log\LoggerAwareInterface;
  20. use Psr\Log\LoggerAwareTrait;
  21. use Symfony\Component\HttpFoundation\Request;
  22. use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
  23. use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
  24. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  25. /**
  26.  * @internal
  27.  */
  28. class AdminSessionHandler implements LoggerAwareInterfaceAdminSessionHandlerInterface
  29. {
  30.     use LoggerAwareTrait;
  31.     use PimcoreContextAwareTrait;
  32.     /**
  33.      * Contains how many sessions are currently open, this is important, because writeClose() must not be called if
  34.      * there is still an open session, this is especially important if something doesn't use the method use() but get()
  35.      * so the session isn't closed automatically after the action is done
  36.      */
  37.     private $openedSessions 0;
  38.     /**
  39.      * @deprecated
  40.      *
  41.      * @var SessionInterface
  42.      */
  43.     protected $session;
  44.     protected $readOnlySessionBagsCache = [];
  45.     /**
  46.      * @var bool|null
  47.      */
  48.     private $canWriteAndClose;
  49.     /**
  50.      * @var RequestHelper
  51.      */
  52.     protected $requestHelper;
  53.     public function __construct(RequestHelper $requestHelper)
  54.     {
  55.         $this->requestHelper $requestHelper;
  56.     }
  57.     /**
  58.      * {@inheritdoc}
  59.      */
  60.     public function getSessionId()
  61.     {
  62.         if (!$this->getSession()->isStarted()) {
  63.             // this is just to initialize the session :)
  64.             $this->useSession(static function (SessionInterface $session) {
  65.                 return $session->getId();
  66.             });
  67.         }
  68.         return $this->getSession()->getId();
  69.     }
  70.     /**
  71.      * @return SessionInterface
  72.      */
  73.     private function getSession()
  74.     {
  75.         try {
  76.             return $this->requestHelper->getSession();
  77.         } catch (\LogicException $e) {
  78.             $this->logger->debug('Error while getting the admin session: {exception}', ['exception' => $e->getMessage()]);
  79.         }
  80.         trigger_deprecation('pimcore/pimcore''10.5',
  81.             sprintf('Session used with non existing request stack in %s, that will not be possible in Pimcore 11.'__CLASS__));
  82.         return \Pimcore::getContainer()->get('session');
  83.     }
  84.     /**
  85.      * {@inheritdoc}
  86.      */
  87.     public function getSessionName()
  88.     {
  89.         return $this->getSession()->getName();
  90.     }
  91.     /**
  92.      * {@inheritdoc}
  93.      */
  94.     public function useSession(callable $callable)
  95.     {
  96.         $session $this->loadSession();
  97.         $result call_user_func_array($callable, [$session]);
  98.         $this->writeClose();
  99.         $this->readOnlySessionBagsCache = []; // clear cache if session was modified manually
  100.         return $result;
  101.     }
  102.     /**
  103.      * {@inheritdoc}
  104.      */
  105.     public function useSessionAttributeBag(callable $callablestring $name 'pimcore_admin')
  106.     {
  107.         $session $this->loadSession();
  108.         $attributeBag $this->loadAttributeBag($name$session);
  109.         $result call_user_func_array($callable, [$attributeBag$session]);
  110.         $this->writeClose();
  111.         return $result;
  112.     }
  113.     /**
  114.      * {@inheritdoc}
  115.      */
  116.     public function getReadOnlyAttributeBag(string $name 'pimcore_admin'): AttributeBagInterface
  117.     {
  118.         if (isset($this->readOnlySessionBagsCache[$name])) {
  119.             $bag $this->readOnlySessionBagsCache[$name];
  120.         } else {
  121.             $bag $this->useSessionAttributeBag(function (AttributeBagInterface $bag) {
  122.                 return $bag;
  123.             }, $name);
  124.         }
  125.         if ($bag instanceof LockableAttributeBagInterface) {
  126.             $bag->lock();
  127.         }
  128.         return $bag;
  129.     }
  130.     /**
  131.      * {@inheritdoc}
  132.      */
  133.     public function invalidate(int $lifetime null): bool
  134.     {
  135.         return $this->getSession()->invalidate($lifetime);
  136.     }
  137.     /**
  138.      * {@inheritdoc}
  139.      */
  140.     public function regenerateId(): bool
  141.     {
  142.         return $this->useSession(static function (SessionInterface $session) {
  143.             return $session->migrate(true);
  144.         });
  145.     }
  146.     /**
  147.      * {@inheritdoc}
  148.      */
  149.     public function loadAttributeBag(string $nameSessionInterface $session null): SessionBagInterface
  150.     {
  151.         if (null === $session) {
  152.             $session $this->loadSession();
  153.         }
  154.         $attributeBag $session->getBag($name);
  155.         if ($attributeBag instanceof LockableAttributeBagInterface) {
  156.             $attributeBag->unlock();
  157.         }
  158.         $this->readOnlySessionBagsCache[$name] = $attributeBag;
  159.         return $attributeBag;
  160.     }
  161.     /**
  162.      * {@inheritdoc}
  163.      */
  164.     public function requestHasSessionId(Request $requestbool $checkRequestParams false): bool
  165.     {
  166.         $sessionName $this->getSessionName();
  167.         if (empty($sessionName)) {
  168.             return false;
  169.         }
  170.         $properties = ['cookies'];
  171.         if ($checkRequestParams) {
  172.             $properties[] = 'request';
  173.             $properties[] = 'query';
  174.         }
  175.         foreach ($properties as $property) {
  176.             if ($request->$property->has($sessionName) && !empty($request->$property->get($sessionName))) {
  177.                 return true;
  178.             }
  179.         }
  180.         return false;
  181.     }
  182.     /**
  183.      * {@inheritdoc}
  184.      */
  185.     public function getSessionIdFromRequest(Request $requestbool $checkRequestParams false): string
  186.     {
  187.         if ($this->requestHasSessionId($request$checkRequestParams)) {
  188.             $sessionName $this->getSessionName();
  189.             if ($sessionId $request->cookies->get($sessionName)) {
  190.                 return $sessionId;
  191.             }
  192.             if ($checkRequestParams) {
  193.                 if ($sessionId $request->request->get($sessionName)) {
  194.                     return $sessionId;
  195.                 }
  196.                 if ($sessionId $request->query->get($sessionName)) {
  197.                     return $sessionId;
  198.                 }
  199.             }
  200.         }
  201.         throw new \RuntimeException('Failed to get session ID from request');
  202.     }
  203.     /**
  204.      * {@inheritdoc}
  205.      */
  206.     public function loadSession(): SessionInterface
  207.     {
  208.         $sessionName $this->getSessionName();
  209.         $this->logger->debug('Opening admin session {name}', ['name' => $sessionName]);
  210.         if (!$this->getSession()->isStarted()) {
  211.             $this->getSession()->start();
  212.         }
  213.         $this->openedSessions++;
  214.         $this->logger->debug('Admin session {name} was successfully opened. Open admin sessions: {count}', [
  215.             'name' => $sessionName,
  216.             'count' => $this->openedSessions,
  217.         ]);
  218.         return $this->getSession();
  219.     }
  220.     /**
  221.      * {@inheritdoc}
  222.      */
  223.     public function writeClose()
  224.     {
  225.         if (!$this->shouldWriteAndClose()) {
  226.             return;
  227.         }
  228.         $this->openedSessions--;
  229.         if (=== $this->openedSessions) {
  230.             $this->getSession()->save();
  231.             $this->logger->debug('Admin session {name} was written and closed', [
  232.                 'name' => $this->getSessionName(),
  233.             ]);
  234.         } else {
  235.             $this->logger->debug('Not writing/closing session admin session {name} as there are still {count} open sessions', [
  236.                 'name' => $this->getSessionName(),
  237.                 'count' => $this->openedSessions,
  238.             ]);
  239.         }
  240.     }
  241.     /**
  242.      * @return bool
  243.      */
  244.     private function shouldWriteAndClose(): bool
  245.     {
  246.         // main request is not available in CLI, so session should be written
  247.         // otherwise session should be written & closed in Admin context only.
  248.         return $this->canWriteAndClose ??= !$this->requestHelper->hasMainRequest()
  249.             || $this->isAdminRequest($this->requestHelper->getMainRequest());
  250.     }
  251.     /**
  252.      * @return bool
  253.      */
  254.     private function isAdminRequest(Request $request): bool
  255.     {
  256.         return $this->matchesPimcoreContext($requestPimcoreContextResolver::CONTEXT_ADMIN)
  257.             || $this->requestHelper->isFrontendRequestByAdmin($request);
  258.     }
  259. }