[HttpFoundation] Add a way to avoid the session be written at each request
This commit is contained in:
parent
c886612c99
commit
38f02eacbf
@ -222,6 +222,10 @@ class Configuration implements ConfigurationInterface
|
||||
->scalarNode('gc_probability')->end()
|
||||
->scalarNode('gc_maxlifetime')->end()
|
||||
->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end()
|
||||
->integerNode('metadata_update_threshold')
|
||||
->defaultValue('0')
|
||||
->info('seconds to wait between 2 session metadata updates, it will also prevent the session handler to write if the session has not changed')
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
|
@ -342,7 +342,14 @@ class FrameworkExtension extends Extension
|
||||
$container->getDefinition('session.storage.native')->replaceArgument(1, null);
|
||||
$container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null);
|
||||
} else {
|
||||
$container->setAlias('session.handler', $config['handler_id']);
|
||||
$handlerId = $config['handler_id'];
|
||||
|
||||
if ($config['metadata_update_threshold'] > 0) {
|
||||
$container->getDefinition('session.handler.write_check')->addArgument(new Reference($handlerId));
|
||||
$handlerId = 'session.handler.write_check';
|
||||
}
|
||||
|
||||
$container->setAlias('session.handler', $handlerId);
|
||||
}
|
||||
|
||||
$container->setParameter('session.save_path', $config['save_path']);
|
||||
@ -362,6 +369,8 @@ class FrameworkExtension extends Extension
|
||||
$container->findDefinition('session.storage')->getClass(),
|
||||
));
|
||||
}
|
||||
|
||||
$container->setParameter('session.metadata.update_threshold', $config['metadata_update_threshold']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,10 +8,13 @@
|
||||
<parameter key="session.class">Symfony\Component\HttpFoundation\Session\Session</parameter>
|
||||
<parameter key="session.flashbag.class">Symfony\Component\HttpFoundation\Session\Flash\FlashBag</parameter>
|
||||
<parameter key="session.attribute_bag.class">Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag</parameter>
|
||||
<parameter key="session.storage.metadata_bag.class">Symfony\Component\HttpFoundation\Session\Storage\MetadataBag</parameter>
|
||||
<parameter key="session.metadata.storage_key">_sf2_meta</parameter>
|
||||
<parameter key="session.storage.native.class">Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage</parameter>
|
||||
<parameter key="session.storage.php_bridge.class">Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage</parameter>
|
||||
<parameter key="session.storage.mock_file.class">Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage</parameter>
|
||||
<parameter key="session.handler.native_file.class">Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler</parameter>
|
||||
<parameter key="session.handler.write_check.class">Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler</parameter>
|
||||
<parameter key="session_listener.class">Symfony\Bundle\FrameworkBundle\EventListener\SessionListener</parameter>
|
||||
</parameters>
|
||||
|
||||
@ -22,13 +25,20 @@
|
||||
<argument type="service" id="session.flash_bag" />
|
||||
</service>
|
||||
|
||||
<service id="session.storage.metadata_bag" class="%session.storage.metadata_bag.class%" public="false">
|
||||
<argument>%session.metadata.storage_key%</argument>
|
||||
<argument>%session.metadata.update_threshold%</argument>
|
||||
</service>
|
||||
|
||||
<service id="session.storage.native" class="%session.storage.native.class%">
|
||||
<argument>%session.storage.options%</argument>
|
||||
<argument type="service" id="session.handler" />
|
||||
<argument type="service" id="session.storage.metadata_bag" />
|
||||
</service>
|
||||
|
||||
<service id="session.storage.php_bridge" class="%session.storage.php_bridge.class%">
|
||||
<argument type="service" id="session.handler" />
|
||||
<argument type="service" id="session.storage.metadata_bag" />
|
||||
</service>
|
||||
|
||||
<service id="session.flash_bag" class="%session.flashbag.class%" public="false" />
|
||||
@ -37,12 +47,16 @@
|
||||
|
||||
<service id="session.storage.mock_file" class="%session.storage.mock_file.class%" public="false">
|
||||
<argument>%kernel.cache_dir%/sessions</argument>
|
||||
<argument>MOCKSESSID</argument>
|
||||
<argument type="service" id="session.storage.metadata_bag" />
|
||||
</service>
|
||||
|
||||
<service id="session.handler.native_file" class="%session.handler.native_file.class%" public="false">
|
||||
<argument>%session.save_path%</argument>
|
||||
</service>
|
||||
|
||||
<service id="session.handler.write_check" class="%session.handler.write_check.class%" public="false" />
|
||||
|
||||
<service id="session_listener" class="%session_listener.class%">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="service" id="service_container" />
|
||||
|
@ -0,0 +1,91 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Wraps another SessionHandlerInterface to only write the session when it has been modified.
|
||||
*
|
||||
* @author Adrien Brault <adrien.brault@gmail.com>
|
||||
*/
|
||||
class WriteCheckSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var \SessionHandlerInterface
|
||||
*/
|
||||
private $wrappedSessionHandler;
|
||||
|
||||
/**
|
||||
* @var array sessionId => session
|
||||
*/
|
||||
private $readSessions;
|
||||
|
||||
public function __construct(\SessionHandlerInterface $wrappedSessionHandler)
|
||||
{
|
||||
$this->wrappedSessionHandler = $wrappedSessionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return $this->wrappedSessionHandler->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
return $this->wrappedSessionHandler->destroy($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gc($maxLifetime)
|
||||
{
|
||||
return $this->wrappedSessionHandler->gc($maxLifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionId)
|
||||
{
|
||||
return $this->wrappedSessionHandler->open($savePath, $sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
$session = $this->wrappedSessionHandler->read($sessionId);
|
||||
|
||||
$this->readSessions[$sessionId] = $session;
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $sessionData)
|
||||
{
|
||||
if (isset($this->readSessions[$sessionId]) && $sessionData === $this->readSessions[$sessionId]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->wrappedSessionHandler->write($sessionId, $sessionData);
|
||||
}
|
||||
}
|
@ -48,14 +48,21 @@ class MetadataBag implements SessionBagInterface
|
||||
*/
|
||||
private $lastUsed;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
private $updateThreshold;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $storageKey The key used to store bag in the session.
|
||||
* @param integer $updateThreshold The time to wait between two UPDATED updates
|
||||
*/
|
||||
public function __construct($storageKey = '_sf2_meta')
|
||||
public function __construct($storageKey = '_sf2_meta', $updateThreshold = 0)
|
||||
{
|
||||
$this->storageKey = $storageKey;
|
||||
$this->updateThreshold = $updateThreshold;
|
||||
$this->meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0);
|
||||
}
|
||||
|
||||
@ -68,7 +75,11 @@ class MetadataBag implements SessionBagInterface
|
||||
|
||||
if (isset($array[self::CREATED])) {
|
||||
$this->lastUsed = $this->meta[self::UPDATED];
|
||||
$this->meta[self::UPDATED] = time();
|
||||
|
||||
$timeStamp = time();
|
||||
if ($timeStamp - $array[self::UPDATED] >= $this->updateThreshold) {
|
||||
$this->meta[self::UPDATED] = $timeStamp;
|
||||
}
|
||||
} else {
|
||||
$this->stampCreated();
|
||||
}
|
||||
|
@ -0,0 +1,94 @@
|
||||
<?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 Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler;
|
||||
|
||||
/**
|
||||
* @author Adrien Brault <adrien.brault@gmail.com>
|
||||
*/
|
||||
class WriteCheckSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function test()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMock('SessionHandlerInterface');
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('close')
|
||||
->with()
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertEquals(true, $writeCheckSessionHandler->close());
|
||||
}
|
||||
|
||||
public function testWrite()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMock('SessionHandlerInterface');
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('write')
|
||||
->with('foo', 'bar')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertEquals(true, $writeCheckSessionHandler->write('foo', 'bar'));
|
||||
}
|
||||
|
||||
public function testSkippedWrite()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMock('SessionHandlerInterface');
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('read')
|
||||
->with('foo')
|
||||
->will($this->returnValue('bar'))
|
||||
;
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->never())
|
||||
->method('write')
|
||||
;
|
||||
|
||||
$this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
|
||||
$this->assertEquals(true, $writeCheckSessionHandler->write('foo', 'bar'));
|
||||
}
|
||||
|
||||
public function testNonSkippedWrite()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMock('SessionHandlerInterface');
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('read')
|
||||
->with('foo')
|
||||
->will($this->returnValue('bar'))
|
||||
;
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('write')
|
||||
->with('foo', 'baZZZ')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
|
||||
$this->assertEquals(true, $writeCheckSessionHandler->write('foo', 'baZZZ'));
|
||||
}
|
||||
}
|
@ -100,4 +100,36 @@ class MetadataBagTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$this->bag->clear();
|
||||
}
|
||||
|
||||
public function testSkipLastUsedUpdate()
|
||||
{
|
||||
$bag = new MetadataBag('', 30);
|
||||
$timeStamp = time();
|
||||
|
||||
$created = $timeStamp - 15;
|
||||
$sessionMetadata = array(
|
||||
MetadataBag::CREATED => $created,
|
||||
MetadataBag::UPDATED => $created,
|
||||
MetadataBag::LIFETIME => 1000
|
||||
);
|
||||
$bag->initialize($sessionMetadata);
|
||||
|
||||
$this->assertEquals($created, $sessionMetadata[MetadataBag::UPDATED]);
|
||||
}
|
||||
|
||||
public function testDoesNotSkipLastUsedUpdate()
|
||||
{
|
||||
$bag = new MetadataBag('', 30);
|
||||
$timeStamp = time();
|
||||
|
||||
$created = $timeStamp - 45;
|
||||
$sessionMetadata = array(
|
||||
MetadataBag::CREATED => $created,
|
||||
MetadataBag::UPDATED => $created,
|
||||
MetadataBag::LIFETIME => 1000
|
||||
);
|
||||
$bag->initialize($sessionMetadata);
|
||||
|
||||
$this->assertEquals($timeStamp, $sessionMetadata[MetadataBag::UPDATED]);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user