diff --git a/.gitignore b/.gitignore index 13aa3d70..580845e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.idea /assets/*.css /assets/*.map /composer.lock diff --git a/collectors/php_errors.php b/collectors/php_errors.php index 80631f04..81ebc7fd 100644 --- a/collectors/php_errors.php +++ b/collectors/php_errors.php @@ -83,7 +83,8 @@ public function set_up() { $prior_error = error_get_last(); // Non-fatal error handler: - $this->previous_error_handler = set_error_handler( array( $this, 'error_handler' ), ( E_ALL ^ QM_ERROR_FATALS ) ); + // To support error handler chaining, we need to set our error handler with E_ALL error_levels. + $this->previous_error_handler = set_error_handler( array( $this, 'error_handler' ) ); // Fatal error and uncaught exception handler: $this->previous_exception_handler = set_exception_handler( array( $this, 'exception_handler' ) ); @@ -176,6 +177,12 @@ public function exception_handler( $e ) { * @return bool */ public function error_handler( $errno, $message, $file = null, $line = null, $context = null, $do_trace = true ) { + if ( $errno & QM_ERROR_FATALS ) { + // Do not proceed with fatal errors. + // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection + return $this->fallback_error_handler( func_get_args() ); + } + $type = null; /** @@ -216,11 +223,13 @@ public function error_handler( $errno, $message, $file = null, $line = null, $co } if ( null === $type ) { - return false; + // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection + return $this->fallback_error_handler( func_get_args() ); } if ( ! class_exists( 'QM_Backtrace' ) ) { - return false; + // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection + return $this->fallback_error_handler( func_get_args() ); } $error_group = 'errors'; @@ -245,7 +254,14 @@ public function error_handler( $errno, $message, $file = null, $line = null, $co // Intentionally skip reporting these core warnings. They're a distraction when developing offline. // The failed HTTP request will still appear in QM's output so it's not a big problem hiding these warnings. if ( false !== strpos( $message, self::$unexpected_error ) ) { - return false; + // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection + return $this->fallback_error_handler( func_get_args() ); + } + + // If the fallback error handler returns true, the error was suppressed. + // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection + if ( $this->fallback_error_handler( func_get_args() ) ) { + return true; } $trace = new QM_Backtrace(); @@ -289,6 +305,21 @@ public function error_handler( $errno, $message, $file = null, $line = null, $co } + /** + * Fallback error handler. + * + * @param mixed[] $args Arguments. + * + * @return bool + * @noinspection PhpTernaryExpressionCanBeReplacedWithConditionInspection + */ + private function fallback_error_handler( array $args ): bool { + return null === $this->previous_error_handler ? + // Use standard error handler. + false : + (bool) call_user_func_array( $this->previous_error_handler, $args ); + } + /** * @param string $error * @param mixed[] $e