Skip to content

Commit

Permalink
[HttpFoundation] Create migration for session table when pdo handler …
Browse files Browse the repository at this point in the history
…is used
  • Loading branch information
alli83 authored and nicolas-grekas committed Dec 16, 2022
1 parent 233b9eb commit aebdc58
Show file tree
Hide file tree
Showing 17 changed files with 265 additions and 70 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Doctrine\SchemaListener;

use Doctrine\Common\EventSubscriber;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
use Doctrine\ORM\Tools\ToolEvents;

abstract class AbstractSchemaSubscriber implements EventSubscriber
{
abstract public function postGenerateSchema(GenerateSchemaEventArgs $event): void;

public function getSubscribedEvents(): array
{
if (!class_exists(ToolEvents::class)) {
return [];
}

return [
ToolEvents::postGenerateSchema,
];
}

protected function getIsSameDatabaseChecker(Connection $connection): \Closure
{
return static function (\Closure $exec) use ($connection): bool {
$checkTable = 'schema_subscriber_check_'.bin2hex(random_bytes(7));
$connection->executeStatement(sprintf('CREATE TABLE %s (id INTEGER NOT NULL)', $checkTable));

try {
$exec(sprintf('DROP TABLE %s', $checkTable));
} catch (\Exception) {
// ignore
}

try {
$connection->executeStatement(sprintf('DROP TABLE %s', $checkTable));

return false;
} catch (TableNotFoundException) {
return true;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@

namespace Symfony\Bridge\Doctrine\SchemaListener;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
use Doctrine\ORM\Tools\ToolEvents;
use Symfony\Component\Cache\Adapter\DoctrineDbalAdapter;

/**
Expand All @@ -22,7 +20,7 @@
*
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
final class DoctrineDbalCacheAdapterSchemaSubscriber implements EventSubscriber
final class DoctrineDbalCacheAdapterSchemaSubscriber extends AbstractSchemaSubscriber
{
private $dbalAdapters;

Expand All @@ -36,20 +34,10 @@ public function __construct(iterable $dbalAdapters)

public function postGenerateSchema(GenerateSchemaEventArgs $event): void
{
$dbalConnection = $event->getEntityManager()->getConnection();
foreach ($this->dbalAdapters as $dbalAdapter) {
$dbalAdapter->configureSchema($event->getSchema(), $dbalConnection);
}
}
$connection = $event->getEntityManager()->getConnection();

public function getSubscribedEvents(): array
{
if (!class_exists(ToolEvents::class)) {
return [];
foreach ($this->dbalAdapters as $dbalAdapter) {
$dbalAdapter->configureSchema($event->getSchema(), $connection, $this->getIsSameDatabaseChecker($connection));
}

return [
ToolEvents::postGenerateSchema,
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@

namespace Symfony\Bridge\Doctrine\SchemaListener;

use Doctrine\Common\EventSubscriber;
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
use Doctrine\DBAL\Events;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
use Doctrine\ORM\Tools\ToolEvents;
use Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineTransport;
use Symfony\Component\Messenger\Transport\TransportInterface;

Expand All @@ -24,7 +22,7 @@
*
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
final class MessengerTransportDoctrineSchemaSubscriber implements EventSubscriber
final class MessengerTransportDoctrineSchemaSubscriber extends AbstractSchemaSubscriber
{
private const PROCESSING_TABLE_FLAG = self::class.':processing';

Expand All @@ -40,13 +38,14 @@ public function __construct(iterable $transports)

public function postGenerateSchema(GenerateSchemaEventArgs $event): void
{
$dbalConnection = $event->getEntityManager()->getConnection();
$connection = $event->getEntityManager()->getConnection();

foreach ($this->transports as $transport) {
if (!$transport instanceof DoctrineTransport) {
continue;
}

$transport->configureSchema($event->getSchema(), $dbalConnection);
$transport->configureSchema($event->getSchema(), $connection, $this->getIsSameDatabaseChecker($connection));
}
}

Expand Down Expand Up @@ -89,11 +88,7 @@ public function onSchemaCreateTable(SchemaCreateTableEventArgs $event): void

public function getSubscribedEvents(): array
{
$subscribedEvents = [];

if (class_exists(ToolEvents::class)) {
$subscribedEvents[] = ToolEvents::postGenerateSchema;
}
$subscribedEvents = parent::getSubscribedEvents();

if (class_exists(Events::class)) {
$subscribedEvents[] = Events::onSchemaCreateTable;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Doctrine\SchemaListener;

use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;

final class PdoSessionHandlerSchemaSubscriber extends AbstractSchemaSubscriber
{
private iterable $pdoSessionHandlers;

/**
* @param iterable<mixed, PdoSessionHandler> $pdoSessionHandlers
*/
public function __construct(iterable $pdoSessionHandlers)
{
$this->pdoSessionHandlers = $pdoSessionHandlers;
}

public function postGenerateSchema(GenerateSchemaEventArgs $event): void
{
$connection = $event->getEntityManager()->getConnection();

foreach ($this->pdoSessionHandlers as $pdoSessionHandler) {
$pdoSessionHandler->configureSchema($event->getSchema(), $this->getIsSameDatabaseChecker($connection));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@

namespace Symfony\Bridge\Doctrine\SchemaListener;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
use Doctrine\ORM\Tools\ToolEvents;
use Symfony\Bridge\Doctrine\Security\RememberMe\DoctrineTokenProvider;
use Symfony\Component\Security\Http\RememberMe\PersistentRememberMeHandler;
use Symfony\Component\Security\Http\RememberMe\RememberMeHandlerInterface;
Expand All @@ -23,7 +21,7 @@
*
* @author Wouter de Jong <wouter@wouterj.nl>
*/
final class RememberMeTokenProviderDoctrineSchemaSubscriber implements EventSubscriber
final class RememberMeTokenProviderDoctrineSchemaSubscriber extends AbstractSchemaSubscriber
{
private iterable $rememberMeHandlers;

Expand All @@ -37,26 +35,15 @@ public function __construct(iterable $rememberMeHandlers)

public function postGenerateSchema(GenerateSchemaEventArgs $event): void
{
$dbalConnection = $event->getEntityManager()->getConnection();
$connection = $event->getEntityManager()->getConnection();

foreach ($this->rememberMeHandlers as $rememberMeHandler) {
if (
$rememberMeHandler instanceof PersistentRememberMeHandler
&& ($tokenProvider = $rememberMeHandler->getTokenProvider()) instanceof DoctrineTokenProvider
) {
$tokenProvider->configureSchema($event->getSchema(), $dbalConnection);
$tokenProvider->configureSchema($event->getSchema(), $connection, $this->getIsSameDatabaseChecker($connection));
}
}
}

public function getSubscribedEvents(): array
{
if (!class_exists(ToolEvents::class)) {
return [];
}

return [
ToolEvents::postGenerateSchema,
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,15 +188,18 @@ public function updateExistingToken(PersistentTokenInterface $token, #[\Sensitiv

/**
* Adds the Table to the Schema if "remember me" uses this Connection.
*
* @param \Closure $isSameDatabase
*/
public function configureSchema(Schema $schema, Connection $forConnection): void
public function configureSchema(Schema $schema, Connection $forConnection/* , \Closure $isSameDatabase */): void
{
// only update the schema for this connection
if ($forConnection !== $this->conn) {
if ($schema->hasTable('rememberme_token')) {
return;
}

if ($schema->hasTable('rememberme_token')) {
$isSameDatabase = 2 < \func_num_args() ? func_get_arg(2) : static fn () => false;

if ($forConnection !== $this->conn && !$isSameDatabase($this->conn->executeStatement(...))) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Doctrine\Tests\SchemaListener;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Doctrine\SchemaListener\PdoSessionHandlerSchemaSubscriber;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;

class PdoSessionHandlerSchemaSubscriberTest extends TestCase
{
public function testPostGenerateSchemaPdo()
{
$schema = new Schema();
$dbalConnection = $this->createMock(Connection::class);
$entityManager = $this->createMock(EntityManagerInterface::class);
$entityManager->expects($this->once())
->method('getConnection')
->willReturn($dbalConnection);
$event = new GenerateSchemaEventArgs($entityManager, $schema);

$pdoSessionHandler = $this->createMock(PdoSessionHandler::class);
$pdoSessionHandler->expects($this->once())
->method('configureSchema')
->with($schema, fn() => true);

$subscriber = new PdoSessionHandlerSchemaSubscriber([$pdoSessionHandler]);
$subscriber->postGenerateSchema($event);
}
}
1 change: 1 addition & 0 deletions src/Symfony/Bridge/Doctrine/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"symfony/cache": "<5.4",
"symfony/dependency-injection": "<6.2",
"symfony/form": "<5.4",
"symfony/http-foundation": "<6.3",
"symfony/http-kernel": "<6.2",
"symfony/messenger": "<5.4",
"symfony/property-info": "<5.4",
Expand Down
12 changes: 8 additions & 4 deletions src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,18 @@ public function createTable(): void
}
}

public function configureSchema(Schema $schema, Connection $forConnection): void
/**
* @param \Closure $isSameDatabase
*/
public function configureSchema(Schema $schema, Connection $forConnection/* , \Closure $isSameDatabase */): void
{
// only update the schema for this connection
if ($forConnection !== $this->conn) {
if ($schema->hasTable($this->table)) {
return;
}

if ($schema->hasTable($this->table)) {
$isSameDatabase = 2 < \func_num_args() ? func_get_arg(2) : static fn () => false;

if ($forConnection !== $this->conn && !$isSameDatabase($this->conn->executeStatement(...))) {
return;
}

Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Component/HttpFoundation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

6.3
---

* Create migration for session table when pdo handler is used

6.2
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,8 @@
*/
class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
{
/**
* @var \SessionHandlerInterface&\SessionUpdateTimestampHandlerInterface
*/
private \SessionHandlerInterface $currentHandler;

/**
* @var \SessionHandlerInterface&\SessionUpdateTimestampHandlerInterface
*/
private \SessionHandlerInterface $writeOnlyHandler;
private \SessionHandlerInterface&\SessionUpdateTimestampHandlerInterface $currentHandler;
private \SessionHandlerInterface&\SessionUpdateTimestampHandlerInterface $writeOnlyHandler;

public function __construct(\SessionHandlerInterface $currentHandler, \SessionHandlerInterface $writeOnlyHandler)
{
Expand Down

0 comments on commit aebdc58

Please sign in to comment.