Skip to content

Commit

Permalink
[Http-Foundation][Session]feat: create automatically session table wi…
Browse files Browse the repository at this point in the history
…th pdo connection
  • Loading branch information
alli83 committed Oct 31, 2022
1 parent 929d5f4 commit aad3487
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?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\ORM\Tools\Event\GenerateSchemaEventArgs;
use Doctrine\ORM\Tools\ToolEvents;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\SessionHandlerDecoratorInterface;

class PdoSessionHandlerSchemaSubscriber implements EventSubscriber
{
private $sessionHandler;

/**
* @param $sessionHandler
*/
public function __construct($sessionHandler = null)
{
$this->sessionHandler = $sessionHandler;
}

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

$sessionHandler = $this->sessionHandler;

if (!$sessionHandler instanceof PdoSessionHandler &&
!$sessionHandler instanceof SessionHandlerDecoratorInterface) {

return;
}

if ($sessionHandler instanceof SessionHandlerDecoratorInterface) {
$handlers = $sessionHandler->getHandlers();

foreach ($handlers as $handler) {
if ($handler instanceof PdoSessionHandler) {
$handler->configureSchema($event->getSchema(), $dbalConnection);
}
}

return;
}

$this->sessionHandler->configureSchema($event->getSchema(), $dbalConnection);
}

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

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

return $subscribedEvents;
}
}
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, $dbalConnection);

$subscriber = new PdoSessionHandlerSchemaSubscriber($pdoSessionHandler);
$subscriber->postGenerateSchema($event);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
*/
class MarshallingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
class MarshallingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface, SessionHandlerDecoratorInterface
{
private AbstractSessionHandler $handler;
private MarshallerInterface $marshaller;
Expand Down Expand Up @@ -73,4 +73,9 @@ public function updateTimestamp(string $sessionId, string $data): bool
{
return $this->handler->updateTimestamp($sessionId, $data);
}

public function getHandlers(): array
{
return [$this->handler];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* @author Ross Motley <ross.motley@amara.com>
* @author Oliver Radwell <oliver.radwell@amara.com>
*/
class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface, SessionHandlerDecoratorInterface
{
/**
* @var \SessionHandlerInterface&\SessionUpdateTimestampHandlerInterface
Expand Down Expand Up @@ -104,4 +104,9 @@ public function updateTimestamp(string $sessionId, string $sessionData): bool

return $result;
}

public function getHandlers(): array
{
return [$this->currentHandler, $this->writeOnlyHandler];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;

/**
* Session handler using a PDO connection to read and write data.
*
Expand Down Expand Up @@ -175,6 +180,79 @@ public function __construct(\PDO|string $pdoOrDsn = null, array $options = [])
$this->ttl = $options['ttl'] ?? null;
}

private function isInListTablesWithoutFilter(AbstractSchemaManager $schemaManager): bool
{
$tablesName = $schemaManager->listTableNames();

if (\in_array($this->table, $tablesName)) {
return true;
}

return false;
}

public function configureSchema(Schema $schema, Connection $connection): void
{
$configuration = $connection->getConfiguration();

$schemaManager = method_exists(Connection::class, 'createSchemaManager')
? $connection->createSchemaManager()
: $connection->getSchemaManager();

$configuration->setSchemaAssetsFilter(null);
$isTableFound = $this->isInListTablesWithoutFilter($schemaManager);

if ($isTableFound) {
return;
}

$this->addTableToSchema($schema);
}

private function addTableToSchema(Schema $schema)
{
$this->getConnection();

$table = $schema->createTable($this->table);
switch ($this->driver) {
case 'mysql':
$table->addColumn($this->idCol, Types::BINARY)->setLength(128)->setNotnull(true);
$table->addColumn($this->dataCol, Types::BLOB)->setNotnull(true);
$table->addColumn($this->lifetimeCol, Types::INTEGER)->setUnsigned(true)->setNotnull(true);
$table->addColumn($this->timeCol, Types::INTEGER)->setUnsigned(true)->setNotnull(true);
$table->addOption('collate', 'utf8mb4_bin');
$table->addOption('engine', 'InnoDB');
break;
case 'sqlite':
$table->addColumn($this->idCol, Types::TEXT)->setNotnull(true);
$table->addColumn($this->dataCol, Types::BLOB)->setNotnull(true);
$table->addColumn($this->lifetimeCol, Types::INTEGER)->setNotnull(true);
$table->addColumn($this->timeCol, Types::INTEGER)->setNotnull(true);
break;
case 'pgsql':
$table->addColumn($this->idCol, Types::STRING)->setLength(128)->setNotnull(true);
$table->addColumn($this->dataCol, Types::BINARY)->setNotnull(true);
$table->addColumn($this->lifetimeCol, Types::INTEGER)->setNotnull(true);
$table->addColumn($this->timeCol, Types::INTEGER)->setNotnull(true);
break;
case 'oci':
$table->addColumn($this->idCol, Types::STRING)->setLength(128)->setNotnull(true);
$table->addColumn($this->dataCol, Types::BLOB)->setNotnull(true);
$table->addColumn($this->lifetimeCol, Types::INTEGER)->setNotnull(true);
$table->addColumn($this->timeCol, Types::INTEGER)->setNotnull(true);
break;
case 'sqlsrv':
$table->addColumn($this->idCol, Types::TEXT)->setLength(128)->setNotnull(true);
$table->addColumn($this->dataCol, Types::BLOB)->setNotnull(true);
$table->addColumn($this->lifetimeCol, Types::INTEGER)->setUnsigned(true)->setNotnull(true);
$table->addColumn($this->timeCol, Types::INTEGER)->setUnsigned(true)->setNotnull(true);
break;
default:
throw new \DomainException(sprintf('Creating the session table is currently not implemented for PDO driver "%s".', $this->driver));
}
$table->setPrimaryKey([$this->idCol]);
}

/**
* Creates the table to store sessions which can be called once for setup.
*
Expand Down Expand Up @@ -441,8 +519,8 @@ private function buildDsnFromUrl(string $dsnOrUrl): string
return $dsn;
}
}
// If "unix_socket" is not in the query, we continue with the same process as pgsql
// no break
// If "unix_socket" is not in the query, we continue with the same process as pgsql
// no break
case 'pgsql':
$dsn ??= 'pgsql:';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?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\Component\HttpFoundation\Session\Storage\Handler;

interface SessionHandlerDecoratorInterface
{
/**
* @return \SessionHandlerInterface[]
*/
public function getHandlers(): array;
}

0 comments on commit aad3487

Please sign in to comment.