Permalink
Browse files

* Added preventative measures against inadvertently flushing unwanted…

… changes.

* Added support to configure the Session object via a prototype service.
* Added a configurable option to not purge old sessions on every request.
  • Loading branch information...
magnusnordlander committed Jan 10, 2012
1 parent a215ec9 commit c6975a8a5bd48603e7dafc492411baecbcad13e3
@@ -0,0 +1,34 @@
+<?php
+
+namespace Ebutik\MongoSessionBundle\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+class SetPrototypeClassPass implements CompilerPassInterface
+{
+ /**
+ * @see Symfony\Component\DependencyInjection\Compiler.CompilerPassInterface::process()
+ */
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->hasDefinition('ebutik.mongosession.storage')) {
+ return;
+ }
+
+ $storage_def = $container->getDefinition('ebutik.mongosession.storage');
+ $args = $storage_def->getArguments();
+
+ $prototype_id = $args[4];
+
+ if (!$container->hasDefinition($prototype_id)) {
+ throw new \RuntimeException("MongoDB Session Prototype doesn't exist");
+ }
+
+ $prototype_def = $container->getDefinition($prototype_id);
+ $args[3] = $prototype_def->getClass();
+
+ $storage_def->setArguments($args);
+ }
+}
@@ -15,7 +15,9 @@ public function getConfigTreeBuilder()
$rootNode
->children()
->scalarNode('document_manager')->defaultValue('default')->end()
+ ->scalarNode('session_prototype_id')->defaultValue('ebutik.mongosession.session.prototype')->end()
->booleanNode('strict_request_checking')->defaultValue(false)->end()
+ ->scalarNode('purge_probability_divisor')->defaultValue(30)->end() // The probability of a purge. Use n where 1/n is the probability for a given request to purge old sessions. If this is 1, the sessions are purged in every request. If this is say 30, sessions are purged on average every thirtieth request.
->end()
;
@@ -28,7 +28,10 @@ public function load(array $configs, ContainerBuilder $container)
);
$storage_def = $container->getDefinition('ebutik.mongosession.storage');
+ $storage_def->addArgument('stdObject'); // This is changed in a compiler
+ $storage_def->addArgument($config['session_prototype_id']);
$storage_def->addArgument($config['strict_request_checking']);
+ $storage_def->addArgument($config['purge_probability_divisor']);
}
public function getAlias()
@@ -4,7 +4,15 @@
use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Ebutik\MongoSessionBundle\DependencyInjection\Compiler\SetPrototypeClassPass;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
class EbutikMongoSessionBundle extends Bundle
{
+ public function build(ContainerBuilder $container)
+ {
+ parent::build($container);
+ $container->addCompilerPass(new SetPrototypeClassPass());
+ }
}
@@ -6,14 +6,22 @@
<parameter key="ebutik.mongosession.storage.class">Ebutik\MongoSessionBundle\SessionStorage\MongoODMSessionStorage</parameter>
<parameter key="ebutik.mongosession.listener.class">Ebutik\MongoSessionBundle\EventListener\MongoSessionListener</parameter>
<parameter key="ebutik.mongosession.key_escaper.class">Ebutik\MongoSessionBundle\Escaper\Escaper</parameter>
+ <parameter key="ebutik.mongosession.session.class">Ebutik\MongoSessionBundle\Document\Session</parameter>
</parameters>
<services>
<service id="ebutik.mongosession.storage" class="%ebutik.mongosession.storage.class%">
<argument type="service" id="ebutik.mongosession.document_manager"/>
<argument type="service" id="ebutik.mongosession.key_escaper"/>
<argument>%session.storage.options%</argument>
+
+ <call method="setContainer">
+ <argument type="service" id="service_container" />
+ </call>
</service>
+
+ <service id="ebutik.mongosession.session.prototype" class="%ebutik.mongosession.session.class%" scope="prototype" />
+
<service id="ebutik.mongosession.listener" class="%ebutik.mongosession.listener.class%">
<argument type="service" id="ebutik.mongosession.storage" />
@@ -4,15 +4,17 @@
use Symfony\Component\HttpFoundation\SessionStorage\SessionStorageInterface;
-use Ebutik\MongoSessionBundle\Document\Session;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
use Ebutik\MongoSessionBundle\Escaper\EscaperInterface;
use Doctrine\ODM\MongoDB\DocumentManager;
/**
* @author Magnus Nordlander
**/
-class MongoODMSessionStorage implements SessionStorageInterface
+class MongoODMSessionStorage implements SessionStorageInterface, ContainerAwareInterface
{
/**
* @var DocumentManager
@@ -29,14 +31,38 @@ class MongoODMSessionStorage implements SessionStorageInterface
*/
protected $options;
- // NULL represents unset, false represents set, but with no session id
+ /**
+ * @var integer|boolean
+ *
+ * NULL represents unset, false represents set, but with no session id
+ */
protected $request_session_id;
+ /**
+ * @var string
+ */
+ protected $session_class;
+
+ /**
+ * @var string
+ */
+ protected $session_prototype_id;
+
/**
* @var boolean
*/
protected $strict_request_checking;
+ /**
+ * @var integer
+ */
+ protected $purge_probability_divisor;
+
+ /**
+ * @var ContainerInterface
+ */
+ protected $container;
+
/**
* @var Session|null
*/
@@ -45,22 +71,30 @@ class MongoODMSessionStorage implements SessionStorageInterface
/**
* @author Magnus Nordlander
**/
- public function __construct(DocumentManager $dm, EscaperInterface $key_escaper, array $options, $strict_request_checking = false)
+ public function __construct(DocumentManager $dm, EscaperInterface $key_escaper, array $options, $session_class, $session_prototype_id, $strict_request_checking = false, $purge_probability_divisor = 30)
{
$this->dm = $dm;
$this->key_escaper = $key_escaper;
$this->setOptions($options);
+ $this->session_class = $session_class;
+ $this->session_prototype_id = $session_prototype_id;
$this->strict_request_checking = (bool)$strict_request_checking;
+ $this->purge_probability_divisor = $purge_probability_divisor;
}
public function setRequestSessionId($request_session_id = false)
{
$this->request_session_id = $request_session_id;
}
+ public function setContainer(ContainerInterface $container = null)
+ {
+ $this->container = $container;
+ }
+
protected function setOptions(array $options)
{
$cookieDefaults = session_get_cookie_params();
@@ -123,16 +157,20 @@ public function start()
{
if (!$this->isStarted())
{
- $this->dm->getRepository('Ebutik\MongoSessionBundle\Document\Session')->purgeBefore(new \DateTime("-".$this->options['lifetime'].' second'));
+ if (mt_rand(0, $this->purge_probability_divisor-1) == 0)
+ {
+ $this->dm->getRepository($this->session_class)->purgeBefore(new \DateTime("-".$this->options['lifetime'].' second'));
+ }
if ($this->getRequestSessionId())
{
- $this->session = $this->dm->find('Ebutik\MongoSessionBundle\Document\Session', $this->getRequestSessionId());
+ $this->session = $this->dm->find($this->session_class, $this->getRequestSessionId());
+ $this->dm->detach($this->session);
}
if (!$this->session)
{
- $this->session = new Session;
+ $this->session = $this->container->get($this->session_prototype_id);
}
}
}
@@ -280,8 +318,11 @@ public function flush()
{
if ($this->session)
{
- $this->dm->persist($this->session);
+ $this->dm->clear();
+ $merged = $this->dm->merge($this->session);
+ $this->dm->persist($merged);
$this->dm->flush();
+ $this->dm->detach($merged);
}
}
}

0 comments on commit c6975a8

Please sign in to comment.