From c9dc4514c63a1bbc86ccead1f99085f055dc3354 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 01:12:59 +0000 Subject: [PATCH 01/28] Increase PHPStan level from 3 to 4 (partial) - Add ignore pattern for "Trait used zero times" errors (framework traits provided for userland but not used internally) - Fix redundant nullsafe operators where type is guaranteed non-null: - NotificationSender: $events is non-nullable EventDispatcherInterface - SanctumGuard: short-circuit logic guarantees truthy value Reduces errors from 218 to 195. More fixes to follow. --- phpstan.neon.dist | 4 +++- src/notifications/src/NotificationSender.php | 2 +- src/sanctum/src/SanctumGuard.php | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4dde79c61..2aec45356 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,7 +3,7 @@ # parameters: - level: 3 + level: 4 parallel: jobSize: 20 maximumNumberOfProcesses: 32 @@ -26,6 +26,8 @@ parameters: - %currentWorkingDirectory%/src/support/src/Js.php - %currentWorkingDirectory%/src/notifications/src/DatabaseNotification.php ignoreErrors: + # Framework traits provided for userland - not used internally but intentionally available + - '#Trait Hypervel\\[A-Za-z\\\\]+ is used zero times and is not analysed\.#' - '#Result of method .* \(void\) is used\.#' - '#Unsafe usage of new static#' - '#Class [a-zA-Z0-9\\\\_]+ not found.#' diff --git a/src/notifications/src/NotificationSender.php b/src/notifications/src/NotificationSender.php index ccd48399e..c2b97a845 100644 --- a/src/notifications/src/NotificationSender.php +++ b/src/notifications/src/NotificationSender.php @@ -119,7 +119,7 @@ protected function shouldSendNotification(mixed $notifiable, mixed $notification } return tap(new NotificationSending($notifiable, $notification, $channel), function ($event) { - $this->events?->dispatch($event); + $this->events->dispatch($event); })->shouldSend(); } diff --git a/src/sanctum/src/SanctumGuard.php b/src/sanctum/src/SanctumGuard.php index 49a5d3bf5..64175b101 100644 --- a/src/sanctum/src/SanctumGuard.php +++ b/src/sanctum/src/SanctumGuard.php @@ -177,7 +177,7 @@ protected function isValidAccessToken(?PersonalAccessToken $accessToken): bool $isValid = (! $this->expiration || $accessToken->getAttribute('created_at')->gt(now()->subMinutes($this->expiration))) - && (! $accessToken->getAttribute('expires_at') || ! $accessToken->getAttribute('expires_at')?->isPast()) + && (! $accessToken->getAttribute('expires_at') || ! $accessToken->getAttribute('expires_at')->isPast()) && $this->hasValidProvider($accessToken->getAttribute('tokenable')); if (is_callable(Sanctum::$accessTokenAuthenticationCallback)) { From d61e65f95dcd77d9c1f3a7e24afd1f667c571901 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 02:00:39 +0000 Subject: [PATCH 02/28] Add PHPDoc type hints to narrow match expression in Mailable Adds literal string union type annotations to hasRecipient and hasEnvelopeRecipient methods, allowing PHPStan to verify the match expression is exhaustive for the 5 valid property values. --- src/mail/src/Mailable.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mail/src/Mailable.php b/src/mail/src/Mailable.php index b44358322..578d01a25 100644 --- a/src/mail/src/Mailable.php +++ b/src/mail/src/Mailable.php @@ -665,6 +665,8 @@ protected function normalizeRecipient(mixed $recipient): object /** * Determine if the given recipient is set on the mailable. + * + * @param 'from'|'to'|'cc'|'bcc'|'replyTo' $property */ protected function hasRecipient(array|object|string $address, ?string $name = null, string $property = 'to'): bool { @@ -696,6 +698,8 @@ protected function hasRecipient(array|object|string $address, ?string $name = nu /** * Determine if the mailable "envelope" method defines a recipient. + * + * @param 'from'|'to'|'cc'|'bcc'|'replyTo' $property */ private function hasEnvelopeRecipient(string $address, ?string $name, string $property): bool { From 8de7fabc249b89beae3204d834479d239313399f Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 02:04:19 +0000 Subject: [PATCH 03/28] Add PHPStan ignores for false positives in static analysis - Redis: Ignore finally.exitPoint (fix in separate PR) - FileStore: Ignore catch.neverThrown (exception thrown inside closure) - TestResponseAssert: Ignore catch.neverThrown (dynamic method call) - NestedSet Collection: Ignore foreach.emptyArray (complex groupBy inference) --- src/cache/src/FileStore.php | 2 +- src/foundation/src/Testing/TestResponseAssert.php | 2 +- src/nested-set/src/Eloquent/Collection.php | 4 ++-- src/redis/src/Redis.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cache/src/FileStore.php b/src/cache/src/FileStore.php index a56fed776..4d2cf1192 100644 --- a/src/cache/src/FileStore.php +++ b/src/cache/src/FileStore.php @@ -88,7 +88,7 @@ public function add(string $key, mixed $value, int $seconds): bool try { $file->getExclusiveLock(); - } catch (LockTimeoutException) { + } catch (LockTimeoutException) { // @phpstan-ignore catch.neverThrown (thrown inside closure) $file->close(); return false; diff --git a/src/foundation/src/Testing/TestResponseAssert.php b/src/foundation/src/Testing/TestResponseAssert.php index 71a90780f..aadeae32f 100644 --- a/src/foundation/src/Testing/TestResponseAssert.php +++ b/src/foundation/src/Testing/TestResponseAssert.php @@ -41,7 +41,7 @@ public function __call(string $name, array $arguments): void { try { Assert::$name(...$arguments); - } catch (ExpectationFailedException $e) { + } catch (ExpectationFailedException $e) { // @phpstan-ignore catch.neverThrown (dynamic call) throw $this->injectResponseContext($e); } } diff --git a/src/nested-set/src/Eloquent/Collection.php b/src/nested-set/src/Eloquent/Collection.php index 17bacd39b..2bab56f72 100644 --- a/src/nested-set/src/Eloquent/Collection.php +++ b/src/nested-set/src/Eloquent/Collection.php @@ -29,7 +29,7 @@ public function linkNodes(): static } $children = $groupedNodes->get($node->getKey(), []); - foreach ($children as $child) { + foreach ($children as $child) { // @phpstan-ignore foreach.emptyArray $child->setRelation('parent', $node); } @@ -113,7 +113,7 @@ public function toFlatTree(bool $root = false): static */ protected function flattenTree(Collection $groupedNodes, mixed $parentId): static { - foreach ($groupedNodes->get($parentId, []) as $node) { + foreach ($groupedNodes->get($parentId, []) as $node) { // @phpstan-ignore foreach.emptyArray $this->push($node); $this->flattenTree($groupedNodes, $node->getKey()); diff --git a/src/redis/src/Redis.php b/src/redis/src/Redis.php index c8da51f78..69b073ddc 100644 --- a/src/redis/src/Redis.php +++ b/src/redis/src/Redis.php @@ -37,7 +37,7 @@ public function __call($name, $arguments) $result = $connection->{$name}(...$arguments); } catch (Throwable $exception) { $hasError = true; - throw $exception; + throw $exception; // @phpstan-ignore finally.exitPoint (fix in separate PR) } finally { $time = round((microtime(true) - $start) * 1000, 2); $connection->getEventDispatcher()?->dispatch( From de100ebf78a10afe5bfcbbbda0d69ac2ed6cee6e Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 02:08:30 +0000 Subject: [PATCH 04/28] Fix PHPStan type errors and remove dead code - Container: Add class-string PHPDoc for callback type keys - RouteDependency: Add class-string PHPDoc for callback array keys - RedisWorkloadRepository: Remove unused $masters property (dead code) - Mailable: CS fixer formatting --- src/container/src/Container.php | 1 + src/horizon/src/Repositories/RedisWorkloadRepository.php | 3 --- src/http/src/RouteDependency.php | 2 ++ src/mail/src/Mailable.php | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/container/src/Container.php b/src/container/src/Container.php index ac027e1d9..384505cce 100644 --- a/src/container/src/Container.php +++ b/src/container/src/Container.php @@ -582,6 +582,7 @@ protected function fireAfterResolvingCallbacks($abstract, $object): void * * @param string $abstract * @param object $object + * @param array $callbacksPerType */ protected function getCallbacksForType($abstract, $object, array $callbacksPerType): array { diff --git a/src/horizon/src/Repositories/RedisWorkloadRepository.php b/src/horizon/src/Repositories/RedisWorkloadRepository.php index 7395f6675..a7d28eb08 100644 --- a/src/horizon/src/Repositories/RedisWorkloadRepository.php +++ b/src/horizon/src/Repositories/RedisWorkloadRepository.php @@ -4,7 +4,6 @@ namespace Hypervel\Horizon\Repositories; -use Hypervel\Horizon\Contracts\MasterSupervisorRepository; use Hypervel\Horizon\Contracts\SupervisorRepository; use Hypervel\Horizon\Contracts\WorkloadRepository; use Hypervel\Horizon\WaitTimeCalculator; @@ -18,13 +17,11 @@ class RedisWorkloadRepository implements WorkloadRepository * * @param QueueFactory $queue the queue factory implementation * @param WaitTimeCalculator $waitTime the wait time calculator instance - * @param MasterSupervisorRepository $masters the master supervisor repository implementation * @param SupervisorRepository $supervisors the supervisor repository implementation */ public function __construct( public QueueFactory $queue, public WaitTimeCalculator $waitTime, - private MasterSupervisorRepository $masters, private SupervisorRepository $supervisors ) { } diff --git a/src/http/src/RouteDependency.php b/src/http/src/RouteDependency.php index d5b463b5a..785fc6f9d 100644 --- a/src/http/src/RouteDependency.php +++ b/src/http/src/RouteDependency.php @@ -22,6 +22,8 @@ class RouteDependency /** * All of the after resolving callbacks by class type. + * + * @var array> */ protected array $afterResolvingCallbacks = []; diff --git a/src/mail/src/Mailable.php b/src/mail/src/Mailable.php index 578d01a25..21f63a98d 100644 --- a/src/mail/src/Mailable.php +++ b/src/mail/src/Mailable.php @@ -666,7 +666,7 @@ protected function normalizeRecipient(mixed $recipient): object /** * Determine if the given recipient is set on the mailable. * - * @param 'from'|'to'|'cc'|'bcc'|'replyTo' $property + * @param 'bcc'|'cc'|'from'|'replyTo'|'to' $property */ protected function hasRecipient(array|object|string $address, ?string $name = null, string $property = 'to'): bool { @@ -699,7 +699,7 @@ protected function hasRecipient(array|object|string $address, ?string $name = nu /** * Determine if the mailable "envelope" method defines a recipient. * - * @param 'from'|'to'|'cc'|'bcc'|'replyTo' $property + * @param 'bcc'|'cc'|'from'|'replyTo'|'to' $property */ private function hasEnvelopeRecipient(string $address, ?string $name, string $property): bool { From 4c724ee09d8f5a3dc3654ab03913774e0dd857b9 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 02:20:59 +0000 Subject: [PATCH 05/28] Fix unreachable code and nullable callable PHPDoc annotations Remove dead code that PHPStan correctly identified as unreachable: - Gate: Remove redundant if check (getMethod always returns or throws) - Mailer: Remove unreachable throw after exhaustive type checks - FakeProcessResult: Remove unreachable return after exhaustive type checks - HttpClientWatcher: Remove unreachable return after try-catch Fix PHPDoc annotations for nullable callables: - AuthenticationException: @var callable -> @var null|callable - Worker: @var callable -> @var null|callable Add temporary ignore for logic bug (fix in separate PR): - ServiceProvider: pathsForProviderOrGroup always returns array, not null --- src/auth/src/Access/Gate.php | 8 ++------ src/auth/src/AuthenticationException.php | 2 +- src/mail/src/Mailer.php | 14 +++++--------- src/process/src/FakeProcessResult.php | 16 ++++++---------- src/queue/src/Worker.php | 2 +- src/support/src/ServiceProvider.php | 1 + src/telescope/src/Watchers/HttpClientWatcher.php | 2 -- 7 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/auth/src/Access/Gate.php b/src/auth/src/Access/Gate.php index 540b00122..fddcdc9fc 100644 --- a/src/auth/src/Access/Gate.php +++ b/src/auth/src/Access/Gate.php @@ -367,13 +367,9 @@ protected function methodAllowsGuests(mixed $class, string $method): bool return false; } - if ($method) { - $parameters = $method->getParameters(); + $parameters = $method->getParameters(); - return isset($parameters[0]) && $this->parameterAllowsGuests($parameters[0]); - } - - return false; + return isset($parameters[0]) && $this->parameterAllowsGuests($parameters[0]); } /** diff --git a/src/auth/src/AuthenticationException.php b/src/auth/src/AuthenticationException.php index 3cc4df70f..4df9c6ba1 100644 --- a/src/auth/src/AuthenticationException.php +++ b/src/auth/src/AuthenticationException.php @@ -22,7 +22,7 @@ class AuthenticationException extends Exception /** * The callback that should be used to generate the authentication redirect path. * - * @var callable + * @var null|callable */ protected static $redirectToCallback; diff --git a/src/mail/src/Mailer.php b/src/mail/src/Mailer.php index ffcb7945d..8d374de15 100644 --- a/src/mail/src/Mailer.php +++ b/src/mail/src/Mailer.php @@ -304,15 +304,11 @@ protected function parseView(array|Closure|string $view): array // If this view is an array but doesn't contain numeric keys, we will assume // the views are being explicitly specified and will extract them via the // named keys instead, allowing the developers to use one or the other. - if (is_array($view)) { - return [ - $view['html'] ?? null, - $view['text'] ?? null, - $view['raw'] ?? null, - ]; - } - - throw new InvalidArgumentException('Invalid view.'); + return [ + $view['html'] ?? null, + $view['text'] ?? null, + $view['raw'] ?? null, + ]; } /** diff --git a/src/process/src/FakeProcessResult.php b/src/process/src/FakeProcessResult.php index e781fedc8..a56a7fb4b 100644 --- a/src/process/src/FakeProcessResult.php +++ b/src/process/src/FakeProcessResult.php @@ -49,16 +49,12 @@ protected function normalizeOutput(array|string $output): string if (is_string($output)) { return rtrim($output, "\n") . "\n"; } - if (is_array($output)) { - return rtrim( - (new Collection($output)) - ->map(fn ($line) => rtrim($line, "\n") . "\n") - ->implode(''), - "\n" - ) . "\n"; - } - - return ''; + return rtrim( + (new Collection($output)) + ->map(fn ($line) => rtrim($line, "\n") . "\n") + ->implode(''), + "\n" + ) . "\n"; } /** diff --git a/src/queue/src/Worker.php b/src/queue/src/Worker.php index 6967c2adb..2d3f93f56 100644 --- a/src/queue/src/Worker.php +++ b/src/queue/src/Worker.php @@ -67,7 +67,7 @@ class Worker /** * The callback used to monitor timeout jobs. * - * @var callable + * @var null|callable */ protected $monitorTimeoutJobs; diff --git a/src/support/src/ServiceProvider.php b/src/support/src/ServiceProvider.php index 92a97a236..a9752f3e9 100644 --- a/src/support/src/ServiceProvider.php +++ b/src/support/src/ServiceProvider.php @@ -248,6 +248,7 @@ public static function pathsToPublish(?string $provider = null, ?string $group = return $paths; } + // @phpstan-ignore deadCode.unreachable (logic bug: method always returns array, fix in separate PR) return collect(static::$publishes)->reduce(function ($paths, $p) { return array_merge($paths, $p); }, []); diff --git a/src/telescope/src/Watchers/HttpClientWatcher.php b/src/telescope/src/Watchers/HttpClientWatcher.php index 2fefee0da..98b0afb3e 100644 --- a/src/telescope/src/Watchers/HttpClientWatcher.php +++ b/src/telescope/src/Watchers/HttpClientWatcher.php @@ -125,8 +125,6 @@ protected function getRequestPayload(RequestInterface $request): array|string $stream->rewind(); } } - - return 'Unknown'; } protected function getResponse(ResponseInterface $response): array From fd11df9f8f07dc38e95858d189d86902002a35be Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 02:45:30 +0000 Subject: [PATCH 06/28] Fix PHPStan identical comparison errors Fixes: - Str::isUuid: Add 'nil' to PHPDoc type (was missing, code checks for it) - QueueFake: Remove dead code (object !== string always false) - RedisStore: Ignore NaN detection idiom ($v === $v is only false for NaN) Temporary ignores for bugs fixed in separate PRs: - FilesystemManager: Operator precedence bug - StartSession: Operator precedence bug - TestResponseAssert: getHeader returns array not string Ignores for defensive/platform-specific code: - BcryptHasher: PHP 7 defensive code (PHP 8 throws instead) - ArgonHasher: Platform-specific PASSWORD_ARGON2_PROVIDER constant --- src/cache/src/RedisStore.php | 4 ++-- src/filesystem/src/FilesystemManager.php | 2 +- src/foundation/src/Testing/TestResponseAssert.php | 2 +- src/hashing/src/ArgonHasher.php | 2 +- src/hashing/src/BcryptHasher.php | 2 +- src/session/src/Middleware/StartSession.php | 2 +- src/support/src/Str.php | 2 +- src/support/src/Testing/Fakes/QueueFake.php | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cache/src/RedisStore.php b/src/cache/src/RedisStore.php index 90ab1e5d6..20a77417f 100644 --- a/src/cache/src/RedisStore.php +++ b/src/cache/src/RedisStore.php @@ -248,8 +248,8 @@ public function setPrefix(string $prefix): void */ protected function serialize(mixed $value): mixed { - // is_nan() doesn't work in strict mode - return is_numeric($value) && ! in_array($value, [INF, -INF]) && ($value === $value) ? $value : serialize($value); + // is_nan() doesn't work in strict mode; NaN is the only value where $v !== $v + return is_numeric($value) && ! in_array($value, [INF, -INF]) && ($value === $value) ? $value : serialize($value); // @phpstan-ignore identical.alwaysTrue } /** diff --git a/src/filesystem/src/FilesystemManager.php b/src/filesystem/src/FilesystemManager.php index a03b6b3e6..f1101e3ed 100644 --- a/src/filesystem/src/FilesystemManager.php +++ b/src/filesystem/src/FilesystemManager.php @@ -379,7 +379,7 @@ function (&$parent) use ($config) { */ protected function createFlysystem(FlysystemAdapter $adapter, array $config): FilesystemOperator { - if ($config['read-only'] ?? false === true) { + if ($config['read-only'] ?? false === true) { // @phpstan-ignore identical.alwaysFalse (operator precedence bug, fix in separate PR) /* @phpstan-ignore-next-line */ $adapter = new ReadOnlyFilesystemAdapter($adapter); } diff --git a/src/foundation/src/Testing/TestResponseAssert.php b/src/foundation/src/Testing/TestResponseAssert.php index aadeae32f..c1deabb15 100644 --- a/src/foundation/src/Testing/TestResponseAssert.php +++ b/src/foundation/src/Testing/TestResponseAssert.php @@ -61,7 +61,7 @@ public static function __callStatic(string $name, array $arguments): void */ protected function injectResponseContext(ExpectationFailedException $exception): ExpectationFailedException { - if ($this->response->getHeader('Content-Type') === 'application/json') { + if ($this->response->getHeader('Content-Type') === 'application/json') { // @phpstan-ignore identical.alwaysFalse (getHeader returns array, fix in separate PR) $testJson = new AssertableJsonString($this->response->getContent()); if (isset($testJson['errors'])) { diff --git a/src/hashing/src/ArgonHasher.php b/src/hashing/src/ArgonHasher.php index 5086689f1..d894b4e28 100644 --- a/src/hashing/src/ArgonHasher.php +++ b/src/hashing/src/ArgonHasher.php @@ -151,7 +151,7 @@ protected function time(array $options): int */ protected function threads(array $options): int { - if (defined('PASSWORD_ARGON2_PROVIDER') && PASSWORD_ARGON2_PROVIDER === 'sodium') { + if (defined('PASSWORD_ARGON2_PROVIDER') && PASSWORD_ARGON2_PROVIDER === 'sodium') { // @phpstan-ignore identical.alwaysFalse (platform-specific constant) return 1; } diff --git a/src/hashing/src/BcryptHasher.php b/src/hashing/src/BcryptHasher.php index 5b395b448..ef746d5f9 100644 --- a/src/hashing/src/BcryptHasher.php +++ b/src/hashing/src/BcryptHasher.php @@ -39,7 +39,7 @@ public function make(string $value, array $options = []): string 'cost' => $this->cost($options), ]); - if ($hash === false) { + if ($hash === false) { // @phpstan-ignore identical.alwaysFalse (PHP 8 throws instead, kept for safety) throw new RuntimeException('Bcrypt hashing not supported.'); } diff --git a/src/session/src/Middleware/StartSession.php b/src/session/src/Middleware/StartSession.php index 7a67a5674..2927e3066 100644 --- a/src/session/src/Middleware/StartSession.php +++ b/src/session/src/Middleware/StartSession.php @@ -158,7 +158,7 @@ protected function configHitsLottery(array $config): bool protected function storeCurrentUrl(Session $session): void { if ($this->request->isMethod('GET') - && ! $this->request->header('X-Requested-With') === 'XMLHttpRequest' // is not ajax + && ! $this->request->header('X-Requested-With') === 'XMLHttpRequest' // @phpstan-ignore identical.alwaysFalse (operator precedence bug, fix in separate PR) && ! $this->isPrefetch() ) { $session->setPreviousUrl($this->request->fullUrl()); diff --git a/src/support/src/Str.php b/src/support/src/Str.php index 59d0b9b56..a4f3a7145 100644 --- a/src/support/src/Str.php +++ b/src/support/src/Str.php @@ -91,7 +91,7 @@ public static function is($pattern, $value, $ignoreCase = false): bool * Determine if a given value is a valid UUID. * * @param mixed $value - * @param null|'max'|int<0, 8> $version + * @param null|'max'|'nil'|int<0, 8> $version */ public static function isUuid($value, $version = null): bool { diff --git a/src/support/src/Testing/Fakes/QueueFake.php b/src/support/src/Testing/Fakes/QueueFake.php index 2e0551154..12e11c002 100644 --- a/src/support/src/Testing/Fakes/QueueFake.php +++ b/src/support/src/Testing/Fakes/QueueFake.php @@ -362,7 +362,7 @@ public function shouldFakeJob(object $job): bool } return $this->jobsToFake->contains( - fn ($jobToFake) => $job instanceof ((string) $jobToFake) || $job === (string) $jobToFake + fn ($jobToFake) => $job instanceof ((string) $jobToFake) ); } From 2ca65fdc9a6ac017e3b5018a71de99eb58cad5ee Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 02:59:03 +0000 Subject: [PATCH 07/28] Fix PHPStan isset.offset errors in ReflectsClosures Add @var annotation to help PHPStan understand that the Collection chain produces a list (integer-indexed array). Simplify the condition by removing redundant isset() check - after the empty($types) guard, a non-empty list always has index 0. --- src/support/src/Traits/ReflectsClosures.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/support/src/Traits/ReflectsClosures.php b/src/support/src/Traits/ReflectsClosures.php index 021453aa9..2dbc07b53 100644 --- a/src/support/src/Traits/ReflectsClosures.php +++ b/src/support/src/Traits/ReflectsClosures.php @@ -48,6 +48,7 @@ protected function firstClosureParameterTypes(Closure $closure) { $reflection = new ReflectionFunction($closure); + /** @var list> $types */ $types = Collection::make($reflection->getParameters())->mapWithKeys(function ($parameter) { if ($parameter->isVariadic()) { return [$parameter->getName() => null]; @@ -60,11 +61,10 @@ protected function firstClosureParameterTypes(Closure $closure) throw new RuntimeException('The given Closure has no parameters.'); } - if (isset($types[0]) && empty($types[0])) { + if (empty($types[0])) { throw new RuntimeException('The first parameter of the given Closure is missing a type hint.'); } - /* @phpstan-ignore-next-line */ return $types[0]; } From c05449401689be8a81b9fc6910aed845302556f2 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 03:04:11 +0000 Subject: [PATCH 08/28] Fix PHPStan nullCoalesce.offset errors - Model.php: Add ignore for defensive backtrace handling - FoundationServiceProvider.php: Add ignore for defensive backtrace handling - Reflector.php: Simplify redundant ?? after isset check (short-circuit guarantees value exists) - ValidatesAttributes.php: Fix PHPDoc from array{0: string} to array{0?: string} (confirmed rule can have 0 or 1 parameters) --- src/core/src/Database/Eloquent/Model.php | 2 +- src/foundation/src/Providers/FoundationServiceProvider.php | 2 +- src/support/src/Reflector.php | 2 +- src/validation/src/Concerns/ValidatesAttributes.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/src/Database/Eloquent/Model.php b/src/core/src/Database/Eloquent/Model.php index 2fb1e5a64..de09102d5 100644 --- a/src/core/src/Database/Eloquent/Model.php +++ b/src/core/src/Database/Eloquent/Model.php @@ -127,7 +127,7 @@ protected function guessBelongsToRelation() { [$one, $two, $three, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4); - return $caller['function'] ?? $three['function']; + return $caller['function'] ?? $three['function']; // @phpstan-ignore nullCoalesce.offset (defensive backtrace handling) } /** diff --git a/src/foundation/src/Providers/FoundationServiceProvider.php b/src/foundation/src/Providers/FoundationServiceProvider.php index 465fbdbd0..172d69148 100644 --- a/src/foundation/src/Providers/FoundationServiceProvider.php +++ b/src/foundation/src/Providers/FoundationServiceProvider.php @@ -90,7 +90,7 @@ protected function isConsoleKernelCall(Throwable $exception): bool { foreach ($exception->getTrace() as $trace) { if (($trace['class'] ?? null) === ConsoleKernel::class - && ($trace['function'] ?? null) === 'call') { + && ($trace['function'] ?? null) === 'call') { // @phpstan-ignore nullCoalesce.offset (defensive backtrace handling) return true; } } diff --git a/src/support/src/Reflector.php b/src/support/src/Reflector.php index ef9667754..ccf86166b 100644 --- a/src/support/src/Reflector.php +++ b/src/support/src/Reflector.php @@ -22,7 +22,7 @@ public static function isCallable(mixed $var, bool $syntaxOnly = false): bool } if ((! isset($var[0]) || ! isset($var[1])) - || ! is_string($var[1] ?? null)) { + || ! is_string($var[1])) { return false; } diff --git a/src/validation/src/Concerns/ValidatesAttributes.php b/src/validation/src/Concerns/ValidatesAttributes.php index ec408fa31..e3ac6c1dc 100644 --- a/src/validation/src/Concerns/ValidatesAttributes.php +++ b/src/validation/src/Concerns/ValidatesAttributes.php @@ -405,7 +405,7 @@ public function validateBoolean(string $attribute, mixed $value): bool /** * Validate that an attribute has a matching confirmation. * - * @param array{0: string} $parameters + * @param array{0?: string} $parameters */ public function validateConfirmed(string $attribute, mixed $value, mixed $parameters): bool { From 78b1396cf39cdbd19688119db13e209744bfb069 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 03:08:59 +0000 Subject: [PATCH 09/28] Fix PHPStan booleanAnd.alwaysFalse and related errors - ArgonHasher.php: Add booleanAnd.alwaysFalse to existing platform-specific ignore - Email.php, File.php, Password.php: Add ignores for instanceof.alwaysTrue and booleanAnd.alwaysFalse (PHPStan's callable|static type narrowing doesn't account for closures not being class instances) - StartSession.php: Add booleanAnd.alwaysFalse to existing bug ignore --- src/hashing/src/ArgonHasher.php | 2 +- src/session/src/Middleware/StartSession.php | 3 ++- src/validation/src/Rules/Email.php | 2 +- src/validation/src/Rules/File.php | 2 +- src/validation/src/Rules/Password.php | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/hashing/src/ArgonHasher.php b/src/hashing/src/ArgonHasher.php index d894b4e28..09c378ca9 100644 --- a/src/hashing/src/ArgonHasher.php +++ b/src/hashing/src/ArgonHasher.php @@ -151,7 +151,7 @@ protected function time(array $options): int */ protected function threads(array $options): int { - if (defined('PASSWORD_ARGON2_PROVIDER') && PASSWORD_ARGON2_PROVIDER === 'sodium') { // @phpstan-ignore identical.alwaysFalse (platform-specific constant) + if (defined('PASSWORD_ARGON2_PROVIDER') && PASSWORD_ARGON2_PROVIDER === 'sodium') { // @phpstan-ignore identical.alwaysFalse, booleanAnd.alwaysFalse (platform-specific constant) return 1; } diff --git a/src/session/src/Middleware/StartSession.php b/src/session/src/Middleware/StartSession.php index 2927e3066..4bc0067cb 100644 --- a/src/session/src/Middleware/StartSession.php +++ b/src/session/src/Middleware/StartSession.php @@ -157,8 +157,9 @@ protected function configHitsLottery(array $config): bool */ protected function storeCurrentUrl(Session $session): void { + // @phpstan-ignore-next-line booleanAnd.alwaysFalse (operator precedence bug, fix in separate PR) if ($this->request->isMethod('GET') - && ! $this->request->header('X-Requested-With') === 'XMLHttpRequest' // @phpstan-ignore identical.alwaysFalse (operator precedence bug, fix in separate PR) + && ! $this->request->header('X-Requested-With') === 'XMLHttpRequest' // @phpstan-ignore identical.alwaysFalse && ! $this->isPrefetch() ) { $session->setPreviousUrl($this->request->fullUrl()); diff --git a/src/validation/src/Rules/Email.php b/src/validation/src/Rules/Email.php index 6f9adc496..fbf0f4216 100644 --- a/src/validation/src/Rules/Email.php +++ b/src/validation/src/Rules/Email.php @@ -71,7 +71,7 @@ public static function defaults(mixed $callback = null): ?static return static::default(); } - if (! is_callable($callback) && ! $callback instanceof static) { + if (! is_callable($callback) && ! $callback instanceof static) { // @phpstan-ignore instanceof.alwaysTrue, booleanAnd.alwaysFalse (callable values like closures are not instances) throw new InvalidArgumentException('The given callback should be callable or an instance of ' . static::class); } diff --git a/src/validation/src/Rules/File.php b/src/validation/src/Rules/File.php index 126b140a3..fe824979a 100644 --- a/src/validation/src/Rules/File.php +++ b/src/validation/src/Rules/File.php @@ -82,7 +82,7 @@ public static function defaults(mixed $callback = null): ?static return static::default(); } - if (! is_callable($callback) && ! $callback instanceof static) { + if (! is_callable($callback) && ! $callback instanceof static) { // @phpstan-ignore instanceof.alwaysTrue, booleanAnd.alwaysFalse (callable values like closures are not instances) throw new InvalidArgumentException('The given callback should be callable or an instance of ' . static::class); } diff --git a/src/validation/src/Rules/Password.php b/src/validation/src/Rules/Password.php index 2a565877c..fddb32a17 100644 --- a/src/validation/src/Rules/Password.php +++ b/src/validation/src/Rules/Password.php @@ -108,7 +108,7 @@ public static function defaults(mixed $callback = null): ?static return static::default(); } - if (! is_callable($callback) && ! $callback instanceof static) { + if (! is_callable($callback) && ! $callback instanceof static) { // @phpstan-ignore instanceof.alwaysTrue, booleanAnd.alwaysFalse (callable values like closures are not instances) throw new InvalidArgumentException('The given callback should be callable or an instance of ' . static::class); } From 17e779af7cddbb5febef096b684bfcef50a907e7 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 03:32:23 +0000 Subject: [PATCH 10/28] Remove dead code and modernize validation rules InteractsWithQueue: - Remove unreachable else branch in fail() method - After type narrowing, $exception is Throwable|null, so the instanceof Throwable || is_null() condition is always true - Remove unused InvalidArgumentException import ExcludeIf/ProhibitedIf: - Change parameter type from mixed to bool|Closure to match property type - Remove redundant runtime validation (PHP type system handles it) - Remove unused InvalidArgumentException import - Update tests to expect TypeError instead of InvalidArgumentException (both correctly reject invalid types, just different exception) --- src/queue/src/InteractsWithQueue.php | 10 ++-------- src/validation/src/Rules/ExcludeIf.php | 13 ++----------- src/validation/src/Rules/ProhibitedIf.php | 13 ++----------- tests/Validation/ValidationExcludeIfTest.php | 6 +++--- tests/Validation/ValidationProhibitedIfTest.php | 6 +++--- 5 files changed, 12 insertions(+), 36 deletions(-) diff --git a/src/queue/src/InteractsWithQueue.php b/src/queue/src/InteractsWithQueue.php index 0e40c82e9..f34355939 100644 --- a/src/queue/src/InteractsWithQueue.php +++ b/src/queue/src/InteractsWithQueue.php @@ -10,7 +10,6 @@ use Hypervel\Queue\Contracts\Job as JobContract; use Hypervel\Queue\Exceptions\ManuallyFailedException; use Hypervel\Queue\Jobs\FakeJob; -use InvalidArgumentException; use PHPUnit\Framework\Assert as PHPUnit; use RuntimeException; use Throwable; @@ -52,13 +51,8 @@ public function fail(string|Throwable|null $exception = null): void $exception = new ManuallyFailedException($exception); } - if ($exception instanceof Throwable || is_null($exception)) { - if ($this->job) { - $this->job->fail($exception); - return; - } - } else { - throw new InvalidArgumentException('The fail method requires a string or an instance of Throwable.'); + if ($this->job) { + $this->job->fail($exception); } } diff --git a/src/validation/src/Rules/ExcludeIf.php b/src/validation/src/Rules/ExcludeIf.php index fd62155d3..a958b6504 100644 --- a/src/validation/src/Rules/ExcludeIf.php +++ b/src/validation/src/Rules/ExcludeIf.php @@ -5,7 +5,6 @@ namespace Hypervel\Validation\Rules; use Closure; -use InvalidArgumentException; use Stringable; class ExcludeIf implements Stringable @@ -17,18 +16,10 @@ class ExcludeIf implements Stringable /** * Create a new exclude validation rule based on a condition. - * - * @param bool|Closure $condition - * - * @throws InvalidArgumentException */ - public function __construct(mixed $condition) + public function __construct(bool|Closure $condition) { - if ($condition instanceof Closure || is_bool($condition)) { - $this->condition = $condition; - } else { - throw new InvalidArgumentException('The provided condition must be a callable or boolean.'); - } + $this->condition = $condition; } /** diff --git a/src/validation/src/Rules/ProhibitedIf.php b/src/validation/src/Rules/ProhibitedIf.php index 965b91743..06f5c78c2 100644 --- a/src/validation/src/Rules/ProhibitedIf.php +++ b/src/validation/src/Rules/ProhibitedIf.php @@ -5,7 +5,6 @@ namespace Hypervel\Validation\Rules; use Closure; -use InvalidArgumentException; use Stringable; class ProhibitedIf implements Stringable @@ -17,18 +16,10 @@ class ProhibitedIf implements Stringable /** * Create a new prohibited validation rule based on a condition. - * - * @param bool|Closure $condition - * - * @throws InvalidArgumentException */ - public function __construct(mixed $condition) + public function __construct(bool|Closure $condition) { - if ($condition instanceof Closure || is_bool($condition)) { - $this->condition = $condition; - } else { - throw new InvalidArgumentException('The provided condition must be a callable or boolean.'); - } + $this->condition = $condition; } /** diff --git a/tests/Validation/ValidationExcludeIfTest.php b/tests/Validation/ValidationExcludeIfTest.php index 82cc78276..5fb719be2 100644 --- a/tests/Validation/ValidationExcludeIfTest.php +++ b/tests/Validation/ValidationExcludeIfTest.php @@ -9,9 +9,9 @@ use Hypervel\Translation\Translator; use Hypervel\Validation\Rules\ExcludeIf; use Hypervel\Validation\Validator; -use InvalidArgumentException; use PHPUnit\Framework\TestCase; use stdClass; +use TypeError; /** * @internal @@ -52,8 +52,8 @@ public function testItValidatesCallableAndBooleanAreAcceptableArguments() try { new ExcludeIf($condition); $this->fail('The ExcludeIf constructor must not accept ' . gettype($condition)); - } catch (InvalidArgumentException $exception) { - $this->assertEquals('The provided condition must be a callable or boolean.', $exception->getMessage()); + } catch (TypeError) { + $this->assertTrue(true); // Invalid types correctly rejected by PHP type system } } } diff --git a/tests/Validation/ValidationProhibitedIfTest.php b/tests/Validation/ValidationProhibitedIfTest.php index 73be86129..74a5cfc8e 100644 --- a/tests/Validation/ValidationProhibitedIfTest.php +++ b/tests/Validation/ValidationProhibitedIfTest.php @@ -9,9 +9,9 @@ use Hypervel\Translation\Translator; use Hypervel\Validation\Rules\ProhibitedIf; use Hypervel\Validation\Validator; -use InvalidArgumentException; use PHPUnit\Framework\TestCase; use stdClass; +use TypeError; /** * @internal @@ -52,8 +52,8 @@ public function testItValidatesCallableAndBooleanAreAcceptableArguments() try { new ProhibitedIf($condition); $this->fail('The ProhibitedIf constructor must not accept ' . gettype($condition)); - } catch (InvalidArgumentException $exception) { - $this->assertEquals('The provided condition must be a callable or boolean.', $exception->getMessage()); + } catch (TypeError) { + $this->assertTrue(true); // Invalid types correctly rejected by PHP type system } } } From e7b3d3e2da270065ec34eb172f156cf7c1e36a77 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 03:38:04 +0000 Subject: [PATCH 11/28] Fix PHPStan instanceof.alwaysTrue errors - CoreMiddleware: Remove incorrect @var annotation that asserted type before validation (let instanceof narrow the mixed return from getAttribute) - HasPermission/HasRole: Add ignores for match expression type narrowing (PHPStan doesn't track types perfectly across match arms) - Pipe/Pool: Add ignores for defensive validation of collection elements - QueryWatcher: Add ignore for PDO check with fallback code --- src/http/src/CoreMiddleware.php | 1 - src/permission/src/Traits/HasPermission.php | 2 +- src/permission/src/Traits/HasRole.php | 2 +- src/process/src/Pipe.php | 2 +- src/process/src/Pool.php | 2 +- src/telescope/src/Watchers/QueryWatcher.php | 2 +- 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/http/src/CoreMiddleware.php b/src/http/src/CoreMiddleware.php index 48269c6bd..2b9b642f6 100644 --- a/src/http/src/CoreMiddleware.php +++ b/src/http/src/CoreMiddleware.php @@ -159,7 +159,6 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface { $request = RequestContext::set($request); - /** @var DispatchedRoute $dispatched */ $dispatched = $request->getAttribute(Dispatched::class); if (! $dispatched instanceof DispatchedRoute) { diff --git a/src/permission/src/Traits/HasPermission.php b/src/permission/src/Traits/HasPermission.php index cca1d8c32..b79049ad9 100644 --- a/src/permission/src/Traits/HasPermission.php +++ b/src/permission/src/Traits/HasPermission.php @@ -393,7 +393,7 @@ private function isPermissionIdType(BackedEnum|int|string|UnitEnum $permission): return match (true) { is_int($permission) => true, $permission instanceof BackedEnum => is_int($permission->value), - is_string($permission), $permission instanceof UnitEnum => false, + is_string($permission), $permission instanceof UnitEnum => false, // @phpstan-ignore instanceof.alwaysTrue default => throw new InvalidArgumentException('Invalid permission type') }; } diff --git a/src/permission/src/Traits/HasRole.php b/src/permission/src/Traits/HasRole.php index 7869cd35d..960906772 100644 --- a/src/permission/src/Traits/HasRole.php +++ b/src/permission/src/Traits/HasRole.php @@ -142,7 +142,7 @@ private function isRoleIdType(BackedEnum|int|string|UnitEnum $role): bool return match (true) { is_int($role) => true, $role instanceof BackedEnum => is_int($role->value), - is_string($role), $role instanceof UnitEnum => false, + is_string($role), $role instanceof UnitEnum => false, // @phpstan-ignore instanceof.alwaysTrue default => throw new InvalidArgumentException('Invalid role type') }; } diff --git a/src/process/src/Pipe.php b/src/process/src/Pipe.php index 40a25acea..acf7fa43c 100644 --- a/src/process/src/Pipe.php +++ b/src/process/src/Pipe.php @@ -52,7 +52,7 @@ public function run(?callable $output = null): ProcessResultContract return (new Collection($this->pendingProcesses)) ->reduce(function ($previousProcessResult, $pendingProcess, $key) use ($output) { - if (! $pendingProcess instanceof PendingProcess) { + if (! $pendingProcess instanceof PendingProcess) { // @phpstan-ignore instanceof.alwaysTrue (defensive validation) throw new InvalidArgumentException('Process pipe must only contain pending processes.'); } diff --git a/src/process/src/Pool.php b/src/process/src/Pool.php index 455da22a6..5eed4e96d 100644 --- a/src/process/src/Pool.php +++ b/src/process/src/Pool.php @@ -55,7 +55,7 @@ public function start(?callable $output = null): InvokedProcessPool return new InvokedProcessPool( (new Collection($this->pendingProcesses)) ->each(function ($pendingProcess) { - if (! $pendingProcess instanceof PendingProcess) { + if (! $pendingProcess instanceof PendingProcess) { // @phpstan-ignore instanceof.alwaysTrue (defensive validation) throw new InvalidArgumentException('Process pool must only contain pending processes.'); } })->mapWithKeys(function ($pendingProcess, $key) use ($output) { diff --git a/src/telescope/src/Watchers/QueryWatcher.php b/src/telescope/src/Watchers/QueryWatcher.php index cc62a6153..2c2ffe832 100644 --- a/src/telescope/src/Watchers/QueryWatcher.php +++ b/src/telescope/src/Watchers/QueryWatcher.php @@ -112,7 +112,7 @@ protected function quoteStringBinding(QueryExecuted $event, string $binding): st try { $pdo = $event->connection->getPdo(); - if ($pdo instanceof PDO) { + if ($pdo instanceof PDO) { // @phpstan-ignore instanceof.alwaysTrue (fallback exists for edge cases) return $pdo->quote($binding); } } catch (PDOException $e) { From 7619b270796d9fb4a6a3beca7de68f5b0ea0c976 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 03:41:51 +0000 Subject: [PATCH 12/28] Fix PHPStan if.alwaysTrue errors - Mailable: Fix PHPDoc @var callable to @var ?callable (property can be null) - UrlGenerator: Fix PHPDoc @var Closure to @var ?Closure for format callbacks - PendingBatch: Remove dead if check (store() returns non-nullable Batch) - TestCase: Add ignore for Mockery::getContainer() defensive check --- src/bus/src/PendingBatch.php | 4 +--- src/foundation/src/Testing/TestCase.php | 2 +- src/mail/src/Mailable.php | 2 +- src/router/src/UrlGenerator.php | 4 ++-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/bus/src/PendingBatch.php b/src/bus/src/PendingBatch.php index 569417ce3..0f5524bee 100644 --- a/src/bus/src/PendingBatch.php +++ b/src/bus/src/PendingBatch.php @@ -275,9 +275,7 @@ public function dispatchAfterResponse(): Batch $batch = $this->store($repository); - if ($batch) { - Coroutine::defer(fn () => $this->dispatchExistingBatch($batch)); - } + Coroutine::defer(fn () => $this->dispatchExistingBatch($batch)); return $batch; } diff --git a/src/foundation/src/Testing/TestCase.php b/src/foundation/src/Testing/TestCase.php index 04c3bea8f..24f8af7df 100644 --- a/src/foundation/src/Testing/TestCase.php +++ b/src/foundation/src/Testing/TestCase.php @@ -133,7 +133,7 @@ protected function tearDown(): void } if (class_exists('Mockery')) { - if ($container = Mockery::getContainer()) { + if ($container = Mockery::getContainer()) { // @phpstan-ignore if.alwaysTrue (defensive check) $this->addToAssertionCount($container->mockery_getExpectationCount()); } diff --git a/src/mail/src/Mailable.php b/src/mail/src/Mailable.php index 21f63a98d..1c035a794 100644 --- a/src/mail/src/Mailable.php +++ b/src/mail/src/Mailable.php @@ -153,7 +153,7 @@ class Mailable implements MailableContract, Renderable /** * The callback that should be invoked while building the view data. * - * @var callable + * @var ?callable */ public static $viewDataCallback; diff --git a/src/router/src/UrlGenerator.php b/src/router/src/UrlGenerator.php index 27afc651f..68f080e1c 100644 --- a/src/router/src/UrlGenerator.php +++ b/src/router/src/UrlGenerator.php @@ -33,14 +33,14 @@ class UrlGenerator implements UrlGeneratorContract /** * The callback to use to format hosts. * - * @var Closure + * @var ?Closure */ protected $formatHostUsing; /** * The callback to use to format paths. * - * @var Closure + * @var ?Closure */ protected $formatPathUsing; From a1fbe102e69e2837bc824c887f706aee69b885cd Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 03:51:25 +0000 Subject: [PATCH 13/28] Fix PHPStan ternary.alwaysFalse errors in BoundMethod Simplify ternary operators that always evaluate to false: - Lines 43-44 use `$defaultMethod ?: '__invoke'` but at this point $defaultMethod is guaranteed to be falsy (line 32 returns early when truthy) - Replace with just `'__invoke'` since that's always the result --- src/container/src/BoundMethod.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/container/src/BoundMethod.php b/src/container/src/BoundMethod.php index 112806d9b..eee19c551 100644 --- a/src/container/src/BoundMethod.php +++ b/src/container/src/BoundMethod.php @@ -40,8 +40,8 @@ public static function call(ContainerContract $container, $callback, array $para } // object method call - if (is_object($callback) && method_exists($callback, $defaultMethod ?: '__invoke')) { - $callback = [$callback, $defaultMethod ?: '__invoke']; + if (is_object($callback) && method_exists($callback, '__invoke')) { + $callback = [$callback, '__invoke']; } // static method call From b927c71685fcd225cefcc67ffb72b70c3fe0312f Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 03:59:19 +0000 Subject: [PATCH 14/28] Fix PHPStan ternary/boolean errors - Translator: Fix PHPDoc @var callable to @var ?callable (property can be null) - ValidatesAttributes: Remove dead ?: null (Carbon::parse returns Carbon) - ClosureValidationRule/InvokableValidationRule: Add ignores for callback state tracking (PHPStan can't track that callback sets $this->failed) --- src/translation/src/Translator.php | 2 +- src/validation/src/ClosureValidationRule.php | 2 +- src/validation/src/Concerns/ValidatesAttributes.php | 2 +- src/validation/src/InvokableValidationRule.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/translation/src/Translator.php b/src/translation/src/Translator.php index 179e903b7..f1f2e2b09 100644 --- a/src/translation/src/Translator.php +++ b/src/translation/src/Translator.php @@ -39,7 +39,7 @@ class Translator extends NamespacedItemResolver implements TranslatorContract /** * The callable that should be invoked to determine applicable locales. * - * @var callable + * @var ?callable */ protected $determineLocalesUsing; diff --git a/src/validation/src/ClosureValidationRule.php b/src/validation/src/ClosureValidationRule.php index 14527c91c..1c3268413 100644 --- a/src/validation/src/ClosureValidationRule.php +++ b/src/validation/src/ClosureValidationRule.php @@ -52,7 +52,7 @@ public function passes(string $attribute, mixed $value): bool return $this->pendingPotentiallyTranslatedString($attribute, $message); }, $this->validator); - return ! $this->failed; + return ! $this->failed; // @phpstan-ignore booleanNot.alwaysTrue (callback sets $this->failed) } /** diff --git a/src/validation/src/Concerns/ValidatesAttributes.php b/src/validation/src/Concerns/ValidatesAttributes.php index e3ac6c1dc..a6421cd51 100644 --- a/src/validation/src/Concerns/ValidatesAttributes.php +++ b/src/validation/src/Concerns/ValidatesAttributes.php @@ -276,7 +276,7 @@ protected function getDateTimeWithOptionalFormat(string $format, string $value): protected function getDateTime(DateTimeInterface|string $value): ?DateTime { try { - return @Carbon::parse($value) ?: null; + return @Carbon::parse($value); } catch (Exception) { } diff --git a/src/validation/src/InvokableValidationRule.php b/src/validation/src/InvokableValidationRule.php index c8c6ad4e9..08b3e5b76 100644 --- a/src/validation/src/InvokableValidationRule.php +++ b/src/validation/src/InvokableValidationRule.php @@ -84,7 +84,7 @@ public function passes(string $attribute, mixed $value): bool return $this->pendingPotentiallyTranslatedString($attribute, $message); }); - return ! $this->failed; + return ! $this->failed; // @phpstan-ignore booleanNot.alwaysTrue (callback sets $this->failed) } /** From e5d53f5dc70077d05ee00019df67306ae9c2ef42 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 04:03:45 +0000 Subject: [PATCH 15/28] Fix PHPStan booleanAnd.leftAlwaysTrue errors - RedisQueue: Remove redundant $this->container check (already non-nullable) - ThrottlesExceptions: Fix PHPDoc @var callable to @var ?callable for $reportCallback and $whenCallback (properties can be null) --- src/horizon/src/RedisQueue.php | 2 +- src/queue/src/Middleware/ThrottlesExceptions.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/horizon/src/RedisQueue.php b/src/horizon/src/RedisQueue.php index 18799d7a4..509cefa76 100644 --- a/src/horizon/src/RedisQueue.php +++ b/src/horizon/src/RedisQueue.php @@ -150,7 +150,7 @@ public function deleteAndRelease(string $queue, RedisJob $job, DateInterval|Date */ protected function event(string $queue, mixed $event): void { - if ($this->container && $this->container->has(Dispatcher::class)) { + if ($this->container->has(Dispatcher::class)) { $queue = Str::replaceFirst('queues:', '', $queue); $this->container->get(Dispatcher::class)->dispatch( diff --git a/src/queue/src/Middleware/ThrottlesExceptions.php b/src/queue/src/Middleware/ThrottlesExceptions.php index d178de833..6f184fff4 100644 --- a/src/queue/src/Middleware/ThrottlesExceptions.php +++ b/src/queue/src/Middleware/ThrottlesExceptions.php @@ -28,14 +28,14 @@ class ThrottlesExceptions /** * The callback that determines if the exception should be reported. * - * @var callable + * @var ?callable */ protected $reportCallback; /** * The callback that determines if rate limiting should apply. * - * @var callable + * @var ?callable */ protected $whenCallback; From 713d35474944b4217130dde30a93f26e9f54cdd1 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 04:06:05 +0000 Subject: [PATCH 16/28] Fix PHPStan nullCoalesce.property and staticMethod errors - Factory: Fix PHPDoc to allow null for $modelNameResolver, $factoryNameResolver - Sleep/EventFake: Add ignores for intentional assertTrue(true) assertion counting --- src/core/src/Database/Eloquent/Factories/Factory.php | 4 ++-- src/support/src/Sleep.php | 2 +- src/support/src/Testing/Fakes/EventFake.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/src/Database/Eloquent/Factories/Factory.php b/src/core/src/Database/Eloquent/Factories/Factory.php index d3484fda4..525255cec 100644 --- a/src/core/src/Database/Eloquent/Factories/Factory.php +++ b/src/core/src/Database/Eloquent/Factories/Factory.php @@ -96,14 +96,14 @@ abstract class Factory /** * The default model name resolver. * - * @var callable(self): class-string + * @var null|(callable(self): class-string) */ protected static $modelNameResolver; /** * The factory name resolver. * - * @var callable(class-string): class-string + * @var null|(callable(class-string): class-string) */ protected static $factoryNameResolver; diff --git a/src/support/src/Sleep.php b/src/support/src/Sleep.php index e78cfac0c..322aa3878 100644 --- a/src/support/src/Sleep.php +++ b/src/support/src/Sleep.php @@ -397,7 +397,7 @@ public static function assertNeverSlept(): void public static function assertInsomniac(): void { if (static::$sequence === []) { - PHPUnit::assertTrue(true); + PHPUnit::assertTrue(true); // @phpstan-ignore staticMethod.alreadyNarrowedType (intentional for assertion count) } foreach (static::$sequence as $duration) { diff --git a/src/support/src/Testing/Fakes/EventFake.php b/src/support/src/Testing/Fakes/EventFake.php index 6d2cd1da6..3db561d6e 100644 --- a/src/support/src/Testing/Fakes/EventFake.php +++ b/src/support/src/Testing/Fakes/EventFake.php @@ -78,7 +78,7 @@ public function assertListening(string $expectedEvent, string $expectedListener) if ($actualListener === $expectedListener || ($actualListener instanceof Closure && $expectedListener === Closure::class)) { - PHPUnit::assertTrue(true); + PHPUnit::assertTrue(true); // @phpstan-ignore staticMethod.alreadyNarrowedType (intentional for assertion count) return; } From 368914a760081d79c7b68cdd45cb72734c25b32b Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 04:10:54 +0000 Subject: [PATCH 17/28] Fix PHPStan nullCoalesce.expr errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove redundant double ?? [] patterns in ProviderConfig, RegisterProviders, RegisterFacades, VendorPublishCommand - Fix operator precedence issues: (int) $x ?? 0 → (int) ($x ?? 0) in SqsQueue, $a . $b ?? '' → $a . ($b ?? '') in Event - Remove dead ?? fallbacks after string casts/functions that never return null - Add ignore for defensive fallback in exception handler --- src/config/src/ProviderConfig.php | 6 +++--- src/console/src/Scheduling/Event.php | 2 +- src/devtool/src/Generator/ObserverCommand.php | 2 +- src/devtool/src/Generator/PolicyCommand.php | 2 +- src/foundation/src/Bootstrap/RegisterFacades.php | 2 +- src/foundation/src/Bootstrap/RegisterProviders.php | 2 +- .../src/Console/Commands/VendorPublishCommand.php | 4 ++-- src/foundation/src/Exceptions/Handler.php | 2 +- src/http/src/Request.php | 2 +- src/queue/src/SqsQueue.php | 6 +++--- src/support/src/HtmlString.php | 2 +- src/validation/src/Concerns/ValidatesAttributes.php | 2 +- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/config/src/ProviderConfig.php b/src/config/src/ProviderConfig.php index 0e8f88b20..0bf9c29c6 100644 --- a/src/config/src/ProviderConfig.php +++ b/src/config/src/ProviderConfig.php @@ -36,9 +36,9 @@ public static function load(): array $providers = array_map( fn (array $package) => array_merge( - Arr::wrap(($package['hyperf']['config'] ?? []) ?? []), - Arr::wrap(($package['hypervel']['config'] ?? []) ?? []), - Arr::wrap(($package['hypervel']['providers'] ?? []) ?? []), + Arr::wrap($package['hyperf']['config'] ?? []), + Arr::wrap($package['hypervel']['config'] ?? []), + Arr::wrap($package['hypervel']['providers'] ?? []), ), Composer::getMergedExtra() ); diff --git a/src/console/src/Scheduling/Event.php b/src/console/src/Scheduling/Event.php index 7cec1b41d..e1c270f12 100644 --- a/src/console/src/Scheduling/Event.php +++ b/src/console/src/Scheduling/Event.php @@ -711,7 +711,7 @@ public function mutexName(): string } return 'framework' . DIRECTORY_SEPARATOR . 'schedule-' - . sha1($this->expression . $this->command ?? ''); + . sha1($this->expression . ($this->command ?? '')); } /** diff --git a/src/devtool/src/Generator/ObserverCommand.php b/src/devtool/src/Generator/ObserverCommand.php index 0550b10d7..ecc6476e6 100644 --- a/src/devtool/src/Generator/ObserverCommand.php +++ b/src/devtool/src/Generator/ObserverCommand.php @@ -31,7 +31,7 @@ protected function replaceClass(string $stub, string $name): string if (! $model = trim($this->input->getOption('model') ?? '')) { $modelParts = explode('\\', $name); $model = end($modelParts); - $model = Str::ucfirst(Str::before($model, 'Observer')) ?? 'Dummy'; + $model = Str::ucfirst(Str::before($model, 'Observer')); } $modelNamespace = $this->getConfig()['model_namespace'] ?? 'App\Models'; diff --git a/src/devtool/src/Generator/PolicyCommand.php b/src/devtool/src/Generator/PolicyCommand.php index 898f0b6cc..85c22cce6 100644 --- a/src/devtool/src/Generator/PolicyCommand.php +++ b/src/devtool/src/Generator/PolicyCommand.php @@ -34,7 +34,7 @@ protected function replaceClass(string $stub, string $name): string if (! $model = trim($this->input->getOption('model') ?? '')) { $modelParts = explode('\\', $name); $model = end($modelParts); - $model = Str::ucfirst(Str::before($model, 'Policy')) ?? 'Dummy'; + $model = Str::ucfirst(Str::before($model, 'Policy')); } $modelNamespace = $this->getConfig()['model_namespace'] ?? 'App\Models'; diff --git a/src/foundation/src/Bootstrap/RegisterFacades.php b/src/foundation/src/Bootstrap/RegisterFacades.php index e3de8ea4e..9c9eda920 100644 --- a/src/foundation/src/Bootstrap/RegisterFacades.php +++ b/src/foundation/src/Bootstrap/RegisterFacades.php @@ -22,7 +22,7 @@ public function bootstrap(ApplicationContract $app): void $composerAliases = []; try { - $composerAliases = Arr::wrap(Composer::getJsonContent()['extra']['hypervel']['aliases']) ?? []; + $composerAliases = Arr::wrap(Composer::getJsonContent()['extra']['hypervel']['aliases'] ?? []); } catch (Throwable $e) { // do nothing } diff --git a/src/foundation/src/Bootstrap/RegisterProviders.php b/src/foundation/src/Bootstrap/RegisterProviders.php index 777ad425d..4e938b64d 100644 --- a/src/foundation/src/Bootstrap/RegisterProviders.php +++ b/src/foundation/src/Bootstrap/RegisterProviders.php @@ -23,7 +23,7 @@ public function bootstrap(ApplicationContract $app): void if (! in_array('*', $packagesToIgnore)) { $providers = array_map( - fn (array $package) => Arr::wrap(($package['hypervel']['providers'] ?? []) ?? []), + fn (array $package) => Arr::wrap($package['hypervel']['providers'] ?? []), Composer::getMergedExtra() ); $providers = array_filter( diff --git a/src/foundation/src/Console/Commands/VendorPublishCommand.php b/src/foundation/src/Console/Commands/VendorPublishCommand.php index 2416114ac..76a91a944 100644 --- a/src/foundation/src/Console/Commands/VendorPublishCommand.php +++ b/src/foundation/src/Console/Commands/VendorPublishCommand.php @@ -115,8 +115,8 @@ protected function getPackagePublishes(): array $extra = Composer::getMergedExtra(); $packages = array_map( fn (array $package) => array_merge( - Arr::wrap(($package['hyperf'] ?? []) ?? []), - Arr::wrap(($package['hypervel'] ?? []) ?? []), + Arr::wrap($package['hyperf'] ?? []), + Arr::wrap($package['hypervel'] ?? []), ), $extra ); diff --git a/src/foundation/src/Exceptions/Handler.php b/src/foundation/src/Exceptions/Handler.php index a8788c999..f5993bf5a 100644 --- a/src/foundation/src/Exceptions/Handler.php +++ b/src/foundation/src/Exceptions/Handler.php @@ -770,7 +770,7 @@ protected function prepareException(Throwable $e): Throwable $e instanceof ModelNotFoundException => new NotFoundHttpException($e->getMessage(), 0, $e), $e instanceof AuthorizationException && $e->hasStatus() => new HttpException( $e->status(), - $e->response()?->message() ?: (BaseResponse::getReasonPhraseByCode($e->status()) ?? 'Whoops, looks like something went wrong.'), + $e->response()?->message() ?: (BaseResponse::getReasonPhraseByCode($e->status()) ?? 'Whoops, looks like something went wrong.'), // @phpstan-ignore nullCoalesce.expr (defensive fallback) $e->getCode(), $e ), diff --git a/src/http/src/Request.php b/src/http/src/Request.php index 76d4acaae..3da99ee8d 100644 --- a/src/http/src/Request.php +++ b/src/http/src/Request.php @@ -456,7 +456,7 @@ public function wantsJson(): bool { $acceptable = explode(',', $this->header('Accept') ?? ''); - return Str::contains(strtolower($acceptable[0]) ?? '', ['/json', '+json']); + return Str::contains(strtolower($acceptable[0]), ['/json', '+json']); } /** diff --git a/src/queue/src/SqsQueue.php b/src/queue/src/SqsQueue.php index db67ed3bf..45eb5059c 100644 --- a/src/queue/src/SqsQueue.php +++ b/src/queue/src/SqsQueue.php @@ -70,7 +70,7 @@ public function pendingSize(?string $queue = null): int 'AttributeNames' => ['ApproximateNumberOfMessages'], ]); - return (int) $response['Attributes']['ApproximateNumberOfMessages'] ?? 0; + return (int) ($response['Attributes']['ApproximateNumberOfMessages'] ?? 0); } /** @@ -83,7 +83,7 @@ public function delayedSize(?string $queue = null): int 'AttributeNames' => ['ApproximateNumberOfMessagesDelayed'], ]); - return (int) $response['Attributes']['ApproximateNumberOfMessagesDelayed'] ?? 0; + return (int) ($response['Attributes']['ApproximateNumberOfMessagesDelayed'] ?? 0); } /** @@ -96,7 +96,7 @@ public function reservedSize(?string $queue = null): int 'AttributeNames' => ['ApproximateNumberOfMessagesNotVisible'], ]); - return (int) $response['Attributes']['ApproximateNumberOfMessagesNotVisible'] ?? 0; + return (int) ($response['Attributes']['ApproximateNumberOfMessagesNotVisible'] ?? 0); } /** diff --git a/src/support/src/HtmlString.php b/src/support/src/HtmlString.php index bb4690c12..c76f0600b 100644 --- a/src/support/src/HtmlString.php +++ b/src/support/src/HtmlString.php @@ -51,6 +51,6 @@ public function isNotEmpty(): bool */ public function __toString(): string { - return $this->toHtml() ?? ''; + return $this->toHtml(); } } diff --git a/src/validation/src/Concerns/ValidatesAttributes.php b/src/validation/src/Concerns/ValidatesAttributes.php index a6421cd51..a994eeaa9 100644 --- a/src/validation/src/Concerns/ValidatesAttributes.php +++ b/src/validation/src/Concerns/ValidatesAttributes.php @@ -2198,7 +2198,7 @@ protected function getSize(string $attribute, mixed $value): float|int|string return $value->getSize() / 1024; } - return mb_strlen((string) $value ?? ''); + return mb_strlen((string) $value); } /** From 36d043eb15a4afcf2ca70a06946eacb8b4040ce6 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 04:14:12 +0000 Subject: [PATCH 18/28] Fix PHPStan miscellaneous type errors - Progress: Add ignore for match arm always-true (correct exhaustive logic) - Listener: Add ignore for intentional infinite while(true) loop - Reflector: Remove redundant is_string check (already validated above) - ExtractProperties: Add ignore for PHP version check - MessageSelector: Add ignore for Arabic plural formula (modulo <= 99) - Prompt/ValidatesAttributes: Add ignores for defensive null checks - EventFake: Add ignore for intentional assertTrue(false) test failure --- src/prompts/src/Progress.php | 2 +- src/prompts/src/Prompt.php | 2 +- src/queue/src/Listener.php | 2 +- src/support/src/Reflector.php | 3 +-- src/support/src/Testing/Fakes/EventFake.php | 2 +- src/telescope/src/ExtractProperties.php | 2 +- src/translation/src/MessageSelector.php | 2 +- src/validation/src/Concerns/ValidatesAttributes.php | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/prompts/src/Progress.php b/src/prompts/src/Progress.php index 550233e49..17a7eac89 100644 --- a/src/prompts/src/Progress.php +++ b/src/prompts/src/Progress.php @@ -39,7 +39,7 @@ public function __construct(public string $label, public int|iterable $steps, pu $this->total = match (true) { // @phpstan-ignore assign.propertyType is_int($this->steps) => $this->steps, is_countable($this->steps) => count($this->steps), - is_iterable($this->steps) => iterator_count($this->steps), + is_iterable($this->steps) => iterator_count($this->steps), // @phpstan-ignore match.alwaysTrue default => throw new InvalidArgumentException('Unable to count steps.'), }; diff --git a/src/prompts/src/Prompt.php b/src/prompts/src/Prompt.php index 8eaf0ef8b..486d39f6f 100644 --- a/src/prompts/src/Prompt.php +++ b/src/prompts/src/Prompt.php @@ -167,7 +167,7 @@ public function prompt(): mixed */ public function runLoop(callable $callable): mixed { - while (($key = static::terminal()->read()) !== null) { + while (($key = static::terminal()->read()) !== null) { // @phpstan-ignore notIdentical.alwaysTrue /** * If $key is an empty string, Terminal::read * has failed. We can continue to the next diff --git a/src/queue/src/Listener.php b/src/queue/src/Listener.php index 8eb5cc525..cf1105704 100644 --- a/src/queue/src/Listener.php +++ b/src/queue/src/Listener.php @@ -61,7 +61,7 @@ public function listen(?string $connection, string $queue, ListenerOptions $opti { $process = $this->makeProcess($connection, $queue, $options); - while (true) { + while (true) { // @phpstan-ignore while.alwaysTrue (intentional infinite loop) $this->runProcess($process, $options->memory); if ($options->rest) { diff --git a/src/support/src/Reflector.php b/src/support/src/Reflector.php index ccf86166b..66310db93 100644 --- a/src/support/src/Reflector.php +++ b/src/support/src/Reflector.php @@ -27,8 +27,7 @@ public static function isCallable(mixed $var, bool $syntaxOnly = false): bool } if ($syntaxOnly - && (is_string($var[0]) || is_object($var[0])) - && is_string($var[1])) { + && (is_string($var[0]) || is_object($var[0]))) { return true; } diff --git a/src/support/src/Testing/Fakes/EventFake.php b/src/support/src/Testing/Fakes/EventFake.php index 3db561d6e..0f222134e 100644 --- a/src/support/src/Testing/Fakes/EventFake.php +++ b/src/support/src/Testing/Fakes/EventFake.php @@ -84,7 +84,7 @@ public function assertListening(string $expectedEvent, string $expectedListener) } } - PHPUnit::assertTrue( + PHPUnit::assertTrue( // @phpstan-ignore staticMethod.impossibleType (intentional test failure) false, sprintf( 'Event [%s] does not have the [%s] listener attached to it', diff --git a/src/telescope/src/ExtractProperties.php b/src/telescope/src/ExtractProperties.php index e21d31900..de66d68b1 100644 --- a/src/telescope/src/ExtractProperties.php +++ b/src/telescope/src/ExtractProperties.php @@ -21,7 +21,7 @@ public static function from(mixed $target): array ->mapWithKeys(function ($property) use ($target) { $property->setAccessible(true); - if (PHP_VERSION_ID >= 70400 && ! $property->isInitialized($target)) { + if (PHP_VERSION_ID >= 70400 && ! $property->isInitialized($target)) { // @phpstan-ignore greaterOrEqual.alwaysTrue return []; } diff --git a/src/translation/src/MessageSelector.php b/src/translation/src/MessageSelector.php index a8bcf4e69..68e1bdb08 100644 --- a/src/translation/src/MessageSelector.php +++ b/src/translation/src/MessageSelector.php @@ -390,7 +390,7 @@ public function getPluralIndex(string $locale, float|int $number): int case 'ar_SY': case 'ar_TN': case 'ar_YE': - return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5)))); + return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5)))); // @phpstan-ignore smallerOrEqual.alwaysTrue default: return 0; } diff --git a/src/validation/src/Concerns/ValidatesAttributes.php b/src/validation/src/Concerns/ValidatesAttributes.php index a994eeaa9..a27feed35 100644 --- a/src/validation/src/Concerns/ValidatesAttributes.php +++ b/src/validation/src/Concerns/ValidatesAttributes.php @@ -2166,7 +2166,7 @@ public function validateUuid(string $attribute, mixed $value, mixed $parameters) { $version = null; - if ($parameters !== null && count($parameters) === 1) { + if ($parameters !== null && count($parameters) === 1) { // @phpstan-ignore notIdentical.alwaysTrue $version = $parameters[0]; if ($version !== 'max') { From 9ad0acbf2484dc75a464ca5e53b11bfb758e41ce Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 04:34:29 +0000 Subject: [PATCH 19/28] Fix PHPStan function.impossibleType errors (batch 1) - Authorize: Remove dead is_null() check on array parameter, fix return type - BroadcastManager: Remove redundant is_null() on string parameter - CacheManager: Remove redundant is_null() on string parameter - SwooleTableManager: Remove redundant is_null() on string parameter - DatabaseStore: Remove no-op .map() converting arrays to objects (already objects) --- src/auth/src/Middleware/Authorize.php | 6 +----- src/broadcasting/src/BroadcastManager.php | 2 +- src/cache/src/CacheManager.php | 2 +- src/cache/src/DatabaseStore.php | 8 ++------ src/cache/src/SwooleTableManager.php | 2 +- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/auth/src/Middleware/Authorize.php b/src/auth/src/Middleware/Authorize.php index fec5ec026..ec842d893 100644 --- a/src/auth/src/Middleware/Authorize.php +++ b/src/auth/src/Middleware/Authorize.php @@ -50,12 +50,8 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface /** * Get the arguments parameter for the gate. */ - protected function getGateArguments(ServerRequestInterface $request, array $models): array|Model|string + protected function getGateArguments(ServerRequestInterface $request, array $models): array { - if (is_null($models)) { - return []; - } - return Collection::make($models)->map(function ($model) use ($request) { return $model instanceof Model ? $model : $this->getModel($request, $model); })->all(); diff --git a/src/broadcasting/src/BroadcastManager.php b/src/broadcasting/src/BroadcastManager.php index e72ad20f5..ce965909e 100644 --- a/src/broadcasting/src/BroadcastManager.php +++ b/src/broadcasting/src/BroadcastManager.php @@ -387,7 +387,7 @@ protected function createNullDriver(array $config): Broadcaster */ protected function getConfig(string $name): ?array { - if (! is_null($name) && $name !== 'null') { + if ($name !== 'null') { return $this->app->get(ConfigInterface::class)->get("broadcasting.connections.{$name}"); } diff --git a/src/cache/src/CacheManager.php b/src/cache/src/CacheManager.php index 2cd9b3995..63a5b0a68 100644 --- a/src/cache/src/CacheManager.php +++ b/src/cache/src/CacheManager.php @@ -326,7 +326,7 @@ protected function getPrefix(array $config): string */ protected function getConfig(string $name): ?array { - if (! is_null($name) && $name !== 'null') { + if ($name !== 'null') { return $this->app->get(ConfigInterface::class)->get("cache.stores.{$name}"); } diff --git a/src/cache/src/DatabaseStore.php b/src/cache/src/DatabaseStore.php index 28ca054d9..8752ad4dd 100644 --- a/src/cache/src/DatabaseStore.php +++ b/src/cache/src/DatabaseStore.php @@ -104,16 +104,12 @@ public function many(array $keys): array $results = array_fill_keys($keys, null); // First we will retrieve all of the items from the cache using their keys and - // the prefix value. Then we will need to iterate through each of the items - // and convert them to an object when they are currently in array format. + // the prefix value. $values = $this->table() ->whereIn('key', array_map(function ($key) { return $this->prefix . $key; }, $keys)) - ->get() - ->map(function ($value) { - return is_array($value) ? (object) $value : $value; - }); + ->get(); $currentTime = $this->currentTime(); diff --git a/src/cache/src/SwooleTableManager.php b/src/cache/src/SwooleTableManager.php index d124f1665..58dce43e5 100644 --- a/src/cache/src/SwooleTableManager.php +++ b/src/cache/src/SwooleTableManager.php @@ -54,7 +54,7 @@ protected function resolve(string $name): Table protected function getConfig(string $name): ?array { - if (! is_null($name) && $name !== 'null') { + if ($name !== 'null') { return $this->app->get(ConfigInterface::class)->get("cache.swoole_tables.{$name}"); } From d3be8ae8f2c07e268221c5cca81604fc55a79d7a Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 04:41:45 +0000 Subject: [PATCH 20/28] Fix PHPStan function.impossibleType errors (batch 2) - Container: Use hasContainer() instead of is_null(getContainer()) - CallQueuedListener: Add ignore for defensive deserialization check - Kernel: Fix PHPDoc to include null for optional upload files - TestResponseAssert: Remove dead is_string() check on Throwable parameter - ContinueSupervisorCommand/PauseSupervisorCommand: Check === 0 instead of is_null after (int) cast - MailManager: Fix return type to ?array (config lookup can return null) - MailMessage: Make priority nullable with default null - QueueManager: Remove redundant is_null() on string parameter - RedisQueue: Remove dead if wrapper (retryAfter always int) --- src/container/src/Container.php | 3 +-- src/event/illuminate/CallQueuedListener.php | 2 +- src/event/src/CallQueuedListener.php | 2 +- src/foundation/src/Http/Kernel.php | 4 ++-- src/foundation/src/Testing/TestResponseAssert.php | 2 +- src/horizon/src/Console/ContinueSupervisorCommand.php | 2 +- src/horizon/src/Console/PauseSupervisorCommand.php | 2 +- src/mail/src/MailManager.php | 2 +- src/notifications/src/Messages/MailMessage.php | 2 +- src/queue/src/QueueManager.php | 2 +- src/queue/src/RedisQueue.php | 5 +---- 11 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/container/src/Container.php b/src/container/src/Container.php index 384505cce..8ae9e0460 100644 --- a/src/container/src/Container.php +++ b/src/container/src/Container.php @@ -682,13 +682,12 @@ public function flush(): void */ public static function getInstance(): ContainerContract { - if (is_null(ApplicationContext::getContainer())) { + if (! ApplicationContext::hasContainer()) { ApplicationContext::setContainer( new static(new DefinitionSource([])) ); } - /* @phpstan-ignore-next-line */ return ApplicationContext::getContainer(); } diff --git a/src/event/illuminate/CallQueuedListener.php b/src/event/illuminate/CallQueuedListener.php index 418f29904..53346030c 100644 --- a/src/event/illuminate/CallQueuedListener.php +++ b/src/event/illuminate/CallQueuedListener.php @@ -114,7 +114,7 @@ public function failed(Throwable $e): void */ protected function prepareData(): void { - if (is_string($this->data)) { + if (is_string($this->data)) { // @phpstan-ignore function.impossibleType (defensive: deserialization can bypass type checks) $this->data = unserialize($this->data); } } diff --git a/src/event/src/CallQueuedListener.php b/src/event/src/CallQueuedListener.php index 8a29d9b5c..8b842ff20 100644 --- a/src/event/src/CallQueuedListener.php +++ b/src/event/src/CallQueuedListener.php @@ -114,7 +114,7 @@ public function failed(Throwable $e): void */ protected function prepareData(): void { - if (is_string($this->data)) { + if (is_string($this->data)) { // @phpstan-ignore function.impossibleType (defensive: deserialization can bypass type checks) $this->data = unserialize($this->data); } } diff --git a/src/foundation/src/Http/Kernel.php b/src/foundation/src/Http/Kernel.php index 4bb0529bc..a64044582 100644 --- a/src/foundation/src/Http/Kernel.php +++ b/src/foundation/src/Http/Kernel.php @@ -104,8 +104,8 @@ public function onRequest($swooleRequest, $swooleResponse): void /** * Convert the given array of Hyperf UploadedFiles to custom Hypervel UploadedFiles. * - * @param array $files - * @return array + * @param array $files + * @return array */ protected function convertUploadedFiles(array $files): array { diff --git a/src/foundation/src/Testing/TestResponseAssert.php b/src/foundation/src/Testing/TestResponseAssert.php index c1deabb15..69c942e92 100644 --- a/src/foundation/src/Testing/TestResponseAssert.php +++ b/src/foundation/src/Testing/TestResponseAssert.php @@ -77,7 +77,7 @@ protected function injectResponseContext(ExpectationFailedException $exception): */ protected function appendExceptionToException(Throwable $exceptionToAppend, ExpectationFailedException $exception): ExpectationFailedException { - $exceptionMessage = is_string($exceptionToAppend) ? $exceptionToAppend : $exceptionToAppend->getMessage(); + $exceptionMessage = $exceptionToAppend->getMessage(); $exceptionToAppend = (string) $exceptionToAppend; diff --git a/src/horizon/src/Console/ContinueSupervisorCommand.php b/src/horizon/src/Console/ContinueSupervisorCommand.php index 277dd6ca3..034207a9d 100644 --- a/src/horizon/src/Console/ContinueSupervisorCommand.php +++ b/src/horizon/src/Console/ContinueSupervisorCommand.php @@ -32,7 +32,7 @@ public function handle(SupervisorRepository $supervisors): int && Str::endsWith($supervisor->name, $this->argument('name')); }))->pid; - if (is_null($processId)) { + if ($processId === 0) { $this->components->error('Failed to find a supervisor with this name'); return 1; diff --git a/src/horizon/src/Console/PauseSupervisorCommand.php b/src/horizon/src/Console/PauseSupervisorCommand.php index 366400f60..0a58dae4b 100644 --- a/src/horizon/src/Console/PauseSupervisorCommand.php +++ b/src/horizon/src/Console/PauseSupervisorCommand.php @@ -32,7 +32,7 @@ public function handle(SupervisorRepository $supervisors): int && Str::endsWith($supervisor->name, $this->argument('name')); }))->pid; - if (is_null($processId)) { + if ($processId === 0) { $this->components->error('Failed to find a supervisor with this name'); return 1; diff --git a/src/mail/src/MailManager.php b/src/mail/src/MailManager.php index d40ce4b6b..460459db1 100644 --- a/src/mail/src/MailManager.php +++ b/src/mail/src/MailManager.php @@ -455,7 +455,7 @@ protected function setGlobalAddress(Mailer $mailer, array $config, string $type) /** * Get the mail connection configuration. */ - protected function getConfig(string $name): array + protected function getConfig(string $name): ?array { // Here we will check if the "driver" key exists and if it does we will use // the entire mail configuration file as the "driver" config in order to diff --git a/src/notifications/src/Messages/MailMessage.php b/src/notifications/src/Messages/MailMessage.php index a48100744..d8c4f3272 100644 --- a/src/notifications/src/Messages/MailMessage.php +++ b/src/notifications/src/Messages/MailMessage.php @@ -80,7 +80,7 @@ class MailMessage extends SimpleMessage implements Renderable /** * Priority level of the message. */ - public int $priority; + public ?int $priority = null; /** * The callbacks for the message. diff --git a/src/queue/src/QueueManager.php b/src/queue/src/QueueManager.php index 81667b86d..780bc7358 100644 --- a/src/queue/src/QueueManager.php +++ b/src/queue/src/QueueManager.php @@ -214,7 +214,7 @@ public function addConnector(string $driver, Closure $resolver): void */ protected function getConfig(string $name): ?array { - if (! is_null($name) && $name !== 'null') { + if ($name !== 'null') { return $this->config->get("queue.connections.{$name}"); } diff --git a/src/queue/src/RedisQueue.php b/src/queue/src/RedisQueue.php index bd986ac54..99008019e 100644 --- a/src/queue/src/RedisQueue.php +++ b/src/queue/src/RedisQueue.php @@ -236,10 +236,7 @@ public function pop(?string $queue = null, int $index = 0): ?JobContract protected function migrate(string $queue): void { $this->migrateExpiredJobs($queue . ':delayed', $queue); - - if (! is_null($this->retryAfter)) { - $this->migrateExpiredJobs($queue . ':reserved', $queue); - } + $this->migrateExpiredJobs($queue . ':reserved', $queue); } /** From 61a852ce011b790e907b6aa029e2553b9ad1f0fe Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 04:45:15 +0000 Subject: [PATCH 21/28] Fix PHPStan function.impossibleType errors (batch 3) - VendorPublishCommand: Add ignore for defensive choice() null check - SanctumGuard: Remove dead null check on non-nullable provider - DataObject: Remove dead null check (already guarded by is_array check) - Manager: Remove dead null check (getDefaultDriver returns string) - ServiceProvider: Add ignore for known logic bug (fix in separate PR) --- .../src/Console/Commands/VendorPublishCommand.php | 2 +- src/sanctum/src/SanctumGuard.php | 4 ---- src/support/src/DataObject.php | 2 +- src/support/src/Manager.php | 7 ------- src/support/src/ServiceProvider.php | 2 +- 5 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/foundation/src/Console/Commands/VendorPublishCommand.php b/src/foundation/src/Console/Commands/VendorPublishCommand.php index 76a91a944..1e69c195c 100644 --- a/src/foundation/src/Console/Commands/VendorPublishCommand.php +++ b/src/foundation/src/Console/Commands/VendorPublishCommand.php @@ -103,7 +103,7 @@ protected function promptForProvider(Collection $publishes): ?string ->prepend($all = 'All providers') ->all() ); - if ($choice == $all || is_null($choice)) { + if ($choice == $all || is_null($choice)) { // @phpstan-ignore function.impossibleType (defensive: choice() could return null on interrupt) return null; } diff --git a/src/sanctum/src/SanctumGuard.php b/src/sanctum/src/SanctumGuard.php index 64175b101..d133bc821 100644 --- a/src/sanctum/src/SanctumGuard.php +++ b/src/sanctum/src/SanctumGuard.php @@ -192,10 +192,6 @@ protected function isValidAccessToken(?PersonalAccessToken $accessToken): bool */ protected function hasValidProvider(?Authenticatable $tokenable): bool { - if (is_null($this->provider)) { - return true; - } - if (! method_exists($this->provider, 'getModel')) { return true; } diff --git a/src/support/src/DataObject.php b/src/support/src/DataObject.php index 0ed692431..a018423e0 100644 --- a/src/support/src/DataObject.php +++ b/src/support/src/DataObject.php @@ -335,7 +335,7 @@ protected static function replaceDependenciesData(array $dependencies, array $da continue; } - if ($children && ! is_null($matched)) { + if ($children) { $data[$key] = static::replaceDependenciesData($children, $matched); } diff --git a/src/support/src/Manager.php b/src/support/src/Manager.php index 6b073404a..9851b30e3 100644 --- a/src/support/src/Manager.php +++ b/src/support/src/Manager.php @@ -50,13 +50,6 @@ public function driver(?string $driver = null): mixed { $driver = $driver ?: $this->getDefaultDriver(); - if (is_null($driver)) { - throw new InvalidArgumentException(sprintf( - 'Unable to resolve NULL driver for [%s].', - static::class - )); - } - // If the given driver has not been created before, we will create the instances // here and cache it so we can return it next time very quickly. If there is // already a driver created by this name, we'll just return that instance. diff --git a/src/support/src/ServiceProvider.php b/src/support/src/ServiceProvider.php index a9752f3e9..663a0e063 100644 --- a/src/support/src/ServiceProvider.php +++ b/src/support/src/ServiceProvider.php @@ -244,7 +244,7 @@ protected function addPublishGroup(string $group, array $paths): void */ public static function pathsToPublish(?string $provider = null, ?string $group = null): array { - if (! is_null($paths = static::pathsForProviderOrGroup($provider, $group))) { + if (! is_null($paths = static::pathsForProviderOrGroup($provider, $group))) { // @phpstan-ignore function.impossibleType (logic bug: method always returns array, fix in separate PR) return $paths; } From 1b85eac5734fc5050cc4275050ce57d7586f97d7 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 05:02:10 +0000 Subject: [PATCH 22/28] Fix PHPStan function.alreadyNarrowedType errors (batch 1) Remove redundant type checks where PHP's type system already guarantees the type: - Remove dead is_string/is_array/is_int checks after type narrowing - Remove method_exists for methods that always exist (PHP 8+, interface contracts) - Remove property_exists checks on typed properties - Add ignores for legitimate defensive checks (platform compat, PHPDoc validation) --- src/api-client/src/PendingRequest.php | 2 +- src/auth/src/Access/Gate.php | 4 ---- src/broadcasting/src/BroadcastManager.php | 6 +----- src/broadcasting/src/Broadcasters/Broadcaster.php | 12 ++++++------ .../src/Broadcasters/PusherBroadcaster.php | 2 +- src/cache/src/Console/ClearCommand.php | 8 +++----- src/cache/src/Functions.php | 2 +- src/console/src/Scheduling/Event.php | 2 +- src/console/src/Scheduling/Schedule.php | 2 +- src/container/src/Container.php | 5 ++--- src/encryption/src/Encrypter.php | 4 ---- src/hashing/src/ArgonHasher.php | 2 +- src/horizon/src/Console/ClearCommand.php | 7 ------- src/horizon/src/Horizon.php | 2 +- src/horizon/src/Tags.php | 4 +--- src/http-client/src/PendingRequest.php | 4 +--- src/mail/src/Mailer.php | 2 +- src/mail/src/Transport/SesTransport.php | 2 +- src/mail/src/Transport/SesV2Transport.php | 2 +- 19 files changed, 24 insertions(+), 50 deletions(-) diff --git a/src/api-client/src/PendingRequest.php b/src/api-client/src/PendingRequest.php index c838fe2aa..43f578f61 100644 --- a/src/api-client/src/PendingRequest.php +++ b/src/api-client/src/PendingRequest.php @@ -155,7 +155,7 @@ public function withResource(string $resource): static ); } - if (! is_subclass_of($resource, ApiResource::class)) { + if (! is_subclass_of($resource, ApiResource::class)) { // @phpstan-ignore function.alreadyNarrowedType (validates PHPDoc contract at runtime) throw new InvalidArgumentException( sprintf('Resource class `%s` must be a subclass of `%s`', $resource, ApiResource::class) ); diff --git a/src/auth/src/Access/Gate.php b/src/auth/src/Access/Gate.php index fddcdc9fc..57ffc3f86 100644 --- a/src/auth/src/Access/Gate.php +++ b/src/auth/src/Access/Gate.php @@ -492,10 +492,6 @@ public function getPolicyFor(object|string $class) $class = get_class($class); } - if (! is_string($class)) { - return; - } - if (isset($this->policies[$class])) { return $this->resolvePolicy($this->policies[$class]); } diff --git a/src/broadcasting/src/BroadcastManager.php b/src/broadcasting/src/BroadcastManager.php index ce965909e..addeca686 100644 --- a/src/broadcasting/src/BroadcastManager.php +++ b/src/broadcasting/src/BroadcastManager.php @@ -207,11 +207,7 @@ public function queue(mixed $event): void */ protected function mustBeUniqueAndCannotAcquireLock(UniqueBroadcastEvent $event): bool { - return ! (new UniqueLock( - method_exists($event, 'uniqueVia') - ? $event->uniqueVia() - : $this->app->get(Cache::class) - ))->acquire($event); + return ! (new UniqueLock($event->uniqueVia()))->acquire($event); } /** diff --git a/src/broadcasting/src/Broadcasters/Broadcaster.php b/src/broadcasting/src/Broadcasters/Broadcaster.php index 41c930f16..da5c40d96 100644 --- a/src/broadcasting/src/Broadcasters/Broadcaster.php +++ b/src/broadcasting/src/Broadcasters/Broadcaster.php @@ -73,7 +73,7 @@ public function channel(HasBroadcastChannel|string $channel, callable|string $ca { if ($channel instanceof HasBroadcastChannel) { $channel = $channel->broadcastChannelRoute(); - } elseif (is_string($channel) && class_exists($channel) && is_a($channel, HasBroadcastChannel::class, true)) { + } elseif (class_exists($channel) && is_a($channel, HasBroadcastChannel::class, true)) { $channel = (new $channel())->broadcastChannelRoute(); } @@ -134,11 +134,11 @@ protected function extractAuthParameters(string $pattern, string $channel, calla */ protected function extractParameters(callable|string $callback): array { - return match (true) { - is_callable($callback) => (new ReflectionFunction($callback))->getParameters(), - is_string($callback) => $this->extractParametersFromClass($callback), - default => [], - }; + if (is_callable($callback)) { + return (new ReflectionFunction($callback))->getParameters(); + } + + return $this->extractParametersFromClass($callback); } /** diff --git a/src/broadcasting/src/Broadcasters/PusherBroadcaster.php b/src/broadcasting/src/Broadcasters/PusherBroadcaster.php index a89e7c397..66adeb614 100644 --- a/src/broadcasting/src/Broadcasters/PusherBroadcaster.php +++ b/src/broadcasting/src/Broadcasters/PusherBroadcaster.php @@ -38,7 +38,7 @@ public function resolveAuthenticatedUser(RequestInterface $request): ?array return null; } - if (method_exists($this->pusher, 'authenticateUser')) { + if (method_exists($this->pusher, 'authenticateUser')) { // @phpstan-ignore function.alreadyNarrowedType (Pusher 6.x compatibility) return json_decode( $this->pusher->authenticateUser($request->input('socket_id'), $user), true, diff --git a/src/cache/src/Console/ClearCommand.php b/src/cache/src/Console/ClearCommand.php index c8f3bebf9..bc5ea3f9a 100644 --- a/src/cache/src/Console/ClearCommand.php +++ b/src/cache/src/Console/ClearCommand.php @@ -35,11 +35,9 @@ public function handle(): ?int $this->app->get(EventDispatcherInterface::class) ->dispatch('cache:clearing', [$this->argument('store'), $this->tags()]); - if (method_exists($store = $this->cache()->getStore(), 'flush')) { - if (! $store->flush()) { - $this->error('Failed to clear cache. Make sure you have the appropriate permissions.'); - return 1; - } + if (! $this->cache()->getStore()->flush()) { + $this->error('Failed to clear cache. Make sure you have the appropriate permissions.'); + return 1; } $this->flushRuntime(); diff --git a/src/cache/src/Functions.php b/src/cache/src/Functions.php index 9e77e6509..d18af2f6c 100644 --- a/src/cache/src/Functions.php +++ b/src/cache/src/Functions.php @@ -30,7 +30,7 @@ function cache($key = null, $default = null) return $manager->get($key, $default); } - if (! is_array($key)) { + if (! is_array($key)) { // @phpstan-ignore function.alreadyNarrowedType (validates PHPDoc contract at runtime) throw new InvalidArgumentException( 'When setting a value in the cache, you must pass an array of key / value pairs.' ); diff --git a/src/console/src/Scheduling/Event.php b/src/console/src/Scheduling/Event.php index e1c270f12..e758757b0 100644 --- a/src/console/src/Scheduling/Event.php +++ b/src/console/src/Scheduling/Event.php @@ -706,7 +706,7 @@ public function mutexName(): string { $mutexNameResolver = $this->mutexNameResolver; - if (! is_null($mutexNameResolver) && is_callable($mutexNameResolver)) { + if (! is_null($mutexNameResolver)) { return $mutexNameResolver($this); } diff --git a/src/console/src/Scheduling/Schedule.php b/src/console/src/Scheduling/Schedule.php index 04d7db4ed..1b9493ebe 100644 --- a/src/console/src/Scheduling/Schedule.php +++ b/src/console/src/Scheduling/Schedule.php @@ -380,7 +380,7 @@ protected function getDispatcher(): Dispatcher } catch (BindingResolutionException $e) { throw new RuntimeException( 'Unable to resolve the dispatcher from the service container. Please bind it or install the hypervel/bus package.', - is_int($e->getCode()) ? $e->getCode() : 0, + $e->getCode(), $e ); } diff --git a/src/container/src/Container.php b/src/container/src/Container.php index 8ae9e0460..2c85a3b19 100644 --- a/src/container/src/Container.php +++ b/src/container/src/Container.php @@ -688,7 +688,7 @@ public static function getInstance(): ContainerContract ); } - return ApplicationContext::getContainer(); + return ApplicationContext::getContainer(); // @phpstan-ignore return.type (we just set it with static() above) } /** @@ -696,8 +696,7 @@ public static function getInstance(): ContainerContract */ public static function setInstance(ContainerContract $container): ContainerContract { - /* @phpstan-ignore-next-line */ - return ApplicationContext::setContainer($container); + return ApplicationContext::setContainer($container); // @phpstan-ignore return.type } public function offsetExists(mixed $offset): bool diff --git a/src/encryption/src/Encrypter.php b/src/encryption/src/Encrypter.php index 879f94585..87943704f 100644 --- a/src/encryption/src/Encrypter.php +++ b/src/encryption/src/Encrypter.php @@ -202,10 +202,6 @@ protected function hash(string $iv, mixed $value, string $key): string */ protected function getJsonPayload(string $payload): array { - if (! is_string($payload)) { - throw new DecryptException('The payload is invalid.'); - } - $payload = json_decode(base64_decode($payload), true); // If the payload is not valid JSON or does not have the proper keys set we will diff --git a/src/hashing/src/ArgonHasher.php b/src/hashing/src/ArgonHasher.php index 09c378ca9..78dc033d1 100644 --- a/src/hashing/src/ArgonHasher.php +++ b/src/hashing/src/ArgonHasher.php @@ -53,7 +53,7 @@ public function make(string $value, array $options = []): string 'threads' => $this->threads($options), ]); - if (! is_string($hash)) { + if (! is_string($hash)) { // @phpstan-ignore function.alreadyNarrowedType (password_hash returns false if algorithm unavailable) throw new RuntimeException('Argon2 hashing not supported.'); } diff --git a/src/horizon/src/Console/ClearCommand.php b/src/horizon/src/Console/ClearCommand.php index 20b022eb5..aaa63591f 100644 --- a/src/horizon/src/Console/ClearCommand.php +++ b/src/horizon/src/Console/ClearCommand.php @@ -7,7 +7,6 @@ use Hypervel\Console\Command; use Hypervel\Console\ConfirmableTrait; use Hypervel\Horizon\Contracts\JobRepository; -use Hypervel\Horizon\RedisQueue; use Hypervel\Queue\QueueManager; use Hypervel\Support\Arr; @@ -37,12 +36,6 @@ public function handle(JobRepository $jobRepository, QueueManager $manager): ?in return 1; } - if (! method_exists(RedisQueue::class, 'clear')) { - $this->components->error('Clearing queues is not supported on this version of Laravel.'); - - return 1; - } - $connection = $this->argument('connection') ?: Arr::first(config('horizon.defaults'))['connection'] ?? 'redis'; $queue = $this->getQueue($connection); diff --git a/src/horizon/src/Horizon.php b/src/horizon/src/Horizon.php index 40cbabd91..1c66800ec 100644 --- a/src/horizon/src/Horizon.php +++ b/src/horizon/src/Horizon.php @@ -77,7 +77,7 @@ public static function use(string $connection): void { if (! is_null($config = config("database.redis.clusters.{$connection}.0"))) { config(["database.redis.{$connection}" => $config]); - } elseif (is_null($config) && is_null($config = config("database.redis.{$connection}"))) { + } elseif (is_null($config = config("database.redis.{$connection}"))) { throw new Exception("Redis connection [{$connection}] has not been configured."); } diff --git a/src/horizon/src/Tags.php b/src/horizon/src/Tags.php index 00a65e61d..f46a3c2cd 100644 --- a/src/horizon/src/Tags.php +++ b/src/horizon/src/Tags.php @@ -118,9 +118,7 @@ public static function modelsFor(array $targets): Collection */ protected static function getValue(ReflectionProperty $property, mixed $target): mixed { - if (method_exists($property, 'isInitialized') - && ! $property->isInitialized($target) - ) { + if (! $property->isInitialized($target)) { return null; } diff --git a/src/http-client/src/PendingRequest.php b/src/http-client/src/PendingRequest.php index 64fa44043..1c06f3c97 100644 --- a/src/http-client/src/PendingRequest.php +++ b/src/http-client/src/PendingRequest.php @@ -989,9 +989,7 @@ protected function parseRequestData(string $method, string $url, array $options) } if (is_string($data)) { - parse_str($data, $parsedData); - - $data = is_array($parsedData) ? $parsedData : []; + parse_str($data, $data); } if ($data instanceof JsonSerializable) { diff --git a/src/mail/src/Mailer.php b/src/mail/src/Mailer.php index 8d374de15..663c072be 100644 --- a/src/mail/src/Mailer.php +++ b/src/mail/src/Mailer.php @@ -297,7 +297,7 @@ protected function parseView(array|Closure|string $view): array // If the given view is an array with numeric keys, we will just assume that // both a "pretty" and "plain" view were provided, so we will return this // array as is, since it should contain both views with numerical keys. - if (is_array($view) && isset($view[0])) { + if (isset($view[0])) { return [$view[0], $view[1], null]; } diff --git a/src/mail/src/Transport/SesTransport.php b/src/mail/src/Transport/SesTransport.php index 2bc0772f6..f26fbd27e 100644 --- a/src/mail/src/Transport/SesTransport.php +++ b/src/mail/src/Transport/SesTransport.php @@ -59,7 +59,7 @@ protected function doSend(SentMessage $message): void throw new TransportException( sprintf('Request to AWS SES API failed. Reason: %s.', $reason), - is_int($e->getCode()) ? $e->getCode() : 0, + $e->getCode(), $e ); } diff --git a/src/mail/src/Transport/SesV2Transport.php b/src/mail/src/Transport/SesV2Transport.php index 0cc0c315d..aeae41ad7 100644 --- a/src/mail/src/Transport/SesV2Transport.php +++ b/src/mail/src/Transport/SesV2Transport.php @@ -63,7 +63,7 @@ protected function doSend(SentMessage $message): void throw new TransportException( sprintf('Request to AWS SES V2 API failed. Reason: %s.', $reason), - is_int($e->getCode()) ? $e->getCode() : 0, + $e->getCode(), $e ); } From 02d116c3506ef2275a02f4cb3e71372638a62189 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 05:11:20 +0000 Subject: [PATCH 23/28] Fix PHPStan function.alreadyNarrowedType errors (batch 2) Complete PHPStan level 4 compliance: - Remove dead type checks where PHP types already guarantee the type - Add ignores for trait flexibility patterns (property_exists, method_exists) - Add ignores for interface optional methods (@method PHPDoc) - Add ignores for defensive checks on external/event data - Simplify telescope/cache watchers using actual property types --- src/object-pool/src/Traits/HasPoolProxy.php | 1 + src/permission/src/Traits/HasPermission.php | 4 ++++ src/queue/src/Console/RetryCommand.php | 2 ++ src/sanctum/src/SanctumGuard.php | 1 + src/sentry/src/Features/CacheFeature.php | 1 + src/sentry/src/Features/RedisFeature.php | 1 + src/telescope/src/IncomingExceptionEntry.php | 5 +---- src/telescope/src/Watchers/CacheWatcher.php | 5 ++--- src/telescope/src/Watchers/RequestWatcher.php | 22 +++++++++---------- .../src/Concerns/ValidatesAttributes.php | 3 ++- src/validation/src/Rules/AnyOf.php | 7 ------ 11 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/object-pool/src/Traits/HasPoolProxy.php b/src/object-pool/src/Traits/HasPoolProxy.php index 9b707c382..4b2e8e357 100644 --- a/src/object-pool/src/Traits/HasPoolProxy.php +++ b/src/object-pool/src/Traits/HasPoolProxy.php @@ -21,6 +21,7 @@ trait HasPoolProxy protected function createPoolProxy(string $driver, Closure $resolver, array $config = [], ?string $proxyClass = null): mixed { if (! $proxyClass) { + // @phpstan-ignore function.alreadyNarrowedType (trait flexibility: using class may not define poolProxyClass) $proxyClass = property_exists($this, 'poolProxyClass') ? $this->poolProxyClass : PoolProxy::class; diff --git a/src/permission/src/Traits/HasPermission.php b/src/permission/src/Traits/HasPermission.php index b79049ad9..2d508f7d5 100644 --- a/src/permission/src/Traits/HasPermission.php +++ b/src/permission/src/Traits/HasPermission.php @@ -137,6 +137,7 @@ public function getPermissionsViaRoles(): BaseCollection $allRolesWithPermissions = $manager->getAllRolesWithPermissions(); // Get cached roles (this method should be available if HasRole trait is used) + // @phpstan-ignore function.alreadyNarrowedType (trait flexibility: HasRole may not be used) if (method_exists($this, 'getCachedRoles')) { $ownerRoles = $this->getCachedRoles(); } else { @@ -206,6 +207,7 @@ public function hasPermissionViaRoles(BackedEnum|int|string|UnitEnum $permission $allRolesWithPermissions = $manager->getAllRolesWithPermissions(); // Get cached roles (this method should be available if HasRole trait is used) + // @phpstan-ignore function.alreadyNarrowedType (trait flexibility: HasRole may not be used) if (method_exists($this, 'getCachedRoles')) { $ownerRoles = $this->getCachedRoles(); } else { @@ -469,6 +471,7 @@ public function hasForbiddenPermission(BackedEnum|int|string|UnitEnum $permissio */ public function hasForbiddenPermissionViaRoles(BackedEnum|int|string|UnitEnum $permission): bool { + // @phpstan-ignore function.alreadyNarrowedType (trait used by both Role and non-Role models) if (is_a(static::class, Role::class, true)) { return false; } @@ -478,6 +481,7 @@ public function hasForbiddenPermissionViaRoles(BackedEnum|int|string|UnitEnum $p $allRolesWithPermissions = $manager->getAllRolesWithPermissions(); // Get cached roles (this method should be available if HasRole trait is used) + // @phpstan-ignore function.alreadyNarrowedType (trait flexibility: HasRole may not be used) if (method_exists($this, 'getCachedRoles')) { $ownerRoles = $this->getCachedRoles(); } else { diff --git a/src/queue/src/Console/RetryCommand.php b/src/queue/src/Console/RetryCommand.php index b3a1e952d..6a8ffb051 100644 --- a/src/queue/src/Console/RetryCommand.php +++ b/src/queue/src/Console/RetryCommand.php @@ -82,6 +82,7 @@ protected function getJobIds(): array $ids = (array) $this->argument('id'); if (count($ids) === 1 && $ids[0] === 'all') { + // @phpstan-ignore function.alreadyNarrowedType (@method PHPDoc is optional, not required) return method_exists($this->failer, 'ids') ? $this->failer->ids() : Arr::pluck($this->failer->all(), 'id'); @@ -103,6 +104,7 @@ protected function getJobIds(): array */ protected function getJobIdsByQueue(string $queue): array { + // @phpstan-ignore function.alreadyNarrowedType (@method PHPDoc is optional, not required) $ids = method_exists($this->failer, 'ids') ? $this->failer->ids($queue) : Collection::make($this->failer->all()) diff --git a/src/sanctum/src/SanctumGuard.php b/src/sanctum/src/SanctumGuard.php index d133bc821..bec3bdebc 100644 --- a/src/sanctum/src/SanctumGuard.php +++ b/src/sanctum/src/SanctumGuard.php @@ -156,6 +156,7 @@ protected function isValidBearerToken(?string $token = null): bool if (! is_null($token) && str_contains($token, '|')) { $model = new (Sanctum::$personalAccessTokenModel); + // @phpstan-ignore function.alreadyNarrowedType (custom token models may not extend Model) if (method_exists($model, 'getKeyType') && $model->getKeyType() === 'int') { [$id, $token] = explode('|', $token, 2); diff --git a/src/sentry/src/Features/CacheFeature.php b/src/sentry/src/Features/CacheFeature.php index 95b44fbf0..575c8556b 100644 --- a/src/sentry/src/Features/CacheFeature.php +++ b/src/sentry/src/Features/CacheFeature.php @@ -264,6 +264,7 @@ private function replaceSessionKeys(array $values): array $sessionKey = $this->getSessionKey(); return array_map(static function ($value) use ($sessionKey) { + // @phpstan-ignore function.alreadyNarrowedType (defensive: event data may contain non-strings) return is_string($value) && $value === $sessionKey ? '{sessionKey}' : $value; }, $values); } diff --git a/src/sentry/src/Features/RedisFeature.php b/src/sentry/src/Features/RedisFeature.php index 54cf04669..deedc828b 100644 --- a/src/sentry/src/Features/RedisFeature.php +++ b/src/sentry/src/Features/RedisFeature.php @@ -130,6 +130,7 @@ private function replaceSessionKeys(array $values): array $sessionKey = $this->getSessionKey(); return array_map(static function ($value) use ($sessionKey) { + // @phpstan-ignore function.alreadyNarrowedType (defensive: event data may contain non-strings) return is_string($value) && $value === $sessionKey ? '{sessionKey}' : $value; }, $values); } diff --git a/src/telescope/src/IncomingExceptionEntry.php b/src/telescope/src/IncomingExceptionEntry.php index 4345accbf..a2f83d1da 100644 --- a/src/telescope/src/IncomingExceptionEntry.php +++ b/src/telescope/src/IncomingExceptionEntry.php @@ -26,10 +26,7 @@ public function __construct( */ public function isReportableException(): bool { - $handler = app(ExceptionHandlerContract::class); - - return method_exists($handler, 'shouldReport') - ? $handler->shouldReport($this->exception) : true; + return app(ExceptionHandlerContract::class)->shouldReport($this->exception); } /** diff --git a/src/telescope/src/Watchers/CacheWatcher.php b/src/telescope/src/Watchers/CacheWatcher.php index df44ceb92..e65a6cc10 100644 --- a/src/telescope/src/Watchers/CacheWatcher.php +++ b/src/telescope/src/Watchers/CacheWatcher.php @@ -137,10 +137,9 @@ private function shouldHideValue(mixed $event): bool ); } - protected function formatExpiration(KeyWritten $event): mixed + protected function formatExpiration(KeyWritten $event): ?int { - return property_exists($event, 'seconds') - ? $event->seconds : $event->minutes * 60; /* @phpstan-ignore-line */ + return $event->seconds; } /** diff --git a/src/telescope/src/Watchers/RequestWatcher.php b/src/telescope/src/Watchers/RequestWatcher.php index 1a907851f..79bec2c25 100644 --- a/src/telescope/src/Watchers/RequestWatcher.php +++ b/src/telescope/src/Watchers/RequestWatcher.php @@ -229,18 +229,16 @@ protected function response(ResponseInterface $response): array|string return 'Purged By Telescope: ' . $e->getMessage(); } - if (is_string($content)) { - if (! $this->contentWithinLimits($content)) { - return 'Purged By Telescope'; - } - if (is_array(json_decode($content, true)) - && json_last_error() === JSON_ERROR_NONE - ) { - return $this->hideParameters(json_decode($content, true), Telescope::$hiddenResponseParameters); - } - if (Str::startsWith(strtolower($response->getHeaderLine('Content-Type') ?: ''), 'text/plain')) { - return $content; - } + if (! $this->contentWithinLimits($content)) { + return 'Purged By Telescope'; + } + if (is_array(json_decode($content, true)) + && json_last_error() === JSON_ERROR_NONE + ) { + return $this->hideParameters(json_decode($content, true), Telescope::$hiddenResponseParameters); + } + if (Str::startsWith(strtolower($response->getHeaderLine('Content-Type') ?: ''), 'text/plain')) { + return $content; } $statusCode = $response->getStatusCode(); diff --git a/src/validation/src/Concerns/ValidatesAttributes.php b/src/validation/src/Concerns/ValidatesAttributes.php index a27feed35..6d7620ff7 100644 --- a/src/validation/src/Concerns/ValidatesAttributes.php +++ b/src/validation/src/Concerns/ValidatesAttributes.php @@ -722,6 +722,7 @@ protected function getDistinctValues(string $attribute): array { $attributeName = $this->getPrimaryAttribute($attribute); + // @phpstan-ignore function.alreadyNarrowedType (trait flexibility: using class may not have distinctValues cache) if (! property_exists($this, 'distinctValues')) { return $this->extractDistinctValues($attributeName); } @@ -1196,7 +1197,7 @@ public function validateImage(string $attribute, mixed $value, array $parameters { $mimes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']; - if (is_array($parameters) && in_array('allow_svg', $parameters)) { + if (in_array('allow_svg', $parameters)) { $mimes[] = 'svg'; } diff --git a/src/validation/src/Rules/AnyOf.php b/src/validation/src/Rules/AnyOf.php index f7436b651..2e14c4c8b 100644 --- a/src/validation/src/Rules/AnyOf.php +++ b/src/validation/src/Rules/AnyOf.php @@ -9,7 +9,6 @@ use Hypervel\Validation\Contracts\Rule; use Hypervel\Validation\Contracts\Validator as ValidatorContract; use Hypervel\Validation\Contracts\ValidatorAwareRule; -use InvalidArgumentException; class AnyOf implements Rule, ValidatorAwareRule { @@ -25,15 +24,9 @@ class AnyOf implements Rule, ValidatorAwareRule /** * Sets the validation rules to match against. - * - * @throws InvalidArgumentException */ public function __construct(array $rules) { - if (! is_array($rules)) { - throw new InvalidArgumentException('The provided value must be an array of validation rules.'); - } - $this->rules = $rules; } From 6c100a77a6c62386235efae8f7c556f360474060 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 05:18:59 +0000 Subject: [PATCH 24/28] Increase PHPStan level from 4 to 5 --- phpstan.neon.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 2aec45356..1ca809110 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,7 +3,7 @@ # parameters: - level: 4 + level: 5 parallel: jobSize: 20 maximumNumberOfProcesses: 32 From a18464c47b91f023a3525ca20c14c488a1028818 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 05:33:30 +0000 Subject: [PATCH 25/28] Fix PHPStan level 5 errors (batch 1) - Change HasLaravelStyleCommand trait to use Application instead of ContainerInterface - this correctly reflects that the container in Hypervel is always an Application instance - Update RetryCommand to match the trait's Application type - Fix ScheduleListCommand to iterate over lines instead of passing array - Add ignore for Redis zadd signature mismatch (Hyperf proxy differs) --- src/cache/src/RedisTagSet.php | 2 +- src/console/src/Commands/ScheduleListCommand.php | 6 +++--- src/queue/src/Console/RetryCommand.php | 4 ++-- src/support/src/Traits/HasLaravelStyleCommand.php | 8 +++++--- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/cache/src/RedisTagSet.php b/src/cache/src/RedisTagSet.php index f5466bfa7..9b8ac86a1 100644 --- a/src/cache/src/RedisTagSet.php +++ b/src/cache/src/RedisTagSet.php @@ -25,7 +25,7 @@ public function addEntry(string $key, int $ttl = 0, ?string $updateWhen = null): foreach ($this->tagIds() as $tagKey) { if ($updateWhen) { - $this->store->connection()->zadd($this->store->getPrefix() . $tagKey, $updateWhen, $ttl, $key); + $this->store->connection()->zadd($this->store->getPrefix() . $tagKey, $updateWhen, $ttl, $key); // @phpstan-ignore argument.type } else { $this->store->connection()->zadd($this->store->getPrefix() . $tagKey, $ttl, $key); } diff --git a/src/console/src/Commands/ScheduleListCommand.php b/src/console/src/Commands/ScheduleListCommand.php index 6c13f3a0d..eac35f280 100644 --- a/src/console/src/Commands/ScheduleListCommand.php +++ b/src/console/src/Commands/ScheduleListCommand.php @@ -73,9 +73,9 @@ public function handle() return $this->listEvent($event, $terminalWidth, $expressionSpacing, $repeatExpressionSpacing, $timezone); }); - $this->line( - $events->flatten()->filter()->prepend('')->push('')->toArray() - ); + foreach ($events->flatten()->filter()->prepend('')->push('')->toArray() as $line) { + $this->line($line); + } } /** diff --git a/src/queue/src/Console/RetryCommand.php b/src/queue/src/Console/RetryCommand.php index 6a8ffb051..7905d9793 100644 --- a/src/queue/src/Console/RetryCommand.php +++ b/src/queue/src/Console/RetryCommand.php @@ -10,11 +10,11 @@ use Hyperf\Collection\Collection; use Hyperf\Command\Command; use Hypervel\Encryption\Contracts\Encrypter; +use Hypervel\Foundation\Contracts\Application; use Hypervel\Queue\Contracts\Factory as QueueFactory; use Hypervel\Queue\Events\JobRetryRequested; use Hypervel\Queue\Failed\FailedJobProviderInterface; use Hypervel\Support\Traits\HasLaravelStyleCommand; -use Psr\Container\ContainerInterface; use Psr\EventDispatcher\EventDispatcherInterface; use RuntimeException; use stdClass; @@ -40,7 +40,7 @@ class RetryCommand extends Command * Create a new queue restart command. */ public function __construct( - protected ContainerInterface $app, + protected Application $app, protected FailedJobProviderInterface $failer ) { parent::__construct(); diff --git a/src/support/src/Traits/HasLaravelStyleCommand.php b/src/support/src/Traits/HasLaravelStyleCommand.php index eea46546e..5b4d4639e 100644 --- a/src/support/src/Traits/HasLaravelStyleCommand.php +++ b/src/support/src/Traits/HasLaravelStyleCommand.php @@ -6,17 +6,19 @@ use Hyperf\Context\ApplicationContext; use Hypervel\Foundation\Console\Contracts\Kernel as KernelContract; -use Psr\Container\ContainerInterface; +use Hypervel\Foundation\Contracts\Application; trait HasLaravelStyleCommand { - protected ContainerInterface $app; + protected Application $app; public function __construct(?string $name = null) { parent::__construct($name); - $this->app = ApplicationContext::getContainer(); + /** @var Application $app */ + $app = ApplicationContext::getContainer(); + $this->app = $app; } /** From d1c2d0eb2b59166da33d5e617b3dde5cbca2b1e5 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 06:06:15 +0000 Subject: [PATCH 26/28] Fix PHPStan level 5 errors (batch 2) - Console commands: Cast option values to int for subHours/plural calls - Filesystem: Add ignores for FtpAdapter/SftpAdapter interface quirk - Foundation: Fix str_replace type, array_filter callback, add type assertion - Horizon: Change Exception to Throwable for job failures (matches Laravel) - Horizon: Cast Redis timestamps to string, add higher-order proxy ignore - Prompts: Add ignores for intentional array_values on lists - Validation: Add ignores for defensive array_values/array_filter - Cors: Add ignore for bug (to be fixed in separate PR) --- src/filesystem/src/FilesystemManager.php | 4 ++-- src/foundation/src/Concerns/ResolvesDumpSource.php | 2 +- .../src/Console/Commands/VendorPublishCommand.php | 2 +- src/foundation/src/Console/Kernel.php | 2 +- src/foundation/src/Exceptions/Handler.php | 2 ++ src/foundation/src/Http/Kernel.php | 4 ++-- src/foundation/src/Http/Traits/HasCasts.php | 2 +- src/horizon/src/AutoScaler.php | 6 +++--- src/horizon/src/Contracts/JobRepository.php | 4 ++-- src/horizon/src/Events/JobFailed.php | 6 +++--- src/horizon/src/Jobs/RetryFailedJob.php | 2 +- src/horizon/src/MasterSupervisor.php | 2 +- src/horizon/src/Repositories/RedisJobRepository.php | 4 ++-- .../src/Repositories/RedisMasterSupervisorRepository.php | 2 +- src/horizon/src/Repositories/RedisSupervisorRepository.php | 2 +- src/http/src/Cors.php | 2 +- src/prompts/src/MultiSelectPrompt.php | 2 +- .../src/Themes/Default/MultiSelectPromptRenderer.php | 2 +- src/prompts/src/Themes/Default/SearchPromptRenderer.php | 2 +- src/prompts/src/Themes/Default/SelectPromptRenderer.php | 2 +- src/queue/src/Console/ListenCommand.php | 2 +- src/queue/src/Console/PruneBatchesCommand.php | 6 +++--- src/queue/src/Console/PruneFailedJobsCommand.php | 2 +- src/queue/src/Console/WorkCommand.php | 2 +- src/telescope/src/Console/PruneCommand.php | 2 +- src/validation/src/Concerns/ValidatesAttributes.php | 2 +- src/validation/src/Rules/Email.php | 2 +- 27 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/filesystem/src/FilesystemManager.php b/src/filesystem/src/FilesystemManager.php index f1101e3ed..afbbcedc3 100644 --- a/src/filesystem/src/FilesystemManager.php +++ b/src/filesystem/src/FilesystemManager.php @@ -213,7 +213,7 @@ public function createFtpDriver(array $config): FileSystem /* @phpstan-ignore-next-line */ $adapter = new FtpAdapter(FtpConnectionOptions::fromArray($config)); - return new FilesystemAdapter($this->createFlysystem($adapter, $config), $adapter, $config); + return new FilesystemAdapter($this->createFlysystem($adapter, $config), $adapter, $config); // @phpstan-ignore-line (FtpAdapter/SftpAdapter implement interface) } /** @@ -233,7 +233,7 @@ public function createSftpDriver(array $config): FileSystem /* @phpstan-ignore-next-line */ $adapter = new SftpAdapter($provider, $root, $visibility); - return new FilesystemAdapter($this->createFlysystem($adapter, $config), $adapter, $config); + return new FilesystemAdapter($this->createFlysystem($adapter, $config), $adapter, $config); // @phpstan-ignore-line (FtpAdapter/SftpAdapter implement interface) } /** diff --git a/src/foundation/src/Concerns/ResolvesDumpSource.php b/src/foundation/src/Concerns/ResolvesDumpSource.php index f3129c922..932889f7d 100644 --- a/src/foundation/src/Concerns/ResolvesDumpSource.php +++ b/src/foundation/src/Concerns/ResolvesDumpSource.php @@ -165,7 +165,7 @@ protected function resolveSourceHref(string $file, ?int $line) return str_replace( ['{file}', '{line}'], - [$file, is_null($line) ? 1 : $line], + [$file, (string) ($line ?? 1)], $href, ); } diff --git a/src/foundation/src/Console/Commands/VendorPublishCommand.php b/src/foundation/src/Console/Commands/VendorPublishCommand.php index 1e69c195c..c2c53814c 100644 --- a/src/foundation/src/Console/Commands/VendorPublishCommand.php +++ b/src/foundation/src/Console/Commands/VendorPublishCommand.php @@ -120,7 +120,7 @@ protected function getPackagePublishes(): array ), $extra ); - $packages = array_filter($packages, fn ($provider) => count($provider)); + $packages = array_filter($packages, fn ($provider) => count($provider) > 0); $publishes = []; foreach ($packages as $packageName => $extra) { diff --git a/src/foundation/src/Console/Kernel.php b/src/foundation/src/Console/Kernel.php index cdc367bd4..bc34fc5cb 100644 --- a/src/foundation/src/Console/Kernel.php +++ b/src/foundation/src/Console/Kernel.php @@ -222,7 +222,7 @@ public function registerCommand(string $command): void return; } - $this->getArtisan()->add($command); + $this->getArtisan()->add($command); // @phpstan-ignore argument.type (interface narrower than parent) } /** diff --git a/src/foundation/src/Exceptions/Handler.php b/src/foundation/src/Exceptions/Handler.php index f5993bf5a..2a3e8ef46 100644 --- a/src/foundation/src/Exceptions/Handler.php +++ b/src/foundation/src/Exceptions/Handler.php @@ -847,6 +847,8 @@ public function dontReportDuplicates() /** * Determine if the given exception is an HTTP exception. + * + * @phpstan-assert-if-true HyperfHttpException $e */ protected function isHttpException(Throwable $e): bool { diff --git a/src/foundation/src/Http/Kernel.php b/src/foundation/src/Http/Kernel.php index a64044582..383a2121d 100644 --- a/src/foundation/src/Http/Kernel.php +++ b/src/foundation/src/Http/Kernel.php @@ -71,7 +71,7 @@ public function onRequest($swooleRequest, $swooleResponse): void } $this->dispatchRequestReceivedEvent( - $request = $this->coreMiddleware->dispatch($request), + $request = $this->coreMiddleware->dispatch($request), // @phpstan-ignore argument.type (dispatch returns Request impl) $response ); @@ -110,7 +110,7 @@ public function onRequest($swooleRequest, $swooleResponse): void protected function convertUploadedFiles(array $files): array { return array_map(function ($file) { - if (is_null($file) || (is_array($file) && empty(array_filter($file)))) { + if (is_null($file) || (is_array($file) && empty(array_filter($file)))) { // @phpstan-ignore arrayFilter.same (nested arrays may contain nulls) return $file; } diff --git a/src/foundation/src/Http/Traits/HasCasts.php b/src/foundation/src/Http/Traits/HasCasts.php index 5ef1e884c..bca1ffb37 100644 --- a/src/foundation/src/Http/Traits/HasCasts.php +++ b/src/foundation/src/Http/Traits/HasCasts.php @@ -131,7 +131,7 @@ protected function castInput(string $key, mixed $value, bool $validate = true): case 'double': return $this->fromFloat($value); case 'decimal': - return $this->asDecimal($value, explode(':', $this->getCasts()[$key], 2)[1]); + return $this->asDecimal($value, (int) explode(':', $this->getCasts()[$key], 2)[1]); case 'string': return (string) $value; case 'bool': diff --git a/src/horizon/src/AutoScaler.php b/src/horizon/src/AutoScaler.php index 7f3d58dda..d74af3c72 100644 --- a/src/horizon/src/AutoScaler.php +++ b/src/horizon/src/AutoScaler.php @@ -57,7 +57,7 @@ protected function poolsByQueue(Supervisor $supervisor): Collection protected function timeToClearPerQueue(Supervisor $supervisor, Collection $pools): Collection { return $pools->mapWithKeys(function ($pool, $queue) use ($supervisor) { - $queues = collect(explode(',', $queue))->map(function ($_queue) use ($supervisor) { + $queues = collect(explode(',', $queue))->map(function ($_queue) use ($supervisor) { // @phpstan-ignore argument.unresolvableType // @phpstan-ignore-next-line RedisQueue has readyNow method $size = $this->queue->connection($supervisor->options->connection)->readyNow($_queue); @@ -68,8 +68,8 @@ protected function timeToClearPerQueue(Supervisor $supervisor, Collection $pools }); return [$queue => [ - 'size' => $queues->sum('size'), - 'time' => $queues->sum('time'), + 'size' => $queues->sum('size'), // @phpstan-ignore argument.unresolvableType + 'time' => $queues->sum('time'), // @phpstan-ignore argument.unresolvableType ]]; }); } diff --git a/src/horizon/src/Contracts/JobRepository.php b/src/horizon/src/Contracts/JobRepository.php index 7066a5b96..cf7f63431 100644 --- a/src/horizon/src/Contracts/JobRepository.php +++ b/src/horizon/src/Contracts/JobRepository.php @@ -4,10 +4,10 @@ namespace Hypervel\Horizon\Contracts; -use Exception; use Hypervel\Horizon\JobPayload; use Hypervel\Support\Collection; use stdClass; +use Throwable; interface JobRepository { @@ -144,7 +144,7 @@ public function findFailed(string $id): ?stdClass; /** * Mark the job as failed. */ - public function failed(Exception $exception, string $connection, string $queue, JobPayload $payload): void; + public function failed(Throwable $exception, string $connection, string $queue, JobPayload $payload): void; /** * Store the retry job ID on the original job record. diff --git a/src/horizon/src/Events/JobFailed.php b/src/horizon/src/Events/JobFailed.php index 09cd6a298..dffcf5d34 100644 --- a/src/horizon/src/Events/JobFailed.php +++ b/src/horizon/src/Events/JobFailed.php @@ -4,19 +4,19 @@ namespace Hypervel\Horizon\Events; -use Exception; use Hypervel\Queue\Jobs\Job; +use Throwable; class JobFailed extends RedisEvent { /** * Create a new event instance. * - * @param Exception $exception the exception that caused the failure + * @param Throwable $exception the exception that caused the failure * @param Job $job the queue job instance */ public function __construct( - public Exception $exception, + public Throwable $exception, public Job $job, string $payload ) { diff --git a/src/horizon/src/Jobs/RetryFailedJob.php b/src/horizon/src/Jobs/RetryFailedJob.php index 788552b0d..8eaa2a1ab 100644 --- a/src/horizon/src/Jobs/RetryFailedJob.php +++ b/src/horizon/src/Jobs/RetryFailedJob.php @@ -64,7 +64,7 @@ protected function prepareNewTimeout(array $payload): ?int $pushedAt = $payload['pushedAt'] ?? microtime(true); return $retryUntil - ? CarbonImmutable::now()->addSeconds(ceil($retryUntil - $pushedAt))->getTimestamp() + ? CarbonImmutable::now()->addSeconds((int) ceil($retryUntil - $pushedAt))->getTimestamp() : null; } } diff --git a/src/horizon/src/MasterSupervisor.php b/src/horizon/src/MasterSupervisor.php index 0d8e7a5aa..9d49249b7 100644 --- a/src/horizon/src/MasterSupervisor.php +++ b/src/horizon/src/MasterSupervisor.php @@ -158,7 +158,7 @@ public function terminate(int $status = 0): void // Here we will wait until all of the child supervisors finish terminating and // then exit the process. We will keep track of a timeout value so that the // process does not get stuck in an infinite loop here waiting for these. - while (count($this->supervisors->filter->isRunning())) { + while (count($this->supervisors->filter->isRunning())) { // @phpstan-ignore argument.type (higher-order proxy) if (CarbonImmutable::now()->subSeconds($longest) ->gte($startedTerminating)) { break; diff --git a/src/horizon/src/Repositories/RedisJobRepository.php b/src/horizon/src/Repositories/RedisJobRepository.php index a779b13ef..b9bafb382 100644 --- a/src/horizon/src/Repositories/RedisJobRepository.php +++ b/src/horizon/src/Repositories/RedisJobRepository.php @@ -5,7 +5,6 @@ namespace Hypervel\Horizon\Repositories; use Carbon\CarbonImmutable; -use Exception; use Hyperf\Redis\RedisFactory; use Hyperf\Redis\RedisProxy; use Hypervel\Horizon\Contracts\JobRepository; @@ -14,6 +13,7 @@ use Hypervel\Support\Arr; use Hypervel\Support\Collection; use stdClass; +use Throwable; class RedisJobRepository implements JobRepository { @@ -521,7 +521,7 @@ public function findFailed(string $id): ?stdClass /** * Mark the job as failed. */ - public function failed(Exception $exception, string $connection, string $queue, JobPayload $payload): void + public function failed(Throwable $exception, string $connection, string $queue, JobPayload $payload): void { $this->connection()->pipeline(function ($pipe) use ($exception, $connection, $queue, $payload) { $this->storeJobReference($pipe, 'failed_jobs', $payload); diff --git a/src/horizon/src/Repositories/RedisMasterSupervisorRepository.php b/src/horizon/src/Repositories/RedisMasterSupervisorRepository.php index eae45d92d..dfd7709c4 100644 --- a/src/horizon/src/Repositories/RedisMasterSupervisorRepository.php +++ b/src/horizon/src/Repositories/RedisMasterSupervisorRepository.php @@ -31,7 +31,7 @@ public function names(): array return $this->connection()->zRevRangeByScore( 'masters', '+inf', - CarbonImmutable::now()->subSeconds(14)->getTimestamp() + (string) CarbonImmutable::now()->subSeconds(14)->getTimestamp() ); } diff --git a/src/horizon/src/Repositories/RedisSupervisorRepository.php b/src/horizon/src/Repositories/RedisSupervisorRepository.php index 124a53242..ed1f0b004 100644 --- a/src/horizon/src/Repositories/RedisSupervisorRepository.php +++ b/src/horizon/src/Repositories/RedisSupervisorRepository.php @@ -30,7 +30,7 @@ public function names(): array return $this->connection()->zRevRangeByScore( 'supervisors', '+inf', - CarbonImmutable::now()->subSeconds(29)->getTimestamp() + (string) CarbonImmutable::now()->subSeconds(29)->getTimestamp() ); } diff --git a/src/http/src/Cors.php b/src/http/src/Cors.php index 7b7c14d7d..01ba3871e 100644 --- a/src/http/src/Cors.php +++ b/src/http/src/Cors.php @@ -293,7 +293,7 @@ public function varyHeader(ResponseInterface $response, string $header): Respons if (count($varyHeaders) === 1) { $response = $response->withHeader('Vary', ((string) $varyHeaders[0]) . ', ' . $header); } else { - $response->withHeader($header, false); + $response->withHeader($header, false); // @phpstan-ignore argument.type (bug: wrong args, result unused - fix in separate PR) } } } diff --git a/src/prompts/src/MultiSelectPrompt.php b/src/prompts/src/MultiSelectPrompt.php index 2ceef9d6d..5dd5702ad 100644 --- a/src/prompts/src/MultiSelectPrompt.php +++ b/src/prompts/src/MultiSelectPrompt.php @@ -129,7 +129,7 @@ protected function toggleAll(): void $this->values = []; } else { $this->values = array_is_list($this->options) - ? array_values($this->options) + ? array_values($this->options) // @phpstan-ignore arrayValues.list (intentional copy) : array_keys($this->options); } } diff --git a/src/prompts/src/Themes/Default/MultiSelectPromptRenderer.php b/src/prompts/src/Themes/Default/MultiSelectPromptRenderer.php index 990b08a31..075501729 100644 --- a/src/prompts/src/Themes/Default/MultiSelectPromptRenderer.php +++ b/src/prompts/src/Themes/Default/MultiSelectPromptRenderer.php @@ -61,7 +61,7 @@ public function __invoke(MultiSelectPrompt $prompt): string protected function renderOptions(MultiSelectPrompt $prompt): string { return implode(PHP_EOL, $this->scrollbar( - array_values(array_map(function ($label, $key) use ($prompt) { + array_values(array_map(function ($label, $key) use ($prompt) { // @phpstan-ignore arrayValues.list $label = $this->truncate($label, $prompt->terminal()->cols() - 12); $index = array_search($key, array_keys($prompt->options)); diff --git a/src/prompts/src/Themes/Default/SearchPromptRenderer.php b/src/prompts/src/Themes/Default/SearchPromptRenderer.php index 072c2a4d6..f7078dcca 100644 --- a/src/prompts/src/Themes/Default/SearchPromptRenderer.php +++ b/src/prompts/src/Themes/Default/SearchPromptRenderer.php @@ -109,7 +109,7 @@ protected function renderOptions(SearchPrompt $prompt): string } return implode(PHP_EOL, $this->scrollbar( - array_values(array_map(function ($label, $key) use ($prompt) { + array_values(array_map(function ($label, $key) use ($prompt) { // @phpstan-ignore arrayValues.list $label = $this->truncate($label, $prompt->terminal()->cols() - 10); $index = array_search($key, array_keys($prompt->matches())); diff --git a/src/prompts/src/Themes/Default/SelectPromptRenderer.php b/src/prompts/src/Themes/Default/SelectPromptRenderer.php index a1b74671e..d0b5aac54 100644 --- a/src/prompts/src/Themes/Default/SelectPromptRenderer.php +++ b/src/prompts/src/Themes/Default/SelectPromptRenderer.php @@ -61,7 +61,7 @@ public function __invoke(SelectPrompt $prompt): string protected function renderOptions(SelectPrompt $prompt): string { return implode(PHP_EOL, $this->scrollbar( - array_values(array_map(function ($label, $key) use ($prompt) { + array_values(array_map(function ($label, $key) use ($prompt) { // @phpstan-ignore arrayValues.list $label = $this->truncate($label, $prompt->terminal()->cols() - 12); $index = array_search($key, array_keys($prompt->options)); diff --git a/src/queue/src/Console/ListenCommand.php b/src/queue/src/Console/ListenCommand.php index 95b788471..2ab6343da 100644 --- a/src/queue/src/Console/ListenCommand.php +++ b/src/queue/src/Console/ListenCommand.php @@ -60,7 +60,7 @@ public function handle() $connection = $this->input->getArgument('connection') ); - $this->info(sprintf('Processing jobs from the [%s] %s.', $queue, Str::of('queue')->plural(explode(',', $queue)))); + $this->info(sprintf('Processing jobs from the [%s] %s.', $queue, Str::of('queue')->plural(count(explode(',', $queue))))); $this->listener->listen( $connection, diff --git a/src/queue/src/Console/PruneBatchesCommand.php b/src/queue/src/Console/PruneBatchesCommand.php index ff1beb6bb..d2bba5731 100644 --- a/src/queue/src/Console/PruneBatchesCommand.php +++ b/src/queue/src/Console/PruneBatchesCommand.php @@ -38,7 +38,7 @@ public function handle() $count = 0; if ($repository instanceof PrunableBatchRepository) { - $count = $repository->prune(Carbon::now()->subHours($this->option('hours'))); + $count = $repository->prune(Carbon::now()->subHours((int) $this->option('hours'))); } $this->info("{$count} entries deleted."); @@ -47,7 +47,7 @@ public function handle() $count = 0; if ($repository instanceof DatabaseBatchRepository) { - $count = $repository->pruneUnfinished(Carbon::now()->subHours($this->option('unfinished'))); + $count = $repository->pruneUnfinished(Carbon::now()->subHours((int) $this->option('unfinished'))); } $this->info("{$count} unfinished entries deleted."); @@ -57,7 +57,7 @@ public function handle() $count = 0; if ($repository instanceof DatabaseBatchRepository) { - $count = $repository->pruneCancelled(Carbon::now()->subHours($this->option('cancelled'))); + $count = $repository->pruneCancelled(Carbon::now()->subHours((int) $this->option('cancelled'))); } $this->info("{$count} cancelled entries deleted."); diff --git a/src/queue/src/Console/PruneFailedJobsCommand.php b/src/queue/src/Console/PruneFailedJobsCommand.php index dfc412ae4..c1f4d916a 100644 --- a/src/queue/src/Console/PruneFailedJobsCommand.php +++ b/src/queue/src/Console/PruneFailedJobsCommand.php @@ -33,7 +33,7 @@ public function handle(): ?int $failer = $this->app->get(FailedJobProviderInterface::class); if ($failer instanceof PrunableFailedJobProvider) { - $count = $failer->prune(Carbon::now()->subHours($this->option('hours'))); + $count = $failer->prune(Carbon::now()->subHours((int) $this->option('hours'))); } else { $this->error('The [' . class_basename($failer) . '] failed job storage driver does not support pruning.'); diff --git a/src/queue/src/Console/WorkCommand.php b/src/queue/src/Console/WorkCommand.php index 04f09e5b9..fef95365a 100644 --- a/src/queue/src/Console/WorkCommand.php +++ b/src/queue/src/Console/WorkCommand.php @@ -102,7 +102,7 @@ public function handle(): ?int if (! $this->outputUsingJson() && Terminal::hasSttyAvailable()) { $this->info( - sprintf('Processing jobs from the [%s] %s.', $queue, Str::of('queue')->plural(explode(',', $queue))) + sprintf('Processing jobs from the [%s] %s.', $queue, Str::of('queue')->plural(count(explode(',', $queue)))) ); } diff --git a/src/telescope/src/Console/PruneCommand.php b/src/telescope/src/Console/PruneCommand.php index 8f6d9049f..e517878e0 100644 --- a/src/telescope/src/Console/PruneCommand.php +++ b/src/telescope/src/Console/PruneCommand.php @@ -25,6 +25,6 @@ class PruneCommand extends Command */ public function handle(PrunableRepository $repository) { - $this->info($repository->prune(Carbon::now()->subHours($this->option('hours')), $this->option('keep-exceptions')) . ' entries pruned.'); + $this->info($repository->prune(Carbon::now()->subHours((int) $this->option('hours')), $this->option('keep-exceptions')) . ' entries pruned.'); } } diff --git a/src/validation/src/Concerns/ValidatesAttributes.php b/src/validation/src/Concerns/ValidatesAttributes.php index 6d7620ff7..10ba647d3 100644 --- a/src/validation/src/Concerns/ValidatesAttributes.php +++ b/src/validation/src/Concerns/ValidatesAttributes.php @@ -822,7 +822,7 @@ protected function getExistCount(mixed $connection, string $table, string $colum $verifier = $this->getPresenceVerifier($connection); $extra = $this->getExtraConditions( - array_values(array_slice($parameters, 2)) + array_values(array_slice($parameters, 2)) // @phpstan-ignore arrayValues.list ); if ($this->currentRule instanceof Exists) { diff --git a/src/validation/src/Rules/Email.php b/src/validation/src/Rules/Email.php index fbf0f4216..7f89ff2a5 100644 --- a/src/validation/src/Rules/Email.php +++ b/src/validation/src/Rules/Email.php @@ -224,7 +224,7 @@ protected function buildValidationRules(): array $rules = ['email']; } - return array_merge(array_filter($rules), $this->customRules); + return array_merge(array_filter($rules), $this->customRules); // @phpstan-ignore arrayFilter.same (defensive) } /** From 0d66b0a4c5632c18a6d88a9c5d31cb3d2515adf6 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 06:18:57 +0000 Subject: [PATCH 27/28] Fix remaining PHPStan level 5 errors - HeaderUtils: Add ignore for implode type (conditional type narrowing) - ParsesLogConfiguration: Add @return PHPDoc for Monolog level constants - MailManager: Add @var for EsmtpTransport factory return type - BaseRelation: Add @var for nested-set QueryBuilder type - HasPermission: Add ignore for Permission contract in Collection::map --- src/http/src/HeaderUtils.php | 2 +- src/log/src/ParsesLogConfiguration.php | 4 ++++ src/mail/src/MailManager.php | 1 + src/nested-set/src/Eloquent/BaseRelation.php | 1 + src/permission/src/Traits/HasPermission.php | 2 +- 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/http/src/HeaderUtils.php b/src/http/src/HeaderUtils.php index 7a0298015..4a23f4616 100644 --- a/src/http/src/HeaderUtils.php +++ b/src/http/src/HeaderUtils.php @@ -258,7 +258,7 @@ public static function parseQuery(string $query, bool $ignoreBrackets = false, s return $q; } - parse_str(implode('&', $q), $q); + parse_str(implode('&', $q), $q); // @phpstan-ignore argument.type ($q is list here, early return handles other case) $query = []; diff --git a/src/log/src/ParsesLogConfiguration.php b/src/log/src/ParsesLogConfiguration.php index 9564de2e2..24db6977d 100644 --- a/src/log/src/ParsesLogConfiguration.php +++ b/src/log/src/ParsesLogConfiguration.php @@ -31,6 +31,8 @@ abstract protected function getFallbackChannelName(): string; /** * Parse the string level into a Monolog constant. * + * @return 100|200|250|300|400|500|550|600 + * * @throws InvalidArgumentException */ protected function level(array $config): int @@ -46,6 +48,8 @@ protected function level(array $config): int /** * Parse the action level from the given configuration. + * + * @return 100|200|250|300|400|500|550|600 */ protected function actionLevel(array $config): int { diff --git a/src/mail/src/MailManager.php b/src/mail/src/MailManager.php index 460459db1..564969b17 100644 --- a/src/mail/src/MailManager.php +++ b/src/mail/src/MailManager.php @@ -202,6 +202,7 @@ protected function createSmtpTransport(array $config): EsmtpTransport : ''; } + /** @var EsmtpTransport $transport */ $transport = $factory->create(new Dsn( $scheme, $config['host'], diff --git a/src/nested-set/src/Eloquent/BaseRelation.php b/src/nested-set/src/Eloquent/BaseRelation.php index ac69a4328..e285a1f64 100644 --- a/src/nested-set/src/Eloquent/BaseRelation.php +++ b/src/nested-set/src/Eloquent/BaseRelation.php @@ -97,6 +97,7 @@ public function addEagerConstraints(array $models): void $this->query->whereNested(function (Builder $inner) use ($models) { // We will use this query in order to apply constraints to the // base query builder + /** @var QueryBuilder $outer */ $outer = $this->parent->newQuery()->setQuery($inner); foreach ($models as $model) { diff --git a/src/permission/src/Traits/HasPermission.php b/src/permission/src/Traits/HasPermission.php index 2d508f7d5..b7378f150 100644 --- a/src/permission/src/Traits/HasPermission.php +++ b/src/permission/src/Traits/HasPermission.php @@ -435,7 +435,7 @@ private function attachPermission(array $permissions, bool $isForbidden = false) // Get existing permissions with the same is_forbidden value $currentPermissions = $this->permissions ->where('pivot.is_forbidden', $isForbidden) - ->map(fn (Permission $permission) => $permission->getKey()) + ->map(fn (Permission $permission) => $permission->getKey()) // @phpstan-ignore argument.type (Permission contract, not Model) ->toArray(); // Only attach permissions that don't already exist with the same is_forbidden value From 594192f7be83843dbd5312852357dccff8476643 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Fri, 26 Dec 2025 06:26:00 +0000 Subject: [PATCH 28/28] Fix Collection generic type inference errors from CI --- src/horizon/src/Console/ForgetFailedCommand.php | 2 +- src/horizon/src/WaitTimeCalculator.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/horizon/src/Console/ForgetFailedCommand.php b/src/horizon/src/Console/ForgetFailedCommand.php index 760d6a646..15e3e822a 100644 --- a/src/horizon/src/Console/ForgetFailedCommand.php +++ b/src/horizon/src/Console/ForgetFailedCommand.php @@ -31,7 +31,7 @@ public function handle(JobRepository $repository): ?int do { $failedJobs = collect($repository->getFailed()); - $failedJobs->pluck('id')->each(function (string $failedId) use ($repository): void { + $failedJobs->pluck('id')->each(function (string $failedId) use ($repository): void { // @phpstan-ignore argument.type $repository->deleteFailed($failedId); if ($this->app->get(FailedJobProviderInterface::class)->forget($failedId)) { diff --git a/src/horizon/src/WaitTimeCalculator.php b/src/horizon/src/WaitTimeCalculator.php index ea083ef2e..84b8d4a6e 100644 --- a/src/horizon/src/WaitTimeCalculator.php +++ b/src/horizon/src/WaitTimeCalculator.php @@ -60,7 +60,7 @@ protected function queueNames(Collection $supervisors, ?string $queue = null): C return array_keys($supervisor->processes); })->collapse()->unique()->values(); - return $queue ? $queues->intersect([$queue]) : $queues; + return $queue ? $queues->intersect([$queue]) : $queues; // @phpstan-ignore argument.type } /**