Go back to classic style PHP and use the PDO to call MySQL
This commit is contained in:
parent
f788ba63ae
commit
d360cceee3
3
.editorconfig
Normal file
3
.editorconfig
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[*.{php,phtml,twig}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
@ -13,10 +13,8 @@
|
|||||||
"slim/flash": "^0.4.0",
|
"slim/flash": "^0.4.0",
|
||||||
"odan/session": "^6.1",
|
"odan/session": "^6.1",
|
||||||
"dotenv-org/phpdotenv-vault": "^0.2.4",
|
"dotenv-org/phpdotenv-vault": "^0.2.4",
|
||||||
"react/react": "^1.4",
|
|
||||||
"robmorgan/phinx": "^0.16.1",
|
"robmorgan/phinx": "^0.16.1",
|
||||||
"react/mysql": "^0.7dev",
|
"ext-pdo": "*"
|
||||||
"react/async": "^4.3"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^11.1",
|
"phpunit/phpunit": "^11.1",
|
||||||
|
1500
app/composer.lock
generated
1500
app/composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,6 @@ use Psr\Container\ContainerInterface;
|
|||||||
use Psr\Container\NotFoundExceptionInterface;
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
use Psr\Http\Message\ResponseFactoryInterface;
|
use Psr\Http\Message\ResponseFactoryInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use React\Mysql\MysqlClient;
|
|
||||||
use Slim\App;
|
use Slim\App;
|
||||||
use Slim\Factory\AppFactory;
|
use Slim\Factory\AppFactory;
|
||||||
use Slim\Psr7\Factory\ResponseFactory;
|
use Slim\Psr7\Factory\ResponseFactory;
|
||||||
@ -27,9 +26,9 @@ use Slovocast\Configuration\SiteInformationSchema;
|
|||||||
use Slovocast\Domain\Repository\User\UserRepository;
|
use Slovocast\Domain\Repository\User\UserRepository;
|
||||||
use Slovocast\Domain\Repository\User\UserRepositoryInterface;
|
use Slovocast\Domain\Repository\User\UserRepositoryInterface;
|
||||||
use Slovocast\Infrastructure\Api\Database\ConnectionPoolInterface;
|
use Slovocast\Infrastructure\Api\Database\ConnectionPoolInterface;
|
||||||
|
use Slovocast\Infrastructure\Api\DatabaseConnectionInterface;
|
||||||
use Slovocast\Infrastructure\Api\User\UserAuthorizationInterface;
|
use Slovocast\Infrastructure\Api\User\UserAuthorizationInterface;
|
||||||
use Slovocast\Infrastructure\Database\ConnectionPoolConfig;
|
use Slovocast\Infrastructure\Database\DatabaseConnection;
|
||||||
use Slovocast\Infrastructure\Database\ConnectionPool;
|
|
||||||
use Slovocast\Infrastructure\User\BasicUserAuthorization;
|
use Slovocast\Infrastructure\User\BasicUserAuthorization;
|
||||||
use Twig\Error\LoaderError;
|
use Twig\Error\LoaderError;
|
||||||
|
|
||||||
@ -128,17 +127,8 @@ class Bootstrap
|
|||||||
/**
|
/**
|
||||||
* Database Connections
|
* Database Connections
|
||||||
*/
|
*/
|
||||||
ConnectionPoolInterface::class => function (ContainerInterface $container) {
|
DatabaseConnectionInterface::class => function (ContainerInterface $container) {
|
||||||
$config = $container->get('config')->get('database');
|
$config = $container->get('config')->get('database');
|
||||||
|
|
||||||
$pool = new ConnectionPool(new ConnectionPoolConfig(
|
|
||||||
$config['username'],
|
|
||||||
$config['password'],
|
|
||||||
$config['host'],
|
|
||||||
$config['database']
|
|
||||||
));
|
|
||||||
|
|
||||||
return $pool;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,7 +143,7 @@ class Bootstrap
|
|||||||
*/
|
*/
|
||||||
UserRepositoryInterface::class => function (ContainerInterface $container) {
|
UserRepositoryInterface::class => function (ContainerInterface $container) {
|
||||||
return new UserRepository(
|
return new UserRepository(
|
||||||
$container->get(ConnectionPoolInterface::class),
|
$container->get(DatabaseConnectionInterface::class),
|
||||||
$container->get(UserAuthorizationInterface::class)
|
$container->get(UserAuthorizationInterface::class)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,13 @@ namespace Slovocast\Domain\Repository\User;
|
|||||||
|
|
||||||
use Slovocast\Domain\Entity\User;
|
use Slovocast\Domain\Entity\User;
|
||||||
use Slovocast\Exception\EntityNotFoundException;
|
use Slovocast\Exception\EntityNotFoundException;
|
||||||
use Slovocast\Infrastructure\Api\Database\ConnectionPoolInterface;
|
use Slovocast\Infrastructure\Api\Database\DatabaseConnectionInterface;
|
||||||
use Slovocast\Infrastructure\Api\User\UserAuthorizationInterface;
|
use Slovocast\Infrastructure\Api\User\UserAuthorizationInterface;
|
||||||
use function React\Async\await;
|
|
||||||
|
|
||||||
class UserRepository implements UserRepositoryInterface
|
class UserRepository implements UserRepositoryInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ConnectionPoolInterface $connectionPool,
|
private DatabaseConnectionInterface $db,
|
||||||
private UserAuthorizationInterface $userAuth
|
private UserAuthorizationInterface $userAuth
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -37,13 +36,13 @@ class UserRepository implements UserRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function get(int $id): User
|
public function get(int $id): User
|
||||||
{
|
{
|
||||||
$query = "SELECT * FROM users WHERE id = ? LIMIT 1";
|
$query = "SELECT * FROM users WHERE id = :id LIMIT 1";
|
||||||
|
|
||||||
/** @var $conn ConnectionPool */
|
$statement = $this->db->getConnection()->prepare($query);
|
||||||
$conn = $this->connectionPool->getConnection();
|
$statement->execute([ ':id' => $id ]);
|
||||||
$results = await($conn->query($query, [ $id ]));
|
$results = $statement->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
return $this->userFromQueryResults($results->resultRows[0]);
|
return $this->userFromQueryResults($results);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,46 +53,47 @@ class UserRepository implements UserRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function getFromEmail(string $email): User
|
public function getFromEmail(string $email): User
|
||||||
{
|
{
|
||||||
$query = "SELECT * FROM users WHERE email = ? LIMIT 1";
|
$query = "SELECT * FROM users WHERE email = :email LIMIT 1";
|
||||||
$results = await($this->db->query($query, [ $email ]));
|
$statement = $this->db->getConnection()->prepare($query);
|
||||||
|
$statement->execute([ ':email' => $email ]);
|
||||||
|
$results = $statement->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if (!count($results->resultRows)) {
|
if (!count($results)) {
|
||||||
throw new EntityNotFoundException("Unable to find User");
|
throw new EntityNotFoundException("Unable to find User");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->userFromQueryResults($results->resultRows[0]);
|
return $this->userFromQueryResults($results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create(User $user): bool
|
public function create(User $user): bool
|
||||||
{
|
{
|
||||||
$query = "INSERT INTO users (email, password, name)
|
$query = "INSERT INTO users (email, password, name)
|
||||||
VALUES (?, ?, ?)";
|
VALUES (:email, :password, :name)";
|
||||||
|
|
||||||
$results = await($this->db->query($query, [
|
$results = $this->db->getConnection()->exec($query, [
|
||||||
$user->getEmail(),
|
':email' => $user->getEmail(),
|
||||||
$this->userAuth->hash($user->getPassword()),
|
':password' => $this->userAuth->hash($user->getPassword()),
|
||||||
$user->getName(),
|
':name' => $user->getName(),
|
||||||
]));
|
]);
|
||||||
|
|
||||||
return (bool) $results->insertId;
|
return (bool) $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(User $user): bool
|
public function update(User $user): bool
|
||||||
{
|
{
|
||||||
$query = "UPDATE users
|
$query = "UPDATE users
|
||||||
SET email = ?,
|
SET email = :email,
|
||||||
name = ?,
|
name = :name,
|
||||||
password = ?
|
password = :password
|
||||||
WHERE id = ?";
|
WHERE id = :id";
|
||||||
|
|
||||||
$results = await($this->db->query($query, [
|
$statement = this->db->prepare($query);
|
||||||
|
return $statement->execute([
|
||||||
$user->getEmail(),
|
$user->getEmail(),
|
||||||
$user->getName(),
|
$user->getName(),
|
||||||
$this->userAuth->hash($user->getPassword()),
|
$this->userAuth->hash($user->getPassword()),
|
||||||
$user->getId()
|
$user->getId()
|
||||||
]));
|
]);
|
||||||
|
|
||||||
return (bool) $results->affectedRows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function verifyPassword(string $email, string $password): bool
|
public function verifyPassword(string $email, string $password): bool
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Slovocast\Infrastructure\Api\Database;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
interface ConnectionPoolInterface
|
|
||||||
{
|
|
||||||
public function getTotalIdleConnections(): int;
|
|
||||||
public function getTotalActiveConnections(): int;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If there is at least one idle connection waiting to be used.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasIdleConnection(): bool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the new wait timeout for acquiring a connection.
|
|
||||||
*
|
|
||||||
* @param int $ms The amount of time in seconds.
|
|
||||||
*/
|
|
||||||
public function setWaitTimeout(int $s): void;
|
|
||||||
public function getWaitTimeout(): int;
|
|
||||||
public function getConnectionLimit(): int;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method will grab a connection from the Idle list, push it into the Active list and returns that same
|
|
||||||
* connection to the caller. If there is no active connections, the method will wait based on the `waitTimeout`
|
|
||||||
* value (in seconds) and throw an exception when the timeout has been reached.
|
|
||||||
*
|
|
||||||
* @throws Exception When the Wait Timeout is surpassed waiting for an idle connection
|
|
||||||
* @return PooledConnectionInterface
|
|
||||||
*/
|
|
||||||
public function getConnection(): PooledConnectionInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method will return a connection from the Active list into the Idle list.
|
|
||||||
*
|
|
||||||
* @param PooledConnectionInterface $connection
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function releaseConnection(PooledConnectionInterface $connection): void;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Slovocast\Infrastructure\Api\Database;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
interface DatabaseConnectionInterface
|
||||||
|
{
|
||||||
|
public function getConnection(): PDO;
|
||||||
|
public function setNewConnection(PDO $connection): void;
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Slovocast\Infrastructure\Api\Database;
|
|
||||||
|
|
||||||
interface PooledConnectionInterface
|
|
||||||
{
|
|
||||||
public function release(): void;
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Slovocast\Infrastructure\Database;
|
|
||||||
|
|
||||||
use Slovocast\Infrastructure\Api\Database\ConnectionPoolInterface;
|
|
||||||
use Slovocast\Infrastructure\Api\Database\PooledConnectionInterface;
|
|
||||||
use SplObjectStorage;
|
|
||||||
|
|
||||||
class ConnectionPool implements ConnectionPoolInterface
|
|
||||||
{
|
|
||||||
private int $waitTimeout;
|
|
||||||
private SplObjectStorage $idleConnections;
|
|
||||||
private SplObjectStorage $activeConnections;
|
|
||||||
private ConnectionPoolConfig $config;
|
|
||||||
|
|
||||||
public function __construct(ConnectionPoolConfig $config)
|
|
||||||
{
|
|
||||||
$this->idleConnections = new SplObjectStorage();
|
|
||||||
$this->activeConnections = new SplObjectStorage();
|
|
||||||
|
|
||||||
for ($i = 0; $i < $this->config->getTotalConnections(); $i++) {
|
|
||||||
$pooledConnection = new PooledConnection($this->config->getDsnString());
|
|
||||||
$this->idleConnections->attach($$pooledConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->waitTimeout = $config->getPoolWaitTimeout();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTotalIdleConnections(): int
|
|
||||||
{
|
|
||||||
return $this->idleConnections->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTotalActiveConnections(): int
|
|
||||||
{
|
|
||||||
return $this->activeConnections->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasIdleConnection(): bool
|
|
||||||
{
|
|
||||||
return $this->idleConnections->count() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setWaitTimeout(int $s): void
|
|
||||||
{
|
|
||||||
$this->waitTimeout = $s;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getWaitTimeout(): int
|
|
||||||
{
|
|
||||||
return $this->waitTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getConnectionLimit(): int
|
|
||||||
{
|
|
||||||
return $this->config->getTotalConnections();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @TODO Throw an exception when a total timeout is exceeded. We do not want
|
|
||||||
* to get into an infinite loop.
|
|
||||||
*
|
|
||||||
* @return PooledConnectionInterface
|
|
||||||
*/
|
|
||||||
public function getConnection(): PooledConnectionInterface
|
|
||||||
{
|
|
||||||
if (!$this->hasIdleConnection()) {
|
|
||||||
\React\Async\delay((float) $this->getWaitTimeout());
|
|
||||||
return $this->getConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from the idle pool
|
|
||||||
$conn = $this->idleConnections->current();
|
|
||||||
$this->idleConnections->detach($conn);
|
|
||||||
|
|
||||||
// Attach to the pool to the connectin, add it to the active pool
|
|
||||||
$conn->setConnectionPool($this);
|
|
||||||
$this->activeConnections->attach($conn);
|
|
||||||
return $conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function releaseConnection(PooledConnectionInterface $connection): void
|
|
||||||
{
|
|
||||||
if ($this->activeConnections->contains($connection)) {
|
|
||||||
$this->activeConnections->detach($connection);
|
|
||||||
$this->idleConnections->attach($connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Slovocast\Infrastructure\Database;
|
|
||||||
|
|
||||||
class ConnectionPoolConfig
|
|
||||||
{
|
|
||||||
const DEFAULT_WAIT_TIMEOUT = 60;
|
|
||||||
const DEFAULT_TOTAL_CONNECTIONS = 10;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public readonly string $username,
|
|
||||||
public readonly string $password,
|
|
||||||
public readonly string $database,
|
|
||||||
public readonly string $host,
|
|
||||||
protected int $port = 3306,
|
|
||||||
protected int $poolWaitTimeout = self::DEFAULT_WAIT_TIMEOUT,
|
|
||||||
protected int $totalConnections = self::DEFAULT_TOTAL_CONNECTIONS
|
|
||||||
) { }
|
|
||||||
|
|
||||||
public function getPort(): int
|
|
||||||
{
|
|
||||||
return $this->port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPoolWaitTimeout(): int
|
|
||||||
{
|
|
||||||
return $this->poolWaitTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTotalConnections(): int
|
|
||||||
{
|
|
||||||
return $this->totalConnections;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDsnString(): string
|
|
||||||
{
|
|
||||||
return sprintf(
|
|
||||||
"%s:%s@%s/%s",
|
|
||||||
rawurlencode($this->username),
|
|
||||||
rawurlencode($this->password),
|
|
||||||
$this->host,
|
|
||||||
$this->database
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
21
app/src/Infrastructure/Database/DatabaseConnection.php
Normal file
21
app/src/Infrastructure/Database/DatabaseConnection.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Slovocast\Infrastructure\Database;
|
||||||
|
|
||||||
|
use Slovocast\Infrastructure\Api\Database\DatabaseConnectionInterface;
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class DatabaseConnection implements DatabaseConnectionInterface
|
||||||
|
{
|
||||||
|
public function __construct(private PDO $pdo) {}
|
||||||
|
|
||||||
|
public function getConnection(): PDO
|
||||||
|
{
|
||||||
|
return $this->pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setNewConnection(PDO $pdo): void
|
||||||
|
{
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Slovocast\Infrastructure\Database;
|
|
||||||
|
|
||||||
use React\Mysql\MysqlClient;
|
|
||||||
use Slovocast\Infrastructure\Api\Database\ConnectionPoolInterface;
|
|
||||||
use Slovocast\Infrastructure\Api\Database\PooledConnectionInterface;
|
|
||||||
|
|
||||||
class PooledConnection extends MysqlClient implements PooledConnectionInterface
|
|
||||||
{
|
|
||||||
protected ConnectionPoolInterface $connectionPool;
|
|
||||||
|
|
||||||
public function setConnectionPool(ConnectionPoolInterface $connectionPool): self
|
|
||||||
{
|
|
||||||
$this->connectionPool = $connectionPool;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function release(): void
|
|
||||||
{
|
|
||||||
$this->connectionPool->releaseConnection($this);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user