Skip to content

Commit fbb20d2

Browse files
authored
PHP 8.4 Support (#722)
* PHP 8.4 Support Add PHP 8.4 to list of CI runners and confirm PHP 8.4 support * Updated dependencies and resolved PHPStan errors * Downgrade symfony/var-dumper for PHP 8.1 support * Support phpunit 10.5 for PHP 8.1 * Corrected call for enum interface inconsistency * Resolved PHPCS errors
1 parent 7b7ae16 commit fbb20d2

24 files changed

+64
-121
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.1', '8.2', '8.3']
21+
php-version: ['8.1', '8.2', '8.3', '8.4']
2222
fail-fast: false
2323

2424
steps:

composer.json

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,16 @@
2727
"kcs/class-finder": "^0.6.0"
2828
},
2929
"require-dev": {
30-
"beberlei/porpaginas": "^1.2 || ^2.0",
31-
"doctrine/coding-standard": "^11.0 || ^12.0",
30+
"beberlei/porpaginas": "^2.0",
31+
"doctrine/coding-standard": "^12.0",
3232
"ecodev/graphql-upload": "^7.0",
33-
"laminas/laminas-diactoros": "^2 || ^3",
33+
"laminas/laminas-diactoros": "^3.5",
3434
"myclabs/php-enum": "^1.6.6",
35-
"php-coveralls/php-coveralls": "^2.1",
36-
"phpstan/extension-installer": "^1.1",
37-
"phpstan/phpstan": "^1.11",
38-
"phpunit/phpunit": "^10.1 || ^11.0",
39-
"symfony/var-dumper": "^5.4 || ^6.0 || ^7",
40-
"thecodingmachine/phpstan-strict-rules": "^1.0"
35+
"php-coveralls/php-coveralls": "^2.7",
36+
"phpstan/extension-installer": "^1.4",
37+
"phpstan/phpstan": "^2.0",
38+
"phpunit/phpunit": "^10.5 || ^11.0",
39+
"symfony/var-dumper": "^6.4"
4140
},
4241
"suggest": {
4342
"beberlei/porpaginas": "If you want automatic pagination in your GraphQL types",

phpstan.neon

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,7 @@ parameters:
44
tmpDir: .phpstan-cache
55
paths:
66
- src
7-
excludePaths:
8-
# TODO: exlude only for PHP < 8.1
9-
- src/Mappers/Root/EnumTypeMapper.php
10-
- src/Types/EnumType.php
117
level: 8
12-
checkGenericClassInNonGenericObjectType: false
138
reportUnmatchedIgnoredErrors: false
149
treatPhpDocTypesAsCertain: false
1510
ignoreErrors:
@@ -42,3 +37,5 @@ parameters:
4237
-
4338
message: '#Call to an undefined method object::__toString\(\)#'
4439
path : src/Types/ID.php
40+
-
41+
identifier: missingType.generics

src/Annotations/Security.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,9 @@
66

77
use Attribute;
88
use BadMethodCallException;
9-
use TypeError;
109

1110
use function array_key_exists;
12-
use function gettype;
13-
use function is_array;
1411
use function is_string;
15-
use function sprintf;
1612

1713
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
1814
class Security implements MiddlewareAnnotationInterface
@@ -37,8 +33,6 @@ public function __construct(array|string $data = [], string|null $expression = n
3733
{
3834
if (is_string($data)) {
3935
$data = ['expression' => $data];
40-
} elseif (! is_array($data)) {
41-
throw new TypeError(sprintf('"%s": Argument $data is expected to be a string or array, got "%s".', __METHOD__, gettype($data)));
4236
}
4337

4438
$this->expression = $data['value'] ?? $data['expression'] ?? $expression;

src/Exceptions/GraphQLAggregateException.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Exception;
88
use GraphQL\Error\ClientAware;
9+
use RuntimeException;
910
use Throwable;
1011

1112
use function array_map;
@@ -22,7 +23,7 @@ class GraphQLAggregateException extends Exception implements GraphQLAggregateExc
2223
/** @param (ClientAware&Throwable)[] $exceptions */
2324
public function __construct(iterable $exceptions = [])
2425
{
25-
parent::__construct('Many exceptions have be thrown:');
26+
parent::__construct('Many exceptions have been thrown:');
2627

2728
foreach ($exceptions as $exception) {
2829
$this->add($exception);
@@ -56,6 +57,11 @@ private function updateCode(): void
5657
$codes = array_map(static function (Throwable $t) {
5758
return $t->getCode();
5859
}, $this->exceptions);
60+
61+
if (count($codes) === 0) {
62+
throw new RuntimeException('Unable to determine code for exception');
63+
}
64+
5965
$this->code = max($codes);
6066
}
6167

src/FactoryContext.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public function getClassFinderComputedCache(): ClassFinderComputedCache
103103
return $this->classFinderComputedCache;
104104
}
105105

106-
public function getClassBoundCache(): ClassBoundCache|null
106+
public function getClassBoundCache(): ClassBoundCache
107107
{
108108
return $this->classBoundCache;
109109
}

src/FieldsBuilder.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ private function getFieldsByMethodAnnotations(
497497

498498
$resolver = is_string($controller)
499499
? new SourceMethodResolver($refMethod)
500+
/** @phpstan-ignore argument.type */
500501
: new ServiceResolver([$controller, $methodName]);
501502

502503
$fieldDescriptor = new QueryFieldDescriptor(
@@ -512,7 +513,7 @@ private function getFieldsByMethodAnnotations(
512513
);
513514

514515
$field = $this->fieldMiddleware->process($fieldDescriptor, new class implements FieldHandlerInterface {
515-
public function handle(QueryFieldDescriptor $fieldDescriptor): FieldDefinition|null
516+
public function handle(QueryFieldDescriptor $fieldDescriptor): FieldDefinition
516517
{
517518
return QueryField::fromFieldDescriptor($fieldDescriptor);
518519
}
@@ -605,7 +606,7 @@ private function getFieldsByPropertyAnnotations(
605606
);
606607

607608
$field = $this->fieldMiddleware->process($fieldDescriptor, new class implements FieldHandlerInterface {
608-
public function handle(QueryFieldDescriptor $fieldDescriptor): FieldDefinition|null
609+
public function handle(QueryFieldDescriptor $fieldDescriptor): FieldDefinition
609610
{
610611
return QueryField::fromFieldDescriptor($fieldDescriptor);
611612
}
@@ -744,7 +745,7 @@ private function getQueryFieldsFromSourceFields(
744745
->withMiddlewareAnnotations($sourceField->getMiddlewareAnnotations());
745746

746747
$field = $this->fieldMiddleware->process($fieldDescriptor, new class implements FieldHandlerInterface {
747-
public function handle(QueryFieldDescriptor $fieldDescriptor): FieldDefinition|null
748+
public function handle(QueryFieldDescriptor $fieldDescriptor): FieldDefinition
748749
{
749750
return QueryField::fromFieldDescriptor($fieldDescriptor);
750751
}
@@ -822,7 +823,6 @@ private function resolvePhpType(
822823

823824
$context = $this->docBlockFactory->createContext($refClass);
824825
$phpdocType = $typeResolver->resolve($phpTypeStr, $context);
825-
assert($phpdocType !== null);
826826

827827
$fakeDocBlock = new DocBlock('', null, [new DocBlock\Tags\Return_($phpdocType)], $context);
828828
return $this->typeMapper->mapReturnType($refMethod, $fakeDocBlock);
@@ -1080,7 +1080,7 @@ private function getInputFieldsByMethodAnnotations(
10801080
);
10811081

10821082
$field = $this->inputFieldMiddleware->process($inputFieldDescriptor, new class implements InputFieldHandlerInterface {
1083-
public function handle(InputFieldDescriptor $inputFieldDescriptor): InputField|null
1083+
public function handle(InputFieldDescriptor $inputFieldDescriptor): InputField
10841084
{
10851085
return InputField::fromFieldDescriptor($inputFieldDescriptor);
10861086
}
@@ -1175,7 +1175,7 @@ private function getInputFieldsByPropertyAnnotations(
11751175
);
11761176

11771177
$field = $this->inputFieldMiddleware->process($inputFieldDescriptor, new class implements InputFieldHandlerInterface {
1178-
public function handle(InputFieldDescriptor $inputFieldDescriptor): InputField|null
1178+
public function handle(InputFieldDescriptor $inputFieldDescriptor): InputField
11791179
{
11801180
return InputField::fromFieldDescriptor($inputFieldDescriptor);
11811181
}

src/Http/Psr15GraphQLMiddlewareBuilder.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public function __construct(Schema $schema)
5151
$this->config->setSchema($schema);
5252
$this->config->setDebugFlag(DebugFlag::RETHROW_UNSAFE_EXCEPTIONS);
5353
$this->config->setErrorFormatter([WebonyxErrorHandler::class, 'errorFormatter']);
54+
/** @phpstan-ignore argument.type */
5455
$this->config->setErrorsHandler([WebonyxErrorHandler::class, 'errorHandler']);
5556
$this->config->setContext(new Context());
5657
$this->config->setPersistedQueryLoader(new NotSupportedPersistedQueryLoader());

src/Http/WebonyxGraphqlMiddleware.php

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use TheCodingMachine\GraphQLite\Context\ResetableContextInterface;
2020

2121
use function array_map;
22+
use function count;
2223
use function explode;
2324
use function in_array;
2425
use function is_array;
@@ -99,11 +100,7 @@ private function processResult(ExecutionResult|array|Promise $result): array
99100
}, $result);
100101
}
101102

102-
if ($result instanceof Promise) {
103-
throw new RuntimeException('Only SyncPromiseAdapter is supported');
104-
}
105-
106-
throw new RuntimeException('Unexpected response from StandardServer::executePsrRequest'); // @codeCoverageIgnore
103+
throw new RuntimeException('Only SyncPromiseAdapter is supported');
107104
}
108105

109106
/** @param ExecutionResult|array<int,ExecutionResult>|Promise $result */
@@ -118,19 +115,14 @@ private function decideHttpCode(ExecutionResult|array|Promise $result): int
118115
return $this->httpCodeDecider->decideHttpStatusCode($executionResult);
119116
}, $result);
120117

121-
return (int) max($codes);
122-
}
118+
if (count($codes) === 0) {
119+
throw new RuntimeException('Unable to determine HTTP status code');
120+
}
123121

124-
// @codeCoverageIgnoreStart
125-
// Code unreachable because exceptions will be triggered in processResult first.
126-
// We keep it for defensive programming purpose
127-
if ($result instanceof Promise) {
128-
throw new RuntimeException('Only SyncPromiseAdapter is supported');
122+
return (int) max($codes);
129123
}
130124

131-
throw new RuntimeException('Unexpected response from StandardServer::executePsrRequest');
132-
133-
// @codeCoverageIgnoreEnd
125+
throw new RuntimeException('Only SyncPromiseAdapter is supported');
134126
}
135127

136128
private function isGraphqlRequest(ServerRequestInterface $request): bool

src/InputTypeUtils.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ private function validateReturnType(ReflectionMethod $refMethod): Fqsen
7373
$typeResolver = new TypeResolver();
7474

7575
$phpdocType = $typeResolver->resolve($type);
76-
assert($phpdocType !== null);
7776
$phpdocType = $this->resolveSelf($phpdocType, $refMethod->getDeclaringClass());
7877
if (! $phpdocType instanceof Object_) {
7978
throw MissingTypeHintRuntimeException::invalidReturnType($refMethod);

0 commit comments

Comments
 (0)