$e->isClientSafe() ? $e->getMessage() : $internalErrorMessage, 'category' => $e->getCategory() ]; } else { $formattedError = [ 'message' => $internalErrorMessage, 'category' => Error::CATEGORY_INTERNAL ]; } if ($e instanceof Error) { $locations = Utils::map($e->getLocations(), function(SourceLocation $loc) { return $loc->toSerializableArray(); }); if (!empty($locations)) { $formattedError['locations'] = $locations; } if (!empty($e->path)) { $formattedError['path'] = $e->path; } } if ($debug) { $formattedError = self::addDebugEntries($formattedError, $e, $debug); } return $formattedError; } /** * Decorates spec-compliant $formattedError with debug entries according to $debug flags * (see GraphQL\Error\Debug for available flags) * * @param array $formattedError * @param \Throwable $e * @param bool $debug * @return array * @throws \Throwable */ public static function addDebugEntries(array $formattedError, $e, $debug) { if (!$debug) { return $formattedError; } Utils::invariant( $e instanceof \Exception || $e instanceof \Throwable, "Expected exception, got %s", Utils::getVariableType($e) ); $debug = (int) $debug; if ($debug & Debug::RETHROW_INTERNAL_EXCEPTIONS) { if (!$e instanceof Error) { throw $e; } else if ($e->getPrevious()) { throw $e->getPrevious(); } } $isInternal = !$e instanceof ClientAware || !$e->isClientSafe(); if (($debug & Debug::INCLUDE_DEBUG_MESSAGE) && $isInternal) { // Displaying debugMessage as a first entry: $formattedError = ['debugMessage' => $e->getMessage()] + $formattedError; } if ($debug & Debug::INCLUDE_TRACE) { if ($e instanceof \ErrorException || $e instanceof \Error) { $formattedError += [ 'file' => $e->getFile(), 'line' => $e->getLine(), ]; } $isTrivial = $e instanceof Error && !$e->getPrevious(); if (!$isTrivial) { $debugging = $e->getPrevious() ?: $e; $formattedError['trace'] = static::toSafeTrace($debugging); } } return $formattedError; } /** * Prepares final error formatter taking in account $debug flags. * If initial formatter is not set, FormattedError::createFromException is used * * @param callable|null $formatter * @param $debug * @return callable|\Closure */ public static function prepareFormatter(callable $formatter = null, $debug) { $formatter = $formatter ?: function($e) { return FormattedError::createFromException($e); }; if ($debug) { $formatter = function($e) use ($formatter, $debug) { return FormattedError::addDebugEntries($formatter($e), $e, $debug); }; } return $formatter; } /** * Returns error trace as serializable array * * @api * @param \Throwable $error * @return array */ public static function toSafeTrace($error) { $trace = $error->getTrace(); // Remove invariant entries as they don't provide much value: if ( isset($trace[0]['function']) && isset($trace[0]['class']) && ('GraphQL\Utils\Utils::invariant' === $trace[0]['class'].'::'.$trace[0]['function'])) { array_shift($trace); } // Remove root call as it's likely error handler trace: else if (!isset($trace[0]['file'])) { array_shift($trace); } return array_map(function($err) { $safeErr = array_intersect_key($err, ['file' => true, 'line' => true]); if (isset($err['function'])) { $func = $err['function']; $args = !empty($err['args']) ? array_map([__CLASS__, 'printVar'], $err['args']) : []; $funcStr = $func . '(' . implode(", ", $args) . ')'; if (isset($err['class'])) { $safeErr['call'] = $err['class'] . '::' . $funcStr; } else { $safeErr['function'] = $funcStr; } } return $safeErr; }, $trace); } /** * @param $var * @return string */ public static function printVar($var) { if ($var instanceof Type) { // FIXME: Replace with schema printer call if ($var instanceof WrappingType) { $var = $var->getWrappedType(true); } return 'GraphQLType: ' . $var->name; } if (is_object($var)) { return 'instance of ' . get_class($var) . ($var instanceof \Countable ? '(' . count($var) . ')' : ''); } if (is_array($var)) { return 'array(' . count($var) . ')'; } if ('' === $var) { return '(empty string)'; } if (is_string($var)) { return "'" . addcslashes($var, "'") . "'"; } if (is_bool($var)) { return $var ? 'true' : 'false'; } if (is_scalar($var)) { return $var; } if (null === $var) { return 'null'; } return gettype($var); } /** * @deprecated as of v0.8.0 * @param $error * @param SourceLocation[] $locations * @return array */ public static function create($error, array $locations = []) { $formatted = [ 'message' => $error ]; if (!empty($locations)) { $formatted['locations'] = array_map(function($loc) { return $loc->toArray();}, $locations); } return $formatted; } /** * @param \ErrorException $e * @deprecated as of v0.10.0, use general purpose method createFromException() instead * @return array */ public static function createFromPHPError(\ErrorException $e) { return [ 'message' => $e->getMessage(), 'severity' => $e->getSeverity(), 'trace' => self::toSafeTrace($e) ]; } }