Issue #2372389 by znerol, almaudoh: Expose session handler in container
parent
08f02073d5
commit
a32a88cbc2
|
@ -1107,11 +1107,28 @@ services:
|
|||
session.attribute_bag:
|
||||
class: Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag
|
||||
public: false
|
||||
session_manager:
|
||||
class: Drupal\Core\Session\SessionManager
|
||||
arguments: ['@request_stack', '@database', '@session_manager.metadata_bag', '@session_configuration']
|
||||
session_handler:
|
||||
alias: session_handler.storage
|
||||
session_handler.storage:
|
||||
class: Drupal\Core\Session\SessionHandler
|
||||
arguments: ['@request_stack', '@database']
|
||||
tags:
|
||||
- { name: backend_overridable }
|
||||
session_handler.write_check:
|
||||
class: Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler
|
||||
tags:
|
||||
- { name: session_handler_proxy, priority: 100 }
|
||||
session_handler.write_safe:
|
||||
class: Drupal\Core\Session\WriteSafeSessionHandler
|
||||
tags:
|
||||
- { name: session_handler_proxy, priority: 150 }
|
||||
session_manager:
|
||||
class: Drupal\Core\Session\SessionManager
|
||||
arguments: ['@request_stack', '@database', '@session_manager.metadata_bag', '@session_configuration', '@session_handler']
|
||||
tags:
|
||||
- { name: backend_overridable }
|
||||
calls:
|
||||
- [setWriteSafeHandler, ['@session_handler.write_safe']]
|
||||
session_manager.metadata_bag:
|
||||
class: Drupal\Core\Session\MetadataBag
|
||||
arguments: ['@settings']
|
||||
|
|
|
@ -14,6 +14,7 @@ use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteEnhancers;
|
|||
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteFilters;
|
||||
use Drupal\Core\DependencyInjection\Compiler\DependencySerializationTraitPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\StackedKernelPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\StackedSessionHandlerPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\RegisterStreamWrappersPass;
|
||||
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
|
@ -63,6 +64,8 @@ class CoreServiceProvider implements ServiceProviderInterface {
|
|||
|
||||
$container->addCompilerPass(new StackedKernelPass());
|
||||
|
||||
$container->addCompilerPass(new StackedSessionHandlerPass());
|
||||
|
||||
$container->addCompilerPass(new MainContentRenderersPass());
|
||||
|
||||
// Collect tagged handler services as method calls on consumer services.
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\DependencyInjection\Compiler\StackedSessionHandlerPass.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Provides a compiler pass for stacked session save handlers.
|
||||
*/
|
||||
class StackedSessionHandlerPass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
|
||||
if ($container->hasDefinition('session_handler')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$session_handler_proxies = [];
|
||||
$priorities = [];
|
||||
|
||||
foreach ($container->findTaggedServiceIds('session_handler_proxy') as $id => $attributes) {
|
||||
$priorities[$id] = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
|
||||
$session_handler_proxies[$id] = $container->getDefinition($id);
|
||||
}
|
||||
|
||||
array_multisort($priorities, SORT_ASC, $session_handler_proxies);
|
||||
|
||||
$decorated_id = 'session_handler.storage';
|
||||
foreach ($session_handler_proxies as $id => $decorator) {
|
||||
// Prepend the inner session handler as first constructor argument.
|
||||
$arguments = $decorator->getArguments();
|
||||
array_unshift($arguments, new Reference($decorated_id));
|
||||
$decorator->setArguments($arguments);
|
||||
|
||||
$decorated_id = $id;
|
||||
}
|
||||
|
||||
$container->setAlias('session_handler', $decorated_id);
|
||||
}
|
||||
|
||||
}
|
|
@ -10,7 +10,6 @@ namespace Drupal\Core\Session;
|
|||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||
|
||||
/**
|
||||
|
@ -63,10 +62,8 @@ class SessionManager extends NativeSessionStorage implements SessionManagerInter
|
|||
/**
|
||||
* The write safe session handler.
|
||||
*
|
||||
* @todo: The write safe session handler should be exposed in the
|
||||
* container and this reference should be removed once all database queries
|
||||
* @todo: This reference should be removed once all database queries
|
||||
* are removed from the session manager class.
|
||||
* @see https://www.drupal.org/node/2372389
|
||||
*
|
||||
* @var \Drupal\Core\Session\WriteSafeSessionHandlerInterface
|
||||
*/
|
||||
|
@ -83,20 +80,17 @@ class SessionManager extends NativeSessionStorage implements SessionManagerInter
|
|||
* The session metadata bag.
|
||||
* @param \Drupal\Core\Session\SessionConfigurationInterface $session_configuration
|
||||
* The session configuration interface.
|
||||
* @param \Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy|Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler|\SessionHandlerInterface|NULL $handler
|
||||
* The object to register as a PHP session handler.
|
||||
* @see \Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage::setSaveHandler()
|
||||
*/
|
||||
public function __construct(RequestStack $request_stack, Connection $connection, MetadataBag $metadata_bag, SessionConfigurationInterface $session_configuration) {
|
||||
public function __construct(RequestStack $request_stack, Connection $connection, MetadataBag $metadata_bag, SessionConfigurationInterface $session_configuration, $handler = NULL) {
|
||||
$options = array();
|
||||
$this->sessionConfiguration = $session_configuration;
|
||||
$this->requestStack = $request_stack;
|
||||
$this->connection = $connection;
|
||||
|
||||
// Register the default session handler.
|
||||
// @todo Extract session storage from session handler into a service.
|
||||
$save_handler = new SessionHandler($this->requestStack, $this->connection);
|
||||
$write_check_handler = new WriteCheckSessionHandler($save_handler);
|
||||
$this->writeSafeHandler = new WriteSafeSessionHandler($write_check_handler);
|
||||
|
||||
parent::__construct($options, $this->writeSafeHandler, $metadata_bag);
|
||||
parent::__construct($options, $handler, $metadata_bag);
|
||||
|
||||
// @todo When not using the Symfony Session object, the list of bags in the
|
||||
// NativeSessionStorage will remain uninitialized. This will lead to
|
||||
|
@ -292,6 +286,13 @@ class SessionManager extends NativeSessionStorage implements SessionManagerInter
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setWriteSafeHandler(WriteSafeSessionHandlerInterface $handler) {
|
||||
$this->writeSafeHandler = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current PHP process runs on CLI.
|
||||
*
|
||||
|
|
|
@ -27,6 +27,9 @@ interface SessionManagerInterface extends SessionStorageInterface {
|
|||
*
|
||||
* @return bool
|
||||
* FALSE if writing session data has been disabled. TRUE otherwise.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x, will be removed before Drupal 8.0.0
|
||||
* Use \Drupal\Core\Session\WriteSafeSessionHandler::isSessionWritable()
|
||||
*/
|
||||
public function isEnabled();
|
||||
|
||||
|
@ -40,6 +43,9 @@ interface SessionManagerInterface extends SessionStorageInterface {
|
|||
* @see https://drupal.org/node/218104
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x, will be removed before Drupal 8.0.0
|
||||
* Use \Drupal\Core\Session\WriteSafeSessionHandler::setSessionWritable(FALSE)
|
||||
*/
|
||||
public function disable();
|
||||
|
||||
|
@ -47,7 +53,20 @@ interface SessionManagerInterface extends SessionStorageInterface {
|
|||
* Re-enables saving of session data.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x, will be removed before Drupal 8.0.0
|
||||
* Use \Drupal\Core\Session\WriteSafeSessionHandler::setSessionWritable(True)
|
||||
*/
|
||||
public function enable();
|
||||
|
||||
/**
|
||||
* Sets the write safe session handler.
|
||||
*
|
||||
* @todo: This should be removed once all database queries are removed from
|
||||
* the session manager class.
|
||||
*
|
||||
* @var \Drupal\Core\Session\WriteSafeSessionHandlerInterface
|
||||
*/
|
||||
public function setWriteSafeHandler(WriteSafeSessionHandlerInterface $handler);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Session\StackSessionHandlerIntegrationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Session;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the stacked session handler functionality.
|
||||
*
|
||||
* @group Session
|
||||
*/
|
||||
class StackSessionHandlerIntegrationTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('session_test');
|
||||
|
||||
/**
|
||||
* Tests a request.
|
||||
*/
|
||||
public function testRequest() {
|
||||
$actual_trace = $this->drupalGetAjax('session-test/trace-handler');
|
||||
$expect_trace = [
|
||||
['BEGIN', 'test_argument', 'open'],
|
||||
['BEGIN', NULL, 'open'],
|
||||
['END', NULL, 'open'],
|
||||
['END', 'test_argument', 'open'],
|
||||
['BEGIN', 'test_argument', 'read', $this->sessionId],
|
||||
['BEGIN', NULL, 'read', $this->sessionId],
|
||||
['END', NULL, 'read', $this->sessionId],
|
||||
['END', 'test_argument', 'read', $this->sessionId],
|
||||
['BEGIN', 'test_argument', 'write', $this->sessionId],
|
||||
['BEGIN', NULL, 'write', $this->sessionId],
|
||||
['END', NULL, 'write', $this->sessionId],
|
||||
['END', 'test_argument', 'write', $this->sessionId],
|
||||
['BEGIN', 'test_argument', 'close'],
|
||||
['BEGIN', NULL, 'close'],
|
||||
['END', NULL, 'close'],
|
||||
['END', 'test_argument', 'close'],
|
||||
];
|
||||
$this->assertEqual($expect_trace, $actual_trace);
|
||||
}
|
||||
|
||||
}
|
|
@ -75,3 +75,11 @@ session_test.form:
|
|||
_title: 'Test form'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
||||
|
||||
session_test.trace_handler:
|
||||
path: '/session-test/trace-handler'
|
||||
defaults:
|
||||
_title: 'Returns the trace recorded by test proxy session handlers as JSON'
|
||||
_controller: '\Drupal\session_test\Controller\SessionTestController::traceHandler'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
||||
|
|
|
@ -3,3 +3,14 @@ services:
|
|||
class: Drupal\session_test\EventSubscriber\SessionTestSubscriber
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
session_test.session_handler.test_proxy:
|
||||
class: Drupal\session_test\Session\TestSessionHandlerProxy
|
||||
tags:
|
||||
- { name: session_handler_proxy }
|
||||
session_test.session_handler.test_proxy2:
|
||||
class: Drupal\session_test\Session\TestSessionHandlerProxy
|
||||
arguments: ['test_argument']
|
||||
tags:
|
||||
- { name: session_handler_proxy, priority: 20 }
|
||||
session_test.session_handler_proxy_trace:
|
||||
class: ArrayObject
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\session_test\Controller;
|
|||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
|
@ -125,4 +126,27 @@ class SessionTestController extends ControllerBase {
|
|||
return ['#markup' => $this->t('User is logged in.')];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the trace recorded by test proxy session handlers as JSON.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* The response.
|
||||
*/
|
||||
public function traceHandler() {
|
||||
// Start a session if necessary, set a value and then save and close it.
|
||||
\Drupal::service('session_manager')->start();
|
||||
if (empty($_SESSION['trace-handler'])) {
|
||||
$_SESSION['trace-handler'] = 1;
|
||||
}
|
||||
else {
|
||||
$_SESSION['trace-handler']++;
|
||||
}
|
||||
\Drupal::service('session_manager')->save();
|
||||
|
||||
// Collect traces and return them in JSON format.
|
||||
$trace = \Drupal::service('session_test.session_handler_proxy_trace')->getArrayCopy();
|
||||
|
||||
return new JsonResponse($trace);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\session_test\Session\TestSessionHandlerProxy.
|
||||
*/
|
||||
|
||||
namespace Drupal\session_test\Session;
|
||||
|
||||
/**
|
||||
* Provides a test session handler proxy.
|
||||
*/
|
||||
class TestSessionHandlerProxy implements \SessionHandlerInterface {
|
||||
|
||||
/**
|
||||
* The decorated session handler.
|
||||
*
|
||||
* @var \SessionHandlerInterface
|
||||
*/
|
||||
protected $sessionHandler;
|
||||
|
||||
/**
|
||||
* An optional argument.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $optionalArgument;
|
||||
|
||||
/**
|
||||
* Constructs a new TestSessionHandlerProxy object.
|
||||
*
|
||||
* @param \SessionHandlerInterface $session_handler
|
||||
* The decorated session handler.
|
||||
* @param mixed $optional_argument
|
||||
* (optional) An optional argument.
|
||||
*/
|
||||
public function __construct(\SessionHandlerInterface $session_handler, $optional_argument = NULL) {
|
||||
$this->sessionHandler = $session_handler;
|
||||
$this->optionalArgument = $optional_argument;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($save_path, $name) {
|
||||
$trace = \Drupal::service('session_test.session_handler_proxy_trace');
|
||||
$trace[] = ['BEGIN', $this->optionalArgument, __FUNCTION__];
|
||||
$result = $this->sessionHandler->open($save_path, $name);
|
||||
$trace[] = ['END', $this->optionalArgument, __FUNCTION__];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close() {
|
||||
$trace = \Drupal::service('session_test.session_handler_proxy_trace');
|
||||
$trace[] = ['BEGIN', $this->optionalArgument, __FUNCTION__];
|
||||
$result = $this->sessionHandler->close();
|
||||
$trace[] = ['END', $this->optionalArgument, __FUNCTION__];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($session_id) {
|
||||
$trace = \Drupal::service('session_test.session_handler_proxy_trace');
|
||||
$trace[] = ['BEGIN', $this->optionalArgument, __FUNCTION__, $session_id];
|
||||
$result = $this->sessionHandler->read($session_id);
|
||||
$trace[] = ['END', $this->optionalArgument, __FUNCTION__, $session_id];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($session_id, $session_data) {
|
||||
$trace = \Drupal::service('session_test.session_handler_proxy_trace');
|
||||
$trace[] = ['BEGIN', $this->optionalArgument, __FUNCTION__, $session_id];
|
||||
$result = $this->sessionHandler->write($session_id, $session_data);
|
||||
$trace[] = ['END', $this->optionalArgument, __FUNCTION__, $session_id];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($session_id) {
|
||||
return $this->sessionHandler->destroy($session_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gc($max_lifetime) {
|
||||
return $this->sessionHandler->gc($max_lifetime);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue