Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
- "^9"
- "^10"
- "^11"
- "^12"
composer:
- name: lowest
arg: "--prefer-lowest --prefer-stable"
Expand All @@ -44,6 +45,10 @@ jobs:
laravel-version: "^11"
- php-version: "8.1"
laravel-version: "^11"
- php-version: "8.0"
laravel-version: "^12"
- php-version: "8.1"
laravel-version: "^12"

steps:
- uses: actions/checkout@v4
Expand All @@ -63,7 +68,8 @@ jobs:
- name: "Remove conflicting dependencies that are not needed here"
run: composer remove --dev --no-update phpbench/phpbench rector/rector

- if: matrix.laravel-version != '^10'
- name: "Remove Pennant for Laravel 9 because it is not compatible"
if: matrix.laravel-version == '^9'
run: composer remove --dev --no-update laravel/pennant

- run: >
Expand Down Expand Up @@ -94,6 +100,7 @@ jobs:
- "^9"
- "^10"
- "^11"
- "^12"
os:
- ubuntu-latest
composer:
Expand All @@ -108,6 +115,10 @@ jobs:
laravel-version: "^11"
- php-version: "8.1"
laravel-version: "^11"
- php-version: "8.0"
laravel-version: "^12"
- php-version: "8.1"
laravel-version: "^12"

services:
mysql:
Expand Down Expand Up @@ -142,7 +153,8 @@ jobs:
- name: "Remove conflicting dependencies that are not needed here"
run: composer remove --dev --no-update larastan/larastan phpstan/phpstan-mockery phpbench/phpbench rector/rector

- if: matrix.laravel-version != '^10' && matrix.laravel-version != '^11'
- name: "Remove Pennant for Laravel 9 because it is not compatible"
if: matrix.laravel-version == '^9'
run: composer remove --dev --no-update laravel/pennant

- run: >
Expand All @@ -162,7 +174,7 @@ jobs:
strategy:
matrix:
php-version: ["8.4"]
laravel-version: ["^11"]
laravel-version: ["^12"]

services:
mysql:
Expand Down Expand Up @@ -209,7 +221,7 @@ jobs:
strategy:
matrix:
php-version: ["8.4"]
laravel-version: ["^11"]
laravel-version: ["^12"]

steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

### Added

- Support Laravel 12 https://github.com/nuwave/lighthouse/pull/2665

## v6.53.0

### Added
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/QueryBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ abstract class QueryBench extends TestCase
/** Cached graphQL endpoint. */
protected string $graphQLEndpoint;

public function __construct()
public function __construct() // @phpstan-ignore method.parentMethodFinalByPhpDoc (yeah this is hacky)
{
parent::__construct(static::class);
}
Expand Down
34 changes: 20 additions & 14 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,37 @@
"php": "^8",
"ext-json": "*",
"haydenpierce/class-finder": "^0.4 || ^0.5",
"illuminate/auth": "^9 || ^10 || ^11",
"illuminate/bus": "^9 || ^10 || ^11",
"illuminate/contracts": "^9 || ^10 || ^11",
"illuminate/http": "^9 || ^10 || ^11",
"illuminate/pagination": "^9 || ^10 || ^11",
"illuminate/queue": "^9 || ^10 || ^11",
"illuminate/routing": "^9 || ^10 || ^11",
"illuminate/support": "^9 || ^10 || ^11",
"illuminate/validation": "^9 || ^10 || ^11",
"illuminate/auth": "^9 || ^10 || ^11 || ^12",
"illuminate/bus": "^9 || ^10 || ^11 || ^12",
"illuminate/contracts": "^9 || ^10 || ^11 || ^12",
"illuminate/http": "^9 || ^10 || ^11 || ^12",
"illuminate/pagination": "^9 || ^10 || ^11 || ^12",
"illuminate/queue": "^9 || ^10 || ^11 || ^12",
"illuminate/routing": "^9 || ^10 || ^11 || ^12",
"illuminate/support": "^9 || ^10 || ^11 || ^12",
"illuminate/validation": "^9 || ^10 || ^11 || ^12",
"laragraph/utils": "^1.5 || ^2",
"thecodingmachine/safe": "^1 || ^2 || ^3",
"webonyx/graphql-php": "^15"
},
"require-dev": {
"algolia/algoliasearch-client-php": "^3",
"bensampo/laravel-enum": "^5 || ^6",
"dms/phpunit-arraysubset-asserts": "^0.4 || ^0.5",
"dms/phpunit-arraysubset-asserts": "^0.4 || ^0.5 || dev-add-phpunit-11-support",
"ergebnis/composer-normalize": "^2.2.2",
"fakerphp/faker": "^1.21",
"google/protobuf": "^3.21",
"larastan/larastan": "^2.9.14 || ^3.0.4",
"laravel/framework": "^9 || ^10 || ^11",
"laravel/framework": "^9 || ^10 || ^11 || ^12",
"laravel/legacy-factories": "^1.1.1",
"laravel/pennant": "^1",
"laravel/scout": "^8 || ^9 || ^10 || ^11",
"laravel/scout": "^8 || ^9 || ^10",
"mattiasgeniar/phpunit-query-count-assertions": "^1.1",
"mll-lab/graphql-php-scalars": "^6",
"mll-lab/php-cs-fixer-config": "^5",
"mockery/mockery": "^1.5",
"nesbot/carbon": "^2.62.1",
"orchestra/testbench": "^7.50 || ^8.32 || ^9.10",
"nesbot/carbon": "^2.62.1 || ^3.8.4",
"orchestra/testbench": "^7.50 || ^8.32 || ^9.10 || ^10.1",
"phpbench/phpbench": "^1.2.6",
"phpstan/extension-installer": "^1",
"phpstan/phpstan": "^1.12.18 || ^2",
Expand All @@ -80,6 +80,12 @@
"mll-lab/laravel-graphiql": "A graphical interactive in-browser GraphQL IDE - integrated with Laravel",
"pusher/pusher-php-server": "Required when using the Pusher Subscriptions driver"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/pieterocp/phpunit-arraysubset-asserts"
}
],
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
Expand Down
6 changes: 6 additions & 0 deletions phpstan-bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php declare(strict_types=1);

