Session: Added Symfony PDO backend

This commit is contained in:
Igor Scheller 2018-09-15 17:24:59 +02:00
parent edeab5e75f
commit 104e4f4c43
9 changed files with 197 additions and 10 deletions

View File

@ -137,4 +137,13 @@ return [
'3XL' => '3XL', '3XL' => '3XL',
'4XL' => '4XL' '4XL' => '4XL'
], ],
// Session config
'session' => [
// Supported: pdo or native
'driver' => env('SESSION_DRIVER', 'pdo'),
// Cookie name
'name' => 'session',
],
]; ];

View File

@ -0,0 +1,28 @@
<?php
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateSessionsTable extends Migration
{
/**
* Run the migration
*/
public function up()
{
$this->schema->create('sessions', function (Blueprint $table) {
$table->string('id')->unique();
$table->text('payload');
$table->integer('last_activity');
$table->integer('lifetime');
});
}
/**
* Reverse the migration
*/
public function down()
{
$this->schema->dropIfExists('sessions');
}
}

View File

@ -31,8 +31,9 @@ class DatabaseServiceProvider extends ServiceProvider
$capsule->bootEloquent(); $capsule->bootEloquent();
$capsule->getConnection()->useDefaultSchemaGrammar(); $capsule->getConnection()->useDefaultSchemaGrammar();
$pdo = null;
try { try {
$capsule->getConnection()->getPdo(); $pdo = $capsule->getConnection()->getPdo();
} catch (PDOException $e) { } catch (PDOException $e) {
$this->exitOnError(); $this->exitOnError();
} }

View File

@ -2,8 +2,10 @@
namespace Engelsystem\Http; namespace Engelsystem\Http;
use Engelsystem\Config\Config;
use Engelsystem\Container\ServiceProvider; use Engelsystem\Container\ServiceProvider;
use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
@ -38,7 +40,35 @@ class SessionServiceProvider extends ServiceProvider
return $this->app->make(MockArraySessionStorage::class); return $this->app->make(MockArraySessionStorage::class);
} }
return $this->app->make(NativeSessionStorage::class, ['options' => ['cookie_httponly' => true]]); /** @var Config $config */
$config = $this->app->get('config');
$sessionConfig = $config->get('session');
$handler = null;
$driver = $sessionConfig['driver'];
switch ($driver) {
case 'pdo':
$handler = $this->app->make(PdoSessionHandler::class, [
'pdoOrDsn' => $this->app->get('db.pdo'),
'options' => [
'db_table' => 'sessions',
'db_id_col' => 'id',
'db_data_col' => 'payload',
'db_lifetime_col' => 'lifetime',
'db_time_col' => 'last_activity',
],
]);
break;
}
return $this->app->make(NativeSessionStorage::class, [
'options' => [
'cookie_httponly' => true,
'name' => $sessionConfig['name'],
],
'handler' => $handler,
]);
} }
/** /**

View File

@ -2,6 +2,8 @@
namespace Engelsystem\Models; namespace Engelsystem\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
abstract class BaseModel extends Model abstract class BaseModel extends Model
@ -10,6 +12,8 @@ abstract class BaseModel extends Model
public $timestamps = false; public $timestamps = false;
/** /**
* Create a new model
*
* @param array $attributes * @param array $attributes
* @return BaseModel * @return BaseModel
*/ */
@ -20,4 +24,16 @@ abstract class BaseModel extends Model
return $instance; return $instance;
} }
/**
* Find a model by its primary key
*
* @param mixed $id
* @param array $columns
* @return Builder|Builder[]|Collection|Model|null
*/
public static function find($id, $columns = ['*'])
{
return static::query()->find($id, $columns);
}
} }

View File

