réorganiser les exceptions

This commit is contained in:
Jephté Clain 2025-10-08 12:42:51 +04:00
parent cf30ff6386
commit 42992c84d4
44 changed files with 442 additions and 324 deletions

View File

@ -2,17 +2,16 @@
namespace cli\pman;
use nulib\cl;
use nulib\exceptions;
use nulib\ext\json;
use nulib\file;
use nulib\os\path;
use nulib\ValueException;
class ComposerFile {
function __construct(string $composerFile=".", bool $ensureExists=true) {
if (is_dir($composerFile)) $composerFile = path::join($composerFile, 'composer.json');
if ($ensureExists && !file_exists($composerFile)) {
$message = path::ppath($composerFile).": fichier introuvable";
throw new ValueException($message);
throw exceptions::invalid_value(path::ppath($composerFile), "ce fichier", "il est introuvable");
}
$this->composerFile = $composerFile;
$this->load();

View File

@ -2,10 +2,10 @@
namespace cli\pman;
use nulib\A;
use nulib\exceptions;
use nulib\ext\yaml;
use nulib\os\path;
use nulib\str;
use nulib\ValueException;
class ComposerPmanFile {
const NAMES = [".composer.pman", ".pman"];
@ -29,8 +29,7 @@ class ComposerPmanFile {
}
}
if ($ensureExists && !file_exists($configFile)) {
$message = path::ppath($configFile).": fichier introuvable";
throw new ValueException($message);
throw exceptions::invalid_value(path::ppath($configFile), "ce fichier", "il est introuvable");
}
$this->configFile = $configFile;
$this->load();
@ -66,9 +65,7 @@ class ComposerPmanFile {
function getProfileConfig(string $profile, ?array $composerRequires=null, ?array $composerRequireDevs=null): array {
$config = $this->data["composer"][$profile] ?? null;
if ($config === null) {
throw new ValueException("$profile: profil invalide");
}
if ($config === null) throw exceptions::invalid_value($profile, "ce profil");
if ($composerRequires !== null) {
$matchRequires = $this->data["composer"]["match_require"];
foreach ($composerRequires as $dep => $version) {

View File

@ -1,36 +1,38 @@
<?php
namespace nulib;
use LogicException;
/**
* Class AccessException: indiquer que la resource ou l'objet auquel on veut
* accéder n'est pas accessible. il s'agit donc d'une erreur de l'utilisateur
*/
class AccessException extends UserException {
class AccessException extends LogicException {
static final function read_only(?string $dest=null, ?string $prefix=null): self {
if ($prefix) $prefix = "$prefix: ";
if ($dest === null) $dest = "this property";
$message = "$dest is read-only";
return new static($prefix.$message);
return new static("$prefix$message");
}
static final function immutable_object(?string $dest=null, ?string $prefix=null): self {
if ($prefix) $prefix = "$prefix: ";
if ($dest === null) $dest = "this object";
$message = "$dest is immutable";
return new static($prefix.$message);
return new static("$prefix$message");
}
static final function not_allowed(?string $action=null, ?string $prefix=null): self {
if ($prefix) $prefix = "$prefix: ";
if ($action === null) $action = "this operation";
$message = "$action is not allowed";
return new static($prefix.$message);
return new static("$prefix$message");
}
static final function not_accessible(?string $dest=null, ?string $prefix=null): self {
if ($prefix) $prefix = "$prefix: ";
if ($dest === null) $dest = "this resource";
$message = "$dest is not accessible";
return new static($prefix.$message);
return new static("$prefix$message");
}
}

View File

@ -38,17 +38,22 @@ class ExceptionShadow {
$this->trace = self::extract_trace($exception->getTrace());
$previous = $exception->getPrevious();
if ($previous !== null) $this->previous = new static($previous);
if ($exception instanceof UserException) {
$this->userMessage = $exception->getUserMessage();
$this->techMessage = $exception->getTechMessage();
} else {
$this->userMessage = null;
$this->techMessage = null;
}
}
/** @var string */
protected $class;
protected string $class;
function getClass(): string {
return $this->class;
}
/** @var string */
protected $message;
protected string $message;
function getMessage(): string {
return $this->message;
@ -61,22 +66,19 @@ class ExceptionShadow {
return $this->code;
}
/** @var string */
protected $file;
protected string $file;
function getFile(): string {
return $this->file;
}
/** @var int */
protected $line;
protected int $line;
function getLine(): int {
return $this->line;
}
/** @var array */
protected $trace;
protected array $trace;
function getTrace(): array {
return $this->trace;
@ -92,10 +94,21 @@ class ExceptionShadow {
return implode("\n", $lines);
}
/** @var ExceptionShadow */
protected $previous;
protected ?ExceptionShadow $previous;
function getPrevious(): ?ExceptionShadow {
return $this->previous;
}
protected ?array $userMessage;
function getUserMessage(): ?array {
return $this->userMessage;
}
protected ?array $techMessage;
function getTechMessage(): ?array {
return $this->techMessage;
}
}

View File

@ -18,8 +18,7 @@ class ExitError extends Error {
return $this->getCode() !== 0;
}
/** @var ?string */
protected $userMessage;
protected ?string $userMessage;
function haveUserMessage(): bool {
return $this->userMessage !== null;

View File

@ -12,12 +12,12 @@ class StateException extends LogicException {
if ($method === null) $method = "this method";
$message = "$method is not implemented";
if ($prefix) $prefix = "$prefix: ";
return new static($prefix.$message);
return new static("$prefix$message");
}
static final function unexpected_state(?string $suffix=null): self {
$message = "unexpected state";
if ($suffix) $suffix = ": $suffix";
return new static($message.$suffix);
return new static("$message$suffix");
}
}

View File

@ -1,90 +1,35 @@
<?php
namespace nulib;
use nulib\php\content\c;
use RuntimeException;
use Throwable;
/**
* Class UserException: une exception qui peut en plus contenir un message
* utilisateur
* Class UserException: une exception qui peut contenir un message utilisateur
* et un message technique
*/
class UserException extends RuntimeException {
/** @param Throwable|ExceptionShadow $e */
static function get_user_message($e): ?string {
if ($e instanceof self) return $e->getUserMessage();
else return null;
function __construct($userMessage, $code=0, ?Throwable $previous=null) {
$this->userMessage = $userMessage = c::resolve($userMessage);
parent::__construct(c::to_string($userMessage), $code, $previous);
}
/** @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);
}
protected ?array $userMessage;
/** @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($userMessage, $techMessage=null, $code=0, ?Throwable $previous=null) {
$this->userMessage = $userMessage;
if ($techMessage === null) $techMessage = $userMessage;
parent::__construct($techMessage, $code, $previous);
}
/** @var ?string */
protected $userMessage;
function getUserMessage(): ?string {
function getUserMessage(): ?array {
return $this->userMessage;
}
protected ?array $techMessage = null;
function getTechMessage(): ?array {
return $this->techMessage;
}
function setTechMessage($techMessage): self {
$techMessage ??= c::resolve($techMessage);
$this->techMessage = $techMessage;
return $this;
}
}

View File

@ -5,72 +5,4 @@ namespace nulib;
* Class ValueException: indiquer qu'une valeur est invalide
*/
class ValueException extends UserException {
private static function value($value): string {
if (is_object($value)) {
return "<".get_class($value).">";
} elseif (is_array($value)) {
$values = $value;
$parts = [];
$index = 0;
foreach ($values as $key => $value) {
if ($key === $index) {
$index++;
$parts[] = self::value($value);
} else {
$parts[] = "$key=>".self::value($value);
}
}
return "[" . implode(", ", $parts) . "]";
} elseif (is_string($value)) {
return $value;
} else {
return var_export($value, true);
}
}
private static function message($value, ?string $message, ?string $kind, ?string $prefix, ?string $suffix): string {
if ($kind === null) $kind = "value";
if ($message === null) $message = "$kind$suffix";
if ($value !== null) {
$value = self::value($value);
if ($prefix) $prefix = "$prefix: $value";
else $prefix = $value;
}
if ($prefix) $prefix = "$prefix: ";
return $prefix.$message;
}
static final function null(?string $kind=null, ?string $prefix=null, ?string $message=null): self {
return new static(self::message(null, $message, $kind, $prefix, " should not be null"));
}
static final function check_null($value, ?string $kind=null, ?string $prefix=null, ?string $message=null) {
if ($value === null) throw static::null($kind, $prefix, $message);
return $value;
}
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 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"));
}
static final function forbidden($value=null, ?string $kind=null, ?string $prefix=null, ?string $message=null): self {
return new static(self::message($value, $message, $kind, $prefix, " is forbidden"));
}
}

View File

@ -5,13 +5,13 @@ use nulib\A;
use nulib\app\cli\Application;
use nulib\app\config\ProfileManager;
use nulib\cl;
use nulib\exceptions;
use nulib\ExitError;
use nulib\os\path;
use nulib\os\sh;
use nulib\php\func;
use nulib\ref\ref_profiles;
use nulib\str;
use nulib\ValueException;
class app {
private static function isa_Application($app): bool {
@ -59,7 +59,7 @@ class app {
} elseif (is_array($app)) {
$params = $app;
} else {
throw ValueException::invalid_type($app, Application::class);
throw exceptions::invalid_type($app, "app", Application::class);
}
return $params;
}
@ -410,7 +410,7 @@ class app {
function fencedJoin(string $basedir, ?string ...$paths): string {
$path = path::reljoin($basedir, ...$paths);
if (!path::is_within($path, $basedir)) {
throw ValueException::invalid_value($path, "path");
throw exceptions::invalid_type($path, $kind, "path");
}
return $path;
}

View File

@ -6,7 +6,8 @@ use stdClass;
abstract class AbstractArgsParser {
protected function notEnoughArgs(int $needed, ?string $arg=null): ArgsException {
if ($arg !== null) $arg .= ": ";
return new ArgsException("${arg}nécessite $needed argument(s) supplémentaires");
$reason = $arg._exceptions::missing_value_message($needed);
return _exceptions::missing_value(null, null, $reason);
}
protected function checkEnoughArgs(?string $option, int $count): void {
@ -15,16 +16,17 @@ abstract class AbstractArgsParser {
protected function tooManyArgs(int $count, int $expected, ?string $arg=null): ArgsException {
if ($arg !== null) $arg .= ": ";
return new ArgsException("${arg}trop d'arguments (attendu $expected, reçu $count)");
$reason = $arg._exceptions::unexpected_value_message($count - $expected);
return _exceptions::unexpected_value(null, null, $reason);
}
protected function invalidArg(string $arg): ArgsException {
return new ArgsException("$arg: argument invalide");
return _exceptions::invalid_value($arg);
}
protected function ambiguousArg(string $arg, array $candidates): ArgsException {
$candidates = implode(", ", $candidates);
return new ArgsException("$arg: argument ambigû (les valeurs possibles sont $candidates)");
return new ArgsException("$arg: cet argument est ambigû (les valeurs possibles sont $candidates)");
}
/**

View File

@ -147,11 +147,11 @@ class Aodef {
protected function processExtends(Aolist $argdefs): void {
$option = $this->extends;
if ($option === null) {
throw ArgsException::missing("extends", "destination arg");
throw _exceptions::null_value("extends", "il doit spécifier l'argument destination");
}
$dest = $argdefs->get($option);
if ($dest === null) {
throw ArgsException::invalid($option, "destination arg");
throw _exceptions::invalid_value($option, "extends", "il doit spécifier un argument valide");
}
if ($this->ensureArray !== null) $dest->ensureArray = $this->ensureArray;
@ -178,7 +178,7 @@ class Aodef {
$args = $ms[2] ?? null;
$option = "--$name";
} else {
throw ArgsException::invalid($option, "long option");
throw _exceptions::invalid_value($option, "cette option longue");
}
} elseif (substr($option, 0, 1) === "-") {
$type = self::TYPE_SHORT;
@ -187,7 +187,7 @@ class Aodef {
$args = $ms[2] ?? null;
$option = "-$name";
} else {
throw ArgsException::invalid($option, "short option");
throw _exceptions::invalid_value($option, " cette option courte");
}
} else {
$type = self::TYPE_COMMAND;
@ -196,7 +196,7 @@ class Aodef {
$args = null;
$option = "$name";
} else {
throw ArgsException::invalid($option, "command");
throw _exceptions::invalid_value($option, "cette commande");
}
}
if ($args === ":") {
@ -347,7 +347,7 @@ class Aodef {
$haveNull = true;
break;
} else {
throw ArgsException::invalid("$desc: $arg", "option arg");
throw _exceptions::invalid_value("$desc: $arg", $kind, "ce n'est pas un argument valide");
}
}
@ -366,7 +366,7 @@ class Aodef {
$haveNull = true;
break;
} else {
throw ArgsException::invalid("$desc: $arg", "option arg");
throw _exceptions::invalid_value("$desc: $arg", $kind, "ce n'est pas un argument valide");
}
}
if (!$haveOpt) $haveNull = true;
@ -519,7 +519,7 @@ class Aodef {
case "--set-args": $this->actionSetArgs($dest, $value); break;
case "--set-command": $this->actionSetCommand($dest, $value); break;
case "--show-help": $parser->actionPrintHelp($arg); break;
default: throw ArgsException::invalid($this->action, "arg action");
default: throw _exceptions::invalid_value($this->action, $kind, "action non supportée");
}
}

View File

@ -10,7 +10,7 @@ class Aogroup extends Aolist {
function __construct(array $defs, bool $setup=false) {
$marker = A::pop($defs, 0);
if ($marker !== "group") {
throw ArgsException::invalid(null, "group");
throw _exceptions::missing_value(null, $kind, "ce n'est pas un groupe valide");
}
# réordonner les clés numériques
$defs = array_merge($defs);

View File

@ -1,20 +1,7 @@
<?php
namespace nulib\app\args;
use nulib\ValueException;
use nulib\UserException;
class ArgsException extends ValueException {
static function missing(?string $value, string $kind): self {
$msg = $value;
if ($msg !== null) $msg .= ": ";
$msg .= "missing $kind";
throw new self($msg);
}
static function invalid(?string $value, string $kind): self {
$msg = $value;
if ($msg !== null) $msg .= ": ";
$msg .= "invalid $kind";
throw new self($msg);
}
class ArgsException extends UserException {
}

View File

@ -0,0 +1,10 @@
<?php
namespace nulib\app\args;
use nulib\exceptions;
class _exceptions extends exceptions {
const EXCEPTION = ArgsException::class;
const WORD = "masc:l'argument#s";
}

View File

@ -4,8 +4,8 @@ namespace nulib\app;
use nulib\app\config\ConfigManager;
use nulib\app\config\JsonConfig;
use nulib\app\config\YamlConfig;
use nulib\exceptions;
use nulib\os\path;
use nulib\ValueException;
/**
* Class config: gestion de la configuration de l'application
@ -37,7 +37,7 @@ class config {
} elseif ($ext === ".json") {
$config = new JsonConfig($file);
} else {
throw ValueException::invalid_value($file, "config file");
throw exceptions::invalid_type($file, $kind, "config file");
}
self::add($config);
}

View File

@ -4,8 +4,8 @@ namespace nulib\app\config;
use nulib\A;
use nulib\app\app;
use nulib\cl;
use nulib\exceptions;
use nulib\php\func;
use nulib\ValueException;
use ReflectionClass;
class ConfigManager {
@ -93,7 +93,7 @@ class ConfigManager {
} elseif (is_array($config)) {
$config = new ArrayConfig($config);
} elseif (!($config instanceof IConfig)) {
throw ValueException::invalid_type($config, "array|IConfig");
throw exceptions::invalid_type($config, "config", ["array", IConfig::class]);
}
if (!$inProfiles) $inProfiles = [IConfig::PROFILE_ALL];

View File

@ -36,7 +36,7 @@ class CacheFile extends SharedFile {
yield from $data;
});
} else {
throw ValueException::invalid_type($source, CacheData::class);
throw exceptions::invalid_type($source, "source", CacheData::class);
}
return true;
}

View File

@ -166,6 +166,12 @@ class cv {
#############################################################################
/** retourner $value si elle est non nulle, lancer une exception sinon */
static final function not_null($value, ?string $kind=null) {
if ($value !== null) return $value;
throw exceptions::null_value($kind);
}
/** vérifier si $value est un booléen, sinon retourner null */
static final function check_bool($value): ?bool {
return is_bool($value)? $value: null;
@ -196,7 +202,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_kind($value, "key", $prefix);
throw exceptions::invalid_type($value, $kind, "key", $prefix);
} else {
return [$index, $key];
}
@ -213,7 +219,7 @@ class cv {
$scalar = !is_bool($value) && is_scalar($value)? $value : null;
$array = is_array($value)? $value : null;
if ($bool === null && $scalar === null && $array === null && $throw_exception) {
throw ValueException::invalid_kind($value, "value", $prefix);
throw exceptions::invalid_type($value, $kind, ["bool", "scalar", "array"], $prefix);
} else {
return [$bool, $scalar, $array];
}

View File

@ -2,8 +2,8 @@
namespace nulib\db;
use nulib\cl;
use nulib\exceptions;
use nulib\php\func;
use nulib\ValueException;
use Traversable;
/**
@ -66,7 +66,7 @@ class Capacitor implements ITransactor {
if ($channel instanceof CapacitorChannel) {
$this->subChannels[] = $channel;
} else {
throw ValueException::invalid_type($channel, CapacitorChannel::class);
throw exceptions::invalid_type($channel, "channel", CapacitorChannel::class);
}
}
}

View File

@ -5,8 +5,8 @@ use nulib\A;
use nulib\cl;
use nulib\cv;
use nulib\db\_private\_migration;
use nulib\exceptions;
use nulib\php\func;
use nulib\ValueException;
use Traversable;
/**
@ -596,7 +596,7 @@ abstract class CapacitorStorage {
* si $filter n'est pas un tableau, il est transformé en ["id_" => $filter]
*/
function _one(CapacitorChannel $channel, $filter, ?array $mergeQuery=null): ?array {
if ($filter === null) throw ValueException::null("filter");
if ($filter === null) throw exceptions::null_value("filter");
$this->_create($channel);
$this->verifixFilter($channel, $filter);
$raw = $this->db()->one(cl::merge([

View File

@ -1,14 +1,14 @@
<?php
namespace nulib\db\_private;
use nulib\ValueException;
use nulib\exceptions;
abstract class _base extends _common {
protected static function verifix(&$sql, ?array &$bindings=null, ?array &$meta=null): void {
if (is_array($sql)) {
$prefix = $sql[0] ?? null;
if ($prefix === null) {
throw new ValueException("requête invalide");
throw exceptions::invalid_type($sql, "cette requête sql");
} elseif (_create::isa($prefix)) {
$sql = _create::parse($sql, $bindings);
$meta = ["isa" => "create", "type" => "ddl"];
@ -28,7 +28,7 @@ abstract class _base extends _common {
$sql = _generic::parse($sql, $bindings);
$meta = ["isa" => "generic", "type" => null];
} else {
throw ValueException::invalid_kind($sql, "query");
throw exceptions::invalid_type($sql, "cette requête sql");
}
} else {
if (!is_string($sql)) $sql = strval($sql);

View File

@ -2,8 +2,8 @@
namespace nulib\db\_private;
use nulib\cl;
use nulib\exceptions;
use nulib\str;
use nulib\ValueException;
class _common {
protected static function consume(string $pattern, string &$string, ?array &$ms=null): bool {
@ -249,7 +249,7 @@ class _common {
protected static function check_eof(string $tmpsql, string $usersql): void {
self::consume(';\s*', $tmpsql);
if ($tmpsql) {
throw new ValueException("unexpected value at end: $usersql");
throw exceptions::invalid_value($usersql, "cette requête sql");
}
}
}

View File

@ -2,7 +2,7 @@
namespace nulib\db\_private;
use nulib\cl;
use nulib\ValueException;
use nulib\exceptions;
class _insert extends _common {
const SCHEMA = [
@ -44,7 +44,7 @@ class _insert extends _common {
} elseif ($into !== null) {
$sql[] = $into;
} else {
throw new ValueException("expected table name: $usersql");
throw exceptions::invalid_value($usersql, "cette requête sql", "il faut spécifier la table");
}
## cols & values

View File

@ -2,8 +2,8 @@
namespace nulib\db\_private;
use nulib\cl;
use nulib\exceptions;
use nulib\str;
use nulib\ValueException;
class _select extends _common {
const SCHEMA = [
@ -101,7 +101,7 @@ class _select extends _common {
$sql[] = "from";
$sql[] = $from;
} else {
throw new ValueException("expected table name: $usersql");
throw exceptions::invalid_value($usersql, "cette requête sql", "il faut spécifier la table");
}
## where

View File

@ -6,8 +6,8 @@ use nulib\db\_private\_config;
use nulib\db\_private\Tvalues;
use nulib\db\IDatabase;
use nulib\db\ITransactor;
use nulib\exceptions;
use nulib\php\func;
use nulib\ValueException;
class Pdo implements IDatabase {
use Tvalues;
@ -203,7 +203,7 @@ class Pdo implements IDatabase {
$this->transactors[] = $transactor;
$transactor->willUpdate();
} else {
throw ValueException::invalid_type($transactor, ITransactor::class);
throw exceptions::invalid_type($transactor, "transactor", ITransactor::class);
}
}
return $this;

View File

@ -6,8 +6,8 @@ use nulib\db\_private\_config;
use nulib\db\_private\Tvalues;
use nulib\db\IDatabase;
use nulib\db\ITransactor;
use nulib\exceptions;
use nulib\php\func;
use nulib\ValueException;
class Pgsql implements IDatabase {
use Tvalues;
@ -253,7 +253,7 @@ class Pgsql implements IDatabase {
$this->transactors[] = $transactor;
$transactor->willUpdate();
} else {
throw ValueException::invalid_type($transactor, ITransactor::class);
throw exceptions::invalid_type($transactor, "transactor", ITransactor::class);
}
}
return $this;

View File

@ -7,8 +7,8 @@ use nulib\db\_private\_config;
use nulib\db\_private\Tvalues;
use nulib\db\IDatabase;
use nulib\db\ITransactor;
use nulib\exceptions;
use nulib\php\func;
use nulib\ValueException;
use SQLite3;
use SQLite3Result;
use SQLite3Stmt;
@ -254,7 +254,7 @@ class Sqlite implements IDatabase {
$this->transactors[] = $transactor;
$transactor->willUpdate();
} else {
throw ValueException::invalid_type($transactor, ITransactor::class);
throw exceptions::invalid_type($transactor, "transactor", ITransactor::class);
}
}
return $this;

View File

@ -1,12 +1,82 @@
<?php
namespace nulib;
use nulib\php\content\c;
use nulib\text\Word;
use Throwable;
/**
* Class exceptions: répertoire d'exceptions normalisées
*/
class exceptions {
/** @param Throwable|ExceptionShadow $e */
public static function get_user_message($e): ?string {
if ($e instanceof UserException) $userMessage = $e->getUserMessage();
elseif ($e instanceof ExceptionShadow) $userMessage = $e->getUserMessage();
else return null;
return c::to_string($userMessage);
}
/** @param Throwable|ExceptionShadow $e */
public static function get_tech_message($e): ?string {
if ($e instanceof UserException) $techMessage = $e->getTechMessage();
elseif ($e instanceof ExceptionShadow) $techMessage = $e->getTechMessage();
else return null;
return c::to_string($techMessage);
}
/** @param Throwable|ExceptionShadow $e */
public static function get_message($e): string {
if ($e instanceof UserException) $userMessage = $e->getUserMessage();
elseif ($e instanceof ExceptionShadow) $userMessage = $e->getUserMessage();
else return $e->getMessage();
return c::to_string($userMessage);
}
/** @param Throwable|ExceptionShadow $e */
public static final function get_summary($e, bool $includePrevious = true): string {
$parts = [];
$first = true;
while ($e !== null) {
$message = self::get_message($e);
if (!$message) $message = "(no message)";
$techMessage = self::get_tech_message($e);
if ($techMessage) $message .= " |$techMessage|";
if ($first) $first = false;
else $parts[] = ", caused by ";
if ($e instanceof ExceptionShadow) $class = $e->getClass();
else $class = get_class($e);
$parts[] = "$class: $message";
$e = $includePrevious ? $e->getPrevious() : null;
}
return implode("", $parts);
}
/** @param Throwable|ExceptionShadow $e */
public 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, false);
}
$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);
}
#############################################################################
const EXCEPTION = ValueException::class;
const WORD = "la valeur#s";
protected static Word $word;
@ -15,23 +85,59 @@ class exceptions {
return self::$word ??= new Word(static::WORD);
}
static function value($value): string {
if (is_object($value)) {
return "<".get_class($value).">";
} elseif (is_array($value)) {
$values = $value;
$parts = [];
$index = 0;
foreach ($values as $key => $value) {
if ($key === $index) {
$index++;
$parts[] = self::value($value);
} else {
$parts[] = "$key=>".self::value($value);
}
}
return "[".implode(", ", $parts)."]";
} elseif (is_string($value)) {
return $value;
} else {
return var_export($value, true);
}
}
static function generic($value, ?string $kind, ?string $cause, ?string $reason=null, ?Throwable $previous=null): UserException {
$msg = "";
if ($value !== null) {
$msg .= self::value($value);
$msg .= ": ";
}
$kind ??= self::word()->_ce();
$msg .= $kind;
$cause ??= "est invalide";
if ($cause) $msg .= " $cause";
if ($reason) $msg .= ": $reason";
$code = $previous !== null? $previous->getCode(): 0;
$class = static::EXCEPTION;
return new $class($msg, $code, $previous);
}
/**
* indiquer qu'une valeur est invalide pour une raison générique
*/
static function invalid_value($value, ?string $reason=null): UserException {
$msg = var_export($value, true);
$msg .= self::word()->_ce();
$msg .= " est invalide";
if ($reason) $msg .= ": $reason";
return new UserException($msg);
static function invalid_value($value, ?string $kind=null, ?string $reason=null, ?Throwable $previous=null): UserException {
return self::generic($value, $kind, null, $reason, $previous);
}
/**
* spécialisation de {@link self::invalid_value()} qui permet d'indiquer les
* types attendus
*/
static function invalid_type($value, $expectedTypes=null): UserException {
$pronom = self::word()->pronom();
static function invalid_type($value, ?string $kind=null, $expectedTypes=null, ?Throwable $previous=null): UserException {
if ($kind !== null) $pronom = "il";
else $pronom = self::word()->pronom();
$expectedTypes = cl::withn($expectedTypes);
if (!$expectedTypes) {
$reason = null;
@ -41,11 +147,12 @@ class exceptions {
$reason = "$pronom doit être d'un des types suivants: ";
}
$reason .= implode(", ", $expectedTypes);
return self::invalid_value($value, $reason);
return self::invalid_value($value, $kind, $reason, $previous);
}
static function invalid_format($value, $expectedFormats=null): UserException {
$pronom = self::word()->pronom();
static function invalid_format($value, ?string $kind=null, $expectedFormats=null, ?Throwable $previous=null): UserException {
if ($kind !== null) $pronom = "il";
else $pronom = self::word()->pronom();
$expectedFormats = cl::withn($expectedFormats);
if (!$expectedFormats) {
$reason = null;
@ -55,51 +162,90 @@ class exceptions {
$reason = "$pronom doit être dans l'un des formats suivants: ";
}
$reason .= implode(", ", $expectedFormats);
return self::invalid_value($value, $reason);
return self::invalid_value($value, $kind, $reason, $previous);
}
static function out_of_range($value, ?int $min=null, ?int $max=null): UserException {
$pronom = self::word()->pronom();
static function forbidden_value($value, ?string $kind=null, $allowedValues=null, ?Throwable $previous=null): UserException {
if ($kind !== null) $pronom = "il";
else $pronom = self::word()->pronom();
$allowedValues = cl::withn($allowedValues);
if (!$allowedValues) $reason = null;
else $reason = "$pronom doit faire partie de cette liste: ";
$reason .= implode(", ", $allowedValues);
return self::invalid_value($value, $kind, $reason, $previous);
}
static function out_of_range($value, ?string $kind=null, ?int $min=null, ?int $max=null, ?Throwable $previous=null): UserException {
if ($kind !== null) {
$pronom = "il";
$compris = "compris";
$superieur = "supérieur";
$inferieur = "inférieur";
} else {
$word = self::word();
$pronom = $word->pronom();
$compris = $word->isFeminin()? "comprise": "compris";
$superieur = $word->isFeminin()? "supérieure": "supérieur";
$inferieur = $word->isFeminin()? "inférieure": "inférieur";
}
if ($min !== null && $max !== null) {
$reason = "$pronom doit être compris entre $min et $max";
$reason = "$pronom doit être $compris entre $min et $max";
} else if ($min !== null) {
$reason = "$pronom doit être supérieur à $min";
$reason = "$pronom doit être $superieur à $min";
} elseif ($max !== null) {
$reason = "$pronom doit être inférieur à $max";
$reason = "$pronom doit être $inferieur à $max";
} else {
$reason = null;
}
return self::invalid_value($value, $reason);
return self::invalid_value($value, $kind, $reason, $previous);
}
static function null_value(?string $reason=null): UserException {
$msg = self::word()->_ce();
$msg .= " ne doit pas être nulle";
if ($reason) $msg .= ": $reason";
return new UserException($msg);
static function null_value(?string $kind=null, ?string $reason=null, ?Throwable $previous=null): UserException {
if ($kind !== null) $nul = "null";
else $nul = self::word()->isFeminin()? "nulle": "nul";
return self::generic(null, $kind, "ne doit pas être $nul", $reason, $previous);
}
static function missing_value_message(?int $amount=null, ?string $kind=null): string {
$message = "il manque ";
if ($kind !== null) {
if ($amount !== null) $message = "$amount $kind";
else $message = $kind;
} else {
if ($amount !== null) $message .= self::word()->q($amount);
else $message .= self::word()->_un();
}
return $message;
}
/**
* indiquer qu'une valeur est manquante
*/
static function missing_value(?int $amout=null, ?string $reason=null): UserException {
$msg = "il manque ";
if ($amout !== null) {
$msg .= self::word()->q($amout);
static function missing_value(?int $amount=null, ?string $kind=null, ?string $reason=null, ?Throwable $previous=null): UserException {
$reason ??= self::missing_value_message($amount, $kind);
$class = static::EXCEPTION;
return new $class($reason, null, $previous);
}
static function unexpected_value_message(?int $amount=null, ?string $kind=null): string {
if ($amount !== null) {
if ($kind !== null) $kind = "$amount $kind";
else $kind = self::word()->q($amount);
$message = "il y a $kind en trop";
} else {
$msg .= self::word()->_le();
if ($kind !== null) $kind = "de $kind";
else $kind = self::word()->_de(2);
$message = "il y a trop $kind";
}
if ($reason) $msg .= ": $reason";
return new UserException($msg);
return $message;
}
/**
* indiquer qu'une valeur est en trop
*/
static function unexpected_value(?string $reason=null): UserException {
$msg = "il y a trop ";
$msg .= self::word()->_de(2);
if ($reason) $msg .= ": $reason";
return new UserException($msg);
static function unexpected_value(?int $amount=null, ?string $kind=null, ?string $reason=null, ?Throwable $previous=null): UserException {
$reason ??= self::unexpected_value_message($amount, $kind);
$class = static::EXCEPTION;
return new $class($reason, null, $previous);
}
}

View File

@ -1,6 +1,7 @@
<?php
namespace nulib\file;
use nulib\exceptions;
use nulib\ValueException;
class SharedFile extends FileWriter {
@ -9,7 +10,7 @@ class SharedFile extends FileWriter {
const DEFAULT_MODE = "c+b";
function __construct($file, ?string $mode=null, ?bool $throwOnError=null, ?bool $allowLocking=null) {
if ($file === null) throw ValueException::null("file");
if ($file === null) throw exceptions::null_value("file");
parent::__construct($file, $mode, $throwOnError, $allowLocking);
}
}

View File

@ -1,6 +1,7 @@
<?php
namespace nulib\file;
use nulib\exceptions;
use nulib\file\csv\csv_flavours;
use nulib\NoMoreDataException;
use nulib\os\EOFException;
@ -61,7 +62,7 @@ class Stream extends AbstractIterator implements IReader, IWriter {
protected $stat;
function __construct($fd, bool $close=true, ?bool $throwOnError=null, ?bool $useLocking=null) {
if ($fd === null) throw ValueException::null("resource");
if ($fd === null) throw exceptions::null_value("resource");
$this->fd = $fd;
$this->close = $close;
$this->throwOnError = $throwOnError ?? static::THROW_ON_ERROR;

View File

@ -2,6 +2,7 @@
namespace nulib\file\tab;
use nulib\cl;
use nulib\exceptions;
use nulib\file\csv\CsvBuilder;
use nulib\file\web\Upload;
use nulib\os\path;
@ -32,7 +33,7 @@ trait TAbstractBuilder {
} elseif (is_array($builder)) {
$params = cl::merge($builder, $params);
} elseif ($builder !== null) {
throw ValueException::invalid_type($builder, self::class);
throw exceptions::invalid_type($builder, "builder", self::class);
}
$output = $params["output"] ?? null;

View File

@ -2,6 +2,7 @@
namespace nulib\file\tab;
use nulib\cl;
use nulib\exceptions;
use nulib\file\csv\CsvReader;
use nulib\file\web\Upload;
use nulib\os\path;
@ -31,7 +32,7 @@ trait TAbstractReader {
} elseif (is_array($reader)) {
$params = cl::merge($reader, $params);
} elseif ($reader !== null) {
throw ValueException::invalid_type($reader, self::class);
throw exceptions::invalid_type($reader, "reader", self::class);
}
$input = $params["input"] ?? null;

View File

@ -19,8 +19,8 @@ class MailTemplate {
$texprs = $mail["exprs"] ?? [];
$this->el = new ExpressionLanguage();
ValueException::check_null($this->subject = $tsubject, "subject");
ValueException::check_null($this->body = $tbody, "body");
$this->subject = cv::not_null($tsubject, "subject");
$this->body = cv::not_null($tbody, "body");
$exprs = [];
# Commencer par extraire les expressions de la forme {name}
if (preg_match_all('/\{([a-zA-Z_][a-zA-Z0-9_.-]*)}/', $this->body, $mss, PREG_SET_ORDER)) {

View File

@ -4,6 +4,7 @@ namespace nulib\mail;
use nulib\app\config;
use nulib\cl;
use nulib\cv;
use nulib\exceptions;
use nulib\output\msg;
use nulib\str;
use nulib\ValueException;
@ -90,7 +91,7 @@ class mailer {
$host = $params["host"] ?? null;
$port = $params["port"] ?? 25;
if ($host === null) {
throw new ValueException("mail host is required");
throw exceptions::null_value("host");
}
msg::debug("new PHPMailer using SMTP to $host:$port");
$mailer->isSMTP();
@ -106,7 +107,7 @@ class mailer {
$mailer->isSendmail();
break;
default:
throw ValueException::invalid_value($backend, "mailer backend");
throw exceptions::forbidden_value($backend, "backend", ["smtp", "phpmail", "sendmail"]);
}
# debug
$debug = $params["debug"] ?? SMTP::DEBUG_OFF;
@ -114,7 +115,7 @@ class mailer {
if ($debug < SMTP::DEBUG_OFF) $debug = SMTP::DEBUG_OFF;
elseif ($debug > SMTP::DEBUG_LOWLEVEL) $debug = SMTP::DEBUG_LOWLEVEL;
} elseif (!self::is_bool($debug)) {
throw ValueException::invalid_value($debug, "debug mode");
throw exceptions::invalid_type($debug, "debug", ["int", "bool"]);
}
$mailer->SMTPDebug = $debug;
# auth, username, password
@ -144,7 +145,10 @@ class mailer {
$mailer->SMTPSecure = $secure;
break;
default:
throw ValueException::invalid_value($secure, "encryption mode");
throw exceptions::forbidden_value($secure, "secure", [
PHPMailer::ENCRYPTION_SMTPS,
PHPMailer::ENCRYPTION_STARTTLS,
]);
}
}
@ -186,7 +190,7 @@ class mailer {
$tos = str::join(",", $tos);
msg::debug("Sending to $tos");
if (!$mailer->send()) {
throw new MailerException("Une erreur s'est produite pendant l'envoi du mail", $mailer->ErrorInfo);
throw new MailerException("erreur d'envoi du mail", $mailer->ErrorInfo);
}
}

View File

@ -2,6 +2,7 @@
namespace nulib\output;
use nulib\cl;
use nulib\exceptions;
use nulib\str;
use nulib\ValueException;
@ -15,7 +16,7 @@ abstract class _messenger {
static function set_messenger_class(string $msg_class, ?array $params=null) {
if (!is_subclass_of($msg_class, IMessenger::class)) {
throw ValueException::invalid_class($msg_class, IMessenger::class);
throw exceptions::invalid_type($msg_class, $kind, IMessenger::class);
}
static::set_messenger(new $msg_class($params));
}

View File

@ -2,6 +2,7 @@
namespace nulib\output;
use nulib\app\app;
use nulib\exceptions;
use nulib\output\std\ProxyMessenger;
use nulib\ValueException;
@ -63,7 +64,7 @@ class console extends _messenger {
]);
break;
default:
throw ValueException::invalid_value($verbosity, "verbosity");
throw exceptions::forbidden_value($verbosity, $kind, ["silent", "quiet", "normal", "verbose", "debug"]);
}
}

View File

@ -4,6 +4,7 @@ namespace nulib\output\std;
use Exception;
use nulib\A;
use nulib\cl;
use nulib\exceptions;
use nulib\ExceptionShadow;
use nulib\output\IMessenger;
use nulib\UserException;
@ -236,9 +237,11 @@ class StdMessenger implements _IMessenger {
return $indentLevel;
}
protected function _printTitle(?string $linePrefix, int $level,
string $type, $content,
int $indentLevel, StdOutput $out): void {
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();
$content = cl::with($content);
@ -284,10 +287,12 @@ class StdMessenger implements _IMessenger {
}
}
protected function _printAction(?string $linePrefix, int $level,
bool $printContent, $content,
bool $printResult, ?bool $rsuccess, $rcontent,
int $indentLevel, StdOutput $out): void {
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";
@ -357,9 +362,11 @@ class StdMessenger implements _IMessenger {
}
}
protected function _printGeneric(?string $linePrefix, int $level,
string $type, $content,
int $indentLevel, StdOutput $out): void {
protected function _printGeneric(
?string $linePrefix, int $level,
string $type, $content,
int $indentLevel, StdOutput $out
): void {
$prefixes = self::GENERIC_PREFIXES[$level][$type];
$content = cl::with($content);
if ($out->isColor()) {
@ -390,7 +397,11 @@ class StdMessenger implements _IMessenger {
}
}
protected function _printGenericOrException(?int $level, string $type, $content, int $indentLevel, StdOutput $out): void {
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;
@ -421,27 +432,18 @@ class StdMessenger implements _IMessenger {
$level1 = $this->decrLevel($level);
$showTraceback = $this->checkLevel($level1);
foreach ($exceptions as $exception) {
# tout d'abord userMessage
if ($exception instanceof UserException) {
$userMessage = UserException::get_user_message($exception);
$userMessage ??= "Une erreur technique s'est produite";
$showSummary = true;
} else {
$userMessage = UserException::get_summary($exception);
$showSummary = false;
}
if ($userMessage !== null && $showContent) {
# tout d'abord message
$message = exceptions::get_message($exception);
if ($showContent) {
if ($printActions) { $this->printActions(); $printActions = false; }
$this->_printGeneric($linePrefix, $level, $type, $userMessage, $indentLevel, $out);
$this->_printGeneric($linePrefix, $level, $type, $message, $indentLevel, $out);
}
# puis summary et traceback
if ($showTraceback) {
if ($printActions) { $this->printActions(); $printActions = false; }
if ($showSummary) {
$summary = UserException::get_summary($exception);
$this->_printGeneric($linePrefix, $level1, $type, $summary, $indentLevel, $out);
}
$traceback = UserException::get_traceback($exception);
$summary = exceptions::get_summary($exception, false);
$this->_printGeneric($linePrefix, $level1, $type, $summary, $indentLevel, $out);
$traceback = exceptions::get_traceback($exception);
$this->_printGeneric($linePrefix, $level1, $type, $traceback, $indentLevel, $out);
}
}

View File

@ -6,8 +6,8 @@ use Exception;
use nulib\A;
use nulib\cl;
use nulib\cv;
use nulib\exceptions;
use nulib\StateException;
use nulib\ValueException;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;
@ -446,11 +446,7 @@ class func {
const TYPE_STATIC = self::TYPE_METHOD | self::FLAG_STATIC;
protected static function not_a_callable($func, ?string $reason) {
if ($reason === null) {
$msg = var_export($func, true);
$reason = "$msg: not a callable";
}
return new ValueException($reason);
throw exceptions::invalid_type($func, null, "callable");
}
private static function _with($func, ?array $args=null, bool $strict=true, ?string &$reason=null): ?self {
@ -604,7 +600,7 @@ class func {
$mask = $staticOnly? self::MASK_PS: self::MASK_P;
$expected = $staticOnly? self::METHOD_PS: self::METHOD_P;
} else {
throw new ValueException("$class_or_object: vous devez spécifier une classe ou un objet");
throw exceptions::invalid_type($class_or_object, null, ["class", "object"]);
}
$methods = [];
foreach ($c->getMethods() as $m) {
@ -777,7 +773,7 @@ class func {
if (is_object($object) && !($this->flags & self::FLAG_STATIC)) {
if (is_object($c)) $c = get_class($c);
if (is_string($c) && !($object instanceof $c)) {
throw ValueException::invalid_type($object, $c);
throw exceptions::invalid_type($object, "object", $c);
}
$this->object = $object;
$this->bound = true;

View File

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

View File

@ -119,6 +119,14 @@ class Word {
$this->w = $spec;
}
function isMasculin(): bool {
return !$this->fem;
}
function isFeminin(): bool {
return $this->fem;
}
/**
* retourner le mot sans article
*

View File

@ -5,9 +5,8 @@ use nulib\UserException;
use Throwable;
class CurlException extends UserException {
function __construct($ch, ?string $message=null, $code=0, ?Throwable $previous=null) {
if ($message === null) $message = "(unknown error)";
$userMessage = $message;
function __construct($ch, $userMessage=null, $code=0, ?Throwable $previous=null) {
$userMessage ??= "erreur curl inconnue";
$techMessage = null;
if ($ch !== null) {
$parts = [];
@ -17,6 +16,7 @@ class CurlException extends UserException {
if ($error != "") $parts[] = "error: $error";
if ($parts) $techMessage = implode(", ", $parts);
}
parent::__construct($userMessage, $techMessage, $code, $previous);
parent::__construct($userMessage, $code, $previous);
$this->setTechMessage($techMessage);
}
}

View File

@ -12,11 +12,11 @@ class curl {
if (!isset($curlOptions[CURLOPT_RETURNTRANSFER])) $curlOptions[CURLOPT_RETURNTRANSFER] = true;
$extractHeaders = isset($curlOptions[CURLOPT_HEADER]) && $curlOptions[CURLOPT_HEADER];
$ch = curl_init();
if ($ch === false) throw new CurlException(null, "init");
if ($ch === false) throw new CurlException(null, "erreur curl lors de l'initialisation");
curl_setopt_array($ch, $curlOptions);
try {
$result = curl_exec($ch);
if ($result === false) throw new CurlException($ch);
if ($result === false) throw new CurlException($ch, "erreur curl lors du téléchargement");
if ($extractHeaders) {
$info = curl_getinfo($ch);
$headersSize = $info["header_size"];

View File

@ -3,6 +3,7 @@
require __DIR__.'/../vendor/autoload.php';
use nulib\app\cli\Application;
use nulib\cv;
use nulib\mail\mailer;
use nulib\ValueException;
@ -20,10 +21,8 @@ Application::run(new class extends Application {
protected $to, $cc, $bcc, $from;
function main() {
$subject = $this->args[0] ?? null;
ValueException::check_null($subject, "subject");
$body = $this->args[1] ?? null;
ValueException::check_null($body, "body");
$subject = cv::not_null($this->args[0] ?? null, "subject");
$body = cv::not_null($this->args[1] ?? null, "body");
mailer::send($this->to, $subject, $body, $this->cc, $this->bcc, $this->from);
}
});

65
php/tbin/test-exceptions.php Executable file
View File

@ -0,0 +1,65 @@
#!/usr/bin/php
<?php
require __DIR__."/../vendor/autoload.php";
use nulib\app\cli\Application;
use nulib\exceptions;
use nulib\output\msg;
use nulib\UserException;
Application::run(new class extends Application {
const ARGS = [
"purpose" => "tester l'affichage des exception",
"merge" => parent::ARGS,
];
function fart(): void {
throw new RuntimeException("fart");
}
function prout(): void {
try {
$this->fart();
} catch (Exception $e) {
throw new RuntimeException("prout", $e->getCode(), $e);
}
}
function main() {
try {
throw new Exception("exception normale");
} catch (Exception $e) {
msg::info("summary: ". exceptions::get_summary($e));
msg::error($e);
}
try {
try {
$this->prout();
} catch (Exception $e) {
throw new Exception("exception normale", $e->getCode(), $e);
}
} catch (Exception $e) {
msg::info("summary: ". exceptions::get_summary($e));
msg::error($e);
}
try {
throw exceptions::invalid_value("valeur", $kind)
->setTechMessage("message technique");
} catch (Exception $e) {
msg::info("summary: ". exceptions::get_summary($e));
msg::error($e);
}
try {
try {
$this->prout();
} catch (Exception $e) {
throw exceptions::invalid_value("valeur", $kind, null, $e)
->setTechMessage("message technique");
}
} catch (Exception $e) {
msg::info("summary: ". exceptions::get_summary($e));
msg::error($e);
}
}
});