Commit a32a88cb authored by alexpott's avatar alexpott
Browse files

Issue #2372389 by znerol, almaudoh: Expose session handler in container

parent 08f02073
......@@ -1107,11 +1107,28 @@ services:
session.attribute_bag:
class: Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag
public: false
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']
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\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 @@ public function register(ContainerBuilder $container) {
$container->addCompilerPass(new StackedKernelPass());
$container->addCompilerPass(new StackedSessionHandlerPass());
$container->addCompilerPass(new MainContentRenderersPass());
// Collect tagged handler services as method calls on consumer services.
......
<?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 @@
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 @@ public function enable() {
return $this;
}
/**
* {@inheritdoc}
*/
public function setWriteSafeHandler(WriteSafeSessionHandlerInterface $handler) {
$this->writeSafeHandler = $handler;
}
/**
* Returns whether the current PHP process runs on CLI.
*
......
......@@ -27,6 +27,9 @@ public function delete($uid);
*
* @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 @@ public function isEnabled();
* @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 @@ public function disable();
* 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);
}
<?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 @@
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 @@ public function isLoggedIn() {
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);
}
}
<?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);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment