blitz-php /
framework
| 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
Loading history...
|
|||||
| 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.