|
27 | 27 | use Whoops\Util\Misc; |
28 | 28 |
|
29 | 29 | /** |
30 | | - * Gestionnaire d'exceptions |
| 30 | + * Capture et affiche les erreurs et exceptions via whoops |
| 31 | + * |
| 32 | + * Necessite l'instalation de `flip/whoops` |
31 | 33 | */ |
32 | 34 | class ExceptionManager |
33 | 35 | { |
34 | 36 | /** |
35 | | - * Gestionnaire d'exceptions de type http (404, 500) qui peuvent avoir une page d'erreur personnalisée. |
| 37 | + * Gestionnaire d'exception (instance Whoops) |
| 38 | + */ |
| 39 | + private ?Run $debugger = null; |
| 40 | + |
| 41 | + /** |
| 42 | + * Configuration du gestionnaire d'exception |
| 43 | + */ |
| 44 | + private object $config; |
| 45 | + |
| 46 | + public function __construct() |
| 47 | + { |
| 48 | + if (class_exists(Run::class)) { |
| 49 | + $this->debugger = new Run(); |
| 50 | + $this->config = (object) config('exceptions'); |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + /** |
| 55 | + * Demarre le processus |
36 | 56 | */ |
37 | | - public static function registerHttpErrors(Run $debugger, array $config): Run |
| 57 | + public function register(): void |
38 | 58 | { |
39 | | - return $debugger->pushHandler(static function (Throwable $exception, InspectorInterface $inspector, RunInterface $run) use ($config): int { |
40 | | - $exception = self::prepareException($exception); |
| 59 | + if (! $this->debugger) { |
| 60 | + return; |
| 61 | + } |
41 | 62 |
|
| 63 | + $this->registerWhoopsHandler() |
| 64 | + ->registerHttpErrorsHandler() |
| 65 | + ->registerAppHandlers(); |
| 66 | + |
| 67 | + $this->debugger->register(); |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * Enregistre les gestionnaires d'exception spécifiques à l'application. |
| 72 | + * |
| 73 | + * Cette méthode parcourt les gestionnaires configurés et les ajoute au débogueur. |
| 74 | + * Elle prend en charge à la fois les gestionnaires callable et les noms de classe sous forme de chaîne qui peuvent être instanciés. |
| 75 | + */ |
| 76 | + private function registerAppHandlers(): self |
| 77 | + { |
| 78 | + foreach ($this->config->handlers as $handler) { |
| 79 | + if (is_callable($handler)) { |
| 80 | + $this->debugger->pushHandler($handler); |
| 81 | + } elseif (is_string($handler) && class_exists($handler)) { |
| 82 | + $class = service('container')->make($handler); |
| 83 | + if (is_callable($class) || $class instanceof HandlerInterface) { |
| 84 | + $this->debugger->pushHandler($class); |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + return $this; |
| 90 | + } |
| 91 | + |
| 92 | + /** |
| 93 | + * Enregistre un gestionnaire pour les erreurs HTTP. |
| 94 | + * |
| 95 | + * Cette méthode met en place un gestionnaire d'erreurs personnalisé qui traite les exceptions, |
| 96 | + * les consigne si elle est configurée, et tente d'afficher les vues d'erreur appropriées. |
| 97 | + * Elle gère les codes d'état HTTP, la journalisation et les vues d'erreur personnalisées. |
| 98 | + */ |
| 99 | + private function registerHttpErrorsHandler(): self |
| 100 | + { |
| 101 | + $this->debugger->pushHandler(function (Throwable $exception, InspectorInterface $inspector, RunInterface $run): int { |
| 102 | + $exception = $this->prepareException($exception); |
42 | 103 | $exception_code = $exception->getCode(); |
| 104 | + |
43 | 105 | if ($exception_code >= 400 && $exception_code < 600) { |
44 | 106 | $run->sendHttpCode($exception_code); |
45 | 107 | } |
46 | 108 |
|
47 | | - if (true === $config['log'] && ! in_array($exception->getCode(), $config['ignore_codes'], true)) { |
| 109 | + if (true === $this->config->log && ! in_array($exception_code, $this->config->ignore_codes, true)) { |
48 | 110 | service('logger')->error($exception); |
49 | 111 | } |
50 | 112 |
|
51 | | - if (is_dir($config['error_view_path'])) { |
52 | | - $files = array_map(static fn (SplFileInfo $file) => $file->getFilenameWithoutExtension(), service('fs')->files($config['error_view_path'])); |
| 113 | + if (is_dir($this->config->error_view_path)) { |
| 114 | + $files = array_map(static fn (SplFileInfo $file) => $file->getFilenameWithoutExtension(), service('fs')->files($this->config->error_view_path)); |
53 | 115 | } else { |
54 | 116 | $files = []; |
55 | 117 | } |
56 | 118 |
|
57 | | - if (in_array((string) $exception->getCode(), $files, true)) { |
58 | | - $view = new View(); |
59 | | - $view->setAdapter(config('view.active_adapter', 'native'), ['view_path' => $config['error_view_path']]) |
60 | | - ->display((string) $exception->getCode()) |
61 | | - ->setData(['message' => $exception->getMessage()]) |
62 | | - ->render(); |
| 119 | + $files = collect($files)->flip()->only($exception_code, is_online() ? 'production' : '')->flip()->all(); |
63 | 120 |
|
64 | | - return Handler::QUIT; |
65 | | - } |
66 | | - if (in_array('production', $files, true) && is_online()) { |
| 121 | + if ($files !== []) { |
67 | 122 | $view = new View(); |
68 | | - $view->setAdapter(config('view.active_adapter', 'native'), ['view_path_locator' => $config['error_view_path']]) |
69 | | - ->display('production') |
| 123 | + |
| 124 | + $view->setAdapter(config('view.active_adapter', 'native'), ['view_path' => $this->config->error_view_path]) |
| 125 | + ->first($files, ['message' => $exception->getMessage()]) |
70 | 126 | ->render(); |
71 | 127 |
|
72 | 128 | return Handler::QUIT; |
73 | 129 | } |
74 | 130 |
|
75 | 131 | return Handler::DONE; |
76 | 132 | }); |
77 | | - } |
78 | | - |
79 | | - /** |
80 | | - * Gestionnaire d'applications fournis par le developpeur. |
81 | | - */ |
82 | | - public static function registerAppHandlers(Run $debugger, array $config): Run |
83 | | - { |
84 | | - foreach ($config['handlers'] ?? [] as $handler) { |
85 | | - if (is_callable($handler)) { |
86 | | - $debugger->pushHandler($handler); |
87 | | - } elseif (is_string($handler) && class_exists($handler)) { |
88 | | - $class = service('container')->make($handler); |
89 | | - if (is_callable($class) || $class instanceof HandlerInterface) { |
90 | | - $debugger->pushHandler($class); |
91 | | - } |
92 | | - } |
93 | | - } |
94 | 133 |
|
95 | | - return $debugger; |
| 134 | + return $this; |
96 | 135 | } |
97 | 136 |
|
98 | 137 | /** |
99 | | - * Gestionnaire d'erreurs globales whoops |
| 138 | + * Enregistre un gestionnaire de Whoops à des fins de débogage. |
| 139 | + * |
| 140 | + * Cette méthode met en place différents gestionnaires en fonction de l'environnement et des paramètres de configuration. |
| 141 | + * Elle vérifie la ligne de commande, l'état en ligne, les requêtes AJAX et les requêtes JSON. |
| 142 | + * En fonction des conditions, elle utilise PlainTextHandler, JsonResponseHandler ou PrettyPageHandler. |
| 143 | + * |
| 144 | + * Le PrettyPageHandler est configuré avec les paramètres de l'éditeur, le titre de la page, les chemins d'accès à l'application, |
| 145 | + * les données sur liste noire et les tables de données. Il gère également différents types de données pour les tables de données. |
100 | 146 | */ |
101 | | - public static function registerWhoopsHandler(Run $debugger, array $config): Run |
| 147 | + private function registerWhoopsHandler(): self |
102 | 148 | { |
103 | 149 | if (Misc::isCommandLine()) { |
104 | | - $debugger->pushHandler(new PlainTextHandler()); |
105 | | - } |
106 | | - |
107 | | - if (! is_online()) { |
108 | | - if (Misc::isAjaxRequest() || service('request')->isJson()) { |
109 | | - $debugger->pushHandler(new JsonResponseHandler()); |
110 | | - } else { |
111 | | - $handler = new PrettyPageHandler(); |
| 150 | + $this->debugger->pushHandler(new PlainTextHandler(service('logger'))); |
112 | 151 |
|
113 | | - $handler->setEditor($config['editor'] ?: PrettyPageHandler::EDITOR_VSCODE); |
114 | | - $handler->setPageTitle($config['title'] ?: $handler->getPageTitle()); |
115 | | - $handler->setApplicationRootPath(APP_PATH); |
116 | | - $handler->setApplicationPaths([APP_PATH, SYST_PATH, VENDOR_PATH]); |
| 152 | + return $this; |
| 153 | + } |
117 | 154 |
|
118 | | - $handler = self::setBlacklist($handler, $config['blacklist']); |
| 155 | + if (is_online()) { |
| 156 | + return $this; |
| 157 | + } |
119 | 158 |
|
120 | | - foreach ($config['data'] as $label => $data) { |
121 | | - if (is_array($data)) { |
122 | | - $handler->addDataTable($label, $data); |
123 | | - } elseif (is_callable($data)) { |
124 | | - $handler->addDataTableCallback($label, $data); |
125 | | - } |
126 | | - } |
| 159 | + if (Misc::isAjaxRequest() || service('request')->isJson()) { |
| 160 | + $this->debugger->pushHandler(new JsonResponseHandler()); |
127 | 161 |
|
128 | | - $debugger->pushHandler($handler); |
129 | | - } |
| 162 | + return $this; |
130 | 163 | } |
131 | 164 |
|
132 | | - return $debugger; |
133 | | - } |
| 165 | + $handler = new PrettyPageHandler(); |
134 | 166 |
|
135 | | - /** |
136 | | - * Enregistre les elements blacklisté dans l'affichage du rapport d'erreur |
137 | | - */ |
138 | | - private static function setBlacklist(PrettyPageHandler $handler, array $blacklists): PrettyPageHandler |
139 | | - { |
140 | | - foreach ($blacklists as $blacklist) { |
| 167 | + $handler->handleUnconditionally(true); |
| 168 | + $handler->setEditor($this->config->editor ?: PrettyPageHandler::EDITOR_VSCODE); |
| 169 | + $handler->setPageTitle($this->config->title ?: $handler->getPageTitle()); |
| 170 | + $handler->setApplicationPaths($this->getApplicationPaths()); |
| 171 | + |
| 172 | + foreach ($this->config->blacklist as $blacklist) { |
141 | 173 | [$name, $key] = explode('/', $blacklist) + [1 => '*']; |
142 | 174 |
|
143 | 175 | if ($name[0] !== '_') { |
@@ -168,18 +200,40 @@ private static function setBlacklist(PrettyPageHandler $handler, array $blacklis |
168 | 200 | } |
169 | 201 | } |
170 | 202 |
|
171 | | - return $handler; |
| 203 | + foreach ($this->config->data as $label => $data) { |
| 204 | + if (is_array($data)) { |
| 205 | + $handler->addDataTable($label, $data); |
| 206 | + } elseif (is_callable($data)) { |
| 207 | + $handler->addDataTableCallback($label, $data); |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + $this->debugger->pushHandler($handler); |
| 212 | + |
| 213 | + return $this; |
172 | 214 | } |
173 | 215 |
|
174 | 216 | /** |
175 | | - * Prepare exception for rendering. |
| 217 | + * Préparer l'exception pour le rendu. |
176 | 218 | */ |
177 | 219 | private static function prepareException(Throwable $e): Throwable |
178 | 220 | { |
179 | 221 | if ($e instanceof TokenMismatchException) { |
180 | | - $e = new HttpException($e->getMessage(), 419, $e); |
| 222 | + return new HttpException($e->getMessage(), 419, $e); |
181 | 223 | } |
182 | 224 |
|
183 | 225 | return $e; |
184 | 226 | } |
| 227 | + |
| 228 | + /** |
| 229 | + * Récupère les chemins d'accès à l'application. |
| 230 | + */ |
| 231 | + private function getApplicationPaths(): array |
| 232 | + { |
| 233 | + return collect(service('fs')->directories(base_path())) |
| 234 | + ->flip() |
| 235 | + ->except(base_path('vendor')) |
| 236 | + ->flip() |
| 237 | + ->all(); |
| 238 | + } |
185 | 239 | } |
0 commit comments