Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f5c7306

Browse files
committed
PHPORM-310 Create dedicated session handler
1 parent c3bab3c commit f5c7306

File tree

3 files changed

+147
-12
lines changed

3 files changed

+147
-12
lines changed

‎src/MongoDBServiceProvider.php‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
use MongoDB\Laravel\Eloquent\Model;
2424
use MongoDB\Laravel\Queue\MongoConnector;
2525
use MongoDB\Laravel\Scout\ScoutEngine;
26+
use MongoDB\Laravel\Session\MongoDbSessionHandler;
2627
use RuntimeException;
27-
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
2828

2929
use function assert;
3030
use function class_exists;
@@ -67,12 +67,10 @@ public function register()
6767
assert($connection instanceof Connection, new InvalidArgumentException(sprintf('The database connection "%s" used for the session does not use the "mongodb" driver.', $connectionName)));
6868

6969
return new MongoDbSessionHandler(
70-
$connection->getClient(),
71-
$app->config->get('session.options', []) + [
72-
'database' => $connection->getDatabaseName(),
73-
'collection' => $app->config->get('session.table') ?: 'sessions',
74-
'ttl' => $app->config->get('session.lifetime'),
75-
],
70+
$connection,
71+
$app->config->get('session.table', 'sessions'),
72+
$app->config->get('session.lifetime'),
73+
$app,
7674
);
7775
});
7876
});
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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 MongoDB\Laravel\Session;
13+
14+
use Illuminate\Session\DatabaseSessionHandler;
15+
use MongoDB\BSON\Binary;
16+
use MongoDB\BSON\UTCDateTime;
17+
use MongoDB\Collection;
18+
use TypeError;
19+
20+
use function assert;
21+
use function get_debug_type;
22+
use function is_string;
23+
use function sprintf;
24+
use function tap;
25+
use function time;
26+
27+
/**
28+
* Session handler using the MongoDB driver extension.
29+
*/
30+
final class MongoDbSessionHandler extends DatabaseSessionHandler
31+
{
32+
private Collection $collection;
33+
34+
public function close(): bool
35+
{
36+
return true;
37+
}
38+
39+
public function gc($lifetime): int
40+
{
41+
$result = $this->getCollection()->deleteMany(['last_activity' => ['$lt' => $this->getUTCDateTime(-$lifetime)]]);
42+
43+
return $result->getDeletedCount() ?? 0;
44+
}
45+
46+
public function destroy($sessionId): bool
47+
{
48+
$this->getCollection()->deleteOne(['_id' => (string) $sessionId]);
49+
50+
return true;
51+
}
52+
53+
public function read($sessionId): string|false
54+
{
55+
$result = $this->getCollection()->findOne(
56+
['_id' => (string) $sessionId, 'expires_at' => ['$gte' => $this->getUTCDateTime()]],
57+
[
58+
'projection' => ['_id' => false, 'payload' => true],
59+
'typeMap' => ['root' => 'bson'],
60+
],
61+
);
62+
63+
return $result ? (string) $result->payload : false;
64+
}
65+
66+
public function write($sessionId, $data): bool
67+
{
68+
$payload = $this->getDefaultPayload($data);
69+
70+
$this->getCollection()->replaceOne(
71+
['_id' => (string) $sessionId],
72+
$payload,
73+
['upsert' => true],
74+
);
75+
76+
return true;
77+
}
78+
79+
/** Creates a TTL index that automatically deletes expired objects. */
80+
public function createTTLIndex(): void
81+
{
82+
$this->collection->createIndex(
83+
// UTCDateTime field that holds the expiration date
84+
['expires_at' => 1],
85+
// Delay to remove items after expiration
86+
['expireAfterSeconds' => 0],
87+
);
88+
}
89+
90+
protected function getDefaultPayload($data): array
91+
{
92+
$payload = [
93+
'payload' => new Binary($data),
94+
'last_activity' => $this->getUTCDateTime(),
95+
'expires_at' => $this->getUTCDateTime($this->minutes * 60),
96+
];
97+
98+
if (! $this->container) {
99+
return $payload;
100+
}
101+
102+
return tap($payload, function (&$payload) {
103+
$this->addUserInformation($payload)
104+
->addRequestInformation($payload);
105+
});
106+
}
107+
108+
private function getCollection(): Collection
109+
{
110+
return $this->collection ??= $this->connection->getCollection($this->table);
111+
}
112+
113+
private function getUTCDateTime(int $additionalSeconds = 0): UTCDateTime
114+
{
115+
return new UTCDateTime((time() + $additionalSeconds * 60) * 1000);
116+
}
117+
}

‎tests/SessionTest.php‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
use Illuminate\Session\DatabaseSessionHandler;
66
use Illuminate\Session\SessionManager;
77
use Illuminate\Support\Facades\DB;
8-
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
8+
use MongoDB\Laravel\Session\MongoDbSessionHandler;
9+
use PHPUnit\Framework\Attributes\TestWith;
10+
use SessionHandlerInterface;
911

1012
class SessionTest extends TestCase
1113
{
@@ -16,21 +18,31 @@ protected function tearDown(): void
1618
parent::tearDown();
1719
}
1820

19-
public function testDatabaseSessionHandlerCompatibility()
21+
/** @param class-string<SessionHandlerInterface> $class */
22+
#[TestWith([DatabaseSessionHandler::class])]
23+
#[TestWith([MongoDbSessionHandler::class])]
24+
public function testSessionHandlerFunctionality(string $class)
2025
{
21-
$sessionId = '123';
22-
23-
$handler = new DatabaseSessionHandler(
26+
$handler = new $class(
2427
$this->app['db']->connection('mongodb'),
2528
'sessions',
2629
10,
2730
);
2831

32+
$sessionId = '123';
33+
2934
$handler->write($sessionId, 'foo');
3035
$this->assertEquals('foo', $handler->read($sessionId));
3136

3237
$handler->write($sessionId, 'bar');
3338
$this->assertEquals('bar', $handler->read($sessionId));
39+
40+
$handler->destroy($sessionId);
41+
$this->assertEmpty($handler->read($sessionId));
42+
43+
$handler->write($sessionId, 'bar');
44+
$handler->gc(-1);
45+
$this->assertEmpty($handler->read($sessionId));
3446
}
3547

3648
public function testDatabaseSessionHandlerRegistration()
@@ -70,5 +82,13 @@ private function assertSessionCanStoreInMongoDB(SessionManager $session): void
7082

7183
self::assertIsObject($data);
7284
self::assertSame($session->getId(), $data->_id);
85+
86+
$session->remove('foo');
87+
$data = DB::connection('mongodb')
88+
->getCollection('sessions')
89+
->findOne(['_id' => $session->getId()]);
90+
91+
self::assertIsObject($data);
92+
self::assertSame($session->getId(), $data->_id);
7393
}
7494
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /