1: | <?php declare(strict_types=1); |
2: | |
3: | namespace Salient\Utility\Exception; |
4: | |
5: | use Salient\Utility\Arr; |
6: | use Salient\Utility\Format; |
7: | use Stringable; |
8: | |
9: | |
10: | |
11: | |
12: | class PcreErrorException extends UtilityException |
13: | { |
14: | private const ERROR_MESSAGE_MAP = [ |
15: | \PREG_NO_ERROR => 'No error', |
16: | \PREG_INTERNAL_ERROR => 'Internal error', |
17: | \PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 characters, possibly incorrectly encoded', |
18: | \PREG_BAD_UTF8_OFFSET_ERROR => 'The offset did not correspond to the beginning of a valid UTF-8 code point', |
19: | \PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit exhausted', |
20: | \PREG_RECURSION_LIMIT_ERROR => 'Recursion limit exhausted', |
21: | \PREG_JIT_STACKLIMIT_ERROR => 'JIT stack limit exhausted', |
22: | ]; |
23: | |
24: | |
25: | protected $Pattern; |
26: | |
27: | protected $Subject; |
28: | |
29: | private static array $ErrorNameMap; |
30: | |
31: | |
32: | |
33: | |
34: | |
35: | |
36: | |
37: | public function __construct(?int $error, string $function, $pattern, $subject) |
38: | { |
39: | $error ??= preg_last_error(); |
40: | $message = \PHP_VERSION_ID < 80000 |
41: | ? self::ERROR_MESSAGE_MAP[$error] ?? 'Unknown error' |
42: | : preg_last_error_msg(); |
43: | |
44: | $this->Pattern = $pattern; |
45: | $this->Subject = $subject; |
46: | |
47: | parent::__construct(sprintf( |
48: | 'Call to %s() failed with %s (%s)', |
49: | $function, |
50: | (self::$ErrorNameMap ??= self::getErrorNameMap())[$error] ?? "#$error", |
51: | $message, |
52: | )); |
53: | } |
54: | |
55: | |
56: | |
57: | |
58: | public function __toString(): string |
59: | { |
60: | $detail = ''; |
61: | foreach ([ |
62: | 'Pattern' => is_array($this->Pattern) |
63: | ? array_map( |
64: | fn($value) => is_string($value) ? $value : '<callable>', |
65: | $this->Pattern, |
66: | ) |
67: | : $this->Pattern, |
68: | 'Subject' => $this->Subject, |
69: | ] as $key => $value) { |
70: | if (is_array($value)) { |
71: | $value = Arr::isList($value) |
72: | ? Format::list($value) |
73: | : Format::array($value); |
74: | } |
75: | $detail .= sprintf("\n\n%s:\n%s", $key, rtrim($value, "\n")); |
76: | } |
77: | |
78: | return parent::__toString() . $detail; |
79: | } |
80: | |
81: | |
82: | |
83: | |
84: | private static function getErrorNameMap(): array |
85: | { |
86: | |
87: | $constants = get_defined_constants(true)['pcre']; |
88: | $map = []; |
89: | foreach ($constants as $name => $value) { |
90: | if (substr($name, -6) === '_ERROR') { |
91: | |
92: | $map[$value] = $name; |
93: | } |
94: | } |
95: | return $map; |
96: | } |
97: | } |
98: | |