242 lines
6.5 KiB
PHP
242 lines
6.5 KiB
PHP
<?php
|
|
namespace nur\sery\db\pdo;
|
|
|
|
use Generator;
|
|
use nur\sery\cl;
|
|
use nur\sery\db\IDatabase;
|
|
use nur\sery\php\func;
|
|
use nur\sery\php\time\Date;
|
|
use nur\sery\php\time\DateTime;
|
|
|
|
class Pdo implements IDatabase {
|
|
static function with($pdo, ?array $params=null): self {
|
|
if ($pdo instanceof static) {
|
|
return $pdo;
|
|
} elseif ($pdo instanceof self) {
|
|
# recréer avec les mêmes paramètres
|
|
return new static(null, cl::merge([
|
|
"dbconn" => $pdo->dbconn,
|
|
"options" => $pdo->options,
|
|
"config" => $pdo->config,
|
|
"migrate" => $pdo->migration,
|
|
], $params));
|
|
} else {
|
|
return new static($pdo, $params);
|
|
}
|
|
}
|
|
|
|
static function config_errmodeException_lowerCase(self $pdo) {
|
|
$pdo->db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
|
$pdo->db->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER);
|
|
}
|
|
|
|
const OPTIONS = [
|
|
\PDO::ATTR_PERSISTENT => true,
|
|
];
|
|
|
|
const CONFIG = [
|
|
[self::class, "config_errmodeException_lowerCase"],
|
|
];
|
|
|
|
const MIGRATE = null;
|
|
|
|
const dbconn_SCHEMA = [
|
|
"name" => "string",
|
|
"user" => "?string",
|
|
"pass" => "?string",
|
|
];
|
|
|
|
const params_SCHEMA = [
|
|
"dbconn" => ["array"],
|
|
"options" => ["?array|callable"],
|
|
"config" => ["?array|callable"],
|
|
"migrate" => ["?array|string|callable"],
|
|
"auto_open" => ["bool", true],
|
|
];
|
|
|
|
function __construct($dbconn=null, ?array $params=null) {
|
|
if ($dbconn !== null) {
|
|
if (!is_array($dbconn)) {
|
|
$dbconn = ["name" => $dbconn];
|
|
#XXX à terme, il faudra interroger config
|
|
#$tmp = config::db($dbconn);
|
|
#if ($tmp !== null) $dbconn = $tmp;
|
|
#else $dbconn = ["name" => $dbconn];
|
|
}
|
|
$params["dbconn"] = $dbconn;
|
|
}
|
|
# dbconn
|
|
$this->dbconn = $params["dbconn"] ?? null;
|
|
$this->dbconn["name"] ??= null;
|
|
$this->dbconn["user"] ??= null;
|
|
$this->dbconn["pass"] ??= null;
|
|
# options
|
|
$this->options = $params["options"] ?? static::OPTIONS;
|
|
# configuration
|
|
$this->config = $params["config"] ?? static::CONFIG;
|
|
# migrations
|
|
$this->migration = $params["migrate"] ?? static::MIGRATE;
|
|
#
|
|
$defaultAutoOpen = self::params_SCHEMA["auto_open"][1];
|
|
if ($params["auto_open"] ?? $defaultAutoOpen) {
|
|
$this->open();
|
|
}
|
|
}
|
|
|
|
protected ?array $dbconn;
|
|
|
|
/** @var array|callable */
|
|
protected array $options;
|
|
|
|
/** @var array|string|callable */
|
|
protected $config;
|
|
|
|
/** @var array|string|callable */
|
|
protected $migration;
|
|
|
|
protected ?\PDO $db = null;
|
|
|
|
function open(): self {
|
|
if ($this->db === null) {
|
|
$dbconn = $this->dbconn;
|
|
$options = $this->options;
|
|
if (is_callable($options)) {
|
|
func::ensure_func($options, $this, $args);
|
|
$options = func::call($options, ...$args);
|
|
}
|
|
$this->db = new \PDO($dbconn["name"], $dbconn["user"], $dbconn["pass"], $options);
|
|
_config::with($this->config)->configure($this);
|
|
//_migration::with($this->migration)->migrate($this);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
function close(): void {
|
|
$this->db = null;
|
|
}
|
|
|
|
protected function db(): \PDO {
|
|
$this->open();
|
|
return $this->db;
|
|
}
|
|
|
|
/** @return int|false */
|
|
function _exec(string $query) {
|
|
return $this->db()->exec($query);
|
|
}
|
|
|
|
private static function is_insert(?string $sql): bool {
|
|
if ($sql === null) return false;
|
|
return preg_match('/^\s*insert\b/i', $sql);
|
|
}
|
|
|
|
function exec($query, ?array $params=null) {
|
|
$db = $this->db();
|
|
$query = new _query_base($query, $params);
|
|
if ($query->useStmt($db, $stmt, $sql)) {
|
|
if ($stmt->execute() === false) return false;
|
|
if ($query->isInsert()) return $db->lastInsertId();
|
|
else return $stmt->rowCount();
|
|
} else {
|
|
$rowCount = $db->exec($sql);
|
|
if (self::is_insert($sql)) return $db->lastInsertId();
|
|
else return $rowCount;
|
|
}
|
|
}
|
|
|
|
function beginTransaction(): void {
|
|
$this->db()->beginTransaction();
|
|
}
|
|
|
|
function commit(): void {
|
|
$this->db()->commit();
|
|
}
|
|
|
|
function rollback(): void {
|
|
$this->db()->rollBack();
|
|
}
|
|
|
|
/**
|
|
* Tester si $date est une date/heure valide de la forme "YYYY-mm-dd HH:MM:SS"
|
|
*
|
|
* Si oui, $ms obtient les 6 éléments de la chaine
|
|
*/
|
|
static function is_datetime($date, ?array &$ms=null): bool {
|
|
return is_string($date) && preg_match('/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/', $date, $ms);
|
|
}
|
|
|
|
/**
|
|
* Tester si $date est une date valide de la forme "YYYY-mm-dd [00:00:00]"
|
|
*
|
|
* Si oui, $ms obtient les 3 éléments de la chaine
|
|
*/
|
|
static function is_date($date, ?array &$ms=null): bool {
|
|
return is_string($date) && preg_match('/^(\d{4})-(\d{2})-(\d{2})(?: 00:00:00)?$/', $date, $ms);
|
|
}
|
|
|
|
function verifixRow(array &$row) {
|
|
foreach ($row as &$value) {
|
|
if (self::is_date($value)) {
|
|
$value = new Date($value);
|
|
} elseif (self::is_date($value)) {
|
|
$value = new DateTime($value);
|
|
}
|
|
}; unset($value);
|
|
}
|
|
|
|
function get($query, ?array $params=null, bool $entireRow=false) {
|
|
$db = $this->db();
|
|
$query = new _query_base($query, $params);
|
|
$stmt = null;
|
|
try {
|
|
/** @var \PDOStatement $stmt */
|
|
if ($query->useStmt($db, $stmt, $sql)) {
|
|
if ($stmt->execute() === false) return null;
|
|
} else {
|
|
$stmt = $db->query($sql);
|
|
}
|
|
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
|
if ($row === false) return null;
|
|
$this->verifixRow($row);
|
|
if ($entireRow) return $row;
|
|
else return cl::first($row);
|
|
} finally {
|
|
if ($stmt instanceof \PDOStatement) $stmt->closeCursor();
|
|
}
|
|
}
|
|
|
|
function one($query, ?array $params=null): ?array {
|
|
return $this->get($query, $params, true);
|
|
}
|
|
|
|
/**
|
|
* si $primaryKeys est fourni, le résultat est indexé sur la(es) colonne(s)
|
|
* spécifiée(s)
|
|
*/
|
|
function all($query, ?array $params=null, $primaryKeys=null): Generator {
|
|
$db = $this->db();
|
|
$query = new _query_base($query, $params);
|
|
$stmt = null;
|
|
try {
|
|
/** @var \PDOStatement $stmt */
|
|
if ($query->useStmt($db, $stmt, $sql)) {
|
|
if ($stmt->execute() === false) return;
|
|
} else {
|
|
$stmt = $db->query($sql);
|
|
}
|
|
if ($primaryKeys !== null) $primaryKeys = cl::with($primaryKeys);
|
|
while (($row = $stmt->fetch(\PDO::FETCH_ASSOC)) !== false) {
|
|
$this->verifixRow($row);
|
|
if ($primaryKeys !== null) {
|
|
$key = implode("-", cl::select($row, $primaryKeys));
|
|
yield $key => $row;
|
|
} else {
|
|
yield $row;
|
|
}
|
|
}
|
|
} finally {
|
|
if ($stmt instanceof \PDOStatement) $stmt->closeCursor();
|
|
}
|
|
}
|
|
}
|