[HttpFoundation] Make sessions secure and lazy
This commit is contained in:
parent
3f0a3f58a6
commit
347939c9b3
|
@ -126,6 +126,8 @@ Form
|
||||||
FrameworkBundle
|
FrameworkBundle
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
* The `session.use_strict_mode` option has been deprecated and is enabled by default.
|
||||||
|
|
||||||
* The `cache:clear` command doesn't clear "app" PSR-6 cache pools anymore,
|
* The `cache:clear` command doesn't clear "app" PSR-6 cache pools anymore,
|
||||||
but still clears "system" ones.
|
but still clears "system" ones.
|
||||||
Use the `cache:pool:clear` command to clear "app" pools instead.
|
Use the `cache:pool:clear` command to clear "app" pools instead.
|
||||||
|
@ -235,18 +237,13 @@ HttpFoundation
|
||||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler`
|
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler`
|
||||||
class has been deprecated and will be removed in 4.0. Use the `\SessionHandler` class instead.
|
class has been deprecated and will be removed in 4.0. Use the `\SessionHandler` class instead.
|
||||||
|
|
||||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy` class has been
|
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler` class has been
|
||||||
deprecated and will be removed in 4.0. Use your `\SessionHandlerInterface` implementation directly.
|
deprecated and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or
|
||||||
|
extend `AbstractSessionHandler` instead.
|
||||||
|
|
||||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` class has been
|
* The `Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` class has been
|
||||||
deprecated and will be removed in 4.0. Use your `\SessionHandlerInterface` implementation directly.
|
deprecated and will be removed in 4.0. Use your `\SessionHandlerInterface` implementation directly.
|
||||||
|
|
||||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy` class has been
|
|
||||||
deprecated and will be removed in 4.0. Use your `\SessionHandlerInterface` implementation directly.
|
|
||||||
|
|
||||||
* `NativeSessionStorage::setSaveHandler()` now takes an instance of `\SessionHandlerInterface` as argument.
|
|
||||||
Not passing it is deprecated and will throw a `TypeError` in 4.0.
|
|
||||||
|
|
||||||
* Using `Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler` with the legacy mongo extension
|
* Using `Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler` with the legacy mongo extension
|
||||||
has been deprecated and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.
|
has been deprecated and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.
|
||||||
|
|
||||||
|
|
|
@ -329,6 +329,8 @@ Form
|
||||||
FrameworkBundle
|
FrameworkBundle
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
* The `session.use_strict_mode` option has been removed and strict mode is always enabled.
|
||||||
|
|
||||||
* The `validator.mapping.cache.doctrine.apc` service has been removed.
|
* The `validator.mapping.cache.doctrine.apc` service has been removed.
|
||||||
|
|
||||||
* The "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter have been removed. Use the `Request::setTrustedProxies()` method in your front controller instead.
|
* The "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter have been removed. Use the `Request::setTrustedProxies()` method in your front controller instead.
|
||||||
|
@ -542,12 +544,11 @@ HttpFoundation
|
||||||
* The ability to check only for cacheable HTTP methods using `Request::isMethodSafe()` is
|
* The ability to check only for cacheable HTTP methods using `Request::isMethodSafe()` is
|
||||||
not supported anymore, use `Request::isMethodCacheable()` instead.
|
not supported anymore, use `Request::isMethodCacheable()` instead.
|
||||||
|
|
||||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler`,
|
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler` class has been
|
||||||
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy`,
|
removed. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.
|
||||||
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` and
|
|
||||||
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy` classes have been removed.
|
|
||||||
|
|
||||||
* `NativeSessionStorage::setSaveHandler()` now requires an instance of `\SessionHandlerInterface` as argument.
|
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler` and
|
||||||
|
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` classes have been removed.
|
||||||
|
|
||||||
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler` does not work with the legacy
|
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler` does not work with the legacy
|
||||||
mongo extension anymore. It requires mongodb/mongodb package and ext-mongodb.
|
mongo extension anymore. It requires mongodb/mongodb package and ext-mongodb.
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
"symfony/polyfill-intl-icu": "~1.0",
|
"symfony/polyfill-intl-icu": "~1.0",
|
||||||
"symfony/polyfill-mbstring": "~1.0",
|
"symfony/polyfill-mbstring": "~1.0",
|
||||||
"symfony/polyfill-php56": "~1.0",
|
"symfony/polyfill-php56": "~1.0",
|
||||||
"symfony/polyfill-php70": "~1.0",
|
"symfony/polyfill-php70": "~1.6",
|
||||||
"symfony/polyfill-util": "~1.0"
|
"symfony/polyfill-util": "~1.0"
|
||||||
},
|
},
|
||||||
"replace": {
|
"replace": {
|
||||||
|
|
|
@ -4,6 +4,7 @@ CHANGELOG
|
||||||
3.4.0
|
3.4.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
* Session `use_strict_mode` is now enabled by default and the corresponding option has been deprecated
|
||||||
* Made the `cache:clear` command to *not* clear "app" PSR-6 cache pools anymore,
|
* Made the `cache:clear` command to *not* clear "app" PSR-6 cache pools anymore,
|
||||||
but to still clear "system" ones; use the `cache:pool:clear` command to clear "app" pools instead
|
but to still clear "system" ones; use the `cache:pool:clear` command to clear "app" pools instead
|
||||||
* Always register a minimalist logger that writes in `stderr`
|
* Always register a minimalist logger that writes in `stderr`
|
||||||
|
|
|
@ -462,11 +462,14 @@ class Configuration implements ConfigurationInterface
|
||||||
->scalarNode('gc_divisor')->end()
|
->scalarNode('gc_divisor')->end()
|
||||||
->scalarNode('gc_probability')->defaultValue(1)->end()
|
->scalarNode('gc_probability')->defaultValue(1)->end()
|
||||||
->scalarNode('gc_maxlifetime')->end()
|
->scalarNode('gc_maxlifetime')->end()
|
||||||
->booleanNode('use_strict_mode')->end()
|
->booleanNode('use_strict_mode')
|
||||||
|
->defaultTrue()
|
||||||
|
->setDeprecated('The "%path%.%node%" option is enabled by default and deprecated since Symfony 3.4. It will be always enabled in 4.0.')
|
||||||
|
->end()
|
||||||
->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end()
|
->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end()
|
||||||
->integerNode('metadata_update_threshold')
|
->integerNode('metadata_update_threshold')
|
||||||
->defaultValue('0')
|
->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')
|
->info('seconds to wait between 2 session metadata updates')
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
|
|
@ -916,14 +916,7 @@ class FrameworkExtension extends Extension
|
||||||
$container->getDefinition('session.storage.native')->replaceArgument(1, null);
|
$container->getDefinition('session.storage.native')->replaceArgument(1, null);
|
||||||
$container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null);
|
$container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null);
|
||||||
} else {
|
} else {
|
||||||
$handlerId = $config['handler_id'];
|
$container->setAlias('session.handler', $config['handler_id'])->setPrivate(true);
|
||||||
|
|
||||||
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)->setPrivate(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$container->setParameter('session.save_path', $config['save_path']);
|
$container->setParameter('session.save_path', $config['save_path']);
|
||||||
|
|
|
@ -48,11 +48,17 @@
|
||||||
<argument type="service" id="session.storage.metadata_bag" />
|
<argument type="service" id="session.storage.metadata_bag" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="session.handler.native_file" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler">
|
<service id="session.handler.native_file" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler">
|
||||||
<argument>%session.save_path%</argument>
|
<argument type="service">
|
||||||
|
<service class="Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler">
|
||||||
|
<argument>%session.save_path%</argument>
|
||||||
|
</service>
|
||||||
|
</argument>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="session.handler.write_check" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler" />
|
<service id="session.handler.write_check" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler">
|
||||||
|
<deprecated>The "%service_id%" service is deprecated since Symfony 3.4 and will be removed in 4.0. Use the `session.lazy_write` ini setting instead.</deprecated>
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="session_listener" class="Symfony\Component\HttpKernel\EventListener\SessionListener">
|
<service id="session_listener" class="Symfony\Component\HttpKernel\EventListener\SessionListener">
|
||||||
<tag name="kernel.event_subscriber" />
|
<tag name="kernel.event_subscriber" />
|
||||||
|
|
|
@ -301,6 +301,7 @@ class ConfigurationTest extends TestCase
|
||||||
'gc_probability' => 1,
|
'gc_probability' => 1,
|
||||||
'save_path' => '%kernel.cache_dir%/sessions',
|
'save_path' => '%kernel.cache_dir%/sessions',
|
||||||
'metadata_update_threshold' => '0',
|
'metadata_update_threshold' => '0',
|
||||||
|
'use_strict_mode' => true,
|
||||||
),
|
),
|
||||||
'request' => array(
|
'request' => array(
|
||||||
'enabled' => false,
|
'enabled' => false,
|
||||||
|
|
|
@ -4,8 +4,9 @@ CHANGELOG
|
||||||
3.4.0
|
3.4.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
* deprecated the `NativeSessionHandler` class,
|
* implemented PHP 7.0's `SessionUpdateTimestampHandlerInterface` with a new
|
||||||
* deprecated the `AbstractProxy`, `NativeProxy` and `SessionHandlerProxy` classes,
|
`AbstractSessionHandler` base class and a new `StrictSessionHandler` wrapper
|
||||||
|
* deprecated the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes
|
||||||
* deprecated setting session save handlers that do not implement `\SessionHandlerInterface` in `NativeSessionStorage::setSaveHandler()`
|
* deprecated setting session save handlers that do not implement `\SessionHandlerInterface` in `NativeSessionStorage::setSaveHandler()`
|
||||||
* deprecated using `MongoDbSessionHandler` with the legacy mongo extension; use it with the mongodb/mongodb package and ext-mongodb instead
|
* deprecated using `MongoDbSessionHandler` with the legacy mongo extension; use it with the mongodb/mongodb package and ext-mongodb instead
|
||||||
* deprecated `MemcacheSessionHandler`; use `MemcachedSessionHandler` instead
|
* deprecated `MemcacheSessionHandler`; use `MemcachedSessionHandler` instead
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
<?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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This abstract session handler provides a generic implementation
|
||||||
|
* of the PHP 7.0 SessionUpdateTimestampHandlerInterface,
|
||||||
|
* enabling strict and lazy session handling.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
abstract class AbstractSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
|
||||||
|
{
|
||||||
|
private $sessionName;
|
||||||
|
private $prefetchId;
|
||||||
|
private $prefetchData;
|
||||||
|
private $newSessionId;
|
||||||
|
private $igbinaryEmptyData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function open($savePath, $sessionName)
|
||||||
|
{
|
||||||
|
$this->sessionName = $sessionName;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sessionId
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract protected function doRead($sessionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sessionId
|
||||||
|
* @param string $data
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
abstract protected function doWrite($sessionId, $data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sessionId
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
abstract protected function doDestroy($sessionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validateId($sessionId)
|
||||||
|
{
|
||||||
|
$this->prefetchData = $this->read($sessionId);
|
||||||
|
$this->prefetchId = $sessionId;
|
||||||
|
|
||||||
|
return '' !== $this->prefetchData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function read($sessionId)
|
||||||
|
{
|
||||||
|
if (null !== $this->prefetchId) {
|
||||||
|
$prefetchId = $this->prefetchId;
|
||||||
|
$prefetchData = $this->prefetchData;
|
||||||
|
$this->prefetchId = $this->prefetchData = null;
|
||||||
|
|
||||||
|
if ($prefetchId === $sessionId || '' === $prefetchData) {
|
||||||
|
$this->newSessionId = '' === $prefetchData ? $sessionId : null;
|
||||||
|
|
||||||
|
return $prefetchData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $this->doRead($sessionId);
|
||||||
|
$this->newSessionId = '' === $data ? $sessionId : null;
|
||||||
|
if (\PHP_VERSION_ID < 70000) {
|
||||||
|
$this->prefetchData = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function write($sessionId, $data)
|
||||||
|
{
|
||||||
|
if (\PHP_VERSION_ID < 70000 && $this->prefetchData) {
|
||||||
|
$readData = $this->prefetchData;
|
||||||
|
$this->prefetchData = null;
|
||||||
|
|
||||||
|
if ($readData === $data) {
|
||||||
|
return $this->updateTimestamp($sessionId, $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (null === $this->igbinaryEmptyData) {
|
||||||
|
// see https://github.com/igbinary/igbinary/issues/146
|
||||||
|
$this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize(array()) : '';
|
||||||
|
}
|
||||||
|
if ('' === $data || $this->igbinaryEmptyData === $data) {
|
||||||
|
return $this->destroy($sessionId);
|
||||||
|
}
|
||||||
|
$this->newSessionId = null;
|
||||||
|
|
||||||
|
return $this->doWrite($sessionId, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function destroy($sessionId)
|
||||||
|
{
|
||||||
|
if (\PHP_VERSION_ID < 70000) {
|
||||||
|
$this->prefetchData = null;
|
||||||
|
}
|
||||||
|
if (!headers_sent() && ini_get('session.use_cookies')) {
|
||||||
|
if (!$this->sessionName) {
|
||||||
|
throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', get_class($this)));
|
||||||
|
}
|
||||||
|
$sessionCookie = sprintf(' %s=', urlencode($this->sessionName));
|
||||||
|
$sessionCookieWithId = sprintf('%s%s;', $sessionCookie, urlencode($sessionId));
|
||||||
|
$sessionCookieFound = false;
|
||||||
|
$otherCookies = array();
|
||||||
|
foreach (headers_list() as $h) {
|
||||||
|
if (0 !== stripos($h, 'Set-Cookie:')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (11 === strpos($h, $sessionCookie, 11)) {
|
||||||
|
$sessionCookieFound = true;
|
||||||
|
|
||||||
|
if (11 !== strpos($h, $sessionCookieWithId, 11)) {
|
||||||
|
$otherCookies[] = $h;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$otherCookies[] = $h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($sessionCookieFound) {
|
||||||
|
header_remove('Set-Cookie');
|
||||||
|
foreach ($otherCookies as $h) {
|
||||||
|
header('Set-Cookie:'.$h, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->newSessionId === $sessionId || $this->doDestroy($sessionId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||||
*
|
*
|
||||||
* @author Drak <drak@zikula.org>
|
* @author Drak <drak@zikula.org>
|
||||||
*/
|
*/
|
||||||
class MemcachedSessionHandler implements \SessionHandlerInterface
|
class MemcachedSessionHandler extends AbstractSessionHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var \Memcached Memcached driver
|
* @var \Memcached Memcached driver
|
||||||
|
@ -39,7 +39,7 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* List of available options:
|
* List of available options:
|
||||||
* * prefix: The prefix to use for the memcached keys in order to avoid collision
|
* * prefix: The prefix to use for the memcached keys in order to avoid collision
|
||||||
* * expiretime: The time to live in seconds
|
* * expiretime: The time to live in seconds.
|
||||||
*
|
*
|
||||||
* @param \Memcached $memcached A \Memcached instance
|
* @param \Memcached $memcached A \Memcached instance
|
||||||
* @param array $options An associative array of Memcached options
|
* @param array $options An associative array of Memcached options
|
||||||
|
@ -60,14 +60,6 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
||||||
$this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
|
$this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function open($savePath, $sessionName)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -79,7 +71,7 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function read($sessionId)
|
protected function doRead($sessionId)
|
||||||
{
|
{
|
||||||
return $this->memcached->get($this->prefix.$sessionId) ?: '';
|
return $this->memcached->get($this->prefix.$sessionId) ?: '';
|
||||||
}
|
}
|
||||||
|
@ -87,7 +79,15 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function write($sessionId, $data)
|
public function updateTimestamp($sessionId, $data)
|
||||||
|
{
|
||||||
|
return $this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doWrite($sessionId, $data)
|
||||||
{
|
{
|
||||||
return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl);
|
return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function destroy($sessionId)
|
protected function doDestroy($sessionId)
|
||||||
{
|
{
|
||||||
$result = $this->memcached->delete($this->prefix.$sessionId);
|
$result = $this->memcached->delete($this->prefix.$sessionId);
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||||
* @see https://packagist.org/packages/mongodb/mongodb
|
* @see https://packagist.org/packages/mongodb/mongodb
|
||||||
* @see http://php.net/manual/en/set.mongodb.php
|
* @see http://php.net/manual/en/set.mongodb.php
|
||||||
*/
|
*/
|
||||||
class MongoDbSessionHandler implements \SessionHandlerInterface
|
class MongoDbSessionHandler extends AbstractSessionHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var \Mongo|\MongoClient|\MongoDB\Client
|
* @var \Mongo|\MongoClient|\MongoDB\Client
|
||||||
|
@ -43,7 +43,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||||
* * id_field: The field name for storing the session id [default: _id]
|
* * id_field: The field name for storing the session id [default: _id]
|
||||||
* * data_field: The field name for storing the session data [default: data]
|
* * data_field: The field name for storing the session data [default: data]
|
||||||
* * time_field: The field name for storing the timestamp [default: time]
|
* * time_field: The field name for storing the timestamp [default: time]
|
||||||
* * expiry_field: The field name for storing the expiry-timestamp [default: expires_at]
|
* * expiry_field: The field name for storing the expiry-timestamp [default: expires_at].
|
||||||
*
|
*
|
||||||
* It is strongly recommended to put an index on the `expiry_field` for
|
* It is strongly recommended to put an index on the `expiry_field` for
|
||||||
* garbage-collection. Alternatively it's possible to automatically expire
|
* garbage-collection. Alternatively it's possible to automatically expire
|
||||||
|
@ -92,14 +92,6 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||||
), $options);
|
), $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function open($savePath, $sessionName)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -111,7 +103,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function destroy($sessionId)
|
protected function doDestroy($sessionId)
|
||||||
{
|
{
|
||||||
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
|
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
|
||||||
|
|
||||||
|
@ -139,7 +131,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function write($sessionId, $data)
|
protected function doWrite($sessionId, $data)
|
||||||
{
|
{
|
||||||
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
|
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
|
||||||
|
|
||||||
|
@ -171,7 +163,34 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function read($sessionId)
|
public function updateTimestamp($sessionId, $data)
|
||||||
|
{
|
||||||
|
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
|
||||||
|
|
||||||
|
if ($this->mongo instanceof \MongoDB\Client) {
|
||||||
|
$methodName = 'updateOne';
|
||||||
|
$options = array();
|
||||||
|
} else {
|
||||||
|
$methodName = 'update';
|
||||||
|
$options = array('multiple' => false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getCollection()->$methodName(
|
||||||
|
array($this->options['id_field'] => $sessionId),
|
||||||
|
array('$set' => array(
|
||||||
|
$this->options['time_field'] => $this->createDateTime(),
|
||||||
|
$this->options['expiry_field'] => $expiry,
|
||||||
|
)),
|
||||||
|
$options
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doRead($sessionId)
|
||||||
{
|
{
|
||||||
$dbData = $this->getCollection()->findOne(array(
|
$dbData = $this->getCollection()->findOne(array(
|
||||||
$this->options['id_field'] => $sessionId,
|
$this->options['id_field'] => $sessionId,
|
||||||
|
|
|
@ -11,12 +11,14 @@
|
||||||
|
|
||||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||||
|
|
||||||
@trigger_error('The '.__NAMESPACE__.'\NativeSessionHandler class is deprecated since version 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.', E_USER_DEPRECATED);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated since version 3.4, to be removed in 4.0. Use \SessionHandler instead.
|
* @deprecated since version 3.4, to be removed in 4.0. Use \SessionHandler instead.
|
||||||
* @see http://php.net/sessionhandler
|
* @see http://php.net/sessionhandler
|
||||||
*/
|
*/
|
||||||
class NativeSessionHandler extends \SessionHandler
|
class NativeSessionHandler extends \SessionHandler
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
@trigger_error('The '.__NAMESPACE__.'\NativeSessionHandler class is deprecated since version 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.', E_USER_DEPRECATED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,8 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||||
*
|
*
|
||||||
* @author Drak <drak@zikula.org>
|
* @author Drak <drak@zikula.org>
|
||||||
*/
|
*/
|
||||||
class NullSessionHandler implements \SessionHandlerInterface
|
class NullSessionHandler extends AbstractSessionHandler
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function open($savePath, $sessionName)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -37,15 +29,7 @@ class NullSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function read($sessionId)
|
public function validateId($sessionId)
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function write($sessionId, $data)
|
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -53,7 +37,31 @@ class NullSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function destroy($sessionId)
|
protected function doRead($sessionId)
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function updateTimestamp($sessionId, $data)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doWrite($sessionId, $data)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDestroy($sessionId)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||||
* @author Michael Williams <michael.williams@funsational.com>
|
* @author Michael Williams <michael.williams@funsational.com>
|
||||||
* @author Tobias Schultze <http://tobion.de>
|
* @author Tobias Schultze <http://tobion.de>
|
||||||
*/
|
*/
|
||||||
class PdoSessionHandler implements \SessionHandlerInterface
|
class PdoSessionHandler extends AbstractSessionHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* No locking is done. This means sessions are prone to loss of data due to
|
* No locking is done. This means sessions are prone to loss of data due to
|
||||||
|
@ -260,11 +260,13 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||||
*/
|
*/
|
||||||
public function open($savePath, $sessionName)
|
public function open($savePath, $sessionName)
|
||||||
{
|
{
|
||||||
|
$this->sessionExpired = false;
|
||||||
|
|
||||||
if (null === $this->pdo) {
|
if (null === $this->pdo) {
|
||||||
$this->connect($this->dsn ?: $savePath);
|
$this->connect($this->dsn ?: $savePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return parent::open($savePath, $sessionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -273,7 +275,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||||
public function read($sessionId)
|
public function read($sessionId)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $this->doRead($sessionId);
|
return parent::read($sessionId);
|
||||||
} catch (\PDOException $e) {
|
} catch (\PDOException $e) {
|
||||||
$this->rollback();
|
$this->rollback();
|
||||||
|
|
||||||
|
@ -296,7 +298,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function destroy($sessionId)
|
protected function doDestroy($sessionId)
|
||||||
{
|
{
|
||||||
// delete the record associated with this id
|
// delete the record associated with this id
|
||||||
$sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
|
$sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
|
||||||
|
@ -317,7 +319,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function write($sessionId, $data)
|
protected function doWrite($sessionId, $data)
|
||||||
{
|
{
|
||||||
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
|
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
|
||||||
|
|
||||||
|
@ -372,6 +374,30 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function updateTimestamp($sessionId, $data)
|
||||||
|
{
|
||||||
|
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$updateStmt = $this->pdo->prepare(
|
||||||
|
"UPDATE $this->table SET $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"
|
||||||
|
);
|
||||||
|
$updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||||
|
$updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
|
||||||
|
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||||
|
$updateStmt->execute();
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
$this->rollback();
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -491,10 +517,8 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||||
*
|
*
|
||||||
* @return string The session data
|
* @return string The session data
|
||||||
*/
|
*/
|
||||||
private function doRead($sessionId)
|
protected function doRead($sessionId)
|
||||||
{
|
{
|
||||||
$this->sessionExpired = false;
|
|
||||||
|
|
||||||
if (self::LOCK_ADVISORY === $this->lockMode) {
|
if (self::LOCK_ADVISORY === $this->lockMode) {
|
||||||
$this->unlockStatements[] = $this->doAdvisoryLock($sessionId);
|
$this->unlockStatements[] = $this->doAdvisoryLock($sessionId);
|
||||||
}
|
}
|
||||||
|
@ -517,7 +541,9 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||||
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
|
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
|
if (!ini_get('session.use_strict_mode') && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
|
||||||
|
// In strict mode, session fixation is not possible: new sessions always start with a unique
|
||||||
|
// random id, so that concurrency is not possible and this code path can be skipped.
|
||||||
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
|
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
|
||||||
// until other connections to the session are committed.
|
// until other connections to the session are committed.
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
<?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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds basic `SessionUpdateTimestampHandlerInterface` behaviors to another `SessionHandlerInterface`.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class StrictSessionHandler extends AbstractSessionHandler
|
||||||
|
{
|
||||||
|
private $handler;
|
||||||
|
|
||||||
|
public function __construct(\SessionHandlerInterface $handler)
|
||||||
|
{
|
||||||
|
if ($handler instanceof \SessionUpdateTimestampHandlerInterface) {
|
||||||
|
throw new \LogicException(sprintf('"%s" is already an instance of "SessionUpdateTimestampHandlerInterface", you cannot wrap it with "%s".', get_class($handler), self::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->handler = $handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function open($savePath, $sessionName)
|
||||||
|
{
|
||||||
|
parent::open($savePath, $sessionName);
|
||||||
|
|
||||||
|
return $this->handler->open($savePath, $sessionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doRead($sessionId)
|
||||||
|
{
|
||||||
|
return $this->handler->read($sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function updateTimestamp($sessionId, $data)
|
||||||
|
{
|
||||||
|
return $this->write($sessionId, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doWrite($sessionId, $data)
|
||||||
|
{
|
||||||
|
return $this->handler->write($sessionId, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDestroy($sessionId)
|
||||||
|
{
|
||||||
|
return $this->handler->destroy($sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
return $this->handler->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function gc($maxlifetime)
|
||||||
|
{
|
||||||
|
return $this->handler->gc($maxlifetime);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,10 +11,14 @@
|
||||||
|
|
||||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||||
|
|
||||||
|
@trigger_error(sprintf('The %s class is deprecated since version 3.4 and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.', WriteCheckSessionHandler::class), E_USER_DEPRECATED);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps another SessionHandlerInterface to only write the session when it has been modified.
|
* Wraps another SessionHandlerInterface to only write the session when it has been modified.
|
||||||
*
|
*
|
||||||
* @author Adrien Brault <adrien.brault@gmail.com>
|
* @author Adrien Brault <adrien.brault@gmail.com>
|
||||||
|
*
|
||||||
|
* @deprecated since version 3.4, to be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.
|
||||||
*/
|
*/
|
||||||
class WriteCheckSessionHandler implements \SessionHandlerInterface
|
class WriteCheckSessionHandler implements \SessionHandlerInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
|
|
||||||
namespace Symfony\Component\HttpFoundation\Session\Storage;
|
namespace Symfony\Component\HttpFoundation\Session\Storage;
|
||||||
|
|
||||||
use Symfony\Component\Debug\Exception\ContextErrorException;
|
|
||||||
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
|
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
|
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||||
/**
|
/**
|
||||||
* @var SessionBagInterface[]
|
* @var SessionBagInterface[]
|
||||||
*/
|
*/
|
||||||
protected $bags;
|
protected $bags = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
|
@ -100,9 +100,9 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||||
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
|
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
|
||||||
{
|
{
|
||||||
$options += array(
|
$options += array(
|
||||||
// disable by default because it's managed by HeaderBag (if used)
|
'cache_limiter' => 'private_no_expire',
|
||||||
'cache_limiter' => '',
|
|
||||||
'use_cookies' => 1,
|
'use_cookies' => 1,
|
||||||
|
'lazy_write' => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
session_register_shutdown();
|
session_register_shutdown();
|
||||||
|
@ -217,15 +217,31 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||||
*/
|
*/
|
||||||
public function save()
|
public function save()
|
||||||
{
|
{
|
||||||
|
$session = $_SESSION;
|
||||||
|
|
||||||
|
foreach ($this->bags as $bag) {
|
||||||
|
if (empty($_SESSION[$key = $bag->getStorageKey()])) {
|
||||||
|
unset($_SESSION[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (array($key = $this->metadataBag->getStorageKey()) === array_keys($_SESSION)) {
|
||||||
|
unset($_SESSION[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
// Register custom error handler to catch a possible failure warning during session write
|
// Register custom error handler to catch a possible failure warning during session write
|
||||||
set_error_handler(function ($errno, $errstr, $errfile, $errline, $errcontext) {
|
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
|
||||||
throw new ContextErrorException($errstr, $errno, E_WARNING, $errfile, $errline, $errcontext);
|
throw new \ErrorException($errstr, $errno, E_WARNING, $errfile, $errline);
|
||||||
}, E_WARNING);
|
}, E_WARNING);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$e = null;
|
||||||
session_write_close();
|
session_write_close();
|
||||||
|
} catch (\ErrorException $e) {
|
||||||
|
} finally {
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
} catch (ContextErrorException $e) {
|
$_SESSION = $session;
|
||||||
|
}
|
||||||
|
if (null !== $e) {
|
||||||
// The default PHP error message is not very helpful, as it does not give any information on the current save handler.
|
// The default PHP error message is not very helpful, as it does not give any information on the current save handler.
|
||||||
// Therefore, we catch this error and trigger a warning with a better error message
|
// Therefore, we catch this error and trigger a warning with a better error message
|
||||||
$handler = $this->getSaveHandler();
|
$handler = $this->getSaveHandler();
|
||||||
|
@ -233,7 +249,6 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||||
$handler = $handler->getHandler();
|
$handler = $handler->getHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
restore_error_handler();
|
|
||||||
trigger_error(sprintf('session_write_close(): Failed to write session data with %s handler', get_class($handler)), E_USER_WARNING);
|
trigger_error(sprintf('session_write_close(): Failed to write session data with %s handler', get_class($handler)), E_USER_WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,13 +401,6 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||||
throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.');
|
throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($saveHandler instanceof AbstractProxy) {
|
|
||||||
@trigger_error(
|
|
||||||
'Using session save handlers that are instances of AbstractProxy is deprecated since version 3.4 and will be removed in 4.0.',
|
|
||||||
E_USER_DEPRECATED
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headers_sent($file, $line)) {
|
if (headers_sent($file, $line)) {
|
||||||
throw new \RuntimeException(sprintf('Failed to set the session handler because headers have already been sent by "%s" at line %d.', $file, $line));
|
throw new \RuntimeException(sprintf('Failed to set the session handler because headers have already been sent by "%s" at line %d.', $file, $line));
|
||||||
}
|
}
|
||||||
|
@ -401,11 +409,13 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||||
if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
|
if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
|
||||||
$saveHandler = new SessionHandlerProxy($saveHandler);
|
$saveHandler = new SessionHandlerProxy($saveHandler);
|
||||||
} elseif (!$saveHandler instanceof AbstractProxy) {
|
} elseif (!$saveHandler instanceof AbstractProxy) {
|
||||||
$saveHandler = new SessionHandlerProxy(new \SessionHandler());
|
$saveHandler = new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler()));
|
||||||
}
|
}
|
||||||
$this->saveHandler = $saveHandler;
|
$this->saveHandler = $saveHandler;
|
||||||
|
|
||||||
if ($this->saveHandler instanceof \SessionHandlerInterface) {
|
if ($this->saveHandler instanceof SessionHandlerProxy) {
|
||||||
|
session_set_save_handler($this->saveHandler->getHandler(), false);
|
||||||
|
} elseif ($this->saveHandler instanceof \SessionHandlerInterface) {
|
||||||
session_set_save_handler($this->saveHandler, false);
|
session_set_save_handler($this->saveHandler, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,7 @@
|
||||||
|
|
||||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
|
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
|
||||||
|
|
||||||
@trigger_error('The '.__NAMESPACE__.'\AbstractProxy class is deprecated since version 3.4 and will be removed in 4.0. Use your session handler implementation directly.', E_USER_DEPRECATED);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated since version 3.4, to be removed in 4.0. Use your session handler implementation directly.
|
|
||||||
*
|
|
||||||
* @author Drak <drak@zikula.org>
|
* @author Drak <drak@zikula.org>
|
||||||
*/
|
*/
|
||||||
abstract class AbstractProxy
|
abstract class AbstractProxy
|
||||||
|
|
|
@ -11,11 +11,7 @@
|
||||||
|
|
||||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
|
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
|
||||||
|
|
||||||
@trigger_error('The '.__NAMESPACE__.'\SessionHandlerProxy class is deprecated since version 3.4 and will be removed in 4.0. Use your session handler implementation directly.', E_USER_DEPRECATED);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated since version 3.4, to be removed in 4.0. Use your session handler implementation directly.
|
|
||||||
*
|
|
||||||
* @author Drak <drak@zikula.org>
|
* @author Drak <drak@zikula.org>
|
||||||
*/
|
*/
|
||||||
class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface
|
class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires PHP 7.0
|
||||||
|
*/
|
||||||
|
class AbstractSessionHandlerTest extends TestCase
|
||||||
|
{
|
||||||
|
private static $server;
|
||||||
|
|
||||||
|
public static function setUpBeforeClass()
|
||||||
|
{
|
||||||
|
$spec = array(
|
||||||
|
1 => array('file', '/dev/null', 'w'),
|
||||||
|
2 => array('file', '/dev/null', 'w'),
|
||||||
|
);
|
||||||
|
if (!self::$server = @proc_open('exec php -S localhost:8053', $spec, $pipes, __DIR__.'/Fixtures')) {
|
||||||
|
self::markTestSkipped('PHP server unable to start.');
|
||||||
|
}
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tearDownAfterClass()
|
||||||
|
{
|
||||||
|
if (self::$server) {
|
||||||
|
proc_terminate(self::$server);
|
||||||
|
proc_close(self::$server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideSession
|
||||||
|
*/
|
||||||
|
public function testSession($fixture)
|
||||||
|
{
|
||||||
|
$context = array('http' => array('header' => "Cookie: sid=123abc\r\n"));
|
||||||
|
$context = stream_context_create($context);
|
||||||
|
$result = file_get_contents(sprintf('http://localhost:8053/%s.php', $fixture), false, $context);
|
||||||
|
|
||||||
|
$this->assertStringEqualsFile(__DIR__.sprintf('/Fixtures/%s.expected', $fixture), $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideSession()
|
||||||
|
{
|
||||||
|
foreach (glob(__DIR__.'/Fixtures/*.php') as $file) {
|
||||||
|
yield array(pathinfo($file, PATHINFO_FILENAME));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler;
|
||||||
|
|
||||||
|
$parent = __DIR__;
|
||||||
|
while (!@file_exists($parent.'/vendor/autoload.php')) {
|
||||||
|
if (!@file_exists($parent)) {
|
||||||
|
// open_basedir restriction in effect
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($parent === dirname($parent)) {
|
||||||
|
echo "vendor/autoload.php not found\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent = dirname($parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
require $parent.'/vendor/autoload.php';
|
||||||
|
|
||||||
|
error_reporting(-1);
|
||||||
|
ini_set('html_errors', 0);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('session.gc_probability', 0);
|
||||||
|
ini_set('session.serialize_handler', 'php');
|
||||||
|
ini_set('session.cookie_lifetime', 0);
|
||||||
|
ini_set('session.cookie_domain', '');
|
||||||
|
ini_set('session.cookie_secure', '');
|
||||||
|
ini_set('session.cookie_httponly', '');
|
||||||
|
ini_set('session.use_cookies', 1);
|
||||||
|
ini_set('session.use_only_cookies', 1);
|
||||||
|
ini_set('session.cache_expire', 180);
|
||||||
|
ini_set('session.cookie_path', '/');
|
||||||
|
ini_set('session.cookie_domain', '');
|
||||||
|
ini_set('session.cookie_secure', 1);
|
||||||
|
ini_set('session.cookie_httponly', 1);
|
||||||
|
ini_set('session.use_strict_mode', 1);
|
||||||
|
ini_set('session.lazy_write', 1);
|
||||||
|
ini_set('session.name', 'sid');
|
||||||
|
ini_set('session.save_path', __DIR__);
|
||||||
|
ini_set('session.cache_limiter', 'private_no_expire');
|
||||||
|
|
||||||
|
header_remove('X-Powered-By');
|
||||||
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
|
|
||||||
|
register_shutdown_function(function () {
|
||||||
|
echo "\n";
|
||||||
|
header_remove('Last-Modified');
|
||||||
|
session_write_close();
|
||||||
|
print_r(headers_list());
|
||||||
|
echo "shutdown\n";
|
||||||
|
});
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
class TestSessionHandler extends AbstractSessionHandler
|
||||||
|
{
|
||||||
|
private $data;
|
||||||
|
|
||||||
|
public function __construct($data = '')
|
||||||
|
{
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function open($path, $name)
|
||||||
|
{
|
||||||
|
echo __FUNCTION__, "\n";
|
||||||
|
|
||||||
|
return parent::open($path, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateId($sessionId)
|
||||||
|
{
|
||||||
|
echo __FUNCTION__, "\n";
|
||||||
|
|
||||||
|
return parent::validateId($sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function read($sessionId)
|
||||||
|
{
|
||||||
|
echo __FUNCTION__, "\n";
|
||||||
|
|
||||||
|
return parent::read($sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function updateTimestamp($sessionId, $data)
|
||||||
|
{
|
||||||
|
echo __FUNCTION__, "\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function write($sessionId, $data)
|
||||||
|
{
|
||||||
|
echo __FUNCTION__, "\n";
|
||||||
|
|
||||||
|
return parent::write($sessionId, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function destroy($sessionId)
|
||||||
|
{
|
||||||
|
echo __FUNCTION__, "\n";
|
||||||
|
|
||||||
|
return parent::destroy($sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
echo __FUNCTION__, "\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function gc($maxLifetime)
|
||||||
|
{
|
||||||
|
echo __FUNCTION__, "\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doRead($sessionId)
|
||||||
|
{
|
||||||
|
echo __FUNCTION__.': ', $this->data, "\n";
|
||||||
|
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doWrite($sessionId, $data)
|
||||||
|
{
|
||||||
|
echo __FUNCTION__.': ', $data, "\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doDestroy($sessionId)
|
||||||
|
{
|
||||||
|
echo __FUNCTION__, "\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
open
|
||||||
|
validateId
|
||||||
|
read
|
||||||
|
doRead: abc|i:123;
|
||||||
|
read
|
||||||
|
|
||||||
|
write
|
||||||
|
destroy
|
||||||
|
doDestroy
|
||||||
|
close
|
||||||
|
Array
|
||||||
|
(
|
||||||
|
[0] => Content-Type: text/plain; charset=utf-8
|
||||||
|
[1] => Cache-Control: private, max-age=10800
|
||||||
|
[2] => Set-Cookie: sid=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly
|
||||||
|
)
|
||||||
|
shutdown
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require __DIR__.'/common.inc';
|
||||||
|
|
||||||
|
session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
unset($_SESSION['abc']);
|
|
@ -0,0 +1,14 @@
|
||||||
|
open
|
||||||
|
validateId
|
||||||
|
read
|
||||||
|
doRead: abc|i:123;
|
||||||
|
read
|
||||||
|
123
|
||||||
|
updateTimestamp
|
||||||
|
close
|
||||||
|
Array
|
||||||
|
(
|
||||||
|
[0] => Content-Type: text/plain; charset=utf-8
|
||||||
|
[1] => Cache-Control: private, max-age=10800
|
||||||
|
)
|
||||||
|
shutdown
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require __DIR__.'/common.inc';
|
||||||
|
|
||||||
|
session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
echo $_SESSION['abc'];
|
|
@ -0,0 +1,24 @@
|
||||||
|
open
|
||||||
|
validateId
|
||||||
|
read
|
||||||
|
doRead: abc|i:123;
|
||||||
|
read
|
||||||
|
destroy
|
||||||
|
doDestroy
|
||||||
|
close
|
||||||
|
open
|
||||||
|
validateId
|
||||||
|
read
|
||||||
|
doRead: abc|i:123;
|
||||||
|
read
|
||||||
|
|
||||||
|
write
|
||||||
|
doWrite: abc|i:123;
|
||||||
|
close
|
||||||
|
Array
|
||||||
|
(
|
||||||
|
[0] => Content-Type: text/plain; charset=utf-8
|
||||||
|
[1] => Cache-Control: private, max-age=10800
|
||||||
|
[2] => Set-Cookie: sid=random_session_id; path=/; secure; HttpOnly
|
||||||
|
)
|
||||||
|
shutdown
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require __DIR__.'/common.inc';
|
||||||
|
|
||||||
|
session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
session_regenerate_id(true);
|
||||||
|
|
||||||
|
ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); });
|
|
@ -0,0 +1,20 @@
|
||||||
|
open
|
||||||
|
validateId
|
||||||
|
read
|
||||||
|
doRead:
|
||||||
|
read
|
||||||
|
Array
|
||||||
|
(
|
||||||
|
[0] => bar
|
||||||
|
)
|
||||||
|
$_SESSION is not empty
|
||||||
|
write
|
||||||
|
destroy
|
||||||
|
close
|
||||||
|
$_SESSION is not empty
|
||||||
|
Array
|
||||||
|
(
|
||||||
|
[0] => Content-Type: text/plain; charset=utf-8
|
||||||
|
[1] => Cache-Control: private, max-age=10800
|
||||||
|
)
|
||||||
|
shutdown
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require __DIR__.'/common.inc';
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||||
|
|
||||||
|
$storage = new NativeSessionStorage();
|
||||||
|
$storage->setSaveHandler(new TestSessionHandler());
|
||||||
|
$flash = new FlashBag();
|
||||||
|
$storage->registerBag($flash);
|
||||||
|
$storage->start();
|
||||||
|
|
||||||
|
$flash->add('foo', 'bar');
|
||||||
|
|
||||||
|
print_r($flash->get('foo'));
|
||||||
|
echo empty($_SESSION) ? '$_SESSION is empty' : '$_SESSION is not empty';
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
$storage->save();
|
||||||
|
|
||||||
|
echo empty($_SESSION) ? '$_SESSION is empty' : '$_SESSION is not empty';
|
||||||
|
|
||||||
|
ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); });
|
|
@ -0,0 +1,15 @@
|
||||||
|
open
|
||||||
|
validateId
|
||||||
|
read
|
||||||
|
doRead: abc|i:123;
|
||||||
|
read
|
||||||
|
|
||||||
|
updateTimestamp
|
||||||
|
close
|
||||||
|
Array
|
||||||
|
(
|
||||||
|
[0] => Content-Type: text/plain; charset=utf-8
|
||||||
|
[1] => Cache-Control: private, max-age=10800
|
||||||
|
[2] => Set-Cookie: abc=def
|
||||||
|
)
|
||||||
|
shutdown
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require __DIR__.'/common.inc';
|
||||||
|
|
||||||
|
session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
setcookie('abc', 'def');
|
|
@ -25,6 +25,9 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandle
|
||||||
*/
|
*/
|
||||||
class NativeSessionHandlerTest extends TestCase
|
class NativeSessionHandlerTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @expectedDeprecation The Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler class is deprecated since version 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.
|
||||||
|
*/
|
||||||
public function testConstruct()
|
public function testConstruct()
|
||||||
{
|
{
|
||||||
$handler = new NativeSessionHandler();
|
$handler = new NativeSessionHandler();
|
||||||
|
|
|
@ -160,6 +160,9 @@ class PdoSessionHandlerTest extends TestCase
|
||||||
if (defined('HHVM_VERSION')) {
|
if (defined('HHVM_VERSION')) {
|
||||||
$this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
|
$this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
|
||||||
}
|
}
|
||||||
|
if (ini_get('session.use_strict_mode')) {
|
||||||
|
$this->markTestSkipped('Strict mode needs no locking for new sessions.');
|
||||||
|
}
|
||||||
|
|
||||||
$pdo = new MockPdo('pgsql');
|
$pdo = new MockPdo('pgsql');
|
||||||
$selectStmt = $this->getMockBuilder('PDOStatement')->getMock();
|
$selectStmt = $this->getMockBuilder('PDOStatement')->getMock();
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
<?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\AbstractSessionHandler;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
|
||||||
|
|
||||||
|
class StrictSessionHandlerTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testOpen()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('open')
|
||||||
|
->with('path', 'name')->willReturn(true);
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertInstanceof('SessionUpdateTimestampHandlerInterface', $proxy);
|
||||||
|
$this->assertInstanceof(AbstractSessionHandler::class, $proxy);
|
||||||
|
$this->assertTrue($proxy->open('path', 'name'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCloseSession()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('close')
|
||||||
|
->willReturn(true);
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertTrue($proxy->close());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateIdOK()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('read')
|
||||||
|
->with('id')->willReturn('data');
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertTrue($proxy->validateId('id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateIdKO()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('read')
|
||||||
|
->with('id')->willReturn('');
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertFalse($proxy->validateId('id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRead()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('read')
|
||||||
|
->with('id')->willReturn('data');
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertSame('data', $proxy->read('id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReadWithValidateIdOK()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('read')
|
||||||
|
->with('id')->willReturn('data');
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertTrue($proxy->validateId('id'));
|
||||||
|
$this->assertSame('data', $proxy->read('id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReadWithValidateIdMismatch()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->exactly(2))->method('read')
|
||||||
|
->withConsecutive(array('id1'), array('id2'))
|
||||||
|
->will($this->onConsecutiveCalls('data1', 'data2'));
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertTrue($proxy->validateId('id1'));
|
||||||
|
$this->assertSame('data2', $proxy->read('id2'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdateTimestamp()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('write')
|
||||||
|
->with('id', 'data')->willReturn(true);
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertTrue($proxy->updateTimestamp('id', 'data'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWrite()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('write')
|
||||||
|
->with('id', 'data')->willReturn(true);
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertTrue($proxy->write('id', 'data'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWriteEmptyNewSession()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('read')
|
||||||
|
->with('id')->willReturn('');
|
||||||
|
$handler->expects($this->never())->method('write');
|
||||||
|
$handler->expects($this->never())->method('destroy');
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertFalse($proxy->validateId('id'));
|
||||||
|
$this->assertSame('', $proxy->read('id'));
|
||||||
|
$this->assertTrue($proxy->write('id', ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWriteEmptyExistingSession()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('read')
|
||||||
|
->with('id')->willReturn('data');
|
||||||
|
$handler->expects($this->never())->method('write');
|
||||||
|
$handler->expects($this->once())->method('destroy')->willReturn(true);
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertSame('data', $proxy->read('id'));
|
||||||
|
$this->assertTrue($proxy->write('id', ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDestroy()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('destroy')
|
||||||
|
->with('id')->willReturn(true);
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertTrue($proxy->destroy('id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDestroyNewSession()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('read')
|
||||||
|
->with('id')->willReturn('');
|
||||||
|
$handler->expects($this->never())->method('destroy');
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertSame('', $proxy->read('id'));
|
||||||
|
$this->assertTrue($proxy->destroy('id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDestroyNonEmptyNewSession()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('read')
|
||||||
|
->with('id')->willReturn('');
|
||||||
|
$handler->expects($this->once())->method('write')
|
||||||
|
->with('id', 'data')->willReturn(true);
|
||||||
|
$handler->expects($this->once())->method('destroy')
|
||||||
|
->with('id')->willReturn(true);
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertSame('', $proxy->read('id'));
|
||||||
|
$this->assertTrue($proxy->write('id', 'data'));
|
||||||
|
$this->assertTrue($proxy->destroy('id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGc()
|
||||||
|
{
|
||||||
|
$handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||||
|
$handler->expects($this->once())->method('gc')
|
||||||
|
->with(123)->willReturn(true);
|
||||||
|
$proxy = new StrictSessionHandler($handler);
|
||||||
|
|
||||||
|
$this->assertTrue($proxy->gc(123));
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,8 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHa
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Adrien Brault <adrien.brault@gmail.com>
|
* @author Adrien Brault <adrien.brault@gmail.com>
|
||||||
|
*
|
||||||
|
* @group legacy
|
||||||
*/
|
*/
|
||||||
class WriteCheckSessionHandlerTest extends TestCase
|
class WriteCheckSessionHandlerTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
||||||
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||||
|
@ -152,7 +152,7 @@ class NativeSessionStorageTest extends TestCase
|
||||||
$this->iniSet('session.cache_limiter', 'nocache');
|
$this->iniSet('session.cache_limiter', 'nocache');
|
||||||
|
|
||||||
$storage = new NativeSessionStorage();
|
$storage = new NativeSessionStorage();
|
||||||
$this->assertEquals('', ini_get('session.cache_limiter'));
|
$this->assertEquals('private_no_expire', ini_get('session.cache_limiter'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testExplicitSessionCacheLimiter()
|
public function testExplicitSessionCacheLimiter()
|
||||||
|
@ -201,9 +201,9 @@ class NativeSessionStorageTest extends TestCase
|
||||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||||
$storage->setSaveHandler(null);
|
$storage->setSaveHandler(null);
|
||||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||||
$storage->setSaveHandler(new SessionHandlerProxy(new NativeSessionHandler()));
|
$storage->setSaveHandler(new SessionHandlerProxy(new NativeFileSessionHandler()));
|
||||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||||
$storage->setSaveHandler(new NativeSessionHandler());
|
$storage->setSaveHandler(new NativeFileSessionHandler());
|
||||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||||
$storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler()));
|
$storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler()));
|
||||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
|
||||||
|
|
|
@ -18,8 +18,6 @@ use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||||
/**
|
/**
|
||||||
* Test class for AbstractProxy.
|
* Test class for AbstractProxy.
|
||||||
*
|
*
|
||||||
* @group legacy
|
|
||||||
*
|
|
||||||
* @author Drak <drak@zikula.org>
|
* @author Drak <drak@zikula.org>
|
||||||
*/
|
*/
|
||||||
class AbstractProxyTest extends TestCase
|
class AbstractProxyTest extends TestCase
|
||||||
|
|
|
@ -21,7 +21,6 @@ use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||||
*
|
*
|
||||||
* @runTestsInSeparateProcesses
|
* @runTestsInSeparateProcesses
|
||||||
* @preserveGlobalState disabled
|
* @preserveGlobalState disabled
|
||||||
* @group legacy
|
|
||||||
*/
|
*/
|
||||||
class SessionHandlerProxyTest extends TestCase
|
class SessionHandlerProxyTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^5.5.9|>=7.0.8",
|
"php": "^5.5.9|>=7.0.8",
|
||||||
"symfony/polyfill-mbstring": "~1.1"
|
"symfony/polyfill-mbstring": "~1.1",
|
||||||
|
"symfony/polyfill-php70": "~1.6"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/expression-language": "~2.8|~3.0|~4.0"
|
"symfony/expression-language": "~2.8|~3.0|~4.0"
|
||||||
|
|
|
@ -97,12 +97,18 @@ class NativeSessionTokenStorage implements TokenStorageInterface
|
||||||
$this->startSession();
|
$this->startSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = isset($_SESSION[$this->namespace][$tokenId])
|
if (!isset($_SESSION[$this->namespace][$tokenId])) {
|
||||||
? (string) $_SESSION[$this->namespace][$tokenId]
|
return;
|
||||||
: null;
|
}
|
||||||
|
|
||||||
|
$token = (string) $_SESSION[$this->namespace][$tokenId];
|
||||||
|
|
||||||
unset($_SESSION[$this->namespace][$tokenId]);
|
unset($_SESSION[$this->namespace][$tokenId]);
|
||||||
|
|
||||||
|
if (!$_SESSION[$this->namespace]) {
|
||||||
|
unset($_SESSION[$this->namespace]);
|
||||||
|
}
|
||||||
|
|
||||||
return $token;
|
return $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue