<pman>Intégration de la branche dev74
This commit is contained in:
commit
d75350d614
@ -39,6 +39,11 @@ class Capacitor implements ITransactor {
|
|||||||
return $this->getChannel()->getTableName();
|
return $this->getChannel()->getTableName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCreateSql(): string {
|
||||||
|
$channel = $this->channel;
|
||||||
|
return $this->storage->_getMigration($channel)->getSql(get_class($channel), $this->db());
|
||||||
|
}
|
||||||
|
|
||||||
/** @var CapacitorChannel[] */
|
/** @var CapacitorChannel[] */
|
||||||
protected ?array $subChannels = null;
|
protected ?array $subChannels = null;
|
||||||
|
|
||||||
|
@ -66,26 +66,47 @@ class CapacitorChannel implements ITransactor {
|
|||||||
$columnDefinitions = cl::withn(static::COLUMN_DEFINITIONS);
|
$columnDefinitions = cl::withn(static::COLUMN_DEFINITIONS);
|
||||||
$primaryKeys = cl::withn(static::PRIMARY_KEYS);
|
$primaryKeys = cl::withn(static::PRIMARY_KEYS);
|
||||||
$migration = cl::withn(static::MIGRATION);
|
$migration = cl::withn(static::MIGRATION);
|
||||||
|
$lastMkey = 1;
|
||||||
if ($columnDefinitions !== null) {
|
if ($columnDefinitions !== null) {
|
||||||
# mettre à jour la liste des clés primaires et des migrations
|
# mettre à jour la liste des clés primaires et des migrations
|
||||||
$index = 0;
|
$index = 0;
|
||||||
foreach ($columnDefinitions as $col => $def) {
|
foreach ($columnDefinitions as $col => $def) {
|
||||||
if ($col === $index) {
|
if ($col === $index) {
|
||||||
|
$index++;
|
||||||
|
if (is_array($def)) {
|
||||||
|
# tableau: c'est une migration
|
||||||
|
$mkey = null;
|
||||||
|
$mvalues = null;
|
||||||
|
$mdefs = $def;
|
||||||
|
$mindex = 0;
|
||||||
|
foreach ($mdefs as $mcol => $mdef) {
|
||||||
|
if ($mindex === 0 && $mcol === 0) {
|
||||||
|
$mindex++;
|
||||||
|
$mkey = $mdef;
|
||||||
|
} elseif ($mcol === $mindex) {
|
||||||
|
# si définition séquentielle, prendre la migration telle quelle
|
||||||
|
$mindex++;
|
||||||
|
$mvalues[] = $mdef;
|
||||||
|
} elseif ($mdef) {
|
||||||
|
# mise à jour d'une colonne
|
||||||
|
$mvalues[] = "alter table $tableName add column $mcol $mdef";
|
||||||
|
} else {
|
||||||
|
# suppression d'une colonne
|
||||||
|
$mvalues[] = "alter table $tableName drop column $mcol";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($mvalues !== null) {
|
||||||
|
if ($mkey === null) $mkey = $lastMkey++;
|
||||||
|
$migration[$mkey] = $mvalues;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
# si définition séquentielle, seules les définitions de clé
|
# si définition séquentielle, seules les définitions de clé
|
||||||
# primaires sont supportées
|
# primaires sont supportées
|
||||||
$index++;
|
|
||||||
if (preg_match('/\bprimary\s+key\s+\((.+)\)/i', $def, $ms)) {
|
if (preg_match('/\bprimary\s+key\s+\((.+)\)/i', $def, $ms)) {
|
||||||
$primaryKeys = preg_split('/\s*,\s*/', trim($ms[1]));
|
$primaryKeys = preg_split('/\s*,\s*/', trim($ms[1]));
|
||||||
}
|
}
|
||||||
} elseif (is_array($def)) {
|
|
||||||
# tableau: c'est une migration
|
|
||||||
$def = implode(" ", $def);
|
|
||||||
if ($def) {
|
|
||||||
$migration["add_$col"] = "alter table $tableName add column $col $def";
|
|
||||||
} else {
|
|
||||||
$migration["drop_$col"] = "alter table $tableName drop column $col";
|
|
||||||
}
|
}
|
||||||
} elseif (is_scalar($def)) {
|
} else {
|
||||||
# chaine: c'est une définition
|
# chaine: c'est une définition
|
||||||
$def = strval($def);
|
$def = strval($def);
|
||||||
if (preg_match('/\bprimary\s+key\b/i', $def)) {
|
if (preg_match('/\bprimary\s+key\b/i', $def)) {
|
||||||
|
@ -40,6 +40,16 @@ abstract class CapacitorStorage {
|
|||||||
const SERSUM_DEFINITION = "varchar(40)";
|
const SERSUM_DEFINITION = "varchar(40)";
|
||||||
const SERTS_DEFINITION = "datetime";
|
const SERTS_DEFINITION = "datetime";
|
||||||
|
|
||||||
|
protected static function sercol($def): string {
|
||||||
|
if (!is_string($def)) $def = strval($def);
|
||||||
|
switch ($def) {
|
||||||
|
case "serdata": $def = static::SERDATA_DEFINITION; break;
|
||||||
|
case "sersum": $def = static::SERSUM_DEFINITION; break;
|
||||||
|
case "serts": $def = static::SERTS_DEFINITION; break;
|
||||||
|
}
|
||||||
|
return $def;
|
||||||
|
}
|
||||||
|
|
||||||
const COLUMN_DEFINITIONS = [
|
const COLUMN_DEFINITIONS = [
|
||||||
"item__" => "serdata",
|
"item__" => "serdata",
|
||||||
"item__sum_" => "sersum",
|
"item__sum_" => "sersum",
|
||||||
@ -61,22 +71,29 @@ abstract class CapacitorStorage {
|
|||||||
$constraints = [];
|
$constraints = [];
|
||||||
$index = 0;
|
$index = 0;
|
||||||
foreach ($tmp as $col => $def) {
|
foreach ($tmp as $col => $def) {
|
||||||
switch ($def) {
|
|
||||||
case "serdata": $def = static::SERDATA_DEFINITION; break;
|
|
||||||
case "sersum": $def = static::SERSUM_DEFINITION; break;
|
|
||||||
case "serts": $def = static::SERTS_DEFINITION; break;
|
|
||||||
}
|
|
||||||
if ($col === $index) {
|
if ($col === $index) {
|
||||||
$index++;
|
$index++;
|
||||||
$constraints[] = $def;
|
if (is_array($def)) {
|
||||||
} elseif (is_array($def)) {
|
if (!$ignoreMigrations) {
|
||||||
# éventuellement, ignorer les migrations
|
$mdefs = $def;
|
||||||
$def = implode(" ", $def);
|
$mindex = 0;
|
||||||
if ($def && !$ignoreMigrations) {
|
foreach ($mdefs as $mcol => $mdef) {
|
||||||
$definitions[$col] = $def;
|
if ($mcol === $mindex) {
|
||||||
|
$mindex++;
|
||||||
|
} else {
|
||||||
|
if ($mdef) {
|
||||||
|
$definitions[$mcol] = self::sercol($mdef);
|
||||||
|
} else {
|
||||||
|
unset($definitions[$mcol]);
|
||||||
}
|
}
|
||||||
} elseif (is_scalar($def)) {
|
}
|
||||||
$definitions[$col] = strval($def);
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$constraints[] = $def;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$definitions[$col] = self::sercol($def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cl::merge($definitions, $constraints);
|
return cl::merge($definitions, $constraints);
|
||||||
@ -155,17 +172,6 @@ abstract class CapacitorStorage {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function format_sql(CapacitorChannel $channel, string $sql): string {
|
|
||||||
$class = get_class($channel);
|
|
||||||
return <<<EOT
|
|
||||||
-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
||||||
-- autogénéré à partir de $class
|
|
||||||
|
|
||||||
$sql;
|
|
||||||
|
|
||||||
EOT;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract protected function tableExists(string $tableName): bool;
|
abstract protected function tableExists(string $tableName): bool;
|
||||||
|
|
||||||
const METADATA_TABLE = "_metadata";
|
const METADATA_TABLE = "_metadata";
|
||||||
@ -183,7 +189,7 @@ EOT;
|
|||||||
]);
|
]);
|
||||||
$db->exec([
|
$db->exec([
|
||||||
"drop table if exists",
|
"drop table if exists",
|
||||||
"table" => _migration::MIGRATION_TABLE
|
"table" => _migration::MIGRATION_TABLE,
|
||||||
]);
|
]);
|
||||||
$db->exec([
|
$db->exec([
|
||||||
"create table",
|
"create table",
|
||||||
@ -388,7 +394,7 @@ EOT;
|
|||||||
if ($func !== null) {
|
if ($func !== null) {
|
||||||
$updates = func::with($func)
|
$updates = func::with($func)
|
||||||
->prependArgs([$item, $values, $pvalues])
|
->prependArgs([$item, $values, $pvalues])
|
||||||
->bind($channel, true)
|
->bind($channel)
|
||||||
->invoke();
|
->invoke();
|
||||||
if ($updates === [false]) return 0;
|
if ($updates === [false]) return 0;
|
||||||
if (is_array($updates) && $updates) {
|
if (is_array($updates) && $updates) {
|
||||||
@ -603,7 +609,7 @@ EOT;
|
|||||||
function _each(CapacitorChannel $channel, $filter, $func, ?array $args, ?array $mergeQuery=null, ?int &$nbUpdated=null): int {
|
function _each(CapacitorChannel $channel, $filter, $func, ?array $args, ?array $mergeQuery=null, ?int &$nbUpdated=null): int {
|
||||||
$this->_create($channel);
|
$this->_create($channel);
|
||||||
if ($func === null) $func = CapacitorChannel::onEach;
|
if ($func === null) $func = CapacitorChannel::onEach;
|
||||||
$onEach = func::with($func)->bind($channel, true);
|
$onEach = func::with($func)->bind($channel);
|
||||||
$db = $this->db();
|
$db = $this->db();
|
||||||
# si on est déjà dans une transaction, désactiver la gestion des transactions
|
# si on est déjà dans une transaction, désactiver la gestion des transactions
|
||||||
$manageTransactions = $channel->isManageTransactions() && !$db->inTransaction();
|
$manageTransactions = $channel->isManageTransactions() && !$db->inTransaction();
|
||||||
@ -671,7 +677,7 @@ EOT;
|
|||||||
function _delete(CapacitorChannel $channel, $filter, $func, ?array $args): int {
|
function _delete(CapacitorChannel $channel, $filter, $func, ?array $args): int {
|
||||||
$this->_create($channel);
|
$this->_create($channel);
|
||||||
if ($func === null) $func = CapacitorChannel::onDelete;
|
if ($func === null) $func = CapacitorChannel::onDelete;
|
||||||
$onEach = func::with($func)->bind($channel, true);
|
$onEach = func::with($func)->bind($channel);
|
||||||
$db = $this->db();
|
$db = $this->db();
|
||||||
# si on est déjà dans une transaction, désactiver la gestion des transactions
|
# si on est déjà dans une transaction, désactiver la gestion des transactions
|
||||||
$manageTransactions = $channel->isManageTransactions() && !$db->inTransaction();
|
$manageTransactions = $channel->isManageTransactions() && !$db->inTransaction();
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
namespace nulib\db;
|
namespace nulib\db;
|
||||||
|
|
||||||
interface IDatabase extends ITransactor {
|
interface IDatabase extends ITransactor {
|
||||||
|
/** obtenir la requête SQL correspondant à $query */
|
||||||
|
function getSql($query, ?array $params=null): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* - si c'est un insert, retourner l'identifiant autogénéré de la ligne
|
* - si c'est un insert, retourner l'identifiant autogénéré de la ligne
|
||||||
* - sinon retourner le nombre de lignes modifiées en cas de succès, ou false
|
* - sinon retourner le nombre de lignes modifiées en cas de succès, ou false
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nulib\db\_private;
|
namespace nulib\db\_private;
|
||||||
|
|
||||||
use nulib\cl;
|
|
||||||
use nulib\str;
|
|
||||||
use nulib\ValueException;
|
use nulib\ValueException;
|
||||||
|
|
||||||
abstract class _base extends _common {
|
abstract class _base extends _common {
|
||||||
|
@ -29,7 +29,7 @@ class _config {
|
|||||||
if (is_string($config) && !func::is_method($config)) {
|
if (is_string($config) && !func::is_method($config)) {
|
||||||
$db->exec($config);
|
$db->exec($config);
|
||||||
} else {
|
} else {
|
||||||
func::with($config)->bind($this, true)->invoke([$db, $key]);
|
func::with($config)->bind($this)->invoke([$db, $key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nulib\db\_private;
|
namespace nulib\db\_private;
|
||||||
|
|
||||||
use nulib\cl;
|
|
||||||
use nulib\str;
|
use nulib\str;
|
||||||
use nulib\ValueException;
|
|
||||||
|
|
||||||
class _generic extends _common {
|
class _generic extends _common {
|
||||||
const SCHEMA = [
|
const SCHEMA = [
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nulib\db\_private;
|
namespace nulib\db\_private;
|
||||||
|
|
||||||
|
use nulib\cl;
|
||||||
use nulib\db\IDatabase;
|
use nulib\db\IDatabase;
|
||||||
use nulib\php\func;
|
use nulib\php\func;
|
||||||
|
|
||||||
@ -61,12 +62,38 @@ abstract class _migration {
|
|||||||
foreach ($this->migrations as $name => $migration) {
|
foreach ($this->migrations as $name => $migration) {
|
||||||
if ($this->isMigrated($name)) continue;
|
if ($this->isMigrated($name)) continue;
|
||||||
$this->setMigrated($name, false);
|
$this->setMigrated($name, false);
|
||||||
if (is_string($migration) || !func::is_callable($migration)) {
|
if (func::is_callable($migration)) {
|
||||||
$db->exec($migration);
|
func::with($migration)->bind($this)->invoke([$db, $name]);
|
||||||
} else {
|
} else {
|
||||||
func::with($migration)->bind($this, true)->invoke([$db, $name]);
|
foreach (cl::with($migration) as $query) {
|
||||||
|
$db->exec($query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->setMigrated($name, true);
|
$this->setMigrated($name, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static function sql_prefix(?string $source=null): string {
|
||||||
|
$prefix = "-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8\n";
|
||||||
|
if ($source !== null) $prefix .= "-- autogénéré à partir de $source\n";
|
||||||
|
return $prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSql(?string $source=null, ?IDatabase $db=null): string {
|
||||||
|
$db = ($this->db ??= $db);
|
||||||
|
$lines = [self::sql_prefix($source)];
|
||||||
|
foreach ($this->migrations as $name => $migration) {
|
||||||
|
$lines[] = "-- $name";
|
||||||
|
if (func::is_callable($migration)) {
|
||||||
|
$lines[] = "-- <fonction PHP>";
|
||||||
|
} else {
|
||||||
|
foreach (cl::with($migration) as $query) {
|
||||||
|
$sql = $db->getSql($query);
|
||||||
|
$lines[] = "$sql;";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$lines[] = "";
|
||||||
|
}
|
||||||
|
return implode("\n", $lines);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,14 +41,10 @@ class MysqlStorage extends CapacitorStorage {
|
|||||||
];
|
];
|
||||||
|
|
||||||
function _getMigration(CapacitorChannel $channel): _mysqlMigration {
|
function _getMigration(CapacitorChannel $channel): _mysqlMigration {
|
||||||
return new _mysqlMigration(cl::merge([
|
$migrations = cl::merge([
|
||||||
$this->_createSql($channel),
|
"0init" => [$this->_createSql($channel)],
|
||||||
], $channel->getMigration()), $channel->getName());
|
], $channel->getMigration());
|
||||||
}
|
return new _mysqlMigration($migrations, $channel->getName());
|
||||||
|
|
||||||
function _getCreateSql(CapacitorChannel $channel): string {
|
|
||||||
$query = new _mysqlQuery($this->_createSql($channel));
|
|
||||||
return self::format_sql($channel, $query->getSql());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHANNELS_COLS = [
|
const CHANNELS_COLS = [
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nulib\db\pdo;
|
namespace nulib\db\pdo;
|
||||||
|
|
||||||
use Generator;
|
|
||||||
use nulib\cl;
|
use nulib\cl;
|
||||||
use nulib\db\_private\_config;
|
use nulib\db\_private\_config;
|
||||||
use nulib\db\_private\Tvalues;
|
use nulib\db\_private\Tvalues;
|
||||||
@ -115,12 +114,17 @@ class Pdo implements IDatabase {
|
|||||||
|
|
||||||
protected ?\PDO $db = null;
|
protected ?\PDO $db = null;
|
||||||
|
|
||||||
|
function getSql($query, ?array $params=null): string {
|
||||||
|
$query = new _pdoQuery($query, $params);
|
||||||
|
return $query->getSql();
|
||||||
|
}
|
||||||
|
|
||||||
function open(): self {
|
function open(): self {
|
||||||
if ($this->db === null) {
|
if ($this->db === null) {
|
||||||
$dbconn = $this->dbconn;
|
$dbconn = $this->dbconn;
|
||||||
$options = $this->options;
|
$options = $this->options;
|
||||||
if (is_callable($options)) {
|
if (is_callable($options)) {
|
||||||
$options = func::with($options)->bind($this, true)->invoke();
|
$options = func::with($options)->bind($this)->invoke();
|
||||||
}
|
}
|
||||||
$this->db = new \PDO($dbconn["name"], $dbconn["user"], $dbconn["pass"], $options);
|
$this->db = new \PDO($dbconn["name"], $dbconn["user"], $dbconn["pass"], $options);
|
||||||
_config::with($this->config)->configure($this);
|
_config::with($this->config)->configure($this);
|
||||||
|
@ -117,6 +117,11 @@ class Pgsql implements IDatabase {
|
|||||||
/** @var resource */
|
/** @var resource */
|
||||||
protected $db = null;
|
protected $db = null;
|
||||||
|
|
||||||
|
function getSql($query, ?array $params=null): string {
|
||||||
|
$query = new _pgsqlQuery($query, $params);
|
||||||
|
return $query->getSql();
|
||||||
|
}
|
||||||
|
|
||||||
function open(): self {
|
function open(): self {
|
||||||
if ($this->db === null) {
|
if ($this->db === null) {
|
||||||
$dbconn = $this->dbconn;
|
$dbconn = $this->dbconn;
|
||||||
@ -136,7 +141,7 @@ class Pgsql implements IDatabase {
|
|||||||
$connection_string = implode(" ", array_filter($connection_string));
|
$connection_string = implode(" ", array_filter($connection_string));
|
||||||
$options = $this->options;
|
$options = $this->options;
|
||||||
if (is_callable($options)) {
|
if (is_callable($options)) {
|
||||||
$options = func::with($options)->bind($this, true)->invoke();
|
$options = func::with($options)->bind($this)->invoke();
|
||||||
}
|
}
|
||||||
$forceNew = $options["force_new"] ?? false;
|
$forceNew = $options["force_new"] ?? false;
|
||||||
$flags = $forceNew? PGSQL_CONNECT_FORCE_NEW: 0;
|
$flags = $forceNew? PGSQL_CONNECT_FORCE_NEW: 0;
|
||||||
|
@ -3,7 +3,6 @@ namespace nulib\db\pgsql;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use SQLite3;
|
|
||||||
|
|
||||||
class PgsqlException extends RuntimeException {
|
class PgsqlException extends RuntimeException {
|
||||||
static final function last_error($db): self {
|
static final function last_error($db): self {
|
||||||
|
@ -42,14 +42,10 @@ class PgsqlStorage extends CapacitorStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _getMigration(CapacitorChannel $channel): _pgsqlMigration {
|
function _getMigration(CapacitorChannel $channel): _pgsqlMigration {
|
||||||
return new _pgsqlMigration(cl::merge([
|
$migrations = cl::merge([
|
||||||
$this->_createSql($channel),
|
"0init" => [$this->_createSql($channel)],
|
||||||
], $channel->getMigration()), $channel->getName());
|
], $channel->getMigration());
|
||||||
}
|
return new _pgsqlMigration($migrations, $channel->getName());
|
||||||
|
|
||||||
function _getCreateSql(CapacitorChannel $channel): string {
|
|
||||||
$query = new _pgsqlQuery($this->_createSql($channel));
|
|
||||||
return self::format_sql($channel, $query->getSql());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _addToChannelsSql(CapacitorChannel $channel): array {
|
protected function _addToChannelsSql(CapacitorChannel $channel): array {
|
||||||
|
@ -146,6 +146,11 @@ class Sqlite implements IDatabase {
|
|||||||
|
|
||||||
protected bool $inTransaction;
|
protected bool $inTransaction;
|
||||||
|
|
||||||
|
function getSql($query, ?array $params=null): string {
|
||||||
|
$query = new _sqliteQuery($query, $params);
|
||||||
|
return $query->getSql();
|
||||||
|
}
|
||||||
|
|
||||||
function open(): self {
|
function open(): self {
|
||||||
if ($this->db === null) {
|
if ($this->db === null) {
|
||||||
$this->db = new SQLite3($this->file, $this->flags, $this->encryptionKey);
|
$this->db = new SQLite3($this->file, $this->flags, $this->encryptionKey);
|
||||||
|
@ -34,14 +34,10 @@ class SqliteStorage extends CapacitorStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _getMigration(CapacitorChannel $channel): _sqliteMigration {
|
function _getMigration(CapacitorChannel $channel): _sqliteMigration {
|
||||||
return new _sqliteMigration(cl::merge([
|
$migrations = cl::merge([
|
||||||
$this->_createSql($channel),
|
"0init" => [$this->_createSql($channel)],
|
||||||
], $channel->getMigration()), $channel->getName());
|
], $channel->getMigration());
|
||||||
}
|
return new _sqliteMigration($migrations, $channel->getName());
|
||||||
|
|
||||||
function _getCreateSql(CapacitorChannel $channel): string {
|
|
||||||
$query = new _sqliteQuery($this->_createSql($channel));
|
|
||||||
return self::format_sql($channel, $query->getSql());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function channelExists(string $name): bool {
|
function channelExists(string $name): bool {
|
||||||
|
@ -2,15 +2,8 @@
|
|||||||
namespace nulib\db\sqlite;
|
namespace nulib\db\sqlite;
|
||||||
|
|
||||||
use nulib\db\_private\_base;
|
use nulib\db\_private\_base;
|
||||||
use nulib\db\_private\_create;
|
|
||||||
use nulib\db\_private\_delete;
|
|
||||||
use nulib\db\_private\_generic;
|
|
||||||
use nulib\db\_private\_insert;
|
|
||||||
use nulib\db\_private\_select;
|
|
||||||
use nulib\db\_private\_update;
|
|
||||||
use nulib\db\_private\Tbindings;
|
use nulib\db\_private\Tbindings;
|
||||||
use nulib\output\msg;
|
use nulib\output\msg;
|
||||||
use nulib\ValueException;
|
|
||||||
use SQLite3;
|
use SQLite3;
|
||||||
use SQLite3Stmt;
|
use SQLite3Stmt;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ abstract class AbstractBuilder extends TempStream implements IBuilder {
|
|||||||
$this->rows = $rows;
|
$this->rows = $rows;
|
||||||
$this->index = 0;
|
$this->index = 0;
|
||||||
$cookFunc = $params["cook_func"] ?? null;
|
$cookFunc = $params["cook_func"] ?? null;
|
||||||
if ($cookFunc !== null) $cookFunc = func::with($cookFunc)->bind($this, true);
|
if ($cookFunc !== null) $cookFunc = func::with($cookFunc)->bind($this);
|
||||||
$this->cookFunc = $cookFunc;
|
$this->cookFunc = $cookFunc;
|
||||||
$this->output = $params["output"] ?? static::OUTPUT;
|
$this->output = $params["output"] ?? static::OUTPUT;
|
||||||
$maxMemory = $params["max_memory"] ?? null;
|
$maxMemory = $params["max_memory"] ?? null;
|
||||||
|
@ -82,7 +82,7 @@ class c {
|
|||||||
$arg = self::resolve($arg, $object_or_class, false);
|
$arg = self::resolve($arg, $object_or_class, false);
|
||||||
if (!$array) $arg = $arg[0];
|
if (!$array) $arg = $arg[0];
|
||||||
}; unset($arg);
|
}; unset($arg);
|
||||||
$value = func::with($func, $args)->bind($object_or_class, true)->invoke();
|
$value = func::with($func, $args)->bind($object_or_class)->invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($seq) $dest[] = $value;
|
if ($seq) $dest[] = $value;
|
||||||
|
@ -3,7 +3,6 @@ namespace nulib\php;
|
|||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Generator;
|
|
||||||
use nulib\A;
|
use nulib\A;
|
||||||
use nulib\cl;
|
use nulib\cl;
|
||||||
use nulib\cv;
|
use nulib\cv;
|
||||||
@ -268,6 +267,8 @@ class func {
|
|||||||
$reason = "$c::$f: method not found";
|
$reason = "$c::$f: method not found";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
$method = new ReflectionMethod($c, $f);
|
||||||
|
if (!$method->isStatic()) return false;
|
||||||
} else {
|
} else {
|
||||||
$reason = "$c::$f: not bound";
|
$reason = "$c::$f: not bound";
|
||||||
}
|
}
|
||||||
@ -402,6 +403,8 @@ class func {
|
|||||||
$reason = "$c::$f: method not found";
|
$reason = "$c::$f: method not found";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
$method = new ReflectionMethod($c, $f);
|
||||||
|
if ($method->isStatic()) return false;
|
||||||
} else {
|
} else {
|
||||||
$reason = "$c::$f: not bound";
|
$reason = "$c::$f: not bound";
|
||||||
}
|
}
|
||||||
@ -649,9 +652,9 @@ class func {
|
|||||||
else return $this->bound && $this->object !== null;
|
else return $this->bound && $this->object !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function bind($object, bool $unlessAlreadyBound=false, bool $replace=false): self {
|
function bind($object, bool $rebind=false, bool $replace=false): self {
|
||||||
if ($this->type !== self::TYPE_METHOD) return $this;
|
if ($this->type !== self::TYPE_METHOD) return $this;
|
||||||
if ($this->bound && $unlessAlreadyBound) return $this;
|
if (!$rebind && $this->isBound()) return $this;
|
||||||
|
|
||||||
[$c, $f] = $this->func;
|
[$c, $f] = $this->func;
|
||||||
if ($replace) {
|
if ($replace) {
|
||||||
|
@ -268,6 +268,21 @@ class str {
|
|||||||
$s .= $prefix.$text.$suffix;
|
$s .= $prefix.$text.$suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dans $s, faire les remplacements $key => $value du tableau $replaces
|
||||||
|
*
|
||||||
|
* si $verifix_order, le tableau est réordonné par taille de chaine source
|
||||||
|
*/
|
||||||
|
static final function replace(?string $s, ?array $replaces, bool $verifix_order=true): ?string {
|
||||||
|
if ($s === null || $replaces === null) return $s;
|
||||||
|
if ($verifix_order) {
|
||||||
|
uksort($replaces, function ($a, $b) {
|
||||||
|
return -cv::compare(strlen($a), strlen($b));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return str_replace(array_keys($replaces), array_values($replaces), $s);
|
||||||
|
}
|
||||||
|
|
||||||
/** si $text est non vide, ajouter $prefix$text$suffix à $s en séparant la valeur avec un espace */
|
/** si $text est non vide, ajouter $prefix$text$suffix à $s en séparant la valeur avec un espace */
|
||||||
static final function add(?string &$s, ?string $text, ?string $prefix=null, ?string $suffix=null): void {
|
static final function add(?string &$s, ?string $text, ?string $prefix=null, ?string $suffix=null): void {
|
||||||
self::addsep($s, " ", $text, $prefix, $suffix);
|
self::addsep($s, " ", $text, $prefix, $suffix);
|
||||||
|
75
php/tests/db/sqlite/ChannelMigrationTest.php
Normal file
75
php/tests/db/sqlite/ChannelMigrationTest.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\db\sqlite;
|
||||||
|
|
||||||
|
use nulib\db\Capacitor;
|
||||||
|
use nulib\db\sqlite\impl\MyChannel;
|
||||||
|
use nulib\db\sqlite\impl\MyChannelV2;
|
||||||
|
use nulib\db\sqlite\impl\MyChannelV3;
|
||||||
|
use nulib\output\msg;
|
||||||
|
use nulib\output\std\StdMessenger;
|
||||||
|
use nulib\php\time\DateTime;
|
||||||
|
use nulib\tests\TestCase;
|
||||||
|
|
||||||
|
class ChannelMigrationTest extends TestCase {
|
||||||
|
static function setUpBeforeClass(): void {
|
||||||
|
parent::setUpBeforeClass();
|
||||||
|
msg::set_messenger_class(StdMessenger::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function addData(MyChannel $channel, array $data): void {
|
||||||
|
foreach ($data as [$name, $value, $dateCre, $dateMod, $age]) {
|
||||||
|
$channel->charge([
|
||||||
|
"name" => $name,
|
||||||
|
"value" => $value,
|
||||||
|
"date_cre" => $dateCre,
|
||||||
|
"date_mod" => $dateMod,
|
||||||
|
"age" => $age,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMigration() {
|
||||||
|
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
|
||||||
|
$data = [
|
||||||
|
["first", "premier", new DateTime(), new DateTime(), 15],
|
||||||
|
["second", "deuxieme", new DateTime(), new DateTime(), 15],
|
||||||
|
];
|
||||||
|
|
||||||
|
new Capacitor($storage, $channel = new MyChannel());
|
||||||
|
$channel->reset(true);
|
||||||
|
$this->addData($channel, $data);
|
||||||
|
|
||||||
|
new Capacitor($storage, $channel = new MyChannelV2());
|
||||||
|
$this->addData($channel, $data);
|
||||||
|
|
||||||
|
new Capacitor($storage, $channel = new MyChannelV3());
|
||||||
|
$this->addData($channel, $data);
|
||||||
|
|
||||||
|
$sql = $channel->getCapacitor()->getCreateSql();
|
||||||
|
$class = MyChannelV3::class;
|
||||||
|
$expected = <<<EOT
|
||||||
|
-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
||||||
|
-- autogénéré à partir de $class
|
||||||
|
|
||||||
|
-- 0init
|
||||||
|
create table if not exists my (
|
||||||
|
id_ integer primary key autoincrement
|
||||||
|
, name varchar
|
||||||
|
, value varchar
|
||||||
|
, item__ mediumtext
|
||||||
|
, item__sum_ varchar(40)
|
||||||
|
, created_ datetime
|
||||||
|
, modified_ datetime
|
||||||
|
);
|
||||||
|
|
||||||
|
-- dates
|
||||||
|
alter table my add column date_cre datetime;
|
||||||
|
alter table my add column date_mod datetime;
|
||||||
|
|
||||||
|
-- infos
|
||||||
|
alter table my add column age integer;
|
||||||
|
|
||||||
|
EOT;
|
||||||
|
self::assertSame($expected, $sql);
|
||||||
|
}
|
||||||
|
}
|
33
php/tests/db/sqlite/impl/MyChannel.php
Normal file
33
php/tests/db/sqlite/impl/MyChannel.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\db\sqlite\impl;
|
||||||
|
|
||||||
|
use nulib\db\CapacitorChannel;
|
||||||
|
|
||||||
|
class MyChannel extends CapacitorChannel {
|
||||||
|
const NAME = "my";
|
||||||
|
const TABLE_NAME = "my";
|
||||||
|
const COLUMN_DEFINITIONS = [
|
||||||
|
"name" => "varchar not null primary key",
|
||||||
|
"value" => "varchar",
|
||||||
|
];
|
||||||
|
|
||||||
|
const VERSION = 1;
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
$this->version = static::VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int $version;
|
||||||
|
|
||||||
|
function getItemValues($item): ?array {
|
||||||
|
|
||||||
|
return [
|
||||||
|
"name" => "{$item["name"]}$this->version",
|
||||||
|
"value" => "{$item["value"]} v$this->version",
|
||||||
|
"date_cre" => $item["date_cre"] ?? null,
|
||||||
|
"date_mod" => $item["date_mod"] ?? null,
|
||||||
|
"age" => $item["age"] ?? null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
14
php/tests/db/sqlite/impl/MyChannelV2.php
Normal file
14
php/tests/db/sqlite/impl/MyChannelV2.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\db\sqlite\impl;
|
||||||
|
|
||||||
|
class MyChannelV2 extends MyChannel {
|
||||||
|
const VERSION = 2;
|
||||||
|
const COLUMN_DEFINITIONS = [
|
||||||
|
"name" => "varchar",
|
||||||
|
"value" => "varchar",
|
||||||
|
["dates",
|
||||||
|
"date_cre" => "datetime",
|
||||||
|
"date_mod" => "datetime",
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
17
php/tests/db/sqlite/impl/MyChannelV3.php
Normal file
17
php/tests/db/sqlite/impl/MyChannelV3.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\db\sqlite\impl;
|
||||||
|
|
||||||
|
class MyChannelV3 extends MyChannel {
|
||||||
|
const VERSION = 3;
|
||||||
|
const COLUMN_DEFINITIONS = [
|
||||||
|
"name" => "varchar",
|
||||||
|
"value" => "varchar",
|
||||||
|
["dates",
|
||||||
|
"date_cre" => "datetime",
|
||||||
|
"date_mod" => "datetime",
|
||||||
|
],
|
||||||
|
["infos",
|
||||||
|
"age" => "integer",
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
@ -562,7 +562,7 @@ namespace nulib\php {
|
|||||||
true, true, [SC::class, "tstatic"],
|
true, true, [SC::class, "tstatic"],
|
||||||
],
|
],
|
||||||
[[SC::class, "tmethod"],
|
[[SC::class, "tmethod"],
|
||||||
true, true, [SC::class, "tmethod"],
|
false, true, [SC::class, "tmethod"],
|
||||||
true, true, [SC::class, "tmethod"],
|
true, true, [SC::class, "tmethod"],
|
||||||
],
|
],
|
||||||
[[SC::class, "->tmethod"],
|
[[SC::class, "->tmethod"],
|
||||||
@ -1102,15 +1102,25 @@ namespace nulib\php {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testRebind() {
|
function testRebind() {
|
||||||
|
# bind if not already bound
|
||||||
|
$func = func::with([C1::class, "tmethod"]);
|
||||||
|
// bind
|
||||||
|
self::assertSame(11, $func->bind(new C1(0))->invoke());
|
||||||
|
// pas de bind, puis que déjà bound
|
||||||
|
self::assertSame(11, $func->bind(new C1(1))->invoke());
|
||||||
|
// même si l'objet est de type différent, pas de bind
|
||||||
|
self::assertSame(11, $func->bind(new C0())->invoke());
|
||||||
|
|
||||||
|
# force rebind
|
||||||
$func = func::with([C1::class, "tmethod"]);
|
$func = func::with([C1::class, "tmethod"]);
|
||||||
// objets du même type
|
// objets du même type
|
||||||
self::assertSame(11, $func->bind(new C1(0))->invoke());
|
self::assertSame(11, $func->bind(new C1(0), true)->invoke());
|
||||||
self::assertSame(12, $func->bind(new C1(1))->invoke());
|
self::assertSame(12, $func->bind(new C1(1), true)->invoke());
|
||||||
// objets de type différent
|
// objets de type différent
|
||||||
self::assertException(ValueException::class, function() use ($func) {
|
self::assertException(ValueException::class, function() use ($func) {
|
||||||
$func->bind(new C0())->invoke();
|
$func->bind(new C0(), true)->invoke();
|
||||||
});
|
});
|
||||||
self::assertSame(11, $func->bind(new C0(), false, true)->invoke());
|
self::assertSame(11, $func->bind(new C0(), true, true)->invoke());
|
||||||
}
|
}
|
||||||
|
|
||||||
function testModifyArgs() {
|
function testModifyArgs() {
|
||||||
|
@ -5,7 +5,22 @@ namespace nulib;
|
|||||||
use nulib\tests\TestCase;
|
use nulib\tests\TestCase;
|
||||||
|
|
||||||
class strTest extends TestCase {
|
class strTest extends TestCase {
|
||||||
function testSplit_tokens() {
|
function test_replace() {
|
||||||
|
self::assertSame("premier deuxieme", str::replace("first second", [
|
||||||
|
"first" => "premier",
|
||||||
|
"second" => "deuxieme",
|
||||||
|
]));
|
||||||
|
self::assertSame("avant OK", str::replace("prefix prefixsuffix", [
|
||||||
|
"prefix" => "avant",
|
||||||
|
"prefixsuffix" => "OK",
|
||||||
|
]));
|
||||||
|
self::assertSame("avant avantsuffix", str::replace("prefix prefixsuffix", [
|
||||||
|
"prefix" => "avant",
|
||||||
|
"prefixsuffix" => "OK",
|
||||||
|
], false));
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_split_tokens() {
|
||||||
self::assertNull(str::split_tokens(null));
|
self::assertNull(str::split_tokens(null));
|
||||||
self::assertSame([], str::split_tokens(""));
|
self::assertSame([], str::split_tokens(""));
|
||||||
self::assertSame(["token"], str::split_tokens("token"));
|
self::assertSame(["token"], str::split_tokens("token"));
|
||||||
@ -13,7 +28,7 @@ class strTest extends TestCase {
|
|||||||
self::assertSame(["t", "u", "v", "w"], str::split_tokens("\nt\n\nu\r\nv\rw"));
|
self::assertSame(["t", "u", "v", "w"], str::split_tokens("\nt\n\nu\r\nv\rw"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testCamel2us() {
|
function test_camel2us() {
|
||||||
self::assertSame("a", str::camel2us("a"));
|
self::assertSame("a", str::camel2us("a"));
|
||||||
self::assertSame("aa", str::camel2us("aa"));
|
self::assertSame("aa", str::camel2us("aa"));
|
||||||
self::assertSame("aaa", str::camel2us("aaa"));
|
self::assertSame("aaa", str::camel2us("aaa"));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user