Maintenance: Started work on PHPStan Level 5

This commit is contained in:
Dan Brown
2026-05-30 21:44:25 +01:00
parent f01bb749ab
commit 5611c8c0da
15 changed files with 40 additions and 14 deletions

View File

@@ -42,7 +42,7 @@ class ExternalBaseUserProvider implements UserProvider
/**
* Retrieve a user by the given credentials.
*/
public function retrieveByCredentials(array $credentials): ?Authenticatable
public function retrieveByCredentials(array $credentials): ?User
{
return User::query()
->where('external_auth_id', $credentials['external_auth_id'])

View File

@@ -50,8 +50,12 @@ class ExternalBaseSessionGuard implements StatefulGuard
/**
* Create a new authentication guard.
*/
public function __construct(string $name, UserProvider $provider, Session $session, RegistrationService $registrationService)
{
public function __construct(
string $name,
UserProvider $provider,
Session $session,
RegistrationService $registrationService
) {
$this->name = $name;
$this->session = $session;
$this->provider = $provider;

View File

@@ -83,6 +83,10 @@ class LdapSessionGuard extends ExternalBaseSessionGuard
}
}
if (!($user instanceof User)) {
throw new LoginAttemptException('Could not find or create a user for LDAP login.');
}
// Sync LDAP groups if required
if ($this->ldapService->shouldSyncGroups()) {
$this->ldapService->syncGroups($user, $username);

View File

@@ -131,8 +131,6 @@ class OidcJwtWithClaims implements ProvidesClaims
}
}, $this->keys);
$parsedKeys = array_filter($parsedKeys);
$contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1];
/** @var OidcJwtSigningKey $parsedKey */
foreach ($parsedKeys as $parsedKey) {

View File

@@ -3,6 +3,7 @@
namespace BookStack\Activity\Models;
use BookStack\App\Model;
use BookStack\Entities\Models\Page;
use BookStack\Permissions\Models\JointPermission;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Users\Models\HasCreatorAndUpdater;
@@ -40,6 +41,9 @@ class Comment extends Model implements Loggable, OwnableInterface
/**
* Get the entity that this comment belongs to.
* It's only pages right now hence the typing below.
* Would need a deeper audit if that changes as many areas assume this is always a page.
* @return MorphTo<Page, $this>
*/
public function entity(): MorphTo
{
@@ -55,7 +59,9 @@ class Comment extends Model implements Loggable, OwnableInterface
// Ultimately, we could just align the method name to 'commentable' but that would be a potential
// breaking change and not really worthwhile in a patch due to the risk of creating extra problems.
return $this->morphTo(null, 'commentable_type', 'commentable_id');
/** @var MorphTo<Page, $this> $relation */
$relation = $this->morphTo(null, 'commentable_type', 'commentable_id');
return $relation;
}
/**

View File

@@ -138,8 +138,8 @@ class ApiDocsGenerator
return $validation;
}
if (is_object($validation) && method_exists($validation, '__toString')) {
return strval($validation);
if (is_object($validation) && $validation instanceof \Stringable) {
return $validation->__toString();
}
if ($validation instanceof Password) {

View File

@@ -7,6 +7,7 @@ use BookStack\Theming\ThemeService;
use BookStack\Theming\ThemeViews;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use Illuminate\View\FileViewFinder;
class ThemeServiceProvider extends ServiceProvider
{
@@ -27,7 +28,11 @@ class ThemeServiceProvider extends ServiceProvider
// Boot up the theme system
$themeService = $this->app->make(ThemeService::class);
$viewFactory = $this->app->make('view');
$themeViews = new ThemeViews($viewFactory->getFinder());
$viewFinder = $viewFactory->getFinder();
if (!($viewFinder instanceof FileViewFinder)) {
throw new \Exception('Only the file view finder is supported for the theme system');
}
$themeViews = new ThemeViews($viewFinder);
// Use a custom include so that we can insert theme views before/after includes.
// This is done, even if no theme is active, so that view caching does not create problems

View File

@@ -6,6 +6,7 @@ use BookStack\Facades\Theme;
use BookStack\Permissions\Permission;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Settings\SettingService;
use BookStack\Users\Models\OwnableInterface;
use BookStack\Users\Models\User;
/**
@@ -40,7 +41,7 @@ function user(): User
* Check if the current user has a permission. If an ownable element
* is passed in the jointPermissions are checked against that particular item.
*/
function userCan(string|Permission $permission, ?Model $ownable = null): bool
function userCan(string|Permission $permission, (Model&OwnableInterface)|null $ownable = null): bool
{
if (is_null($ownable)) {
return user()->can($permission);

View File

@@ -46,7 +46,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int|null $created_by
* @property int|null $updated_by
* @property int|null $owned_by
* @property Collection $tags
* @property Collection<int, Tag> $tags
*
* @method static Entity|Builder visible()
* @method static Builder withLastView()

View File

@@ -6,6 +6,7 @@ use BookStack\Activity\Models\View;
use BookStack\Entities\EntityProvider;
use BookStack\Entities\Tools\MixedEntityListLoader;
use BookStack\Permissions\PermissionApplicator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
@@ -20,6 +21,7 @@ class QueryPopular
public function run(int $count, int $page, array $filterModels): Collection
{
/** @var Builder<View> $query */
$query = $this->permissions
->restrictEntityRelationQuery(View::query(), 'views', 'viewable_id', 'viewable_type')
->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count'))

View File

@@ -5,6 +5,7 @@ namespace BookStack\Entities\Queries;
use BookStack\Activity\Models\View;
use BookStack\Entities\Tools\MixedEntityListLoader;
use BookStack\Permissions\PermissionApplicator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
class QueryRecentlyViewed
@@ -22,6 +23,7 @@ class QueryRecentlyViewed
return collect();
}
/** @var Builder<View> $query */
$query = $this->permissions->restrictEntityRelationQuery(
View::query(),
'views',

View File

@@ -5,6 +5,7 @@ namespace BookStack\Entities\Queries;
use BookStack\Activity\Models\Favourite;
use BookStack\Entities\Tools\MixedEntityListLoader;
use BookStack\Permissions\PermissionApplicator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
class QueryTopFavourites
@@ -22,6 +23,7 @@ class QueryTopFavourites
return collect();
}
/** @var Builder<Favourite> $query */
$query = $this->permissions
->restrictEntityRelationQuery(Favourite::query(), 'favourites', 'favouritable_id', 'favouritable_type')
->select('favourites.*')

View File

@@ -7,6 +7,7 @@ use BookStack\App\Model;
use BookStack\Exceptions\NotifyException;
use BookStack\Facades\Activity;
use BookStack\Permissions\Permission;
use BookStack\Users\Models\OwnableInterface;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\JsonResponse;
@@ -80,7 +81,7 @@ abstract class Controller extends BaseController
/**
* Check the current user's permissions against an ownable item otherwise throw an exception.
*/
protected function checkOwnablePermission(string|Permission $permission, Model $ownable, string $redirectLocation = '/'): void
protected function checkOwnablePermission(string|Permission $permission, Model&OwnableInterface $ownable, string $redirectLocation = '/'): void
{
if (!userCan($permission, $ownable)) {
$this->showPermissionError($redirectLocation);

View File

@@ -3,6 +3,7 @@
namespace BookStack\Util;
use Closure;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Throwable;
@@ -24,7 +25,7 @@ use Throwable;
class DatabaseTransaction
{
/**
* @param (Closure(static): TReturn) $callback
* @param (Closure(Connection): TReturn) $callback
*/
public function __construct(
protected Closure $callback

View File

@@ -7,7 +7,7 @@ parameters:
- app
# The level 8 is the highest level
level: 4
level: 5
phpVersion:
min: 80200