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; } }