From e423686c1b56230444bf2580e23d5a5de62b800a Mon Sep 17 00:00:00 2001 From: Fred-Jan van der Eijken Date: Fri, 2 Feb 2024 11:39:28 +0100 Subject: [PATCH] Reworked SF session dependency --- composer.json | 2 +- spec/Storage/SessionFlowsBagSpec.php | 2 +- spec/Storage/SessionStorageSpec.php | 18 ++- src/Resources/config/container/storages.xml | 2 +- src/Storage/SessionFlowsBag.php | 4 +- src/Storage/SessionStorage.php | 54 ++------ src/Symfony/NamespacedAttributeBag.php | 144 ++++++++++++++++++++ 7 files changed, 171 insertions(+), 55 deletions(-) create mode 100644 src/Symfony/NamespacedAttributeBag.php diff --git a/composer.json b/composer.json index 35a4f56..eca165a 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "symfony/http-foundation": "^5.4 || ^6.0" }, "require-dev": { - "phpspec/phpspec": "^7.2", + "phpspec/phpspec": "^7.5", "symfony/yaml": "^5.4", "vimeo/psalm": "^4.22", "friendsofsymfony/rest-bundle": "^3.1" diff --git a/spec/Storage/SessionFlowsBagSpec.php b/spec/Storage/SessionFlowsBagSpec.php index 961b92e..b2e2b3a 100644 --- a/spec/Storage/SessionFlowsBagSpec.php +++ b/spec/Storage/SessionFlowsBagSpec.php @@ -13,7 +13,7 @@ use PhpSpec\ObjectBehavior; use Sylius\Bundle\FlowBundle\Storage\SessionFlowsBag; -use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag; +use Sylius\Bundle\FlowBundle\Symfony\NamespacedAttributeBag; final class SessionFlowsBagSpec extends ObjectBehavior { diff --git a/spec/Storage/SessionStorageSpec.php b/spec/Storage/SessionStorageSpec.php index 5f354ea..767cd88 100644 --- a/spec/Storage/SessionStorageSpec.php +++ b/spec/Storage/SessionStorageSpec.php @@ -13,15 +13,17 @@ use PhpSpec\ObjectBehavior; use Sylius\Bundle\FlowBundle\Storage\SessionFlowsBag; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\SessionInterface; final class SessionStorageSpec extends ObjectBehavior { - function let(SessionInterface $session, SessionFlowsBag $bag) + function let(RequestStack $requestStack, SessionInterface $session, SessionFlowsBag $bag) { + $requestStack->getSession()->willReturn($session); $session->getBag(SessionFlowsBag::NAME)->willReturn($bag); - $this->beConstructedWith($session); + $this->beConstructedWith($requestStack); $this->initialize('domain'); } @@ -42,7 +44,9 @@ function itd_message_is_mutable($bag) function it_has_message($bag) { - $bag->get('domain/message_key', null)->shouldBeCalled(); + $bag->get('domain/message_key', null) + ->willReturn('test') + ->shouldBeCalled(); $this->get('message_key'); } @@ -56,14 +60,18 @@ function it_checks_is_the_message_exists($bag) function it_removes_a_message($bag) { - $bag->remove('domain/message_key')->shouldBeCalled(); + $bag->remove('domain/message_key') + ->willReturn('test') + ->shouldBeCalled(); $this->remove('message_key'); } function it_removes_all_messages($bag) { - $bag->remove('domain/message_key')->shouldBeCalled(); + $bag->remove('domain/message_key') + ->willReturn('test') + ->shouldBeCalled(); $this->remove('message_key'); } diff --git a/src/Resources/config/container/storages.xml b/src/Resources/config/container/storages.xml index 40229e8..927862f 100644 --- a/src/Resources/config/container/storages.xml +++ b/src/Resources/config/container/storages.xml @@ -24,7 +24,7 @@ - + diff --git a/src/Storage/SessionFlowsBag.php b/src/Storage/SessionFlowsBag.php index d6c5059..a442d55 100644 --- a/src/Storage/SessionFlowsBag.php +++ b/src/Storage/SessionFlowsBag.php @@ -11,7 +11,7 @@ namespace Sylius\Bundle\FlowBundle\Storage; -use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag; +use Sylius\Bundle\FlowBundle\Symfony\NamespacedAttributeBag; /** * Separate session bag to store flows data. @@ -34,7 +34,7 @@ public function __construct() /** * {@inheritdoc} */ - public function getName() + public function getName(): string { return self::NAME; } diff --git a/src/Storage/SessionStorage.php b/src/Storage/SessionStorage.php index 860e39e..1fc51a3 100644 --- a/src/Storage/SessionStorage.php +++ b/src/Storage/SessionStorage.php @@ -11,7 +11,7 @@ namespace Sylius\Bundle\FlowBundle\Storage; -use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpFoundation\RequestStack; /** * Session storage. @@ -20,81 +20,45 @@ */ class SessionStorage extends AbstractStorage { - /** - * Session. - * - * @var SessionInterface - */ - protected $session; - - /** - * Constructor. - * - * @param SessionInterface $session - */ - public function __construct(SessionInterface $session) + public function __construct(protected RequestStack $requestStack) { - $this->session = $session; } - /** - * {@inheritdoc} - */ public function get($key, $default = null) { return $this->getBag()->get($this->resolveKey($key), $default); } - /** - * {@inheritdoc} - */ public function set($key, $value) { $this->getBag()->set($this->resolveKey($key), $value); } - /** - * {@inheritdoc} - */ public function has($key) { return $this->getBag()->has($this->resolveKey($key)); } - /** - * {@inheritdoc} - */ public function remove($key) { $this->getBag()->remove($this->resolveKey($key)); } - /** - * {@inheritdoc} - */ public function clear() { $this->getBag()->remove($this->domain); } - /** - * Get session flows bag. - * - * @return SessionFlowsBag - */ - private function getBag() + private function getBag(): SessionFlowsBag { - return $this->session->getBag(SessionFlowsBag::NAME); + $bag = $this->requestStack->getSession()->getBag(SessionFlowsBag::NAME); + \assert($bag instanceof SessionFlowsBag); + + return $bag; } - /** - * Resolve key for current domain. - * - * @param string $key - * - * @return string - */ - private function resolveKey($key) + /** Resolve key for current domain. */ + private function resolveKey(string $key): string { return $this->domain.'/'.$key; } diff --git a/src/Symfony/NamespacedAttributeBag.php b/src/Symfony/NamespacedAttributeBag.php new file mode 100644 index 0000000..4587a04 --- /dev/null +++ b/src/Symfony/NamespacedAttributeBag.php @@ -0,0 +1,144 @@ +resolveAttributePath($name); + $name = $this->resolveKey($name); + + if (null === $attributes) { + return false; + } + + return \array_key_exists($name, $attributes); + } + + /** + * {@inheritdoc} + */ + public function get(string $name, mixed $default = null): mixed + { + // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is + $attributes = $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + + if (null === $attributes) { + return $default; + } + + return \array_key_exists($name, $attributes) ? $attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function set(string $name, mixed $value) + { + $attributes = &$this->resolveAttributePath($name, true); + $name = $this->resolveKey($name); + $attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function remove(string $name): mixed + { + $retval = null; + $attributes = &$this->resolveAttributePath($name); + $name = $this->resolveKey($name); + if (null !== $attributes && \array_key_exists($name, $attributes)) { + $retval = $attributes[$name]; + unset($attributes[$name]); + } + + return $retval; + } + + /** + * Resolves a path in attributes property and returns it as a reference. + * + * This method allows structured namespacing of session attributes. + * + * @param string $name Key name + * @param bool $writeContext Write context, default false + * + * @return array|null + */ + protected function &resolveAttributePath(string $name, bool $writeContext = false) + { + $array = &$this->attributes; + $name = (0 === strpos($name, $this->namespaceCharacter)) ? substr($name, 1) : $name; + + // Check if there is anything to do, else return + if (!$name) { + return $array; + } + + $parts = explode($this->namespaceCharacter, $name); + if (\count($parts) < 2) { + if (!$writeContext) { + return $array; + } + + $array[$parts[0]] = []; + + return $array; + } + + unset($parts[\count($parts) - 1]); + + foreach ($parts as $part) { + if (null !== $array && !\array_key_exists($part, $array)) { + if (!$writeContext) { + $null = null; + + return $null; + } + + $array[$part] = []; + } + + $array = &$array[$part]; + } + + return $array; + } + + /** + * Resolves the key from the name. + * + * This is the last part in a dot separated string. + */ + protected function resolveKey(string $name): string + { + if (false !== $pos = strrpos($name, $this->namespaceCharacter)) { + $name = substr($name, $pos + 1); + } + + return $name; + } +}