Version 4.6.0

This commit is contained in:
Neo committed 2025-12-19 05:55:31 -08:00
1 parent f79dcf067a
commit 517a5e1f70
2036 files changed
+110041 -26162

No files matched your search

+194 -112
View File
@@ -1,9 +1,23 @@
<?php
declare(strict_types=1);
namespace GraphQL\Error;
use Exception;
use GraphQL\Language\AST\Node;
use GraphQL\Language\Source;
use GraphQL\Language\SourceLocation;
use GraphQL\Utils\Utils;
use JsonSerializable;
use Throwable;
use Traversable;
use function array_filter;
use function array_map;
use function array_values;
use function count;
use function is_array;
use function iterator_to_array;
/**
* Describes an Error found during the parse, validate, or
@@ -19,19 +33,14 @@ use GraphQL\Utils\Utils;
* Class extends standard PHP `\Exception`, so all standard methods of base `\Exception` class
* are available in addition to those listed below.
*/
class Error extends \Exception implements \JsonSerializable, ClientAware
class Error extends Exception implements JsonSerializable, ClientAware
{
const CATEGORY_GRAPHQL = 'graphql';
const CATEGORY_GRAPHQL = 'graphql';
const CATEGORY_INTERNAL = 'internal';
/**
* A message describing the Error for debugging purposes.
* Lazily initialized.
*
* @var string
*/
public $message;
/**
* @var SourceLocation[]
*/
private $locations;
@@ -40,135 +49,148 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
* An array describing the JSON-path into the execution response which
* corresponds to this error. Only included for errors during execution.
*
* @var array
* @var mixed[]|null
*/
public $path;
/**
* An array of GraphQL AST Nodes corresponding to this error.
*
* @var array
* @var Node[]|null
*/
public $nodes;
/**
* The source GraphQL document corresponding to this error.
* The source GraphQL document for the first location of this error.
*
* Note that if this Error represents more than one node, the source may not
* represent nodes after the first node.
*
* @var Source|null
*/
private $source;
/**
* @var array
*/
/** @var int[] */
private $positions;
/**
* @var bool
*/
/** @var bool */
private $isClientSafe;
/**
* @var string
*/
/** @var string */
protected $category;
/** @var mixed[]|null */
protected $extensions;
/**
* @param string $message
* @param Node|Node[]|Traversable|null $nodes
* @param mixed[] $positions
* @param mixed[]|null $path
* @param Throwable $previous
* @param mixed[] $extensions
*/
public function __construct(
$message = '',
$nodes = null,
?Source $source = null,
array $positions = [],
$path = null,
$previous = null,
array $extensions = []
) {
parent::__construct($message, 0, $previous);
// Compute list of blame nodes.
if ($nodes instanceof Traversable) {
$nodes = iterator_to_array($nodes);
} elseif ($nodes !== null && ! is_array($nodes)) {
$nodes = [$nodes];
}
$this->nodes = $nodes;
$this->source = $source;
$this->positions = $positions;
$this->path = $path;
$this->extensions = count($extensions) > 0 ? $extensions : (
$previous instanceof self
? $previous->extensions
: []
);
if ($previous instanceof ClientAware) {
$this->isClientSafe = $previous->isClientSafe();
$cat = $previous->getCategory();
$this->category = $cat === '' || $cat === null ? self::CATEGORY_INTERNAL: $cat;
} elseif ($previous !== null) {
$this->isClientSafe = false;
$this->category = self::CATEGORY_INTERNAL;
} else {
$this->isClientSafe = true;
$this->category = self::CATEGORY_GRAPHQL;
}
}
/**
* Given an arbitrary Error, presumably thrown while attempting to execute a
* GraphQL operation, produce a new GraphQLError aware of the location in the
* document responsible for the original Error.
*
* @param $error
* @param array|null $nodes
* @param array|null $path
* @param mixed $error
* @param Node[]|null $nodes
* @param mixed[]|null $path
*
* @return Error
*/
public static function createLocatedError($error, $nodes = null, $path = null)
{
if ($error instanceof self) {
if ($error->path && $error->nodes) {
if ($error->path !== null && $error->nodes !== null && count($error->nodes) !== 0) {
return $error;
} else {
$nodes = $nodes ?: $error->nodes;
$path = $path ?: $error->path;
}
$nodes = $nodes ?? $error->nodes;
$path = $path ?? $error->path;
}
$source = $positions = $originalError = null;
$source = null;
$originalError = null;
$positions = [];
$extensions = [];
if ($error instanceof self) {
$message = $error->getMessage();
$message = $error->getMessage();
$originalError = $error;
$nodes = $error->nodes ?: $nodes;
$source = $error->source;
$positions = $error->positions;
} else if ($error instanceof \Exception || $error instanceof \Throwable) {
$message = $error->getMessage();
$nodes = $error->nodes ?? $nodes;
$source = $error->source;
$positions = $error->positions;
$extensions = $error->extensions;
} elseif ($error instanceof Throwable) {
$message = $error->getMessage();
$originalError = $error;
} else {
$message = (string) $error;
}
return new static(
$message ?: 'An unknown error occurred.',
$message === '' || $message === null ? 'An unknown error occurred.' : $message,
$nodes,
$source,
$positions,
$path,
$originalError
$originalError,
$extensions
);
}
/**
* @param Error $error
* @return array
* @return mixed[]
*/
public static function formatError(Error $error)
{
return $error->toSerializableArray();
}
/**
* @param string $message
* @param array|null $nodes
* @param Source $source
* @param array|null $positions
* @param array|null $path
* @param \Throwable $previous
*/
public function __construct(
$message,
$nodes = null,
Source $source = null,
$positions = null,
$path = null,
$previous = null
)
{
parent::__construct($message, 0, $previous);
if ($nodes instanceof \Traversable) {
$nodes = iterator_to_array($nodes);
}
$this->nodes = $nodes;
$this->source = $source;
$this->positions = $positions;
$this->path = $path;
if ($previous instanceof ClientAware) {
$this->isClientSafe = $previous->isClientSafe();
$this->category = $previous->getCategory() ?: static::CATEGORY_INTERNAL;
} else if ($previous) {
$this->isClientSafe = false;
$this->category = static::CATEGORY_INTERNAL;
} else {
$this->isClientSafe = true;
$this->category = static::CATEGORY_GRAPHQL;
}
}
/**
* @inheritdoc
*/
@@ -185,34 +207,40 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
return $this->category;
}
/**
* @return Source|null
*/
public function getSource()
public function getSource() : ?Source
{
if (null === $this->source) {
if (!empty($this->nodes[0]) && !empty($this->nodes[0]->loc)) {
if ($this->source === null) {
if (isset($this->nodes[0]) && $this->nodes[0]->loc !== null) {
$this->source = $this->nodes[0]->loc->source;
}
}
return $this->source;
}
/**
* @return array
* @return int[]
*/
public function getPositions()
public function getPositions() : array
{
if (null === $this->positions) {
if (!empty($this->nodes)) {
$positions = array_map(function($node) {
if (count($this->positions) === 0 && count($this->nodes ?? []) > 0) {
$positions = array_map(
static function ($node) : ?int {
return isset($node->loc) ? $node->loc->start : null;
}, $this->nodes);
$this->positions = array_filter($positions, function($p) {
},
$this->nodes
);
$positions = array_filter(
$positions,
static function ($p) : bool {
return $p !== null;
});
}
}
);
$this->positions = array_values($positions);
}
return $this->positions;
}
@@ -227,19 +255,38 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
* point out to field mentioned in multiple fragments. Errors during execution include a
* single location, the field which produced the error.
*
* @api
* @return SourceLocation[]
*
* @api
*/
public function getLocations()
public function getLocations() : array
{
if (null === $this->locations) {
if (! isset($this->locations)) {
$positions = $this->getPositions();
$source = $this->getSource();
$source = $this->getSource();
$nodes = $this->nodes;
if ($positions && $source) {
$this->locations = array_map(function ($pos) use ($source) {
return $source->getLocation($pos);
}, $positions);
if ($source !== null && count($positions) !== 0) {
$this->locations = array_map(
static function ($pos) use ($source) : SourceLocation {
return $source->getLocation($pos);
},
$positions
);
} elseif ($nodes !== null && count($nodes) !== 0) {
$locations = array_filter(
array_map(
static function ($node) : ?SourceLocation {
if (isset($node->loc->source)) {
return $node->loc->source->getLocation($node->loc->start);
}
return null;
},
$nodes
)
);
$this->locations = array_values($locations);
} else {
$this->locations = [];
}
@@ -248,53 +295,88 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
return $this->locations;
}
/**
* @return Node[]|null
*/
public function getNodes()
{
return $this->nodes;
}
/**
* Returns an array describing the path from the root value to the field which produced this error.
* Only included for execution errors.
*
* @return mixed[]|null
*
* @api
* @return array|null
*/
public function getPath()
{
return $this->path;
}
/**
* @return mixed[]
*/
public function getExtensions()
{
return $this->extensions;
}
/**
* Returns array representation of error suitable for serialization
*
* @deprecated Use FormattedError::createFromException() instead
* @return array
*
* @return mixed[]
*
* @codeCoverageIgnore
*/
public function toSerializableArray()
{
$arr = [
'message' => $this->getMessage()
'message' => $this->getMessage(),
];
$locations = Utils::map($this->getLocations(), function(SourceLocation $loc) {
return $loc->toSerializableArray();
});
$locations = Utils::map(
$this->getLocations(),
static function (SourceLocation $loc) : array {
return $loc->toSerializableArray();
}
);
if (!empty($locations)) {
if (count($locations) > 0) {
$arr['locations'] = $locations;
}
if (!empty($this->path)) {
if (count($this->path ?? []) > 0) {
$arr['path'] = $this->path;
}
if (count($this->extensions ?? []) > 0) {
$arr['extensions'] = $this->extensions;
}
return $arr;
}
/**
* Specify data which should be serialized to JSON
*
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
*
* @return mixed data which can be serialized by <b>json_encode</b>,
* which is a value of any type other than a resource.
* @since 5.4.0
*/
function jsonSerialize()
public function jsonSerialize()
{
return $this->toSerializableArray();
}
/**
* @return string
*/
public function __toString()
{
return FormattedError::printError($this);
}
}