1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * This file is part of Blitz PHP framework. |
||||
5 | * |
||||
6 | * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]> |
||||
7 | * |
||||
8 | * For the full copyright and license information, please view |
||||
9 | * the LICENSE file that was distributed with this source code. |
||||
10 | */ |
||||
11 | |||||
12 | namespace BlitzPHP\Debug; |
||||
13 | |||||
14 | use BlitzPHP\Exceptions\HttpException; |
||||
15 | use BlitzPHP\Exceptions\TokenMismatchException; |
||||
16 | use BlitzPHP\View\View; |
||||
17 | use Symfony\Component\Finder\SplFileInfo; |
||||
18 | use Throwable; |
||||
19 | use Whoops\Handler\Handler; |
||||
20 | use Whoops\Handler\HandlerInterface; |
||||
21 | use Whoops\Handler\JsonResponseHandler; |
||||
22 | use Whoops\Handler\PlainTextHandler; |
||||
23 | use Whoops\Handler\PrettyPageHandler; |
||||
24 | use Whoops\Inspector\InspectorInterface; |
||||
25 | use Whoops\Run; |
||||
26 | use Whoops\RunInterface; |
||||
27 | use Whoops\Util\Misc; |
||||
28 | |||||
29 | /** |
||||
30 | * Capture et affiche les erreurs et exceptions via whoops |
||||
31 | * |
||||
32 | * Necessite l'instalation de `flip/whoops` |
||||
33 | */ |
||||
34 | class ExceptionManager |
||||
35 | { |
||||
36 | /** |
||||
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 |
||||
56 | */ |
||||
57 | public function register(): void |
||||
58 | { |
||||
59 | if (! $this->debugger) { |
||||
60 | return; |
||||
61 | } |
||||
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); |
||||
0 ignored issues
–
show
|
|||||
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); |
||||
103 | $exception_code = $exception->getCode(); |
||||
104 | |||||
105 | if ($exception_code >= 400 && $exception_code < 600) { |
||||
106 | $run->sendHttpCode($exception_code); |
||||
107 | } |
||||
108 | |||||
109 | if (true === $this->config->log && ! in_array($exception_code, $this->config->ignore_codes, true)) { |
||||
110 | service('logger')->error($exception); |
||||
111 | } |
||||
112 | |||||
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)); |
||||
115 | } else { |
||||
116 | $files = []; |
||||
117 | } |
||||
118 | |||||
119 | $files = collect($files)->flip()->only($exception_code, is_online() ? 'production' : '')->flip()->all(); |
||||
120 | |||||
121 | if ($files !== []) { |
||||
122 | $view = new View(); |
||||
123 | |||||
124 | $view->setAdapter(config('view.active_adapter', 'native'), ['view_path' => $this->config->error_view_path]) |
||||
0 ignored issues
–
show
It seems like
config('view.active_adapter', 'native') can also be of type null and object ; however, parameter $adapter of BlitzPHP\View\View::setAdapter() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
125 | ->first($files, ['message' => $exception->getMessage()]) |
||||
126 | ->render(); |
||||
127 | |||||
128 | return Handler::QUIT; |
||||
129 | } |
||||
130 | |||||
131 | return Handler::DONE; |
||||
132 | }); |
||||
133 | |||||
134 | return $this; |
||||
135 | } |
||||
136 | |||||
137 | /** |
||||
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. |
||||
146 | */ |
||||
147 | private function registerWhoopsHandler(): self |
||||
148 | { |
||||
149 | if (Misc::isCommandLine()) { |
||||
150 | $this->debugger->pushHandler(new PlainTextHandler(service('logger'))); |
||||
151 | |||||
152 | return $this; |
||||
153 | } |
||||
154 | |||||
155 | if (is_online()) { |
||||
156 | return $this; |
||||
157 | } |
||||
158 | |||||
159 | if (Misc::isAjaxRequest() || service('request')->isJson()) { |
||||
160 | $this->debugger->pushHandler(new JsonResponseHandler()); |
||||
161 | |||||
162 | return $this; |
||||
163 | } |
||||
164 | |||||
165 | $handler = new PrettyPageHandler(); |
||||
166 | |||||
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) { |
||||
173 | [$name, $key] = explode('/', $blacklist) + [1 => '*']; |
||||
174 | |||||
175 | if ($name[0] !== '_') { |
||||
176 | $name = '_' . $name; |
||||
177 | } |
||||
178 | |||||
179 | $name = strtoupper($name); |
||||
180 | |||||
181 | if ($key !== '*') { |
||||
182 | foreach (explode(',', $key) as $k) { |
||||
183 | $handler->blacklist($name, $k); |
||||
184 | } |
||||
185 | } else { |
||||
186 | $values = match ($name) { |
||||
187 | '_GET' => $_GET, |
||||
188 | '_POST' => $_POST, |
||||
189 | '_COOKIE' => $_COOKIE, |
||||
190 | '_SERVER' => $_SERVER, |
||||
191 | '_ENV' => $_ENV, |
||||
192 | '_FILES' => $_FILES ?: [], |
||||
193 | '_SESSION' => $_SESSION ?? [], |
||||
194 | default => [], |
||||
195 | }; |
||||
196 | |||||
197 | foreach (array_keys($values) as $key) { |
||||
198 | $handler->blacklist($name, $key); |
||||
199 | } |
||||
200 | } |
||||
201 | } |
||||
202 | |||||
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; |
||||
214 | } |
||||
215 | |||||
216 | /** |
||||
217 | * Préparer l'exception pour le rendu. |
||||
218 | */ |
||||
219 | private static function prepareException(Throwable $e): Throwable |
||||
220 | { |
||||
221 | if ($e instanceof TokenMismatchException) { |
||||
222 | return new HttpException($e->getMessage(), 419, $e); |
||||
223 | } |
||||
224 | |||||
225 | return $e; |
||||
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 | } |
||||
239 | } |
||||
240 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.