Skip to content

Commit

Permalink
Reworked SF session dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
fred-jan committed Feb 2, 2024
1 parent 857e17e commit e423686
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 55 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion spec/Storage/SessionFlowsBagSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
18 changes: 13 additions & 5 deletions spec/Storage/SessionStorageSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
Expand All @@ -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');
}
Expand All @@ -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');
}
Expand Down
2 changes: 1 addition & 1 deletion src/Resources/config/container/storages.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

<services>
<service id="sylius.process_storage.session" class="%sylius.process_storage.session.class%">
<argument type="service" id="session" />
<argument type="service" id="request_stack" />
</service>
<service id="sylius.process_storage.session.bag" class="%sylius.process_storage.session.bag.class%" />
</services>
Expand Down
4 changes: 2 additions & 2 deletions src/Storage/SessionFlowsBag.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -34,7 +34,7 @@ public function __construct()
/**
* {@inheritdoc}
*/
public function getName()
public function getName(): string
{
return self::NAME;
}
Expand Down
54 changes: 9 additions & 45 deletions src/Storage/SessionStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace Sylius\Bundle\FlowBundle\Storage;

use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
* Session storage.
Expand All @@ -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;
}
Expand Down
144 changes: 144 additions & 0 deletions src/Symfony/NamespacedAttributeBag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php
declare(strict_types=1);

namespace Sylius\Bundle\FlowBundle\Symfony;

use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;

/**
* This class provides structured storage of session attributes using
* a name spacing character in the key.
*/
class NamespacedAttributeBag extends AttributeBag
{
/**
* @param string $storageKey Session storage key
* @param string $namespaceCharacter Namespace character to use in keys
*/
public function __construct(string $storageKey = '_sf2_attributes', private string $namespaceCharacter = '/')
{
parent::__construct($storageKey);
}

/**
* {@inheritdoc}
*/
public function has(string $name): bool
{
// 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 false;
}

return \array_key_exists($name, $attributes);
}

/**
* {@inheritdoc}
*/
public function get(string $name, mixed $default = null): mixed

Check failure on line 42 in src/Symfony/NamespacedAttributeBag.php

View workflow job for this annotation

GitHub Actions / Test (8.0)

ReservedWord

src/Symfony/NamespacedAttributeBag.php:42:39: ReservedWord: mixed is a reserved word (see https://psalm.dev/095)

Check failure on line 42 in src/Symfony/NamespacedAttributeBag.php

View workflow job for this annotation

GitHub Actions / Test (8.0)

ReservedWord

src/Symfony/NamespacedAttributeBag.php:42:63: ReservedWord: mixed is a reserved word (see https://psalm.dev/095)

Check failure on line 42 in src/Symfony/NamespacedAttributeBag.php

View workflow job for this annotation

GitHub Actions / Test (8.1)

ReservedWord

src/Symfony/NamespacedAttributeBag.php:42:39: ReservedWord: mixed is a reserved word (see https://psalm.dev/095)

Check failure on line 42 in src/Symfony/NamespacedAttributeBag.php

View workflow job for this annotation

GitHub Actions / Test (8.1)

ReservedWord

src/Symfony/NamespacedAttributeBag.php:42:63: ReservedWord: mixed is a reserved word (see https://psalm.dev/095)
{
// 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)

Check failure on line 58 in src/Symfony/NamespacedAttributeBag.php

View workflow job for this annotation

GitHub Actions / Test (8.0)

ReservedWord

src/Symfony/NamespacedAttributeBag.php:58:39: ReservedWord: mixed is a reserved word (see https://psalm.dev/095)

Check failure on line 58 in src/Symfony/NamespacedAttributeBag.php

View workflow job for this annotation

GitHub Actions / Test (8.1)

ReservedWord

src/Symfony/NamespacedAttributeBag.php:58:39: ReservedWord: mixed is a reserved word (see https://psalm.dev/095)
{
$attributes = &$this->resolveAttributePath($name, true);
$name = $this->resolveKey($name);
$attributes[$name] = $value;
}

/**
* {@inheritdoc}
*/
public function remove(string $name): mixed

Check failure on line 68 in src/Symfony/NamespacedAttributeBag.php

View workflow job for this annotation

GitHub Actions / Test (8.0)

ReservedWord

src/Symfony/NamespacedAttributeBag.php:68:43: ReservedWord: mixed is a reserved word (see https://psalm.dev/095)

Check failure on line 68 in src/Symfony/NamespacedAttributeBag.php

View workflow job for this annotation

GitHub Actions / Test (8.1)

ReservedWord

src/Symfony/NamespacedAttributeBag.php:68:43: ReservedWord: mixed is a reserved word (see https://psalm.dev/095)
{
$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;
}
}

0 comments on commit e423686

Please sign in to comment.