modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-05-22 15:33:19 +04:00
parent a70bccbeb8
commit 6435bd0d1d
11 changed files with 369 additions and 24 deletions

View File

@ -6,9 +6,10 @@ namespace nur\sery\db;
* instance de {@link CapacitorStorage}
*/
class Capacitor {
function __construct(CapacitorStorage $storage, CapacitorChannel $channel) {
function __construct(CapacitorStorage $storage, CapacitorChannel $channel, bool $ensureExists=true) {
$this->storage = $storage;
$this->channel = $channel;
if ($ensureExists) $this->ensureExists();
}
/** @var CapacitorStorage */
@ -21,14 +22,22 @@ class Capacitor {
return $this->storage->_exists($this->channel);
}
function ensureExists(): void {
$this->storage->_ensureExists($this->channel);
}
function reset(): void {
$this->storage->_reset($this->channel);
}
function charge($item, ?callable $func=null, ?array $args=null): bool {
function charge($item, ?callable $func=null, ?array $args=null): int {
return $this->storage->_charge($this->channel, $item, $func, $args);
}
function count($filter=null): int {
return $this->storage->_count($this->channel, $filter);
}
function discharge($filter=null, ?bool $reset=null): iterable {
return $this->storage->_discharge($this->channel, $filter, $reset);
}
@ -37,8 +46,8 @@ class Capacitor {
return $this->storage->_get($this->channel, $filter);
}
function each($filter, ?callable $func=null, ?array $args=null): void {
$this->storage->_each($this->channel, $filter, $func, $args);
function each($filter, ?callable $func=null, ?array $args=null): int {
return $this->storage->_each($this->channel, $filter, $func, $args);
}
function close(): void {

View File

@ -15,6 +15,13 @@ abstract class CapacitorStorage {
return $this->_exists($this->getChannel($channel));
}
abstract function _ensureExists(CapacitorChannel $channel): void;
/** s'assurer que le canal spécifié existe */
function ensureExists(?string $channel): void {
$this->_ensureExists($this->getChannel($channel));
}
abstract function _reset(CapacitorChannel $channel): void;
/** supprimer le canal spécifié */
@ -22,7 +29,7 @@ abstract class CapacitorStorage {
$this->_reset($this->getChannel($channel));
}
abstract function _charge(CapacitorChannel $channel, $item, ?callable $func, ?array $args): bool;
abstract function _charge(CapacitorChannel $channel, $item, ?callable $func, ?array $args): int;
/**
* charger une valeur dans le canal
@ -33,13 +40,20 @@ abstract class CapacitorStorage {
* Si la fonction retourne un tableau, il est utilisé pour modifier les valeurs
* insérées/mises à jour
*
* @return true si l'objet a été chargé ou mis à jour, false s'il existait
* @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(?string $channel, $item, ?callable $func=null, ?array $args=null): bool {
function charge(?string $channel, $item, ?callable $func=null, ?array $args=null): int {
return $this->_charge($this->getChannel($channel), $item, $func, $args);
}
abstract function _count(CapacitorChannel $channel, $filter): int;
/** indiquer le nombre d'éléments du canal spécifié */
function count(?string $channel, $filter=null): int {
return $this->_count($this->getChannel($channel), $filter);
}
abstract function _discharge(CapacitorChannel $channel, $filter, ?bool $reset): iterable;
/** décharger les données du canal spécifié */
@ -58,7 +72,7 @@ abstract class CapacitorStorage {
return $this->_get($this->getChannel($channel), $filter);
}
abstract function _each(CapacitorChannel $channel, $filter, ?callable $func, ?array $args): void;
abstract function _each(CapacitorChannel $channel, $filter, ?callable $func, ?array $args): int;
/**
* appeler une fonction pour chaque élément du canal spécifié.
@ -67,9 +81,11 @@ abstract class CapacitorStorage {
*
* $func est appelé avec la signature ($item, $row, ...$args). si la fonction
* retourne un tableau, il est utilisé pour mettre à jour la ligne
*
* @return int le nombre de lignes parcourues
*/
function each(?string $channel, $filter, ?callable $func=null, ?array $args=null): void {
$this->_each($this->getChannel($channel), $filter, $func, $args);
function each(?string $channel, $filter, ?callable $func=null, ?array $args=null): int {
return $this->_each($this->getChannel($channel), $filter, $func, $args);
}
abstract function close(): void;

View File

@ -68,6 +68,10 @@ class SqliteCapacitor extends CapacitorStorage {
return $tableName !== null;
}
function _ensureExists(CapacitorChannel $channel): void {
$this->_create($channel);
}
function _reset(CapacitorChannel $channel): void {
$this->sqlite->exec([
"drop table if exists",
@ -76,7 +80,7 @@ class SqliteCapacitor extends CapacitorStorage {
$channel->setCreated(false);
}
function _charge(CapacitorChannel $channel, $item, ?callable $func, ?array $args): bool {
function _charge(CapacitorChannel $channel, $item, ?callable $func, ?array $args): int {
$this->_create($channel);
$now = date("Y-m-d H:i:s");
$_item = serialize($item);
@ -129,7 +133,7 @@ class SqliteCapacitor extends CapacitorStorage {
if ($insert === null) {
# aucune modification
return false;
return 0;
} elseif ($insert) {
$this->sqlite->exec([
"insert",
@ -144,7 +148,16 @@ class SqliteCapacitor extends CapacitorStorage {
"where" => ["_id" => $id],
]);
}
return true;
return 1;
}
function _count(CapacitorChannel $channel, $filter): int {
if ($filter !== null && !is_array($filter)) $filter = ["_id" => $filter];
return $this->sqlite->get([
"select count(*)",
"from" => $channel->getTableName(),
"where" => $filter,
]);
}
function _discharge(CapacitorChannel $channel, $filter, ?bool $reset): iterable {
@ -174,13 +187,14 @@ class SqliteCapacitor extends CapacitorStorage {
else return unserialize($row["_item"]);
}
function _each(CapacitorChannel $channel, $filter, ?callable $func, ?array $args): void {
function _each(CapacitorChannel $channel, $filter, ?callable $func, ?array $args): int {
if ($func === null) $func = [$channel, "onEach"];
$onEach = func::_prepare($func);
if ($filter !== null && !is_array($filter)) $filter = ["_id" => $filter];
$sqlite = $this->sqlite;
$tableName = $channel->getTableName();
$commited = false;
$count = 0;
$sqlite->beginTransaction();
try {
$rows = $sqlite->all([
@ -203,9 +217,11 @@ class SqliteCapacitor extends CapacitorStorage {
"where" => ["_id" => $row["_id"]],
]);
}
$count++;
}
$sqlite->commit();
$commited = true;
return $count;
} finally {
if (!$commited) $sqlite->rollback();
}

View File

@ -70,6 +70,17 @@ interface IMessenger {
*/
function adone($content=null, ?int $overrideLevel=null): void;
/**
* terminer l'action courante avec le résultat "succès", "échec" ou "neutre"
* en fonction de la valeur de $result
* - si c'est un booléen, true vaut succès, false vaut échec
* - si c'est une exception, c'est un échec et le message est affiché
* - sinon, le résultat est neutre et le message est affiché s'il n'est pas null
*
* démarrer une action le cas échéant (et la terminer aussitôt)
*/
function aresult($result=null, ?int $overrideLevel=null): void;
/** afficher une donnée non structurée */
function print($content, ?int $level=null): void;

View File

@ -4,8 +4,22 @@
rotation des logs
* [ ] lors de la rotation, si l'ouverture du nouveau fichier échoue, continuer
à écrire dans l'ancien fichier
* ou alors un moyen pour ré-ouvrir la sortie, afin de pouvoir indiquer à un
long running process qu'une rotation a eu lieu
* [ ] dans `StdMessenger::resetParams()`, `[output]` peut être une instance de
StdOutput pour mettre à jour $out ET $err, ou un tableau de deux éléments pour
mettre à jour séparément $out et $err
* [ ] vérifier que la date affichée pour un TITLE est celle à laquelle l'appel
a été fait, même si le premier événement en dessous arrive bien plus tard
* [ ] pareil pour action: sauf si c'est une seule ligne, la date de action est
la date du premier appel, alors que la date de $result est celui du result si
c'est affiché sur une autre ligne
* réorganiser pour que msg:: attaque un proxy dans lequel est configuré un
ensemble standard de sorties: say, log, debuglog
* `--aD, --av, --aq, --asilent` pour les valeurs d'ajustement qui sont un
incrément à la valeur courante (+2, +1, -1, -2)
* `--yD, --yv, --yq, --ysilent, -D, -v, -q, --silent` pour say
* `--lD, --lv, --lq, --lsilent` pour log, `-L:, --L` l'active
* `--DD, --Dv, --Dq, --Dsilent` pour debuglog, `--DL:` l'active
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

View File

@ -62,6 +62,7 @@ abstract class _messenger {
static function asuccess($content=null, ?int $override_level=null): void { static::get()->asuccess($content, $override_level); }
static function afailure($content=null, ?int $override_level=null): void { static::get()->afailure($content, $override_level); }
static function adone($content=null, ?int $override_level=null): void { static::get()->adone($content, $override_level); }
static function aresult($result=null, ?int $override_level=null): void { static::get()->aresult($result, $override_level); }
static function print($content, ?int $level=null): void { static::get()->print($content, $level); }
static function info($content, ?int $level=null): void { static::get()->info($content, $level); }
static function note($content, ?int $level=null): void { static::get()->note($content, $level); }

View File

@ -85,12 +85,11 @@ class ProxyMessenger implements IMessenger {
if ($useFunc && $func !== null) {
try {
$result = $func($this);
if ($result !== null) {
/** @var _IMessenger $msg */
foreach ($this->msgs as $msg) {
if ($result === true) $msg->asuccess();
elseif ($result === false) $msg->afailure();
else $msg->adone($result);
/** @var _IMessenger $msg */
$index = 0;
foreach ($this->msgs as $msg) {
if ($msg->_getActionMark() > $untils[$index++]) {
$msg->aresult($result);
}
}
} catch (Exception $e) {
@ -112,6 +111,7 @@ class ProxyMessenger implements IMessenger {
function asuccess($content=null, ?int $overrideLevel=null): void { foreach ($this->msgs as $msg) { $msg->asuccess($content, $overrideLevel); } }
function afailure($content=null, ?int $overrideLevel=null): void { foreach ($this->msgs as $msg) { $msg->afailure($content, $overrideLevel); } }
function adone($content=null, ?int $overrideLevel=null): void { foreach ($this->msgs as $msg) { $msg->adone($content, $overrideLevel); } }
function aresult($result=null, ?int $overrideLevel=null): void { foreach ($this->msgs as $msg) { $msg->aresult($result, $overrideLevel); } }
function print($content, ?int $level=null): void { foreach ($this->msgs as $msg) { $msg->print($content, $level); } }
function info($content, ?int $level=null): void { foreach ($this->msgs as $msg) { $msg->info($content, $level); } }
function note($content, ?int $level=null): void { foreach ($this->msgs as $msg) { $msg->note($content, $level); } }

View File

@ -580,10 +580,8 @@ class StdMessenger implements _IMessenger {
if ($func !== null) {
try {
$result = $func($this);
if ($result !== null) {
if ($result === true) $this->asuccess();
elseif ($result === false) $this->afailure();
else $this->adone($result);
if ($this->_getActionMark() > $until) {
$this->aresult($result);
}
} catch (Exception $e) {
$this->afailure($e);
@ -653,6 +651,14 @@ class StdMessenger implements _IMessenger {
$this->printActions(true, $overrideLevel);
}
function aresult($result=null, ?int $overrideLevel=null): void {
if (!$this->actions) $this->action(null);
if ($result === true) $this->asuccess(null, $overrideLevel);
elseif ($result === false) $this->afailure(null, $overrideLevel);
elseif ($result instanceof Exception) $this->afailure($result, $overrideLevel);
else $this->adone($result, $overrideLevel);
}
function _endAction(?int $until=null): void {
if ($until === null) $until = $this->_getActionMark() - 1;
while (count($this->actions) > $until) {

212
src/text/Word.php Normal file
View File

@ -0,0 +1,212 @@
<?php
namespace nur\sery\text;
use nur\b\ValueException;
use nur\txt;
/**
* Class Word: accord d'un nom ou d'un adjectif en genre et en nombre
*
* Pour accorder un nom, construire l'objet avec une spécification de la forme
* "GENRE:ARTICLE NOM"
* - L'article peut être "l'", "le", "la". Le genre est requis avec "l'"
* - Le genre peut être "masculin:" ou "féminin:"
* - Le nom est composé d'un ou plusieurs mots qui se terminent par
* - #s pour un pluriel en "s", e.g porte#s
* - #x pour un pluriel en "x", e.g lieu#x
* - rien si le mot est invariable
*
* Pour accorder un adjectif, la spécification peut se limiter à "ADJECTIF"
* - L'adjectif est composé d'un ou plusieurs mots, qui en plus des marques du
* pluriel peuvent se terminent par
* - #e pour indiquer la marque du féminin, e.g "né#e"
* - rien si le mot est invariable
*
* Chaque mot peut aussi commencer par "^" pour indiquer les caractères qui
* peuvent être mis en majuscule par la méthode u(). Par défaut, seule la
* première lettre est mise en majuscule
*/
class Word {
/** @var bool le mot est-il féminin? */
private $fem;
/** @var string article "le", "la", "l'" */
private $le;
/** @var string article "du", "de la", "de l'" */
private $du;
/** @var string article "au", "à la", "à l'" */
private $au;
/** @var string le mot sans article */
private $w;
function __construct(string $spec, bool $adjective=false) {
if (preg_match('/^f([eé]m(inin)?)?\s*:\s*/iu', $spec, $ms)) {
$fem = true;
$spec = substr($spec, strlen($ms[0]));
} elseif (preg_match('/^m(asc(ulin)?)?\s*:\s*/i', $spec, $ms)) {
$fem = false;
$spec = substr($spec, $ms[0]);
} elseif (preg_match('/\s*\|f(?:[eé]m(?:inin)?)?\s*$/iu', $spec, $ms, PREG_OFFSET_CAPTURE)) {
$fem = true;
$spec = substr($spec, 0, $ms[0][1]);
} elseif (preg_match('/\s*\|m(?:asc(?:ulin)?)?\s*$/i', $spec, $ms, PREG_OFFSET_CAPTURE)) {
$fem = false;
$spec = substr($spec, 0, $ms[0][1]);
} else {
$fem = null;
}
if (preg_match('/^l\'\s*/i', $spec, $ms) && $fem !== null) {
$le = "l'";
$du = "de l'";
$au = "à l'";
$spec = substr($spec, strlen($ms[0]));
} elseif (preg_match('/^la\s+/i', $spec, $ms)) {
$fem = true;
$le = "la ";
$du = "de la ";
$au = "à la ";
$spec = substr($spec, strlen($ms[0]));
} elseif (preg_match('/^le\s+/i', $spec, $ms)) {
$fem = false;
$le = "le ";
$du = "du ";
$au = "au ";
$spec = substr($spec, strlen($ms[0]));
} else {
$le = null;
$du = null;
$au = null;
}
if (!$adjective) {
# si c'est un nom, il faut l'article et le genre
if ($fem === null) {
throw new ValueException("Vous devez spécifier le genre du nom");
} elseif ($le === null || $du === null || $au === null) {
throw new ValueException("Vous devez spécifier l'article du nom");
}
}
$this->fem = $fem;
$this->le = $le;
$this->du = $du;
$this->au = $au;
$this->w = $spec;
}
/**
* retourner le mot sans article
*
* @param bool|int $amount nombre du nom, avec l'équivalence false===0 et
* true===2. à partir de 2, le mot est ecrit au pluriel
* @param bool|string $fem genre du nom avec lequel accorder les adjectifs,
* avec l'équivalence false==="M" et true==="F"
*/
function w($amount=1, bool $upper1=false, $fem=false): string {
if ($amount === true) $amount = 2;
elseif ($amount === false) $amount = 0;
$amount = abs($amount);
$w = $this->w;
# marque du nombre
if ($amount <= 1) {
$w = preg_replace('/#[sx]/', "", $w);
} else {
$w = preg_replace('/#([sx])/', "$1", $w);
}
# marque du genre
if ($fem === "f" || $fem === "F") $fem = true;
elseif ($fem === "m" || $fem === "M") $fem = false;
$repl = $fem? "$1": "";
$w = preg_replace('/#([e])/', $repl, $w);
# mise en majuscule
if ($upper1) {
if (strpos($w, "^") === false) {
# uniquement la première lettre
$w = txt::upper1($w);
} else {
# toutes les lettres qui suivent les occurences de ^
$w = preg_replace_callback('/\^([[:alpha:]])/u', function ($ms) {
return mb_strtoupper($ms[1]);
}, $w);
}
}
return $w;
}
/**
* retourner le mot sans article avec la première lettre en majuscule.
* alias pour $this->w($amount, true, $fem)
*
* @param bool|int $amount
*/
function u($amount=1, $fem=false): string {
return $this->w($amount, true, $fem);
}
/**
* retourner l'adjectif accordé avec le genre spécifié.
* alias pour $this->w($amount, false, $fem)
*
* @param bool|int $amount
*/
function a($fem=false, $amount=1): string {
return $this->w($amount, false, $fem);
}
/** retourner le mot sans article et avec la quantité */
function q(int $amount=1, $fem=false): string {
return $amount." ".$this->w($amount, $fem);
}
/** retourner le mot sans article et avec la quantité $amount/$max */
function r(int $amount, int $max, $fem=false): string {
return "$amount/$max ".$this->w($amount, $fem);
}
/** retourner le mot avec l'article indéfini et la quantité */
function un(int $amount=1, $fem=false): string {
$abs_amount = abs($amount);
if ($abs_amount == 0) {
$aucun = $this->fem? "aucune ": "aucun ";
return $aucun.$this->w($amount, $fem);
} elseif ($abs_amount == 1) {
$un = $this->fem? "une ": "un ";
return $un.$this->w($amount, $fem);
} else {
return "les $amount ".$this->w($amount, $fem);
}
}
function le(int $amount=1, $fem=false): string {
$abs_amount = abs($amount);
if ($abs_amount == 0) {
$le = $this->fem? "la 0 ": "le 0 ";
return $le.$this->w($amount, $fem);
} elseif ($abs_amount == 1) {
return $this->le.$this->w($amount, $fem);
} else {
return "les $amount ".$this->w($amount, $fem);
}
}
function du(int $amount=1, $fem=false): string {
$abs_amount = abs($amount);
if ($abs_amount == 0) {
$du = $this->fem? "de la 0 ": "du 0 ";
return $du.$this->w($amount, $fem);
} elseif ($abs_amount == 1) {
return $this->du.$this->w($amount, $fem);
} else {
return "des $amount ".$this->w($amount, $fem);
}
}
function au(int $amount=1, $fem=false): string {
$abs_amount = abs($amount);
if ($abs_amount == 0) {
$au = $this->fem? "à la 0 ": "au 0 ";
return $au.$this->w($amount, $fem);
} elseif ($abs_amount == 1) {
return $this->au.$this->w($amount, $fem);
} else {
return "aux $amount ".$this->w($amount, $fem);
}
}
}

14
src/text/words.php Normal file
View File

@ -0,0 +1,14 @@
<?php
namespace nur\sery\text;
class words {
static function q(int $count, string $spec, bool $adjective=true): string {
$word = new Word($spec, $adjective);
return $word->q($count);
}
static function r(int $count, int $max, string $spec, bool $adjective=true): string {
$word = new Word($spec, $adjective);
return $word->r($count, $max);
}
}

View File

@ -30,5 +30,51 @@ Application::run(new class extends Application {
});
$msg->asuccess();
});
msg::title("auto result");
msg::action("sans result 1", function ($msg) {
return true;
});
msg::print("hello 1");
msg::action("sans result 2", function ($msg) {
return false;
});
msg::print("hello 2");
msg::action("sans result 3", function ($msg) {
return "whatever";
});
msg::print("hello 3");
try {
msg::action("sans result 4", function ($msg) {
throw new Exception();
});
} catch (Exception $e) {}
msg::print("hello 4");
msg::action("avec result 1", function ($msg) {
$msg->asuccess();
});
msg::print("hello 1");
msg::action("avec result 2", function ($msg) {
$msg->afailure();
});
msg::print("hello 2");
msg::action("avec result 3", function ($msg) {
$msg->adone();
});
msg::print("hello 3");
try {
msg::action("avec result 4", function ($msg) {
$msg->aresult(new Exception());
});
} catch (Exception $e) {}
msg::print("hello 4");
msg::end();
}
});