modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2023-12-28 12:33:06 +04:00
parent 8cf225ea1e
commit 43b7b046fa
8 changed files with 245 additions and 11 deletions

View File

@ -0,0 +1,101 @@
<?php
namespace nulib;
use Throwable;
/**
* Class ExceptionShadow: une classe qui capture les informations d'une
* exception afin de pouvoir les sérialiser
*/
class ExceptionShadow {
protected static function extract_trace(array $trace): array {
$frames = [];
foreach ($trace as $frame) {
$file = cl::get($frame, "file");
$line = cl::get($frame, "line");
$class = cl::get($frame, "class");
$function = cl::get($frame, "function");
$type = cl::get($frame, "type");
$frames[] = [
"file" => $file,
"line" => $line,
"class" => $class,
"object" => null,
"type" => $type,
"function" => $function,
"args" => [],
];
}
return $frames;
}
function __construct(Throwable $exception) {
$this->class = get_class($exception);
$this->message = $exception->getMessage();
$this->code = $exception->getCode();
$this->file = $exception->getFile();
$this->line = $exception->getLine();
$this->trace = self::extract_trace($exception->getTrace());
$previous = $exception->getPrevious();
if ($previous !== null) $this->previous = new static($previous);
}
/** @var string */
protected $class;
function getClass(): string {
return $this->class;
}
/** @var string */
protected $message;
function getMessage(): string {
return $this->message;
}
/** @var mixed */
protected $code;
function getCode() {
return $this->code;
}
/** @var string */
protected $file;
function getFile(): string {
return $this->file;
}
/** @var int */
protected $line;
function getLine(): int {
return $this->line;
}
/** @var array */
protected $trace;
function getTrace(): array {
return $this->trace;
}
function getTraceAsString(): string {
$lines = [];
foreach ($this->trace as $index => $frame) {
$lines[] = "#$index $frame[file]($frame[line]): $frame[class]$frame[type]$frame[function]()";
}
$index++;
$lines[] = "#$index {main}";
return implode("\n", $lines);
}
/** @var ExceptionShadow */
protected $previous;
function getPrevious(): ?ExceptionShadow {
return $this->previous;
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace nulib;
use Throwable;
/**
* Class ExitException: une exception qui indique que l'application souhaite
* quitter normalement, avec éventuellement un code d'erreur.
*/
class ExitException extends UserException {
function __construct(int $exitcode=0, $user_message=null, Throwable $previous=null) {
parent::__construct($user_message, null, $exitcode, $previous);
}
function isError(): bool {
return $this->getCode() !== 0;
}
function haveMessage(): bool {
return $this->getUserMessage() !== null;
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace nulib;
use Exception;
/**
* Class StopException: une exception qui par convention est envoyée à un
* générateur pour indiquer qu'il doit s'arrêter "proprement". cela peut être
* utilisé pour implémenter des itérateur "closeable"
*/
class StopException extends Exception {
}

View File

@ -0,0 +1,89 @@
<?php
namespace nulib;
use Exception;
use Throwable;
/**
* Class UserException: une exception qui peut en plus contenir un message
* utilisateur
*/
class UserException extends Exception {
/** @param Throwable|ExceptionShadow $e */
static function get_user_message($e): ?string {
if ($e instanceof self) return $e->getUserMessage();
else return null;
}
/** @param Throwable|ExceptionShadow $e */
static final function get_user_summary($e): string {
$parts = [];
$first = true;
while ($e !== null) {
$message = self::get_user_message($e);
if (!$message) $message = "(no message)";
if ($first) $first = false;
else $parts[] = "caused by ";
$parts[] = get_class($e) . ": " . $message;
$e = $e->getPrevious();
}
return implode(", ", $parts);
}
/** @param Throwable|ExceptionShadow $e */
static function get_message($e): ?string {
$message = $e->getMessage();
if (!$message && $e instanceof self) $message = $e->getUserMessage();
return $message;
}
/** @param Throwable|ExceptionShadow $e */
static final function get_summary($e): string {
$parts = [];
$first = true;
while ($e !== null) {
$message = self::get_message($e);
if (!$message) $message = "(no message)";
if ($first) $first = false;
else $parts[] = "caused by ";
if ($e instanceof ExceptionShadow) $class = $e->getClass();
else $class = get_class($e);
$parts[] = "$class: $message";
$e = $e->getPrevious();
}
return implode(", ", $parts);
}
/** @param Throwable|ExceptionShadow $e */
static final function get_traceback($e): string {
$tbs = [];
$previous = false;
while ($e !== null) {
if (!$previous) {
$efile = $e->getFile();
$eline = $e->getLine();
$tbs[] = "at $efile($eline)";
} else {
$tbs[] = "~~ caused by: " . self::get_summary($e);
}
$tbs[] = $e->getTraceAsString();
$e = $e->getPrevious();
$previous = true;
#XXX il faudrait ne pas réinclure les lignes communes aux exceptions qui
# ont déjà été affichées
}
return implode("\n", $tbs);
}
function __construct($user_message, $tech_message=null, $code=0, ?Throwable $previous=null) {
$this->userMessage = $user_message;
if ($tech_message === null) $tech_message = $user_message;
parent::__construct($tech_message, $code, $previous);
}
protected $userMessage;
function getUserMessage(): ?string {
return $this->userMessage;
}
}

View File

@ -1,12 +1,10 @@
<?php
namespace nulib;
use Exception;
/**
* Class ValueException: indiquer qu'une valeur est invalide
*/
class ValueException extends Exception {
class ValueException extends UserException {
private static function value($value): string {
if (is_object($value)) {
return "<".get_class($value).">";
@ -40,11 +38,23 @@ class ValueException extends Exception {
return new static(self::message(null, $message, $kind, $prefix, " is null"));
}
static final function invalid($value=null, ?string $kind=null, ?string $prefix=null, ?string $message=null): self {
static final function invalid_kind($value=null, ?string $kind=null, ?string $prefix=null, ?string $message=null): self {
return new static(self::message($value, $message, $kind, $prefix, " is invalid"));
}
static final function unexpected_class($class, string $expected_class) {
static final function invalid_key($value, ?string $prefix=null, ?string $message=null): self {
return self::invalid_kind($value, "key", $prefix, $message);
}
static final function invalid_value($value, ?string $prefix=null, ?string $message=null): self {
return self::invalid_kind($value, "value", $prefix, $message);
}
static final function invalid_type($value, string $expected_type): self {
return new static(self::message($value, null, "type", null, " is invalid, expected $expected_type"));
}
static final function invalid_class($class, string $expected_class): self {
if (is_object($class)) $class = get_class($class);
return new static(self::message($class, null, "class", null, " is invalid, expected $expected_class"));
}

View File

@ -181,7 +181,7 @@ class cv {
$index = is_int($value)? $value : null;
$key = is_string($value)? $value : null;
if ($index === null && $key === null && $throw_exception) {
throw ValueException::invalid($value, "key", $prefix);
throw ValueException::invalid_kind($value, "key", $prefix);
} else {
return [$index, $key];
}
@ -198,7 +198,7 @@ class cv {
$string = is_string($value)? $value : null;
$array = is_array($value)? $value : null;
if ($bool === null && $string === null && $array === null && $throw_exception) {
throw ValueException::invalid($value, "value", $prefix);
throw ValueException::invalid_kind($value, "value", $prefix);
} else {
return [$bool, $string, $array];
}

View File

@ -348,7 +348,7 @@ class str {
} elseif (preg_match(self::CAMEL_PATTERN2, $camel, $vs, PREG_OFFSET_CAPTURE)) {
# préfixe en minuscule
} else {
throw ValueException::invalid($camel, "camel string");
throw ValueException::invalid_kind($camel, "camel string");
}
$parts[] = strtolower($vs[1][0]);
$index = intval($vs[1][1]) + strlen($vs[1][0]);

View File

@ -4,11 +4,11 @@ namespace nulib;
use nulib\tests\TestCase;
class ValueExceptionTest extends TestCase {
function testUnexpectedClass() {
$e = ValueException::unexpected_class(null, self::class);
function testInvalidClass() {
$e = ValueException::invalid_class(null, self::class);
self::assertSame("class is invalid, expected ".self::class, $e->getMessage());
$e = ValueException::unexpected_class(ValueException::class, self::class);
$e = ValueException::invalid_class(ValueException::class, self::class);
self::assertSame(ValueException::class.": class is invalid, expected ".self::class, $e->getMessage());
}
}