Skip to content

Commit cf81541

Browse files
l-youoojacoboo
andauthored
graphql-php v15 (#542)
* psr/container v2 * Implement replacement for Picotainer * Replace Picotainer with LazyContainer in tests and documentation for version >=6 * Fix tests for phpdocumentor/type-resolver minor update * run cs-fix script * Fix some cs-fixer errors manually * fix return type * abandon psr/container ^1 * Test for LazyContainer * Normalize RootTypeMapperInterface * end-to-end test with enums in mutation and as single-returned field * Bump ecodev/graphql-upload and webonyx/graphql-php" * Fix some tests for webonyx ^15.0 * clearer types to fix tests * clearer types to fix failing tests for webonyx 15 * clearer types to fix failing tests for webonyx 15 * fix failing tests for webonyx 15. Make clearer types. * fix failing tests for webonyx 15. Make clearer types. * fix some breaking changes for webonyx 15 * fix breaking changes for webonyx 15 * Update continuous_integration.yml Remove support for PHP 8.0 test matrix * Specify PHP 8.1 minimum support Co-authored-by: Jacob Thomason <[email protected]>
1 parent 53f9d49 commit cf81541

32 files changed

+158
-81
lines changed

.github/workflows/continuous_integration.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
strategy:
1919
matrix:
2020
install-args: ['', '--prefer-lowest']
21-
php-version: ['8.0', '8.1', '8.2']
21+
php-version: ['8.1', '8.2']
2222
fail-fast: false
2323

2424
steps:

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
}
1111
],
1212
"require": {
13-
"php": ">=8.0",
13+
"php": ">=8.1",
1414
"ext-json": "*",
1515
"doctrine/annotations": "1.13.2",
1616
"composer/package-versions-deprecated": "^1.8",
@@ -26,12 +26,12 @@
2626
"symfony/expression-language": "^4 || ^5 || ^6",
2727
"thecodingmachine/cache-utils": "^1",
2828
"thecodingmachine/class-explorer": "^1.1.0",
29-
"webonyx/graphql-php": "^v14.9.0"
29+
"webonyx/graphql-php": "^v15.0"
3030
},
3131
"require-dev": {
3232
"beberlei/porpaginas": "^1.2",
3333
"doctrine/coding-standard": "^9.0 || ^10.0",
34-
"ecodev/graphql-upload": "^6.1",
34+
"ecodev/graphql-upload": "^7.0",
3535
"laminas/laminas-diactoros": "^2",
3636
"myclabs/php-enum": "^1.6.6",
3737
"php-coveralls/php-coveralls": "^2.1",

src/Exceptions/GraphQLAggregateException.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ private function updateCode(): void
6464
* If many exceptions are passed, they are bundled in the GraphQLAggregateException
6565
*
6666
* @param (ClientAware&Throwable)[] $exceptions
67+
*
68+
* @throws GraphQLAggregateException|Throwable
6769
*/
6870
public static function throwExceptions(array $exceptions): void
6971
{

src/Exceptions/WebonyxErrorHandler.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,26 @@
77
use GraphQL\Error\ClientAware;
88
use GraphQL\Error\Error;
99
use GraphQL\Error\FormattedError;
10+
use GraphQL\Executor\ExecutionResult;
11+
use Throwable;
1012

1113
use function array_map;
1214

1315
/**
1416
* A custom error handler and error formatter for Webonyx that can read the GraphQLAggregateExceptionInterface
1517
* and the GraphQLExceptionInterface.
18+
*
19+
* @phpstan-import-type SerializableError from ExecutionResult
1620
*/
1721
final class WebonyxErrorHandler
1822
{
19-
/** @return mixed[] */
20-
public static function errorFormatter(Error $error): array
23+
/** @return SerializableError */
24+
public static function errorFormatter(Throwable $error): array
2125
{
2226
$formattedError = FormattedError::createFromException($error);
2327
$previous = $error->getPrevious();
2428
if ($previous instanceof GraphQLExceptionInterface && ! empty($previous->getExtensions())) {
25-
$formattedError['extensions'] += $previous->getExtensions();
29+
$formattedError['extensions'] = isset($formattedError['extensions']) ? $previous->getExtensions() + $formattedError['extensions'] : $previous->getExtensions();
2630
}
2731

2832
return $formattedError;

src/FieldsBuilder.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Doctrine\Common\Annotations\AnnotationException;
88
use GraphQL\Type\Definition\FieldDefinition;
9+
use GraphQL\Type\Definition\InputType;
910
use GraphQL\Type\Definition\NonNull;
1011
use GraphQL\Type\Definition\OutputType;
1112
use GraphQL\Type\Definition\Type;
@@ -895,7 +896,7 @@ private function getInputFieldsByMethodAnnotations(string|object $controller, Re
895896
if (! in_array($name, $constructerParameters)) {
896897
$inputFieldDescriptor->setTargetMethodOnSource($methodName);
897898
}
898-
899+
assert($type instanceof InputType);
899900
$inputFieldDescriptor->setType($type);
900901
$inputFieldDescriptor->setInjectSource($injectSource);
901902

@@ -990,7 +991,7 @@ private function getInputFieldsByPropertyAnnotations(string|object $controller,
990991
if (! $inputType && $isUpdate && $type instanceof NonNull) {
991992
$type = $type->getWrappedType();
992993
}
993-
994+
assert($type instanceof InputType);
994995
$inputFieldDescriptor->setType($type);
995996
$inputFieldDescriptor->setInjectSource(false);
996997
$inputFieldDescriptor->setTargetPropertyOnSource($refProperty->getName());

src/InputField.php

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
namespace TheCodingMachine\GraphQLite;
66

77
use GraphQL\Error\ClientAware;
8+
use GraphQL\Language\AST\InputValueDefinitionNode;
89
use GraphQL\Type\Definition\InputObjectField;
910
use GraphQL\Type\Definition\InputType;
1011
use GraphQL\Type\Definition\ListOfType;
1112
use GraphQL\Type\Definition\NonNull;
12-
use GraphQL\Type\Definition\NullableType;
1313
use GraphQL\Type\Definition\ResolveInfo;
1414
use GraphQL\Type\Definition\Type;
1515
use TheCodingMachine\GraphQLite\Exceptions\GraphQLAggregateException;
@@ -19,11 +19,15 @@
1919
use TheCodingMachine\GraphQLite\Parameters\MissingArgumentException;
2020
use TheCodingMachine\GraphQLite\Parameters\ParameterInterface;
2121
use TheCodingMachine\GraphQLite\Parameters\SourceParameter;
22+
use Throwable;
2223

2324
/**
2425
* A GraphQL input field that maps to a PHP method automatically.
2526
*
2627
* @internal
28+
*
29+
* @phpstan-import-type InputObjectFieldConfig from InputObjectField
30+
* @phpstan-import-type ArgumentType from InputObjectField
2731
*/
2832
class InputField extends InputObjectField
2933
{
@@ -33,14 +37,12 @@ class InputField extends InputObjectField
3337
private bool $forConstructorHydration = false;
3438

3539
/**
36-
* @param InputType|(NullableType&Type) $type
40+
* @param (Type&InputType) $type
3741
* @param array<string, ParameterInterface> $arguments Indexed by argument name.
38-
* @param ResolverInterface $originalResolver A pointer to the resolver being called (but not wrapped by any field middleware)
39-
* @param callable $resolver The resolver actually called
4042
* @param mixed|null $defaultValue the default value set for this field
41-
* @param array<string, mixed> $additionalConfig
43+
* @param array{defaultValue?: mixed,description?: string|null,astNode?: InputValueDefinitionNode|null}|null $additionalConfig
4244
*/
43-
public function __construct(string $name, $type, array $arguments, ResolverInterface|null $originalResolver, callable|null $resolver, string|null $comment, bool $isUpdate, bool $hasDefaultValue, mixed $defaultValue, array $additionalConfig = [])
45+
public function __construct(string $name, InputType $type, array $arguments, ResolverInterface|null $originalResolver, callable|null $resolver, string|null $comment, bool $isUpdate, bool $hasDefaultValue, mixed $defaultValue, array|null $additionalConfig = null)
4446
{
4547
$config = [
4648
'name' => $name,
@@ -77,8 +79,18 @@ public function __construct(string $name, $type, array $arguments, ResolverInter
7779
return $result;
7880
};
7981
}
82+
if ($additionalConfig !== null) {
83+
if (isset($additionalConfig['astNode'])) {
84+
$config['astNode'] = $additionalConfig['astNode'];
85+
}
86+
if (isset($additionalConfig['defaultValue'])) {
87+
$config['defaultValue'] = $additionalConfig['defaultValue'];
88+
}
89+
if (isset($additionalConfig['description'])) {
90+
$config['description'] = $additionalConfig['description'];
91+
}
92+
}
8093

81-
$config += $additionalConfig;
8294
parent::__construct($config);
8395
}
8496

@@ -167,6 +179,7 @@ public static function fromFieldDescriptor(InputFieldDescriptor $fieldDescriptor
167179
private function paramsToArguments(array $parameters, object|null $source, array $args, mixed $context, ResolveInfo $info, callable $resolve): array
168180
{
169181
$toPassArgs = [];
182+
/** @var (ClientAware&Throwable)[] $exceptions */
170183
$exceptions = [];
171184
foreach ($parameters as $parameter) {
172185
try {

src/InputFieldDescriptor.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
class InputFieldDescriptor
2727
{
2828
private string $name;
29-
/** @var InputType|(NullableType&Type) */
30-
private $type;
29+
/** @var (InputType&Type)|(InputType&Type&NullableType) */
30+
private InputType $type;
3131
/** @var array<string, ParameterInterface> */
3232
private array $parameters = [];
3333
/** @var callable|null */
@@ -95,14 +95,14 @@ public function setName(string $name): void
9595
$this->name = $name;
9696
}
9797

98-
/** @return InputType|(NullableType&Type) */
99-
public function getType()
98+
/** @return ((InputType&Type)|(InputType&Type&NullableType)) */
99+
public function getType(): InputType
100100
{
101101
return $this->type;
102102
}
103103

104-
/** @param InputType|(NullableType&Type) $type */
105-
public function setType($type): void
104+
/** @param (InputType&Type) $type */
105+
public function setType(InputType $type): void
106106
{
107107
$this->type = $type;
108108
}

src/InputTypeUtils.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
namespace TheCodingMachine\GraphQLite;
66

7+
use GraphQL\Type\Definition\Argument;
8+
use GraphQL\Type\Definition\InputObjectField;
9+
use GraphQL\Type\Definition\InputObjectType;
710
use GraphQL\Type\Definition\InputType;
811
use phpDocumentor\Reflection\Fqsen;
912
use phpDocumentor\Reflection\Type;
@@ -22,6 +25,11 @@
2225
use function assert;
2326
use function ltrim;
2427

28+
/**
29+
* @phpstan-import-type FieldConfig from InputObjectType
30+
* @phpstan-import-type ArgumentConfig from Argument
31+
* @phpstan-import-type InputObjectFieldConfig from InputObjectField
32+
*/
2533
class InputTypeUtils
2634
{
2735
public function __construct(
@@ -96,15 +104,15 @@ private function resolveSelf(Type $type, ReflectionClass $reflectionClass): Type
96104
*
97105
* @param ParameterInterface[] $args
98106
*
99-
* @return array<string, array<string, mixed|InputType>>
107+
* @return array{defaultValue?:mixed,type:\GraphQL\Type\Definition\Type&InputType}[]
100108
*/
101109
public static function getInputTypeArgs(array $args): array
102110
{
103111
$inputTypeArgs = array_filter($args, static function (ParameterInterface $parameter) {
104112
return $parameter instanceof InputTypeParameterInterface;
105113
});
106114

107-
return array_map(static function (InputTypeParameterInterface $parameter) {
115+
return array_map(static function (InputTypeParameterInterface $parameter): array {
108116
$desc = [
109117
'type' => $parameter->getType(),
110118
];

src/Mappers/PorpaginasTypeMapper.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function canMapClassToType(string $className): bool
4343
* Maps a PHP fully qualified class name to a GraphQL type.
4444
*
4545
* @param class-string<object> $className The exact class name to look for (this function does not look into parent classes).
46-
* @param (OutputType&Type)|null $subType An optional sub-type if the main class is an iterator that needs to be typed.
46+
* @param (OutputType&Type&NamedType)|null $subType An optional sub-type if the main class is an iterator that needs to be typed.
4747
*
4848
* @return MutableObjectType|MutableInterfaceType
4949
*
@@ -62,15 +62,16 @@ public function mapClassToType(string $className, OutputType|null $subType): Mut
6262
}
6363

6464
/**
65-
* @param OutputType&Type $subType
65+
* @param OutputType&Type&NamedType $subType
6666
*
6767
* @return MutableObjectType|MutableInterfaceType
6868
*/
6969
private function getObjectType(OutputType $subType): MutableInterface
7070
{
7171
/** @var mixed $name - invalid vendor mapping */
72-
$name = $subType->name;
72+
$name = $subType->name();
7373

74+
/** @noinspection PhpConditionAlreadyCheckedInspection */
7475
if ($name === null) {
7576
throw new RuntimeException('Cannot get name property from sub type ' . $subType::class);
7677
}

src/Mappers/Proxys/MutableAdapterTrait.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,14 @@ public function getFields(): array
9999

100100
$this->finalFields = $this->type->getFields();
101101
foreach ($this->fieldsCallables as $fieldsCallable) {
102-
$this->finalFields = FieldDefinition::defineFieldMap($this, $fieldsCallable()) + $this->finalFields;
102+
/**
103+
* @var FieldDefinition[] $fields
104+
*/
105+
$fields = FieldDefinition::defineFieldMap($this, $fieldsCallable()) + $this->finalFields;
106+
107+
108+
109+
$this->finalFields = $fields;
103110
}
104111
if (empty($this->finalFields)) {
105112
throw NoFieldsException::create($this->name);

0 commit comments

Comments
 (0)