Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.44% |
34 / 36 |
|
87.50% |
7 / 8 |
CRAP | |
0.00% |
0 / 1 |
Throws | |
94.44% |
34 / 36 |
|
87.50% |
7 / 8 |
22.08 | |
0.00% |
0 / 1 |
getErrorCode | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
reThrow | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
wrap | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
9 | |||
throwCore | |
50.00% |
2 / 4 |
|
0.00% |
0 / 1 |
2.50 | |||
throwIf | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
throwIfNull | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
throwIfNullOrEmpty | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
throwIfNullOrWhiteSpace | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core\Throws; |
6 | |
7 | use Throwable; |
8 | use TypeError; |
9 | use PeServer\Core\Collection\Arr; |
10 | use PeServer\Core\ReflectionUtility; |
11 | use PeServer\Core\Text; |
12 | use PeServer\Core\Type; |
13 | |
14 | /** |
15 | * 例外処理系。 |
16 | */ |
17 | abstract class Throws |
18 | { |
19 | #region function |
20 | |
21 | /** |
22 | * `Throwable::getCode` のラッパー。 |
23 | * |
24 | * なんかもうつらい。 |
25 | * |
26 | * @param Throwable $throwable |
27 | * @return int 取得したエラーコード。取得できなかった場合は `PHP_INT_MIN` を返す。 |
28 | */ |
29 | public static function getErrorCode(Throwable $throwable): int |
30 | { |
31 | $rawCode = $throwable->getCode(); |
32 | $code = PHP_INT_MIN; |
33 | if (is_int($rawCode)) { |
34 | $code = $rawCode; |
35 | } |
36 | |
37 | return $code; |
38 | } |
39 | |
40 | /** |
41 | * 例外の再スロー。 |
42 | * |
43 | * @param class-string<Throwable> $className 例外名。 |
44 | * @param Throwable $previous ラップする元の例外。 |
45 | */ |
46 | public static function reThrow(string $className, Throwable $previous, string $message = null): never |
47 | { |
48 | $message = $message ?? $previous->getMessage(); |
49 | $code = self::getErrorCode($previous); |
50 | |
51 | /** @var Throwable */ |
52 | $exception = ReflectionUtility::create($className, Throwable::class, $message, $code, $previous); |
53 | throw $exception; |
54 | } |
55 | |
56 | /** |
57 | * 対象の例外を受け取った場合に指定した例外として再送出する。 |
58 | * |
59 | * @template TResult |
60 | * @param $catchExceptions 対象の例外名(複数ある場合は配列で指定)。 |
61 | * @phpstan-param class-string<Throwable>|non-empty-array<class-string<Throwable>> $catchExceptions 対象の例外名(複数ある場合は配列で指定)。 |
62 | * @param class-string<Throwable> $throwException 再送出する例外名。 |
63 | * @param callable $callback 例外を発生させる可能性のある処理。 |
64 | * @phpstan-param callable():TResult $callback |
65 | * @return mixed `$callback` が戻り値を持つ場合にその値。戻り値を持たない場合は `null`。 |
66 | * @phpstan-return TResult |
67 | * @throws TypeError 入力パラメータが不正。 |
68 | */ |
69 | public static function wrap(string|array $catchExceptions, string $throwException, callable $callback): mixed |
70 | { |
71 | if (is_string($catchExceptions)) { |
72 | $catchExceptions = [$catchExceptions]; |
73 | } elseif (Arr::isNullOrEmpty($catchExceptions)) { //@phpstan-ignore-line [DOCTYPE] non-empty-array |
74 | throw new TypeError('array: $catchException'); |
75 | } |
76 | |
77 | foreach ($catchExceptions as $key => $catchException) { |
78 | if (!is_subclass_of($catchException, Throwable::class)) { //@phpstan-ignore-line [DOCTYPE] class-string<Throwable> |
79 | throw new TypeError('$catchException[' . $key . ']: ' . $catchException); |
80 | } |
81 | } |
82 | |
83 | if (!is_subclass_of($throwException, Throwable::class)) { //@phpstan-ignore-line [DOCTYPE] class-string<Throwable> |
84 | throw new TypeError('$throwException'); |
85 | } |
86 | |
87 | try { |
88 | return $callback(); |
89 | } catch (Throwable $throwable) { |
90 | foreach ($catchExceptions as $key => $catchException) { |
91 | if (is_a($throwable, $catchException)) { |
92 | self::reThrow($throwException, $throwable); |
93 | } |
94 | } |
95 | throw $throwable; |
96 | } |
97 | } |
98 | |
99 | /** |
100 | * 例外ぶん投げ。 |
101 | * |
102 | * @param string $argument |
103 | * @param class-string<Throwable> $exceptionClass |
104 | */ |
105 | private static function throwCore(string $argument, string $exceptionClass): never |
106 | { |
107 | try { |
108 | $exception = ReflectionUtility::create($exceptionClass, Throwable::class, $argument); |
109 | } catch (TypeError $ex) { |
110 | throw new InvalidClassNameError($exceptionClass); |
111 | } |
112 | |
113 | throw $exception; |
114 | } |
115 | |
116 | /** |
117 | * 偽の場合に例外。 |
118 | * |
119 | * @param boolean $value |
120 | * @param string $argument |
121 | * @param class-string<Throwable> $exceptionClass |
122 | * @return void |
123 | */ |
124 | public static function throwIf(bool $value, string $argument = '', string $exceptionClass = InvalidException::class): void |
125 | { |
126 | if (!$value) { |
127 | self::throwCore($argument, $exceptionClass); |
128 | } |
129 | } |
130 | |
131 | /** |
132 | * nullの場合に例外。 |
133 | * |
134 | * @param mixed $value |
135 | * @param string $argument |
136 | * @param class-string<Throwable> $exceptionClass |
137 | * @return void |
138 | */ |
139 | public static function throwIfNull(mixed $value, string $argument = '', string $exceptionClass = InvalidException::class): void |
140 | { |
141 | if ($value === null) { |
142 | self::throwCore($argument, $exceptionClass); |
143 | } |
144 | } |
145 | |
146 | /** |
147 | * 文字列がnullか空の場合に例外。 |
148 | * |
149 | * @param string|null $value |
150 | * @param string $argument |
151 | * @param class-string<Throwable> $exceptionClass |
152 | * @return void |
153 | */ |
154 | public static function throwIfNullOrEmpty(?string $value, string $argument = '', string $exceptionClass = InvalidException::class): void |
155 | { |
156 | if (Text::isNullOrEmpty($value)) { |
157 | self::throwCore($argument, $exceptionClass); |
158 | } |
159 | } |
160 | |
161 | /** |
162 | * 文字列がnullかホワイトスペースのみの場合に例外。 |
163 | * |
164 | * @param string|null $value |
165 | * @param string $argument |
166 | * @param class-string<Throwable> $exceptionClass |
167 | * @return void |
168 | */ |
169 | public static function throwIfNullOrWhiteSpace(?string $value, string $argument = '', string $exceptionClass = InvalidException::class): void |
170 | { |
171 | if (Text::isNullOrWhiteSpace($value)) { |
172 | self::throwCore($argument, $exceptionClass); |
173 | } |
174 | } |
175 | |
176 | #endregion |
177 | } |