null, ], false); parent::__construct($params); } const PARAMETRABLE_PARAMS_SCHEMA = [ "output" => [null, null, "destination des messages affichés"], "color" => ["?bool", null, "la sortie dans la destination se fait-elle en couleur?"], ]; /** @var IWriter */ protected $ppOutput; /** @var bool */ protected $ppColor; /** @var IWriter */ protected $output; /** @var ?bool */ protected $color; protected function afterSetParametrableParams(array $modifiedKeys, ?Metadata $md=null): void { parent::afterSetParametrableParams($modifiedKeys, $md); if (self::was_parametrable_param_modified($modifiedKeys, "color")) { $this->color = $this->ppColor; } if (self::was_parametrable_param_modified($modifiedKeys, "output")) { $output = $this->ppOutput; if ($output === null) $output = STDERR; $this->output = $output = writer::with($output); if (!self::was_parametrable_param_modified($modifiedKeys, "color")) { $this->color = $output->isatty(); } } } const KEY_EXIT = "exit"; const MESSAGE_OPTIONS_SCHEMA = [ self::KEY_EXIT => [null, null, "faut-il arrêter le script après avoir affiché le message?", # la valeur peut être numérique auquel cas c'est le code de retour # sinon ce doit être un booléan ], ]; /** @var Metadata */ private static $message_md; protected static function message_md(): Metadata { return md_utils::ensure_md(self::$message_md , array_merge(self::MESSAGE_SCHEMA, self::MESSAGE_OPTIONS_SCHEMA)); } protected function processMsgOptions(array $options): void { $exit = $options[self::KEY_EXIT]; if ($exit !== null && $exit !== false) { throw new ExitError( is_int($exit)? $exit: 1, is_string($exit)? $exit: null, ); } } const TYPE_PREFIXES = [ # les clés doivent être ordonnées de la plus grande à la plus petite # la 4ème valeur indique s'il faut garder le préfixe s'il y a un result self::LEVEL_CRITICAL => [ self::TYPE_ERROR => ["CRITICAL!", "E!", "", true], self::TYPE_WARNING => ["ATTENTION!", "W!", "", true], self::TYPE_DEBUG => ["IMPORTANT!", "N!", "", true], ], self::LEVEL_MAJOR => [ self::TYPE_ERROR => ["ERROR:", "E", "", true], self::TYPE_WARNING => ["WARN:", "W", "", true], self::TYPE_INFO => ["INFO:", "I", "", false], self::TYPE_DEBUG => ["DEBUG:", "D", "", true], ], self::LEVEL_NORMAL => [ self::TYPE_ERROR => ["E", "", "", true], self::TYPE_WARNING => ["W", "", "", true], self::TYPE_INFO => ["", "", "", false], self::TYPE_DEBUG => ["D", "", "", true], ], self::LEVEL_MINOR => [ self::TYPE_ERROR => ["e", "", "", true], self::TYPE_WARNING => ["w", "", "", true], self::TYPE_INFO => ["i", "", "", false], self::TYPE_DEBUG => ["d", "", "", true], ], ]; const RESULT_PREFIXES = [ self::RESULT_FAILURE => ["(FAILURE)", ""], self::RESULT_SUCCESS => ["(SUCCESS)", ""], self::RESULT_NEUTRAL => ["*", "."], self::RESULT_NONE => [null, null], ]; 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", ]; private static function replace_colors(array $ms): string { $colors = []; foreach (preg_split('/\s+/', $ms[1]) as $color) { while ($color && !A::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 filterTags(string $text): string { # couleur au début $text = preg_replace_callback('/]*)>/', [self::class, "replace_colors"], $text); # reset à la fin $text = preg_replace('/<\/color>/', "\x1B[0m", $text); return parent::filterTags($text); } protected function filterColors(string $text): string { return preg_replace('/\x1B\[.*?m/', "", $text); } protected function printStartSection($title, ?int $msgType, ?int $msgLevel): void { $datetime = $this->ppAddDate? self::date()." ": "\n"; $title = "$datetime>>>> $title <<<<"; $this->wnl($this->output, $this->color, "", $title); } protected function printEndSection(): void { } protected function getResultPrefix(int $result, bool $color): ?string { return self::RESULT_PREFIXES[$result][$color ? 1 : 0]; } protected function getTypePrefixSuffix(int $type, int $level, bool $color, bool $haveResultPrefix): array { $typePrefixSuffix = false; foreach (self::TYPE_PREFIXES as $prefixLevel => $prefixes) { if ($level >= $prefixLevel) { foreach ($prefixes as $prefixType => $prefixValue) { if ($type >= $prefixType) { $typePrefixSuffix = $prefixValue; break; } } } if ($typePrefixSuffix !== false) break; } if ($typePrefixSuffix === false) return [null, null]; elseif ($haveResultPrefix && !$typePrefixSuffix[3]) return [null, null]; $prefix = $typePrefixSuffix[$color ? 1 : 0]; $suffix = $color? $typePrefixSuffix[2]: ""; return [$prefix, $suffix]; } protected function printStartGroup(array $group, ?array $groups): void { [ "indent" => $indent, "prefix" => $groupPrefix, "count" => $count, "type" => $type, "level" => $level, ] = $group; $count = $this->getGroupCount($group); if ($count > 1) { $groupIndent = $this->getGroupIndent($group, $indent - 1); if ($this->ppAddDate) $groupIndent = self::date()." $groupIndent"; if ($type === null) $type = self::TYPE_INFO + self::RESULT_NEUTRAL; if ($level === null) $level = self::LEVEL_ALWAYS; $color = $this->color; $resultPrefix = $this->getResultPrefix($type & self::RESULT_MASK, $color); [$typePrefix, $typeSuffix] = $this->getTypePrefixSuffix($type & self::TYPE_MASK, $level, $color, $resultPrefix !== null); $this->wnl($this->output, $color, " ", $groupIndent.$typePrefix.$resultPrefix, $groupPrefix, $typeSuffix); } } protected function printEndGroup(array $group): void { } protected function getUserMsg($msg): array { return [$msg]; } protected function getTechMsg($msg): array { return [$msg]; } protected function getExceptionMsg($exception, $user, $tech, bool $haveTechMsgOrSummary): array { $msg = [null, UserException::get_traceback($exception)]; if (!$haveTechMsgOrSummary || ($exception !== $user && $exception !== $tech)) { $msg[0] = UserException::get_summary($exception); } return $msg; } protected function _printMsg( IWriter $output, bool $color, ?string $groupIndent, ?string $groupPrefix, bool $showUser, $userMsg, bool $showTech, $techMsg, bool $showException, $exceptionMsg, int $type, int $level ): void { $result = $type & self::RESULT_MASK; $type = $type & self::TYPE_MASK; $resultPrefix = $this->getResultPrefix($result, $color); [$typePrefix, $typeSuffix] = $this->getTypePrefixSuffix($type, $level, $color, $resultPrefix !== null); $prefix = $groupIndent.$typePrefix.$resultPrefix; if ($showUser) $this->wnl($output, $color, " ", $prefix, $groupPrefix, $userMsg, $typeSuffix); if ($showTech) $this->wnl($output, $color, " ", $prefix, "TECH:", $techMsg, $typeSuffix); if ($showException) { [$summary, $traceback] = $exceptionMsg; $this->wnl($output, $color, " ", $prefix, "TRACEBACK:", $summary, $typeSuffix); $this->wnl($output, false, "", $traceback); } } protected function printMsg( ?array $groups, bool $printUser, $userMsg, bool $printTech, $techMsg, bool $printException, $exceptionMsg, int $type, int $level, array $options ): void { $group = A::last($groups); if ($group === false) { # groupe neutralisé return; } $groupIndent = $this->getGroupIndent($group); $groupPrefix = $this->getGroupPrefix($group); if ($this->ppAddDate) $groupIndent = self::date()." $groupIndent"; $this->_printMsg($this->output, $this->color , $groupIndent, $groupPrefix , $printUser, $userMsg , $printTech, $techMsg , $printException, $exceptionMsg , $type, $level); } }