• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 12673986434

08 Jan 2025 03:42PM UTC coverage: 84.455% (+0.001%) from 84.454%
12673986434

Pull #9385

github

web-flow
Merge 06e47f0ee into e475fd8fa
Pull Request #9385: refactor: Fix phpstan expr.resultUnused

20699 of 24509 relevant lines covered (84.45%)

190.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.33
/system/Debug/ExceptionHandler.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\Debug;
15

16
use CodeIgniter\API\ResponseTrait;
17
use CodeIgniter\Exceptions\PageNotFoundException;
18
use CodeIgniter\HTTP\Exceptions\HTTPException;
19
use CodeIgniter\HTTP\IncomingRequest;
20
use CodeIgniter\HTTP\RequestInterface;
21
use CodeIgniter\HTTP\ResponseInterface;
22
use Config\Paths;
23
use Throwable;
24

25
/**
26
 * @see \CodeIgniter\Debug\ExceptionHandlerTest
27
 */
28
final class ExceptionHandler extends BaseExceptionHandler implements ExceptionHandlerInterface
29
{
30
    use ResponseTrait;
31

32
    /**
33
     * ResponseTrait needs this.
34
     */
35
    private ?RequestInterface $request = null;
36

37
    /**
38
     * ResponseTrait needs this.
39
     */
40
    private ?ResponseInterface $response = null;
41

42
    /**
43
     * Determines the correct way to display the error.
44
     */
45
    public function handle(
46
        Throwable $exception,
47
        RequestInterface $request,
48
        ResponseInterface $response,
49
        int $statusCode,
50
        int $exitCode
51
    ): void {
52
        // ResponseTrait needs these properties.
53
        $this->request  = $request;
3✔
54
        $this->response = $response;
3✔
55

56
        if ($request instanceof IncomingRequest) {
3✔
57
            try {
58
                $response->setStatusCode($statusCode);
2✔
59
            } catch (HTTPException) {
×
60
                // Workaround for invalid HTTP status code.
61
                $statusCode = 500;
×
62
                $response->setStatusCode($statusCode);
×
63
            }
64

65
            if (! headers_sent()) {
2✔
66
                header(
2✔
67
                    sprintf(
2✔
68
                        'HTTP/%s %s %s',
2✔
69
                        $request->getProtocolVersion(),
2✔
70
                        $response->getStatusCode(),
2✔
71
                        $response->getReasonPhrase()
2✔
72
                    ),
2✔
73
                    true,
2✔
74
                    $statusCode
2✔
75
                );
2✔
76
            }
77

78
            // Handles non-HTML requests.
79
            if (! str_contains($request->getHeaderLine('accept'), 'text/html')) {
2✔
80
                // If display_errors is enabled, shows the error details.
81
                $data = $this->isDisplayErrorsEnabled()
1✔
82
                    ? $this->collectVars($exception, $statusCode)
1✔
83
                    : '';
×
84

85
                $this->respond($data, $statusCode)->send();
1✔
86

87
                if (ENVIRONMENT !== 'testing') {
1✔
88
                    // @codeCoverageIgnoreStart
89
                    exit($exitCode);
×
90
                    // @codeCoverageIgnoreEnd
91
                }
92

93
                return;
1✔
94
            }
95
        }
96

97
        // Determine possible directories of error views
98
        $addPath = ($request instanceof IncomingRequest ? 'html' : 'cli') . DIRECTORY_SEPARATOR;
2✔
99
        $path    = $this->viewPath . $addPath;
2✔
100
        $altPath = rtrim((new Paths())->viewDirectory, '\\/ ')
2✔
101
            . DIRECTORY_SEPARATOR . 'errors' . DIRECTORY_SEPARATOR . $addPath;
2✔
102

103
        // Determine the views
104
        $view    = $this->determineView($exception, $path, $statusCode);
2✔
105
        $altView = $this->determineView($exception, $altPath, $statusCode);
2✔
106

107
        // Check if the view exists
108
        $viewFile = null;
2✔
109
        if (is_file($path . $view)) {
2✔
110
            $viewFile = $path . $view;
2✔
111
        } elseif (is_file($altPath . $altView)) {
×
112
            $viewFile = $altPath . $altView;
×
113
        }
114

115
        // Displays the HTML or CLI error code.
116
        $this->render($exception, $statusCode, $viewFile);
2✔
117

118
        if (ENVIRONMENT !== 'testing') {
2✔
119
            // @codeCoverageIgnoreStart
120
            exit($exitCode);
×
121
            // @codeCoverageIgnoreEnd
122
        }
123
    }
124

125
    /**
126
     * Determines the view to display based on the exception thrown, HTTP status
127
     * code, whether an HTTP or CLI request, etc.
128
     *
129
     * @return string The filename of the view file to use
130
     */
131
    protected function determineView(
132
        Throwable $exception,
133
        string $templatePath,
134
        int $statusCode = 500
135
    ): string {
136
        // Production environments should have a custom exception file.
137
        $view = 'production.php';
6✔
138

139
        if ($this->isDisplayErrorsEnabled()) {
6✔
140
            // If display_errors is enabled, shows the error details.
141
            $view = 'error_exception.php';
5✔
142
        }
143

144
        // 404 Errors
145
        if ($exception instanceof PageNotFoundException) {
6✔
146
            return 'error_404.php';
3✔
147
        }
148

149
        $templatePath = rtrim($templatePath, '\\/ ') . DIRECTORY_SEPARATOR;
3✔
150

151
        // Allow for custom views based upon the status code
152
        if (is_file($templatePath . 'error_' . $statusCode . '.php')) {
3✔
153
            return 'error_' . $statusCode . '.php';
×
154
        }
155

156
        return $view;
3✔
157
    }
158

159
    private function isDisplayErrorsEnabled(): bool
160
    {
161
        return in_array(
7✔
162
            strtolower(ini_get('display_errors')),
7✔
163
            ['1', 'true', 'on', 'yes'],
7✔
164
            true
7✔
165
        );
7✔
166
    }
167
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc