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(); } } }