Merge pull request #288 from simPod/phpcs

RFC: PHP CS
This commit is contained in:
Vladimir Razuvaev 2018-06-23 12:32:26 +07:00 committed by GitHub
commit 5a90e9bd64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 426 additions and 245 deletions

3
.gitignore vendored
View File

@ -1,5 +1,4 @@
.idea/
composer.phar composer.phar
composer.lock composer.lock
phpcs.xml
vendor/ vendor/
bin/

View File

@ -2,8 +2,6 @@ dist: trusty
language: php language: php
php: php:
- 5.6
- 7.0
- 7.1 - 7.1
- 7.2 - 7.2
- nightly - nightly
@ -47,3 +45,21 @@ jobs:
after_script: after_script:
- wget https://scrutinizer-ci.com/ocular.phar - wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover clover.xml - php ocular.phar code-coverage:upload --format=php-clover clover.xml
- stage: Pull request coding standard
if: type = pull_request
install: travis_retry composer install --prefer-dist
script:
- |
if [ $TRAVIS_BRANCH != "master" ]; then
git remote set-branches --add origin $TRAVIS_BRANCH;
git fetch origin $TRAVIS_BRANCH;
fi
- git merge-base origin/$TRAVIS_BRANCH $TRAVIS_PULL_REQUEST_SHA || git fetch origin +refs/pull/$TRAVIS_PULL_REQUEST/merge --unshallow
- wget https://github.com/diff-sniffer/git/releases/download/0.1.0/git-phpcs.phar
- php git-phpcs.phar origin/$TRAVIS_BRANCH...$TRAVIS_PULL_REQUEST_SHA
- stage: Coding standard
if: NOT type = pull_request
php: 7.1
install: travis_retry composer install --prefer-dist
script:
- ./vendor/bin/phpcs

View File

@ -9,10 +9,11 @@
"API" "API"
], ],
"require": { "require": {
"php": ">=5.6", "php": "^7.1",
"ext-mbstring": "*" "ext-mbstring": "*"
}, },
"require-dev": { "require-dev": {
"doctrine/coding-standard": "^4.0",
"phpunit/phpunit": "^4.8", "phpunit/phpunit": "^4.8",
"psr/http-message": "^1.0" "psr/http-message": "^1.0"
}, },
@ -35,5 +36,8 @@
"suggest": { "suggest": {
"react/promise": "To leverage async resolving on React PHP platform", "react/promise": "To leverage async resolving on React PHP platform",
"psr/http-message": "To use standard GraphQL server" "psr/http-message": "To use standard GraphQL server"
},
"scripts": {
"lint" : "vendor/bin/phpcs"
} }
} }

93
phpcs.xml.dist Normal file
View File

@ -0,0 +1,93 @@
<?xml version="1.0"?>
<ruleset>
<arg name="basepath" value="." />
<arg name="extensions" value="php" />
<arg name="parallel" value="80" />
<arg name="cache" value=".phpcs-cache" />
<arg name="colors" />
<!-- Ignore warnings, show progress of the run and show sniff names -->
<arg value="nps" />
<file>src</file>
<file>tests</file>
<rule ref="Doctrine">
<!--Disable PHP7+ features that might cause BC breaks for now -->
<exclude name="SlevomatCodingStandard.Classes.ClassConstantVisibility.MissingConstantVisibility" />
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint" />
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint" />
</rule>
<!--@api annotation is required for now -->
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration">
<properties>
<property
name="usefulAnnotations"
type="array"
value="
@after,
@afterClass,
@AfterMethods,
@api,
@Attribute,
@Attributes,
@before,
@beforeClass,
@BeforeMethods,
@covers,
@coversDefaultClass,
@coversNothing,
@dataProvider,
@depends,
@deprecated,
@doesNotPerformAssertions,
@Enum,
@expectedDeprecation,
@expectedException,
@expectedExceptionCode,
@expectedExceptionMessage,
@expectedExceptionMessageRegExp,
@group,
@Groups,
@IgnoreAnnotation,
@internal,
@Iterations,
@link,
@ODM\,
@ORM\,
@requires,
@Required,
@Revs,
@runInSeparateProcess,
@runTestsInSeparateProcesses,
@see,
@Target,
@test,
@throws,
@uses
"
/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Commenting.ForbiddenAnnotations">
<properties>
<property
name="forbiddenAnnotations"
type="array"
value="
@author,
@category,
@copyright,
@created,
@license,
@package,
@since,
@subpackage,
@version
"
/>
</properties>
</rule>
</ruleset>

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Error; namespace GraphQL\Error;
/** /**

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Error; namespace GraphQL\Error;
/** /**
@ -6,7 +9,7 @@ namespace GraphQL\Error;
*/ */
class Debug class Debug
{ {
const INCLUDE_DEBUG_MESSAGE = 1; const INCLUDE_DEBUG_MESSAGE = 1;
const INCLUDE_TRACE = 2; const INCLUDE_TRACE = 2;
const RETHROW_INTERNAL_EXCEPTIONS = 4; const RETHROW_INTERNAL_EXCEPTIONS = 4;
} }

