|
|
|
@ -4,12 +4,40 @@ declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace flight\database;
|
|
|
|
|
|
|
|
|
|
use flight\core\EventDispatcher;
|
|
|
|
|
use flight\util\Collection;
|
|
|
|
|
use PDO;
|
|
|
|
|
use PDOStatement;
|
|
|
|
|
|
|
|
|
|
class PdoWrapper extends PDO
|
|
|
|
|
{
|
|
|
|
|
/** @var bool $trackApmQueries Whether to track application performance metrics (APM) for queries. */
|
|
|
|
|
protected bool $trackApmQueries = false;
|
|
|
|
|
|
|
|
|
|
/** @var array<int,array<string,mixed>> $queryMetrics Metrics related to the database connection. */
|
|
|
|
|
protected array $queryMetrics = [];
|
|
|
|
|
|
|
|
|
|
/** @var array<string,string> $connectionMetrics Metrics related to the database connection. */
|
|
|
|
|
protected array $connectionMetrics = [];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructor for the PdoWrapper class.
|
|
|
|
|
*
|
|
|
|
|
* @param string $dsn The Data Source Name (DSN) for the database connection.
|
|
|
|
|
* @param string|null $username The username for the database connection.
|
|
|
|
|
* @param string|null $password The password for the database connection.
|
|
|
|
|
* @param array<string, mixed>|null $options An array of options for the PDO connection.
|
|
|
|
|
* @param bool $trackApmQueries Whether to track application performance metrics (APM) for queries.
|
|
|
|
|
*/
|
|
|
|
|
public function __construct(?string $dsn = null, ?string $username = '', ?string $password = '', ?array $options = null, bool $trackApmQueries = false)
|
|
|
|
|
{
|
|
|
|
|
parent::__construct($dsn, $username, $password, $options);
|
|
|
|
|
$this->trackApmQueries = $trackApmQueries;
|
|
|
|
|
if ($this->trackApmQueries === true) {
|
|
|
|
|
$this->connectionMetrics = $this->pullDataFromDsn($dsn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Use this for INSERTS, UPDATES, or if you plan on using a SELECT in a while loop
|
|
|
|
|
*
|
|
|
|
@ -31,8 +59,19 @@ class PdoWrapper extends PDO
|
|
|
|
|
$processed_sql_data = $this->processInStatementSql($sql, $params);
|
|
|
|
|
$sql = $processed_sql_data['sql'];
|
|
|
|
|
$params = $processed_sql_data['params'];
|
|
|
|
|
$start = $this->trackApmQueries === true ? microtime(true) : 0;
|
|
|
|
|
$memory_start = $this->trackApmQueries === true ? memory_get_usage() : 0;
|
|
|
|
|
$statement = $this->prepare($sql);
|
|
|
|
|
$statement->execute($params);
|
|
|
|
|
if ($this->trackApmQueries === true) {
|
|
|
|
|
$this->queryMetrics[] = [
|
|
|
|
|
'sql' => $sql,
|
|
|
|
|
'params' => $params,
|
|
|
|
|
'execution_time' => microtime(true) - $start,
|
|
|
|
|
'row_count' => $statement->rowCount(),
|
|
|
|
|
'memory_usage' => memory_get_usage() - $memory_start
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
return $statement;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -88,9 +127,20 @@ class PdoWrapper extends PDO
|
|
|
|
|
$processed_sql_data = $this->processInStatementSql($sql, $params);
|
|
|
|
|
$sql = $processed_sql_data['sql'];
|
|
|
|
|
$params = $processed_sql_data['params'];
|
|
|
|
|
$start = $this->trackApmQueries === true ? microtime(true) : 0;
|
|
|
|
|
$memory_start = $this->trackApmQueries === true ? memory_get_usage() : 0;
|
|
|
|
|
$statement = $this->prepare($sql);
|
|
|
|
|
$statement->execute($params);
|
|
|
|
|
$results = $statement->fetchAll();
|
|
|
|
|
if ($this->trackApmQueries === true) {
|
|
|
|
|
$this->queryMetrics[] = [
|
|
|
|
|
'sql' => $sql,
|
|
|
|
|
'params' => $params,
|
|
|
|
|
'execution_time' => microtime(true) - $start,
|
|
|
|
|
'row_count' => $statement->rowCount(),
|
|
|
|
|
'memory_usage' => memory_get_usage() - $memory_start
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
if (is_array($results) === true && count($results) > 0) {
|
|
|
|
|
foreach ($results as &$result) {
|
|
|
|
|
$result = new Collection($result);
|
|
|
|
@ -101,6 +151,56 @@ class PdoWrapper extends PDO
|
|
|
|
|
return $results;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Pulls the engine, database, and host from the DSN string.
|
|
|
|
|
*
|
|
|
|
|
* @param string $dsn The Data Source Name (DSN) string.
|
|
|
|
|
*
|
|
|
|
|
* @return array<string,string> An associative array containing the engine, database, and host.
|
|
|
|
|
*/
|
|
|
|
|
protected function pullDataFromDsn(string $dsn): array
|
|
|
|
|
{
|
|
|
|
|
// pull the engine from the dsn (sqlite, mysql, pgsql, etc)
|
|
|
|
|
preg_match('/^([a-zA-Z]+):/', $dsn, $matches);
|
|
|
|
|
$engine = $matches[1] ?? 'unknown';
|
|
|
|
|
|
|
|
|
|
if ($engine === 'sqlite') {
|
|
|
|
|
// pull the path from the dsn
|
|
|
|
|
preg_match('/sqlite:(.*)/', $dsn, $matches);
|
|
|
|
|
$dbname = basename($matches[1] ?? 'unknown');
|
|
|
|
|
$host = 'localhost';
|
|
|
|
|
} else {
|
|
|
|
|
// pull the database from the dsn
|
|
|
|
|
preg_match('/dbname=([^;]+)/', $dsn, $matches);
|
|
|
|
|
$dbname = $matches[1] ?? 'unknown';
|
|
|
|
|
// pull the host from the dsn
|
|
|
|
|
preg_match('/host=([^;]+)/', $dsn, $matches);
|
|
|
|
|
$host = $matches[1] ?? 'unknown';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'engine' => $engine,
|
|
|
|
|
'database' => $dbname,
|
|
|
|
|
'host' => $host
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Logs the executed queries through the event dispatcher.
|
|
|
|
|
*
|
|
|
|
|
* This method enables logging of all the queries executed by the PDO wrapper.
|
|
|
|
|
* It can be useful for debugging and monitoring purposes.
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
public function logQueries(): void
|
|
|
|
|
{
|
|
|
|
|
if ($this->trackApmQueries === true && $this->connectionMetrics !== [] && $this->queryMetrics !== []) {
|
|
|
|
|
EventDispatcher::getInstance()->trigger('flight.db.queries', $this->connectionMetrics, $this->queryMetrics);
|
|
|
|
|
$this->queryMetrics = []; // Reset after logging
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Don't worry about this guy. Converts stuff for IN statements
|
|
|
|
|
*
|
|
|
|
|