@ -2,6 +2,7 @@
namespace Engelsystem\Test\Unit\Database; namespace Engelsystem\Test\Unit\Database;
use Engelsystem\Application;
use Engelsystem\Config\Config; use Engelsystem\Config\Config;
use Engelsystem\Database\Database; use Engelsystem\Database\Database;
use Engelsystem\Database\DatabaseServiceProvider; use Engelsystem\Database\DatabaseServiceProvider;
@ -10,6 +11,7 @@ use Engelsystem\Test\Unit\ServiceProviderTest;
use Exception; use Exception;
use Illuminate\Database\Capsule\Manager as CapsuleManager; use Illuminate\Database\Capsule\Manager as CapsuleManager;
use Illuminate\Database\Connection; use Illuminate\Database\Connection;
use PDO;
use PDOException; use PDOException;
use PHPUnit_Framework_MockObject_MockObject as MockObject; use PHPUnit_Framework_MockObject_MockObject as MockObject;
@ -117,12 +119,12 @@ class DatabaseServiceProviderTest extends ServiceProviderTest
$this->setExpects($connection, 'useDefaultSchemaGrammar'); $this->setExpects($connection, 'useDefaultSchemaGrammar');
$connection->expects($this->once()) $connection->expects($this->once())
->method('getPdo') ->method('getPdo')
->willReturnCallback(function () use ($getPdoThrowException) { ->willReturnCallback(function () use ($getPdoThrowException, $pdo) {
if ($getPdoThrowException) { if ($getPdoThrowException) {
throw new PDOException(); throw new PDOException();
} }
return ''; return $pdo;
}); });
$this->setExpects($dbManager, 'getConnection', [], $connection, $this->atLeastOnce()); $this->setExpects($dbManager, 'getConnection', [], $connection, $this->atLeastOnce());

View File