View File

@ -1,10 +1,18 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Error; namespace GraphQL\Error;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
use GraphQL\Language\Source; use GraphQL\Language\Source;
use GraphQL\Language\SourceLocation; use GraphQL\Language\SourceLocation;
use GraphQL\Utils\Utils; use GraphQL\Utils\Utils;
use function array_filter;
use function array_map;
use function array_merge;
use function is_array;
use function iterator_to_array;
/** /**
* Describes an Error found during the parse, validate, or * Describes an Error found during the parse, validate, or
@ -22,7 +30,7 @@ use GraphQL\Utils\Utils;
*/ */
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'; const CATEGORY_INTERNAL = 'internal';
/** /**
@ -32,23 +40,21 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
*/ */
public $message; public $message;
/** /** @var SourceLocation[] */
* @var SourceLocation[]
*/
private $locations; private $locations;
/** /**
* An array describing the JSON-path into the execution response which * An array describing the JSON-path into the execution response which
* corresponds to this error. Only included for errors during execution. * corresponds to this error. Only included for errors during execution.
* *
* @var array * @var mixed[]|null
*/ */
public $path; public $path;
/** /**
* An array of GraphQL AST Nodes corresponding to this error. * An array of GraphQL AST Nodes corresponding to this error.
* *
* @var array * @var Node[]|null
*/ */
public $nodes; public $nodes;
@ -62,34 +68,74 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
*/ */
private $source; private $source;
/** /** @var int[]|null */
* @var array
*/
private $positions; private $positions;
/** /** @var bool */
* @var bool
*/
private $isClientSafe; private $isClientSafe;
/** /** @var string */
* @var string
*/
protected $category; protected $category;
/** /** @var mixed[]|null */
* @var array
*/
protected $extensions; protected $extensions;
/**
* @param string $message
* @param Node[]|null $nodes
* @param mixed[]|null $positions
* @param mixed[]|null $path
* @param \Throwable $previous
* @param mixed[] $extensions
*/
public function __construct(
$message,
$nodes = null,
?Source $source = null,
$positions = null,
$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 && ! is_array($nodes)) {
$nodes = [$nodes];
}
$this->nodes = $nodes;
$this->source = $source;
$this->positions = $positions;
$this->path = $path;
$this->extensions = $extensions ?: (
$previous && $previous instanceof self
? $previous->extensions
: []
);
if ($previous instanceof ClientAware) {
$this->isClientSafe = $previous->isClientSafe();
$this->category = $previous->getCategory() ?: static::CATEGORY_INTERNAL;
} elseif ($previous) {
$this->isClientSafe = false;
$this->category = static::CATEGORY_INTERNAL;
} else {
$this->isClientSafe = true;
$this->category = static::CATEGORY_GRAPHQL;
}
}
/** /**
* Given an arbitrary Error, presumably thrown while attempting to execute a * Given an arbitrary Error, presumably thrown while attempting to execute a
* GraphQL operation, produce a new GraphQLError aware of the location in the * GraphQL operation, produce a new GraphQLError aware of the location in the
* document responsible for the original Error. * document responsible for the original Error.
* *
* @param $error * @param mixed $error
* @param array|null $nodes * @param Node[]|null $nodes
* @param array|null $path * @param mixed[]|null $path
* @return Error * @return Error
*/ */
public static function createLocatedError($error, $nodes = null, $path = null) public static function createLocatedError($error, $nodes = null, $path = null)
@ -99,22 +145,22 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
return $error; return $error;
} else { } else {
$nodes = $nodes ?: $error->nodes; $nodes = $nodes ?: $error->nodes;
$path = $path ?: $error->path; $path = $path ?: $error->path;
} }
} }
$source = $positions = $originalError = null; $source = $positions = $originalError = null;
$extensions = []; $extensions = [];
if ($error instanceof self) { if ($error instanceof self) {
$message = $error->getMessage(); $message = $error->getMessage();
$originalError = $error; $originalError = $error;
$nodes = $error->nodes ?: $nodes; $nodes = $error->nodes ?: $nodes;
$source = $error->source; $source = $error->source;
$positions = $error->positions; $positions = $error->positions;
$extensions = $error->extensions; $extensions = $error->extensions;
} else if ($error instanceof \Exception || $error instanceof \Throwable) { } elseif ($error instanceof \Exception || $error instanceof \Throwable) {
$message = $error->getMessage(); $message = $error->getMessage();
$originalError = $error; $originalError = $error;
} else { } else {
$message = (string) $error; $message = (string) $error;
@ -131,66 +177,14 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
); );
} }
/** /**
* @param Error $error * @return mixed[]
* @return array
*/ */
public static function formatError(Error $error) public static function formatError(Error $error)
{ {
return $error->toSerializableArray(); return $error->toSerializableArray();
} }
/**
* @param string $message
* @param array|Node|null $nodes
* @param Source $source
* @param array|null $positions
* @param array|null $path
* @param \Throwable $previous
* @param array $extensions
*/
public function __construct(
$message,
$nodes = null,
Source $source = null,
$positions = null,
$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);
} else if ($nodes && !is_array($nodes)) {
$nodes = [$nodes];
}
$this->nodes = $nodes;
$this->source = $source;
$this->positions = $positions;
$this->path = $path;
$this->extensions = $extensions ?: (
$previous && $previous instanceof self
? $previous->extensions
: []
);
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 * @inheritdoc
*/ */
@ -212,29 +206,36 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
*/ */
public function getSource() public function getSource()
{ {
if (null === $this->source) { if ($this->source === null) {
if (!empty($this->nodes[0]) && !empty($this->nodes[0]->loc)) { if (! empty($this->nodes[0]) && ! empty($this->nodes[0]->loc)) {
$this->source = $this->nodes[0]->loc->source; $this->source = $this->nodes[0]->loc->source;
} }
} }
return $this->source; return $this->source;
} }
/** /**
* @return array * @return int[]
*/ */
public function getPositions() public function getPositions()
{ {
if (null === $this->positions) { if ($this->positions === null && ! empty($this->nodes)) {
if (!empty($this->nodes)) { $positions = array_map(
$positions = array_map(function($node) { function ($node) {
return isset($node->loc) ? $node->loc->start : null; return isset($node->loc) ? $node->loc->start : null;
}, $this->nodes); },
$this->positions = array_filter($positions, function($p) { $this->nodes
);
$this->positions = array_filter(
$positions,
function ($p) {
return $p !== null; return $p !== null;
}); }
} );
} }
return $this->positions; return $this->positions;
} }
@ -254,21 +255,29 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
*/ */
public function getLocations() public function getLocations()
{ {
if (null === $this->locations) { if ($this->locations === null) {
$positions = $this->getPositions(); $positions = $this->getPositions();
$source = $this->getSource(); $source = $this->getSource();
$nodes = $this->nodes; $nodes = $this->nodes;
if ($positions && $source) { if ($positions && $source) {
$this->locations = array_map(function ($pos) use ($source) { $this->locations = array_map(
return $source->getLocation($pos); function ($pos) use ($source) {
}, $positions); return $source->getLocation($pos);
} else if ($nodes) { },
$this->locations = array_filter(array_map(function ($node) { $positions
if ($node->loc) { );
return $node->loc->source->getLocation($node->loc->start); } elseif ($nodes) {
} $this->locations = array_filter(
}, $nodes)); array_map(
function ($node) {
if ($node->loc) {
return $node->loc->source->getLocation($node->loc->start);
}
},
$nodes
)
);
} else { } else {
$this->locations = []; $this->locations = [];
} }
@ -278,7 +287,7 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
} }
/** /**
* @return array|Node[]|null * @return Node[]|null
*/ */
public function getNodes() public function getNodes()
{ {
@ -290,7 +299,7 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
* Only included for execution errors. * Only included for execution errors.
* *
* @api * @api
* @return array|null * @return mixed[]|null
*/ */
public function getPath() public function getPath()
{ {
@ -298,7 +307,7 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
} }
/** /**
* @return array * @return mixed[]
*/ */
public function getExtensions() public function getExtensions()
{ {
@ -309,26 +318,29 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
* Returns array representation of error suitable for serialization * Returns array representation of error suitable for serialization
* *
* @deprecated Use FormattedError::createFromException() instead * @deprecated Use FormattedError::createFromException() instead
* @return array * @return mixed[]
*/ */
public function toSerializableArray() public function toSerializableArray()
{ {
$arr = [ $arr = [
'message' => $this->getMessage() 'message' => $this->getMessage(),
]; ];
if ($this->getExtensions()) { if ($this->getExtensions()) {
$arr = array_merge($this->getExtensions(), $arr); $arr = array_merge($this->getExtensions(), $arr);
} }
$locations = Utils::map($this->getLocations(), function(SourceLocation $loc) { $locations = Utils::map(
return $loc->toSerializableArray(); $this->getLocations(),
}); function (SourceLocation $loc) {
return $loc->toSerializableArray();
}
);
if (!empty($locations)) { if (! empty($locations)) {
$arr['locations'] = $locations; $arr['locations'] = $locations;
} }
if (!empty($this->path)) { if (! empty($this->path)) {
$arr['path'] = $this->path; $arr['path'] = $this->path;
} }
@ -340,9 +352,8 @@ class Error extends \Exception implements \JsonSerializable, ClientAware
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
* @return mixed data which can be serialized by <b>json_encode</b>, * @return mixed data which can be serialized by <b>json_encode</b>,
* which is a value of any type other than a resource. * which is a value of any type other than a resource.
* @since 5.4.0
*/ */
function jsonSerialize() public function jsonSerialize()
{ {
return $this->toSerializableArray(); return $this->toSerializableArray();
} }

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Error; namespace GraphQL\Error;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
@ -7,6 +10,26 @@ use GraphQL\Language\SourceLocation;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\WrappingType; use GraphQL\Type\Definition\WrappingType;
use GraphQL\Utils\Utils; use GraphQL\Utils\Utils;
use function addcslashes;
use function array_filter;
use function array_intersect_key;
use function array_map;
use function array_merge;
use function array_shift;
use function count;
use function get_class;
use function gettype;
use function implode;
use function is_array;
use function is_bool;
use function is_object;
use function is_scalar;
use function is_string;
use function mb_strlen;
use function preg_split;
use function sprintf;
use function str_repeat;
use function strlen;
/** /**
* This class is used for [default error formatting](error-handling.md). * This class is used for [default error formatting](error-handling.md).
@ -15,6 +38,7 @@ use GraphQL\Utils\Utils;
*/ */
class FormattedError class FormattedError
{ {
/** @var string */
private static $internalErrorMessage = 'Internal server error'; private static $internalErrorMessage = 'Internal server error';
/** /**
@ -33,7 +57,6 @@ class FormattedError
* Prints a GraphQLError to a string, representing useful location information * Prints a GraphQLError to a string, representing useful location information
* about the error's position in the source. * about the error's position in the source.
* *
* @param Error $error
* @return string * @return string
*/ */
public static function printError(Error $error) public static function printError(Error $error)
@ -41,63 +64,65 @@ class FormattedError
$printedLocations = []; $printedLocations = [];
if ($error->nodes) { if ($error->nodes) {
/** @var Node $node */ /** @var Node $node */
foreach($error->nodes as $node) { foreach ($error->nodes as $node) {
if ($node->loc) { if (! $node->loc) {
$printedLocations[] = self::highlightSourceAtLocation( continue;
$node->loc->source,
$node->loc->source->getLocation($node->loc->start)
);
} }
if ($node->loc->source === null) {
continue;
}
$printedLocations[] = self::highlightSourceAtLocation(
$node->loc->source,
$node->loc->source->getLocation($node->loc->start)
);
} }
} else if ($error->getSource() && $error->getLocations()) { } elseif ($error->getSource() && $error->getLocations()) {
$source = $error->getSource(); $source = $error->getSource();
foreach($error->getLocations() as $location) { foreach ($error->getLocations() as $location) {
$printedLocations[] = self::highlightSourceAtLocation($source, $location); $printedLocations[] = self::highlightSourceAtLocation($source, $location);
} }
} }
return !$printedLocations return ! $printedLocations
? $error->getMessage() ? $error->getMessage()
: join("\n\n", array_merge([$error->getMessage()], $printedLocations)) . "\n"; : implode("\n\n", array_merge([$error->getMessage()], $printedLocations)) . "\n";
} }
/** /**
* Render a helpful description of the location of the error in the GraphQL * Render a helpful description of the location of the error in the GraphQL
* Source document. * Source document.
* *
* @param Source $source
* @param SourceLocation $location
* @return string * @return string
*/ */
private static function highlightSourceAtLocation(Source $source, SourceLocation $location) private static function highlightSourceAtLocation(Source $source, SourceLocation $location)
{ {
$line = $location->line; $line = $location->line;
$lineOffset = $source->locationOffset->line - 1; $lineOffset = $source->locationOffset->line - 1;
$columnOffset = self::getColumnOffset($source, $location); $columnOffset = self::getColumnOffset($source, $location);
$contextLine = $line + $lineOffset; $contextLine = $line + $lineOffset;
$contextColumn = $location->column + $columnOffset; $contextColumn = $location->column + $columnOffset;
$prevLineNum = (string) ($contextLine - 1); $prevLineNum = (string) ($contextLine - 1);
$lineNum = (string) $contextLine; $lineNum = (string) $contextLine;
$nextLineNum = (string) ($contextLine + 1); $nextLineNum = (string) ($contextLine + 1);
$padLen = strlen($nextLineNum); $padLen = strlen($nextLineNum);
$lines = preg_split('/\r\n|[\n\r]/', $source->body); $lines = preg_split('/\r\n|[\n\r]/', $source->body);
$lines[0] = self::whitespace($source->locationOffset->column - 1) . $lines[0]; $lines[0] = self::whitespace($source->locationOffset->column - 1) . $lines[0];
$outputLines = [ $outputLines = [
"{$source->name} ($contextLine:$contextColumn)", sprintf('%s (%s:%s)', $source->name, $contextLine, $contextColumn),
$line >= 2 ? (self::lpad($padLen, $prevLineNum) . ': ' . $lines[$line - 2]) : null, $line >= 2 ? (self::lpad($padLen, $prevLineNum) . ': ' . $lines[$line - 2]) : null,
self::lpad($padLen, $lineNum) . ': ' . $lines[$line - 1], self::lpad($padLen, $lineNum) . ': ' . $lines[$line - 1],
self::whitespace(2 + $padLen + $contextColumn - 1) . '^', self::whitespace(2 + $padLen + $contextColumn - 1) . '^',
$line < count($lines)? self::lpad($padLen, $nextLineNum) . ': ' . $lines[$line] : null $line < count($lines) ? self::lpad($padLen, $nextLineNum) . ': ' . $lines[$line] : null,
]; ];
return join("\n", array_filter($outputLines)); return implode("\n", array_filter($outputLines));
} }
/** /**
* @param Source $source
* @param SourceLocation $location
* @return int * @return int
*/ */
private static function getColumnOffset(Source $source, SourceLocation $location) private static function getColumnOffset(Source $source, SourceLocation $location)
@ -109,7 +134,8 @@ class FormattedError
* @param int $len * @param int $len
* @return string * @return string
*/ */
private static function whitespace($len) { private static function whitespace($len)
{
return str_repeat(' ', $len); return str_repeat(' ', $len);
} }
@ -117,7 +143,8 @@ class FormattedError
* @param int $len * @param int $len
* @return string * @return string
*/ */
private static function lpad($len, $str) { private static function lpad($len, $str)
{
return self::whitespace($len - mb_strlen($str)) . $str; return self::whitespace($len - mb_strlen($str)) . $str;
} }
@ -132,16 +159,16 @@ class FormattedError
* *
* @api * @api
* @param \Throwable $e * @param \Throwable $e
* @param bool|int $debug * @param bool|int $debug
* @param string $internalErrorMessage * @param string $internalErrorMessage
* @return array * @return mixed[]
* @throws \Throwable * @throws \Throwable
*/ */
public static function createFromException($e, $debug = false, $internalErrorMessage = null) public static function createFromException($e, $debug = false, $internalErrorMessage = null)
{ {
Utils::invariant( Utils::invariant(
$e instanceof \Exception || $e instanceof \Throwable, $e instanceof \Exception || $e instanceof \Throwable,
"Expected exception, got %s", 'Expected exception, got %s',
Utils::getVariableType($e) Utils::getVariableType($e)
); );
@ -149,13 +176,13 @@ class FormattedError
if ($e instanceof ClientAware) { if ($e instanceof ClientAware) {
$formattedError = [ $formattedError = [
'message' => $e->isClientSafe() ? $e->getMessage() : $internalErrorMessage, 'message' => $e->isClientSafe() ? $e->getMessage() : $internalErrorMessage,
'category' => $e->getCategory() 'category' => $e->getCategory(),
]; ];
} else { } else {
$formattedError = [ $formattedError = [
'message' => $internalErrorMessage, 'message' => $internalErrorMessage,
'category' => Error::CATEGORY_INTERNAL 'category' => Error::CATEGORY_INTERNAL,
]; ];
} }
@ -164,14 +191,17 @@ class FormattedError
$formattedError = array_merge($e->getExtensions(), $formattedError); $formattedError = array_merge($e->getExtensions(), $formattedError);
} }
$locations = Utils::map($e->getLocations(), function(SourceLocation $loc) { $locations = Utils::map(
return $loc->toSerializableArray(); $e->getLocations(),
}); function (SourceLocation $loc) {
return $loc->toSerializableArray();
}
);
if (!empty($locations)) { if (! empty($locations)) {
$formattedError['locations'] = $locations; $formattedError['locations'] = $locations;
} }
if (!empty($e->path)) { if (! empty($e->path)) {
$formattedError['path'] = $e->path; $formattedError['path'] = $e->path;
} }
} }
@ -187,35 +217,37 @@ class FormattedError
* Decorates spec-compliant $formattedError with debug entries according to $debug flags * Decorates spec-compliant $formattedError with debug entries according to $debug flags
* (see GraphQL\Error\Debug for available flags) * (see GraphQL\Error\Debug for available flags)
* *
* @param array $formattedError * @param mixed[] $formattedError
* @param \Throwable $e * @param \Throwable $e
* @param bool $debug * @param bool $debug
* @return array * @return mixed[]
* @throws \Throwable * @throws \Throwable
*/ */
public static function addDebugEntries(array $formattedError, $e, $debug) public static function addDebugEntries(array $formattedError, $e, $debug)
{ {
if (!$debug) { if (! $debug) {
return $formattedError; return $formattedError;
} }
Utils::invariant( Utils::invariant(
$e instanceof \Exception || $e instanceof \Throwable, $e instanceof \Exception || $e instanceof \Throwable,
"Expected exception, got %s", 'Expected exception, got %s',
Utils::getVariableType($e) Utils::getVariableType($e)
); );
$debug = (int) $debug; $debug = (int) $debug;
if ($debug & Debug::RETHROW_INTERNAL_EXCEPTIONS) { if ($debug & Debug::RETHROW_INTERNAL_EXCEPTIONS) {
if (!$e instanceof Error) { if (! $e instanceof Error) {
throw $e; throw $e;
} else if ($e->getPrevious()) { }
if ($e->getPrevious()) {
throw $e->getPrevious(); throw $e->getPrevious();
} }
} }
$isInternal = !$e instanceof ClientAware || !$e->isClientSafe(); $isInternal = ! $e instanceof ClientAware || ! $e->isClientSafe();
if (($debug & Debug::INCLUDE_DEBUG_MESSAGE) && $isInternal) { if (($debug & Debug::INCLUDE_DEBUG_MESSAGE) && $isInternal) {
// Displaying debugMessage as a first entry: // Displaying debugMessage as a first entry:
@ -230,13 +262,14 @@ class FormattedError
]; ];
} }
$isTrivial = $e instanceof Error && !$e->getPrevious(); $isTrivial = $e instanceof Error && ! $e->getPrevious();
if (!$isTrivial) { if (! $isTrivial) {
$debugging = $e->getPrevious() ?: $e; $debugging = $e->getPrevious() ?: $e;
$formattedError['trace'] = static::toSafeTrace($debugging); $formattedError['trace'] = static::toSafeTrace($debugging);
} }
} }
return $formattedError; return $formattedError;
} }
@ -244,20 +277,20 @@ class FormattedError
* Prepares final error formatter taking in account $debug flags. * Prepares final error formatter taking in account $debug flags.
* If initial formatter is not set, FormattedError::createFromException is used * If initial formatter is not set, FormattedError::createFromException is used
* *
* @param callable|null $formatter * @param bool $debug
* @param $debug
* @return callable|\Closure * @return callable|\Closure
*/ */
public static function prepareFormatter(callable $formatter = null, $debug) public static function prepareFormatter(?callable $formatter = null, $debug)
{ {
$formatter = $formatter ?: function($e) { $formatter = $formatter ?: function ($e) {
return FormattedError::createFromException($e); return FormattedError::createFromException($e);
}; };
if ($debug) { if ($debug) {
$formatter = function($e) use ($formatter, $debug) { $formatter = function ($e) use ($formatter, $debug) {
return FormattedError::addDebugEntries($formatter($e), $e, $debug); return FormattedError::addDebugEntries($formatter($e), $e, $debug);
}; };
} }
return $formatter; return $formatter;
} }
@ -266,45 +299,45 @@ class FormattedError
* *
* @api * @api
* @param \Throwable $error * @param \Throwable $error
* @return array * @return mixed[]
*/ */
public static function toSafeTrace($error) public static function toSafeTrace($error)
{ {
$trace = $error->getTrace(); $trace = $error->getTrace();
// Remove invariant entries as they don't provide much value: if (isset($trace[0]['function']) && isset($trace[0]['class']) &&
if ( // Remove invariant entries as they don't provide much value:
isset($trace[0]['function']) && isset($trace[0]['class']) && ($trace[0]['class'] . '::' . $trace[0]['function'] === 'GraphQL\Utils\Utils::invariant')) {
('GraphQL\Utils\Utils::invariant' === $trace[0]['class'].'::'.$trace[0]['function'])) { array_shift($trace);
} elseif (! isset($trace[0]['file'])) {
// Remove root call as it's likely error handler trace:
array_shift($trace); array_shift($trace);
} }
// Remove root call as it's likely error handler trace: return array_map(
else if (!isset($trace[0]['file'])) { function ($err) {
array_shift($trace); $safeErr = array_intersect_key($err, ['file' => true, 'line' => true]);
}
return array_map(function($err) { if (isset($err['function'])) {
$safeErr = array_intersect_key($err, ['file' => true, 'line' => true]); $func = $err['function'];
$args = ! empty($err['args']) ? array_map([__CLASS__, 'printVar'], $err['args']) : [];
$funcStr = $func . '(' . implode(', ', $args) . ')';
if (isset($err['function'])) { if (isset($err['class'])) {
$func = $err['function']; $safeErr['call'] = $err['class'] . '::' . $funcStr;
$args = !empty($err['args']) ? array_map([__CLASS__, 'printVar'], $err['args']) : []; } else {
$funcStr = $func . '(' . implode(", ", $args) . ')'; $safeErr['function'] = $funcStr;
}
if (isset($err['class'])) {
$safeErr['call'] = $err['class'] . '::' . $funcStr;
} else {
$safeErr['function'] = $funcStr;
} }
}
return $safeErr; return $safeErr;
}, $trace); },
$trace
);
} }
/** /**
* @param $var * @param mixed $var
* @return string * @return string
*/ */
public static function printVar($var) public static function printVar($var)
@ -314,6 +347,7 @@ class FormattedError
if ($var instanceof WrappingType) { if ($var instanceof WrappingType) {
$var = $var->getWrappedType(true); $var = $var->getWrappedType(true);
} }
return 'GraphQLType: ' . $var->name; return 'GraphQLType: ' . $var->name;
} }
@ -323,7 +357,7 @@ class FormattedError
if (is_array($var)) { if (is_array($var)) {
return 'array(' . count($var) . ')'; return 'array(' . count($var) . ')';
} }
if ('' === $var) { if ($var === '') {
return '(empty string)'; return '(empty string)';
} }
if (is_string($var)) { if (is_string($var)) {
@ -335,42 +369,45 @@ class FormattedError
if (is_scalar($var)) { if (is_scalar($var)) {
return $var; return $var;
} }
if (null === $var) { if ($var === null) {
return 'null'; return 'null';
} }
return gettype($var); return gettype($var);
} }
/** /**
* @deprecated as of v0.8.0 * @deprecated as of v0.8.0
* @param $error * @param string $error
* @param SourceLocation[] $locations * @param SourceLocation[] $locations
* @return array * @return mixed[]
*/ */
public static function create($error, array $locations = []) public static function create($error, array $locations = [])
{ {
$formatted = [ $formatted = ['message' => $error];
'message' => $error
];
if (!empty($locations)) { if (! empty($locations)) {
$formatted['locations'] = array_map(function($loc) { return $loc->toArray();}, $locations); $formatted['locations'] = array_map(
function ($loc) {
return $loc->toArray();
},
$locations
);
} }
return $formatted; return $formatted;
} }
/** /**
* @param \ErrorException $e
* @deprecated as of v0.10.0, use general purpose method createFromException() instead * @deprecated as of v0.10.0, use general purpose method createFromException() instead
* @return array * @return mixed[]
*/ */
public static function createFromPHPError(\ErrorException $e) public static function createFromPHPError(\ErrorException $e)
{ {
return [ return [
'message' => $e->getMessage(), 'message' => $e->getMessage(),
'severity' => $e->getSeverity(), 'severity' => $e->getSeverity(),
'trace' => self::toSafeTrace($e) 'trace' => self::toSafeTrace($e),
]; ];
} }
} }

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Error; namespace GraphQL\Error;
/** /**
@ -7,8 +10,6 @@ namespace GraphQL\Error;
* Note: * Note:
* This exception should not inherit base Error exception as it is raised when there is an error somewhere in * This exception should not inherit base Error exception as it is raised when there is an error somewhere in
* user-land code * user-land code
*
* @package GraphQL\Error
*/ */
class InvariantViolation extends \LogicException class InvariantViolation extends \LogicException
{ {

View File

@ -1,19 +1,22 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Error; namespace GraphQL\Error;
use GraphQL\Language\Source; use GraphQL\Language\Source;
use function sprintf;
class SyntaxError extends Error class SyntaxError extends Error
{ {
/** /**
* @param Source $source * @param int $position
* @param int $position
* @param string $description * @param string $description
*/ */
public function __construct(Source $source, $position, $description) public function __construct(Source $source, $position, $description)
{ {
parent::__construct( parent::__construct(
"Syntax Error: $description", sprintf('Syntax Error: %s', $description),
null, null,
$source, $source,
[$position] [$position]

View File

@ -1,12 +1,13 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Error; namespace GraphQL\Error;
/** /**
* Class UserError * Class UserError
* *
* Error caused by actions of GraphQL clients. Can be safely displayed to a client... * Error caused by actions of GraphQL clients. Can be safely displayed to a client...
*
* @package GraphQL\Error
*/ */
class UserError extends \RuntimeException implements ClientAware class UserError extends \RuntimeException implements ClientAware
{ {

View File

@ -1,6 +1,12 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Error; namespace GraphQL\Error;
use const E_USER_WARNING;
use function trigger_error;
/** /**
* Encapsulates warnings produced by the library. * Encapsulates warnings produced by the library.
* *
@ -9,27 +15,29 @@ namespace GraphQL\Error;
*/ */
final class Warning final class Warning
{ {
const WARNING_ASSIGN = 2; const WARNING_ASSIGN = 2;
const WARNING_CONFIG = 4; const WARNING_CONFIG = 4;
const WARNING_FULL_SCHEMA_SCAN = 8; const WARNING_FULL_SCHEMA_SCAN = 8;
const WARNING_CONFIG_DEPRECATION = 16; const WARNING_CONFIG_DEPRECATION = 16;
const WARNING_NOT_A_TYPE = 32; const WARNING_NOT_A_TYPE = 32;
const ALL = 63; const ALL = 63;
static $enableWarnings = self::ALL; /** @var int */
private static $enableWarnings = self::ALL;
static $warned = []; /** @var mixed[] */
private static $warned = [];
static private $warningHandler; /** @var callable|null */
private static $warningHandler;
/** /**
* Sets warning handler which can intercept all system warnings. * Sets warning handler which can intercept all system warnings.
* When not set, trigger_error() is used to notify about warnings. * When not set, trigger_error() is used to notify about warnings.
* *
* @api * @api
* @param callable|null $warningHandler
*/ */
public static function setWarningHandler(callable $warningHandler = null) public static function setWarningHandler(?callable $warningHandler = null)
{ {
self::$warningHandler = $warningHandler; self::$warningHandler = $warningHandler;
} }
@ -45,14 +53,15 @@ final class Warning
* @api * @api
* @param bool|int $suppress * @param bool|int $suppress
*/ */
static function suppress($suppress = true) public static function suppress($suppress = true)
{ {
if (true === $suppress) { if ($suppress === true) {
self::$enableWarnings = 0; self::$enableWarnings = 0;
} else if (false === $suppress) { } elseif ($suppress === false) {
self::$enableWarnings = self::ALL; self::$enableWarnings = self::ALL;
} else { } else {
$suppress = (int) $suppress; $suppress = (int) $suppress;
self::$enableWarnings &= ~$suppress; self::$enableWarnings &= ~$suppress;
} }
} }
@ -70,33 +79,34 @@ final class Warning
*/ */
public static function enable($enable = true) public static function enable($enable = true)
{ {
if (true === $enable) { if ($enable === true) {
self::$enableWarnings = self::ALL; self::$enableWarnings = self::ALL;
} else if (false === $enable) { } elseif ($enable === false) {
self::$enableWarnings = 0; self::$enableWarnings = 0;
} else { } else {
$enable = (int) $enable; $enable = (int) $enable;
self::$enableWarnings |= $enable; self::$enableWarnings |= $enable;
} }
} }
static function warnOnce($errorMessage, $warningId, $messageLevel = null) public static function warnOnce($errorMessage, $warningId, $messageLevel = null)
{ {
if (self::$warningHandler) { if (self::$warningHandler) {
$fn = self::$warningHandler; $fn = self::$warningHandler;
$fn($errorMessage, $warningId); $fn($errorMessage, $warningId);
} else if ((self::$enableWarnings & $warningId) > 0 && !isset(self::$warned[$warningId])) { } elseif ((self::$enableWarnings & $warningId) > 0 && ! isset(self::$warned[$warningId])) {
self::$warned[$warningId] = true; self::$warned[$warningId] = true;
trigger_error($errorMessage, $messageLevel ?: E_USER_WARNING); trigger_error($errorMessage, $messageLevel ?: E_USER_WARNING);
} }
} }
static function warn($errorMessage, $warningId, $messageLevel = null) public static function warn($errorMessage, $warningId, $messageLevel = null)
{ {
if (self::$warningHandler) { if (self::$warningHandler) {
$fn = self::$warningHandler; $fn = self::$warningHandler;
$fn($errorMessage, $warningId); $fn($errorMessage, $warningId);
} else if ((self::$enableWarnings & $warningId) > 0) { } elseif ((self::$enableWarnings & $warningId) > 0) {
trigger_error($errorMessage, $messageLevel ?: E_USER_WARNING); trigger_error($errorMessage, $messageLevel ?: E_USER_WARNING);
} }
} }