Test Failed
Push — master ( 73c405...d57cd2 )
by Konstantins
03:14
created

ErrorHandler::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
crap 1
1
<?php declare(strict_types = 1);
2
3
namespace Venta\Debug;
4
5
use ErrorException;
6
use Throwable;
7
use Venta\Contracts\Debug\ErrorHandler as ErrorHandlerContract;
8
use Venta\Contracts\Debug\ErrorRenderer;
9
use Venta\Contracts\Debug\ErrorReporterStack;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Venta\Debug\ErrorReporterStack.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
10
11
/**
12
 * Class ErrorHandler
13
 *
14
 * @package Venta\Debug
15
 */
16
final class ErrorHandler implements ErrorHandlerContract
17
{
18
    /**
19
     * Controls whether error handler can throw exceptions.
20
     * E.g. shutdown handler cannot produce new exceptions.
21
     *
22
     * @var bool
23
     */
24
    private $canThrowException = true;
25
26
    /**
27
     * The error renderer.
28
     *
29
     * @var ErrorRenderer
30
     */
31
    private $renderer;
32
33
    /**
34
     * The error reporter pipeline.
35
     *
36
     * @var ErrorReporterStack
37
     */
38
    private $reporters;
39
40
    /**
41
     * ErrorHandler constructor.
42
     *
43
     * @param ErrorRenderer $renderer
44
     * @param ErrorReporterStack $reporters
45
     */
46 1
    public function __construct(ErrorRenderer $renderer, ErrorReporterStack $reporters)
47
    {
48 1
        $this->renderer = $renderer;
49 1
        $this->reporters = $reporters;
50 1
    }
51
52
    /**
53
     * @inheritdoc
54
     * @throws ErrorException
55
     */
56
    public function handleError(int $severity, string $message, string $filename, int $line): bool
57
    {
58
        if (!$this->isSevereEnough($severity)) {
59
            // Pass error to the next handler.
60
            return false;
61
        }
62
63
        $exception = new ErrorException($message, 0, $severity, $filename, $line);
64
        if ($this->canThrowException) {
65
            throw $exception;
66
        }
67
68
        $this->handleThrowable($exception);
69
70
        return true;
71
    }
72
73
    /**
74
     * {@inheritDoc}
75
     */
76
    public function handleShutdown()
77
    {
78
        $this->canThrowException = false;
79
        $error = error_get_last();
80
        if (!empty($error) && ($error['type'] & self::FATAL)) {
81
            // Fatal error is not passed to handleError, we need to pass it manually.
82
            $this->handleError($error['type'], $error['message'], $error['file'], $error['line']);
83
        }
84
    }
85
86
    /**
87
     * {@inheritDoc}
88
     */
89
    public function handleThrowable(Throwable $e)
90
    {
91
        foreach ($this->reporters as $reporter) {
92
            $reporter->report($e);
93
        }
94
95
        $this->renderer->render($e);
96
    }
97
98
    /**
99
     * Checks if the error is severe enough and must be handled.
100
     *
101
     * @param int $severity
102
     * @return bool
103
     */
104
    private function isSevereEnough(int $severity): bool
105
    {
106
        return boolval(error_reporting() & $severity);
107
    }
108
109
}