require_once __DIR__ . '/vendor/autoload.php';

// Required by \Nuwave\Lighthouse\Schema\AST constructor when PHPStan analyzes code
config(['lighthouse.schema_cache.enable' => false]);
15 changes: 13 additions & 2 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
parameters:
level: 8 # TODO level up to max
bootstrapFiles:
- phpstan-bootstrap.php
stubFiles:
- _ide_helper.php
paths:
Expand Down Expand Up @@ -34,33 +36,42 @@ parameters:
- path: tests/database/factories
message: '#Variable \$factory might not be defined#'

# Mixins are magical
# Mixins magically rebind $this to be something else and do other funky stuff
- path: src/Testing/TestResponseMixin.php
message: '#Method Nuwave\\Lighthouse\\Testing\\TestResponseMixin::assertGraphQLErrorMessage\(\) invoked with 1 parameter, 0 required\.#'
- path: src/Testing/TestResponseMixin.php
message: '#Method Nuwave\\Lighthouse\\Testing\\TestResponseMixin::assertGraphQLDebugMessage\(\) invoked with 1 parameter, 0 required\.#'
- path: src/Testing/TestResponseMixin.php
message: '#Anonymous function should return Illuminate\\Testing\\TestResponse but returns .*#'

# Recognition of mixin methods is broken in some PHPStan/Larastan versions
- '#Call to an undefined method Illuminate\\Testing\\TestResponse::(assertGraphQLValidationError|assertGraphQLValidationKeys|assertGraphQLValidationPasses|assertGraphQLError|assertGraphQLErrorMessage|assertGraphQLDebugMessage|assertGraphQLErrorFree|assertGraphQLSubscriptionAuthorized|assertGraphQLSubscriptionNotAuthorized|graphQLSubscriptionMock|graphQLSubscriptionChannelName|assertGraphQLBroadcasted|assertGraphQLNotBroadcasted)\(\)\.#'

# Relation forwards calls to Builder
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder<Illuminate\\Database\\Eloquent\\Model>|Illuminate\\Database\\Eloquent\\Relations\\Relation<Illuminate\\Database\\Eloquent\\Model>|Illuminate\\Database\\Query\\Builder::(orderBy|where|whereIn|whereNotIn|whereBetween|whereJsonContains|whereNotBetween)\(\)\.#'

# Laravel 11 changes a lot of generics
- '#generic class (Illuminate\\Database\\Eloquent\\Builder|Laravel\\Scout\\Builder)( but)? does not specify its types#'
- '#contains generic type Illuminate\\Testing\\TestResponse<.+> but class Illuminate\\Testing\\TestResponse is not generic\.#'
- '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(HasOne|HasMany|BelongsToMany|MorphOne|MorphMany|MorphToMany)<.+, .+> in PHPDoc tag @return specifies 2 template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only 1: TRelatedModel#'
- '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(HasOne|HasMany|BelongsToMany|MorphOne|MorphMany|MorphToMany)<.+> in PHPDoc tag @return specifies \d template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only \d: .+#'
- '#Method .+ should return Illuminate\\Database\\Eloquent\\Relations\\(HasOne|HasMany|BelongsToMany|MorphOne|MorphMany|MorphToMany)<(.+), .+> but returns Illuminate\\Database\\Eloquent\\Relations\\\1<\2>.#'
- '#Method .+ should return Illuminate\\Database\\Eloquent\\Relations\\(BelongsTo|MorphTo)<(.+), \$this\((.+)\)> but returns Illuminate\\Database\\Eloquent\\Relations\\\1<\2, \3>.#'
- '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(HasOneThrough|HasManyThrough)<.+, .+, .+> in PHPDoc tag @return specifies 3 template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only 1: TRelatedModel#'
- '#Method .+ should return Illuminate\\Database\\Eloquent\\Relations\\(HasOneThrough|HasManyThrough)<(.+), .+> but returns Illuminate\\Database\\Eloquent\\Relations\\\1<\2>.#'

