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',
'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->getConnection()->useDefaultSchemaGrammar();
$pdo = null;
try {
$capsule->getConnection()->getPdo();
$pdo = $capsule->getConnection()->getPdo();
} catch (PDOException $e) {
$this->exitOnError();
}

View File

@ -2,8 +2,10 @@
namespace Engelsystem\Http;
use Engelsystem\Config\Config;
use Engelsystem\Container\ServiceProvider;
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\NativeSessionStorage;
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(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;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
abstract class BaseModel extends Model
@ -10,6 +12,8 @@ abstract class BaseModel extends Model
public $timestamps = false;
/**
* Create a new model
*
* @param array $attributes
* @return BaseModel
*/
@ -20,4 +24,16 @@ abstract class BaseModel extends Model
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;
use Engelsystem\Application;
use Engelsystem\Config\Config;
use Engelsystem\Database\Database;
use Engelsystem\Database\DatabaseServiceProvider;
@ -10,6 +11,7 @@ use Engelsystem\Test\Unit\ServiceProviderTest;
use Exception;
use Illuminate\Database\Capsule\Manager as CapsuleManager;
use Illuminate\Database\Connection;
use PDO;
use PDOException;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
@ -117,12 +119,12 @@ class DatabaseServiceProviderTest extends ServiceProviderTest
$this->setExpects($connection, 'useDefaultSchemaGrammar');
$connection->expects($this->once())
->method('getPdo')
->willReturnCallback(function () use ($getPdoThrowException) {
->willReturnCallback(function () use ($getPdoThrowException, $pdo) {
if ($getPdoThrowException) {
throw new PDOException();
}
return '';
return $pdo;
});
$this->setExpects($dbManager, 'getConnection', [], $connection, $this->atLeastOnce());

View File

@ -2,11 +2,14 @@
namespace Engelsystem\Test\Unit\Http;
use Engelsystem\Config\Config;
use Engelsystem\Http\Request;
use Engelsystem\Http\SessionServiceProvider;
use Engelsystem\Test\Unit\ServiceProviderTest;
use PDO;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
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\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface as StorageInterface;
@ -23,6 +26,9 @@ class SessionServiceProviderTest extends ServiceProviderTest
$sessionStorage = $this->getMockForAbstractClass(StorageInterface::class);
$sessionStorage2 = $this->getMockForAbstractClass(StorageInterface::class);
$pdoSessionHandler = $this->getMockBuilder(PdoSessionHandler::class)
->disableOriginalConstructor()
->getMock();
$session = $this->getSessionMock();
$request = $this->getRequestMock();
@ -32,22 +38,54 @@ class SessionServiceProviderTest extends ServiceProviderTest
->setConstructorArgs([$app])
->setMethods(['isCli'])
->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')
->withConsecutive(
[MockArraySessionStorage::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]
)
->willReturnOnConsecutiveCalls(
$sessionStorage,
$session,
$sessionStorage2,
$session,
$pdoSessionHandler,
$sessionStorage2,
$session
);
$app->expects($this->atLeastOnce())
@ -58,13 +96,40 @@ class SessionServiceProviderTest extends ServiceProviderTest
['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, 'get', ['request'], $request, $this->atLeastOnce());
$this->setExpects($request, 'setSession', [$session], null, $this->atLeastOnce());
$this->setExpects($session, 'start', null, null, $this->atLeastOnce());
$serviceProvider->register();
$serviceProvider->register();
$serviceProvider->register();
}
/**

View File

@ -3,6 +3,8 @@
namespace Engelsystem\Test\Unit\Models;
use Engelsystem\Test\Unit\Models\Stub\BaseModelImplementation;
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class BaseModelTest extends TestCase
@ -19,4 +21,26 @@ class BaseModelTest extends TestCase
$this->assertEquals('bar', $newModel->foo);
$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;
use Engelsystem\Models\BaseModel;
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
/**
* @property string foo
@ -15,6 +16,9 @@ class BaseModelImplementation extends BaseModel
/** @var int */
public $saveCount = 0;
/** @var QueryBuilder */
public static $queryBuilder = null;
/**
* @param array $options
* @return bool
@ -24,4 +28,12 @@ class BaseModelImplementation extends BaseModel
$this->saveCount++;
return true;
}
/**
* @return QueryBuilder
*/
public static function query()
{
return self::$queryBuilder;
}
}