Files
faveo/vendor/bugsnag/bugsnag-laravel/src/Internal/BacktraceProcessor.php
RafficMohammed 774eed8b0e laravel-6 support
2023-01-08 01:17:22 +05:30

187 lines
5.4 KiB
PHP

<?php
namespace Bugsnag\BugsnagLaravel\Internal;
/**
* The criteria for an error to be unhandled in a Laravel or Lumen app is as
* follows.
*
* 1. All unhandled exceptions must pass through one of the `HANDLER_CLASSES`
* report method
* 2. Unhandled exceptions will have had a caller from inside a vendor namespace
* or the App exception handler
* 3. The above exception handler must have originally been called from
* within a vendor namespace
*/
final class BacktraceProcessor
{
/**
* Searching for a frame where the framework's error handler is called.
*/
const STATE_FRAMEWORK_HANDLER = 1;
/**
* Searching for a frame where the framework's error handler was called by
* the framework itself, or the user's exception handler.
*/
const STATE_HANDLER_CALLER = 2;
/**
* Deciding if the frame was unhandled.
*/
const STATE_IS_UNHANDLED = 3;
/**
* A state to signal that we're done processing frames and know if the error
* was unhandled.
*/
const STATE_DONE = 4;
/**
* Laravel and Lumen use different exception handlers which live in different
* namespaces.
*/
const LARAVEL_HANDLER_CLASS = \Illuminate\Foundation\Exceptions\Handler::class;
const LUMEN_HANDLER_CLASS = \Laravel\Lumen\Exceptions\Handler::class;
/**
* The method used by the x_HANDLER_CLASS to report errors.
*/
const HANDLER_METHOD = 'report';
/**
* Laravel uses the "Exception" namespace, Lumen uses the plural "Exceptions"
* for the default app exception handler.
*/
const LARAVEL_APP_EXCEPTION_HANDLER = \App\Exception\Handler::class;
const LUMEN_APP_EXCEPTION_HANDLER = \App\Exceptions\Handler::class;
/**
* Namespaces used by Laravel and Lumen so we can determine if code was
* called by the user's app or the framework itself.
*
* Note this is not a mistake - Lumen uses the 'Laravel' namespace but
* Laravel itself does not
*/
const LARAVEL_VENDOR_NAMESPACE = 'Illuminate\\';
const LUMEN_VENDOR_NAMESPACE = 'Laravel\\';
/**
* The current state; one of the self::STATE_ constants.
*
* @var int
*/
private $state = self::STATE_FRAMEWORK_HANDLER;
/**
* This flag will be set to 'true' if we determine the error was unhandled.
*
* @var bool
*/
private $unhandled = false;
/**
* A backtrace matching the format of PHP's 'debug_backtrace'.
*
* @var array
*/
private $backtrace;
/**
* @param array $backtrace
*/
public function __construct(array $backtrace)
{
$this->backtrace = $backtrace;
}
/**
* Determine if the backtrace was from an unhandled error.
*
* @return bool
*/
public function isUnhandled()
{
foreach ($this->backtrace as $frame) {
$this->processFrame($frame);
// stop iterating early if we know we're done
if ($this->state === self::STATE_DONE) {
break;
}
}
return $this->unhandled;
}
/**
* @param array $frame
*
* @return void
*/
private function processFrame(array $frame)
{
if (!isset($frame['class'])) {
return;
}
$class = $frame['class'];
switch ($this->state) {
case self::STATE_FRAMEWORK_HANDLER:
// if this class is a framework exception handler and the function
// matches self::HANDLER_METHOD, we can move on to searching for
// the caller
if (($class === self::LARAVEL_HANDLER_CLASS || $class === self::LUMEN_HANDLER_CLASS)
&& isset($frame['function'])
&& $frame['function'] === self::HANDLER_METHOD
) {
$this->state = self::STATE_HANDLER_CALLER;
}
break;
case self::STATE_HANDLER_CALLER:
// if this is an app exception handler or a framework class, we
// can move on to determine if this was unhandled or not
if ($class === self::LARAVEL_APP_EXCEPTION_HANDLER
|| $class === self::LUMEN_APP_EXCEPTION_HANDLER
|| $this->isVendor($class)
) {
$this->state = self::STATE_IS_UNHANDLED;
}
break;
case self::STATE_IS_UNHANDLED:
// we are only interested in running this once so move immediately
// into the "done" state. This ensures we only check the frame
// immediately before the caller of the exception handler
$this->state = self::STATE_DONE;
// if this class is internal to the framework then the exception
// was unhandled
if ($this->isVendor($class)) {
$this->unhandled = true;
}
break;
}
}
/**
* Does the given class belong to a vendor namespace?
*
* @see self::VENDOR_NAMESPACES
*
* @param string $class
*
* @return bool
*/
private function isVendor($class)
{
return substr($class, 0, strlen(self::LARAVEL_VENDOR_NAMESPACE)) === self::LARAVEL_VENDOR_NAMESPACE
|| substr($class, 0, strlen(self::LUMEN_VENDOR_NAMESPACE)) === self::LUMEN_VENDOR_NAMESPACE;
}
}