@ -2,11 +2,14 @@
namespace Engelsystem\Test\Unit\Http; namespace Engelsystem\Test\Unit\Http;
use Engelsystem\Config\Config;
use Engelsystem\Http\Request; use Engelsystem\Http\Request;
use Engelsystem\Http\SessionServiceProvider; use Engelsystem\Http\SessionServiceProvider;
use Engelsystem\Test\Unit\ServiceProviderTest; use Engelsystem\Test\Unit\ServiceProviderTest;
use PDO;
use PHPUnit_Framework_MockObject_MockObject as MockObject; use PHPUnit_Framework_MockObject_MockObject as MockObject;
use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface as StorageInterface; use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface as StorageInterface;
@ -23,6 +26,9 @@ class SessionServiceProviderTest extends ServiceProviderTest
$sessionStorage = $this->getMockForAbstractClass(StorageInterface::class); $sessionStorage = $this->getMockForAbstractClass(StorageInterface::class);
$sessionStorage2 = $this->getMockForAbstractClass(StorageInterface::class); $sessionStorage2 = $this->getMockForAbstractClass(StorageInterface::class);
$pdoSessionHandler = $this->getMockBuilder(PdoSessionHandler::class)
->disableOriginalConstructor()
->getMock();
$session = $this->getSessionMock(); $session = $this->getSessionMock();
$request = $this->getRequestMock(); $request = $this->getRequestMock();
@ -32,22 +38,54 @@ class SessionServiceProviderTest extends ServiceProviderTest
->setConstructorArgs([$app]) ->setConstructorArgs([$app])
->setMethods(['isCli']) ->setMethods(['isCli'])
->getMock(); ->getMock();
$serviceProvider->expects($this->exactly(2))
->method('isCli')
->willReturnOnConsecutiveCalls(true, false);
$app->expects($this->exactly(4)) /** @var Config|MockObject $config */
$config = $this->createMock(Config::class);
/** @var PDO|MockObject $pdo */
$pdo = $this->getMockBuilder(PDO::class)
->disableOriginalConstructor()
->getMock();
$serviceProvider->expects($this->exactly(3))
->method('isCli')
->willReturnOnConsecutiveCalls(true, false, false);
$app->expects($this->exactly(7))
->method('make') ->method('make')
->withConsecutive( ->withConsecutive(
[MockArraySessionStorage::class], [MockArraySessionStorage::class],
[Session::class], [Session::class],
[NativeSessionStorage::class, ['options' => ['cookie_httponly' => true]]], [
NativeSessionStorage::class,
['options' => ['cookie_httponly' => true, 'name' => 'session'], 'handler' => null]
],
[Session::class],
[
PdoSessionHandler::class,
[
'pdoOrDsn' => $pdo,
'options' => [
'db_table' => 'sessions',
'db_id_col' => 'id',
'db_data_col' => 'payload',
'db_lifetime_col' => 'lifetime',
'db_time_col' => 'last_activity',
],
]
],
[
NativeSessionStorage::class,
['options' => ['cookie_httponly' => true, 'name' => 'foobar'], 'handler' => $pdoSessionHandler]
],
[Session::class] [Session::class]
) )
->willReturnOnConsecutiveCalls( ->willReturnOnConsecutiveCalls(
$sessionStorage, $sessionStorage,
$session, $session,
$sessionStorage2, $sessionStorage2,
$session,
$pdoSessionHandler,
$sessionStorage2,
$session $session
); );
$app->expects($this->atLeastOnce()) $app->expects($this->atLeastOnce())
@ -58,13 +96,40 @@ class SessionServiceProviderTest extends ServiceProviderTest
['session', $session] ['session', $session]
); );
$app->expects($this->exactly(6))
->method('get')
->withConsecutive(
['request'],
['config'],
['request'],
['config'],
['db.pdo'],
['request']
)
->willReturnOnConsecutiveCalls(
$request,
$config,
$request,
$config,
$pdo,
$request
);
$config->expects($this->exactly(2))
->method('get')
->with('session')
->willReturnOnConsecutiveCalls(
['driver' => 'native', 'name' => 'session'],
['driver' => 'pdo', 'name' => 'foobar']
);
$this->setExpects($app, 'bind', [StorageInterface::class, 'session.storage'], null, $this->atLeastOnce()); $this->setExpects($app, 'bind', [StorageInterface::class, 'session.storage'], null, $this->atLeastOnce());
$this->setExpects($app, 'get', ['request'], $request, $this->atLeastOnce());
$this->setExpects($request, 'setSession', [$session], null, $this->atLeastOnce()); $this->setExpects($request, 'setSession', [$session], null, $this->atLeastOnce());
$this->setExpects($session, 'start', null, null, $this->atLeastOnce()); $this->setExpects($session, 'start', null, null, $this->atLeastOnce());
$serviceProvider->register(); $serviceProvider->register();
$serviceProvider->register(); $serviceProvider->register();
$serviceProvider->register();
} }
/** /**

View File

@ -3,6 +3,8 @@
namespace Engelsystem\Test\Unit\Models; namespace Engelsystem\Test\Unit\Models;
use Engelsystem\Test\Unit\Models\Stub\BaseModelImplementation; use Engelsystem\Test\Unit\Models\Stub\BaseModelImplementation;
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class BaseModelTest extends TestCase class BaseModelTest extends TestCase
@ -19,4 +21,26 @@ class BaseModelTest extends TestCase
$this->assertEquals('bar', $newModel->foo); $this->assertEquals('bar', $newModel->foo);
$this->assertEquals(1, $newModel->saveCount); $this->assertEquals(1, $newModel->saveCount);
} }
/**
* @covers \Engelsystem\Models\BaseModel::find
*/
public function testFind()
{
/** @var QueryBuilder|MockObject $queryBuilder */
$queryBuilder = $this->createMock(QueryBuilder::class);
BaseModelImplementation::$queryBuilder = $queryBuilder;
$anotherModel = new BaseModelImplementation();
$queryBuilder->expects($this->once())
->method('find')
->with(1337, ['foo', 'bar'])
->willReturn($anotherModel);
$model = new BaseModelImplementation();
$newModel = $model->find(1337, ['foo', 'bar']);
$this->assertEquals($anotherModel, $newModel);
}
} }

View File

@ -3,6 +3,7 @@
namespace Engelsystem\Test\Unit\Models\Stub; namespace Engelsystem\Test\Unit\Models\Stub;
use Engelsystem\Models\BaseModel; use Engelsystem\Models\BaseModel;
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
/** /**
* @property string foo * @property string foo
@ -15,6 +16,9 @@ class BaseModelImplementation extends BaseModel
/** @var int */ /** @var int */
public $saveCount = 0; public $saveCount = 0;
/** @var QueryBuilder */
public static $queryBuilder = null;
/** /**
* @param array $options * @param array $options
* @return bool * @return bool
@ -24,4 +28,12 @@ class BaseModelImplementation extends BaseModel
$this->saveCount++; $this->saveCount++;
return true; return true;
} }
/**
* @return QueryBuilder
*/
public static function query()
{
return self::$queryBuilder;
}
} }