$pdo->dbconn, "options" => $pdo->options, "config" => $pdo->config, "migration" => $pdo->migration, ], $params)); } else { return new static($pdo, $params); } } static function config_errmodeException_lowerCase(self $pdo): void { $pdo->db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $pdo->db->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_LOWER); } const CONFIG_errmodeException_lowerCase = [self::class, "config_errmodeException_lowerCase"]; static function config_unbufferedQueries(self $pdo): void { $pdo->db->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); } const CONFIG_unbufferedQueries = [self::class, "config_unbufferedQueries"]; protected const OPTIONS = [ \PDO::ATTR_PERSISTENT => true, ]; protected const DEFAULT_CONFIG = [ self::CONFIG_errmodeException_lowerCase, ]; protected const CONFIG = null; protected const MIGRATION = null; const dbconn_SCHEMA = [ "name" => "string", "user" => "?string", "pass" => "?string", ]; const params_SCHEMA = [ "dbconn" => ["array"], "options" => ["?array|callable"], "replace_config" => ["?array|callable"], "config" => ["?array|callable"], "migration" => ["?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 $config = $params["replace_config"] ?? null; if ($config === null) { $config = $params["config"] ?? static::CONFIG; if (is_callable($config)) $config = [$config]; $config = cl::merge(static::DEFAULT_CONFIG, $config); } $this->config = $config; # migrations $this->migration = $params["migration"] ?? static::MIGRATION; # $defaultAutoOpen = self::params_SCHEMA["auto_open"][1]; if ($params["auto_open"] ?? $defaultAutoOpen) { $this->open(); } } protected ?array $dbconn; /** @var array|callable */ protected $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)) { $options = func::with($options)->bind($this, true)->invoke(); } $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); } function exec($query, ?array $params=null) { $db = $this->db(); $query = new _pdoQuery($query, $params); if ($query->_use_stmt($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 ($query->isInsert()) return $db->lastInsertId(); else return $rowCount; } } /** @var ITransactor[] */ protected ?array $transactors = null; function willUpdate(...$transactors): self { foreach ($transactors as $transactor) { if ($transactor instanceof ITransactor) { $this->transactors[] = $transactor; $transactor->willUpdate(); } else { throw ValueException::invalid_type($transactor, ITransactor::class); } } return $this; } function inTransaction(): bool { return $this->db()->inTransaction(); } function beginTransaction(?callable $func=null, bool $commit=true): void { $this->db()->beginTransaction(); if ($this->transactors !== null) { foreach ($this->transactors as $transactor) { $transactor->beginTransaction(); } } if ($func !== null) { $commited = false; try { func::call($func, $this); if ($commit) { $this->commit(); $commited = true; } } finally { if ($commit && !$commited) $this->rollback(); } } } function commit(): void { $this->db()->commit(); if ($this->transactors !== null) { foreach ($this->transactors as $transactor) { $transactor->commit(); } } } function rollback(): void { $this->db()->rollBack(); if ($this->transactors !== null) { foreach ($this->transactors as $transactor) { $transactor->rollback(); } } } function get($query, ?array $params=null, bool $entireRow=false) { $db = $this->db(); $query = new _pdoQuery($query, $params); $stmt = null; try { /** @var \PDOStatement $stmt */ if ($query->_use_stmt($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); } function all($query, ?array $params=null, $primaryKeys=null): iterable { $db = $this->db(); $query = new _pdoQuery($query, $params); $stmt = null; try { /** @var \PDOStatement $stmt */ if ($query->_use_stmt($db, $stmt, $sql)) { if ($stmt->execute() === false) return; } else { $stmt = $db->query($sql); } $primaryKeys = cl::withn($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(); } } }