Skip to content

Commit 57eacea

Browse files
committed
merged branch drak/session_detect (PR #7571)
This PR was squashed before being merged into the master branch (closes #7571). Discussion ---------- [2.3] Handle PHP sessions started outside of Symfony | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | symfony/symfony-docs#2474 This PR brings a way to allow Symfony2 to manage a session started outside of Symfony in such a way that quite explicit. It also introduces more robust detection of previously started sessions under PHP 5.3 and supports real session status detection under PHP 5.4 Commits ------- df99902 [2.3] Handle PHP sessions started outside of Symfony
2 parents baf3ba6 + e209145 commit 57eacea

File tree

7 files changed

+457
-32
lines changed

7 files changed

+457
-32
lines changed

Session/Storage/NativeSessionStorage.php

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpFoundation\Session\Storage;
1313

1414
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
15+
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
1516
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
1617
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
1718
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
@@ -91,9 +92,9 @@ class NativeSessionStorage implements SessionStorageInterface
9192
* upload_progress.min-freq, "1"
9293
* url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
9394
*
94-
* @param array $options Session configuration options.
95-
* @param object $handler SessionHandlerInterface.
96-
* @param MetadataBag $metaBag MetadataBag.
95+
* @param array $options Session configuration options.
96+
* @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
97+
* @param MetadataBag $metaBag MetadataBag.
9798
*/
9899
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
99100
{
@@ -130,27 +131,28 @@ public function start()
130131
return true;
131132
}
132133

133-
// catch condition where session was started automatically by PHP
134-
if (!$this->started && !$this->closed && $this->saveHandler->isActive()
135-
&& $this->saveHandler->isSessionHandlerInterface()) {
136-
$this->loadSession();
134+
if (version_compare(phpversion(), '5.4.0', '>=') && \PHP_SESSION_ACTIVE === session_status()) {
135+
throw new \RuntimeException('Failed to start the session: already started by PHP.');
136+
}
137137

138-
return true;
138+
if (version_compare(phpversion(), '5.4.0', '<') && isset($_SESSION) && session_id()) {
139+
// not 100% fool-proof, but is the most reliable way to determine if a session is active in PHP 5.3
140+
throw new \RuntimeException('Failed to start the session: already started by PHP ($_SESSION is set).');
139141
}
140142

141143
if (ini_get('session.use_cookies') && headers_sent()) {
142144
throw new \RuntimeException('Failed to start the session because headers have already been sent.');
143145
}
144146

145-
// start the session
147+
// ok to try and start the session
146148
if (!session_start()) {
147149
throw new \RuntimeException('Failed to start the session');
148150
}
149151

150152
$this->loadSession();
151-
152153
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
153-
$this->saveHandler->setActive(false);
154+
// This condition matches only PHP 5.3 with internal save handlers
155+
$this->saveHandler->setActive(true);
154156
}
155157

156158
return true;
@@ -215,7 +217,8 @@ public function save()
215217
{
216218
session_write_close();
217219

218-
if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {
220+
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
221+
// This condition matches only PHP 5.3 with internal save handlers
219222
$this->saveHandler->setActive(false);
220223
}
221224

@@ -329,29 +332,43 @@ public function setOptions(array $options)
329332
}
330333

331334
/**
332-
* Registers save handler as a PHP session handler.
335+
* Registers session save handler as a PHP session handler.
333336
*
334337
* To use internal PHP session save handlers, override this method using ini_set with
335338
* session.save_handler and session.save_path e.g.
336339
*
337340
* ini_set('session.save_handler', 'files');
338341
* ini_set('session.save_path', /tmp');
339342
*
343+
* or pass in a NativeSessionHandler instance which configures session.save_handler in the
344+
* constructor, for a template see NativeFileSessionHandler or use handlers in
345+
* composer package drak/native-session
346+
*
340347
* @see http://php.net/session-set-save-handler
341348
* @see http://php.net/sessionhandlerinterface
342349
* @see http://php.net/sessionhandler
350+
* @see http://github.com/drak/NativeSession
351+
*
352+
* @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $saveHandler
343353
*
344-
* @param object $saveHandler Default null means NativeProxy.
354+
* @throws \InvalidArgumentException
345355
*/
346356
public function setSaveHandler($saveHandler = null)
347357
{
348-
// Wrap $saveHandler in proxy
358+
if (!$saveHandler instanceof AbstractProxy &&
359+
!$saveHandler instanceof NativeSessionHandler &&
360+
!$saveHandler instanceof \SessionHandlerInterface &&
361+
null !== $saveHandler) {
362+
throw new \InvalidArgumentException('Must be instance of AbstractProxy or NativeSessionHandler; implement \SessionHandlerInterface; or be null.');
363+
}
364+
365+
// Wrap $saveHandler in proxy and prevent double wrapping of proxy
349366
if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
350367
$saveHandler = new SessionHandlerProxy($saveHandler);
351368
} elseif (!$saveHandler instanceof AbstractProxy) {
352-
$saveHandler = new NativeProxy();
369+
$saveHandler = version_compare(phpversion(), '5.4.0', '>=') ?
370+
new SessionHandlerProxy(new \SessionHandler()) : new NativeProxy();
353371
}
354-
355372
$this->saveHandler = $saveHandler;
356373

357374
if ($this->saveHandler instanceof \SessionHandlerInterface) {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation\Session\Storage;
13+
14+
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
15+
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
16+
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
17+
18+
/**
19+
* Allows session to be started by PHP and managed by Symfony2
20+
*
21+
* @author Drak <drak@zikula.org>
22+
*/
23+
class PhpBridgeSessionStorage extends NativeSessionStorage
24+
{
25+
/**
26+
* Constructor.
27+
*
28+
* @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
29+
* @param MetadataBag $metaBag MetadataBag
30+
*/
31+
public function __construct($handler = null, MetadataBag $metaBag = null)
32+
{
33+
$this->setMetadataBag($metaBag);
34+
$this->setSaveHandler($handler);
35+
}
36+
37+
/**
38+
* {@inheritdoc}
39+
*/
40+
public function start()
41+
{
42+
if ($this->started && !$this->closed) {
43+
return true;
44+
}
45+
46+
$this->loadSession();
47+
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
48+
// This condition matches only PHP 5.3 + internal save handlers
49+
$this->saveHandler->setActive(true);
50+
}
51+
52+
return true;
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
public function clear()
59+
{
60+
// clear out the bags and nothing else that may be set
61+
// since the purpose of this driver is to share a handler
62+
foreach ($this->bags as $bag) {
63+
$bag->clear();
64+
}
65+
66+
// reconnect the bags to the session
67+
$this->loadSession();
68+
}
69+
}

Session/Storage/Proxy/AbstractProxy.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,31 @@ public function isWrapper()
7272
*/
7373
public function isActive()
7474
{
75+
if (version_compare(phpversion(), '5.4.0', '>=')) {
76+
return $this->active = \PHP_SESSION_ACTIVE === session_status();
77+
}
78+
7579
return $this->active;
7680
}
7781

7882
/**
7983
* Sets the active flag.
8084
*
85+
* Has no effect under PHP 5.4+ as status is detected
86+
* automatically in isActive()
87+
*
88+
* @internal
89+
*
8190
* @param Boolean $flag
91+
*
92+
* @throws \LogicException
8293
*/
8394
public function setActive($flag)
8495
{
96+
if (version_compare(phpversion(), '5.4.0', '>=')) {
97+
throw new \LogicException('This method is disabled in PHP 5.4.0+');
98+
}
99+
85100
$this->active = (bool) $flag;
86101
}
87102

0 commit comments

Comments
 (0)