intégration output et web
This commit is contained in:
parent
080ccc4f4d
commit
b817ad6c98
|
@ -5,6 +5,7 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/php/src_base" isTestSource="false" packagePrefix="nulib\" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/php/tests" isTestSource="true" packagePrefix="nulib\" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/php/src_output" isTestSource="false" packagePrefix="nulib\output\" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/php/src_web" isTestSource="false" packagePrefix="nulib\web\" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
##@cooked nocomments
|
||||
module: base.tools "Fonctions de base: outils divers"
|
||||
|
||||
function: mkdirof 'Créer le répertoire correspondant au fichier $1'
|
||||
function mkdirof() {
|
||||
mkdir -p "$(dirname -- "$1")"
|
||||
}
|
||||
|
||||
function __la_cmd() {
|
||||
[ $# -gt 0 ] || set '*'
|
||||
local arg
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
"autoload": {
|
||||
"psr-4": {
|
||||
"nulib\\": "php/src_base",
|
||||
"nulib\\output\\": "php/src_output"
|
||||
"nulib\\output\\": "php/src_output",
|
||||
"nulib\\web\\": "php/src_web"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
namespace nulib\output;
|
||||
|
||||
/**
|
||||
* Interface IContent: un objet capable de produire du contenu à afficher
|
||||
*/
|
||||
interface IContent {
|
||||
/** retourner le contenu à afficher */
|
||||
function getContent(): iterable;
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
namespace nulib\output;
|
||||
|
||||
/**
|
||||
* Interface IMessenger: un objet pouvant afficher des messages de l'application
|
||||
*/
|
||||
interface IMessenger {
|
||||
const DEBUG = -1, MINOR = 0, NORMAL = 1, MAJOR = 2, NONE = 3;
|
||||
const MIN_LEVEL = self::DEBUG, MAX_LEVEL = self::MAJOR;
|
||||
|
||||
/** réinitialiser les paramètres de l'objet */
|
||||
function resetParams(?array $params=null): void;
|
||||
|
||||
/**
|
||||
* créer une copie de cet objet avec éventuellement des paramètres différents
|
||||
*/
|
||||
function clone(?array $params=null): self;
|
||||
|
||||
/**
|
||||
* 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, ?callable $func=null, ?int $level=null): 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, ?callable $func=null, ?int $level=null): void;
|
||||
|
||||
/** ajouter une description au chapitre courant. */
|
||||
function desc($content, ?int $level=null): void;
|
||||
|
||||
/**
|
||||
* comencer une action dans le chapitre courant.
|
||||
*
|
||||
* peut être imbriqué dans une autre action.
|
||||
*/
|
||||
function action($content, ?callable $func=null, ?int $level=null): void;
|
||||
|
||||
/**
|
||||
* ajouter une étape à l'action courante.
|
||||
*
|
||||
* démarrer une action le cas échéant (et la terminer aussitôt)
|
||||
*/
|
||||
function step($content, ?int $level=null): 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 asuccess($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 afailure($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 adone($content=null): void;
|
||||
|
||||
/** afficher une donnée non structurée */
|
||||
function print($content, ?int $level=null): void;
|
||||
|
||||
/** afficher un événément "information" */
|
||||
function info($content, ?int $level=null): void;
|
||||
|
||||
/** afficher un événément "information importante" */
|
||||
function note($content, ?int $level=null): void;
|
||||
|
||||
/** afficher un événément "avertissement" */
|
||||
function warn($content, ?int $level=null): void;
|
||||
|
||||
/** afficher un événément "erreur" */
|
||||
function error($content, ?int $level=null): 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): void;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
namespace nulib\output;
|
||||
|
||||
/**
|
||||
* Interface IPrintable: un objet qui peut écrire du contenu sur la sortie
|
||||
* standard
|
||||
*/
|
||||
interface IPrintable {
|
||||
/** afficher le contenu */
|
||||
function print(): void;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
# TOOD
|
||||
|
||||
* [ ] possibilité de paramétrer le nom du fichier destination pour faire une
|
||||
rotation des logs
|
||||
* [ ] lors de la rotation, si l'ouverture du nouveau fichier échoue, continuer
|
||||
à écrire dans l'ancien fichier
|
||||
|
||||
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
namespace nulib\output;
|
||||
|
||||
use nulib\str;
|
||||
use nulib\ValueException;
|
||||
|
||||
/**
|
||||
* Class _messenger: classe de base pour say, log et msg
|
||||
*/
|
||||
abstract class _messenger {
|
||||
/** @var IMessenger */
|
||||
protected static $say;
|
||||
|
||||
/** @var IMessenger */
|
||||
protected static $log;
|
||||
|
||||
abstract static function get(): IMessenger;
|
||||
|
||||
/** obtenir une nouvelle instance, avec un nouveau paramétrage */
|
||||
static function new(?array $params=null): IMessenger {
|
||||
return static::get()->clone($params);
|
||||
}
|
||||
|
||||
/** @var IMessenger */
|
||||
protected static $msg;
|
||||
|
||||
/** @var IMessenger[] */
|
||||
protected static $stack;
|
||||
|
||||
/** pousser une nouvelle instance avec un nouveau paramétrage sur la pile */
|
||||
static function push(?array $params=null) {
|
||||
self::$stack[] = static::get();
|
||||
self::$msg = self::new($params);
|
||||
}
|
||||
|
||||
/** dépiler la précédente instance */
|
||||
static function pop(): IMessenger {
|
||||
if (self::$stack) $msg = self::$msg = array_pop(self::$stack);
|
||||
else $msg = self::$msg;
|
||||
return $msg;
|
||||
}
|
||||
|
||||
static final function __callStatic($name, $args) {
|
||||
$name = str::us2camel($name);
|
||||
call_user_func_array([static::get(), $name], $args);
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
|
||||
const DEBUG = IMessenger::DEBUG;
|
||||
const MINOR = IMessenger::MINOR;
|
||||
const NORMAL = IMessenger::NORMAL;
|
||||
const MAJOR = IMessenger::MAJOR;
|
||||
const NONE = IMessenger::NONE;
|
||||
|
||||
static function reset_params(?array $params=null): void { static::get()->resetParams($params); }
|
||||
static function section($content, ?callable $func=null, ?int $level=null): void { static::get()->section($content, $func, $level); }
|
||||
static function title($content, ?callable $func=null, ?int $level=null): void { static::get()->title($content, $func, $level); }
|
||||
static function desc($content, ?int $level=null): void { static::get()->desc($content, $level); }
|
||||
static function action($content, ?callable $func=null, ?int $level=null): void { static::get()->action($content, $func, $level); }
|
||||
static function step($content, ?int $level=null): void { static::get()->step($content, $level); }
|
||||
static function asuccess($content=null): void { static::get()->asuccess($content); }
|
||||
static function afailure($content=null): void { static::get()->afailure($content); }
|
||||
static function adone($content=null): void { static::get()->adone($content); }
|
||||
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); }
|
||||
static function warn($content, ?int $level=null): void { static::get()->warn($content, $level); }
|
||||
static function error($content, ?int $level=null): void { static::get()->error($content, $level); }
|
||||
static function end(bool $all=false): void { static::get()->end($all); }
|
||||
|
||||
static function debug($content): void { self::info($content, self::DEBUG);}
|
||||
static function normal($content): void { self::info($content, self::NORMAL);}
|
||||
static function minor($content): void { self::info($content, self::MINOR);}
|
||||
static function important($content): void { self::info($content, self::MAJOR);}
|
||||
static function attention($content): void { self::note($content, self::MAJOR);}
|
||||
static function critwarn($content): void { self::warn($content, self::MAJOR);}
|
||||
static function criterror($content): void { self::error($content, self::MAJOR);}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
namespace nulib\output;
|
||||
|
||||
use nulib\ValueException;
|
||||
use nulib\output\std\ProxyMessenger;
|
||||
|
||||
/**
|
||||
* Class log: inscrire un message dans les logs uniquement
|
||||
*/
|
||||
class log extends _messenger {
|
||||
static function set_messenger(IMessenger $log=null) {
|
||||
self::$log = $log;
|
||||
}
|
||||
|
||||
static function set_messenger_class(string $log_class=null, ?array $params=null) {
|
||||
if (!is_subclass_of($log_class, IMessenger::class)) {
|
||||
throw ValueException::invalid_class($log_class, IMessenger::class);
|
||||
}
|
||||
self::$log = new $log_class($params);
|
||||
}
|
||||
|
||||
static function get(): IMessenger {
|
||||
if (self::$msg === null) {
|
||||
$msg = self::$log;
|
||||
if ($msg === null) $msg = new ProxyMessenger();
|
||||
self::$msg = $msg;
|
||||
}
|
||||
return self::$msg;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
namespace nulib\output;
|
||||
|
||||
use nulib\ValueException;
|
||||
use nulib\output\std\ProxyMessenger;
|
||||
|
||||
/**
|
||||
* Class msg: inscrire un message dans les logs ET l'afficher sur la console
|
||||
*/
|
||||
class msg extends _messenger {
|
||||
static function set_messenger(IMessenger $say, ?IMessenger $log=null) {
|
||||
self::$say = $say;
|
||||
if ($log !== null) self::$log = $log;
|
||||
}
|
||||
|
||||
static function set_messenger_class(string $say_class, ?string $log_class=null) {
|
||||
if (!is_subclass_of($say_class, IMessenger::class)) {
|
||||
throw ValueException::invalid_class($say_class, IMessenger::class);
|
||||
}
|
||||
self::$say = new $say_class();
|
||||
if ($log_class !== null) {
|
||||
if (!is_subclass_of($log_class, IMessenger::class)) {
|
||||
throw ValueException::invalid_class($log_class, IMessenger::class);
|
||||
}
|
||||
self::$log = new $log_class();
|
||||
}
|
||||
}
|
||||
|
||||
static function get(): IMessenger {
|
||||
if (self::$msg === null) {
|
||||
$log = self::$log;
|
||||
$say = self::$say;
|
||||
if ($log !== null && $say !== null) $msg = new ProxyMessenger($log, $say);
|
||||
elseif ($log !== null) $msg = $log;
|
||||
elseif ($say !== null) $msg = $say;
|
||||
else $msg = new ProxyMessenger();
|
||||
self::$msg = $msg;
|
||||
}
|
||||
return self::$msg;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
namespace nulib\output;
|
||||
|
||||
use nulib\ValueException;
|
||||
use nulib\output\std\ProxyMessenger;
|
||||
|
||||
/**
|
||||
* Class say: afficher un message sur la console uniquement
|
||||
*/
|
||||
class say extends _messenger {
|
||||
static function set_messenger(IMessenger $say) {
|
||||
self::$say = $say;
|
||||
}
|
||||
|
||||
static function set_messenger_class(string $say_class, ?array $params=null) {
|
||||
if (!is_subclass_of($say_class, IMessenger::class)) {
|
||||
throw ValueException::invalid_class($say_class, IMessenger::class);
|
||||
}
|
||||
self::$say = new $say_class($params);
|
||||
}
|
||||
|
||||
static function get(): IMessenger {
|
||||
if (self::$msg === null) {
|
||||
$msg = self::$say;
|
||||
if ($msg === null) $msg = new ProxyMessenger();
|
||||
self::$msg = $msg;
|
||||
}
|
||||
return self::$msg;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
namespace nulib\output\std;
|
||||
|
||||
use nulib\output\IMessenger;
|
||||
|
||||
/**
|
||||
* Class ProxyMessenger: un proxy vers ou un plusieurs instances de IMessenger
|
||||
*/
|
||||
class ProxyMessenger implements IMessenger {
|
||||
function __construct(?IMessenger ...$msgs) {
|
||||
$this->msgs = [];
|
||||
foreach ($msgs as $msg) {
|
||||
if ($msg !== null) $this->msgs[] = $msg;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var IMessenger[] */
|
||||
protected $msgs;
|
||||
|
||||
function resetParams(?array $params=null): void { foreach ($this->msgs as $msg) { $msg->resetParams($params); } }
|
||||
function clone(?array $params=null): self {
|
||||
$clone = clone $this;
|
||||
foreach ($clone->msgs as &$msg) {
|
||||
$msg = $msg->clone($params);
|
||||
}; unset($msg);
|
||||
return $clone;
|
||||
}
|
||||
function section($content, ?callable $func=null, ?int $level=null): void {
|
||||
$useFunc = false;
|
||||
foreach ($this->msgs as $msg) {
|
||||
$msg->section($content, null, $level);
|
||||
if ($msg instanceof _IMessenger) $useFunc = true;
|
||||
}
|
||||
if ($useFunc && $func !== null) {
|
||||
try {
|
||||
$func($this);
|
||||
} finally {
|
||||
/** @var _IMessenger $msg */
|
||||
foreach ($this->msgs as $msg) {
|
||||
$msg->_endSection();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function title($content, ?callable $func=null, ?int $level=null): void {
|
||||
$useFunc = false;
|
||||
$untils = [];
|
||||
foreach ($this->msgs as $msg) {
|
||||
$msg->title($content, null, $level);
|
||||
if ($msg instanceof _IMessenger) {
|
||||
$useFunc = true;
|
||||
$untils[] = $msg->_getTitleMark();
|
||||
}
|
||||
}
|
||||
if ($useFunc && $func !== null) {
|
||||
try {
|
||||
$func($this);
|
||||
} finally {
|
||||
/** @var _IMessenger $msg */
|
||||
$index = 0;
|
||||
foreach ($this->msgs as $msg) {
|
||||
$msg->_endTitle($untils[$index++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function desc($content, ?int $level=null): void { foreach ($this->msgs as $msg) { $msg->desc($content, $level); } }
|
||||
function action($content, ?callable $func=null, ?int $level=null): void {
|
||||
$useFunc = false;
|
||||
$untils = [];
|
||||
foreach ($this->msgs as $msg) {
|
||||
$msg->action($content, null, $level);
|
||||
if ($msg instanceof _IMessenger) {
|
||||
$useFunc = true;
|
||||
$untils[] = $msg->_getTitleMark();
|
||||
}
|
||||
}
|
||||
if ($useFunc && $func !== null) {
|
||||
try {
|
||||
$func($this);
|
||||
} finally {
|
||||
/** @var _IMessenger $msg */
|
||||
$index = 0;
|
||||
foreach ($this->msgs as $msg) {
|
||||
$msg->_endAction($untils[$index++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function step($content, ?int $level=null): void { foreach ($this->msgs as $msg) { $msg->step($content, $level); } }
|
||||
function asuccess($content=null): void { foreach ($this->msgs as $msg) { $msg->asuccess($content); } }
|
||||
function afailure($content=null): void { foreach ($this->msgs as $msg) { $msg->afailure($content); } }
|
||||
function adone($content=null): void { foreach ($this->msgs as $msg) { $msg->adone($content); } }
|
||||
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); } }
|
||||
function warn($content, ?int $level=null): void { foreach ($this->msgs as $msg) { $msg->warn($content, $level); } }
|
||||
function error($content, ?int $level=null): void { foreach ($this->msgs as $msg) { $msg->error($content, $level); } }
|
||||
function end(bool $all=false): void { foreach ($this->msgs as $msg) { $msg->end($all); } }
|
||||
}
|
|
@ -0,0 +1,693 @@
|
|||
<?php
|
||||
namespace nulib\output\std;
|
||||
|
||||
use Exception;
|
||||
use nulib\cl;
|
||||
use nulib\ExceptionShadow;
|
||||
use nulib\UserException;
|
||||
use nulib\output\IMessenger;
|
||||
use Throwable;
|
||||
|
||||
class StdMessenger implements _IMessenger {
|
||||
const INDENT = " ";
|
||||
const DATE_FORMAT = 'Y-m-d\TH:i:s.u';
|
||||
|
||||
const VALID_LEVELS = [self::DEBUG, self::MINOR, self::NORMAL, self::MAJOR, self::NONE];
|
||||
const LEVEL_MAP = [
|
||||
"debug" => self::DEBUG,
|
||||
"minor" => self::MINOR, "verbose" => self::MINOR,
|
||||
"normal" => self::NORMAL,
|
||||
"major" => self::MAJOR, "quiet" => self::MAJOR,
|
||||
"none" => self::NONE, "silent" => self::NONE,
|
||||
];
|
||||
|
||||
protected static function verifix_level($level, int $max_level=self::MAX_LEVEL): int {
|
||||
if (!in_array($level, self::VALID_LEVELS, true)) {
|
||||
$level = cl::get(self::LEVEL_MAP, $level, $level);
|
||||
}
|
||||
if (!in_array($level, self::VALID_LEVELS, true)) {
|
||||
throw new Exception("$level: invalid level");
|
||||
}
|
||||
if ($level > $max_level) {
|
||||
throw new Exception("$level: level not allowed here");
|
||||
}
|
||||
return $level;
|
||||
}
|
||||
|
||||
const GENERIC_PREFIXES = [
|
||||
self::MAJOR => [
|
||||
"section" => [true, "SECTION!", "===", "<color @b>=", "=</color>", "==="],
|
||||
"title" => [false, "TITLE!", null, "<color @b>T", "</color>", "==="],
|
||||
"desc" => ["DESC!", "<color @b>></color>", ""],
|
||||
"error" => ["CRIT.ERROR!", "<color @r>E!", "</color>"],
|
||||
"warn" => ["CRIT.WARN!", "<color @y>W!", "</color>"],
|
||||
"note" => ["ATTENTION!", "<color @g>N!", "</color>"],
|
||||
"info" => ["IMPORTANT!", "<color @b>N!", "</color>"],
|
||||
"step" => ["*", "<color @w>.</color>", ""],
|
||||
"print" => [null, null, null],
|
||||
],
|
||||
self::NORMAL => [
|
||||
"section" => [true, "SECTION:", "---", "<color @b>-", "-</color>", "---"],
|
||||
"title" => [false, "TITLE:", null, "<color @b>T</color><color b>", "</color>", "---"],
|
||||
"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>", ""],
|
||||
"step" => ["*", "<color @w>.</color>", ""],
|
||||
"print" => [null, null, null],
|
||||
],
|
||||
self::MINOR => [
|
||||
"section" => [true, "section", null, "<color @w>>>", "<<</color>", null],
|
||||
"title" => [false, "title", null, "<color b>t", "</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><color w>", "</color>"],
|
||||
"step" => ["*", "<color w>.</color>", ""],
|
||||
"print" => [null, null, null],
|
||||
],
|
||||
self::DEBUG => [
|
||||
"section" => [true, "section", null, "<color @w>>>", "<<</color>", null],
|
||||
"title" => [false, "title", null, "<color b>t", "</color>", null],
|
||||
"desc" => ["desc", "<color b>></color>", ""],
|
||||
"error" => ["debugE", "<color r>e</color><color -r>", "</color>"],
|
||||
"warn" => ["debugW", "<color y>w</color><color -y>", "</color>"],
|
||||
"note" => ["debugN", "<color b>i</color>", ""],
|
||||
"info" => ["debug", "<color @w>D</color><color w>", "</color>"],
|
||||
"step" => ["*", "<color w>.</color>", ""],
|
||||
"print" => [null, null, null],
|
||||
],
|
||||
];
|
||||
|
||||
const RESULT_PREFIXES = [
|
||||
"failure" => ["(FAILURE)", "<color r>✘</color>"],
|
||||
"success" => ["(SUCCESS)", "<color @g>✔</color>"],
|
||||
"done" => [null, null],
|
||||
];
|
||||
|
||||
function __construct(?array $params=null) {
|
||||
$output = cl::get($params, "output");
|
||||
$color = cl::get($params, "color");
|
||||
$indent = cl::get($params, "indent", static::INDENT);
|
||||
|
||||
$defaultLevel = cl::get($params, "default_level");
|
||||
if ($defaultLevel === null) $defaultLevel = self::NORMAL;
|
||||
$defaultLevel = self::verifix_level($defaultLevel);
|
||||
|
||||
$debug = boolval(cl::get($params, "debug"));
|
||||
$minLevel = cl::get($params, "min_level");
|
||||
if ($minLevel === null && $debug) $minLevel = self::DEBUG;
|
||||
if ($minLevel === null) $minLevel = cl::get($params, "verbosity"); # alias
|
||||
if ($minLevel === null) $minLevel = self::NORMAL;
|
||||
$minLevel = self::verifix_level($minLevel, self::NONE);
|
||||
|
||||
$addDate = boolval(cl::get($params, "add_date"));
|
||||
$dateFormat = cl::get($params, "date_format", static::DATE_FORMAT);
|
||||
$id = cl::get($params, "id");
|
||||
|
||||
$params = [
|
||||
"color" => $color,
|
||||
"indent" => $indent,
|
||||
];
|
||||
if ($output !== null) {
|
||||
$this->err = $this->out = new StdOutput($output, $params);
|
||||
} else {
|
||||
$this->out = new StdOutput(STDOUT, $params);
|
||||
$this->err = new StdOutput(STDERR, $params);
|
||||
}
|
||||
$this->defaultLevel = $defaultLevel;
|
||||
$this->minLevel = $minLevel;
|
||||
$this->addDate = $addDate;
|
||||
$this->dateFormat = $dateFormat;
|
||||
$this->id = $id;
|
||||
$this->inSection = false;
|
||||
$this->titles = [];
|
||||
$this->actions = [];
|
||||
}
|
||||
|
||||
function resetParams(?array $params=null): void {
|
||||
$output = cl::get($params, "output");
|
||||
$color = cl::get($params, "color");
|
||||
$indent = cl::get($params, "indent");
|
||||
|
||||
$defaultLevel = cl::get($params, "default_level");
|
||||
if ($defaultLevel !== null) $defaultLevel = self::verifix_level($defaultLevel);
|
||||
|
||||
$debug = cl::get($params, "debug");
|
||||
$minLevel = cl::get($params, "min_level");
|
||||
if ($minLevel === null && $debug !== null) $minLevel = $debug? self::DEBUG: self::NORMAL;
|
||||
if ($minLevel === null) $minLevel = cl::get($params, "verbosity"); # alias
|
||||
if ($minLevel !== null) $minLevel = self::verifix_level($minLevel, self::NONE);
|
||||
|
||||
$addDate = cl::get($params, "add_date");
|
||||
$dateFormat = cl::get($params, "date_format");
|
||||
$id = cl::get($params, "id");
|
||||
|
||||
$params = [
|
||||
"output" => $output,
|
||||
"color" => $color,
|
||||
"indent" => $indent,
|
||||
];
|
||||
if ($output !== null) {
|
||||
$this->out->resetParams($params);
|
||||
} else {
|
||||
$this->out->resetParams($params);
|
||||
$this->err->resetParams($params);
|
||||
}
|
||||
if ($defaultLevel !== null) $this->defaultLevel = $defaultLevel;
|
||||
if ($minLevel !== null) $this->minLevel = $minLevel;
|
||||
if ($addDate !== null) $this->addDate = boolval($addDate);
|
||||
if ($dateFormat !== null) $this->dateFormat = $dateFormat;
|
||||
if ($id !== null) $this->id = $id;
|
||||
}
|
||||
|
||||
function clone(?array $params=null): IMessenger {
|
||||
$clone = clone $this;
|
||||
if ($params !== null) $clone->resetParams($params);
|
||||
#XXX faut-il marquer la section et les titres du clone à "print" => false?
|
||||
# ou en faire des références au parent?
|
||||
# dans tous les cas, on considère qu'il n'y a pas d'actions en cours, et on
|
||||
# ne doit pas dépiler avec end() plus que l'état que l'on a eu lors du clone
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/** @var StdOutput la sortie standard */
|
||||
protected $out;
|
||||
|
||||
/** @var StdOutput la sortie d'erreur */
|
||||
protected $err;
|
||||
|
||||
/** @var int level par défaut dans lequel les messages sont affichés */
|
||||
protected $defaultLevel;
|
||||
|
||||
/** @var int level minimum que doivent avoir les messages pour être affichés */
|
||||
protected $minLevel;
|
||||
|
||||
/** @var bool faut-il ajouter la date à chaque ligne? */
|
||||
protected $addDate;
|
||||
|
||||
/** @var string format de la date */
|
||||
protected $dateFormat;
|
||||
|
||||
/** @var ?string identifiant de ce messenger, à ajouter à chaque ligne */
|
||||
protected $id;
|
||||
|
||||
protected function getLinePrefix(): ?string {
|
||||
$linePrefix = null;
|
||||
if ($this->addDate) {
|
||||
$date = date_create()->format($this->dateFormat);
|
||||
$linePrefix .= "$date ";
|
||||
}
|
||||
if ($this->id !== null) {
|
||||
$linePrefix .= "$this->id ";
|
||||
}
|
||||
return $linePrefix;
|
||||
}
|
||||
|
||||
protected function decrLevel(int $level, int $amount=-1): int {
|
||||
$level += $amount;
|
||||
if ($level < self::MIN_LEVEL) $level = self::MIN_LEVEL;
|
||||
return $level;
|
||||
}
|
||||
|
||||
protected function checkLevel(?int &$level): bool {
|
||||
if ($level === null) $level = $this->defaultLevel;
|
||||
elseif ($level < 0) $level = $this->decrLevel($this->defaultLevel, $level);
|
||||
return $level >= $this->minLevel;
|
||||
}
|
||||
|
||||
protected function getIndentLevel(bool $withActions=true): int {
|
||||
$indentLevel = count($this->titles) - 1;
|
||||
if ($indentLevel < 0) $indentLevel = 0;
|
||||
if ($withActions) {
|
||||
foreach ($this->actions as $action) {
|
||||
if ($action["level"] < $this->minLevel) continue;
|
||||
$indentLevel++;
|
||||
}
|
||||
}
|
||||
return $indentLevel;
|
||||
}
|
||||
|
||||
protected function _printTitle(?string $linePrefix, int $level,
|
||||
string $type, $content,
|
||||
int $indentLevel, StdOutput $out): void {
|
||||
$prefixes = self::GENERIC_PREFIXES[$level][$type];
|
||||
if ($prefixes[0]) $out->print();
|
||||
if ($out->isColor()) {
|
||||
$before = $prefixes[2];
|
||||
$prefix = $prefixes[3];
|
||||
$prefix2 = $prefix !== null? "$prefix ": null;
|
||||
$suffix = $prefixes[4];
|
||||
$suffix2 = $suffix !== null? " $suffix": null;
|
||||
$after = $prefixes[5];
|
||||
|
||||
$lines = $out->getLines(false, $content);
|
||||
$maxlen = 0;
|
||||
foreach ($lines as &$content) {
|
||||
$line = $out->filterColors($content);
|
||||
$len = mb_strlen($line);
|
||||
if ($len > $maxlen) $maxlen = $len;
|
||||
$content = [$content, $len];
|
||||
}; unset($content);
|
||||
if ($before !== null) {
|
||||
if ($linePrefix !== null) $out->write($linePrefix);
|
||||
$out->iprint($indentLevel, $prefix, substr($before, 1), str_repeat($before[0], $maxlen), $suffix);
|
||||
}
|
||||
foreach ($lines as [$content, $len]) {
|
||||
if ($linePrefix !== null) $out->write($linePrefix);
|
||||
$padding = $len < $maxlen? str_repeat(" ", $maxlen - $len): null;
|
||||
$out->iprint($indentLevel, $prefix2, $content, $padding, $suffix2);
|
||||
}
|
||||
if ($after !== null) {
|
||||
if ($linePrefix !== null) $out->write($linePrefix);
|
||||
$out->iprint($indentLevel, $prefix, substr($after, 1), str_repeat($after[0], $maxlen), $suffix);
|
||||
}
|
||||
} else {
|
||||
$prefix = $prefixes[1];
|
||||
if ($prefix !== null) $prefix .= " ";
|
||||
$prefix2 = str_repeat(" ", mb_strlen($prefix));
|
||||
$lines = $out->getLines(false, $content);
|
||||
foreach ($lines as $content) {
|
||||
if ($linePrefix !== null) $out->write($linePrefix);
|
||||
$out->iprint($indentLevel, $prefix, $content);
|
||||
$prefix = $prefix2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function _printAction(?string $linePrefix, int $level,
|
||||
bool $printContent, $content,
|
||||
bool $printResult, ?bool $rsuccess, $rcontent,
|
||||
int $indentLevel, StdOutput $out): void {
|
||||
$color = $out->isColor();
|
||||
if ($rsuccess === true) $type = "success";
|
||||
elseif ($rsuccess === false) $type = "failure";
|
||||
else $type = "done";
|
||||
$rprefixes = self::RESULT_PREFIXES[$type];
|
||||
if ($color) {
|
||||
$rprefix = $rprefixes[1];
|
||||
$rprefix2 = null;
|
||||
if ($rprefix !== null) {
|
||||
$rprefix .= " ";
|
||||
$rprefix2 = $out->filterColors($out->filterContent($rprefix));
|
||||
$rprefix2 = str_repeat(" ", mb_strlen($rprefix2));
|
||||
}
|
||||
} else {
|
||||
$rprefix = $rprefixes[0];
|
||||
if ($rprefix !== null) $rprefix .= " ";
|
||||
$rprefix2 = str_repeat(" ", mb_strlen($rprefix));
|
||||
}
|
||||
if ($printContent && $printResult) {
|
||||
if ($rcontent) {
|
||||
cl::ensure_array($content);
|
||||
$content[] = ": ";
|
||||
$content[] = $rcontent;
|
||||
}
|
||||
$lines = $out->getLines(false, $content);
|
||||
foreach ($lines as $content) {
|
||||
if ($linePrefix !== null) $out->write($linePrefix);
|
||||
$out->iprint($indentLevel, $rprefix, $content);
|
||||
$rprefix = $rprefix2;
|
||||
}
|
||||
} elseif ($printContent) {
|
||||
$prefixes = self::GENERIC_PREFIXES[$level]["step"];
|
||||
if ($color) {
|
||||
$prefix = $prefixes[1];
|
||||
if ($prefix !== null) $prefix .= " ";
|
||||
$prefix2 = $out->filterColors($out->filterContent($prefix));
|
||||
$prefix2 = str_repeat(" ", mb_strlen($prefix2));
|
||||
$suffix = $prefixes[2];
|
||||
} else {
|
||||
$prefix = $prefixes[0];
|
||||
if ($prefix !== null) $prefix .= " ";
|
||||
$prefix2 = str_repeat(" ", mb_strlen($prefix));
|
||||
$suffix = null;
|
||||
}
|
||||
$lines = $out->getLines(false, $content, ":");
|
||||
foreach ($lines as $content) {
|
||||
if ($linePrefix !== null) $out->write($linePrefix);
|
||||
$out->iprint($indentLevel, $prefix, $content, $suffix);
|
||||
$prefix = $prefix2;
|
||||
}
|
||||
} elseif ($printResult) {
|
||||
if (!$rcontent) {
|
||||
if ($type === "success") $rcontent = $color? "succès": "";
|
||||
elseif ($type === "failure") $rcontent = $color? "échec": "";
|
||||
elseif ($type === "done") $rcontent = "fait";
|
||||
}
|
||||
$rprefix = " $rprefix";
|
||||
$rprefix2 = " $rprefix2";
|
||||
$lines = $out->getLines(false, $rcontent);
|
||||
foreach ($lines as $rcontent) {
|
||||
if ($linePrefix !== null) $out->write($linePrefix);
|
||||
$out->iprint($indentLevel, $rprefix, $rcontent);
|
||||
$rprefix = $rprefix2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function _printGeneric(?string $linePrefix, int $level,
|
||||
string $type, $content,
|
||||
int $indentLevel, StdOutput $out): void {
|
||||
$prefixes = self::GENERIC_PREFIXES[$level][$type];
|
||||
if ($out->isColor()) {
|
||||
$prefix = $prefixes[1];
|
||||
$prefix2 = null;
|
||||
if ($prefix !== null) {
|
||||
$prefix .= " ";
|
||||
$prefix2 = $out->filterColors($out->filterContent($prefix));
|
||||
$prefix2 = str_repeat(" ", mb_strlen($prefix2));
|
||||
}
|
||||
$suffix = $prefixes[2];
|
||||
$lines = $out->getLines(false, $content);
|
||||
foreach ($lines as $content) {
|
||||
if ($linePrefix !== null) $out->write($linePrefix);
|
||||
$out->iprint($indentLevel, $prefix, $content, $suffix);
|
||||
$prefix = $prefix2;
|
||||
}
|
||||
} else {
|
||||
$prefix = $prefixes[0];
|
||||
if ($prefix !== null) $prefix .= " ";
|
||||
$prefix2 = str_repeat(" ", mb_strlen($prefix));
|
||||
$lines = $out->getLines(false, $content);
|
||||
foreach ($lines as $content) {
|
||||
if ($linePrefix !== null) $out->write($linePrefix);
|
||||
$out->iprint($indentLevel, $prefix, $content);
|
||||
$prefix = $prefix2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function _printGenericOrException(?int $level, string $type, $content, int $indentLevel, StdOutput $out): void {
|
||||
$linePrefix = $this->getLinePrefix();
|
||||
# si $content contient des exceptions, les afficher avec un level moindre
|
||||
$exceptions = null;
|
||||
if (is_array($content)) {
|
||||
$valueContent = null;
|
||||
foreach ($content as $value) {
|
||||
if ($value instanceof Throwable || $value instanceof ExceptionShadow) {
|
||||
$exceptions[] = $value;
|
||||
} else {
|
||||
$valueContent[] = $value;
|
||||
}
|
||||
}
|
||||
$content = $valueContent;
|
||||
} elseif ($content instanceof Throwable || $content instanceof ExceptionShadow) {
|
||||
$exceptions[] = $content;
|
||||
$content = null;
|
||||
}
|
||||
|
||||
$printActions = true;
|
||||
$showContent = $this->checkLevel($level);
|
||||
if ($content !== null && $showContent) {
|
||||
$this->printActions(); $printActions = false;
|
||||
$this->_printGeneric($linePrefix, $level, $type, $content, $indentLevel, $out);
|
||||
}
|
||||
if ($exceptions !== null) {
|
||||
$level1 = $this->decrLevel($level);
|
||||
$showTraceback = $this->checkLevel($level1);
|
||||
foreach ($exceptions as $exception) {
|
||||
# tout d'abord userMessage
|
||||
$userMessage = UserException::get_user_message($exception);
|
||||
if ($userMessage !== null && $showContent) {
|
||||
if ($printActions) { $this->printActions(); $printActions = false; }
|
||||
$this->_printGeneric($linePrefix, $level, $type, $userMessage, $indentLevel, $out);
|
||||
}
|
||||
# puis summary et traceback
|
||||
if ($showTraceback) {
|
||||
if ($printActions) { $this->printActions(); $printActions = false; }
|
||||
$summary = UserException::get_summary($exception);
|
||||
$traceback = UserException::get_traceback($exception);
|
||||
$this->_printGeneric($linePrefix, $level1, $type, $summary, $indentLevel, $out);
|
||||
$this->_printGeneric($linePrefix, $level1, $type, $traceback, $indentLevel, $out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var bool est-on dans une section? */
|
||||
protected $inSection;
|
||||
|
||||
/** @var array section qui est en attente d'affichage */
|
||||
protected $section;
|
||||
|
||||
function section($content, ?callable $func=null, ?int $level=null): void {
|
||||
$this->_endSection();
|
||||
$this->inSection = true;
|
||||
if (!$this->checkLevel($level)) return;
|
||||
$this->section = [
|
||||
"line_prefix" => $this->getLinePrefix(),
|
||||
"level" => $level,
|
||||
"content" => $content,
|
||||
"print_content" => true,
|
||||
];
|
||||
if ($func !== null) {
|
||||
try {
|
||||
$func($this);
|
||||
} finally {
|
||||
$this->_endSection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function printSection() {
|
||||
$section =& $this->section;
|
||||
if ($section !== null && $section["print_content"]) {
|
||||
$this->_printTitle(
|
||||
$section["line_prefix"], $section["level"],
|
||||
"section", $section["content"],
|
||||
0, $this->err);
|
||||
$section["print_content"] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function _endSection(): void {
|
||||
$this->inSection = false;
|
||||
$this->section = null;
|
||||
}
|
||||
|
||||
/** @var array */
|
||||
protected $titles;
|
||||
|
||||
/** @var array */
|
||||
protected $title;
|
||||
|
||||
function _getTitleMark(): int {
|
||||
return count($this->titles);
|
||||
}
|
||||
|
||||
function title($content, ?callable $func=null, ?int $level=null): void {
|
||||
if (!$this->checkLevel($level)) return;
|
||||
$until = $this->_getTitleMark();
|
||||
$this->titles[] = [
|
||||
"line_prefix" => $this->getLinePrefix(),
|
||||
"level" => $level,
|
||||
"content" => $content,
|
||||
"print_content" => true,
|
||||
"descs" => [],
|
||||
"print_descs" => false,
|
||||
];
|
||||
$this->title =& $this->titles[$until];
|
||||
if ($func !== null) {
|
||||
try {
|
||||
$func($this);
|
||||
} finally {
|
||||
$this->_endTitle($until);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function desc($content, ?int $level=null): void {
|
||||
if (!$this->checkLevel($level)) return;
|
||||
$title =& $this->title;
|
||||
$title["descs"][] = [
|
||||
"line_prefix" => $this->getLinePrefix(),
|
||||
"level" => $level,
|
||||
"content" => $content,
|
||||
];
|
||||
$title["print_descs"] = true;
|
||||
}
|
||||
|
||||
protected function printTitles(): void {
|
||||
$this->printSection();
|
||||
$err = $this->err;
|
||||
$indentLevel = 0;
|
||||
foreach ($this->titles as &$title) {
|
||||
if ($title["print_content"]) {
|
||||
$this->_printTitle(
|
||||
$title["line_prefix"], $title["level"],
|
||||
"title", $title["content"],
|
||||
$indentLevel, $err);
|
||||
$title["print_content"] = false;
|
||||
}
|
||||
if ($title["print_descs"]) {
|
||||
foreach ($title["descs"] as $desc) {
|
||||
$this->_printGeneric(
|
||||
$desc["line_prefix"], $desc["level"],
|
||||
"desc", $desc["content"],
|
||||
$indentLevel, $err);
|
||||
}
|
||||
$title["descs"] = [];
|
||||
$title["print_descs"] = false;
|
||||
}
|
||||
$indentLevel++;
|
||||
}; unset($title);
|
||||
}
|
||||
|
||||
function _endTitle(?int $until=null): void {
|
||||
if ($until === null) $until = $this->_getTitleMark() - 1;
|
||||
while (count($this->titles) > $until) {
|
||||
array_pop($this->titles);
|
||||
}
|
||||
if ($this->titles) {
|
||||
$this->title =& $this->titles[count($this->titles) - 1];
|
||||
} else {
|
||||
$this->titles = [];
|
||||
unset($this->title);
|
||||
}
|
||||
}
|
||||
|
||||
/** @var array */
|
||||
protected $actions;
|
||||
|
||||
/** @var array */
|
||||
protected $action;
|
||||
|
||||
function _getActionMark(): int {
|
||||
return count($this->actions);
|
||||
}
|
||||
|
||||
function action($content, ?callable $func=null, ?int $level=null): void {
|
||||
$this->checkLevel($level);
|
||||
$until = $this->_getActionMark();
|
||||
$this->actions[] = [
|
||||
"line_prefix" => $this->getLinePrefix(),
|
||||
"level" => $level,
|
||||
"content" => $content,
|
||||
"print_content" => true,
|
||||
"result_success" => null,
|
||||
"result_content" => null,
|
||||
];
|
||||
$this->action =& $this->actions[$until];
|
||||
if ($func !== null) {
|
||||
try {
|
||||
$result = $func($this);
|
||||
if ($result !== null) {
|
||||
if ($result === true) $this->asuccess();
|
||||
elseif ($result === false) $this->afailure();
|
||||
else $this->adone($result);
|
||||
}
|
||||
} finally {
|
||||
$this->_endAction($until);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function printActions(bool $endAction=false): void {
|
||||
$this->printTitles();
|
||||
$err = $this->err;
|
||||
$indentLevel = $this->getIndentLevel(false);
|
||||
$lastIndex = count($this->actions) - 1;
|
||||
$index = 0;
|
||||
foreach ($this->actions as &$action) {
|
||||
$mergeResult = $index++ == $lastIndex && $endAction;
|
||||
$linePrefix = $action["line_prefix"];
|
||||
$level = $action["level"];
|
||||
$content = $action["content"];
|
||||
$printContent = $action["print_content"];
|
||||
$rsuccess = $action["result_success"];
|
||||
$rcontent = $action["result_content"];
|
||||
if ($level < $this->minLevel) continue;
|
||||
if ($mergeResult) {
|
||||
$this->_printAction(
|
||||
$linePrefix, $level,
|
||||
$printContent, $content,
|
||||
true, $rsuccess, $rcontent,
|
||||
$indentLevel, $err);
|
||||
} elseif ($printContent) {
|
||||
$this->_printAction(
|
||||
$linePrefix, $level,
|
||||
$printContent, $content,
|
||||
false, $rsuccess, $rcontent,
|
||||
$indentLevel, $err);
|
||||
$action["print_content"] = false;
|
||||
}
|
||||
$indentLevel++;
|
||||
}; unset($action);
|
||||
if ($endAction) $this->_endAction();
|
||||
}
|
||||
|
||||
function step($content, ?int $level=null): void {
|
||||
$this->_printGenericOrException($level, "step", $content, $this->getIndentLevel(), $this->err);
|
||||
}
|
||||
|
||||
function asuccess($content=null): void {
|
||||
if (!$this->actions) $this->action(null);
|
||||
$this->action["result_success"] = true;
|
||||
$this->action["result_content"] = $content;
|
||||
$this->printActions(true);
|
||||
}
|
||||
|
||||
function afailure($content=null): void {
|
||||
if (!$this->actions) $this->action(null);
|
||||
$this->action["result_success"] = false;
|
||||
$this->action["result_content"] = $content;
|
||||
$this->printActions(true);
|
||||
}
|
||||
|
||||
function adone($content=null): void {
|
||||
if (!$this->actions) $this->action(null);
|
||||
$this->action["result_success"] = null;
|
||||
$this->action["result_content"] = $content;
|
||||
$this->printActions(true);
|
||||
}
|
||||
|
||||
function _endAction(?int $until=null): void {
|
||||
if ($until === null) $until = $this->_getActionMark() - 1;
|
||||
while (count($this->actions) > $until) {
|
||||
array_pop($this->actions);
|
||||
}
|
||||
if ($this->actions) {
|
||||
$this->action =& $this->actions[count($this->actions) - 1];
|
||||
} else {
|
||||
$this->actions = [];
|
||||
unset($this->action);
|
||||
}
|
||||
}
|
||||
|
||||
function print($content, ?int $level=null): void {
|
||||
$this->_printGenericOrException($level, "print", $content, $this->getIndentLevel(), $this->out);
|
||||
}
|
||||
|
||||
function info($content, ?int $level=null): void {
|
||||
$this->_printGenericOrException($level, "info", $content, $this->getIndentLevel(), $this->err);
|
||||
}
|
||||
|
||||
function note($content, ?int $level=null): void {
|
||||
$this->_printGenericOrException($level, "note", $content, $this->getIndentLevel(), $this->err);
|
||||
}
|
||||
|
||||
function warn($content, ?int $level=null): void {
|
||||
$this->_printGenericOrException($level, "warn", $content, $this->getIndentLevel(), $this->err);
|
||||
}
|
||||
|
||||
function error($content, ?int $level=null): void {
|
||||
$this->_printGenericOrException($level, "error", $content, $this->getIndentLevel(), $this->err);
|
||||
}
|
||||
|
||||
function end(bool $all=false): void {
|
||||
if ($all) {
|
||||
while ($this->actions) $this->adone();
|
||||
while ($this->titles) $this->_endTitle();
|
||||
$this->_endSection();
|
||||
} elseif ($this->actions) {
|
||||
$this->_endAction();
|
||||
} elseif ($this->titles) {
|
||||
$this->_endTitle();
|
||||
} else {
|
||||
$this->_endSection();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,272 @@
|
|||
<?php
|
||||
namespace nulib\output\std;
|
||||
|
||||
use Exception;
|
||||
use nulib\cl;
|
||||
use nulib\output\IContent;
|
||||
use nulib\output\IPrintable;
|
||||
|
||||
/**
|
||||
* 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) $params["output"] = $output;
|
||||
elseif (!isset($params["output"])) $params["output"] = STDOUT;
|
||||
if (!isset($params["filter_tags"])) $params["filter_tags"] = true;
|
||||
if (!isset($params["indent"])) $params["indent"] = " ";
|
||||
$this->resetParams($params);
|
||||
}
|
||||
|
||||
function resetParams(?array $params=null): void {
|
||||
$output = cl::get($params, "output");
|
||||
$maskErrors = null;
|
||||
$color = cl::get($params, "color");
|
||||
$filterTags = cl::get($params, "filter_tags");
|
||||
$indent = cl::get($params, "indent");
|
||||
$flush = cl::get($params, "flush");
|
||||
|
||||
if ($output !== null) {
|
||||
if ($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, à
|
||||
# part afficher les erreurs d'écriture la première fois qu'elles se
|
||||
# produisent
|
||||
$maskErrors = false;
|
||||
$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;
|
||||
}
|
||||
$this->outf = $outf;
|
||||
$this->maskErrors = $maskErrors;
|
||||
if ($color === null) $color = stream_isatty($outf);
|
||||
if ($flush === null) $flush = false;
|
||||
}
|
||||
if ($color !== null) $this->color = boolval($color);
|
||||
if ($filterTags !== null) $this->filterTags = boolval($filterTags);
|
||||
if ($indent !== null) $this->indent = strval($indent);
|
||||
if ($flush !== null) $this->flush = boolval($flush);
|
||||
}
|
||||
|
||||
/** @var resource */
|
||||
protected $outf;
|
||||
|
||||
/** @var bool faut-il masquer les erreurs d'écriture? */
|
||||
protected $maskErrors;
|
||||
|
||||
/** @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 string indentation unitaire */
|
||||
protected $indent;
|
||||
|
||||
/** @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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
function filterColors(string $text): string {
|
||||
return preg_replace('/\x1B\[.*?m/', "", $text);
|
||||
}
|
||||
|
||||
static function flatten($values, ?array &$dest=null): array {
|
||||
if ($dest === null) $dest = [];
|
||||
if ($values === null) return $dest;
|
||||
if (is_string($values)) {
|
||||
$dest[] = $values;
|
||||
return $dest;
|
||||
} elseif (!is_array($values)) {
|
||||
if ($values instanceof IContent) {
|
||||
$values = $values->getContent();
|
||||
} elseif ($values instanceof IPrintable) {
|
||||
ob_start(null, 0, PHP_OUTPUT_HANDLER_STDFLAGS ^ PHP_OUTPUT_HANDLER_FLUSHABLE);
|
||||
$values->print();
|
||||
$dest[] = ob_get_clean();
|
||||
return $dest;
|
||||
} elseif (!is_iterable($values)) {
|
||||
$dest[] = strval($values);
|
||||
return $dest;
|
||||
}
|
||||
}
|
||||
foreach ($values as $value) {
|
||||
self::flatten($value, $dest);
|
||||
}
|
||||
return $dest;
|
||||
}
|
||||
|
||||
function getIndent(int $indentLevel): string {
|
||||
return str_repeat($this->indent, $indentLevel);
|
||||
}
|
||||
|
||||
function getLines(bool $withNl, ...$values): array {
|
||||
$values = self::flatten($values);
|
||||
if (!$values) return [];
|
||||
$text = implode("", $values);
|
||||
if ($text === "") return [""];
|
||||
$text = $this->filterContent($text);
|
||||
if (!$this->color) $text = $this->filterColors($text);
|
||||
$lines = explode("\n", $text);
|
||||
$max = count($lines) - 1;
|
||||
if ($withNl) {
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
$lines[$i] .= "\n";
|
||||
}
|
||||
}
|
||||
if ($lines[$max] === "") unset($lines[$max]);
|
||||
return $lines;
|
||||
}
|
||||
|
||||
private function _fwrite($outf, string $data): void {
|
||||
if ($this->maskErrors === null) {
|
||||
# masquer les erreurs d'écriture en permanence
|
||||
@fwrite($outf, $data);
|
||||
return;
|
||||
}
|
||||
# masquer uniquement la première erreur, jusqu'à ce que l'erreur disparaisse
|
||||
if ($this->maskErrors) $r = @fwrite($outf, $data);
|
||||
else $r = fwrite($outf, $data);
|
||||
$this->maskErrors = $r === false;
|
||||
}
|
||||
|
||||
function writeLines($indent, array $lines, bool $addNl=false): void {
|
||||
$outf = $this->outf;
|
||||
foreach ($lines as $line) {
|
||||
if ($indent !== null) $this->_fwrite($outf, $indent);
|
||||
$this->_fwrite($outf, $line);
|
||||
if ($addNl) $this->_fwrite($outf, "\n");
|
||||
}
|
||||
if ($this->flush) @fflush($outf);
|
||||
}
|
||||
|
||||
function write(...$values): void {
|
||||
$this->writeLines(null, $this->getLines(true, ...$values));
|
||||
}
|
||||
|
||||
function print(...$values): void {
|
||||
$values[] = "\n";
|
||||
$this->writeLines(null, $this->getLines(true, ...$values));
|
||||
}
|
||||
|
||||
function iwrite(int $indentLevel, ...$values): void {
|
||||
$indent = $this->getIndent($indentLevel);
|
||||
$this->writeLines($indent, $this->getLines(true, ...$values));
|
||||
}
|
||||
|
||||
function iprint(int $indentLevel, ...$values): void {
|
||||
$values[] = "\n";
|
||||
$indent = $this->getIndent($indentLevel);
|
||||
$this->writeLines($indent, $this->getLines(true, ...$values));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
namespace nulib\output\std;
|
||||
|
||||
use nulib\output\IMessenger;
|
||||
|
||||
/**
|
||||
* Interface _IMessenger: méthodes privées de IMessenger
|
||||
*/
|
||||
interface _IMessenger extends IMessenger {
|
||||
function _endSection(): void;
|
||||
|
||||
function _getTitleMark(): int;
|
||||
|
||||
function _endTitle(?int $until=null): void;
|
||||
|
||||
function _getActionMark(): int;
|
||||
|
||||
function _endAction(?int $until=null): void;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
namespace nulib\web\params;
|
||||
|
||||
use nulib\str;
|
||||
|
||||
/**
|
||||
* Class F: gestion unifiée des paramètres POST + GET
|
||||
*/
|
||||
class F {
|
||||
/** tester si le paramètre $name existe dans $_POST ou $_GET */
|
||||
static final function has($name): bool {
|
||||
if ($name === null || $name === false) return false;
|
||||
elseif (array_key_exists($name, $_POST)) return true;
|
||||
elseif (array_key_exists($name, $_GET)) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
/** obtenir le paramètre $name en cherchant dans $_POST puis $_GET */
|
||||
static final function get($name, $default=null, bool $trim=false) {
|
||||
if ($name === null || $name === false) $value = $default;
|
||||
elseif (array_key_exists($name, $_POST)) $value = $_POST[$name];
|
||||
elseif (array_key_exists($name, $_GET)) $value = $_GET[$name];
|
||||
else $value = $default;
|
||||
if ($trim) $value = str::trim($value);
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* modifier le paramètre.
|
||||
*
|
||||
* - s'il provenait de $_POST, modifier $_POST
|
||||
* - s'il provenait de $_GET, modifier $_GET
|
||||
* - sinon modifier $_POST
|
||||
*/
|
||||
static final function set(string $name, ?string $value): void {
|
||||
if (array_key_exists($name, $_POST)) $_POST[$name] = $value;
|
||||
elseif (array_key_exists($name, $_GET)) $_GET[$name] = $value;
|
||||
else $_POST[$name] = $value;
|
||||
}
|
||||
|
||||
/** obtenir la liste des clés définies dans les paramètres */
|
||||
protected static final function get_names(): array {
|
||||
return array_keys(array_merge(
|
||||
array_fill_keys(array_keys($_POST), true),
|
||||
array_fill_keys(array_keys($_GET), true),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* retourner une liste des paramètres qui ont été passés, en les sélectionnant
|
||||
* selon le contenu de $includes et $excludes. ensuite, fusionner le tableau
|
||||
* $merge s'il est spécifié
|
||||
*
|
||||
* pour être sélectionné, un paramètre ne doit pas être dans $excludes, et si
|
||||
* $includes n'est pas null, doit être dans $includes
|
||||
*/
|
||||
static final function select(?array $includes=null, ?array $excludes=null, ?array $merges=null): array {
|
||||
$params = [];
|
||||
foreach (self::get_names() as $name) {
|
||||
if ($excludes !== null && in_array($name, $excludes)) continue;
|
||||
if ($includes !== null && !in_array($name, $includes)) continue;
|
||||
$params[$name] = self::get($name);
|
||||
}
|
||||
if ($merges !== null) $params = array_merge($params, $merges);
|
||||
return $params;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
namespace nulib\web\params;
|
||||
|
||||
use nulib\cl;
|
||||
use nulib\str;
|
||||
|
||||
/**
|
||||
* Class G: gestion des paramètres $_GET
|
||||
*/
|
||||
class G {
|
||||
/** tester si le paramètre $name existe */
|
||||
static final function has($name): bool {
|
||||
if ($name === null || $name === false) return false;
|
||||
return array_key_exists($name, $_GET);
|
||||
}
|
||||
|
||||
/** obtenir le paramètre $name */
|
||||
static final function get($name, $default=null, bool $trim=false) {
|
||||
$value = cl::get($_GET, $name, $default);
|
||||
if ($trim) $value = str::trim($value);
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** modifier le paramètre. */
|
||||
static final function set(string $name, ?string $value): void {
|
||||
$_GET[$name] = $value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
namespace nulib\web\params;
|
||||
|
||||
use nulib\cl;
|
||||
use nulib\str;
|
||||
|
||||
/**
|
||||
* Class P: gestion des paramètres $_POST
|
||||
*/
|
||||
class P {
|
||||
/** tester si le paramètre $name existe */
|
||||
static final function has($name): bool {
|
||||
if ($name === null || $name === false) return false;
|
||||
return array_key_exists($name, $_POST);
|
||||
}
|
||||
|
||||
/** obtenir le paramètre $name */
|
||||
static final function get($name, $default=null, bool $trim=false) {
|
||||
$value = cl::get($_POST, $name, $default);
|
||||
if ($trim) $value = str::trim($value);
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** modifier le paramètre. */
|
||||
static final function set(string $name, ?string $value): void {
|
||||
$_POST[$name] = $value;
|
||||
}
|
||||
|
||||
/** obtenir le corps de la requête POST */
|
||||
static final function raw(): string {
|
||||
return file_get_contents("php://input");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
namespace nulib\web\params;
|
||||
|
||||
use nulib\cl;
|
||||
use nulib\str;
|
||||
|
||||
/**
|
||||
* Class R: gestion des paramètres $_REQUEST (GET + POST + COOKIES)
|
||||
*/
|
||||
class R {
|
||||
/** tester si le paramètre $name existe */
|
||||
static final function has($name): bool {
|
||||
if ($name === null || $name === false) return false;
|
||||
return array_key_exists($name, $_REQUEST);
|
||||
}
|
||||
|
||||
/** obtenir le paramètre $name */
|
||||
static final function get($name, $default=null, bool $trim=false) {
|
||||
$value = cl::get($_REQUEST, $name, $default);
|
||||
if ($trim) $value = str::trim($value);
|
||||
return $value;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue