diff --git a/.idea/nulib-base.iml b/.idea/nulib-base.iml
index c88c3c8..55df4ce 100644
--- a/.idea/nulib-base.iml
+++ b/.idea/nulib-base.iml
@@ -4,6 +4,7 @@
+
diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml
new file mode 100644
index 0000000..4b2e111
--- /dev/null
+++ b/.idea/php-test-framework.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/php.xml b/.idea/php.xml
index aced70d..7e6be21 100644
--- a/.idea/php.xml
+++ b/.idea/php.xml
@@ -1,5 +1,10 @@
+
+
+
+
+
@@ -10,6 +15,11 @@
+
+
+
+
+
@@ -55,7 +65,7 @@
-
+
diff --git a/.idea/phpunit.xml b/.idea/phpunit.xml
new file mode 100644
index 0000000..4f8104c
--- /dev/null
+++ b/.idea/phpunit.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.udir b/.udir
index f0bc74b..1f19bde 100644
--- a/.udir
+++ b/.udir
@@ -9,7 +9,7 @@ uinc_options=()
uinc_args=()
preconfig_scripts=()
configure_variables=(dest)
-configure_dest_for=(lib/profile.d/nulib-base)
+configure_dest_for=(lib/profile.d/nulib)
config_scripts=(lib/uinst/conf)
install_profiles=true
profiledir=lib/profile.d
diff --git a/README.md b/README.md
index 642d838..40f800a 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,8 @@ echo "commit=$commit"
# reprendre la valeur affichée par la précédente commande
commit=XXX
+pu
+
git checkout dev74
git cherry-pick "$commit"
diff --git a/php/src/db/Capacitor.php b/php/src/db/Capacitor.php
index 5551f1b..4b3eb89 100644
--- a/php/src/db/Capacitor.php
+++ b/php/src/db/Capacitor.php
@@ -138,9 +138,9 @@ class Capacitor implements ITransactor {
$this->storage->_reset($this->channel, $recreate);
}
- function charge($item, $func=null, ?array $args=null, ?array &$values=null): int {
+ function charge($item, $func=null, ?array $args=null, ?array &$row=null): int {
if ($this->subChannels !== null) $this->beginTransaction();
- return $this->storage->_charge($this->channel, $item, $func, $args, $values);
+ return $this->storage->_charge($this->channel, $item, $func, $args, $row);
}
function discharge(bool $reset=true): Traversable {
diff --git a/php/src/db/CapacitorChannel.php b/php/src/db/CapacitorChannel.php
index 4349e4b..8e2d089 100644
--- a/php/src/db/CapacitorChannel.php
+++ b/php/src/db/CapacitorChannel.php
@@ -25,8 +25,6 @@ class CapacitorChannel implements ITransactor {
const EACH_COMMIT_THRESHOLD = 100;
- const USE_CACHE = false;
-
static function verifix_name(?string &$name, ?string &$tableName=null): void {
if ($name !== null) {
$name = strtolower($name);
@@ -60,7 +58,6 @@ class CapacitorChannel implements ITransactor {
$this->tableName = $tableName;
$this->manageTransactions = $manageTransactions ?? static::MANAGE_TRANSACTIONS;
$this->eachCommitThreshold = self::verifix_eachCommitThreshold($eachCommitThreshold);
- $this->useCache = static::USE_CACHE;
$this->setup = false;
$this->created = false;
$columnDefinitions = $this->COLUMN_DEFINITIONS();
@@ -111,6 +108,8 @@ class CapacitorChannel implements ITransactor {
$def = strval($def);
if (preg_match('/\bprimary\s+key\b/i', $def)) {
$primaryKeys[] = $col;
+ } elseif ($def === "genserial") {
+ $primaryKeys[] = $col;
}
}
}
@@ -166,23 +165,6 @@ class CapacitorChannel implements ITransactor {
return $this;
}
- /**
- * @var bool faut-il passer par le cache pour les requêtes de all(), each()
- * et delete()?
- * ça peut être nécessaire avec MySQL/MariaDB si on utilise les requêtes non
- * bufférisées, et que la fonction manipule la base de données
- */
- protected bool $useCache;
-
- function isUseCache(): bool {
- return $this->useCache;
- }
-
- function setUseCache(bool $useCache=true): self {
- $this->useCache = $useCache;
- return $this;
- }
-
/**
* initialiser ce channel avant sa première utilisation.
*/
@@ -255,6 +237,10 @@ class CapacitorChannel implements ITransactor {
* Retourner la clé primaire par cette méthode est l'unique moyen de
* déclencher une mise à jour plutôt qu'une nouvelle création.
*
+ * Bien que ce ne soit pas prévu à la base, si on veut modifier $item dans
+ * cette méthode pour des raisons pratiques, il suffit de retournerla valeur
+ * modifiée avec la clé "item"
+ *
* Retourner [false] pour annuler le chargement
*/
function getItemValues($item): ?array {
@@ -277,8 +263,8 @@ class CapacitorChannel implements ITransactor {
*
* cette méthode doit être utilisée dans {@link self::onUpdate()}
*/
- function wasRowModified(array $values, array $pvalues): bool {
- return $values["item__sum_"] !== $pvalues["item__sum_"];
+ function wasRowModified(array $row, array $prow): bool {
+ return $row["item__sum_"] !== $prow["item__sum_"];
}
final function serialize($item): ?string {
@@ -309,17 +295,17 @@ class CapacitorChannel implements ITransactor {
return array_combine($sumCols, [$serial, $sum]);
}
- function wasSumModified(string $key, $value, array $pvalues): bool {
+ function wasSumModified(string $key, $value, array $prow): bool {
$sumCol = $this->getSumCols($key)[1];
$sum = $this->sum(null, $value);
- $psum = $pvalues[$sumCol] ?? $this->sum(null, $pvalues[$key] ?? null);
+ $psum = $prow[$sumCol] ?? $this->sum(null, $prow[$key] ?? null);
return $sum !== $psum;
}
- function _wasSumModified(string $key, array $row, array $prow): bool {
+ function _wasSumModified(string $key, array $raw, array $praw): bool {
$sumCol = $this->getSumCols($key)[1];
- $sum = $row[$sumCol] ?? null;
- $psum = $prow[$sumCol] ?? null;
+ $sum = $raw[$sumCol] ?? null;
+ $psum = $praw[$sumCol] ?? null;
return $sum !== $psum;
}
@@ -328,21 +314,21 @@ class CapacitorChannel implements ITransactor {
* créer un nouvel élément
*
* @param mixed $item l'élément à charger
- * @param array $values la ligne à créer, calculée à partir de $item et des
+ * @param array $row la ligne à créer, calculée à partir de $item et des
* valeurs retournées par {@link getItemValues()}
- * @return ?array le cas échéant, un tableau non null à merger dans $values et
+ * @return ?array le cas échéant, un tableau non null à merger dans $row et
* utilisé pour provisionner la ligne nouvellement créée.
* Retourner [false] pour annuler le chargement (la ligne n'est pas créée)
*
* Si $item est modifié dans cette méthode, il est possible de le retourner
- * avec la clé "item" pour mettre à jour la ligne correspondante.
+ * avec la clé "item" pour mettre à jour la colonne correspondante.
*
* la création ou la mise à jour est uniquement décidée en fonction des
* valeurs calculées par {@link self::getItemValues()}. Bien que cette méthode
* peut techniquement retourner de nouvelles valeurs pour la clé primaire, ça
* risque de créer des doublons
*/
- function onCreate($item, array $values, ?array $alwaysNull): ?array {
+ function onCreate($item, array $row, ?array $alwaysNull): ?array {
return null;
}
@@ -351,12 +337,12 @@ class CapacitorChannel implements ITransactor {
* mettre à jour un élément existant
*
* @param mixed $item l'élément à charger
- * @param array $values la nouvelle ligne, calculée à partir de $item et
+ * @param array $row la nouvelle ligne, calculée à partir de $item et
* des valeurs retournées par {@link getItemValues()}
- * @param array $pvalues la précédente ligne, chargée depuis la base de
+ * @param array $prow la précédente ligne, chargée depuis la base de
* données
* @return ?array null s'il ne faut pas mettre à jour la ligne. sinon, ce
- * tableau est mergé dans $values puis utilisé pour mettre à jour la ligne
+ * tableau est mergé dans $row puis utilisé pour mettre à jour la ligne
* existante
* Retourner [false] pour annuler le chargement (la ligne n'est pas mise à
* jour)
@@ -365,7 +351,7 @@ class CapacitorChannel implements ITransactor {
* - La clé primaire (il s'agit généralement de "id_") ne peut pas être
* modifiée. si elle est retournée, elle est ignorée
*/
- function onUpdate($item, array $values, array $pvalues): ?array {
+ function onUpdate($item, array $row, array $prow): ?array {
return null;
}
@@ -373,8 +359,8 @@ class CapacitorChannel implements ITransactor {
* méthode appelée lors du parcours des éléments avec
* {@link Capacitor::each()}
*
- * @param mixed $item l'élément courant
- * @param ?array $values la ligne courante
+ * @param ?array $row la ligne courante. l'élément courant est accessible via
+ * $row["item"]
* @return ?array le cas échéant, un tableau non null utilisé pour mettre à
* jour la ligne courante
*
@@ -382,7 +368,7 @@ class CapacitorChannel implements ITransactor {
* - La clé primaire (il s'agit généralement de "id_") ne peut pas être
* modifiée. si elle est retournée, elle est ignorée
*/
- function onEach($item, array $values): ?array {
+ function onEach(array $row): ?array {
return null;
}
const onEach = "->".[self::class, "onEach"][1];
@@ -391,11 +377,11 @@ class CapacitorChannel implements ITransactor {
* méthode appelée lors du parcours des éléments avec
* {@link Capacitor::delete()}
*
- * @param mixed $item l'élément courant
- * @param ?array $values la ligne courante
+ * @param ?array $row la ligne courante. l'élément courant est accessible via
+ * $row["item"]
* @return bool true s'il faut supprimer la ligne, false sinon
*/
- function onDelete($item, array $values): bool {
+ function onDelete(array $row): bool {
return true;
}
const onDelete = "->".[self::class, "onDelete"][1];
@@ -454,8 +440,8 @@ class CapacitorChannel implements ITransactor {
$this->capacitor->reset($recreate);
}
- function charge($item, $func=null, ?array $args=null, ?array &$values=null): int {
- return $this->capacitor->charge($item, $func, $args, $values);
+ function charge($item, $func=null, ?array $args=null, ?array &$row=null): int {
+ return $this->capacitor->charge($item, $func, $args, $row);
}
function discharge(bool $reset=true): Traversable {
diff --git a/php/src/db/CapacitorStorage.php b/php/src/db/CapacitorStorage.php
index 9d4cdb1..1d8e785 100644
--- a/php/src/db/CapacitorStorage.php
+++ b/php/src/db/CapacitorStorage.php
@@ -1,6 +1,7 @@
"genserial",
+ ];
+ # les définitions sont par défaut pour MariaDB/MySQL
const SERDATA_DEFINITION = "mediumtext";
const SERSUM_DEFINITION = "varchar(40)";
const SERTS_DEFINITION = "datetime";
+ const GENSERIAL_DEFINITION = "integer primary key auto_increment";
+ const GENLIC_DEFINITION = "varchar(80)";
+ const GENLIB_DEFINITION = "varchar(255)";
+ const GENTEXT_DEFINITION = "mediumtext";
+ const GENBOOL_DEFINITION = "integer(1)";
+ const GENUUID_DEFINITION = "varchar(36)";
- protected static function sercol($def): string {
+ protected static function gencol($def): string {
if (!is_string($def)) $def = strval($def);
+ $def = trim($def);
+ $parts = preg_split('/\s+/', $def, 2);
+ if (count($parts) == 2) {
+ $def = $parts[0];
+ $rest = " $parts[1]";
+ } else {
+ $rest = null;
+ }
switch ($def) {
case "serdata": $def = static::SERDATA_DEFINITION; break;
case "sersum": $def = static::SERSUM_DEFINITION; break;
case "serts": $def = static::SERTS_DEFINITION; break;
+ case "genserial": $def = static::GENSERIAL_DEFINITION; break;
+ case "genlic": $def = static::GENLIC_DEFINITION; break;
+ case "genlib": $def = static::GENLIB_DEFINITION; break;
+ case "gentext": $def = static::GENTEXT_DEFINITION; break;
+ case "genbool": $def = static::GENBOOL_DEFINITION; break;
+ case "genuuid": $def = static::GENUUID_DEFINITION; break;
}
- return $def;
+ return "$def$rest";
}
const COLUMN_DEFINITIONS = [
@@ -81,7 +104,7 @@ abstract class CapacitorStorage {
$mindex++;
} else {
if ($mdef) {
- $definitions[$mcol] = self::sercol($mdef);
+ $definitions[$mcol] = self::gencol($mdef);
} else {
unset($definitions[$mcol]);
}
@@ -92,7 +115,7 @@ abstract class CapacitorStorage {
$constraints[] = $def;
}
} else {
- $definitions[$col] = self::sercol($def);
+ $definitions[$col] = self::gencol($def);
}
}
return cl::merge($definitions, $constraints);
@@ -102,9 +125,35 @@ abstract class CapacitorStorage {
return $channel->getMigration();
}
- /** sérialiser les valeurs qui doivent l'être dans $values */
- protected function serialize(CapacitorChannel $channel, ?array $values): ?array {
- if ($values === null) return null;
+ /** sérialiser les valeurs qui doivent l'être dans $row */
+ protected function serialize(CapacitorChannel $channel, ?array $row): ?array {
+ if ($row === null) return null;
+ $cols = $this->ColumnDefinitions($channel);
+ $index = 0;
+ $raw = [];
+ foreach (array_keys($cols) as $col) {
+ $key = $col;
+ if ($key === $index) {
+ $index++;
+ } elseif ($channel->isSerialCol($key)) {
+ [$serialCol, $sumCol] = $channel->getSumCols($key);
+ if (array_key_exists($key, $row)) {
+ $sum = $channel->getSum($key, $row[$key]);
+ $raw[$serialCol] = $sum[$serialCol];
+ if (array_key_exists($sumCol, $cols)) {
+ $raw[$sumCol] = $sum[$sumCol];
+ }
+ }
+ } elseif (array_key_exists($key, $row)) {
+ $raw[$col] = $row[$key];
+ }
+ }
+ return $raw;
+ }
+
+ /** désérialiser les valeurs qui doivent l'être dans $values */
+ protected function unserialize(CapacitorChannel $channel, ?array $raw): ?array {
+ if ($raw === null) return null;
$cols = $this->ColumnDefinitions($channel);
$index = 0;
$row = [];
@@ -112,44 +161,18 @@ abstract class CapacitorStorage {
$key = $col;
if ($key === $index) {
$index++;
+ } elseif (!array_key_exists($col, $raw)) {
} elseif ($channel->isSerialCol($key)) {
- [$serialCol, $sumCol] = $channel->getSumCols($key);
- if (array_key_exists($key, $values)) {
- $sum = $channel->getSum($key, $values[$key]);
- $row[$serialCol] = $sum[$serialCol];
- if (array_key_exists($sumCol, $cols)) {
- $row[$sumCol] = $sum[$sumCol];
- }
- }
- } elseif (array_key_exists($key, $values)) {
- $row[$col] = $values[$key];
+ $value = $raw[$col];
+ if ($value !== null) $value = $channel->unserialize($value);
+ $row[$key] = $value;
+ } else {
+ $row[$key] = $raw[$col];
}
}
return $row;
}
- /** désérialiser les valeurs qui doivent l'être dans $values */
- protected function unserialize(CapacitorChannel $channel, ?array $row): ?array {
- if ($row === null) return null;
- $cols = $this->ColumnDefinitions($channel);
- $index = 0;
- $values = [];
- foreach (array_keys($cols) as $col) {
- $key = $col;
- if ($key === $index) {
- $index++;
- } elseif (!array_key_exists($col, $row)) {
- } elseif ($channel->isSerialCol($key)) {
- $value = $row[$col];
- if ($value !== null) $value = $channel->unserialize($value);
- $values[$key] = $value;
- } else {
- $values[$key] = $row[$col];
- }
- }
- return $values;
- }
-
function getPrimaryKeys(CapacitorChannel $channel): array {
$primaryKeys = $channel->getPrimaryKeys();
if ($primaryKeys === null) $primaryKeys = ["id_"];
@@ -215,6 +238,22 @@ abstract class CapacitorStorage {
"class_name" => "varchar",
];
+ function channelExists(string $name, ?array &$raw=null): bool {
+ $raw = $this->db()->one([
+ "select",
+ "from" => static::CHANNELS_TABLE,
+ "where" => ["name" => $name],
+ ]);
+ return $raw !== null;
+ }
+
+ function getChannels(): iterable {
+ return $this->db()->all([
+ "select",
+ "from" => static::CHANNELS_TABLE,
+ ]);
+ }
+
protected function _createChannelsSql(): array {
return [
"create table if not exists",
@@ -316,7 +355,7 @@ abstract class CapacitorStorage {
* en fonction du type d'opération: création ou mise à jour
*
* Dans les deux cas, si la fonction retourne un tableau, il est utilisé pour
- * modifier les valeurs insérées/mises à jour. De plus, $values obtient la
+ * modifier les valeurs insérées/mises à jour. De plus, $row obtient la
* valeur finale des données insérées/mises à jour
*
* Si $args est renseigné, il est ajouté aux arguments utilisés pour appeler
@@ -327,23 +366,27 @@ abstract class CapacitorStorage {
* @return int 1 si l'objet a été chargé ou mis à jour, 0 s'il existait
* déjà à l'identique dans le canal
*/
- function _charge(CapacitorChannel $channel, $item, $func, ?array $args, ?array &$values=null): int {
+ function _charge(CapacitorChannel $channel, $item, $func, ?array $args, ?array &$row=null): int {
$this->_create($channel);
$tableName = $channel->getTableName();
$db = $this->db();
$args ??= [];
- $values = func::call([$channel, "getItemValues"], $item, ...$args);
- if ($values === [false]) return 0;
+ $row = func::call([$channel, "getItemValues"], $item, ...$args);
+ if ($row === [false]) return 0;
- $row = cl::merge(
+ if ($row !== null && array_key_exists("item", $row)) {
+ $item = A::pop($row, "item");
+ }
+
+ $raw = cl::merge(
$channel->getSum("item", $item),
- $this->serialize($channel, $values));
- $prow = null;
- $rowIds = $this->getRowIds($channel, $row, $primaryKeys);
+ $this->serialize($channel, $row));
+ $praw = null;
+ $rowIds = $this->getRowIds($channel, $raw, $primaryKeys);
if ($rowIds !== null) {
# modification
- $prow = $db->one([
+ $praw = $db->one([
"select",
"from" => $tableName,
"where" => $rowIds,
@@ -352,47 +395,47 @@ abstract class CapacitorStorage {
$now = date("Y-m-d H:i:s");
$insert = null;
- if ($prow === null) {
+ if ($praw === null) {
# création
- $row = cl::merge($row, [
+ $raw = cl::merge($raw, [
"created_" => $now,
"modified_" => $now,
]);
$insert = true;
$initFunc = func::with([$channel, "onCreate"], $args);
- $values = $this->unserialize($channel, $row);
- $pvalues = null;
+ $row = $this->unserialize($channel, $raw);
+ $prow = null;
} else {
# modification
- # intégrer autant que possible les valeurs de prow dans row, de façon que
+ # intégrer autant que possible les valeurs de praw dans raw, de façon que
# l'utilisateur puisse voir clairement ce qui a été modifié
- if ($channel->_wasSumModified("item", $row, $prow)) {
+ if ($channel->_wasSumModified("item", $raw, $praw)) {
$insert = false;
- $row = cl::merge($prow, $row, [
+ $raw = cl::merge($praw, $raw, [
"modified_" => $now,
]);
} else {
- $row = cl::merge($prow, $row);
+ $raw = cl::merge($praw, $raw);
}
$initFunc = func::with([$channel, "onUpdate"], $args);
- $values = $this->unserialize($channel, $row);
- $pvalues = $this->unserialize($channel, $prow);
+ $row = $this->unserialize($channel, $raw);
+ $prow = $this->unserialize($channel, $praw);
}
- $updates = $initFunc->prependArgs([$item, $values, $pvalues])->invoke();
+ $updates = $initFunc->prependArgs([$item, $row, $prow])->invoke();
if ($updates === [false]) return 0;
if (is_array($updates) && $updates) {
if ($insert === null) $insert = false;
if (!array_key_exists("modified_", $updates)) {
$updates["modified_"] = $now;
}
- $values = cl::merge($values, $updates);
- $row = cl::merge($row, $this->serialize($channel, $updates));
+ $row = cl::merge($row, $updates);
+ $raw = cl::merge($raw, $this->serialize($channel, $updates));
}
if ($func !== null) {
$updates = func::with($func)
- ->prependArgs([$item, $values, $pvalues])
+ ->prependArgs([$item, $row, $prow])
->bind($channel)
->invoke();
if ($updates === [false]) return 0;
@@ -401,8 +444,8 @@ abstract class CapacitorStorage {
if (!array_key_exists("modified_", $updates)) {
$updates["modified_"] = $now;
}
- $values = cl::merge($values, $updates);
- $row = cl::merge($row, $this->serialize($channel, $updates));
+ $row = cl::merge($row, $updates);
+ $raw = cl::merge($raw, $this->serialize($channel, $updates));
}
}
@@ -421,25 +464,23 @@ abstract class CapacitorStorage {
$id = $db->exec([
"insert",
"into" => $tableName,
- "values" => $row,
+ "values" => $raw,
]);
if (count($primaryKeys) == 1 && $rowIds === null) {
# mettre à jour avec l'id généré
- $values[$primaryKeys[0]] = $id;
+ $row[$primaryKeys[0]] = $id;
}
$nbModified = 1;
} else {
# calculer ce qui a changé pour ne mettre à jour que le nécessaire
$updates = [];
- foreach ($row as $col => $value) {
+ foreach ($raw as $col => $value) {
if (array_key_exists($col, $rowIds)) {
# ne jamais mettre à jour la clé primaire
continue;
}
- $pvalue = $prow[$col] ?? null;
- if ($value !== ($pvalue)) {
- $updates[$col] = $value;
- }
+ $pvalue = $praw[$col] ?? null;
+ if ($value !== $pvalue) $updates[$col] = $value;
}
if (count($updates) == 1 && array_key_first($updates) == "modified_") {
# si l'unique modification porte sur la date de modification, alors
@@ -467,19 +508,22 @@ abstract class CapacitorStorage {
}
}
- function charge(?string $channel, $item, $func=null, ?array $args=null, ?array &$values=null): int {
- return $this->_charge($this->getChannel($channel), $item, $func, $args, $values);
+ function charge(?string $channel, $item, $func=null, ?array $args=null, ?array &$row=null): int {
+ return $this->_charge($this->getChannel($channel), $item, $func, $args, $row);
}
- /** décharger les données du canal spécifié */
+ /**
+ * décharger les données du canal spécifié. seul la valeur de $item est
+ * fournie
+ */
function _discharge(CapacitorChannel $channel, bool $reset=true): Traversable {
$this->_create($channel);
- $rows = $this->db()->all([
+ $raws = $this->db()->all([
"select item__",
"from" => $channel->getTableName(),
]);
- foreach ($rows as $row) {
- yield unserialize($row['item__']);
+ foreach ($raws as $raw) {
+ yield unserialize($raw['item__']);
}
if ($reset) $this->_reset($channel);
}
@@ -548,45 +592,34 @@ abstract class CapacitorStorage {
if ($filter === null) throw ValueException::null("filter");
$this->_create($channel);
$this->verifixFilter($channel, $filter);
- $row = $this->db()->one(cl::merge([
+ $raw = $this->db()->one(cl::merge([
"select",
"from" => $channel->getTableName(),
"where" => $filter,
], $mergeQuery));
- return $this->unserialize($channel, $row);
+ return $this->unserialize($channel, $raw);
}
function one(?string $channel, $filter, ?array $mergeQuery=null): ?array {
return $this->_one($this->getChannel($channel), $filter, $mergeQuery);
}
- private function _allCached(string $id, CapacitorChannel $channel, $filter, ?array $mergeQuery=null): Traversable {
- $this->_create($channel);
- $this->verifixFilter($channel, $filter);
- $rows = $this->db()->all(cl::merge([
- "select",
- "from" => $channel->getTableName(),
- "where" => $filter,
- ], $mergeQuery), null, $this->getPrimaryKeys($channel));
- if ($channel->isUseCache()) {
- $cacheIds = [$id, get_class($channel)];
- cache::get()->resetCached($cacheIds);
- $rows = cache::new(null, $cacheIds, function() use ($rows) {
- yield from $rows;
- });
- }
- foreach ($rows as $key => $row) {
- yield $key => $this->unserialize($channel, $row);
- }
- }
-
/**
* obtenir les lignes correspondant au filtre sur le canal spécifié
*
* si $filter n'est pas un tableau, il est transformé en ["id_" => $filter]
*/
function _all(CapacitorChannel $channel, $filter, ?array $mergeQuery=null): Traversable {
- return $this->_allCached("all", $channel, $filter, $mergeQuery);
+ $this->_create($channel);
+ $this->verifixFilter($channel, $filter);
+ $raws = $this->db()->all(cl::merge([
+ "select",
+ "from" => $channel->getTableName(),
+ "where" => $filter,
+ ], $mergeQuery), null, $this->getPrimaryKeys($channel));
+ foreach ($raws as $key => $raw) {
+ yield $key => $this->unserialize($channel, $raw);
+ }
}
function all(?string $channel, $filter, $mergeQuery=null): Traversable {
@@ -622,10 +655,10 @@ abstract class CapacitorStorage {
$tableName = $channel->getTableName();
try {
$args ??= [];
- $all = $this->_allCached("each", $channel, $filter, $mergeQuery);
- foreach ($all as $values) {
- $rowIds = $this->getRowIds($channel, $values);
- $updates = $onEach->invoke([$values["item"], $values, ...$args]);
+ $rows = $this->_all($channel, $filter, $mergeQuery);
+ foreach ($rows as $row) {
+ $rowIds = $this->getRowIds($channel, $row);
+ $updates = $onEach->invoke([$row, ...$args]);
if (is_array($updates) && $updates) {
if (!array_key_exists("modified_", $updates)) {
$updates["modified_"] = date("Y-m-d H:i:s");
@@ -689,10 +722,10 @@ abstract class CapacitorStorage {
$tableName = $channel->getTableName();
try {
$args ??= [];
- $all = $this->_allCached("delete", $channel, $filter);
- foreach ($all as $values) {
- $rowIds = $this->getRowIds($channel, $values);
- $shouldDelete = boolval($onDelete->invoke([$values["item"], $values, ...$args]));
+ $rows = $this->_all($channel, $filter);
+ foreach ($rows as $row) {
+ $rowIds = $this->getRowIds($channel, $row);
+ $shouldDelete = boolval($onDelete->invoke([$row, ...$args]));
if ($shouldDelete) {
$db->exec([
"delete",
diff --git a/php/src/db/_private/_update.php b/php/src/db/_private/_update.php
index 7b297c4..5791387 100644
--- a/php/src/db/_private/_update.php
+++ b/php/src/db/_private/_update.php
@@ -6,7 +6,6 @@ class _update extends _common {
"prefix" => "?string",
"table" => "?string",
"schema" => "?array",
- "cols" => "?array",
"values" => "?array",
"where" => "?array",
"suffix" => "?string",
diff --git a/php/src/db/mysql/MysqlStorage.php b/php/src/db/mysql/MysqlStorage.php
index ace8beb..41a7c65 100644
--- a/php/src/db/mysql/MysqlStorage.php
+++ b/php/src/db/mysql/MysqlStorage.php
@@ -19,10 +19,6 @@ class MysqlStorage extends CapacitorStorage {
return $this->db;
}
- const PRIMARY_KEY_DEFINITION = [
- "id_" => "integer primary key auto_increment",
- ];
-
protected function tableExists(string $tableName): bool {
$db = $this->db;
$found = $db->get([
diff --git a/php/src/db/pgsql/PgsqlStorage.php b/php/src/db/pgsql/PgsqlStorage.php
index dd89e2a..649b3eb 100644
--- a/php/src/db/pgsql/PgsqlStorage.php
+++ b/php/src/db/pgsql/PgsqlStorage.php
@@ -9,6 +9,10 @@ class PgsqlStorage extends CapacitorStorage {
const SERDATA_DEFINITION = "text";
const SERSUM_DEFINITION = "varchar(40)";
const SERTS_DEFINITION = "timestamp";
+ const GENSERIAL_DEFINITION = "serial primary key";
+ const GENTEXT_DEFINITION = "text";
+ const GENBOOL_DEFINITION = "boolean";
+ const GENUUID_DEFINITION = "uuid";
function __construct($pgsql) {
$this->db = Pgsql::with($pgsql);
@@ -20,10 +24,6 @@ class PgsqlStorage extends CapacitorStorage {
return $this->db;
}
- const PRIMARY_KEY_DEFINITION = [
- "id_" => "serial primary key",
- ];
-
protected function tableExists(string $tableName): bool {
if (($index = strpos($tableName, ".")) !== false) {
$schemaName = substr($tableName, 0, $index);
diff --git a/php/src/db/sqlite/SqliteStorage.php b/php/src/db/sqlite/SqliteStorage.php
index 7495b46..e9423b9 100644
--- a/php/src/db/sqlite/SqliteStorage.php
+++ b/php/src/db/sqlite/SqliteStorage.php
@@ -9,6 +9,8 @@ use nulib\db\CapacitorStorage;
* Class SqliteStorage
*/
class SqliteStorage extends CapacitorStorage {
+ const GENSERIAL_DEFINITION = "integer primary key autoincrement";
+
function __construct($sqlite) {
$this->db = Sqlite::with($sqlite);
}
@@ -19,10 +21,6 @@ class SqliteStorage extends CapacitorStorage {
return $this->db;
}
- const PRIMARY_KEY_DEFINITION = [
- "id_" => "integer primary key autoincrement",
- ];
-
protected function tableExists(string $tableName): bool {
$found = $this->db->get([
# depuis la version 3.33.0 le nom officiel de la table est sqlite_schema,
@@ -40,22 +38,6 @@ class SqliteStorage extends CapacitorStorage {
return new _sqliteMigration($migrations, $channel->getName());
}
- function channelExists(string $name, ?array &$row=null): bool {
- $row = $this->db->one([
- "select",
- "from" => static::CHANNELS_TABLE,
- "where" => ["name" => $name],
- ]);
- return $row !== null;
- }
-
- function getChannels(): iterable {
- return $this->db->all([
- "select",
- "from" => static::CHANNELS_TABLE,
- ]);
- }
-
protected function _addToChannelsSql(CapacitorChannel $channel): array {
$sql = parent::_addToChannelsSql($channel);
$sql[0] = "insert or ignore";
diff --git a/php/src/file.php b/php/src/file.php
index 6bd1928..a8175d4 100644
--- a/php/src/file.php
+++ b/php/src/file.php
@@ -117,4 +117,18 @@ class file {
}
return $file;
}
+
+ static function string_reader(string $content, ?callable $func=null): MemoryStream {
+ $file = new MemoryStream();
+ $file->fwrite($content);
+ $file->rewind();
+ if ($func !== null) {
+ try {
+ $func($file);
+ } finally {
+ $file ->close();
+ }
+ }
+ return $file;
+ }
}
diff --git a/php/src/file/tab/AbstractReader.php b/php/src/file/tab/AbstractReader.php
index 6418235..8bffb47 100644
--- a/php/src/file/tab/AbstractReader.php
+++ b/php/src/file/tab/AbstractReader.php
@@ -52,6 +52,7 @@ abstract class AbstractReader implements IReader {
$this->headers = $params["headers"] ?? static::HEADERS;
$this->useHeaders = $params["use_headers"] ?? static::USE_HEADERS;
$this->input = $params["input"] ?? static::INPUT;
+ $this->skip = $params["skip"] ?? 0;
$this->trim = boolval($params["trim"] ?? static::TRIM);
$this->emptyAsNull = boolval($params["empty_as_null"] ?? static::EMPTY_AS_NULL);
$this->parseNone = boolval($params["parse_none"] ?? static::PARSE_NONE);
@@ -67,6 +68,8 @@ abstract class AbstractReader implements IReader {
protected $input;
+ protected int $skip;
+
protected bool $trim;
protected bool $emptyAsNull;
@@ -81,15 +84,16 @@ abstract class AbstractReader implements IReader {
protected function cookRow(array &$row): bool {
if (!$this->useHeaders) return true;
- if ($this->isrc == 0) {
+ if ($this->skip > 0) {
+ $this->skip--;
+ return false;
+ }
+ if ($this->headers === null) {
# ligne d'en-tête
- $headers = $this->headers;
- if ($headers === null) {
- if ($this->schema === null) $headers = null;
- else $headers = array_keys($this->schema);
- if ($headers === null) $headers = $row;
- $this->headers = $headers;
- }
+ if ($this->schema === null) $headers = null;
+ else $headers = array_keys($this->schema);
+ if ($headers === null) $headers = $row;
+ $this->headers = $headers;
return false;
}
A::ensure_size($row, count($this->headers));
diff --git a/php/src/tools/BgLauncherApp.php b/php/src/tools/BgLauncherApp.php
deleted file mode 100644
index 88aa522..0000000
--- a/php/src/tools/BgLauncherApp.php
+++ /dev/null
@@ -1,124 +0,0 @@
- "lancer un script en tâche de fond",
- "usage" => "ApplicationClass args...",
-
- "sections" => [
- parent::VERBOSITY_SECTION,
- ],
-
- ["-i", "--infos", "name" => "action", "value" => self::ACTION_INFOS,
- "help" => "Afficher des informations sur la tâche",
- ],
- ["-s", "--start", "name" => "action", "value" => self::ACTION_START,
- "help" => "Démarrer la tâche",
- ],
- ["-k", "--stop", "name" => "action", "value" => self::ACTION_STOP,
- "help" => "Arrêter la tâche",
- ],
- ];
-
- protected int $action = self::ACTION_START;
-
- protected ?array $args = null;
-
- static function show_infos(RunFile $runfile, ?int $level=null): void {
- msg::print($runfile->getDesc(), $level);
- msg::print(yaml::with(["data" => $runfile->read()]), ($level ?? 0) - 1);
- }
-
- function main() {
- $args = $this->args;
-
- $appClass = $args[0] ?? null;
- if ($appClass === null) {
- self::die("Vous devez spécifier la classe de l'application");
- }
- $appClass = $args[0] = str_replace("/", "\\", $appClass);
- if (!class_exists($appClass)) {
- self::die("$appClass: classe non trouvée");
- }
-
- $useRunfile = constant("$appClass::USE_RUNFILE");
- if (!$useRunfile) {
- self::die("Cette application ne supporte le lancement en tâche de fond");
- }
-
- $runfile = app::with($appClass)->getRunfile();
- switch ($this->action) {
- case self::ACTION_START:
- $argc = count($args);
- $appClass::_manage_runfile($argc, $args, $runfile);
- if ($runfile->warnIfLocked()) self::exit(app::EC_LOCKED);
- array_splice($args, 0, 0, [
- PHP_BINARY,
- path::abspath(NULIB_APP_app_launcher),
- ]);
- app::params_putenv();
- self::_start($args, $runfile);
- break;
- case self::ACTION_STOP:
- self::_stop($runfile);
- self::show_infos($runfile, -1);
- break;
- case self::ACTION_INFOS:
- self::show_infos($runfile);
- break;
- }
- }
-
- public static function _start(array $args, Runfile $runfile): void {
- $pid = pcntl_fork();
- if ($pid == -1) {
- # parent, impossible de forker
- throw new ExitError(app::EC_FORK_PARENT, "Unable to fork");
- } elseif (!$pid) {
- # child, fork ok
- $runfile->wfPrepare($pid);
- $outfile = $runfile->getOutfile() ?? "/tmp/NULIB_APP_app_console.out";
- $exitcode = app::EC_FORK_CHILD;
- try {
- # rediriger STDIN, STDOUT et STDERR
- fclose(fopen($outfile, "wb")); // vider le fichier
- fclose(STDIN); $in = fopen("/dev/null", "rb");
- fclose(STDOUT); $out = fopen($outfile, "ab");
- fclose(STDERR); $err = fopen($outfile, "ab");
- # puis lancer la commande
- $cmd = new Cmd($args);
- $cmd->addSource("/g/init.env");
- $cmd->addRedir("both", $outfile, true);
- $cmd->fork_exec($exitcode, false);
- sh::_waitpid(-$pid, $exitcode);
- } finally {
- $runfile->wfReaped($exitcode);
- }
- }
- }
-
- public static function _stop(Runfile $runfile): bool {
- $data = $runfile->read();
- $pid = $runfile->_getCid($data);
- msg::action("stop $pid");
- if ($runfile->wfKill($reason)) {
- msg::asuccess();
- return true;
- } else {
- msg::afailure($reason);
- return false;
- }
- }
-}
diff --git a/php/src/tools/SteamTrainApp.php b/php/src/tools/SteamTrainApp.php
deleted file mode 100644
index 7e00ef0..0000000
--- a/php/src/tools/SteamTrainApp.php
+++ /dev/null
@@ -1,53 +0,0 @@
- self::TITLE,
- "description" => << 1,
- "help" => "spécifier le nombre d'étapes",
- ],
- ["-f", "--force-enabled", "value" => true,
- "help" => "lancer la commande même si les tâches planifiées sont désactivées",
- ],
- ["-n", "--no-install-signal-handler", "value" => false,
- "help" => "ne pas installer le gestionnaire de signaux",
- ],
- ];
-
- protected $count = 100;
-
- protected bool $forceEnabled = false;
-
- protected bool $installSignalHandler = true;
-
- function main() {
- app::check_bgapplication_enabled($this->forceEnabled);
- if ($this->installSignalHandler) app::install_signal_handler();
- $count = intval($this->count);
- msg::info("Starting train for ".words::q($count, "step#s"));
- app::action("Running train...", $count);
- for ($i = 1; $i <= $count; $i++) {
- msg::print("Tchou-tchou! x $i");
- app::step();
- sleep(1);
- }
- msg::info("Stopping train at ".new DateTime());
- }
-}
diff --git a/php/support/copy-templates b/php/support/copy-templates
deleted file mode 100755
index a426860..0000000
--- a/php/support/copy-templates
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
-source "$(dirname -- "$0")/../../load.sh" || exit 1
-
-declare -A DESTDIRS=(
- [template-_bg_launcher.php]=sbin
- [template-.launcher.php]=_cli
- [template-_wrapper.sh]=_cli
-)
-declare -A MODES=(
- [template-_bg_launcher.php]=+x
- [template-.launcher.php]=
- [template-_wrapper.sh]=+x
-)
-
-projdir=
-args=(
- "copier les templates dans le projet en cours"
- #"usage"
- -d:,--projdir: .
-)
-parse_args "$@"; set -- "${args[@]}"
-
-if [ -n "$projdir" ]; then
- cd "$projdir" || die
-fi
-
-[ -f composer.json ] || die "$(basename "$(dirname "$(pwd)")"): n'est pas un projet composer"
-
-setx -a templates=ls_files "$MYDIR" "template-*"
-for template in "${templates[@]}"; do
- destdir="${DESTDIRS[$template]}"
- [ -n "$destdir" ] || die "$template: la destination n'est pas configurée"
- mode="${MODES[$template]}"
- destname="${template#template-}"
-
- tail -n+4 "$MYDIR/$template" >"$destdir/$destname"
- [ -n "$mode" ] && chmod "$mode" "$destdir/$destname"
-done
diff --git a/php/tests/db/sqlite/SqliteStorageTest.php b/php/tests/db/sqlite/SqliteStorageTest.php
index e7197a3..540dfb7 100644
--- a/php/tests/db/sqlite/SqliteStorageTest.php
+++ b/php/tests/db/sqlite/SqliteStorageTest.php
@@ -7,6 +7,12 @@ use nulib\db\Capacitor;
use nulib\db\CapacitorChannel;
class SqliteStorageTest extends TestCase {
+ static function Txx(...$values): void {
+ foreach ($values as $value) {
+ var_export($value);
+ }
+ }
+
function _testChargeStrings(SqliteStorage $storage, ?string $channel) {
$storage->reset($channel);
$storage->charge($channel, "first");
@@ -67,7 +73,8 @@ class SqliteStorageTest extends TestCase {
$capacitor->charge(["name" => "third", "age" => 15]);
$capacitor->charge(["name" => "fourth", "age" => 20]);
- $setDone = function ($item, $row, $suffix=null) {
+ $setDone = function ($row, $suffix=null) {
+ $item = $row["item"];
$updates = ["done" => 1];
if ($suffix !== null) {
$item["name"] .= $suffix;
@@ -76,9 +83,9 @@ class SqliteStorageTest extends TestCase {
return $updates;
};
$capacitor->each(["age" => [">", 10]], $setDone, ["++"]);
- $capacitor->each(["done" => 0], $setDone, null);
+ $capacitor->each(["done" => 0], $setDone);
- Txx(cl::all($capacitor->discharge(false)));
+ self::Txx(cl::all($capacitor->discharge(false)));
$capacitor->close();
self::assertTrue(true);
}
@@ -133,16 +140,16 @@ class SqliteStorageTest extends TestCase {
$capacitor->charge(["a" => null, "b" => null]);
$capacitor->charge(["a" => "first", "b" => "second"]);
- Txx("=== all");
+ self::Txx("=== all");
/** @var Sqlite $sqlite */
$sqlite = $capacitor->getStorage()->db();
- Txx(cl::all($sqlite->all([
+ self::Txx(cl::all($sqlite->all([
"select",
"from" => $capacitor->getChannel()->getTableName(),
])));
- Txx("=== each");
- $capacitor->each(null, function ($item, $values) {
- Txx($values);
+ self::Txx("=== each");
+ $capacitor->each(null, function ($row) {
+ self::Txx($row);
});
$capacitor->close();
@@ -170,99 +177,100 @@ class SqliteStorageTest extends TestCase {
});
$capacitor->reset();
- $capacitor->charge(["name" => "first", "age" => 5], function($item, ?array $values, ?array $pvalues) {
+ $capacitor->charge(["name" => "first", "age" => 5], function($item, ?array $row, ?array $prow) {
self::assertSame("first", $item["name"]);
self::assertSame(5, $item["age"]);
- self::assertnotnull($values);
- self::assertSame(["name", "age", "item", "item__sum_", "created_", "modified_"], array_keys($values));
+ self::assertnotnull($row);
+ self::assertSame(["name", "age", "item", "item__sum_", "created_", "modified_"], array_keys($row));
self::assertSame([
"name" => "first",
"age" => 5,
"item" => $item,
- ], cl::select($values, ["name", "age", "item"]));
- self::assertNull($pvalues);
+ ], cl::select($row, ["name", "age", "item"]));
+ self::assertNull($prow);
});
- $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) {
+ $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $row, ?array $prow) {
self::assertSame("first", $item["name"]);
self::assertSame(10, $item["age"]);
- self::assertnotnull($values);
- self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
+ self::assertnotnull($row);
+ self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row));
self::assertSame([
"name" => "first",
"age" => 10,
"done" => 0,
"notes" => null,
"item" => $item,
- ], cl::select($values, ["name", "age", "done", "notes", "item"]));
- self::assertNotNull($pvalues);
+ ], cl::select($row, ["name", "age", "done", "notes", "item"]));
+ self::assertNotNull($prow);
self::assertSame([
"name" => "first",
"age" => 5,
"done" => 0,
"notes" => null,
"item" => ["name" => "first", "age" => 5],
- ], cl::select($pvalues, ["name", "age", "done", "notes", "item"]));
+ ], cl::select($prow, ["name", "age", "done", "notes", "item"]));
});
- $capacitor->each(null, function($item, ?array $values) {
+ $capacitor->each(null, function(array $row) {
+ $item = $row["item"];
self::assertSame("first", $item["name"]);
self::assertSame(10, $item["age"]);
- self::assertnotnull($values);
- self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
+ self::assertnotnull($row);
+ self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row));
self::assertSame([
"name" => "first",
"age" => 10,
"done" => 0,
"notes" => null,
"item" => $item,
- ], cl::select($values, ["name", "age", "done", "notes", "item"]));
+ ], cl::select($row, ["name", "age", "done", "notes", "item"]));
return [
"done" => 1,
"notes" => "modified",
];
});
- $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) {
+ $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $row, ?array $prow) {
self::assertSame("first", $item["name"]);
self::assertSame(10, $item["age"]);
- self::assertnotnull($values);
- self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
+ self::assertnotnull($row);
+ self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row));
self::assertSame([
"name" => "first",
"age" => 10,
"done" => 1,
"notes" => "modified",
"item" => $item,
- ], cl::select($values, ["name", "age", "done", "notes", "item"]));
- self::assertNotNull($pvalues);
+ ], cl::select($row, ["name", "age", "done", "notes", "item"]));
+ self::assertNotNull($prow);
self::assertSame([
"name" => "first",
"age" => 10,
"done" => 1,
"notes" => "modified",
"item" => $item,
- ], cl::select($pvalues, ["name", "age", "done", "notes", "item"]));
+ ], cl::select($prow, ["name", "age", "done", "notes", "item"]));
});
- $capacitor->charge(["name" => "first", "age" => 20], function($item, ?array $values, ?array $pvalues) {
+ $capacitor->charge(["name" => "first", "age" => 20], function($item, ?array $row, ?array $prow) {
self::assertSame("first", $item["name"]);
self::assertSame(20, $item["age"]);
- self::assertnotnull($values);
- self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
+ self::assertnotnull($row);
+ self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row));
self::assertSame([
"name" => "first",
"age" => 20,
"done" => 1,
"notes" => "modified",
"item" => $item,
- ], cl::select($values, ["name", "age", "done", "notes", "item"]));
- self::assertNotNull($pvalues);
+ ], cl::select($row, ["name", "age", "done", "notes", "item"]));
+ self::assertNotNull($prow);
self::assertSame([
"name" => "first",
"age" => 10,
"done" => 1,
"notes" => "modified",
"item" => ["name" => "first", "age" => 10],
- ], cl::select($pvalues, ["name", "age", "done", "notes", "item"]));
+ ], cl::select($prow, ["name", "age", "done", "notes", "item"]));
});
}
@@ -287,55 +295,55 @@ class SqliteStorageTest extends TestCase {
});
$capacitor->reset();
- $nbModified = $capacitor->charge(["name" => "first", "age" => 5], function ($item, ?array $values, ?array $pvalues) {
+ $nbModified = $capacitor->charge(["name" => "first", "age" => 5], function ($item, ?array $row, ?array $prow) {
self::assertSame([
"name" => "first", "age" => 5,
"item" => $item,
- ], cl::select($values, ["name", "age", "item"]));
+ ], cl::select($row, ["name", "age", "item"]));
return ["item" => null];
});
self::assertSame(1, $nbModified);
sleep(1);
# nb: on met des sleep() pour que la date de modification soit systématiquement différente
- $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $values, ?array $pvalues) {
+ $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $row, ?array $prow) {
self::assertSame([
"name" => "first", "age" => 10,
"item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc",
- ], cl::select($values, ["name", "age", "item", "item__sum_"]));
+ ], cl::select($row, ["name", "age", "item", "item__sum_"]));
self::assertSame([
"name" => "first", "age" => 5,
"item" => null, "item__sum_" => null,
- ], cl::select($pvalues, ["name", "age", "item", "item__sum_"]));
+ ], cl::select($prow, ["name", "age", "item", "item__sum_"]));
return ["item" => null];
});
self::assertSame(1, $nbModified);
sleep(1);
# pas de modification ici
- $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $values, ?array $pvalues) {
+ $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $row, ?array $prow) {
self::assertSame([
"name" => "first", "age" => 10,
"item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc",
- ], cl::select($values, ["name", "age", "item", "item__sum_"]));
+ ], cl::select($row, ["name", "age", "item", "item__sum_"]));
self::assertSame([
"name" => "first", "age" => 10,
"item" => null, "item__sum_" => null,
- ], cl::select($pvalues, ["name", "age", "item", "item__sum_"]));
+ ], cl::select($prow, ["name", "age", "item", "item__sum_"]));
return ["item" => null];
});
self::assertSame(0, $nbModified);
sleep(1);
- $nbModified = $capacitor->charge(["name" => "first", "age" => 20], function ($item, ?array $values, ?array $pvalues) {
+ $nbModified = $capacitor->charge(["name" => "first", "age" => 20], function ($item, ?array $row, ?array $prow) {
self::assertSame([
"name" => "first", "age" => 20,
"item" => $item, "item__sum_" => "001b91982b4e0883b75428c0eb28573a5dc5f7a5",
- ], cl::select($values, ["name", "age", "item", "item__sum_"]));
+ ], cl::select($row, ["name", "age", "item", "item__sum_"]));
self::assertSame([
"name" => "first", "age" => 10,
"item" => null, "item__sum_" => null,
- ], cl::select($pvalues, ["name", "age", "item", "item__sum_"]));
+ ], cl::select($prow, ["name", "age", "item", "item__sum_"]));
return ["item" => null];
});
self::assertSame(1, $nbModified);
diff --git a/php/tests/db/sqlite/SqliteTest.php b/php/tests/db/sqlite/SqliteTest.php
index a6e23a1..8aa7277 100644
--- a/php/tests/db/sqlite/SqliteTest.php
+++ b/php/tests/db/sqlite/SqliteTest.php
@@ -3,6 +3,7 @@ namespace nulib\db\sqlite;
use Exception;
use nulib\tests\TestCase;
+use nulib\ValueException;
class SqliteTest extends TestCase {
const CREATE_PERSON = "create table person(nom varchar, prenom varchar, age integer)";
@@ -12,8 +13,8 @@ class SqliteTest extends TestCase {
function testMigration() {
$sqlite = new Sqlite(":memory:", [
"migration" => [
- self::CREATE_PERSON,
- self::INSERT_JEPHTE,
+ "create" => self::CREATE_PERSON,
+ "insert" => self::INSERT_JEPHTE,
],
]);
self::assertSame("clain", $sqlite->get("select nom, age from person"));
@@ -30,15 +31,15 @@ class SqliteTest extends TestCase {
], $sqlite->get("select nom, age from person where nom = 'payet'", null, true));
self::assertSame([
- ["key" => "0", "value" => self::CREATE_PERSON, "done" => 1],
- ["key" => "1", "value" => self::INSERT_JEPHTE, "done" => 1],
- ], iterator_to_array($sqlite->all("select key, value, done from _migration")));
+ ["channel" => "", "name" => "create", "done" => 1],
+ ["channel" => "", "name" => "insert", "done" => 1],
+ ], iterator_to_array($sqlite->all("select channel, name, done from _migration")));
}
function testException() {
$sqlite = new Sqlite(":memory:");
self::assertException(Exception::class, [$sqlite, "exec"], "prout");
- self::assertException(SqliteException::class, [$sqlite, "exec"], ["prout"]);
+ self::assertException(ValueException::class, [$sqlite, "exec"], ["prout"]);
}
protected function assertInserted(Sqlite $sqlite, array $row, array $query): void {
@@ -141,6 +142,10 @@ class SqliteTest extends TestCase {
self::assertSame([
["count" => 2],
- ], iterator_to_array($sqlite->all(["select count(name) as count from user", "group by" => ["amount"], "having" => ["count(name) = 2"]])));
+ ], iterator_to_array($sqlite->all([
+ "select count(name) as count from user",
+ "group by" => ["amount"],
+ "having" => ["count(name) = 2"],
+ ])));
}
}
diff --git a/runphp/build b/runphp/build
index 4e796ad..b9f4b51 100755
--- a/runphp/build
+++ b/runphp/build
@@ -74,7 +74,7 @@ function build_check_env() {
if template_copy_missing "$PROJDIR/$file"; then
updated=1
setx name=basename -- "$file"
- name="${name#.}"; name="${name%.}"
+ name="${name#.}"; name="${name%.*}"
setx file=dirname -- "$file"
file="$file/$name"
updatedfiles+=("$file")
@@ -86,7 +86,7 @@ function build_check_env() {
if template_copy_missing "$file"; then
updated=1
setx name=basename -- "$file"
- name="${name#.}"; name="${name%.}"
+ name="${name#.}"; name="${name%.*}"
setx file=dirname -- "$file"
file="$file/$name"
updatedfiles+=("${file#$PROJDIR/}")
diff --git a/php/support/template-.launcher.php b/runphp/phpwrapper-.launcher.php
similarity index 69%
rename from php/support/template-.launcher.php
rename to runphp/phpwrapper-.launcher.php
index b1a3879..adabe3d 100644
--- a/php/support/template-.launcher.php
+++ b/runphp/phpwrapper-.launcher.php
@@ -1,12 +1,12 @@
# TODO Faire une copie de ce script dans un répertoire de l'application web
-# (dans le répertoire _cli/ par défaut) et modifier les paramètres si nécessaire
+# (dans le répertoire cli_config/ par défaut) et modifier les paramètres si nécessaire
#-------------------------------------------------------------------------------
__DIR__ . '/..',
+ "projdir" => __DIR__.'/..',
"appcode" => \app\config\bootstrap::APPCODE,
];
require __DIR__.'/../vendor/nulib/base/php/src/app/cli/include-launcher.php';
diff --git a/php/support/template-_bg_launcher.php b/runphp/phpwrapper-_bg_launcher.php
similarity index 83%
rename from php/support/template-_bg_launcher.php
rename to runphp/phpwrapper-_bg_launcher.php
index 88147f5..783576a 100644
--- a/php/support/template-_bg_launcher.php
+++ b/runphp/phpwrapper-_bg_launcher.php
@@ -6,10 +6,10 @@ require __DIR__.'/../vendor/autoload.php';
# Lancer une application en tâche de fond
use nulib\app;
-use nulib\tools\BgLauncherApp;
+use nulib\cli\BgLauncherApp;
# chemin vers le lanceur PHP
-const NULIB_APP_app_launcher = __DIR__.'/../_cli/.launcher.php';
+const NULIB_APP_app_launcher = __DIR__.'/../@@CLI@@/.launcher.php';
app::init([
"projdir" => __DIR__.'/..',
diff --git a/php/support/template-_wrapper.sh b/runphp/phpwrapper-_wrapper.sh
similarity index 96%
rename from php/support/template-_wrapper.sh
rename to runphp/phpwrapper-_wrapper.sh
index 22c34f6..05569e8 100644
--- a/php/support/template-_wrapper.sh
+++ b/runphp/phpwrapper-_wrapper.sh
@@ -1,5 +1,5 @@
# TODO Faire une copie de ce script dans un répertoire de l'application web
-# (dans le répertoire _cli/ par défaut) et modifier les paramétres si nécessaire
+# (dans le répertoire cli_config/ par défaut) et modifier les paramétres si nécessaire
#-------------------------------------------------------------------------------
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
@@ -57,7 +57,7 @@ Vérification des liens..."
for i in *.php*; do
[ -f "$i" ] || continue
name="bin/${i%.*}.php"
- dest="../_cli/_wrapper.sh"
+ dest="../@@CLI@@/_wrapper.sh"
link="../bin/${i%.*}.php"
if [ -L "$link" ]; then
echo "* $name OK"
diff --git a/runphp/update-runphp.sh b/runphp/update-runphp.sh
index 05403fe..b4555f5 100755
--- a/runphp/update-runphp.sh
+++ b/runphp/update-runphp.sh
@@ -40,11 +40,31 @@ p == 1 {
ac_clean "$conf0"
}
+declare -A PHPWRAPPER_DESTDIRS=(
+ [_bg_launcher.php]=@@SBIN@@
+ [.launcher.php]=@@CLI@@
+ [_wrapper.sh]=@@CLI@@
+)
+declare -A PHPWRAPPER_MODES=(
+ [_bg_launcher.php]=+x
+ [.launcher.php]=
+ [_wrapper.sh]=+x
+)
+
projdir=
+install_phpwrappers=auto
args=(
"Mettre à jour le script runphp"
"[path/to/runphp]"
- -d:,--projdir:PROJDIR . "Copier les fichiers pour un projet de l'université de la Réunion"
+ -d:,--projdir:PROJDIR . "\
+Copier les fichiers pour un projet de l'université de la Réunion:
+- BUILDENV0 et BUILDENV sont fixés à ..env.dist et .env
+- les fichiers ..env.dist et .runphp.conf sont créés le cas échéant
+- le script build est mis à jour
+- les wrappers PHP pour la gestion des tâches de fond sont mis à jour le cas
+ échéant"
+ -p,--phpwrappers-install install_phpwrappers=1 "forcer l'installation des wrappers PHP"
+ --np,--no-phpwrappers-install install_phpwrappers= "ne pas installer les wrappers PHP"
)
parse_args "$@"; set -- "${args[@]}"
@@ -93,6 +113,7 @@ else
fi
# (Re)construire le fichier destination
+estep "$(relpath "$runphp")"
(
cat "$preamble"
echo
@@ -102,24 +123,81 @@ fi
) >"$runphp"
[ -x "$runphp" ] || chmod +x "$runphp"
+eval "$(
+vars=(PROJDIR COMPOSERDIR COMPOSERPHAR VENDORDIR BUILDENV0 BUILDENV BUILD_FLAVOUR DIST IMAGENAME)
+arrays=(BUILD_IMAGES DISTFILES TEMPLATEFILES VARFILES)
+for var in "${vars[@]}"; do eval "$var="; done
+for array in "${arrays[@]}"; do eval "$array=()"; done
+source "$runphp"
+for var in "${vars[@]}"; do echo_setv2 "$var"; done
+for array in "${arrays[@]}"; do echo_seta2 "$array"; done
+)"
+
+estep "$(relpath "$rundir/Dockerfile.runphp")"
rsync -lpt "$MYDIR/Dockerfile.runphp" "$rundir/"
if [ -n "$projdir" ]; then
if testdiff "$rundir/build" "$MYDIR/build"; then
+ estep "$(relpath "$rundir/build")"
cp "$MYDIR/build" "$rundir/build"
chmod +x "$rundir/build"
fi
if [ ! -f "$projdir/..env.dist" ]; then
+ estep "$(relpath "$projdir/..env.dist")"
sed <"$MYDIR/dot-build.env.dist" >"$projdir/..env.dist" '
/^IMAGENAME=/s/=.*\//='"$(basename -- "$projdir")"'\//
'
initial_config=1
fi
if [ ! -f "$projdir/.runphp.conf" ]; then
+ estep "$(relpath "$projdir/.runphp.conf")"
sed <"$MYDIR/dot-runphp.conf" >"$projdir/.runphp.conf" '
/^RUNPHP=/s/=.*/=sbin\/runphp/
'
fi
+
+ sbin_path=sbin
+ cli_path=cli_config
+ if [ "$install_phpwrappers" == auto ]; then
+ if [ ! -f "$PROJDIR/$COMPOSERDIR/composer.json" ]; then
+ # ce doit être un projet PHP
+ install_phpwrappers=
+ elif [ -d "$projdir/cli_config" ]; then
+ install_phpwrappers=1
+ sbin_path=sbin
+ cli_path=cli_config
+ elif [ -d "$projdir/_cli" ]; then
+ install_phpwrappers=1
+ sbin_path=sbin
+ cli_path=_cli
+ else
+ install_phpwrappers=
+ fi
+ fi
+
+ if [ -n "$install_phpwrappers" ]; then
+ setx -a phpwrappers=ls_files "$MYDIR" "phpwrapper-*"
+ for phpwrapper in "${phpwrappers[@]}"; do
+ destname="${phpwrapper#phpwrapper-}"
+ destdir="${PHPWRAPPER_DESTDIRS[$destname]}"
+ [ -n "$destdir" ] || die "$phpwrapper: la destination n'est pas configurée"
+ mode="${PHPWRAPPER_MODES[$destname]}"
+
+ case "$destdir" in
+ @@SBIN@@) destdir="$PROJDIR/$sbin_path";;
+ @@CLI@@) destdir="$PROJDIR/$cli_path";;
+ *) destdir="$PROJDIR/$destdir";;
+ esac
+
+ estep "$(relpath "$destdir/$destname")"
+ mkdir -p "$destdir"
+ tail -n+4 "$MYDIR/$phpwrapper" | sed "
+s|/@@SBIN@@/|/$sbin_path/|
+s|/@@CLI@@/|/$cli_path/|
+" >"$destdir/$destname"
+ [ -n "$mode" ] && chmod "$mode" "$destdir/$destname"
+ done
+ fi
fi
[ -n "$initial_config" ]
diff --git a/wip/pman.md b/wip/pman.md
index 3968ad8..50460ec 100644
--- a/wip/pman.md
+++ b/wip/pman.md
@@ -6,9 +6,22 @@ outil pour gérer les projets PHP
projets dépendants du projet courant
* pver: gestion des versions.
calculer la prochaine version en respectant semver
-* pmer: gérer les branches de features et hotfixes.
-* prel: faire une release.
- ces outils peuvent agir sur les projets dépendants: faire une release sur un
- projet downstream, ou synchroniser la version depuis un projet upstream
+
+## scripts de gestion de projet
+
+définir précisément le rôle des scripts
+* pdist: créer la branche DIST, basculer dessus, merger MAIN dans DIST
+* pmain: initialiser la branche MAIN (si nouveau dépôt), basculer dessus, merger DEVELOP dans MAIN
+ (s'occupe aussi de la configuration pman.conf)
+* pdev: créer la branche DEVELOP, basculer dessus
+* pwip: créer une branche WIP, basculer dessus si unique (ou laisser le choix), merger WIP dans DEVELOP
+* PEUT-ETRE: pfix: créer une branche HOTFIX, basculer dessus si unique (ou laisser le choix), merger HOTFIX dans MAIN
+* prel: faire une release de DEVELOP dans MAIN. à terme, support des branches de hotfix
+ * il s'agit d'une spécialisation de pmain et/ou pfix pour la gestion des releases
+ * à terme, gestion en cascade des projets dépendants: release sur un projet downstream, ou synchroniser la version depuis un projet upstream
+
+il faudra supprimer
+* pman: fonctionnalités réparties dans les autres scripts spécialisés
+* pmer: fonctionnalités réperties dans les autres scripts spécialisés
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary
\ No newline at end of file