self::LEVEL_DEBUG,
"d" => self::LEVEL_DEBUG,
"normal" => self::LEVEL_NORMAL,
"n" => self::LEVEL_NORMAL,
"major" => self::LEVEL_MAJOR,
"m" => self::LEVEL_MAJOR,
];
protected static function verifix_level($level, bool $debug): int {
if ($level === null) $level = $debug? self::LEVEL_DEBUG: self::LEVEL_NORMAL;
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");
}
return $level;
}
const GENERIC_PREFIXES = [
self::LEVEL_MAJOR => [
"section" => [true, "SECTION!", "===", "=", "=", "==="],
"title" => [false, "TITLE!", null, "T", "", "---"],
"desc" => ["DESC!", ">", ""],
"error" => ["CRITICAL!", "E!", ""],
"warn" => ["ATTENTION!", "W!", ""],
"note" => ["IMPORTANT!", "N!", ""],
"info" => ["IMPORTANT!", "I!", ""],
"print" => [null, null, null],
],
self::LEVEL_NORMAL => [
"section" => [true, "SECTION:", "---", "-", "-", "---"],
"title" => [false, "TITLE:", null, "T", "", null],
"desc" => ["DESC:", ">", ""],
"error" => ["ERROR:", "E", ""],
"warn" => ["WARN:", "W", ""],
"note" => ["NOTE:", "N", ""],
"info" => ["INFO:", "I", ""],
"print" => [null, null, null],
],
self::LEVEL_DEBUG => [
"section" => [false, "section", null, ">>", "<<", null],
"title" => [false, "title", null, "t", "", null],
"desc" => [">", ">", ""],
"error" => ["e", "e", ""],
"warn" => ["w", "w", ""],
"note" => ["i", "i", ""],
"info" => ["D", "D", ""],
"print" => [null, null, null],
],
];
const RESULT_PREFIXES = [
"step" => ["*", "."],
"failure" => ["(FAILURE)", "✘"],
"success" => ["(SUCCESS)", "✔"],
"neutral" => [null, null],
];
function __construct(?array $params=null) {
$color = cl::get($params, "color");
$debug = boolval(cl::get($params, "debug"));
$minLevel = self::verifix_level(cl::get($params, "min_level"), $debug);
$defaultLevel = self::verifix_level(cl::get($params, "default_level"), false);
$params = [
"color" => $color,
"indent" => static::INDENT,
];
$this->out = new StdOutput(STDOUT, $params);
$this->err = new StdOutput(STDERR, $params);
$this->minLevel = intval($minLevel);
$this->defaultLevel = intval($defaultLevel);
$this->inSection = false;
$this->titles = [];
$this->title = null;
$this->actions = [];
$this->action = 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 int level par défaut dans lequel les messages sont affichés */
protected $defaultLevel;
protected function checkLevel(?int &$level): bool {
if ($level === null) $level = $this->defaultLevel;
return $level >= $this->minLevel;
}
protected function _printTitle(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 = strlen($line);
if ($len > $maxlen) $maxlen = $len;
$content = [$content, $len];
}; unset($content);
if ($before !== null) {
$out->iprint($indentLevel, $prefix, substr($before, 1), str_repeat($before[0], $maxlen), $suffix);
}
foreach ($lines as [$content, $len]) {
$padding = $len < $maxlen? str_repeat(" ", $maxlen - $len): null;
$out->iprint($indentLevel, $prefix2, $content, $padding, $suffix2);
}
if ($after !== null) {
$out->iprint($indentLevel, $prefix, substr($after, 1), str_repeat($after[0], $maxlen), $suffix);
}
} else {
$prefix = $prefixes[1];
if ($prefix !== null) $prefix .= " ";
$prefix2 = str_repeat(" ", strlen($prefix));
$lines = $out->getLines(false, $content);
foreach ($lines as $content) {
$out->iprint($indentLevel, $prefix, $content);
$prefix = $prefix2;
}
}
}
protected function _printGeneric(int $level, string $type, $content, int $indentLevel, StdOutput $out): void {
$prefixes = self::GENERIC_PREFIXES[$level][$type];
if ($out->isColor()) {
$prefix = $prefixes[1];
$suffix = $prefixes[2];
} else {
$prefix = $prefixes[0];
$suffix = null;
}
$line = [$prefix];
if ($prefix !== null) $line[] = " ";
$line[] = $content;
$line[] = $suffix;
$out->iprint($indentLevel, ...$line);
}
/** @var bool est-on dans une section? */
protected $inSection;
/** @var array section qui est en attente d'affichage */
protected $section;
function section($content, ?int $level=null): void {
$this->endSection();
$this->inSection = true;
if (!$this->checkLevel($level)) return;
$this->section = [
"level" => $level,
"content" => $content,
"print_content" => true,
];
}
protected function printSection() {
$section =& $this->section;
if ($section["print_content"]) {
$this->_printTitle($section["level"], "section", $section["content"], 0, $this->err);
$section["print_content"] = false;
}
}
protected function endSection(): void {
$this->inSection = false;
$this->section = null;
}
/** @var array */
protected $titles;
/** @var array */
protected $title;
function title($content, ?int $level=null): void {
if (!$this->checkLevel($level)) return;
$this->titles[] = [
"level" => $level,
"content" => $content,
"print_content" => true,
"descs" => [],
"print_descs" => false,
];
$this->title =& $this->titles[count($this->titles) - 1];
}
function desc($content, ?int $level=null): void {
if (!$this->checkLevel($level)) return;
$title =& $this->title;
$title["descs"][] = [
"level" => $level,
"content" => $content,
];
$title["print_descs"] = true;
}
protected function printTitles(): void {
$this->printSection();
$out = $this->err;
$indentLevel = 0;
foreach ($this->titles as &$title) {
if ($title["print_content"]) {
$this->_printTitle($title["level"], "title", $title["content"], $indentLevel, $out);
$title["print_content"] = false;
}
if ($title["print_descs"]) {
foreach ($title["descs"] as $desc) {
$this->_printGeneric($desc["level"], "desc", $desc["content"], $indentLevel, $out);
}
$title["descs"] = [];
$title["print_descs"] = false;
}
$indentLevel++;
}; unset($title);
}
protected function endTitle(): void {
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;
protected function getIndentLevel(): int {
$indentLevel = count($this->titles) - 1;
if ($indentLevel < 0) $indentLevel = 0;
foreach ($this->actions as $action) {
if ($action["level"] < $this->minLevel) continue;
$indentLevel++;
}
return $indentLevel;
}
function action($content, ?int $level=null): void {
$this->actions[] = [
"level" => $level,
"content" => $content,
"print_content" => true,
"success" => null,
"result" => null,
"print_result" => true,
];
$this->action =& $this->actions[count($this->actions) - 1];
}
function printActions(bool $willEnd=false): void {
$this->printTitles();
}
function step($content, ?int $level=null): void {
if (!$this->actions) $this->action(null);
$this->printActions();
}
function success($content=null): void {
if (!$this->actions) $this->action(null);
$this->action["success"] = true;
$this->action["result"] = $content;
$this->printActions(true);
$this->endAction();
}
function failure($content=null): void {
if (!$this->actions) $this->action(null);
$this->action["success"] = false;
$this->action["result"] = $content;
$this->printActions(true);
$this->endAction();
}
function neutral($content=null): void {
if (!$this->actions) $this->action(null);
$this->action["success"] = null;
$this->action["result"] = $content;
$this->printActions(true);
$this->endAction();
}
protected function endAction(): void {
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 {
if (!$this->checkLevel($level)) return;
$this->printActions();
$this->_printGeneric($level, "print", $content, $this->getIndentLevel(), $this->out);
}
function info($content, ?int $level=null): void {
if (!$this->checkLevel($level)) return;
$this->printActions();
$this->_printGeneric($level, "info", $content, $this->getIndentLevel(), $this->err);
}
function note($content, ?int $level=null): void {
if (!$this->checkLevel($level)) return;
$this->printActions();
$this->_printGeneric($level, "note", $content, $this->getIndentLevel(), $this->err);
}
function warn($content, ?int $level=null): void {
if (!$this->checkLevel($level)) return;
$this->printActions();
$this->_printGeneric($level, "warn", $content, $this->getIndentLevel(), $this->err);
}
function error($content, ?int $level=null): void {
if (!$this->checkLevel($level)) return;
$this->printActions();
$this->_printGeneric($level, "error", $content, $this->getIndentLevel(), $this->err);
}
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();
}
}
}