[HttpFoundation][Cache] Added MarshallingSessionHandler

This commit is contained in:
Ahmed TAILOULOUTE 2020-02-20 18:35:20 +01:00
parent cde44fcad1
commit 155d980aea
9 changed files with 383 additions and 0 deletions

View File

@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
*/
class RemoveUnusedSessionMarshallingHandlerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('session.marshalling_handler')) {
return;
}
$isMarshallerDecorated = false;
foreach ($container->getDefinitions() as $definition) {
$decorated = $definition->getDecoratedService();
if (null !== $decorated && 'session.marshaller' === $decorated[0]) {
$isMarshallerDecorated = true;
break;
}
}
if (!$isMarshallerDecorated) {
$container->removeDefinition('session.marshalling_handler');
}
}
}

View File

@ -18,6 +18,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ContainerBuilder
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\DataCollectorTranslatorPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RemoveUnusedSessionMarshallingHandlerPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerRealRefPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
@ -130,6 +131,7 @@ class FrameworkBundle extends Bundle
$this->addCompilerPassIfExists($container, AddAutoMappingConfigurationPass::class);
$container->addCompilerPass(new RegisterReverseContainerPass(true));
$container->addCompilerPass(new RegisterReverseContainerPass(false), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new RemoveUnusedSessionMarshallingHandlerPass());
if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 2);

View File

@ -71,5 +71,12 @@
<!-- for BC -->
<service id="session.storage.filesystem" alias="session.storage.mock_file" />
<service id="session.marshaller" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\IdentityMarshaller" />
<service id="session.marshalling_handler" decorates="session.handler" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\MarshallingSessionHandler">
<argument type="service" id="session.marshalling_handler.inner" />
<argument type="service" id="session.marshaller" />
</service>
</services>
</container>

View File

@ -13,6 +13,7 @@ CHANGELOG
* added `Request::preferSafeContent()` and `Response::setContentSafe()` to handle "safe" HTTP preference
according to [RFC 8674](https://tools.ietf.org/html/rfc8674)
* made the Mime component an optional dependency
* added `MarshallingSessionHandler`, `IdentityMarshaller`
5.0.0
-----

View File

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
/**
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
*/
class IdentityMarshaller implements MarshallerInterface
{
/**
* {@inheritdoc}
*/
public function marshall(array $values, ?array &$failed): array
{
foreach ($values as $key => $value) {
if (!\is_string($value)) {
throw new \LogicException(sprintf('%s accepts only string as data.', __METHOD__));
}
}
return $values;
}
/**
* {@inheritdoc}
*/
public function unmarshall(string $value): string
{
return $value;
}
}

View File

@ -0,0 +1,100 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
/**
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
*/
class MarshallingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
{
private $handler;
private $marshaller;
public function __construct(AbstractSessionHandler $handler, MarshallerInterface $marshaller)
{
$this->handler = $handler;
$this->marshaller = $marshaller;
}
/**
* {@inheritdoc}
*/
public function open($savePath, $name)
{
return $this->handler->open($savePath, $name);
}
/**
* {@inheritdoc}
*/
public function close()
{
return $this->handler->close();
}
/**
* {@inheritdoc}
*/
public function destroy($sessionId)
{
return $this->handler->destroy($sessionId);
}
/**
* {@inheritdoc}
*/
public function gc($maxlifetime)
{
return $this->handler->gc($maxlifetime);
}
/**
* {@inheritdoc}
*/
public function read($sessionId)
{
return $this->marshaller->unmarshall($this->handler->read($sessionId));
}
/**
* {@inheritdoc}
*/
public function write($sessionId, $data)
{
$failed = [];
$marshalledData = $this->marshaller->marshall(['data' => $data], $failed);
if (isset($failed['data'])) {
return false;
}
return $this->handler->write($sessionId, $marshalledData['data']);
}
/**
* {@inheritdoc}
*/
public function validateId($sessionId)
{
return $this->handler->validateId($sessionId);
}
/**
* {@inheritdoc}
*/
public function updateTimestamp($sessionId, $data)
{
return $this->handler->updateTimestamp($sessionId, $data);
}
}

View File

@ -0,0 +1,59 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\IdentityMarshaller;
/**
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
*/
class IdentityMarshallerTest extends Testcase
{
public function testMarshall()
{
$marshaller = new IdentityMarshaller();
$values = ['data' => 'string_data'];
$failed = [];
$this->assertSame($values, $marshaller->marshall($values, $failed));
}
/**
* @dataProvider invalidMarshallDataProvider
*/
public function testMarshallInvalidData($values)
{
$marshaller = new IdentityMarshaller();
$failed = [];
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Symfony\Component\HttpFoundation\Session\Storage\Handler\IdentityMarshaller::marshall accepts only string as data');
$marshaller->marshall($values, $failed);
}
public function testUnmarshall()
{
$marshaller = new IdentityMarshaller();
$this->assertEquals('data', $marshaller->unmarshall('data'));
}
public function invalidMarshallDataProvider(): iterable
{
return [
[['object' => new \stdClass()]],
[['foo' => ['bar']]],
];
}
}

View File

@ -0,0 +1,128 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MarshallingSessionHandler;
/**
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
*/
class MarshallingSessionHandlerTest extends TestCase
{
/**
* @var MockObject|\SessionHandlerInterface
*/
protected $handler;
/**
* @var MockObject|MarshallerInterface
*/
protected $marshaller;
protected function setUp(): void
{
$this->marshaller = $this->getMockBuilder(MarshallerInterface::class)->getMock();
$this->handler = $this->getMockBuilder(AbstractSessionHandler::class)->getMock();
}
public function testOpen()
{
$marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
$this->handler->expects($this->once())->method('open')
->with('path', 'name')->willReturn(true);
$marshallingSessionHandler->open('path', 'name');
}
public function testClose()
{
$marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
$this->handler->expects($this->once())->method('close')->willReturn(true);
$this->assertTrue($marshallingSessionHandler->close());
}
public function testDestroy()
{
$marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
$this->handler->expects($this->once())->method('destroy')
->with('session_id')->willReturn(true);
$marshallingSessionHandler->destroy('session_id');
}
public function testGc()
{
$marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
$this->handler->expects($this->once())->method('gc')
->with('maxlifetime')->willReturn(true);
$marshallingSessionHandler->gc('maxlifetime');
}
public function testRead()
{
$marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
$this->handler->expects($this->once())->method('read')->with('session_id')
->willReturn('data');
$this->marshaller->expects($this->once())->method('unmarshall')->with('data')
->willReturn('unmarshalled_data')
;
$result = $marshallingSessionHandler->read('session_id');
$this->assertEquals('unmarshalled_data', $result);
}
public function testWrite()
{
$marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
$this->marshaller->expects($this->once())->method('marshall')
->with(['data' => 'data'], [])
->willReturn(['data' => 'marshalled_data']);
$this->handler->expects($this->once())->method('write')
->with('session_id', 'marshalled_data')
;
$marshallingSessionHandler->write('session_id', 'data');
}
public function testValidateId()
{
$marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
$this->handler->expects($this->once())->method('validateId')
->with('session_id')->willReturn(true);
$marshallingSessionHandler->validateId('session_id');
}
public function testUpdateTimestamp()
{
$marshallingSessionHandler = new MarshallingSessionHandler($this->handler, $this->marshaller);
$this->handler->expects($this->once())->method('updateTimestamp')
->with('session_id', 'data')->willReturn(true);
$marshallingSessionHandler->updateTimestamp('session_id', 'data');
}
}

View File

@ -22,6 +22,7 @@
},
"require-dev": {
"predis/predis": "~1.0",
"symfony/cache": "^4.4|^5.0",
"symfony/mime": "^4.4|^5.0",
"symfony/expression-language": "^4.4|^5.0"
},