Test Failed
Push — develop ( 66c2b7...c7403e )
by nguereza
02:57
created

ErrorHandlerMiddleware   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 162
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 45
dl 0
loc 162
rs 10
c 2
b 0
f 0
wmc 15

7 Methods

Rating   Name   Duplication   Size   Complexity  
A addErrorHandler() 0 12 2
A handleException() 0 13 2
A getExceptionErrorHandler() 0 17 5
A process() 0 28 3
A setErrorHandler() 0 5 1
A __construct() 0 5 1
A getDefaultErrorHandler() 0 3 1
1
<?php
2
3
/**
4
 * Platine Framework
5
 *
6
 * Platine Framework is a lightweight, high-performance, simple and elegant
7
 * PHP Web framework
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine Framework
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file ErrorHandlerMiddleware.php
34
 *
35
 *  The error handler middleware
36
 *
37
 *  @package    Platine\Framework\Http\Middleware
38
 *  @author Platine Developers team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   http://www.iacademy.cf
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Framework\Http\Middleware;
49
50
use ErrorException;
51
use Platine\Framework\Handler\Error\ErrorHandler;
52
use Platine\Framework\Handler\Error\ErrorHandlerInterface;
53
use Platine\Framework\Http\Exception\HttpException;
54
use Platine\Http\Handler\MiddlewareInterface;
55
use Platine\Http\Handler\RequestHandlerInterface;
56
use Platine\Http\ResponseInterface;
57
use Platine\Http\ServerRequestInterface;
58
use Platine\Logger\LoggerInterface;
59
use Throwable;
60
61
62
/**
63
 * @class ErrorHandlerMiddleware
64
 * @package Platine\Framework\Http\Middleware
65
 */
66
class ErrorHandlerMiddleware implements MiddlewareInterface
67
{
68
    /**
69
     * The logger instance to use to save error
70
     * @var LoggerInterface
71
     */
72
    protected LoggerInterface $logger;
73
74
    /**
75
     * The default error handler to use
76
     * @var ErrorHandlerInterface
77
     */
78
    protected ErrorHandlerInterface $errorHandler;
79
80
    /**
81
     * Whether to show error details
82
     * @var bool
83
     */
84
    protected bool $detail = true;
85
86
    /**
87
     * List of error handler
88
     * @var array<string, ErrorHandlerInterface>
89
     */
90
    protected array $handlers = [];
91
92
    /**
93
     * List of sub classed error handlers
94
     * @var array<string, ErrorHandlerInterface>
95
     */
96
    protected array $subClassHandlers = [];
97
98
    /**
99
     * Create new instance
100
     * @param bool $detail
101
     * @param LoggerInterface $logger
102
     */
103
    public function __construct(bool $detail, LoggerInterface $logger)
104
    {
105
        $this->detail = $detail;
106
        $this->logger = $logger;
107
        $this->errorHandler = $this->getDefaultErrorHandler();
108
    }
109
110
    /**
111
     * Set error handler
112
     * @param ErrorHandlerInterface $errorHandler
113
     * @return $this
114
     */
115
    public function setErrorHandler(ErrorHandlerInterface $errorHandler): self
116
    {
117
        $this->errorHandler = $errorHandler;
118
119
        return $this;
120
    }
121
122
    /**
123
     * Add error handler for given type
124
     * @param string $type
125
     * @param ErrorHandlerInterface $handler
126
     * @param bool $useSubClasses
127
     * @return $this
128
     */
129
    public function addErrorHandler(
130
        string $type,
131
        ErrorHandlerInterface $handler,
132
        bool $useSubClasses = false
133
    ): self {
134
        if ($useSubClasses) {
135
            $this->subClassHandlers[$type] = $handler;
136
        } else {
137
            $this->handlers[$type] = $handler;
138
        }
139
140
        return $this;
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146
    public function process(
147
        ServerRequestInterface $request,
148
        RequestHandlerInterface $handler
149
    ): ResponseInterface {
150
        set_error_handler(static function (
151
            int $severity,
152
            string $message,
153
            string $file,
154
            int $line
155
        ): bool {
156
            // https://www.php.net/manual/en/function.error-reporting.php#8866
157
            // Usages the defined levels of `error_reporting()`.
158
            if (!(error_reporting() & $severity)) {
159
                // This error code is not included in `error_reporting()`.
160
                return true;
161
            }
162
163
            throw new ErrorException($message, 0, $severity, $file, $line);
164
        });
165
166
        try {
167
            $response = $handler->handle($request);
168
        } catch (Throwable $exception) {
169
            $response = $this->handleException($request, $exception);
170
        }
171
172
        restore_error_handler();
173
        return $response;
174
    }
175
176
    /**
177
     * Handle exception
178
     * @param ServerRequestInterface $request
179
     * @param Throwable $exception
180
     * @return ResponseInterface
181
     */
182
    protected function handleException(
183
        ServerRequestInterface $request,
184
        Throwable $exception
185
    ): ResponseInterface {
186
        if ($exception instanceof HttpException) {
187
            $request = $exception->getRequest();
188
        }
189
190
        $type = get_class($exception);
191
192
        $handler = $this->getExceptionErrorHandler($type);
193
194
        return $handler->handle($request, $exception, $this->detail);
195
    }
196
197
    /**
198
     * Get the error handler for the given type
199
     * @param string $type
200
     * @return ErrorHandlerInterface
201
     */
202
    protected function getExceptionErrorHandler(string $type): ErrorHandlerInterface
203
    {
204
        if (isset($this->handlers[$type])) {
205
            return $this->handlers[$type];
206
        }
207
208
        if (isset($this->subClassHandlers[$type])) {
209
            return $this->subClassHandlers[$type];
210
        }
211
212
        foreach ($this->subClassHandlers as $class => $handler) {
213
            if (is_subclass_of($type, $class)) {
214
                return $handler;
215
            }
216
        }
217
218
        return $this->errorHandler;
219
    }
220
221
    /**
222
     * Return the default error handler if none is set before
223
     * @return ErrorHandlerInterface
224
     */
225
    protected function getDefaultErrorHandler(): ErrorHandlerInterface
226
    {
227
        return new ErrorHandler($this->logger);
228
    }
229
}
230