# Different between PHPUnit versions
- '#PHPDoc tag @return contains generic type PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker<stdClass> but class PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker is not generic.#'

# This test cheats and uses reflection to make assertions
- path: tests/Unit/Schema/Directives/BaseDirectiveTest.php
message: '#Call to protected method getModelClass\(\) of class Nuwave\\Lighthouse\\Schema\\Directives\\BaseDirective\.#'

# This is a library, so it should be extendable
- '#Unsafe usage of new static.*#'

# Possible footgun, but people who extend Lighthouse classes should know what they are doing
- '#Cannot unset property .+ because it might have hooks in a subclass\.#'

# Ease transition for non-nullable properties towards native types https://github.com/phpstan/phpstan/issues/5150
- '#Property .* in isset\(\) is not nullable\.#'

Expand Down
3 changes: 3 additions & 0 deletions src/Console/FieldCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ protected function getDefaultNamespace($rootNamespace): string
protected function nameParts(): array
{
$name = $this->argument('name');
if (! is_string($name)) {
throw new \InvalidArgumentException('You must specify the name for the class to generate.');
}

$parts = explode('.', $name);
if (count($parts) !== 2) {
Expand Down
3 changes: 3 additions & 0 deletions src/Console/LighthouseGeneratorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ abstract class LighthouseGeneratorCommand extends GeneratorCommand
protected function getNameInput(): string
{
$name = $this->argument('name');
if (! is_string($name)) {
throw new \InvalidArgumentException('You must specify the name for the class to generate.');
}

return ucfirst(trim($name));
}
Expand Down
12 changes: 6 additions & 6 deletions src/GraphQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,9 @@ public function executeParsedQueryRaw(
$queryComplexityRule = $validationRules[QueryComplexity::class] ?? null;
$queryComplexity = $queryComplexityRule instanceof QueryComplexity
// TODO remove this check when updating the required version of webonyx/graphql-php
&& method_exists($queryComplexityRule, 'getQueryComplexity')
? $queryComplexityRule->getQueryComplexity()
: null;
&& method_exists($queryComplexityRule, 'getQueryComplexity') // @phpstan-ignore function.alreadyNarrowedType (depends on the used library version)
? $queryComplexityRule->getQueryComplexity()
: null;

/** @var array<\Nuwave\Lighthouse\Execution\ExtensionsResponse|null> $extensionsResponses */
$extensionsResponses = (array) $this->eventDispatcher->dispatch(
Expand Down Expand Up @@ -404,13 +404,13 @@ protected function validateCacheableRules(
}

if ($queryHash === null) {
return DocumentValidator::validate($schema, $query, $validationRules);
return DocumentValidator::validate($schema, $query, $validationRules); // @phpstan-ignore return.type (TODO remove ignore when requiring a newer version of webonyx/graphql-php)
}

$cacheConfig = $this->configRepository->get('lighthouse.validation_cache');

if (! isset($cacheConfig['enable']) || ! $cacheConfig['enable']) {
return DocumentValidator::validate($schema, $query, $validationRules);
return DocumentValidator::validate($schema, $query, $validationRules); // @phpstan-ignore return.type (TODO remove ignore when requiring a newer version of webonyx/graphql-php)
}

$cacheKey = "lighthouse:validation:{$schemaHash}:{$queryHash}";
Expand All @@ -429,7 +429,7 @@ protected function validateCacheableRules(
// As of webonyx/graphql-php 15.14.0, GraphQL\Error\Error is not serializable.
// We would have to figure out how to serialize them properly to cache them.
if ($result !== []) {
return $result;
return $result; // @phpstan-ignore return.type (TODO remove ignore when requiring a newer version of webonyx/graphql-php)
}

$store->put($cacheKey, $result, $cacheConfig['ttl']);
Expand Down
4 changes: 2 additions & 2 deletions src/Schema/AST/ASTCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

/**
* @phpstan-type CacheConfig array{
* enable: bool,
* path: string|null,
* enable: bool|int,
* path?: ?string,
* }
*
* @phpstan-import-type SerializableDocumentAST from DocumentAST
Expand Down
2 changes: 1 addition & 1 deletion src/Schema/Directives/RelationDirectiveHelpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protected function relation(): string
/**
* @param array<string, mixed> $args
*
* @return \Closure(QueryBuilder|\Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>|\Illuminate\Database\Eloquent\Relations\Relation<\Illuminate\Database\Eloquent\Model>, ?mixed = null): void
* @return \Closure(\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>|\Illuminate\Database\Eloquent\Relations\Relation<\Illuminate\Database\Eloquent\Model>, mixed=): void
*/
protected function makeBuilderDecorator(mixed $root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): \Closure
{
Expand Down
2 changes: 1 addition & 1 deletion src/Schema/Types/Scalars/Date.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ protected function format(Carbon $carbon): string
return $carbon->toDateString();
}

protected function parse(mixed $value): Carbon
protected function parse(string $value): Carbon
{
// @phpstan-ignore-next-line We know the format to be good, so this can never return `false`
return Carbon::createFromFormat('Y-m-d', $value)->startOfDay();
Expand Down
8 changes: 6 additions & 2 deletions src/Schema/Types/Scalars/DateScalar.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ protected function tryParsingDate(mixed $value, string $exceptionClass): Illumin
}
}

if (! is_string($value)) {
throw new $exceptionClass('Query error: Can only parse strings.');
}

return $this->parse($value);
} catch (\Exception $exception) {
throw new $exceptionClass($exception->getMessage());
Expand All @@ -92,7 +96,7 @@ abstract protected function format(IlluminateCarbon $carbon): string;
/**
* Try turning a client value into a Carbon instance.
*
* @param mixed $value a possibly faulty client value
* @param string $value a possibly faulty client value
*/
abstract protected function parse(mixed $value): IlluminateCarbon;
abstract protected function parse(string $value): IlluminateCarbon;
}
2 changes: 1 addition & 1 deletion src/Schema/Types/Scalars/DateTime.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ protected function format(Carbon $carbon): string
return $carbon->toDateTimeString();
}

protected function parse(mixed $value): Carbon
protected function parse(string $value): Carbon
{
// @phpstan-ignore-next-line We know the format to be good, so this can never return `false`
return Carbon::createFromFormat(Carbon::DEFAULT_TO_STRING_FORMAT, $value);
Expand Down
2 changes: 1 addition & 1 deletion src/Schema/Types/Scalars/DateTimeTz.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ protected function format(Carbon $carbon): string
return $carbon->toIso8601String();
}

protected function parse(mixed $value): Carbon
protected function parse(string $value): Carbon
{
// @phpstan-ignore-next-line We know the format to be good, so this can never return `false`
return Carbon::createFromFormat(
Expand Down
4 changes: 2 additions & 2 deletions src/Schema/Types/Scalars/DateTimeUtc.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Illuminate\Support\Carbon;

/**
* Only works with Carbon 2.
* Works with Carbon 2.x & 3.x.
*/
class DateTimeUtc extends DateScalar
{
Expand All @@ -14,7 +14,7 @@ protected function format(Carbon $carbon): string
return $carbon->toJSON();
}

protected function parse(mixed $value): Carbon
protected function parse(string $value): Carbon
{
// @phpstan-ignore-next-line We know the format to be good, so this can never return `false`
return Carbon::createFromIsoFormat('YYYY-MM-DDTHH:mm:ss.SSSSSSZ', $value);
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Schema/Directives/ComplexityDirectiveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function testDefaultComplexity(): void
$this->assertCount(1, $events);

// TODO remove this check when updating the required version of webonyx/graphql-php
if (method_exists(QueryComplexity::class, 'getQueryComplexity')) {
if (method_exists(QueryComplexity::class, 'getQueryComplexity')) { // @phpstan-ignore function.alreadyNarrowedType (depends on the used library version)
$event = $events[0];
$this->assertSame(2, $event->queryComplexity);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ final class RedisStorageManagerTest extends TestCase
* @param array<mixed> $firstCallArguments
* @param array<mixed> ...$consecutiveCallsArguments
*
* @return iterable<\PHPUnit\Framework\Constraint\Callback<mixed>>
* @return iterable<int, \PHPUnit\Framework\Constraint\Callback<mixed>>
*/
private function withConsecutive(array $firstCallArguments, array ...$consecutiveCallsArguments): iterable
{
Expand Down
2 changes: 1 addition & 1 deletion tests/Utils/Models/Category.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function parent(): BelongsTo
return $this->belongsTo(Category::class, 'parent_id');
}

/** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\Post, $this> */
/** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\Post, $this, \Illuminate\Database\Eloquent\Relations\Pivot> */
public function posts(): BelongsToMany
{
return $this->belongsToMany(Post::class, 'category_post', 'post_id', 'category_id');
Expand Down
Loading