diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4dde79c61..1ca809110 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,7 +3,7 @@ # parameters: - level: 3 + level: 5 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/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 540b00122..57ffc3f86 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]); } /** @@ -496,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/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/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..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); } /** @@ -387,7 +383,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/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/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/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/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/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/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/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/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/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/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}"); } 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/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/console/src/Scheduling/Event.php b/src/console/src/Scheduling/Event.php index 7cec1b41d..e758757b0 100644 --- a/src/console/src/Scheduling/Event.php +++ b/src/console/src/Scheduling/Event.php @@ -706,12 +706,12 @@ public function mutexName(): string { $mutexNameResolver = $this->mutexNameResolver; - if (! is_null($mutexNameResolver) && is_callable($mutexNameResolver)) { + if (! is_null($mutexNameResolver)) { return $mutexNameResolver($this); } return 'framework' . DIRECTORY_SEPARATOR . 'schedule-' - . sha1($this->expression . $this->command ?? ''); + . sha1($this->expression . ($this->command ?? '')); } /** 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/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 diff --git a/src/container/src/Container.php b/src/container/src/Container.php index ac027e1d9..2c85a3b19 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 { @@ -681,14 +682,13 @@ 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(); + 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/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/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/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/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/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/filesystem/src/FilesystemManager.php b/src/filesystem/src/FilesystemManager.php index a03b6b3e6..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) } /** @@ -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/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/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 2416114ac..c2c53814c 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; } @@ -115,12 +115,12 @@ 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 ); - $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 a8788c999..2a3e8ef46 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 ), @@ -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 4bb0529bc..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 ); @@ -104,13 +104,13 @@ 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 { 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/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/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/foundation/src/Testing/TestResponseAssert.php b/src/foundation/src/Testing/TestResponseAssert.php index 71a90780f..69c942e92 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); } } @@ -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'])) { @@ -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/hashing/src/ArgonHasher.php b/src/hashing/src/ArgonHasher.php index 5086689f1..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.'); } @@ -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, booleanAnd.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/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/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/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/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/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/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/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/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/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/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/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/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/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 } /** 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/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/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/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/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/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/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 d40ce4b6b..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'], @@ -455,7 +456,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/mail/src/Mailable.php b/src/mail/src/Mailable.php index b44358322..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; @@ -665,6 +665,8 @@ protected function normalizeRecipient(mixed $recipient): object /** * Determine if the given recipient is set on the mailable. + * + * @param 'bcc'|'cc'|'from'|'replyTo'|'to' $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 'bcc'|'cc'|'from'|'replyTo'|'to' $property */ private function hasEnvelopeRecipient(string $address, ?string $name, string $property): bool { diff --git a/src/mail/src/Mailer.php b/src/mail/src/Mailer.php index ffcb7945d..663c072be 100644 --- a/src/mail/src/Mailer.php +++ b/src/mail/src/Mailer.php @@ -297,22 +297,18 @@ 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]; } // 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/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 ); } 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/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/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/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/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 cca1d8c32..b7378f150 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 { @@ -393,7 +395,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') }; } @@ -433,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 @@ -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/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/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/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/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/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/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/RetryCommand.php b/src/queue/src/Console/RetryCommand.php index b3a1e952d..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(); @@ -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/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/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/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/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; 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); } /** 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/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/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( 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; diff --git a/src/sanctum/src/SanctumGuard.php b/src/sanctum/src/SanctumGuard.php index 49a5d3bf5..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); @@ -177,7 +178,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)) { @@ -192,10 +193,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/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/session/src/Middleware/StartSession.php b/src/session/src/Middleware/StartSession.php index 7a67a5674..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' // is not ajax + && ! $this->request->header('X-Requested-With') === 'XMLHttpRequest' // @phpstan-ignore identical.alwaysFalse && ! $this->isPrefetch() ) { $session->setPreviousUrl($this->request->fullUrl()); 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/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/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/Reflector.php b/src/support/src/Reflector.php index ef9667754..66310db93 100644 --- a/src/support/src/Reflector.php +++ b/src/support/src/Reflector.php @@ -22,13 +22,12 @@ 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; } 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/ServiceProvider.php b/src/support/src/ServiceProvider.php index 92a97a236..663a0e063 100644 --- a/src/support/src/ServiceProvider.php +++ b/src/support/src/ServiceProvider.php @@ -244,10 +244,11 @@ 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; } + // @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/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/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/EventFake.php b/src/support/src/Testing/Fakes/EventFake.php index 6d2cd1da6..0f222134e 100644 --- a/src/support/src/Testing/Fakes/EventFake.php +++ b/src/support/src/Testing/Fakes/EventFake.php @@ -78,13 +78,13 @@ 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; } } - 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/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) ); } 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; } /** 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]; } 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/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/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/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 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) { 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/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/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 ec408fa31..10ba647d3 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) { } @@ -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 { @@ -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); } @@ -821,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) { @@ -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'; } @@ -2166,7 +2167,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') { @@ -2198,7 +2199,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); } /** 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) } /** 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; } diff --git a/src/validation/src/Rules/Email.php b/src/validation/src/Rules/Email.php index 6f9adc496..7f89ff2a5 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); } @@ -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) } /** 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/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); } 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 } } }