Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
AppErrorHandler
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 2
132
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 catchError
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 1
110
1<?php
2
3declare(strict_types=1);
4
5namespace PeServer\App\Models;
6
7use PeServer\Core\DI\Inject;
8use PeServer\Core\Environment;
9use PeServer\Core\Errors\ErrorHandler;
10use PeServer\Core\Errors\HttpErrorHandler;
11use PeServer\Core\Http\ContentType;
12use PeServer\Core\Http\HttpRequest;
13use PeServer\Core\Http\HttpResponse;
14use PeServer\Core\Http\IResponsePrinterFactory;
15use PeServer\Core\Http\RequestPath;
16use PeServer\Core\IO\Directory;
17use PeServer\Core\IO\Path;
18use PeServer\Core\Log\ILogger;
19use PeServer\Core\Log\Logging;
20use PeServer\Core\Log\NullLogger;
21use PeServer\Core\Mime;
22use PeServer\Core\Mvc\Template\ITemplateFactory;
23use PeServer\Core\Mvc\Template\TemplateFactory;
24use PeServer\Core\Mvc\Template\TemplateOptions;
25use PeServer\Core\Mvc\Template\TemplateParameter;
26use PeServer\Core\ProgramContext;
27use PeServer\Core\Serialization\JsonSerializer;
28use PeServer\Core\Text;
29use PeServer\Core\Web\IUrlHelper;
30use PeServer\Core\Web\WebSecurity;
31use Throwable;
32
33final class AppErrorHandler extends HttpErrorHandler
34{
35    #region variable
36
37    private RequestPath $requestPath;
38    private JsonSerializer $jsonSerializer;
39    private ITemplateFactory $templateFactory;
40
41    #endregion
42
43    public function __construct(
44        RequestPath $requestPath,
45        TemplateFactory $templateFactory,
46        private IResponsePrinterFactory $responsePrinterFactory,
47        private ProgramContext $programContext,
48        private IUrlHelper $urlHelper,
49        private WebSecurity $webSecurity,
50        ?JsonSerializer $jsonSerializer,
51        private Environment $environment,
52        ILogger $logger
53    ) {
54        parent::__construct($logger);
55
56        $this->requestPath = $requestPath;
57        $this->templateFactory = $templateFactory;
58        $this->jsonSerializer = $jsonSerializer ?? new JsonSerializer();
59    }
60
61    #region ErrorHandler
62
63    protected function catchError(int $errorNumber, string $message, string $file, int $lineNumber, ?Throwable $throwable): void
64    {
65        $next = true;
66
67        $isProduction = $this->environment->isProduction();
68
69        if ($isProduction) {
70            $next = false;
71        }
72
73        $isJson = Text::startsWith($this->requestPath->full, 'api/', true) || Text::startsWith($this->requestPath->full, 'ajax/', true);
74        if ($isJson) {
75            $next = false;
76        }
77
78        // デバッグ環境で本番用エラー検証用
79        //$next = false;
80
81        if (!$next) {
82            $response = new HttpResponse();
83            $response->status = $this->getHttpStatus($throwable);
84
85            $values = [
86                'error_number' => $errorNumber,
87                'message' => $message,
88                'file' => $file,
89                'line_number' => $lineNumber,
90                'throwable' => $throwable,
91            ];
92
93            $isSuppressionStatus = false;
94            foreach ($this->getSuppressionStatusList() as $suppressionStatus) {
95                if ($response->status === $suppressionStatus) {
96                    $isSuppressionStatus = true;
97                    $this->logger->info('HTTP {0}: {1}', $suppressionStatus->value, $suppressionStatus->name);
98                    break;
99                }
100            }
101            if (!$isSuppressionStatus) {
102                $this->logger->error($values);
103            }
104
105            if ($isJson) {
106                unset($values['error_number']);
107                unset($values['message']);
108                if ($isProduction) {
109                    unset($values['throwable']);
110                }
111
112                $responseJson = ResponseJson::error(
113                    $message,
114                    strval($errorNumber),
115                    $values
116                );
117
118                $json = [
119                    'data' => $responseJson->data,
120                    'error' => $responseJson->error,
121                ];
122
123                $response->header->addValue(ContentType::NAME, Mime::JSON);
124                $response->content = $this->jsonSerializer->save($json);
125            } else {
126                $rootDir = Path::combine($this->programContext->applicationDirectory, 'App', 'Views');
127                $baseDir = Path::combine('template', 'page');
128
129                $options = new TemplateOptions(
130                    $rootDir,
131                    $baseDir,
132                    $this->programContext,
133                    $this->urlHelper,
134                    $this->webSecurity,
135                    Path::combine(Directory::getTemporaryDirectory(), 'PeServer-App')
136                );
137                $template = $this->templateFactory->createTemplate($options);
138                $response->content =  $template->build('error.tpl', new TemplateParameter($response->status, $values, []));
139            }
140
141            $printer = $this->responsePrinterFactory->createResponsePrinter(HttpRequest::none(), $response);
142            $printer->execute();
143            return;
144        }
145
146        parent::catchError($errorNumber, $message, $file, $lineNumber, $throwable);
147    }
148
149    #endregion
150}