From 39cf0533d98875cfa9c33fcf4bb75c07e21829d4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 7 Sep 2025 10:59:07 +0200 Subject: [PATCH] Fix various phpstan issues. --- .ci/phpstan.neon | 12 +++--- app/Api/V1/Controllers/Controller.php | 1 - .../Upgrade/RemovesDatabaseDecryption.php | 2 +- app/Exceptions/GracefulNotFoundHandler.php | 2 +- app/Handlers/Events/UserEventHandler.php | 2 +- app/Http/Controllers/ProfileController.php | 2 +- .../Transaction/BulkController.php | 2 +- app/Http/Requests/RecurrenceFormRequest.php | 2 +- .../UserGroup/UserGroupRepository.php | 40 +++++++++---------- app/Support/Form/FormSupport.php | 24 ++++------- app/Support/Http/Api/CleansChartData.php | 2 +- app/Support/Request/ConvertsDataTypes.php | 3 ++ .../Engine/SearchRuleEngine.php | 4 +- database/seeders/TransactionTypeSeeder.php | 2 +- database/seeders/UserRoleSeeder.php | 2 +- 15 files changed, 44 insertions(+), 58 deletions(-) diff --git a/.ci/phpstan.neon b/.ci/phpstan.neon index 7755c93511..c85e03774f 100644 --- a/.ci/phpstan.neon +++ b/.ci/phpstan.neon @@ -11,8 +11,6 @@ parameters: reportUnmatchedIgnoredErrors: true ignoreErrors: # TODO: slowly remove these exceptions and fix the issues found. - - '#Dynamic call to static method#' # all the Laravel ORM things depend on this. - - identifier: varTag.nativeType - identifier: varTag.type - identifier: larastan.noEnvCallsOutsideOfConfig @@ -24,11 +22,11 @@ parameters: - '#expects view-string\|null, string given#' # phpstan can't handle this so we ignore them. - - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::before#' - - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::after#' - - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::withTrashed#' - - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::accountTypeIn#' - - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\BelongsTo::withTrashed#' + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::before#' # is custom scope + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::after#' # is custom scope + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::withTrashed#' # is to allow soft delete + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::accountTypeIn#' # is a custom scope + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\BelongsTo::withTrashed#' # is to allow soft delete # The level 8 is the highest level. original was 5 # 7 is more than enough, higher just leaves NULL things. diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index f6629f8be0..09deef3055 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -157,7 +157,6 @@ abstract class Controller extends BaseController /** @var User $user */ $user = auth()->user(); - /** @var Preference $pageSize */ $pageSize = (int)app('preferences')->getForUser($user, 'listPageSize', 50)->data; $bag->set($integer, $pageSize); } diff --git a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php index 87e99cd706..b1e3d198db 100644 --- a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php +++ b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php @@ -66,7 +66,7 @@ class RemovesDatabaseDecryption extends Command * @var string $table * @var array $fields */ - foreach ($tables as $table => $fields) { + foreach ($tables as $table => $fields) { // @phpstan-ignore-line $this->decryptTable($table, $fields); } diff --git a/app/Exceptions/GracefulNotFoundHandler.php b/app/Exceptions/GracefulNotFoundHandler.php index 7866d20047..4ec31dadad 100644 --- a/app/Exceptions/GracefulNotFoundHandler.php +++ b/app/Exceptions/GracefulNotFoundHandler.php @@ -170,7 +170,7 @@ class GracefulNotFoundHandler extends ExceptionHandler } /** @var null|Account $account */ - $account = $user->accounts()->with(['accountType'])->withTrashed()->find($accountId); + $account = $user->accounts()->withTrashed()->with(['accountType'])->find($accountId); if (null === $account) { app('log')->error(sprintf('Could not find account %d, so give big fat error.', $accountId)); diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index f7a0404672..d2358bd8fc 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -129,7 +129,7 @@ class UserEventHandler $groupTitle = $user->email; $index = 1; - /** @var UserGroup $group */ + /** @var UserGroup|null $group */ $group = null; // create a new group. diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 13052d733a..2575cf0084 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -412,7 +412,7 @@ class ProfileController extends Controller // found user.which email address to return to? $set = app('preferences')->beginsWith($user, 'previous_email_'); - /** @var string $match */ + /** @var string|null $match */ $match = null; foreach ($set as $entry) { $hashed = hash('sha256', sprintf('%s%s', (string) config('app.key'), $entry->data)); diff --git a/app/Http/Controllers/Transaction/BulkController.php b/app/Http/Controllers/Transaction/BulkController.php index bca008b147..ab5365b1f4 100644 --- a/app/Http/Controllers/Transaction/BulkController.php +++ b/app/Http/Controllers/Transaction/BulkController.php @@ -117,7 +117,7 @@ class BulkController extends Controller // run rules on changed journals: /** @var TransactionJournal $journal */ - foreach ($collection as $journal) { + foreach ($collection as $journal) { // @phpstan-ignore-line event(new UpdatedTransactionGroup($journal->transactionGroup, true, true, false)); } diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index c26f8cc0b8..a4f9014078 100644 --- a/app/Http/Requests/RecurrenceFormRequest.php +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -138,7 +138,7 @@ class RecurrenceFormRequest extends FormRequest * @var int $index * @var array $transaction */ - foreach ($return['transactions'] as $index => $transaction) { + foreach ($return['transactions'] as $index => $transaction) { // @phpstan-ignore-line $categoryName = $transaction['category_name'] ?? null; if (null !== $categoryName) { $category = $factory->findOrCreate(null, $categoryName); diff --git a/app/Repositories/UserGroup/UserGroupRepository.php b/app/Repositories/UserGroup/UserGroupRepository.php index f8c9e3a488..0ed1921890 100644 --- a/app/Repositories/UserGroup/UserGroupRepository.php +++ b/app/Repositories/UserGroup/UserGroupRepository.php @@ -53,7 +53,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte /** @var GroupMembership $membership */ foreach ($memberships as $membership) { /** @var null|User $user */ - $user = $membership->user()->first(); + $user = $membership->user()->first(); if (null === $user) { continue; } @@ -82,8 +82,8 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte // all users are now moved away from user group. // time to DESTROY all objects. // we have to do this one by one to trigger the necessary observers :( - $objects = ['availableBudgets', 'bills', 'budgets', 'categories', 'currencyExchangeRates', 'objectGroups', - 'recurrences', 'rules', 'ruleGroups', 'tags', 'transactionGroups', 'transactionJournals', 'piggyBanks', 'accounts', 'webhooks', + $objects = ['availableBudgets', 'bills', 'budgets', 'categories', 'currencyExchangeRates', 'objectGroups', + 'recurrences', 'rules', 'ruleGroups', 'tags', 'transactionGroups', 'transactionJournals', 'piggyBanks', 'accounts', 'webhooks', ]; foreach ($objects as $object) { foreach ($userGroup->{$object}()->get() as $item) { // @phpstan-ignore-line @@ -110,7 +110,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte /** @var null|UserGroup $group */ $group = $membership->userGroup()->first(); if (null !== $group) { - $groupId = $group->id; + $groupId = $group->id; if (in_array($groupId, array_keys($set), true)) { continue; } @@ -135,15 +135,12 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte while ($exists && $loop < 10) { $existingGroup = $this->findByName($groupName); if (!$existingGroup instanceof UserGroup) { - $exists = false; + $exists = false; - /** @var null|UserGroup $existingGroup */ + /** @var UserGroup $existingGroup */ $existingGroup = $this->store(['user' => $user, 'title' => $groupName]); } - if (null !== $existingGroup) { - // group already exists - $groupName = sprintf('%s-%s', $user->email, substr(sha1(random_int(1000, 9999).microtime()), 0, 4)); - } + $groupName = sprintf('%s-%s', $user->email, substr(sha1(random_int(1000, 9999) . microtime()), 0, 4)); ++$loop; } @@ -163,7 +160,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte $data['user'] = $this->user; /** @var UserGroupFactory $factory */ - $factory = app(UserGroupFactory::class); + $factory = app(UserGroupFactory::class); return $factory->create($data); } @@ -194,10 +191,10 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte { $userGroup->title = $data['title']; $userGroup->save(); - $currency = null; + $currency = null; /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class); + $repository = app(CurrencyRepositoryInterface::class); if (array_key_exists('primary_currency_code', $data)) { $repository->setUser($this->user); @@ -206,7 +203,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte if (array_key_exists('primary_currency_id', $data) && null === $currency) { $repository->setUser($this->user); - $currency = $repository->find((int) $data['primary_currency_id']); + $currency = $repository->find((int)$data['primary_currency_id']); } if (null !== $currency) { $repository->makePrimary($currency); @@ -223,17 +220,17 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte */ public function updateMembership(UserGroup $userGroup, array $data): UserGroup { - $owner = UserRole::whereTitle(UserRoleEnum::OWNER)->first(); + $owner = UserRole::whereTitle(UserRoleEnum::OWNER)->first(); app('log')->debug('in update membership'); /** @var null|User $user */ - $user = null; + $user = null; if (array_key_exists('id', $data)) { /** @var null|User $user */ $user = User::find($data['id']); app('log')->debug('Found user by ID'); } - if (array_key_exists('email', $data) && '' !== (string) $data['email']) { + if (array_key_exists('email', $data) && '' !== (string)$data['email']) { /** @var null|User $user */ $user = User::whereEmail($data['email'])->first(); app('log')->debug('Found user by email'); @@ -251,13 +248,13 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte if (1 === $membershipCount) { $lastUserId = $userGroup->groupMemberships()->distinct()->first(['group_memberships.user_id'])->user_id; // if this is also the user we're editing right now, and we remove all of their roles: - if ($lastUserId === (int) $user->id && 0 === count($data['roles'])) { + if ($lastUserId === (int)$user->id && 0 === count($data['roles'])) { app('log')->debug('User is last in this group, refuse to act'); throw new FireflyException('You cannot remove the last member from this user group. Delete the user group instead.'); } // if this is also the user we're editing right now, and do not grant them the owner role: - if ($lastUserId === (int) $user->id && count($data['roles']) > 0 && !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)) { + if ($lastUserId === (int)$user->id && count($data['roles']) > 0 && !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)) { app('log')->debug('User needs to have owner role in this group, refuse to act'); throw new FireflyException('The last member in this user group must get or keep the "owner" role.'); @@ -266,9 +263,8 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte if ($membershipCount > 1) { // group has multiple members. How many are owner, except the user we're editing now? $ownerCount = $userGroup->groupMemberships() - ->where('user_role_id', $owner->id) - ->where('user_id', '!=', $user->id)->count() - ; + ->where('user_role_id', $owner->id) + ->where('user_id', '!=', $user->id)->count(); // if there are no other owners and the current users does not get or keep the owner role, refuse. if ( 0 === $ownerCount diff --git a/app/Support/Form/FormSupport.php b/app/Support/Form/FormSupport.php index 8240bc45a1..38e124954b 100644 --- a/app/Support/Form/FormSupport.php +++ b/app/Support/Form/FormSupport.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Support\Form; use Carbon\Carbon; -use Carbon\Exceptions\InvalidDateException; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Support\MessageBag; use Throwable; @@ -37,7 +36,7 @@ trait FormSupport { public function multiSelect(string $name, ?array $list = null, mixed $selected = null, ?array $options = null): string { - $list ??= []; + $list ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -63,7 +62,7 @@ trait FormSupport } $name = str_replace('[]', '', $name); - return (string) trans('form.'.$name); + return (string)trans('form.' . $name); } /** @@ -71,12 +70,12 @@ trait FormSupport */ protected function expandOptionArray(string $name, $label, ?array $options = null): array { - $options ??= []; + $options ??= []; $name = str_replace('[]', '', $name); $options['class'] = 'form-control'; - $options['id'] = 'ffInput_'.$name; + $options['id'] = 'ffInput_' . $name; $options['autocomplete'] = 'off'; - $options['placeholder'] = ucfirst((string) $label); + $options['placeholder'] = ucfirst((string)$label); return $options; } @@ -122,7 +121,7 @@ trait FormSupport */ public function select(string $name, ?array $list = null, $selected = null, ?array $options = null): string { - $list ??= []; + $list ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -146,15 +145,6 @@ trait FormSupport protected function getDate(): Carbon { - /** @var Carbon $date */ - $date = null; - - try { - $date = today(config('app.timezone')); - } catch (InvalidDateException $e) { // @phpstan-ignore-line - app('log')->error($e->getMessage()); - } - - return $date; + return today(config('app.timezone')); } } diff --git a/app/Support/Http/Api/CleansChartData.php b/app/Support/Http/Api/CleansChartData.php index 4b02726c1b..a3d54974cf 100644 --- a/app/Support/Http/Api/CleansChartData.php +++ b/app/Support/Http/Api/CleansChartData.php @@ -43,7 +43,7 @@ trait CleansChartData $return = []; /** - * @var mixed $index + * @var int $index * @var array $array */ foreach ($data as $index => $array) { diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index 2d16024f31..a59d5e755b 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -102,6 +102,9 @@ trait ConvertsDataTypes { // assume this all works, because the validator would have caught any errors. $parameter = (string)request()->query->get($field); + if('' === $parameter) { + return []; + } $parts = explode(',', $parameter); $sortParameters = []; foreach ($parts as $part) { diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index f8f8700a7e..c4b4822ba4 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -317,7 +317,7 @@ class SearchRuleEngine implements RuleEngineInterface Log::debug(sprintf('SearchRuleEngine:: found %d rule(s) to fire.', $this->rules->count())); /** @var Rule $rule */ - foreach ($this->rules as $rule) { + foreach ($this->rules as $rule) { // @phpstan-ignore-line $result = $this->fireRule($rule); if (true === $result && true === $rule->stop_processing) { Log::debug(sprintf('Rule #%d has triggered and executed, but calls to stop processing. Since not in the context of a group, do not stop.', $rule->id)); @@ -335,7 +335,7 @@ class SearchRuleEngine implements RuleEngineInterface // fire each group: /** @var RuleGroup $group */ - foreach ($this->groups as $group) { + foreach ($this->groups as $group) { // @phpstan-ignore-line $this->fireGroup($group); } } diff --git a/database/seeders/TransactionTypeSeeder.php b/database/seeders/TransactionTypeSeeder.php index b4ed1c1ef3..71d247a799 100644 --- a/database/seeders/TransactionTypeSeeder.php +++ b/database/seeders/TransactionTypeSeeder.php @@ -36,7 +36,7 @@ class TransactionTypeSeeder extends Seeder public function run(): void { /** @var TransactionTypeEnum $type */ - foreach (TransactionTypeEnum::cases() as $type) { + foreach (TransactionTypeEnum::cases() as $type) { // @phpstan-ignore-line if (null === TransactionType::where('type', $type->value)->first()) { try { TransactionType::create(['type' => $type->value]); diff --git a/database/seeders/UserRoleSeeder.php b/database/seeders/UserRoleSeeder.php index e9f1a08515..3325f08aee 100644 --- a/database/seeders/UserRoleSeeder.php +++ b/database/seeders/UserRoleSeeder.php @@ -40,7 +40,7 @@ class UserRoleSeeder extends Seeder public function run(): void { /** @var UserRoleEnum $role */ - foreach (UserRoleEnum::cases() as $role) { + foreach (UserRoleEnum::cases() as $role) { // @phpstan-ignore-line if (null === UserRole::where('title', $role->value)->first()) { try { UserRole::create(['title' => $role->value]);