modifs.mineures sans commentaires
This commit is contained in:
		
							parent
							
								
									ee80fad847
								
							
						
					
					
						commit
						f431ce248a
					
				| @ -15,10 +15,15 @@ use nur\sery\file\Stream; | ||||
| use nur\yaml; | ||||
| 
 | ||||
| Application::run(new class extends Application { | ||||
|   const ACTION_QUERY = 0, ACTION_SQL = 1; | ||||
| 
 | ||||
|   const ARGS = [ | ||||
|     "merge" => parent::ARGS, | ||||
|     "purpose" => "gestion d'un capacitor mysql", | ||||
|     "usage" => "-d DBCONN -c CHANNEL key=value...", | ||||
|     "usage" => [ | ||||
|       "-d DBCONN -c CHANNEL [--query] key=value...", | ||||
|       "-d DBCONN -c CHANNEL --sql-create", | ||||
|     ], | ||||
|     ["-d", "--dbconn", "args" => 1, | ||||
|       "help" => "nom de la connexion à la base de données", | ||||
|     ], | ||||
| @ -28,6 +33,12 @@ Application::run(new class extends Application { | ||||
|     ["-c", "--channel-class", "args" => 1, | ||||
|       "help" => "nom de la classe dérivée de CapacitorChannel", | ||||
|     ], | ||||
|     ["--query", "name" => "action", "value" => self::ACTION_QUERY, | ||||
|       "help" => "lister les lignes correspondant aux valeurs spécifiées. c'est l'action par défaut", | ||||
|     ], | ||||
|     ["-s", "--sql-create", "name" => "action", "value" => self::ACTION_SQL, | ||||
|       "help" => "afficher la requête pour créer la table", | ||||
|     ], | ||||
|   ]; | ||||
| 
 | ||||
|   protected ?string $dbconn = null; | ||||
| @ -36,6 +47,8 @@ Application::run(new class extends Application { | ||||
| 
 | ||||
|   protected ?string $channelClass = null; | ||||
| 
 | ||||
|   protected int $action = self::ACTION_QUERY; | ||||
| 
 | ||||
|   protected ?array $args = null; | ||||
| 
 | ||||
|   protected static function isa_cond(string $arg, ?array &$ms=null): bool { | ||||
| @ -66,42 +79,49 @@ Application::run(new class extends Application { | ||||
|     $storage = new MysqlStorage($dbconn); | ||||
|     $capacitor = new Capacitor($storage, $channel); | ||||
| 
 | ||||
|     $args = $this->args; | ||||
|     if (!$args) { | ||||
|       # lister les id
 | ||||
|       $out = new Stream(STDOUT); | ||||
|       $primaryKeys = $storage->getPrimaryKeys($channel); | ||||
|       $rows = $storage->db()->all([ | ||||
|         "select", | ||||
|         "cols" => $primaryKeys, | ||||
|         "from" => $channel->getTableName(), | ||||
|       ]); | ||||
|       $out->fputcsv($primaryKeys); | ||||
|       foreach ($rows as $row) { | ||||
|         $rowIds = $storage->getRowIds($channel, $row); | ||||
|         $out->fputcsv($rowIds); | ||||
|       } | ||||
|     } else { | ||||
|       # afficher les lignes correspondantes
 | ||||
|       if (count($args) == 1 && !self::isa_cond($args[0])) { | ||||
|         $filter = $args[0]; | ||||
|     switch ($this->action) { | ||||
|     case self::ACTION_QUERY: | ||||
|       $args = $this->args; | ||||
|       if (!$args) { | ||||
|         # lister les id
 | ||||
|         $out = new Stream(STDOUT); | ||||
|         $primaryKeys = $storage->getPrimaryKeys($channel); | ||||
|         $rows = $storage->db()->all([ | ||||
|           "select", | ||||
|           "cols" => $primaryKeys, | ||||
|           "from" => $channel->getTableName(), | ||||
|         ]); | ||||
|         $out->fputcsv($primaryKeys); | ||||
|         foreach ($rows as $row) { | ||||
|           $rowIds = $storage->getRowIds($channel, $row); | ||||
|           $out->fputcsv($rowIds); | ||||
|         } | ||||
|       } else { | ||||
|         $filter = []; | ||||
|         $ms = null; | ||||
|         foreach ($args as $arg) { | ||||
|           if (self::isa_cond($arg, $ms)) { | ||||
|             $filter[$ms[1]] = [$ms[2], $ms[3]]; | ||||
|           } else { | ||||
|             $filter[$arg] = ["not null"]; | ||||
|         # afficher les lignes correspondantes
 | ||||
|         if (count($args) == 1 && !self::isa_cond($args[0])) { | ||||
|           $filter = $args[0]; | ||||
|         } else { | ||||
|           $filter = []; | ||||
|           $ms = null; | ||||
|           foreach ($args as $arg) { | ||||
|             if (self::isa_cond($arg, $ms)) { | ||||
|               $filter[$ms[1]] = [$ms[2], $ms[3]]; | ||||
|             } else { | ||||
|               $filter[$arg] = ["not null"]; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         $first = true; | ||||
|         $capacitor->each($filter, function ($item, $row) use (&$first) { | ||||
|           if ($first) $first = false; | ||||
|           else echo "---\n"; | ||||
|           yaml::dump($row); | ||||
|         }); | ||||
|       } | ||||
|       $first = true; | ||||
|       $capacitor->each($filter, function ($item, $row) use (&$first) { | ||||
|         if ($first) $first = false; | ||||
|         else echo "---\n"; | ||||
|         yaml::dump($row); | ||||
|       }); | ||||
|       break; | ||||
|     case self::ACTION_SQL: | ||||
|       echo $capacitor->getCreateSql()."\n"; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| }); | ||||
|  | ||||
| @ -13,10 +13,15 @@ use nur\sery\file\Stream; | ||||
| use nur\yaml; | ||||
| 
 | ||||
| Application::run(new class extends Application { | ||||
|   const ACTION_QUERY = 0, ACTION_SQL = 1; | ||||
| 
 | ||||
|   const ARGS = [ | ||||
|     "merge" => parent::ARGS, | ||||
|     "purpose" => "gestion d'un capacitor sqlite", | ||||
|     "usage" => "-f DBFILE -c CHANNEL key=value...", | ||||
|     "usage" => [ | ||||
|       "-f DBFILE -c CHANNEL [--query] key=value...", | ||||
|       "-f DBFILE -c CHANNEL --sql-create", | ||||
|     ], | ||||
|     ["-f", "--dbfile", "args" => 1, | ||||
|       "help" => "chemin vers la base de données", | ||||
|     ], | ||||
| @ -26,6 +31,12 @@ Application::run(new class extends Application { | ||||
|     ["-c", "--channel-class", "args" => 1, | ||||
|       "help" => "nom de la classe dérivée de CapacitorChannel", | ||||
|     ], | ||||
|     ["--query", "name" => "action", "value" => self::ACTION_QUERY, | ||||
|       "help" => "lister les lignes correspondant aux valeurs spécifiées. c'est l'action par défaut", | ||||
|     ], | ||||
|     ["-s", "--sql-create", "name" => "action", "value" => self::ACTION_SQL, | ||||
|       "help" => "afficher la requête pour créer la table", | ||||
|     ], | ||||
|   ]; | ||||
| 
 | ||||
|   protected ?string $dbfile = null; | ||||
| @ -34,6 +45,8 @@ Application::run(new class extends Application { | ||||
| 
 | ||||
|   protected ?string $channelClass = null; | ||||
| 
 | ||||
|   protected int $action = self::ACTION_QUERY; | ||||
| 
 | ||||
|   protected ?array $args = null; | ||||
| 
 | ||||
|   protected static function isa_cond(string $arg, ?array &$ms=null): bool { | ||||
| @ -62,42 +75,49 @@ Application::run(new class extends Application { | ||||
|     $storage = new SqliteStorage($dbfile); | ||||
|     $capacitor = new Capacitor($storage, $channel); | ||||
| 
 | ||||
|     $args = $this->args; | ||||
|     if (!$args) { | ||||
|       # lister les id
 | ||||
|       $out = new Stream(STDOUT); | ||||
|       $primaryKeys = $storage->getPrimaryKeys($channel); | ||||
|       $rows = $storage->db()->all([ | ||||
|         "select", | ||||
|         "cols" => $primaryKeys, | ||||
|         "from" => $channel->getTableName(), | ||||
|       ]); | ||||
|       $out->fputcsv($primaryKeys); | ||||
|       foreach ($rows as $row) { | ||||
|         $rowIds = $storage->getRowIds($channel, $row); | ||||
|         $out->fputcsv($rowIds); | ||||
|       } | ||||
|     } else { | ||||
|       # afficher les lignes correspondantes
 | ||||
|       if (count($args) == 1 && !self::isa_cond($args[0])) { | ||||
|         $filter = $args[0]; | ||||
|     switch ($this->action) { | ||||
|     case self::ACTION_QUERY: | ||||
|       $args = $this->args; | ||||
|       if (!$args) { | ||||
|         # lister les id
 | ||||
|         $out = new Stream(STDOUT); | ||||
|         $primaryKeys = $storage->getPrimaryKeys($channel); | ||||
|         $rows = $storage->db()->all([ | ||||
|           "select", | ||||
|           "cols" => $primaryKeys, | ||||
|           "from" => $channel->getTableName(), | ||||
|         ]); | ||||
|         $out->fputcsv($primaryKeys); | ||||
|         foreach ($rows as $row) { | ||||
|           $rowIds = $storage->getRowIds($channel, $row); | ||||
|           $out->fputcsv($rowIds); | ||||
|         } | ||||
|       } else { | ||||
|         $filter = []; | ||||
|         $ms = null; | ||||
|         foreach ($args as $arg) { | ||||
|           if (self::isa_cond($arg, $ms)) { | ||||
|             $filter[$ms[1]] = [$ms[2], $ms[3]]; | ||||
|           } else { | ||||
|             $filter[$arg] = ["not null"]; | ||||
|         # afficher les lignes correspondantes
 | ||||
|         if (count($args) == 1 && !self::isa_cond($args[0])) { | ||||
|           $filter = $args[0]; | ||||
|         } else { | ||||
|           $filter = []; | ||||
|           $ms = null; | ||||
|           foreach ($args as $arg) { | ||||
|             if (self::isa_cond($arg, $ms)) { | ||||
|               $filter[$ms[1]] = [$ms[2], $ms[3]]; | ||||
|             } else { | ||||
|               $filter[$arg] = ["not null"]; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         $first = true; | ||||
|         $capacitor->each($filter, function ($item, $row) use (&$first) { | ||||
|           if ($first) $first = false; | ||||
|           else echo "---\n"; | ||||
|           yaml::dump($row); | ||||
|         }); | ||||
|       } | ||||
|       $first = true; | ||||
|       $capacitor->each($filter, function ($item, $row) use (&$first) { | ||||
|         if ($first) $first = false; | ||||
|         else echo "---\n"; | ||||
|         yaml::dump($row); | ||||
|       }); | ||||
|       break; | ||||
|     case self::ACTION_SQL: | ||||
|       echo $capacitor->getCreateSql()."\n"; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| }); | ||||
|  | ||||
| @ -34,6 +34,10 @@ class Capacitor { | ||||
|     return $this->getChannel()->getTableName(); | ||||
|   } | ||||
| 
 | ||||
|   function getCreateSql(): string { | ||||
|     return $this->storage->_getCreateSql($this->channel); | ||||
|   } | ||||
| 
 | ||||
|   function exists(): bool { | ||||
|     return $this->storage->_exists($this->channel); | ||||
|   } | ||||
| @ -42,8 +46,8 @@ class Capacitor { | ||||
|     $this->storage->_ensureExists($this->channel); | ||||
|   } | ||||
| 
 | ||||
|   function reset(bool $ensureExists=false): void { | ||||
|     $this->storage->_reset($this->channel, $ensureExists); | ||||
|   function reset(bool $recreate=false): void { | ||||
|     $this->storage->_reset($this->channel, $recreate); | ||||
|   } | ||||
| 
 | ||||
|   function charge($item, $func=null, ?array $args=null): int { | ||||
|  | ||||
| @ -27,6 +27,7 @@ class CapacitorChannel { | ||||
|     $this->name = self::verifix_name($name ?? static::NAME); | ||||
|     $this->tableName = static::TABLE_NAME ?? ($this->name."_channel"); | ||||
|     $this->eachCommitThreshold = $eachCommitThreshold ?? static::EACH_COMMIT_THRESHOLD; | ||||
|     $this->setup = false; | ||||
|     $this->created = false; | ||||
|     $columnDefinitions = cl::withn(static::COLUMN_DEFINITIONS); | ||||
|     $primaryKeys = cl::withn(static::PRIMARY_KEYS); | ||||
| @ -73,6 +74,21 @@ class CapacitorChannel { | ||||
|     return $this->eachCommitThreshold; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * initialiser ce channel avant sa première utilisation. | ||||
|    */ | ||||
|   protected function setup(): void { | ||||
|   } | ||||
| 
 | ||||
|   protected $setup; | ||||
| 
 | ||||
|   function ensureSetup() { | ||||
|     if (!$this->setup) { | ||||
|       $this->setup(); | ||||
|       $this->setup = true; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   protected $created; | ||||
| 
 | ||||
|   function isCreated(): bool { | ||||
|  | ||||
| @ -113,6 +113,39 @@ abstract class CapacitorStorage { | ||||
|     else return $rowIds; | ||||
|   } | ||||
| 
 | ||||
|   protected function _createSql(CapacitorChannel $channel): array { | ||||
|     $cols = $this->ColumnDefinitions($channel); | ||||
|     return [ | ||||
|       "create table if not exists", | ||||
|       "table" => $channel->getTableName(), | ||||
|       "cols" => $cols, | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   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 function _getCreateSql(CapacitorChannel $channel): string; | ||||
| 
 | ||||
|   /** obtenir la requête SQL utilisée pour créer la table */ | ||||
|   function getCreateSql(?string $channel): string { | ||||
|     return $this->_getCreateSql($this->getChannel($channel)); | ||||
|   } | ||||
| 
 | ||||
|   protected function _create(CapacitorChannel $channel): void { | ||||
|     $channel->ensureSetup(); | ||||
|     if (!$channel->isCreated()) { | ||||
|       $this->db->exec($this->_createSql($channel)); | ||||
|       $channel->setCreated(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   abstract function _exists(CapacitorChannel $channel): bool; | ||||
| 
 | ||||
|   /** tester si le canal spécifié existe */ | ||||
| @ -120,18 +153,27 @@ abstract class CapacitorStorage { | ||||
|     return $this->_exists($this->getChannel($channel)); | ||||
|   } | ||||
| 
 | ||||
|   abstract function _ensureExists(CapacitorChannel $channel): void; | ||||
|   function _ensureExists(CapacitorChannel $channel): void { | ||||
|     $this->_create($channel); | ||||
|   } | ||||
| 
 | ||||
|   /** s'assurer que le canal spécifié existe */ | ||||
|   function ensureExists(?string $channel): void { | ||||
|     $this->_ensureExists($this->getChannel($channel)); | ||||
|   } | ||||
| 
 | ||||
|   abstract function _reset(CapacitorChannel $channel, bool $ensureExists=false): void; | ||||
|   function _reset(CapacitorChannel $channel, bool $recreate=false): void { | ||||
|     $this->db->exec([ | ||||
|       "drop table if exists", | ||||
|       $channel->getTableName(), | ||||
|     ]); | ||||
|     $channel->setCreated(false); | ||||
|     if ($recreate) $this->_ensureExists($channel); | ||||
|   } | ||||
| 
 | ||||
|   /** supprimer le canal spécifié */ | ||||
|   function reset(?string $channel, bool $ensureExists=false): void { | ||||
|     $this->_reset($this->getChannel($channel), $ensureExists); | ||||
|   function reset(?string $channel, bool $recreate=false): void { | ||||
|     $this->_reset($this->getChannel($channel), $recreate); | ||||
|   } | ||||
| 
 | ||||
|   function _charge(CapacitorChannel $channel, $item, $func, ?array $args): int { | ||||
| @ -228,6 +270,7 @@ abstract class CapacitorStorage { | ||||
|   } | ||||
| 
 | ||||
|   function _discharge(CapacitorChannel $channel, bool $reset=true): iterable { | ||||
|     $this->_create($channel); | ||||
|     $rows = $this->db()->all([ | ||||
|       "select item__", | ||||
|       "from" => $channel->getTableName(), | ||||
| @ -253,6 +296,7 @@ abstract class CapacitorStorage { | ||||
|   } | ||||
| 
 | ||||
|   function _count(CapacitorChannel $channel, $filter): int { | ||||
|     $this->_create($channel); | ||||
|     $this->verifixFilter($channel, $filter); | ||||
|     return $this->db()->get([ | ||||
|       "select count(*)", | ||||
| @ -268,6 +312,7 @@ abstract class CapacitorStorage { | ||||
| 
 | ||||
|   function _one(CapacitorChannel $channel, $filter): ?array { | ||||
|     if ($filter === null) throw ValueException::null("filter"); | ||||
|     $this->_create($channel); | ||||
|     $this->verifixFilter($channel, $filter); | ||||
|     $row = $this->db()->one([ | ||||
|       "select", | ||||
| @ -287,6 +332,7 @@ abstract class CapacitorStorage { | ||||
|   } | ||||
| 
 | ||||
|   function _all(CapacitorChannel $channel, $filter): iterable { | ||||
|     $this->_create($channel); | ||||
|     $this->verifixFilter($channel, $filter); | ||||
|     $rows = $this->db()->all([ | ||||
|       "select", | ||||
| @ -308,6 +354,7 @@ abstract class CapacitorStorage { | ||||
|   } | ||||
| 
 | ||||
|   function _each(CapacitorChannel $channel, $filter, $func, ?array $args): int { | ||||
|     $this->_create($channel); | ||||
|     if ($func === null) $func = "->onEach"; | ||||
|     func::ensure_func($func, $channel, $args); | ||||
|     $onEach = func::_prepare($func); | ||||
| @ -367,6 +414,7 @@ abstract class CapacitorStorage { | ||||
|   } | ||||
| 
 | ||||
|   function _delete(CapacitorChannel $channel, $filter, $func, ?array $args): int { | ||||
|     $this->_create($channel); | ||||
|     if ($func === null) $func = "->onDelete"; | ||||
|     func::ensure_func($func, $channel, $args); | ||||
|     $onEach = func::_prepare($func); | ||||
|  | ||||
| @ -28,7 +28,7 @@ trait Tcreate { | ||||
|         $definition = "$col $definition"; | ||||
|       } | ||||
|     }; unset($definition); | ||||
|     $sql[] = "(".implode(", ", $cols).")"; | ||||
|     $sql[] = "(\n  ".implode("\n, ", $cols).")"; | ||||
| 
 | ||||
|     ## suffixe
 | ||||
|     if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix; | ||||
|  | ||||
| @ -242,9 +242,17 @@ abstract class _base { | ||||
|   /** @var string */ | ||||
|   protected $sql; | ||||
| 
 | ||||
|   function getSql(): string { | ||||
|     return $this->sql; | ||||
|   } | ||||
| 
 | ||||
|   /** @var ?array */ | ||||
|   protected $bindings; | ||||
| 
 | ||||
|   function getBindings(): ?array { | ||||
|     return $this->bindings; | ||||
|   } | ||||
| 
 | ||||
|   /** @var ?array */ | ||||
|   protected $meta; | ||||
| } | ||||
|  | ||||
| @ -1,11 +1,8 @@ | ||||
| <?php | ||||
| namespace nur\sery\db\mysql; | ||||
| 
 | ||||
| use nur\sery\cl; | ||||
| use nur\sery\db\CapacitorChannel; | ||||
| use nur\sery\db\CapacitorStorage; | ||||
| use nur\sery\php\func; | ||||
| use nur\sery\ValueException; | ||||
| 
 | ||||
| /** | ||||
|  * Class MysqlStorage | ||||
| @ -26,16 +23,9 @@ class MysqlStorage extends CapacitorStorage { | ||||
|     "id_" => "integer primary key auto_increment", | ||||
|   ]; | ||||
| 
 | ||||
|   protected function _create(CapacitorChannel $channel): void { | ||||
|     if (!$channel->isCreated()) { | ||||
|       $cols = $this->ColumnDefinitions($channel); | ||||
|       $this->db->exec([ | ||||
|         "create table if not exists", | ||||
|         "table" => $channel->getTableName(), | ||||
|         "cols" => $cols, | ||||
|       ]); | ||||
|       $channel->setCreated(); | ||||
|     } | ||||
|   function _getCreateSql(CapacitorChannel $channel): string { | ||||
|     $query = new _query_base($this->_createSql($channel)); | ||||
|     return self::format_sql($channel, $query->getSql()); | ||||
|   } | ||||
| 
 | ||||
|   function _exists(CapacitorChannel $channel): bool { | ||||
| @ -50,19 +40,6 @@ class MysqlStorage extends CapacitorStorage { | ||||
|     return $tableName !== null; | ||||
|   } | ||||
| 
 | ||||
|   function _ensureExists(CapacitorChannel $channel): void { | ||||
|     $this->_create($channel); | ||||
|   } | ||||
| 
 | ||||
|   function _reset(CapacitorChannel $channel, bool $ensureExists=false): void { | ||||
|     $this->db->exec([ | ||||
|       "drop table if exists", | ||||
|       $channel->getTableName(), | ||||
|     ]); | ||||
|     $channel->setCreated(false); | ||||
|     if ($ensureExists) $this->_ensureExists($channel); | ||||
|   } | ||||
| 
 | ||||
|   function close(): void { | ||||
|     $this->db->close(); | ||||
|   } | ||||
|  | ||||
| @ -1,11 +1,8 @@ | ||||
| <?php | ||||
| namespace nur\sery\db\sqlite; | ||||
| 
 | ||||
| use nur\sery\cl; | ||||
| use nur\sery\db\CapacitorChannel; | ||||
| use nur\sery\db\CapacitorStorage; | ||||
| use nur\sery\php\func; | ||||
| use nur\sery\ValueException; | ||||
| 
 | ||||
| /** | ||||
|  * Class SqliteStorage | ||||
| @ -26,16 +23,9 @@ class SqliteStorage extends CapacitorStorage { | ||||
|     "id_" => "integer primary key autoincrement", | ||||
|   ]; | ||||
| 
 | ||||
|   protected function _create(CapacitorChannel $channel): void { | ||||
|     if (!$channel->isCreated()) { | ||||
|       $cols = $this->ColumnDefinitions($channel); | ||||
|       $this->db->exec([ | ||||
|         "create table if not exists", | ||||
|         "table" => $channel->getTableName(), | ||||
|         "cols" => $cols, | ||||
|       ]); | ||||
|       $channel->setCreated(); | ||||
|     } | ||||
|   function _getCreateSql(CapacitorChannel $channel): string { | ||||
|     $query = new _query_base($this->_createSql($channel)); | ||||
|     return self::format_sql($channel, $query->getSql()); | ||||
|   } | ||||
| 
 | ||||
|   function _exists(CapacitorChannel $channel): bool { | ||||
| @ -48,19 +38,6 @@ class SqliteStorage extends CapacitorStorage { | ||||
|     return $tableName !== null; | ||||
|   } | ||||
| 
 | ||||
|   function _ensureExists(CapacitorChannel $channel): void { | ||||
|     $this->_create($channel); | ||||
|   } | ||||
| 
 | ||||
|   function _reset(CapacitorChannel $channel, bool $ensureExists=false): void { | ||||
|     $this->db->exec([ | ||||
|       "drop table if exists", | ||||
|       $channel->getTableName(), | ||||
|     ]); | ||||
|     $channel->setCreated(false); | ||||
|     if ($ensureExists) $this->_ensureExists($channel); | ||||
|   } | ||||
| 
 | ||||
|   function close(): void { | ||||
|     $this->db->close(); | ||||
|   } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user