Completed
Push — master ( 0e5dfa...8fd6d1 )
by Ron
03:28
created

CoreErrorHandlers::printException()   C

Complexity

Conditions 12
Paths 4

Size

Total Lines 47
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 47
rs 5.1384
cc 12
eloc 33
nc 4
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace Logger;
3
4
use ErrorException;
5
use Exception;
6
use Logger\Filters\LogLevelRangeFilter;
7
use Logger\Loggers\LoggerCollection;
8
use Psr\Log\LoggerInterface;
9
use Psr\Log\LogLevel;
10
11
class CoreErrorHandlers {
12
	/**
13
	 * @var array
14
	 */
15
	private static $phpErrorLevels = array(
16
		E_NOTICE => LogLevel::NOTICE,
17
		E_DEPRECATED => LogLevel::NOTICE,
18
		E_USER_DEPRECATED => LogLevel::NOTICE,
19
		E_WARNING => LogLevel::WARNING,
20
		E_STRICT => LogLevel::WARNING,
21
		E_USER_WARNING => LogLevel::WARNING,
22
		E_CORE_WARNING => LogLevel::WARNING,
23
		E_ERROR => LogLevel::ERROR,
24
		E_USER_ERROR => LogLevel::ERROR,
25
	);
26
27
	/**
28
	 * @param int|null $bitmask
29
	 */
30
	public static function enableExceptionsForErrors($bitmask = null) {
31
		set_error_handler(function ($level, $message, $file, $line) use ($bitmask) {
32
			// PHP-7 fix: What once was an E_STRICT is now an E_WARNING:
33
			if(preg_match('/^Declaration of .*? should be compatible with/', $message)) {
34
				$level = E_STRICT;
35
			}
36
			if (0 === error_reporting()) {
37
				return false;
38
			}
39
			if($bitmask & $level) {
40
				throw new ErrorException($message, 0, $level, $file, $line);
41
			}
42
		});
43
	}
44
45
	/**
46
	 * @param LoggerInterface $logger
47
	 * @param string $logLevel PSR-3 Log-Level
48
	 */
49
	public static function registerAssertionHandler(LoggerInterface $logger, $logLevel) {
50
		static $errorLogger = null;
51
		if($errorLogger === null) {
52
			$errorLogger = new LoggerCollection();
53
			assert_options(ASSERT_ACTIVE, true);
54
			assert_options(ASSERT_WARNING, false);
55
			assert_options(ASSERT_CALLBACK, function ($file, $line, $message) use ($errorLogger, $logLevel) {
56
				$errorLogger->log($logLevel, $message, array(
57
					'file' => $file,
58
					'line' => $line
59
				));
60
			});
61
		}
62
		$errorLogger->add($logger);
63
	}
64
65
	/**
66
	 * @param LoggerInterface $logger
67
	 */
68
	public static function registerFatalErrorHandler(LoggerInterface $logger) {
69
		static $errorLogger = null;
70
		if($errorLogger === null) {
71
			$errorLogger = new LoggerCollection();
72
			$errorLevels = self::$phpErrorLevels;
73
			register_shutdown_function(function () use ($errorLogger, $errorLevels) {
74
				$error = error_get_last();
75
				if($error['type'] === E_ERROR) {
76
					$errorLogger = new LogLevelRangeFilter($errorLogger, LogLevel::ERROR);
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $errorLogger, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
77
					$errorLogger->log(LogLevel::ALERT, $error['message'], $error);
78
				}
79
			});
80
		}
81
		$errorLogger->add($logger);
82
	}
83
84
	/**
85
	 * @param LoggerInterface $logger
86
	 */
87
	public static function registerExceptionHandler(LoggerInterface $logger) {
88
		static $errorLogger = null;
89
		if($errorLogger === null) {
90
			$errorLogger = new LoggerCollection();
91
			set_exception_handler(function ($exception) use ($errorLogger) {
92
				/** @var \Exception|\Throwable $exception */
93
				$errorLogger = new LogLevelRangeFilter($errorLogger, LogLevel::ERROR);
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $errorLogger, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
94
				$errorLogger->log(LogLevel::CRITICAL, $exception->getMessage(), array('exception' => $exception));
95
				if($exception instanceof Exception) {
96
					self::printException($exception, "PHP Fatal Error: Uncaught exception: ");
97
					die(1);
1 ignored issue
show
Coding Style Compatibility introduced by
The method registerExceptionHandler() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
98
				}
99
			});
100
		}
101
		$errorLogger->add($logger);
102
	}
103
104
	/**
105
	 * @param Exception $e
106
	 * @param string $messageIntro
107
	 */
108
	private static function printException(Exception $e, $messageIntro = '') {
109
		$p = function ($format = "\n") {
110
			$fp = fopen('php://stderr', 'w');
111
			fwrite($fp, vsprintf($format, array_slice(func_get_args(), 1)));
112
			fclose($fp);
113
		};
114
115
		$p("%s%s\n", $messageIntro, $e->getMessage());
116
117
		$formatStation = function ($idx, $station) use ($p) {
118
			$defaults = [
119
				'file' => null,
120
				'line' => null,
121
				'class' => null,
122
				'function' => null,
123
				'type' => null,
124
				'args' => [],
125
			];
126
			$station = array_merge($defaults, $station);
127
			$p("#%- 3s%s:%d\n", $idx, $station['file'] ?: 'unknown', $station['line']);
128
			if($station['class'] !== null || $station['function'] !== null) {
129
				$params = [];
130
				foreach(is_array($station['args']) ? $station['args'] : [] as $argument) {
131
					if(is_array($argument)) {
132
						$params[] = sprintf('array%d', count($argument));
133
					} elseif(is_object($argument)) {
134
						$params[] = sprintf('%s', get_class($argument));
135
					} else {
136
						$params[] = gettype($argument);
137
					}
138
				}
139
				if(strpos($station['function'], '{closure}') !== false && $station['class'] !== null) {
140
					$station['function'] = '{closure}';
141
				}
142
				$p("    %s%s%s%s%s%s\n", $station['class'], $station['type'], $station['function'], "(", join(', ', $params), ")");
143
			}
144
		};
145
146
		foreach($e->getTrace() as $idx => $station) {
147
			$formatStation($idx, $station);
148
		}
149
150
		if($e->getPrevious() instanceof Exception) {
151
			$p();
152
			self::printException($e->getPrevious(), 'Previous: ');
153
		}
154
	}
155
}
156