modifs.mineures sans commentaires
This commit is contained in:
parent
2a0eece015
commit
7fb34f0276
|
@ -0,0 +1,240 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\output;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use nulib\cl;
|
||||||
|
|
||||||
|
class Console implements IMessenger {
|
||||||
|
const VALID_LEVELS = [
|
||||||
|
self::LEVEL_DEBUG,
|
||||||
|
self::LEVEL_NORMAL,
|
||||||
|
self::LEVEL_MAJOR,
|
||||||
|
];
|
||||||
|
const LEVEL_MAP = [
|
||||||
|
"debug" => self::LEVEL_DEBUG,
|
||||||
|
"d" => self::LEVEL_DEBUG,
|
||||||
|
"normal" => self::LEVEL_NORMAL,
|
||||||
|
"n" => self::LEVEL_NORMAL,
|
||||||
|
"major" => self::LEVEL_MAJOR,
|
||||||
|
"m" => self::LEVEL_MAJOR,
|
||||||
|
];
|
||||||
|
|
||||||
|
const TYPE_PREFIXES = [
|
||||||
|
self::LEVEL_MAJOR => [
|
||||||
|
"section" => [true, "SECTION!", "===", "<color @b>= ", " =</color>", "==="],
|
||||||
|
"title" => ["TITLE!", null, "<color @b>T</color><color b> ", "</color>", "---"],
|
||||||
|
"desc" => ["DESC!", "<color @b>></color> ", ""],
|
||||||
|
"error" => ["CRITICAL!", "<color @r>E! ", "</color>"],
|
||||||
|
"warn" => ["ATTENTION!", "<color @y>W! ", "</color>"],
|
||||||
|
"note" => ["IMPORTANT!", "<color @g>N! ", "</color>"],
|
||||||
|
"info" => ["IMPORTANT!", "<color @b>I! ", "</color>"],
|
||||||
|
],
|
||||||
|
self::LEVEL_NORMAL => [
|
||||||
|
"section" => [true, "SECTION:", null, "<color @b>>> ", " <<</color>", "---"],
|
||||||
|
"title" => ["TITLE:", null, "<color @b>T</color><color b> ", "</color>", null],
|
||||||
|
"desc" => ["DESC:", "<color @b>></color> ", ""],
|
||||||
|
"error" => ["ERROR:", "<color @r>E</color><color r> ", "</color>"],
|
||||||
|
"warn" => ["WARN:", "<color @y>W</color><color y> ", "</color>"],
|
||||||
|
"note" => ["NOTE:", "<color @g>N</color> ", ""],
|
||||||
|
"info" => ["INFO:", "<color @b>I</color> ", ""],
|
||||||
|
],
|
||||||
|
self::LEVEL_DEBUG => [
|
||||||
|
"section" => [false, "s", null, "<color @w>>> ", " <<</color>", null],
|
||||||
|
"title" => ["t", "<color b>t ", "</color>"],
|
||||||
|
"desc" => [">", "<color b>></color> ", ""],
|
||||||
|
"error" => ["e", "<color r>e</color><color -r> ", "</color>"],
|
||||||
|
"warn" => ["w", "<color y>w</color><color -y> ", "</color>"],
|
||||||
|
"note" => ["i", "<color b>i</color> ", ""],
|
||||||
|
"info" => ["D", "<color @w>D</color><color w> ", "</color>"],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const RESULT_PREFIXES = [
|
||||||
|
"step" => ["*", "<color @w>.</color>"],
|
||||||
|
"failure" => ["(FAILURE)", "<color r>✘</color>"],
|
||||||
|
"success" => ["(SUCCESS)", "<color @g>✔</color>"],
|
||||||
|
"neutral" => [null, null],
|
||||||
|
];
|
||||||
|
|
||||||
|
function __construct(?array $params=null) {
|
||||||
|
$debug = boolval(cl::get($params, "debug"));
|
||||||
|
$minLevel = cl::get($params, "min_level");
|
||||||
|
if ($minLevel === null) $minLevel = $debug? self::LEVEL_DEBUG: self::LEVEL_NORMAL;
|
||||||
|
if (!in_array($minLevel, self::VALID_LEVELS)) {
|
||||||
|
$minLevel = cl::get(self::LEVEL_MAP, $minLevel, $minLevel);
|
||||||
|
}
|
||||||
|
if (!in_array($minLevel, self::VALID_LEVELS)) {
|
||||||
|
throw new Exception("$minLevel: invalid level");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->out = new StdOutput(STDOUT);
|
||||||
|
$this->err = new StdOutput(STDERR);
|
||||||
|
$this->minLevel = intval($minLevel);
|
||||||
|
$this->pending = [];
|
||||||
|
$this->inSection = false;
|
||||||
|
$this->titles = null;
|
||||||
|
$this->actions = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var StdOutput la sortie standard */
|
||||||
|
protected $out;
|
||||||
|
|
||||||
|
/** @var StdOutput la sortie d'erreur */
|
||||||
|
protected $err;
|
||||||
|
|
||||||
|
/** @var int level minimum que doivent avoir les messages pour être affichés */
|
||||||
|
protected $minLevel;
|
||||||
|
|
||||||
|
/** @var bool est-on dans une section? */
|
||||||
|
protected $inSection;
|
||||||
|
|
||||||
|
/** @var array|string section qui est en attente d'affichage */
|
||||||
|
protected $section;
|
||||||
|
|
||||||
|
function section($content, int $level=self::LEVEL_NORMAL): void {
|
||||||
|
$this->endSection();
|
||||||
|
$this->inSection = true;
|
||||||
|
if ($level < $this->minLevel) return;
|
||||||
|
$this->section = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function printSection() {
|
||||||
|
if ($this->section !== null) {
|
||||||
|
$this->section = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function endSection(): void {
|
||||||
|
$this->inSection = false;
|
||||||
|
$this->section = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $titles;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $currentTitle;
|
||||||
|
|
||||||
|
function title($content, int $level=self::LEVEL_NORMAL): void {
|
||||||
|
if ($level < $this->minLevel) return;
|
||||||
|
$this->titles[] = [
|
||||||
|
"title" => $content,
|
||||||
|
"descs" => [],
|
||||||
|
"print" => true,
|
||||||
|
];
|
||||||
|
$this->currentTitle =& $this->titles[count($this->titles) - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function desc($content, int $level=self::LEVEL_NORMAL): void {
|
||||||
|
if ($level < $this->minLevel) return;
|
||||||
|
$this->currentTitle["descs"][] = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function printTitles(): void {
|
||||||
|
$this->printSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function endTitle(): void {
|
||||||
|
array_pop($this->titles);
|
||||||
|
if ($this->titles) {
|
||||||
|
$this->currentTitle =& $this->titles[count($this->titles) - 1];
|
||||||
|
} else {
|
||||||
|
unset($this->currentTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function print($content, int $level=self::LEVEL_NORMAL): void {
|
||||||
|
if ($level < $this->minLevel) return;
|
||||||
|
$this->printTitles();
|
||||||
|
$this->out->print($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $actions;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $currentAction;
|
||||||
|
|
||||||
|
function action($content, int $level=self::LEVEL_NORMAL): void {
|
||||||
|
$this->actions[] = [
|
||||||
|
"level" => $level,
|
||||||
|
"contents" => [$content],
|
||||||
|
"result" => null,
|
||||||
|
"print" => true,
|
||||||
|
];
|
||||||
|
$this->currentAction =& $this->actions[count($this->actions) - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function printActions(): void {
|
||||||
|
$this->printTitles();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function step($content): void {
|
||||||
|
$this->printActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function success($content=null): void {
|
||||||
|
$this->currentAction["contents"][] = $content;
|
||||||
|
$this->currentAction["result"] = true;
|
||||||
|
$this->printActions();
|
||||||
|
$this->endAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
function failure($content=null): void {
|
||||||
|
$this->currentAction["contents"][] = $content;
|
||||||
|
$this->currentAction["result"] = false;
|
||||||
|
$this->printActions();
|
||||||
|
$this->endAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
function neutral($content=null): void {
|
||||||
|
$this->currentAction["contents"][] = $content;
|
||||||
|
$this->currentAction["result"] = null;
|
||||||
|
$this->printActions();
|
||||||
|
$this->endAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function endAction(): void {
|
||||||
|
array_pop($this->actions);
|
||||||
|
if ($this->actions) {
|
||||||
|
$this->currentAction =& $this->actions[count($this->actions) - 1];
|
||||||
|
} else {
|
||||||
|
unset($this->currentAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function info($content, int $level=self::LEVEL_NORMAL): void {
|
||||||
|
if ($level < $this->minLevel) return;
|
||||||
|
$this->printActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function note($content, int $level=self::LEVEL_NORMAL): void {
|
||||||
|
if ($level < $this->minLevel) return;
|
||||||
|
$this->printActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function warn($content, int $level=self::LEVEL_NORMAL): void {
|
||||||
|
if ($level < $this->minLevel) return;
|
||||||
|
$this->printActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function error($content, int $level=self::LEVEL_NORMAL): void {
|
||||||
|
if ($level < $this->minLevel) return;
|
||||||
|
$this->printActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function end(bool $all=false) {
|
||||||
|
if ($all) {
|
||||||
|
while ($this->actions) $this->neutral();
|
||||||
|
while ($this->titles) $this->endTitle();
|
||||||
|
$this->endSection();
|
||||||
|
} elseif ($this->actions) {
|
||||||
|
$this->endAction();
|
||||||
|
} elseif ($this->titles) {
|
||||||
|
$this->endTitle();
|
||||||
|
} else {
|
||||||
|
$this->endSection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\output;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface IMessenger: un objet pouvant afficher des messages de l'application
|
||||||
|
*/
|
||||||
|
interface IMessenger {
|
||||||
|
const LEVEL_DEBUG = -1, LEVEL_NORMAL = 0, LEVEL_MAJOR = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* commencer une section.
|
||||||
|
*
|
||||||
|
* flush tous les messages en cours. ne peut être imbriqué. on peut considérer
|
||||||
|
* que c'est une sorte de "reset" de l'objet
|
||||||
|
*/
|
||||||
|
function section($content, int $level=self::LEVEL_NORMAL): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* commencer un chapitre.
|
||||||
|
*
|
||||||
|
* peut être imbriqué dans un autre chapitre. terminer automatiquement
|
||||||
|
* l'action en cours avec un résultat neutre
|
||||||
|
*/
|
||||||
|
function title($content, int $level=self::LEVEL_NORMAL): void;
|
||||||
|
|
||||||
|
/** ajouter une description au chapitre courant. */
|
||||||
|
function desc($content, int $level=self::LEVEL_NORMAL): void;
|
||||||
|
|
||||||
|
/** afficher une donnée non structurée dans la chapitre courant */
|
||||||
|
function print($content, int $level=self::LEVEL_NORMAL): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* comencer une action dans le chapitre courant.
|
||||||
|
*
|
||||||
|
* peut être imbriqué dans une autre action.
|
||||||
|
*/
|
||||||
|
function action($content, int $level=self::LEVEL_NORMAL): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ajouter une étape à l'action courante.
|
||||||
|
*
|
||||||
|
* démarrer une action le cas échéant (et la terminer aussitôt)
|
||||||
|
*/
|
||||||
|
function step($content): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* terminer l'action courante avec le résultat "succès"
|
||||||
|
*
|
||||||
|
* démarrer une action le cas échéant (et la terminer aussitôt)
|
||||||
|
*/
|
||||||
|
function success($content=null): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* terminer l'action courante avec le résultat "échec"
|
||||||
|
*
|
||||||
|
* démarrer une action le cas échéant (et la terminer aussitôt)
|
||||||
|
*/
|
||||||
|
function failure($content=null): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* terminer l'action courante avec le résultat "neutre"
|
||||||
|
*
|
||||||
|
* démarrer une action le cas échéant (et la terminer aussitôt)
|
||||||
|
*/
|
||||||
|
function neutral($content=null): void;
|
||||||
|
|
||||||
|
/** ajouter un événément "information" */
|
||||||
|
function info($content, int $level=self::LEVEL_NORMAL): void;
|
||||||
|
|
||||||
|
/** ajouter un événément "information importante" */
|
||||||
|
function note($content, int $level=self::LEVEL_NORMAL): void;
|
||||||
|
|
||||||
|
/** ajouter un événément "avertissement" */
|
||||||
|
function warn($content, int $level=self::LEVEL_NORMAL): void;
|
||||||
|
|
||||||
|
/** ajouter un événément "erreur" */
|
||||||
|
function error($content, int $level=self::LEVEL_NORMAL): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* terminer le chapitre en cours. toutes les actions en cours sont terminées
|
||||||
|
* avec un résultat neutre.
|
||||||
|
*
|
||||||
|
* @param bool $all faut-il terminer *tous* les chapitres ainsi que la section
|
||||||
|
* en cours?
|
||||||
|
*/
|
||||||
|
function end(bool $all=false);
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\output;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use nulib\cl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class StdOutput: affichage sur STDOUT, STDERR ou dans un fichier quelconque
|
||||||
|
*
|
||||||
|
* si la destination est un fichier, aucune gestion d'erreur n'est faite, sauf
|
||||||
|
* lors de l'ouverture.
|
||||||
|
*/
|
||||||
|
class StdOutput {
|
||||||
|
const COLORS = [
|
||||||
|
"reset" => "0",
|
||||||
|
"bold" => "1",
|
||||||
|
"faint" => "2",
|
||||||
|
"underlined" => "4",
|
||||||
|
"reverse" => "7",
|
||||||
|
"normal" => "22",
|
||||||
|
"black" => "30",
|
||||||
|
"red" => "31",
|
||||||
|
"green" => "32",
|
||||||
|
"yellow" => "33",
|
||||||
|
"blue" => "34",
|
||||||
|
"magenta" => "35",
|
||||||
|
"cyan" => "36",
|
||||||
|
"white" => "37",
|
||||||
|
"default" => "39",
|
||||||
|
"black-bg" => "40",
|
||||||
|
"red-bg" => "41",
|
||||||
|
"green-bg" => "42",
|
||||||
|
"yellow-bg" => "43",
|
||||||
|
"blue-bg" => "44",
|
||||||
|
"magenta-bg" => "45",
|
||||||
|
"cyan-bg" => "46",
|
||||||
|
"white-bg" => "47",
|
||||||
|
"default-bg" => "49",
|
||||||
|
];
|
||||||
|
|
||||||
|
const COLOR_MAP = [
|
||||||
|
"z" => "reset",
|
||||||
|
"o" => "black",
|
||||||
|
"r" => "red",
|
||||||
|
"g" => "green",
|
||||||
|
"y" => "yellow",
|
||||||
|
"b" => "blue",
|
||||||
|
"m" => "magenta",
|
||||||
|
"c" => "cyan",
|
||||||
|
"w" => "white",
|
||||||
|
"O" => "black_bg",
|
||||||
|
"R" => "red_bg",
|
||||||
|
"G" => "green_bg",
|
||||||
|
"Y" => "yellow_bg",
|
||||||
|
"B" => "blue_bg",
|
||||||
|
"M" => "magenta_bg",
|
||||||
|
"C" => "cyan_bg",
|
||||||
|
"W" => "white_bg",
|
||||||
|
"@" => "bold",
|
||||||
|
"-" => "faint",
|
||||||
|
"_" => "underlined",
|
||||||
|
"~" => "reverse",
|
||||||
|
"n" => "normal",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param resource|null $outf
|
||||||
|
* @throws Exception si la destination est un fichier et que son ouverture a
|
||||||
|
* échoué
|
||||||
|
*/
|
||||||
|
function __construct($output=null, ?array $params=null) {
|
||||||
|
if ($output === null) $output = cl::get($params, "output");
|
||||||
|
$color = cl::get($params, "color");
|
||||||
|
$filterTags = cl::get($params, "filter_tags", true);
|
||||||
|
$flush = cl::get($params, "flush");
|
||||||
|
|
||||||
|
if ($output === null) {
|
||||||
|
$outf = STDOUT;
|
||||||
|
} elseif ($output === "php://stdout") {
|
||||||
|
$outf = STDOUT;
|
||||||
|
} elseif ($output === "php://stderr") {
|
||||||
|
$outf = STDERR;
|
||||||
|
} elseif (!is_resource($output)) {
|
||||||
|
# si $outf est un nom de fichier, vérifier que l'ouverture se fait sans
|
||||||
|
# erreur. à partir de là, plus aucune gestion d'erreur n'est faite
|
||||||
|
$outf = @fopen($output, "ab");
|
||||||
|
if ($outf === false) {
|
||||||
|
$error = error_get_last();
|
||||||
|
if ($error !== null) $message = $error["message"];
|
||||||
|
else $message = "$output: open error";
|
||||||
|
throw new Exception($message);
|
||||||
|
}
|
||||||
|
if ($flush === null) $flush = true;
|
||||||
|
} else {
|
||||||
|
$outf = $output;
|
||||||
|
}
|
||||||
|
if ($color === null) $color = stream_isatty($outf);
|
||||||
|
if ($flush === null) $flush = false;
|
||||||
|
|
||||||
|
$this->outf = $outf;
|
||||||
|
$this->color = boolval($color);
|
||||||
|
$this->filterTags = boolval($filterTags);
|
||||||
|
$this->flush = boolval($flush);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var resource */
|
||||||
|
protected $outf;
|
||||||
|
|
||||||
|
/** @var bool faut-il autoriser la sortie en couleur? */
|
||||||
|
protected $color;
|
||||||
|
|
||||||
|
function isColor(): bool {
|
||||||
|
return $this->color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var bool faut-il enlever les tags dans la sortie? */
|
||||||
|
protected $filterTags;
|
||||||
|
|
||||||
|
/** @var bool faut-il flush le fichier après l'écriture de chaque ligne */
|
||||||
|
protected $flush;
|
||||||
|
|
||||||
|
function isatty(): bool {
|
||||||
|
return stream_isatty($this->outf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function replace_colors(array $ms): string {
|
||||||
|
$colors = [];
|
||||||
|
foreach (preg_split('/\s+/', $ms[1]) as $color) {
|
||||||
|
while ($color && !cl::has(self::COLORS, $color)) {
|
||||||
|
$alias = substr($color, 0, 1);
|
||||||
|
$colors[] = self::COLOR_MAP[$alias];
|
||||||
|
$color = substr($color, 1);
|
||||||
|
}
|
||||||
|
if ($color) $colors[] = $color;
|
||||||
|
}
|
||||||
|
$text = "\x1B[";
|
||||||
|
$first = true;
|
||||||
|
foreach ($colors as $color) {
|
||||||
|
if (!$color) continue;
|
||||||
|
if ($first) $first = false;
|
||||||
|
else $text .= ";";
|
||||||
|
$text .= self::COLORS[$color];
|
||||||
|
}
|
||||||
|
$text .= "m";
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
protected function filterContent(string $text): string {
|
||||||
|
# couleur au début
|
||||||
|
$text = preg_replace_callback('/<color([^>]*)>/', [self::class, "replace_colors"], $text);
|
||||||
|
# reset à la fin
|
||||||
|
$text = preg_replace('/<\/color>/', "\x1B[0m", $text);
|
||||||
|
# enlever les tags classiques
|
||||||
|
if ($this->filterTags) {
|
||||||
|
$text = preg_replace('/<[^>]*>/', "", $text);
|
||||||
|
}
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
protected function filterColors(string $text): string {
|
||||||
|
return preg_replace('/\x1B\[.*?m/', "", $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function fwrite(array $values): void {
|
||||||
|
$text = implode("", $values);
|
||||||
|
$text = $this->filterContent($text);
|
||||||
|
if (!$this->color) $text = $this->filterColors($text);
|
||||||
|
fwrite($this->outf, $text);
|
||||||
|
if ($this->flush) fflush($this->outf);
|
||||||
|
}
|
||||||
|
|
||||||
|
function write(...$values): void {
|
||||||
|
$this->fwrite($values);
|
||||||
|
}
|
||||||
|
|
||||||
|
function print(...$values): void {
|
||||||
|
$values[] = "\n";
|
||||||
|
$this->fwrite($values);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
# msg
|
# IMessenger
|
||||||
|
|
||||||
* 3 niveaux: DEBUG, NORMAL, MAJOR
|
* 3 niveaux: DEBUG, NORMAL, MAJOR
|
||||||
* plusieurs types de messages:
|
* plusieurs types de messages:
|
Loading…
Reference in New Issue