From 7f7d507f7af2354c9497708820784f2b5fb8c5d0 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 14:30:12 +0530 Subject: [PATCH 01/13] Add OAuth2 resource indicator helpers --- README.md | 29 ++++++- src/Auth/Issuers/Asymmetric/AccessToken.php | 4 +- src/Auth/OAuth2/ResourceIndicators.php | 85 ++++++++++++++++++ .../Issuers/Asymmetric/AccessTokenTest.php | 9 ++ tests/Auth/OAuth2/ResourceIndicatorsTest.php | 86 +++++++++++++++++++ 5 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 src/Auth/OAuth2/ResourceIndicators.php create mode 100644 tests/Auth/OAuth2/ResourceIndicatorsTest.php diff --git a/README.md b/README.md index b3df7dc..ace3ab8 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,16 @@ $jwt = $accessToken->issue( scopes: ['openid', 'profile', 'email'] ); +// RFC 9068 also allows multiple resource-server audiences. +$jwt = $accessToken->issue( + subject: 'user-123', + audience: ['https://api.example.com', 'https://mcp.example.com'], + clientId: 'client-abc', + authTime: time(), + duration: 3600, + scopes: ['openid', 'profile'] +); + // Publish the public key as a JWK so resource servers can verify tokens $jwk = $accessToken->getPublicJwk(); $keyId = $accessToken->getKeyId(); @@ -256,6 +266,23 @@ $jwt = $idToken->issue( > Both asymmetric and symmetric issuers accept an optional `keyId` constructor argument (the JWS `kid` header) for key rotation. For asymmetric issuers it is derived deterministically from the public key when omitted. +#### OAuth2 Resource Indicators (RFC 8707) + +```php + $audience The "aud" claim (the resource server identifier). * @param string $clientId The "client_id" claim (the client the token was issued to). * @param int $authTime Time the end-user authenticated ("auth_time"), as a Unix timestamp. * @param int $duration Lifetime of the token in seconds (used for "exp"). @@ -46,7 +46,7 @@ protected function getType(): string */ public function issue( string $subject, - string $audience, + string|array $audience, string $clientId, int $authTime, int $duration, diff --git a/src/Auth/OAuth2/ResourceIndicators.php b/src/Auth/OAuth2/ResourceIndicators.php new file mode 100644 index 0000000..bcd25c2 --- /dev/null +++ b/src/Auth/OAuth2/ResourceIndicators.php @@ -0,0 +1,85 @@ + + */ + public static function normalize(mixed $value): array + { + if ($value === null || $value === '') { + return []; + } + + $resources = \is_array($value) ? $value : [$value]; + $normalized = []; + + foreach ($resources as $resource) { + if (!\is_string($resource) || $resource === '') { + throw new \InvalidArgumentException('resource must be a non-empty absolute URI.'); + } + + if (!self::isValid($resource)) { + throw new \InvalidArgumentException('resource must be an absolute URI without a fragment.'); + } + + if (!\in_array($resource, $normalized, true)) { + $normalized[] = $resource; + } + } + + return $normalized; + } + + public static function isValid(string $resource): bool + { + $parts = \parse_url($resource); + + return \is_array($parts) + && !empty($parts['scheme']) + && !isset($parts['fragment']) + && (!empty($parts['host']) || !empty($parts['path'])); + } + + /** + * @param array $requested + * @param array $granted + */ + public static function isSubset(array $requested, array $granted): bool + { + return empty(\array_diff($requested, $granted)); + } + + /** + * @param array $left + * @param array $right + */ + public static function sameSet(array $left, array $right): bool + { + $sortedLeft = $left; + $sortedRight = $right; + \sort($sortedLeft, \SORT_STRING); + \sort($sortedRight, \SORT_STRING); + + return $sortedLeft === $sortedRight; + } + + /** + * @param array $resources + * @return string|array + */ + public static function audience(string $defaultAudience, array $resources): string|array + { + if (empty($resources)) { + return $defaultAudience; + } + + $resourcesWithoutDefault = \array_values( + \array_filter($resources, fn ($resource) => $resource !== $defaultAudience) + ); + + return \array_values(\array_merge([$defaultAudience], $resourcesWithoutDefault)); + } +} diff --git a/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php b/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php index 8039cf1..82b5f09 100644 --- a/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php +++ b/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php @@ -68,6 +68,15 @@ public function testClaims(): void $this->assertMatchesRegularExpression('/^[a-f0-9]{32}$/', $jti); } + public function testArrayAudience(): void + { + $audience = ['https://api.example.com', 'https://mcp.example.com']; + $token = $this->accessToken->issue('user-123', $audience, 'client-abc', 1000, 3600); + $claims = $this->decodeSegment(\explode('.', $token)[1]); + + $this->assertEquals($audience, $claims['aud']); + } + public function testSignatureIsValid(): void { $token = $this->accessToken->issue('user-123', 'https://api.example.com', 'client-abc', 1000, 3600); diff --git a/tests/Auth/OAuth2/ResourceIndicatorsTest.php b/tests/Auth/OAuth2/ResourceIndicatorsTest.php new file mode 100644 index 0000000..7f46088 --- /dev/null +++ b/tests/Auth/OAuth2/ResourceIndicatorsTest.php @@ -0,0 +1,86 @@ +assertSame([], ResourceIndicators::normalize(null)); + $this->assertSame([], ResourceIndicators::normalize('')); + } + + public function testStringNormalizesToArray(): void + { + $this->assertSame(['https://api.example.com/'], ResourceIndicators::normalize('https://api.example.com/')); + } + + public function testArrayIsNormalizedAndDeduplicated(): void + { + $this->assertSame( + ['https://api.example.com/', 'urn:example:resource'], + ResourceIndicators::normalize([ + 'https://api.example.com/', + 'urn:example:resource', + 'https://api.example.com/', + ]) + ); + } + + public function testFragmentIsRejected(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('resource must be an absolute URI without a fragment.'); + + ResourceIndicators::normalize('https://api.example.com/#section'); + } + + public function testRelativeUriIsRejected(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('resource must be an absolute URI without a fragment.'); + + ResourceIndicators::normalize('/relative'); + } + + public function testNonStringIsRejected(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('resource must be a non-empty absolute URI.'); + + ResourceIndicators::normalize(['https://api.example.com/', 42]); + } + + public function testSubset(): void + { + $this->assertTrue(ResourceIndicators::isSubset(['https://api.example.com/'], ['https://api.example.com/', 'https://files.example.com/'])); + $this->assertFalse(ResourceIndicators::isSubset(['https://api.example.com/'], ['https://files.example.com/'])); + } + + public function testSameSetIgnoresOrder(): void + { + $this->assertTrue(ResourceIndicators::sameSet( + ['https://api.example.com/', 'https://files.example.com/'], + ['https://files.example.com/', 'https://api.example.com/'] + )); + } + + public function testAudienceUsesDefaultWhenNoResourcesRequested(): void + { + $this->assertSame('https://cloud.appwrite.io/v1/project1', ResourceIndicators::audience('https://cloud.appwrite.io/v1/project1', [])); + } + + public function testAudiencePrependsDefaultAndDeduplicatesIt(): void + { + $this->assertSame( + ['https://cloud.appwrite.io/v1/project1', 'https://mcp.example.com/'], + ResourceIndicators::audience('https://cloud.appwrite.io/v1/project1', [ + 'https://mcp.example.com/', + 'https://cloud.appwrite.io/v1/project1', + ]) + ); + } +} From ee7563a84eafc9cb3ae044e4bfef1ac50ad2bc2e Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 14:38:15 +0530 Subject: [PATCH 02/13] Fix single resource audience shape --- src/Auth/OAuth2/ResourceIndicators.php | 8 +++----- tests/Auth/OAuth2/ResourceIndicatorsTest.php | 10 ++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Auth/OAuth2/ResourceIndicators.php b/src/Auth/OAuth2/ResourceIndicators.php index bcd25c2..04b14f7 100644 --- a/src/Auth/OAuth2/ResourceIndicators.php +++ b/src/Auth/OAuth2/ResourceIndicators.php @@ -72,14 +72,12 @@ public static function sameSet(array $left, array $right): bool */ public static function audience(string $defaultAudience, array $resources): string|array { - if (empty($resources)) { - return $defaultAudience; - } - $resourcesWithoutDefault = \array_values( \array_filter($resources, fn ($resource) => $resource !== $defaultAudience) ); - return \array_values(\array_merge([$defaultAudience], $resourcesWithoutDefault)); + $audience = \array_values(\array_merge([$defaultAudience], $resourcesWithoutDefault)); + + return \count($audience) === 1 ? $audience[0] : $audience; } } diff --git a/tests/Auth/OAuth2/ResourceIndicatorsTest.php b/tests/Auth/OAuth2/ResourceIndicatorsTest.php index 7f46088..dbd2b0e 100644 --- a/tests/Auth/OAuth2/ResourceIndicatorsTest.php +++ b/tests/Auth/OAuth2/ResourceIndicatorsTest.php @@ -83,4 +83,14 @@ public function testAudiencePrependsDefaultAndDeduplicatesIt(): void ]) ); } + + public function testAudienceReturnsStringWhenOnlyDefaultAudienceRemains(): void + { + $this->assertSame( + 'https://cloud.appwrite.io/v1/project1', + ResourceIndicators::audience('https://cloud.appwrite.io/v1/project1', [ + 'https://cloud.appwrite.io/v1/project1', + ]) + ); + } } From 53948b2c6f34170044f8d6f2663d0e42e2ccc7b4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 15:38:18 +0530 Subject: [PATCH 03/13] Refine resource indicators API --- README.md | 10 ++- src/Auth/OAuth2/ResourceIndicators.php | 78 +++++++++++--------- tests/Auth/OAuth2/ResourceIndicatorsTest.php | 47 +++++++----- 3 files changed, 79 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index ace3ab8..42e60d4 100644 --- a/README.md +++ b/README.md @@ -273,14 +273,16 @@ $jwt = $idToken->issue( use Utopia\Auth\OAuth2\ResourceIndicators; -$resources = ResourceIndicators::normalize([ +$resources = ResourceIndicators::from([ 'https://api.example.com/', 'https://mcp.example.com/', ]); +$previouslyGrantedResources = ResourceIndicators::from(['https://api.example.com/']); -$isAllowed = ResourceIndicators::isSubset($resources, $previouslyGrantedResources); -$unchanged = ResourceIndicators::sameSet($resources, $previouslyGrantedResources); -$audience = ResourceIndicators::audience('https://cloud.example.com/v1/project', $resources); +$isAllowed = $resources->isSubsetOf($previouslyGrantedResources); +$unchanged = $resources->equals($previouslyGrantedResources); +$audience = $resources->audience('https://cloud.example.com/v1/project'); +$serialized = $resources->toArray(); ``` ## Tests diff --git a/src/Auth/OAuth2/ResourceIndicators.php b/src/Auth/OAuth2/ResourceIndicators.php index 04b14f7..287e0d3 100644 --- a/src/Auth/OAuth2/ResourceIndicators.php +++ b/src/Auth/OAuth2/ResourceIndicators.php @@ -5,12 +5,25 @@ class ResourceIndicators { /** - * @return array + * @var array + */ + private array $resources; + + /** + * @param array $resources + */ + private function __construct(array $resources) + { + $this->resources = $resources; + } + + /** + * @param string|array|null $value */ - public static function normalize(mixed $value): array + public static function from(string|array|null $value): self { if ($value === null || $value === '') { - return []; + return new self([]); } $resources = \is_array($value) ? $value : [$value]; @@ -30,54 +43,53 @@ public static function normalize(mixed $value): array } } - return $normalized; + return new self($normalized); } - public static function isValid(string $resource): bool + public function isSubsetOf(self $granted): bool { - $parts = \parse_url($resource); - - return \is_array($parts) - && !empty($parts['scheme']) - && !isset($parts['fragment']) - && (!empty($parts['host']) || !empty($parts['path'])); + return empty(\array_diff($this->resources, $granted->resources)); } - /** - * @param array $requested - * @param array $granted - */ - public static function isSubset(array $requested, array $granted): bool + public function equals(self $resources): bool { - return empty(\array_diff($requested, $granted)); - } + $left = $this->resources; + $right = $resources->resources; + \sort($left, \SORT_STRING); + \sort($right, \SORT_STRING); - /** - * @param array $left - * @param array $right - */ - public static function sameSet(array $left, array $right): bool - { - $sortedLeft = $left; - $sortedRight = $right; - \sort($sortedLeft, \SORT_STRING); - \sort($sortedRight, \SORT_STRING); - - return $sortedLeft === $sortedRight; + return $left === $right; } /** - * @param array $resources * @return string|array */ - public static function audience(string $defaultAudience, array $resources): string|array + public function audience(string $defaultAudience): string|array { $resourcesWithoutDefault = \array_values( - \array_filter($resources, fn ($resource) => $resource !== $defaultAudience) + \array_filter($this->resources, fn ($resource) => $resource !== $defaultAudience) ); $audience = \array_values(\array_merge([$defaultAudience], $resourcesWithoutDefault)); return \count($audience) === 1 ? $audience[0] : $audience; } + + /** + * @return array + */ + public function toArray(): array + { + return $this->resources; + } + + private static function isValid(string $resource): bool + { + $parts = \parse_url($resource); + + return \is_array($parts) + && !empty($parts['scheme']) + && !isset($parts['fragment']) + && (!empty($parts['host']) || !empty($parts['path'])); + } } diff --git a/tests/Auth/OAuth2/ResourceIndicatorsTest.php b/tests/Auth/OAuth2/ResourceIndicatorsTest.php index dbd2b0e..a11e38c 100644 --- a/tests/Auth/OAuth2/ResourceIndicatorsTest.php +++ b/tests/Auth/OAuth2/ResourceIndicatorsTest.php @@ -9,24 +9,24 @@ class ResourceIndicatorsTest extends TestCase { public function testEmptyValueNormalizesToEmptyArray(): void { - $this->assertSame([], ResourceIndicators::normalize(null)); - $this->assertSame([], ResourceIndicators::normalize('')); + $this->assertSame([], ResourceIndicators::from(null)->toArray()); + $this->assertSame([], ResourceIndicators::from('')->toArray()); } public function testStringNormalizesToArray(): void { - $this->assertSame(['https://api.example.com/'], ResourceIndicators::normalize('https://api.example.com/')); + $this->assertSame(['https://api.example.com/'], ResourceIndicators::from('https://api.example.com/')->toArray()); } public function testArrayIsNormalizedAndDeduplicated(): void { $this->assertSame( ['https://api.example.com/', 'urn:example:resource'], - ResourceIndicators::normalize([ + ResourceIndicators::from([ 'https://api.example.com/', 'urn:example:resource', 'https://api.example.com/', - ]) + ])->toArray() ); } @@ -35,7 +35,7 @@ public function testFragmentIsRejected(): void $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('resource must be an absolute URI without a fragment.'); - ResourceIndicators::normalize('https://api.example.com/#section'); + ResourceIndicators::from('https://api.example.com/#section'); } public function testRelativeUriIsRejected(): void @@ -43,7 +43,7 @@ public function testRelativeUriIsRejected(): void $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('resource must be an absolute URI without a fragment.'); - ResourceIndicators::normalize('/relative'); + ResourceIndicators::from('/relative'); } public function testNonStringIsRejected(): void @@ -51,36 +51,45 @@ public function testNonStringIsRejected(): void $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('resource must be a non-empty absolute URI.'); - ResourceIndicators::normalize(['https://api.example.com/', 42]); + ResourceIndicators::from(['https://api.example.com/', 42]); } public function testSubset(): void { - $this->assertTrue(ResourceIndicators::isSubset(['https://api.example.com/'], ['https://api.example.com/', 'https://files.example.com/'])); - $this->assertFalse(ResourceIndicators::isSubset(['https://api.example.com/'], ['https://files.example.com/'])); + $this->assertTrue( + ResourceIndicators::from(['https://api.example.com/']) + ->isSubsetOf(ResourceIndicators::from(['https://api.example.com/', 'https://files.example.com/'])) + ); + $this->assertFalse( + ResourceIndicators::from(['https://api.example.com/']) + ->isSubsetOf(ResourceIndicators::from(['https://files.example.com/'])) + ); } public function testSameSetIgnoresOrder(): void { - $this->assertTrue(ResourceIndicators::sameSet( - ['https://api.example.com/', 'https://files.example.com/'], - ['https://files.example.com/', 'https://api.example.com/'] - )); + $this->assertTrue( + ResourceIndicators::from(['https://api.example.com/', 'https://files.example.com/']) + ->equals(ResourceIndicators::from(['https://files.example.com/', 'https://api.example.com/'])) + ); } public function testAudienceUsesDefaultWhenNoResourcesRequested(): void { - $this->assertSame('https://cloud.appwrite.io/v1/project1', ResourceIndicators::audience('https://cloud.appwrite.io/v1/project1', [])); + $this->assertSame( + 'https://cloud.appwrite.io/v1/project1', + ResourceIndicators::from(null)->audience('https://cloud.appwrite.io/v1/project1') + ); } public function testAudiencePrependsDefaultAndDeduplicatesIt(): void { $this->assertSame( ['https://cloud.appwrite.io/v1/project1', 'https://mcp.example.com/'], - ResourceIndicators::audience('https://cloud.appwrite.io/v1/project1', [ + ResourceIndicators::from([ 'https://mcp.example.com/', 'https://cloud.appwrite.io/v1/project1', - ]) + ])->audience('https://cloud.appwrite.io/v1/project1') ); } @@ -88,9 +97,9 @@ public function testAudienceReturnsStringWhenOnlyDefaultAudienceRemains(): void { $this->assertSame( 'https://cloud.appwrite.io/v1/project1', - ResourceIndicators::audience('https://cloud.appwrite.io/v1/project1', [ + ResourceIndicators::from([ 'https://cloud.appwrite.io/v1/project1', - ]) + ])->audience('https://cloud.appwrite.io/v1/project1') ); } } From 0de36673eb951ec0c1ef544a2f404c764f4478f3 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 17:17:19 +0530 Subject: [PATCH 04/13] Refine access token audience handling --- README.md | 2 +- src/Auth/Issuers/Asymmetric/AccessToken.php | 4 +- src/Auth/OAuth2/ResourceIndicators.php | 28 ++++++- .../Issuers/Asymmetric/AccessTokenTest.php | 26 +++---- tests/Auth/OAuth2/ResourceIndicatorsTest.php | 74 +++++++++---------- 5 files changed, 75 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 42e60d4..336c284 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ $accessToken = new AccessToken( // Issue a signed RS256 access token $jwt = $accessToken->issue( subject: 'user-123', // "sub" — the resource owner - audience: 'https://api.example.com', // "aud" — the resource server + audience: ['https://api.example.com'], // "aud" — the resource server clientId: 'client-abc', // "client_id" — the client it was issued to authTime: time(), // "auth_time" — when the user authenticated duration: 3600, // Lifetime in seconds ("exp") diff --git a/src/Auth/Issuers/Asymmetric/AccessToken.php b/src/Auth/Issuers/Asymmetric/AccessToken.php index 1d27c2e..c704219 100644 --- a/src/Auth/Issuers/Asymmetric/AccessToken.php +++ b/src/Auth/Issuers/Asymmetric/AccessToken.php @@ -34,7 +34,7 @@ protected function getType(): string * authorization server, supplied to the constructor). * * @param string $subject The "sub" claim (the resource owner / user). - * @param string|array $audience The "aud" claim (the resource server identifier). + * @param array $audience The "aud" claim (the resource server identifiers). * @param string $clientId The "client_id" claim (the client the token was issued to). * @param int $authTime Time the end-user authenticated ("auth_time"), as a Unix timestamp. * @param int $duration Lifetime of the token in seconds (used for "exp"). @@ -46,7 +46,7 @@ protected function getType(): string */ public function issue( string $subject, - string|array $audience, + array $audience, string $clientId, int $authTime, int $duration, diff --git a/src/Auth/OAuth2/ResourceIndicators.php b/src/Auth/OAuth2/ResourceIndicators.php index 287e0d3..1edad9c 100644 --- a/src/Auth/OAuth2/ResourceIndicators.php +++ b/src/Auth/OAuth2/ResourceIndicators.php @@ -14,6 +14,28 @@ class ResourceIndicators */ private function __construct(array $resources) { + if ($resources !== \array_values($resources)) { + throw new \InvalidArgumentException('resources must be a list of absolute URIs.'); + } + + $seen = []; + + foreach ($resources as $resource) { + if (!\is_string($resource) || $resource === '') { + throw new \InvalidArgumentException('resource must be a non-empty absolute URI.'); + } + + if (!self::isValid($resource)) { + throw new \InvalidArgumentException('resource must be an absolute URI without a fragment.'); + } + + if (\in_array($resource, $seen, true)) { + throw new \InvalidArgumentException('resources must not contain duplicates.'); + } + + $seen[] = $resource; + } + $this->resources = $resources; } @@ -62,9 +84,9 @@ public function equals(self $resources): bool } /** - * @return string|array + * @return array */ - public function audience(string $defaultAudience): string|array + public function audience(string $defaultAudience): array { $resourcesWithoutDefault = \array_values( \array_filter($this->resources, fn ($resource) => $resource !== $defaultAudience) @@ -72,7 +94,7 @@ public function audience(string $defaultAudience): string|array $audience = \array_values(\array_merge([$defaultAudience], $resourcesWithoutDefault)); - return \count($audience) === 1 ? $audience[0] : $audience; + return $audience; } /** diff --git a/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php b/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php index 82b5f09..1686be0 100644 --- a/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php +++ b/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php @@ -37,7 +37,7 @@ private function decodeSegment(string $segment): array public function testHeaderType(): void { - $token = $this->accessToken->issue('user-123', 'https://api.example.com', 'client-abc', 1000, 3600); + $token = $this->accessToken->issue('user-123', ['https://api.example.com'], 'client-abc', 1000, 3600); $header = $this->decodeSegment(\explode('.', $token)[0]); // RFC 9068 §2.1: access tokens carry the "at+jwt" media type. @@ -49,13 +49,13 @@ public function testHeaderType(): void public function testClaims(): void { $before = \time(); - $token = $this->accessToken->issue('user-123', 'https://api.example.com', 'client-abc', 1000, 3600, ['read', 'write']); + $token = $this->accessToken->issue('user-123', ['https://api.example.com'], 'client-abc', 1000, 3600, ['read', 'write']); $after = \time(); $claims = $this->decodeSegment(\explode('.', $token)[1]); $this->assertEquals('https://example.com/v1/oauth2/test', $claims['iss']); - $this->assertEquals('https://api.example.com', $claims['aud']); + $this->assertEquals(['https://api.example.com'], $claims['aud']); $this->assertEquals('user-123', $claims['sub']); $this->assertEquals('client-abc', $claims['client_id']); $this->assertEquals('read write', $claims['scope']); @@ -68,7 +68,7 @@ public function testClaims(): void $this->assertMatchesRegularExpression('/^[a-f0-9]{32}$/', $jti); } - public function testArrayAudience(): void + public function testAudienceClaim(): void { $audience = ['https://api.example.com', 'https://mcp.example.com']; $token = $this->accessToken->issue('user-123', $audience, 'client-abc', 1000, 3600); @@ -79,7 +79,7 @@ public function testArrayAudience(): void public function testSignatureIsValid(): void { - $token = $this->accessToken->issue('user-123', 'https://api.example.com', 'client-abc', 1000, 3600); + $token = $this->accessToken->issue('user-123', ['https://api.example.com'], 'client-abc', 1000, 3600); $parts = \explode('.', $token); $result = \openssl_verify( @@ -94,7 +94,7 @@ public function testSignatureIsValid(): void public function testScopeOmittedWhenEmpty(): void { - $token = $this->accessToken->issue('user-123', 'https://api.example.com', 'client-abc', 1000, 3600); + $token = $this->accessToken->issue('user-123', ['https://api.example.com'], 'client-abc', 1000, 3600); $claims = $this->decodeSegment(\explode('.', $token)[1]); $this->assertArrayNotHasKey('scope', $claims); @@ -102,7 +102,7 @@ public function testScopeOmittedWhenEmpty(): void public function testScopeCannotBeInjectedViaClaimsWhenEmpty(): void { - $token = $this->accessToken->issue('user-123', 'https://api.example.com', 'client-abc', 1000, 3600, [], null, [ + $token = $this->accessToken->issue('user-123', ['https://api.example.com'], 'client-abc', 1000, 3600, [], null, [ 'scope' => 'admin', ]); $claims = $this->decodeSegment(\explode('.', $token)[1]); @@ -112,7 +112,7 @@ public function testScopeCannotBeInjectedViaClaimsWhenEmpty(): void public function testScopeCannotBeOverriddenViaClaims(): void { - $token = $this->accessToken->issue('user-123', 'https://api.example.com', 'client-abc', 1000, 3600, ['read'], null, [ + $token = $this->accessToken->issue('user-123', ['https://api.example.com'], 'client-abc', 1000, 3600, ['read'], null, [ 'scope' => 'admin', ]); $claims = $this->decodeSegment(\explode('.', $token)[1]); @@ -122,15 +122,15 @@ public function testScopeCannotBeOverriddenViaClaims(): void public function testJtiIsGeneratedAndUnique(): void { - $first = $this->decodeSegment(\explode('.', $this->accessToken->issue('user-123', 'aud', 'client', 1000, 3600))[1]); - $second = $this->decodeSegment(\explode('.', $this->accessToken->issue('user-123', 'aud', 'client', 1000, 3600))[1]); + $first = $this->decodeSegment(\explode('.', $this->accessToken->issue('user-123', ['aud'], 'client', 1000, 3600))[1]); + $second = $this->decodeSegment(\explode('.', $this->accessToken->issue('user-123', ['aud'], 'client', 1000, 3600))[1]); $this->assertNotEquals($first['jti'], $second['jti']); } public function testCustomJti(): void { - $token = $this->accessToken->issue('user-123', 'aud', 'client', 1000, 3600, [], 'fixed-jti'); + $token = $this->accessToken->issue('user-123', ['aud'], 'client', 1000, 3600, [], 'fixed-jti'); $claims = $this->decodeSegment(\explode('.', $token)[1]); $this->assertEquals('fixed-jti', $claims['jti']); @@ -138,7 +138,7 @@ public function testCustomJti(): void public function testAdditionalClaims(): void { - $token = $this->accessToken->issue('user-123', 'aud', 'client', 1000, 3600, [], null, [ + $token = $this->accessToken->issue('user-123', ['aud'], 'client', 1000, 3600, [], null, [ 'tokenId' => 'identity-row-1', ]); $claims = $this->decodeSegment(\explode('.', $token)[1]); @@ -148,7 +148,7 @@ public function testAdditionalClaims(): void public function testAdditionalClaimsCannotOverrideRegisteredClaims(): void { - $token = $this->accessToken->issue('user-123', 'aud', 'client', 1000, 3600, [], null, [ + $token = $this->accessToken->issue('user-123', ['aud'], 'client', 1000, 3600, [], null, [ 'sub' => 'attacker', 'iss' => 'https://evil.example.com', 'client_id' => 'evil', diff --git a/tests/Auth/OAuth2/ResourceIndicatorsTest.php b/tests/Auth/OAuth2/ResourceIndicatorsTest.php index a11e38c..e657b90 100644 --- a/tests/Auth/OAuth2/ResourceIndicatorsTest.php +++ b/tests/Auth/OAuth2/ResourceIndicatorsTest.php @@ -7,19 +7,12 @@ class ResourceIndicatorsTest extends TestCase { - public function testEmptyValueNormalizesToEmptyArray(): void + public function testNormalizesResources(): void { $this->assertSame([], ResourceIndicators::from(null)->toArray()); $this->assertSame([], ResourceIndicators::from('')->toArray()); - } - - public function testStringNormalizesToArray(): void - { $this->assertSame(['https://api.example.com/'], ResourceIndicators::from('https://api.example.com/')->toArray()); - } - public function testArrayIsNormalizedAndDeduplicated(): void - { $this->assertSame( ['https://api.example.com/', 'urn:example:resource'], ResourceIndicators::from([ @@ -30,31 +23,20 @@ public function testArrayIsNormalizedAndDeduplicated(): void ); } - public function testFragmentIsRejected(): void - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('resource must be an absolute URI without a fragment.'); - - ResourceIndicators::from('https://api.example.com/#section'); - } - - public function testRelativeUriIsRejected(): void - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('resource must be an absolute URI without a fragment.'); - - ResourceIndicators::from('/relative'); - } - - public function testNonStringIsRejected(): void + /** + * @param string|array $resources + * + * @dataProvider invalidResourceProvider + */ + public function testRejectsInvalidResources(string|array $resources, string $message): void { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('resource must be a non-empty absolute URI.'); + $this->expectExceptionMessage($message); - ResourceIndicators::from(['https://api.example.com/', 42]); + ResourceIndicators::from($resources); } - public function testSubset(): void + public function testComparesResourceSets(): void { $this->assertTrue( ResourceIndicators::from(['https://api.example.com/']) @@ -64,26 +46,20 @@ public function testSubset(): void ResourceIndicators::from(['https://api.example.com/']) ->isSubsetOf(ResourceIndicators::from(['https://files.example.com/'])) ); - } - public function testSameSetIgnoresOrder(): void - { $this->assertTrue( ResourceIndicators::from(['https://api.example.com/', 'https://files.example.com/']) ->equals(ResourceIndicators::from(['https://files.example.com/', 'https://api.example.com/'])) ); } - public function testAudienceUsesDefaultWhenNoResourcesRequested(): void + public function testBuildsAudience(): void { $this->assertSame( - 'https://cloud.appwrite.io/v1/project1', + ['https://cloud.appwrite.io/v1/project1'], ResourceIndicators::from(null)->audience('https://cloud.appwrite.io/v1/project1') ); - } - public function testAudiencePrependsDefaultAndDeduplicatesIt(): void - { $this->assertSame( ['https://cloud.appwrite.io/v1/project1', 'https://mcp.example.com/'], ResourceIndicators::from([ @@ -91,15 +67,33 @@ public function testAudiencePrependsDefaultAndDeduplicatesIt(): void 'https://cloud.appwrite.io/v1/project1', ])->audience('https://cloud.appwrite.io/v1/project1') ); - } - public function testAudienceReturnsStringWhenOnlyDefaultAudienceRemains(): void - { $this->assertSame( - 'https://cloud.appwrite.io/v1/project1', + ['https://cloud.appwrite.io/v1/project1'], ResourceIndicators::from([ 'https://cloud.appwrite.io/v1/project1', ])->audience('https://cloud.appwrite.io/v1/project1') ); } + + /** + * @return array, message: string}> + */ + public function invalidResourceProvider(): array + { + return [ + 'fragment' => [ + 'resources' => 'https://api.example.com/#section', + 'message' => 'resource must be an absolute URI without a fragment.', + ], + 'relative URI' => [ + 'resources' => '/relative', + 'message' => 'resource must be an absolute URI without a fragment.', + ], + 'non-string' => [ + 'resources' => ['https://api.example.com/', 42], + 'message' => 'resource must be a non-empty absolute URI.', + ], + ]; + } } From 751b4f267e0983960e1bdb9967ef1f157c394540 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 17:17:58 +0530 Subject: [PATCH 05/13] Use spread operator for audience list --- src/Auth/OAuth2/ResourceIndicators.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Auth/OAuth2/ResourceIndicators.php b/src/Auth/OAuth2/ResourceIndicators.php index 1edad9c..812df6a 100644 --- a/src/Auth/OAuth2/ResourceIndicators.php +++ b/src/Auth/OAuth2/ResourceIndicators.php @@ -92,7 +92,7 @@ public function audience(string $defaultAudience): array \array_filter($this->resources, fn ($resource) => $resource !== $defaultAudience) ); - $audience = \array_values(\array_merge([$defaultAudience], $resourcesWithoutDefault)); + $audience = [$defaultAudience, ...$resourcesWithoutDefault]; return $audience; } From dd8f8badbc873c576840c0a4b6f9b4a9d46ee9fb Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 17:18:37 +0530 Subject: [PATCH 06/13] Honor requested resource audiences --- README.md | 1 + src/Auth/OAuth2/ResourceIndicators.php | 10 ++++------ tests/Auth/OAuth2/ResourceIndicatorsTest.php | 3 +-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 336c284..9d56727 100644 --- a/README.md +++ b/README.md @@ -281,6 +281,7 @@ $previouslyGrantedResources = ResourceIndicators::from(['https://api.example.com $isAllowed = $resources->isSubsetOf($previouslyGrantedResources); $unchanged = $resources->equals($previouslyGrantedResources); +// Falls back to the default only when no resource was requested. $audience = $resources->audience('https://cloud.example.com/v1/project'); $serialized = $resources->toArray(); ``` diff --git a/src/Auth/OAuth2/ResourceIndicators.php b/src/Auth/OAuth2/ResourceIndicators.php index 812df6a..02037a1 100644 --- a/src/Auth/OAuth2/ResourceIndicators.php +++ b/src/Auth/OAuth2/ResourceIndicators.php @@ -88,13 +88,11 @@ public function equals(self $resources): bool */ public function audience(string $defaultAudience): array { - $resourcesWithoutDefault = \array_values( - \array_filter($this->resources, fn ($resource) => $resource !== $defaultAudience) - ); - - $audience = [$defaultAudience, ...$resourcesWithoutDefault]; + if ($this->resources === []) { + return [$defaultAudience]; + } - return $audience; + return $this->resources; } /** diff --git a/tests/Auth/OAuth2/ResourceIndicatorsTest.php b/tests/Auth/OAuth2/ResourceIndicatorsTest.php index e657b90..6c1357b 100644 --- a/tests/Auth/OAuth2/ResourceIndicatorsTest.php +++ b/tests/Auth/OAuth2/ResourceIndicatorsTest.php @@ -61,10 +61,9 @@ public function testBuildsAudience(): void ); $this->assertSame( - ['https://cloud.appwrite.io/v1/project1', 'https://mcp.example.com/'], + ['https://mcp.example.com/'], ResourceIndicators::from([ 'https://mcp.example.com/', - 'https://cloud.appwrite.io/v1/project1', ])->audience('https://cloud.appwrite.io/v1/project1') ); From 5f3987830ec923d6374d8005b09ddd4d06b4af48 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 17:19:11 +0530 Subject: [PATCH 07/13] Remove duplicate resource validation --- src/Auth/OAuth2/ResourceIndicators.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Auth/OAuth2/ResourceIndicators.php b/src/Auth/OAuth2/ResourceIndicators.php index 02037a1..c2bc346 100644 --- a/src/Auth/OAuth2/ResourceIndicators.php +++ b/src/Auth/OAuth2/ResourceIndicators.php @@ -52,14 +52,6 @@ public static function from(string|array|null $value): self $normalized = []; foreach ($resources as $resource) { - if (!\is_string($resource) || $resource === '') { - throw new \InvalidArgumentException('resource must be a non-empty absolute URI.'); - } - - if (!self::isValid($resource)) { - throw new \InvalidArgumentException('resource must be an absolute URI without a fragment.'); - } - if (!\in_array($resource, $normalized, true)) { $normalized[] = $resource; } From fe454ed47fa43ea33a4b43690c3be116c16ed5c2 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 17:22:16 +0530 Subject: [PATCH 08/13] Surface invalid resource error code --- README.md | 4 ++++ src/Auth/OAuth2/InvalidResourceException.php | 11 +++++++++++ src/Auth/OAuth2/ResourceIndicators.php | 19 ++++++++++--------- tests/Auth/OAuth2/ResourceIndicatorsTest.php | 5 ++++- 4 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 src/Auth/OAuth2/InvalidResourceException.php diff --git a/README.md b/README.md index 9d56727..ca26874 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,7 @@ $jwt = $idToken->issue( ```php equals($previouslyGrantedResources); // Falls back to the default only when no resource was requested. $audience = $resources->audience('https://cloud.example.com/v1/project'); $serialized = $resources->toArray(); + +// Invalid resources throw InvalidResourceException with the RFC 8707 error code: +// InvalidResourceException::ERROR_CODE === 'invalid_target' ``` ## Tests diff --git a/src/Auth/OAuth2/InvalidResourceException.php b/src/Auth/OAuth2/InvalidResourceException.php new file mode 100644 index 0000000..d73e386 --- /dev/null +++ b/src/Auth/OAuth2/InvalidResourceException.php @@ -0,0 +1,11 @@ +|null $value + * + * @throws InvalidResourceException */ public static function from(string|array|null $value): self { diff --git a/tests/Auth/OAuth2/ResourceIndicatorsTest.php b/tests/Auth/OAuth2/ResourceIndicatorsTest.php index 6c1357b..221b955 100644 --- a/tests/Auth/OAuth2/ResourceIndicatorsTest.php +++ b/tests/Auth/OAuth2/ResourceIndicatorsTest.php @@ -3,6 +3,7 @@ namespace Utopia\Tests\Auth\OAuth2; use PHPUnit\Framework\TestCase; +use Utopia\Auth\OAuth2\InvalidResourceException; use Utopia\Auth\OAuth2\ResourceIndicators; class ResourceIndicatorsTest extends TestCase @@ -30,7 +31,9 @@ public function testNormalizesResources(): void */ public function testRejectsInvalidResources(string|array $resources, string $message): void { - $this->expectException(\InvalidArgumentException::class); + $this->assertSame('invalid_target', InvalidResourceException::ERROR_CODE); + + $this->expectException(InvalidResourceException::class); $this->expectExceptionMessage($message); ResourceIndicators::from($resources); From 85b03308601a65d4ec5082ae53a3dc5226df0934 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 17:26:52 +0530 Subject: [PATCH 09/13] Document resource downscope check --- README.md | 1 + src/Auth/OAuth2/ResourceIndicators.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/README.md b/README.md index ca26874..96b2471 100644 --- a/README.md +++ b/README.md @@ -280,6 +280,7 @@ $resources = ResourceIndicators::from([ ]); $previouslyGrantedResources = ResourceIndicators::from(['https://api.example.com/']); +// Refresh/downscope requests must stay within the previously granted resources. $isAllowed = $resources->isSubsetOf($previouslyGrantedResources); $unchanged = $resources->equals($previouslyGrantedResources); // Falls back to the default only when no resource was requested. diff --git a/src/Auth/OAuth2/ResourceIndicators.php b/src/Auth/OAuth2/ResourceIndicators.php index eadc6d9..2e7f459 100644 --- a/src/Auth/OAuth2/ResourceIndicators.php +++ b/src/Auth/OAuth2/ResourceIndicators.php @@ -61,6 +61,10 @@ public static function from(string|array|null $value): self return new self($normalized); } + /** + * Requested resources must be a subset of previously granted resources; + * use this on refresh and token requests per RFC 8707 Section 2.2. + */ public function isSubsetOf(self $granted): bool { return empty(\array_diff($this->resources, $granted->resources)); From 886d8b20bfa75861b77bb6707c05e6524f2258ed Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 17:27:27 +0530 Subject: [PATCH 10/13] Clarify resource URI validation message --- src/Auth/OAuth2/ResourceIndicators.php | 2 +- tests/Auth/OAuth2/ResourceIndicatorsTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Auth/OAuth2/ResourceIndicators.php b/src/Auth/OAuth2/ResourceIndicators.php index 2e7f459..1cf4449 100644 --- a/src/Auth/OAuth2/ResourceIndicators.php +++ b/src/Auth/OAuth2/ResourceIndicators.php @@ -26,7 +26,7 @@ private function __construct(array $resources) throw new InvalidResourceException('resource must be a non-empty absolute URI.'); case \is_string($resource) && !self::isValid($resource): - throw new InvalidResourceException('resource must be an absolute URI without a fragment.'); + throw new InvalidResourceException('resource must be an absolute URI with no fragment component.'); case \in_array($resource, $seen, true): throw new InvalidResourceException('resources must not contain duplicates.'); diff --git a/tests/Auth/OAuth2/ResourceIndicatorsTest.php b/tests/Auth/OAuth2/ResourceIndicatorsTest.php index 221b955..319af76 100644 --- a/tests/Auth/OAuth2/ResourceIndicatorsTest.php +++ b/tests/Auth/OAuth2/ResourceIndicatorsTest.php @@ -86,11 +86,11 @@ public function invalidResourceProvider(): array return [ 'fragment' => [ 'resources' => 'https://api.example.com/#section', - 'message' => 'resource must be an absolute URI without a fragment.', + 'message' => 'resource must be an absolute URI with no fragment component.', ], 'relative URI' => [ 'resources' => '/relative', - 'message' => 'resource must be an absolute URI without a fragment.', + 'message' => 'resource must be an absolute URI with no fragment component.', ], 'non-string' => [ 'resources' => ['https://api.example.com/', 42], From ac8709da5c5867bc10e7669649ab2a42d74bc2c1 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 17:28:17 +0530 Subject: [PATCH 11/13] Simplify resource indicators README example --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index 96b2471..a8a564c 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,6 @@ $jwt = $accessToken->issue( scopes: ['openid', 'profile', 'email'] ); -// RFC 9068 also allows multiple resource-server audiences. $jwt = $accessToken->issue( subject: 'user-123', audience: ['https://api.example.com', 'https://mcp.example.com'], @@ -271,7 +270,6 @@ $jwt = $idToken->issue( ```php isSubsetOf($previouslyGrantedResources); $unchanged = $resources->equals($previouslyGrantedResources); -// Falls back to the default only when no resource was requested. $audience = $resources->audience('https://cloud.example.com/v1/project'); $serialized = $resources->toArray(); - -// Invalid resources throw InvalidResourceException with the RFC 8707 error code: -// InvalidResourceException::ERROR_CODE === 'invalid_target' ``` ## Tests From 9a15057bf3e0dcfda5e1b2197ca04e7bc1147d72 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 17:29:55 +0530 Subject: [PATCH 12/13] Fix resource indicator PHPStan type --- src/Auth/OAuth2/ResourceIndicators.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Auth/OAuth2/ResourceIndicators.php b/src/Auth/OAuth2/ResourceIndicators.php index 1cf4449..0dac945 100644 --- a/src/Auth/OAuth2/ResourceIndicators.php +++ b/src/Auth/OAuth2/ResourceIndicators.php @@ -10,7 +10,7 @@ class ResourceIndicators private array $resources; /** - * @param array $resources + * @param array $resources */ private function __construct(array $resources) { @@ -35,6 +35,7 @@ private function __construct(array $resources) $seen[] = $resource; } + /** @var array $resources */ $this->resources = $resources; } From 9ee4a93359ff1accabe1fff8ef32896bb413d336 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 19 Jun 2026 17:36:54 +0530 Subject: [PATCH 13/13] Consolidate CI workflow --- .github/workflows/ci.yml | 59 + .github/workflows/code-ql-analysis.yml | 20 - .github/workflows/linter.yml | 20 - .github/workflows/tests.yml | 24 - README.md | 4 +- composer.json | 9 +- composer.lock | 3822 ++++------------- src/Auth/Hashes/Scrypt.php | 32 +- src/Auth/Hashes/ScryptModified.php | 7 +- src/Auth/Issuers/Asymmetric/AccessToken.php | 14 + .../Issuers/Asymmetric/AccessTokenTest.php | 8 + 11 files changed, 978 insertions(+), 3041 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/code-ql-analysis.yml delete mode 100644 .github/workflows/linter.yml delete mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f4424b8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: "CI" + +on: [pull_request] + +jobs: + codeql: + name: CodeQL + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + with: + fetch-depth: 2 + + - run: git checkout HEAD^2 + + - name: Run CodeQL + run: | + docker run --rm -v $PWD:/app composer:2.6 sh -c \ + "composer install --profile --ignore-platform-reqs && composer check" + + linter: + name: Linter + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + with: + fetch-depth: 2 + + - run: git checkout HEAD^2 + + - name: Run Linter + run: | + docker run --rm -v $PWD:/app composer sh -c \ + "composer install --profile --ignore-platform-reqs && composer lint" + + tests: + name: Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + with: + fetch-depth: 2 + + - run: git checkout HEAD^2 + + - name: Build + run: | + docker compose build + docker compose up -d + sleep 10 + + - name: Run Tests + run: docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests diff --git a/.github/workflows/code-ql-analysis.yml b/.github/workflows/code-ql-analysis.yml deleted file mode 100644 index 55d230a..0000000 --- a/.github/workflows/code-ql-analysis.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: "CodeQL" - -on: [pull_request] -jobs: - lint: - name: CodeQL - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - run: git checkout HEAD^2 - - - name: Run CodeQL - run: | - docker run --rm -v $PWD:/app composer:2.6 sh -c \ - "composer install --profile --ignore-platform-reqs && composer check" \ No newline at end of file diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml deleted file mode 100644 index bca79de..0000000 --- a/.github/workflows/linter.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: "Linter" - -on: [pull_request] -jobs: - lint: - name: Linter - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - fetch-depth: 2 - - - run: git checkout HEAD^2 - - - name: Run Linter - run: | - docker run --rm -v $PWD:/app composer sh -c \ - "composer install --profile --ignore-platform-reqs && composer lint" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 31158be..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: "Tests" - -on: [ pull_request ] -jobs: - lint: - name: Tests - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - fetch-depth: 2 - - - run: git checkout HEAD^2 - - - name: Build - run: | - docker compose build - docker compose up -d - sleep 10 - - - name: Run Tests - run: docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests \ No newline at end of file diff --git a/README.md b/README.md index a8a564c..0e1a3cd 100644 --- a/README.md +++ b/README.md @@ -292,10 +292,10 @@ To run all unit tests, use the following Docker command: docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests ``` -To run static code analysis, use the following Psalm command: +To run static code analysis, use the following command: ```bash -docker compose exec tests vendor/bin/psalm --show-info=true +docker compose exec tests composer check ``` ## Security diff --git a/composer.json b/composer.json index 9081ac8..75c3368 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ } }, "scripts": { - "check": "./vendor/bin/phpstan analyse --level max src tests", + "check": "./vendor/bin/phpstan analyse --level max --memory-limit=1G src tests", "lint": "./vendor/bin/pint --config pint.json --test", "format": "./vendor/bin/pint --config pint.json" }, @@ -35,8 +35,7 @@ }, "require-dev": { "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1", - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.9.x-dev" + "laravel/pint": "^1.29", + "phpstan/phpstan": "^1.12" } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index fd0b7a7..e82940e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,43 +4,39 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bd9d398f71b4f34b59347ac9a84ccc9e", + "content-hash": "5880477303f39f49a62931e2dcc65aa5", "packages": [], "packages-dev": [ { - "name": "amphp/amp", - "version": "v2.6.5", + "name": "doctrine/instantiator", + "version": "2.1.0", "source": { "type": "git", - "url": "https://github.com/amphp/amp.git", - "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207" + "url": "https://github.com/doctrine/instantiator.git", + "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/d7dda98dae26e56f3f6fcfbf1c1f819c9a993207", - "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/23da848e1a2308728fe5fdddabf4be17ff9720c7", + "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7", "shasum": "" }, "require": { - "php": ">=7.1" + "php": "^8.4" }, "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "ext-json": "*", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^7 | ^8 | ^9", - "react/promise": "^2", - "vimeo/psalm": "^3.12" + "doctrine/coding-standard": "^14", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5.58" }, "type": "library", "autoload": { - "files": [ - "lib/functions.php", - "lib/Internal/functions.php" - ], "psr-4": { - "Amp\\": "lib" + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", @@ -49,81 +45,77 @@ ], "authors": [ { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" } ], - "description": "A non-blocking concurrency framework for PHP applications.", - "homepage": "https://amphp.org/amp", + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ - "async", - "asynchronous", - "awaitable", - "concurrency", - "event", - "event-loop", - "future", - "non-blocking", - "promise" + "constructor", + "instantiate" ], "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.5" + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.1.0" }, "funding": [ { - "url": "https://github.com/amphp", - "type": "github" + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" } ], - "time": "2025-09-03T19:41:28+00:00" + "time": "2026-01-05T06:47:08+00:00" }, { - "name": "amphp/byte-stream", - "version": "v1.8.2", + "name": "laravel/pint", + "version": "v1.29.3", "source": { "type": "git", - "url": "https://github.com/amphp/byte-stream.git", - "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" + "url": "https://github.com/laravel/pint.git", + "reference": "da1d1111a6aa2e082d2a388b194afe1ba0a05d14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", - "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", + "url": "https://api.github.com/repos/laravel/pint/zipball/da1d1111a6aa2e082d2a388b194afe1ba0a05d14", + "reference": "da1d1111a6aa2e082d2a388b194afe1ba0a05d14", "shasum": "" }, "require": { - "amphp/amp": "^2", - "php": ">=7.1" + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.2.0" }, "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.4", - "friendsofphp/php-cs-fixer": "^2.3", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^6 || ^7 || ^8", - "psalm/phar": "^3.11.4" + "friendsofphp/php-cs-fixer": "^3.95.8", + "illuminate/view": "^12.62.0", + "larastan/larastan": "^3.10.0", + "laravel-zero/framework": "^12.1.0", + "laravel/agent-detector": "^2.0.2", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest": "^3.8.6" }, - "type": "library", + "bin": [ + "builds/pint" + ], + "type": "project", "autoload": { - "files": [ - "lib/functions.php" - ], "psr-4": { - "Amp\\ByteStream\\": "lib" + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" } }, "notification-url": "https://packagist.org/downloads/", @@ -132,2184 +124,348 @@ ], "authors": [ { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" } ], - "description": "A stream abstraction to make working with non-blocking I/O simple.", - "homepage": "https://amphp.org/byte-stream", + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", "keywords": [ - "amp", - "amphp", - "async", - "io", - "non-blocking", - "stream" + "dev", + "format", + "formatter", + "lint", + "linter", + "php" ], "support": { - "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-04-13T18:00:56+00:00" + "time": "2026-06-16T15:34:04+00:00" }, { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.5", + "name": "myclabs/deep-copy", + "version": "1.13.4", "source": { "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" + "php": "^7.1 || ^8.0" }, - "replace": { - "ocramius/package-versions": "1.11.99" + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, + "type": "library", "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], "psr-4": { - "PackageVersions\\": "src/PackageVersions" + "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", "type": "tidelift" } ], - "abandoned": true, - "time": "2022-01-17T14:14:24+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { - "name": "composer/semver", - "version": "3.4.4", + "name": "nikic/php-parser", + "version": "v4.19.5", "source": { "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/51bd93cc741b7fc3d63d20b6bdcd99fdaa359837", + "reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" + "ext-tokenizer": "*", + "php": ">=7.1" }, "require-dev": { - "phpstan/phpstan": "^1.11", - "symfony/phpunit-bridge": "^3 || ^7" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, + "bin": [ + "bin/php-parse" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, "autoload": { "psr-4": { - "Composer\\Semver\\": "src" + "PhpParser\\": "lib/PhpParser" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" + "name": "Nikita Popov" } ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", + "description": "A PHP parser written in PHP", "keywords": [ - "semantic", - "semver", - "validation", - "versioning" + "parser", + "php" ], "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.4" + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.5" }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - } - ], - "time": "2025-08-20T19:15:30+00:00" + "time": "2025-12-06T11:45:25+00:00" }, { - "name": "composer/xdebug-handler", - "version": "1.4.6", + "name": "phar-io/manifest", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "f27e06cd9675801df441b3656569b328e04aa37c" + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f27e06cd9675801df441b3656569b328e04aa37c", - "reference": "f27e06cd9675801df441b3656569b328e04aa37c", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0", - "psr/log": "^1.0" - }, - "require-dev": { - "phpstan/phpstan": "^0.12.55", - "symfony/phpunit-bridge": "^4.2 || ^5" + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/1.4.6" + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, "funding": [ { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", + "url": "https://github.com/theseer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2021-03-25T17:01:18+00:00" + "time": "2024-03-03T12:33:53+00:00" }, { - "name": "dnoegel/php-xdg-base-dir", - "version": "v0.1.1", + "name": "phar-io/version", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { - "psr-4": { - "XdgBaseDir\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } ], - "description": "implementation of xdg base directory specification for php", + "description": "Library for handling version information and constraints", "support": { - "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", - "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "time": "2019-12-04T15:06:13+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.6", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", - "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "phpunit/phpunit": "<=7.5 || >=14" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^14", - "phpstan/phpstan": "1.4.10 || 2.1.30", - "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", - "psr/log": "^1 || ^2 || ^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.6" - }, - "time": "2026-02-07T07:09:04+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/23da848e1a2308728fe5fdddabf4be17ff9720c7", - "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7", - "shasum": "" - }, - "require": { - "php": "^8.4" - }, - "require-dev": { - "doctrine/coding-standard": "^14", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^10.5.58" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.1.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2026-01-05T06:47:08+00:00" - }, - { - "name": "felixfbecker/advanced-json-rpc", - "version": "v3.2.1", - "source": { - "type": "git", - "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", - "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", - "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", - "shasum": "" - }, - "require": { - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "php": "^7.1 || ^8.0", - "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" - }, - "require-dev": { - "phpunit/phpunit": "^7.0 || ^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "AdvancedJsonRpc\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - } - ], - "description": "A more advanced JSONRPC implementation", - "support": { - "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", - "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" - }, - "time": "2021-06-11T22:34:44+00:00" - }, - { - "name": "felixfbecker/language-server-protocol", - "version": "v1.5.3", - "source": { - "type": "git", - "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", - "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpstan/phpstan": "*", - "squizlabs/php_codesniffer": "^3.1", - "vimeo/psalm": "^4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "LanguageServerProtocol\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - } - ], - "description": "PHP classes for the Language Server Protocol", - "keywords": [ - "language", - "microsoft", - "php", - "server" - ], - "support": { - "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" - }, - "time": "2024-04-30T00:40:11+00:00" - }, - { - "name": "laravel/pint", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e60e2112ee779ce60f253695b273d1646a17d6f1", - "reference": "e60e2112ee779ce60f253695b273d1646a17d6f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.11.0", - "illuminate/view": "^9.32.0", - "laravel-zero/framework": "^9.2.0", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.2.0", - "nunomaduro/termwind": "^1.14.0", - "pestphp/pest": "^1.22.1" - }, - "bin": [ - "builds/pint" - ], - "type": "project", - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", - "keywords": [ - "format", - "formatter", - "lint", - "linter", - "php" - ], - "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" - }, - "time": "2022-11-29T16:25:20+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.13.4", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2025-08-01T08:46:24+00:00" - }, - { - "name": "netresearch/jsonmapper", - "version": "v3.1.1", - "source": { - "type": "git", - "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "ba09f0e456d4f00cef84e012da5715625594407c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/ba09f0e456d4f00cef84e012da5715625594407c", - "reference": "ba09f0e456d4f00cef84e012da5715625594407c", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "~4.8.35 || ~5.7 || ~6.4 || ~7.0", - "squizlabs/php_codesniffer": "~3.5" - }, - "type": "library", - "autoload": { - "psr-0": { - "JsonMapper": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "OSL-3.0" - ], - "authors": [ - { - "name": "Christian Weiske", - "email": "cweiske@cweiske.de", - "homepage": "http://github.com/cweiske/jsonmapper/", - "role": "Developer" - } - ], - "description": "Map nested JSON structures onto PHP classes", - "support": { - "email": "cweiske@cweiske.de", - "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v3.1.1" - }, - "time": "2020-11-02T19:19:54+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.19.5", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/51bd93cc741b7fc3d63d20b6bdcd99fdaa359837", - "reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.1" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.5" - }, - "time": "2025-12-06T11:45:25+00:00" - }, - { - "name": "openlss/lib-array2xml", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/nullivex/lib-array2xml.git", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "autoload": { - "psr-0": { - "LSS": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Bryan Tong", - "email": "bryan@nullivex.com", - "homepage": "https://www.nullivex.com" - }, - { - "name": "Tony Butler", - "email": "spudz76@gmail.com", - "homepage": "https://www.nullivex.com" - } - ], - "description": "Array2XML conversion library credit to lalit.org", - "homepage": "https://www.nullivex.com", - "keywords": [ - "array", - "array conversion", - "xml", - "xml conversion" - ], - "support": { - "issues": "https://github.com/nullivex/lib-array2xml/issues", - "source": "https://github.com/nullivex/lib-array2xml/tree/master" - }, - "time": "2019-03-29T20:06:56+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", - "reference": "54750ef60c58e43759730615a392c31c80e23176", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2024-03-03T12:33:53+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.6.7", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "31a105931bc8ffa3a123383829772e832fd8d903" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/31a105931bc8ffa3a123383829772e832fd8d903", - "reference": "31a105931bc8ffa3a123383829772e832fd8d903", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.1", - "ext-filter": "*", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7|^2.0", - "webmozart/assert": "^1.9.1 || ^2" - }, - "require-dev": { - "mockery/mockery": "~1.3.5 || ~1.6.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-webmozart-assert": "^1.2", - "phpunit/phpunit": "^9.5", - "psalm/phar": "^5.26" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.7" - }, - "time": "2026-03-18T20:47:46+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.12.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", - "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.18|^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" - }, - "time": "2025-11-21T15:09:14+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "2.3.2", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^5.3.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" - }, - "time": "2026-01-25T14:56:51+00:00" - }, - { - "name": "phpstan/phpstan", - "version": "1.9.x-dev", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "6c0217aa2b146c3e28474e8be3e87188fac55dac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6c0217aa2b146c3e28474e8be3e87188fac55dac", - "reference": "6c0217aa2b146c3e28474e8be3e87188fac55dac", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0" - }, - "conflict": { - "phpstan/phpstan-shim": "*" - }, - "bin": [ - "phpstan", - "phpstan.phar" - ], - "type": "library", - "autoload": { - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPStan - PHP Static Analysis Tool", - "keywords": [ - "dev", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.9.x" - }, - "funding": [ - { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://github.com/phpstan", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" - } - ], - "time": "2023-02-18T15:01:46+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "9.2.32", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", - "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.19.1 || ^5.1.0", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.6", - "phpunit/php-text-template": "^2.0.4", - "sebastian/code-unit-reverse-lookup": "^2.0.3", - "sebastian/complexity": "^2.0.3", - "sebastian/environment": "^5.1.5", - "sebastian/lines-of-code": "^1.0.4", - "sebastian/version": "^3.0.2", - "theseer/tokenizer": "^1.2.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.6" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "9.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-08-22T04:23:01+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:48:52+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:58:55+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T05:33:50+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:16:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "9.6.34", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b36f02317466907a230d3aa1d34467041271ef4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b36f02317466907a230d3aa1d34467041271ef4a", - "reference": "b36f02317466907a230d3aa1d34467041271ef4a", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.5.0 || ^2", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.4", - "phar-io/manifest": "^2.0.4", - "phar-io/version": "^3.2.1", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.32", - "phpunit/php-file-iterator": "^3.0.6", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.4", - "phpunit/php-timer": "^5.0.3", - "sebastian/cli-parser": "^1.0.2", - "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.10", - "sebastian/diff": "^4.0.6", - "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.8", - "sebastian/global-state": "^5.0.8", - "sebastian/object-enumerator": "^4.0.4", - "sebastian/resource-operations": "^3.0.4", - "sebastian/type": "^3.2.1", - "sebastian/version": "^3.0.2" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.6-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.34" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2026-01-27T05:45:00+00:00" - }, - { - "name": "psr/container", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" - }, - "time": "2021-11-05T16:47:00+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-02T06:27:43+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.10", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d", - "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", - "type": "tidelift" - } - ], - "time": "2026-01-24T09:22:56+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { - "name": "sebastian/complexity", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" - }, + "name": "phpstan/phpstan", + "version": "1.12.33", "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37982d6fc7cbb746dda7773530cda557cdf119e1", + "reference": "37982d6fc7cbb746dda7773530cda557cdf119e1", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] + "php": "^7.2|^8.0" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + "conflict": { + "phpstan/phpstan-shim": "*" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } + "bin": [ + "phpstan", + "phpstan.phar" ], - "time": "2023-12-22T06:19:30+00:00" - }, - { - "name": "sebastian/diff", - "version": "4.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" - }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } + "MIT" ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "dev", + "static analysis" ], "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/ondrejmirtes", "type": "github" - } - ], - "time": "2024-03-02T06:30:58+00:00" - }, - { - "name": "sebastian/environment", - "version": "5.1.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" - }, - "funding": [ + }, { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/phpstan", "type": "github" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2026-02-28T20:30:03+00:00" }, { - "name": "sebastian/exporter", - "version": "4.0.8", + "name": "phpunit/php-code-coverage", + "version": "9.2.32", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", - "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -2323,86 +479,55 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ - "export", - "exporter" + "coverage", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", - "type": "tidelift" } ], - "time": "2025-09-24T06:03:27+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { - "name": "sebastian/global-state", - "version": "5.0.8", + "name": "phpunit/php-file-iterator", + "version": "3.0.6", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", - "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=7.3" }, "require-dev": { - "ext-dom": "*", "phpunit/phpunit": "^9.3" }, - "suggest": { - "ext-uopz": "*" - }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2417,63 +542,56 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ - "global state" + "filesystem", + "iterator" ], "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", - "type": "tidelift" } ], - "time": "2025-08-10T07:10:35+00:00" + "time": "2021-12-02T12:48:52+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "1.0.4", + "name": "phpunit/php-invoker", + "version": "3.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { + "ext-pcntl": "*", "phpunit/phpunit": "^9.3" }, + "suggest": { + "ext-pcntl": "*" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -2492,11 +610,14 @@ "role": "lead" } ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" }, "funding": [ { @@ -2504,26 +625,24 @@ "type": "github" } ], - "time": "2023-12-22T06:20:34+00:00" + "time": "2020-09-28T05:58:55+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "4.0.4", + "name": "phpunit/php-text-template", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=7.3" }, "require-dev": { "phpunit/phpunit": "^9.3" @@ -2531,7 +650,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2546,14 +665,18 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" }, "funding": [ { @@ -2561,20 +684,20 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2020-10-26T05:33:50+00:00" }, { - "name": "sebastian/object-reflector", - "version": "2.0.4", + "name": "phpunit/php-timer", + "version": "5.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { @@ -2586,7 +709,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -2601,14 +724,18 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" }, "funding": [ { @@ -2616,35 +743,68 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2020-10-26T13:16:10+00:00" }, { - "name": "sebastian/recursion-context", - "version": "4.0.6", + "name": "phpunit/phpunit", + "version": "9.6.34", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "b36f02317466907a230d3aa1d34467041271ef4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", - "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b36f02317466907a230d3aa1d34467041271ef4a", + "reference": "b36f02317466907a230d3aa1d34467041271ef4a", "shasum": "" }, "require": { - "php": ">=7.3" + "doctrine/instantiator": "^1.5.0 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.10", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", + "sebastian/version": "^3.0.2" }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, + "bin": [ + "phpunit" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "9.6-dev" } }, "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], "classmap": [ "src/" ] @@ -2656,24 +816,27 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.34" }, "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, { "url": "https://github.com/sebastianbergmann", "type": "github" @@ -2687,36 +850,36 @@ "type": "thanks_dev" }, { - "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2025-08-10T06:57:39+00:00" + "time": "2026-01-27T05:45:00+00:00" }, { - "name": "sebastian/resource-operations", - "version": "3.0.4", + "name": "sebastian/cli-parser", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -2731,13 +894,15 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" }, "funding": [ { @@ -2745,32 +910,32 @@ "type": "github" } ], - "time": "2024-03-14T16:00:52+00:00" + "time": "2024-03-02T06:27:43+00:00" }, { - "name": "sebastian/type", - "version": "3.2.1", + "name": "sebastian/code-unit", + "version": "1.0.8", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -2789,11 +954,11 @@ "role": "lead" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" }, "funding": [ { @@ -2801,29 +966,32 @@ "type": "github" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2020-10-26T13:08:54+00:00" }, { - "name": "sebastian/version", - "version": "3.0.2", + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { "php": ">=7.3" }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2838,15 +1006,14 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" }, "funding": [ { @@ -2854,874 +1021,640 @@ "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2020-09-28T05:30:19+00:00" }, { - "name": "symfony/console", - "version": "v5.4.47", + "name": "sebastian/comparator", + "version": "4.0.10", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", - "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d", + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" - }, - "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" }, "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "phpunit/phpunit": "^9.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v5.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" }, { - "url": "https://github.com/fabpot", - "type": "github" + "name": "Volker Dusch", + "email": "github@wallbash.com" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-11-06T11:30:55+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.7.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.7-dev" + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://github.com/nicolas-grekas", - "type": "github" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", "type": "tidelift" } ], - "time": "2026-04-13T15:52:40+00:00" + "time": "2026-01-24T09:22:56+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.37.0", + "name": "sebastian/complexity", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "141046a8f9477948ff284fa65be2095baafb94f2" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", - "reference": "141046a8f9477948ff284fa65be2095baafb94f2", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", "shasum": "" }, "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" }, - "suggest": { - "ext-ctype": "For best performance" + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "branch-alias": { + "dev-master": "2.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2026-04-10T16:19:22+00:00" + "time": "2023-12-22T06:19:30+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.38.1", + "name": "sebastian/diff", + "version": "4.0.6", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "e9247d281d694a5120554d9afaf54e070e88a603" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", - "reference": "e9247d281d694a5120554d9afaf54e070e88a603", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.3" }, - "suggest": { - "ext-intl": "For best performance" + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "branch-alias": { + "dev-master": "4.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" + "diff", + "udiff", + "unidiff", + "unified diff" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2026-05-26T05:58:03+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.38.0", + "name": "sebastian/environment", + "version": "5.1.5", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", - "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-intl": "For best performance" + "ext-posix": "*" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "branch-alias": { + "dev-master": "5.1-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, "classmap": [ - "Resources/stubs" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" + "Xdebug", + "environment", + "hhvm" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2026-05-25T13:48:31+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.38.1", + "name": "sebastian/exporter", + "version": "4.0.8", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92", - "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { - "ext-iconv": "*", - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, - "suggest": { - "ext-mbstring": "For best performance" + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "branch-alias": { + "dev-master": "4.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" + "export", + "exporter" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1" + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://github.com/nicolas-grekas", - "type": "github" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", "type": "tidelift" } ], - "time": "2026-05-26T12:51:13+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { - "name": "symfony/polyfill-php73", - "version": "v1.37.0", + "name": "sebastian/global-state", + "version": "5.0.8", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "branch-alias": { + "dev-master": "5.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, "classmap": [ - "Resources/stubs" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "global state" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.37.0" + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://github.com/nicolas-grekas", - "type": "github" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-08-10T07:10:35+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.37.0", + "name": "sebastian/lines-of-code", + "version": "1.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", - "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", "shasum": "" }, "require": { - "php": ">=7.2" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "branch-alias": { + "dev-master": "1.0-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, "classmap": [ - "Resources/stubs" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2026-04-10T16:19:22+00:00" + "time": "2023-12-22T06:20:34+00:00" }, { - "name": "symfony/service-contracts", - "version": "v3.7.0", + "name": "sebastian/object-enumerator", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", - "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, - "conflict": { - "ext-psr": "<1.1|>=2" + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, "branch-alias": { - "dev-main": "3.7-dev" + "dev-master": "4.0-dev" } }, "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2026-03-28T09:44:51+00:00" + "time": "2020-10-26T13:12:34+00:00" }, { - "name": "symfony/string", - "version": "v6.4.39", + "name": "sebastian/object-reflector", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "62e3c927de664edadb5bef260987eb047a17a113" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/62e3c927de664edadb5bef260987eb047a17a113", - "reference": "62e3c927de664edadb5bef260987eb047a17a113", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" + "php": ">=7.3" }, "require-dev": { - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "phpunit/phpunit": "^9.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "source": "https://github.com/symfony/string/tree/v6.4.39" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2026-05-12T11:44:19+00:00" + "time": "2020-10-26T13:14:26+00:00" }, { - "name": "theseer/tokenizer", - "version": "1.3.1", + "name": "sebastian/recursion-context", + "version": "4.0.6", "source": { "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -3733,298 +1666,267 @@ ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { - "url": "https://github.com/theseer", + "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2025-11-17T20:03:58+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { - "name": "vimeo/psalm", - "version": "4.0.1", + "name": "sebastian/resource-operations", + "version": "3.0.4", "source": { "type": "git", - "url": "https://github.com/vimeo/psalm.git", - "reference": "b1e2e30026936ef8d5bf6a354d1c3959b6231f44" + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/b1e2e30026936ef8d5bf6a354d1c3959b6231f44", - "reference": "b1e2e30026936ef8d5bf6a354d1c3959b6231f44", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { - "amphp/amp": "^2.1", - "amphp/byte-stream": "^1.5", - "composer/package-versions-deprecated": "^1.8.0", - "composer/semver": "^1.4 || ^2.0 || ^3.0", - "composer/xdebug-handler": "^1.1", - "dnoegel/php-xdg-base-dir": "^0.1.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-simplexml": "*", - "ext-tokenizer": "*", - "felixfbecker/advanced-json-rpc": "^3.0.3", - "felixfbecker/language-server-protocol": "^1.4", - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0", - "nikic/php-parser": "^4.10.1", - "openlss/lib-array2xml": "^1.0", - "php": "^7.3|^8", - "sebastian/diff": "^3.0 || ^4.0", - "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", - "webmozart/glob": "^4.1", - "webmozart/path-util": "^2.3" - }, - "provide": { - "psalm/psalm": "self.version" + "php": ">=7.3" }, "require-dev": { - "amphp/amp": "^2.4.2", - "bamarni/composer-bin-plugin": "^1.2", - "brianium/paratest": "^4.0.0", - "ext-curl": "*", - "phpdocumentor/reflection-docblock": "^5", - "phpmyadmin/sql-parser": "5.1.0", - "phpspec/prophecy": ">=1.9.0", - "phpunit/phpunit": "^9.0", - "psalm/plugin-phpunit": "^0.13", - "slevomat/coding-standard": "^5.0", - "squizlabs/php_codesniffer": "^3.5", - "symfony/process": "^4.3", - "weirdan/prophecy-shim": "^1.0 || ^2.0" - }, - "suggest": { - "ext-igbinary": "^2.0.5" + "phpunit/phpunit": "^9.0" }, - "bin": [ - "psalm", - "psalm-language-server", - "psalm-plugin", - "psalm-refactor", - "psalter" - ], "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev", - "dev-2.x": "2.x-dev", - "dev-3.x": "3.x-dev", - "dev-master": "4.x-dev" + "dev-main": "3.0-dev" } }, "autoload": { - "files": [ - "src/functions.php", - "src/spl_object_id.php" - ], - "psr-4": { - "Psalm\\": "src/Psalm/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Matthew Brown" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "A static analysis tool for finding errors in PHP applications", - "keywords": [ - "code", - "inspection", - "php" - ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/4.0.1" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, - "time": "2020-10-20T13:40:17+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" }, { - "name": "webmozart/assert", - "version": "1.12.1", + "name": "sebastian/type", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { - "ext-ctype": "*", - "ext-date": "*", - "ext-filter": "*", - "php": "^7.2 || ^8.0" + "php": ">=7.3" }, - "suggest": { - "ext-intl": "", - "ext-simplexml": "", - "ext-spl": "" + "require-dev": { + "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10-dev" + "dev-master": "3.2-dev" } }, "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.1" + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, - "time": "2025-10-29T15:56:20+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" }, { - "name": "webmozart/glob", - "version": "4.7.0", + "name": "sebastian/version", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/webmozarts/glob.git", - "reference": "8a2842112d6916e61e0e15e316465b611f3abc17" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/glob/zipball/8a2842112d6916e61e0e15e316465b611f3abc17", - "reference": "8a2842112d6916e61e0e15e316465b611f3abc17", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": "^7.3 || ^8.0.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5", - "symfony/filesystem": "^5.3" + "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "3.0-dev" } }, "autoload": { - "psr-4": { - "Webmozart\\Glob\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "A PHP implementation of Ant's glob.", + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "issues": "https://github.com/webmozarts/glob/issues", - "source": "https://github.com/webmozarts/glob/tree/4.7.0" + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, - "time": "2024-03-07T20:33:40+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" }, { - "name": "webmozart/path-util", - "version": "2.3.0", + "name": "theseer/tokenizer", + "version": "1.3.1", "source": { "type": "git", - "url": "https://github.com/webmozart/path-util.git", - "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725", - "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { - "php": ">=5.3.3", - "webmozart/assert": "~1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, "autoload": { - "psr-4": { - "Webmozart\\PathUtil\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { - "issues": "https://github.com/webmozart/path-util/issues", - "source": "https://github.com/webmozart/path-util/tree/2.3.0" + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, - "abandoned": "symfony/filesystem", - "time": "2015-12-17T08:42:14+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "phpstan/phpstan": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=8.0", "ext-hash": "*", + "ext-openssl": "*", "ext-scrypt": "*", "ext-sodium": "*" }, diff --git a/src/Auth/Hashes/Scrypt.php b/src/Auth/Hashes/Scrypt.php index 4f038c0..806f998 100644 --- a/src/Auth/Hashes/Scrypt.php +++ b/src/Auth/Hashes/Scrypt.php @@ -28,14 +28,34 @@ public function hash(string $value): string throw new \RuntimeException('The scrypt extension is required. Please install php-scrypt.'); } - return \scrypt( + $salt = $this->getOption('salt'); + $costCpu = $this->getOption('costCpu'); + $costMemory = $this->getOption('costMemory'); + $costParallel = $this->getOption('costParallel'); + $length = $this->getOption('length'); + + if (! \is_string($salt)) { + throw new \InvalidArgumentException('Salt must be a string'); + } + + if (! \is_int($costCpu) || ! \is_int($costMemory) || ! \is_int($costParallel) || ! \is_int($length)) { + throw new \InvalidArgumentException('Scrypt cost and length options must be integers'); + } + + $hash = \scrypt( $value, - $this->getOption('salt'), - $this->getOption('costCpu'), - $this->getOption('costMemory'), - $this->getOption('costParallel'), - $this->getOption('length') + $salt, + $costCpu, + $costMemory, + $costParallel, + $length ); + + if ($hash === false) { + throw new \RuntimeException('Failed to hash using scrypt'); + } + + return $hash; } /** diff --git a/src/Auth/Hashes/ScryptModified.php b/src/Auth/Hashes/ScryptModified.php index 2e3f896..2c9d861 100644 --- a/src/Auth/Hashes/ScryptModified.php +++ b/src/Auth/Hashes/ScryptModified.php @@ -71,6 +71,9 @@ private function generateDerivedKey(string $value): string $value = \mb_convert_encoding($value, 'UTF-8'); $derivedKey = \scrypt($value, $saltBytes.$saltSeparatorBytes, 16384, 8, 1, 64); + if ($derivedKey === false) { + throw new \RuntimeException('Failed to generate derived key using scrypt'); + } $result = \hex2bin($derivedKey); if ($result === false) { @@ -87,10 +90,6 @@ private function generateDerivedKey(string $value): string */ private function hashKeys(string $signerKeyBytes, string $derivedKeyBytes): string { - if (! is_string($signerKeyBytes)) { - throw new \InvalidArgumentException('Signer key must be a string'); - } - $key = \substr($derivedKeyBytes, 0, 32); $iv = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; diff --git a/src/Auth/Issuers/Asymmetric/AccessToken.php b/src/Auth/Issuers/Asymmetric/AccessToken.php index c704219..8845688 100644 --- a/src/Auth/Issuers/Asymmetric/AccessToken.php +++ b/src/Auth/Issuers/Asymmetric/AccessToken.php @@ -54,6 +54,20 @@ public function issue( ?string $jti = null, array $claims = [], ): string { + if ($audience === []) { + throw new \InvalidArgumentException('audience must contain at least one resource server identifier.'); + } + + if ($audience !== \array_values($audience)) { + throw new \InvalidArgumentException('audience must be a list of resource server identifiers.'); + } + + foreach ($audience as $identifier) { + if (!\is_string($identifier) || $identifier === '') { + throw new \InvalidArgumentException('audience must contain non-empty resource server identifiers.'); + } + } + $now = \time(); // "scope" is issuer-controlled; drop any caller-supplied value so it diff --git a/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php b/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php index 1686be0..eb19ca2 100644 --- a/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php +++ b/tests/Auth/Issuers/Asymmetric/AccessTokenTest.php @@ -77,6 +77,14 @@ public function testAudienceClaim(): void $this->assertEquals($audience, $claims['aud']); } + public function testEmptyAudienceIsRejected(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('audience must contain at least one resource server identifier.'); + + $this->accessToken->issue('user-123', [], 'client-abc', 1000, 3600); + } + public function testSignatureIsValid(): void { $token = $this->accessToken->issue('user-123', ['https://api.example.com'], 'client-abc', 1000, 3600);