Compare commits
	
		
			5 Commits
		
	
	
		
			89f24d8d90
			...
			d75350d614
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d75350d614 | |||
| 3ee92ef338 | |||
| 737e9d17b6 | |||
| 37354525ec | |||
| 9767028da6 | 
@ -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) {
 | 
				
			||||||
          # si définition séquentielle, seules les définitions de clé
 | 
					 | 
				
			||||||
          # primaires sont supportées
 | 
					 | 
				
			||||||
          $index++;
 | 
					          $index++;
 | 
				
			||||||
          if (preg_match('/\bprimary\s+key\s+\((.+)\)/i', $def, $ms)) {
 | 
					          if (is_array($def)) {
 | 
				
			||||||
            $primaryKeys = preg_split('/\s*,\s*/', trim($ms[1]));
 | 
					            # tableau: c'est une migration
 | 
				
			||||||
          }
 | 
					            $mkey = null;
 | 
				
			||||||
        } elseif (is_array($def)) {
 | 
					            $mvalues = null;
 | 
				
			||||||
          # tableau: c'est une migration
 | 
					            $mdefs = $def;
 | 
				
			||||||
          $def = implode(" ", $def);
 | 
					            $mindex = 0;
 | 
				
			||||||
          if ($def) {
 | 
					            foreach ($mdefs as $mcol => $mdef) {
 | 
				
			||||||
            $migration["add_$col"] = "alter table $tableName add column $col $def";
 | 
					              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 {
 | 
					          } else {
 | 
				
			||||||
            $migration["drop_$col"] = "alter table $tableName drop column $col";
 | 
					            # si définition séquentielle, seules les définitions de clé
 | 
				
			||||||
 | 
					            # primaires sont supportées
 | 
				
			||||||
 | 
					            if (preg_match('/\bprimary\s+key\s+\((.+)\)/i', $def, $ms)) {
 | 
				
			||||||
 | 
					              $primaryKeys = preg_split('/\s*,\s*/', trim($ms[1]));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } 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]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          $constraints[] = $def;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } elseif (is_scalar($def)) {
 | 
					      } else {
 | 
				
			||||||
        $definitions[$col] = strval($def);
 | 
					        $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"));
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user