Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c9dc451
Increase PHPStan level from 3 to 4 (partial)
binaryfire Dec 26, 2025
d61e65f
Add PHPDoc type hints to narrow match expression in Mailable
binaryfire Dec 26, 2025
8de7fab
Add PHPStan ignores for false positives in static analysis
binaryfire Dec 26, 2025
de100eb
Fix PHPStan type errors and remove dead code
binaryfire Dec 26, 2025
4c724ee
Fix unreachable code and nullable callable PHPDoc annotations
binaryfire Dec 26, 2025
fd11df9
Fix PHPStan identical comparison errors
binaryfire Dec 26, 2025
2ca65fd
Fix PHPStan isset.offset errors in ReflectsClosures
binaryfire Dec 26, 2025
c054494
Fix PHPStan nullCoalesce.offset errors
binaryfire Dec 26, 2025
78b1396
Fix PHPStan booleanAnd.alwaysFalse and related errors
binaryfire Dec 26, 2025
17e779a
Remove dead code and modernize validation rules
binaryfire Dec 26, 2025
e7b3d3e
Fix PHPStan instanceof.alwaysTrue errors
binaryfire Dec 26, 2025
7619b27
Fix PHPStan if.alwaysTrue errors
binaryfire Dec 26, 2025
a1fbe10
Fix PHPStan ternary.alwaysFalse errors in BoundMethod
binaryfire Dec 26, 2025
b927c71
Fix PHPStan ternary/boolean errors
binaryfire Dec 26, 2025
e5d53f5
Fix PHPStan booleanAnd.leftAlwaysTrue errors
binaryfire Dec 26, 2025
713d354
Fix PHPStan nullCoalesce.property and staticMethod errors
binaryfire Dec 26, 2025
368914a
Fix PHPStan nullCoalesce.expr errors
binaryfire Dec 26, 2025
36d043e
Fix PHPStan miscellaneous type errors
binaryfire Dec 26, 2025
9ad0acb
Fix PHPStan function.impossibleType errors (batch 1)
binaryfire Dec 26, 2025
d3be8ae
Fix PHPStan function.impossibleType errors (batch 2)
binaryfire Dec 26, 2025
61a852c
Fix PHPStan function.impossibleType errors (batch 3)
binaryfire Dec 26, 2025
1b85eac
Fix PHPStan function.alreadyNarrowedType errors (batch 1)
binaryfire Dec 26, 2025
02d116c
Fix PHPStan function.alreadyNarrowedType errors (batch 2)
binaryfire Dec 26, 2025
6c100a7
Increase PHPStan level from 4 to 5
binaryfire Dec 26, 2025
a18464c
Fix PHPStan level 5 errors (batch 1)
binaryfire Dec 26, 2025
d1c2d0e
Fix PHPStan level 5 errors (batch 2)
binaryfire Dec 26, 2025
0d66b0a
Fix remaining PHPStan level 5 errors
binaryfire Dec 26, 2025
594192f
Fix Collection generic type inference errors from CI
binaryfire Dec 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#

parameters:
level: 3
level: 5
parallel:
jobSize: 20
maximumNumberOfProcesses: 32
Expand All @@ -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.#'
Expand Down
2 changes: 1 addition & 1 deletion src/api-client/src/PendingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
Expand Down
12 changes: 2 additions & 10 deletions src/auth/src/Access/Gate.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
}

/**
Expand Down Expand Up @@ -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]);
}
Expand Down
2 changes: 1 addition & 1 deletion src/auth/src/AuthenticationException.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
6 changes: 1 addition & 5 deletions src/auth/src/Middleware/Authorize.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
8 changes: 2 additions & 6 deletions src/broadcasting/src/BroadcastManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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}");
}

Expand Down
12 changes: 6 additions & 6 deletions src/broadcasting/src/Broadcasters/Broadcaster.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/broadcasting/src/Broadcasters/PusherBroadcaster.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 1 addition & 3 deletions src/bus/src/PendingBatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/cache/src/CacheManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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}");
}

Expand Down
8 changes: 3 additions & 5 deletions src/cache/src/Console/ClearCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
8 changes: 2 additions & 6 deletions src/cache/src/DatabaseStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
2 changes: 1 addition & 1 deletion src/cache/src/FileStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/cache/src/Functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.'
);
Expand Down
4 changes: 2 additions & 2 deletions src/cache/src/RedisStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/cache/src/RedisTagSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/cache/src/SwooleTableManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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}");
}

Expand Down
6 changes: 3 additions & 3 deletions src/config/src/ProviderConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
);
Expand Down
6 changes: 3 additions & 3 deletions src/console/src/Commands/ScheduleListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/console/src/Scheduling/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 ?? ''));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/console/src/Scheduling/Schedule.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/container/src/BoundMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 4 additions & 5 deletions src/container/src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,7 @@ protected function fireAfterResolvingCallbacks($abstract, $object): void
*
* @param string $abstract
* @param object $object
* @param array<class-string, array> $callbacksPerType
*/
protected function getCallbacksForType($abstract, $object, array $callbacksPerType): array
{
Expand Down Expand Up @@ -681,23 +682,21 @@ 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)
}

/**
* Set the shared instance of the container.
*/
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
Expand Down
4 changes: 2 additions & 2 deletions src/core/src/Database/Eloquent/Factories/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,14 @@ abstract class Factory
/**
* The default model name resolver.
*
* @var callable(self): class-string<TModel>
* @var null|(callable(self): class-string<TModel>)
*/
protected static $modelNameResolver;

/**
* The factory name resolver.
*
* @var callable(class-string<Model>): class-string<Factory>
* @var null|(callable(class-string<Model>): class-string<Factory>)
*/
protected static $factoryNameResolver;

Expand Down
2 changes: 1 addition & 1 deletion src/core/src/Database/Eloquent/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/devtool/src/Generator/ObserverCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion src/devtool/src/Generator/PolicyCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
4 changes: 0 additions & 4 deletions src/encryption/src/Encrypter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/event/illuminate/CallQueuedListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
Loading