Applied another set of static analysis improvements

This commit is contained in:
Dan Brown 2021-11-20 14:03:56 +00:00
parent 754403a29e
commit 876bc10d4d
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
18 changed files with 65 additions and 40 deletions

View File

@ -4,6 +4,7 @@ namespace BookStack\Actions;
use BookStack\Auth\Permissions\PermissionService; use BookStack\Auth\Permissions\PermissionService;
use BookStack\Auth\User; use BookStack\Auth\User;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page; use BookStack\Entities\Models\Page;
@ -100,13 +101,13 @@ class ActivityService
*/ */
public function entityActivity(Entity $entity, int $count = 20, int $page = 1): array public function entityActivity(Entity $entity, int $count = 20, int $page = 1): array
{ {
/** @var [string => int[]] $queryIds */ /** @var array<string, int[]> $queryIds */
$queryIds = [$entity->getMorphClass() => [$entity->id]]; $queryIds = [$entity->getMorphClass() => [$entity->id]];
if ($entity->isA('book')) { if ($entity instanceof Book) {
$queryIds[(new Chapter())->getMorphClass()] = $entity->chapters()->visible()->pluck('id'); $queryIds[(new Chapter())->getMorphClass()] = $entity->chapters()->visible()->pluck('id');
} }
if ($entity->isA('book') || $entity->isA('chapter')) { if ($entity instanceof Book || $entity instanceof Chapter) {
$queryIds[(new Page())->getMorphClass()] = $entity->pages()->visible()->pluck('id'); $queryIds[(new Page())->getMorphClass()] = $entity->pages()->visible()->pluck('id');
} }

View File

@ -4,6 +4,7 @@ namespace BookStack\Auth\Access;
use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Database\Eloquent\Model;
class ExternalBaseUserProvider implements UserProvider class ExternalBaseUserProvider implements UserProvider
{ {
@ -16,8 +17,6 @@ class ExternalBaseUserProvider implements UserProvider
/** /**
* LdapUserProvider constructor. * LdapUserProvider constructor.
*
* @param $model
*/ */
public function __construct(string $model) public function __construct(string $model)
{ {
@ -27,7 +26,7 @@ class ExternalBaseUserProvider implements UserProvider
/** /**
* Create a new instance of the model. * Create a new instance of the model.
* *
* @return \Illuminate\Database\Eloquent\Model * @return Model
*/ */
public function createModel() public function createModel()
{ {
@ -41,7 +40,7 @@ class ExternalBaseUserProvider implements UserProvider
* *
* @param mixed $identifier * @param mixed $identifier
* *
* @return \Illuminate\Contracts\Auth\Authenticatable|null * @return Authenticatable|null
*/ */
public function retrieveById($identifier) public function retrieveById($identifier)
{ {
@ -54,7 +53,7 @@ class ExternalBaseUserProvider implements UserProvider
* @param mixed $identifier * @param mixed $identifier
* @param string $token * @param string $token
* *
* @return \Illuminate\Contracts\Auth\Authenticatable|null * @return Authenticatable|null
*/ */
public function retrieveByToken($identifier, $token) public function retrieveByToken($identifier, $token)
{ {
@ -64,7 +63,7 @@ class ExternalBaseUserProvider implements UserProvider
/** /**
* Update the "remember me" token for the given user in storage. * Update the "remember me" token for the given user in storage.
* *
* @param \Illuminate\Contracts\Auth\Authenticatable $user * @param Authenticatable $user
* @param string $token * @param string $token
* *
* @return void * @return void
@ -79,7 +78,7 @@ class ExternalBaseUserProvider implements UserProvider
* *
* @param array $credentials * @param array $credentials
* *
* @return \Illuminate\Contracts\Auth\Authenticatable|null * @return Authenticatable|null
*/ */
public function retrieveByCredentials(array $credentials) public function retrieveByCredentials(array $credentials)
{ {
@ -94,7 +93,7 @@ class ExternalBaseUserProvider implements UserProvider
/** /**
* Validate a user against the given credentials. * Validate a user against the given credentials.
* *
* @param \Illuminate\Contracts\Auth\Authenticatable $user * @param Authenticatable $user
* @param array $credentials * @param array $credentials
* *
* @return bool * @return bool

View File

@ -4,6 +4,7 @@ namespace BookStack\Auth\Permissions;
use BookStack\Auth\Role; use BookStack\Auth\Role;
use BookStack\Model; use BookStack\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
/** /**
* @property int $id * @property int $id
@ -13,19 +14,15 @@ class RolePermission extends Model
/** /**
* The roles that belong to the permission. * The roles that belong to the permission.
*/ */
public function roles() public function roles(): BelongsToMany
{ {
return $this->belongsToMany(Role::class, 'permission_role', 'permission_id', 'role_id'); return $this->belongsToMany(Role::class, 'permission_role', 'permission_id', 'role_id');
} }
/** /**
* Get the permission object by name. * Get the permission object by name.
*
* @param $name
*
* @return mixed
*/ */
public static function getByName($name) public static function getByName(string $name): ?RolePermission
{ {
return static::where('name', '=', $name)->first(); return static::where('name', '=', $name)->first();
} }

View File

@ -49,7 +49,7 @@ class RegenerateSearch extends Command
DB::setDefaultConnection($this->option('database')); DB::setDefaultConnection($this->option('database'));
} }
$this->searchIndex->indexAllEntities(function (Entity $model, int $processed, int $total) { $this->searchIndex->indexAllEntities(function (Entity $model, int $processed, int $total): void {
$this->info('Indexed ' . class_basename($model) . ' entries (' . $processed . '/' . $total . ')'); $this->info('Indexed ' . class_basename($model) . ' entries (' . $processed . '/' . $total . ')');
}); });

View File

@ -3,13 +3,14 @@
namespace BookStack\Entities\Models; namespace BookStack\Entities\Models;
use BookStack\Auth\User; use BookStack\Auth\User;
use BookStack\Interfaces\Deletable;
use BookStack\Interfaces\Loggable; use BookStack\Interfaces\Loggable;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\MorphTo;
/** /**
* @property Model $deletable * @property Deletable $deletable
*/ */
class Deletion extends Model implements Loggable class Deletion extends Model implements Loggable
{ {

View File

@ -12,6 +12,7 @@ use BookStack\Auth\Permissions\JointPermission;
use BookStack\Entities\Tools\SearchIndex; use BookStack\Entities\Tools\SearchIndex;
use BookStack\Entities\Tools\SlugGenerator; use BookStack\Entities\Tools\SlugGenerator;
use BookStack\Facades\Permissions; use BookStack\Facades\Permissions;
use BookStack\Interfaces\Deletable;
use BookStack\Interfaces\Favouritable; use BookStack\Interfaces\Favouritable;
use BookStack\Interfaces\Sluggable; use BookStack\Interfaces\Sluggable;
use BookStack\Interfaces\Viewable; use BookStack\Interfaces\Viewable;
@ -44,7 +45,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static Builder withLastView() * @method static Builder withLastView()
* @method static Builder withViewCount() * @method static Builder withViewCount()
*/ */
abstract class Entity extends Model implements Sluggable, Favouritable, Viewable abstract class Entity extends Model implements Sluggable, Favouritable, Viewable, Deletable
{ {
use SoftDeletes; use SoftDeletes;
use HasCreatorAndUpdater; use HasCreatorAndUpdater;
@ -210,6 +211,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
/** /**
* Check if this instance or class is a certain type of entity. * Check if this instance or class is a certain type of entity.
* Examples of $type are 'page', 'book', 'chapter'. * Examples of $type are 'page', 'book', 'chapter'.
* @deprecated Use instanceof instead.
*/ */
public static function isA(string $type): bool public static function isA(string $type): bool
{ {

View File

@ -84,11 +84,9 @@ class PageRevision extends Model
* Included here to align with entities in similar use cases. * Included here to align with entities in similar use cases.
* (Yup, Bit of an awkward hack). * (Yup, Bit of an awkward hack).
* *
* @param $type * @deprecated Use instanceof instead.
*
* @return bool
*/ */
public static function isA($type) public static function isA(string $type): bool
{ {
return $type === 'revision'; return $type === 'revision';
} }

View File

@ -67,10 +67,12 @@ class BaseRepo
/** /**
* Update the given items' cover image, or clear it. * Update the given items' cover image, or clear it.
* *
* @param Entity&HasCoverImage $entity
*
* @throws ImageUploadException * @throws ImageUploadException
* @throws \Exception * @throws \Exception
*/ */
public function updateCoverImage(HasCoverImage $entity, ?UploadedFile $coverImage, bool $removeImage = false) public function updateCoverImage($entity, ?UploadedFile $coverImage, bool $removeImage = false)
{ {
if ($coverImage) { if ($coverImage) {
$this->imageRepo->destroyImage($entity->cover); $this->imageRepo->destroyImage($entity->cover);

View File

@ -290,6 +290,8 @@ class PageRepo
public function restoreRevision(Page $page, int $revisionId): Page public function restoreRevision(Page $page, int $revisionId): Page
{ {
$page->revision_count++; $page->revision_count++;
/** @var PageRevision $revision */
$revision = $page->revisions()->where('id', '=', $revisionId)->first(); $revision = $page->revisions()->where('id', '=', $revisionId)->first();
$page->fill($revision->toArray()); $page->fill($revision->toArray());
@ -334,7 +336,8 @@ class PageRepo
} }
$page->chapter_id = ($parent instanceof Chapter) ? $parent->id : null; $page->chapter_id = ($parent instanceof Chapter) ? $parent->id : null;
$page->changeBook($parent instanceof Book ? $parent->id : $parent->book->id); $newBookId = ($parent instanceof Chapter) ? $parent->book->id : $parent->id;
$page->changeBook($newBookId);
$page->rebuildPermissions(); $page->rebuildPermissions();
Activity::addForEntity($page, ActivityType::PAGE_MOVE); Activity::addForEntity($page, ActivityType::PAGE_MOVE);
@ -406,7 +409,7 @@ class PageRepo
*/ */
protected function changeParent(Page $page, Entity $parent) protected function changeParent(Page $page, Entity $parent)
{ {
$book = ($parent instanceof Book) ? $parent : $parent->book; $book = ($parent instanceof Chapter) ? $parent->book : $parent;
$page->chapter_id = ($parent instanceof Chapter) ? $parent->id : 0; $page->chapter_id = ($parent instanceof Chapter) ? $parent->id : 0;
$page->save(); $page->save();
@ -467,6 +470,7 @@ class PageRepo
{ {
$parent = $page->getParent(); $parent = $page->getParent();
if ($parent instanceof Chapter) { if ($parent instanceof Chapter) {
/** @var ?Page $lastPage */
$lastPage = $parent->pages('desc')->first(); $lastPage = $parent->pages('desc')->first();
return $lastPage ? $lastPage->priority + 1 : 0; return $lastPage ? $lastPage->priority + 1 : 0;

View File

@ -67,7 +67,7 @@ class BookContents
$all->each(function (Entity $entity) use ($renderPages) { $all->each(function (Entity $entity) use ($renderPages) {
$entity->setRelation('book', $this->book); $entity->setRelation('book', $this->book);
if ($renderPages && $entity->isA('page')) { if ($renderPages && $entity instanceof Page) {
$entity->html = (new PageContent($entity))->render(); $entity->html = (new PageContent($entity))->render();
} }
}); });
@ -151,7 +151,7 @@ class BookContents
$priorityChanged = intval($model->priority) !== intval($sortMapItem->sort); $priorityChanged = intval($model->priority) !== intval($sortMapItem->sort);
$bookChanged = intval($model->book_id) !== intval($sortMapItem->book); $bookChanged = intval($model->book_id) !== intval($sortMapItem->book);
$chapterChanged = ($sortMapItem->type === 'page') && intval($model->chapter_id) !== $sortMapItem->parentChapter; $chapterChanged = ($model instanceof Page) && intval($model->chapter_id) !== $sortMapItem->parentChapter;
if ($bookChanged) { if ($bookChanged) {
$model->changeBook($sortMapItem->book); $model->changeBook($sortMapItem->book);

View File

@ -64,7 +64,7 @@ class NextPreviousContentLocator
/** @var Entity $item */ /** @var Entity $item */
foreach ($bookTree->all() as $item) { foreach ($bookTree->all() as $item) {
$flatOrdered->push($item); $flatOrdered->push($item);
$childPages = $item->visible_pages ?? []; $childPages = $item->getAttribute('visible_pages') ?? [];
$flatOrdered = $flatOrdered->concat($childPages); $flatOrdered = $flatOrdered->concat($childPages);
} }

View File

@ -156,7 +156,7 @@ class PageContent
/** /**
* Parse a base64 image URI into the data and extension. * Parse a base64 image URI into the data and extension.
* *
* @return array{extension: array, data: string} * @return array{extension: string, data: string}
*/ */
protected function parseBase64ImageUri(string $uri): array protected function parseBase64ImageUri(string $uri): array
{ {
@ -230,7 +230,7 @@ class PageContent
*/ */
protected function setUniqueId(\DOMNode $element, array &$idMap): array protected function setUniqueId(\DOMNode $element, array &$idMap): array
{ {
if (get_class($element) !== 'DOMElement') { if (!$element instanceof \DOMElement) {
return ['', '']; return ['', ''];
} }
@ -242,7 +242,7 @@ class PageContent
return [$existingId, $existingId]; return [$existingId, $existingId];
} }
// Create an unique id for the element // Create a unique id for the element
// Uses the content as a basis to ensure output is the same every time // Uses the content as a basis to ensure output is the same every time
// the same content is passed through. // the same content is passed through.
$contentId = 'bkmrk-' . mb_substr(strtolower(preg_replace('/\s+/', '-', trim($element->nodeValue))), 0, 20); $contentId = 'bkmrk-' . mb_substr(strtolower(preg_replace('/\s+/', '-', trim($element->nodeValue))), 0, 20);

View File

@ -67,7 +67,7 @@ class SearchIndex
* - The number that have been processed so far. * - The number that have been processed so far.
* - The total number of that model to be processed. * - The total number of that model to be processed.
* *
* @param callable(Entity, int, int)|null $progressCallback * @param callable(Entity, int, int):void|null $progressCallback
*/ */
public function indexAllEntities(?callable $progressCallback = null) public function indexAllEntities(?callable $progressCallback = null)
{ {

View File

@ -9,6 +9,7 @@ use BookStack\Entities\Models\BookChild;
use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page; use BookStack\Entities\Models\Page;
use BookStack\Entities\Models\SearchTerm; use BookStack\Entities\Models\SearchTerm;
use Illuminate\Database\Connection;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Collection as EloquentCollection; use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -356,7 +357,9 @@ class SearchRunner
// We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will // We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will
// search the value as a string which prevents being able to do number-based operations // search the value as a string which prevents being able to do number-based operations
// on the tag values. We ensure it has a numeric value and then cast it just to be sure. // on the tag values. We ensure it has a numeric value and then cast it just to be sure.
$tagValue = (float) trim($query->getConnection()->getPdo()->quote($tagValue), "'"); /** @var Connection $connection */
$connection = $query->getConnection();
$tagValue = (float) trim($connection->getPdo()->quote($tagValue), "'");
$query->whereRaw("value ${tagOperator} ${tagValue}"); $query->whereRaw("value ${tagOperator} ${tagValue}");
} else { } else {
$query->where('value', $tagOperator, $tagValue); $query->where('value', $tagOperator, $tagValue);

View File

@ -5,6 +5,7 @@ namespace BookStack\Entities\Tools;
use BookStack\Entities\EntityProvider; use BookStack\Entities\EntityProvider;
use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Bookshelf; use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Page; use BookStack\Entities\Models\Page;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -24,7 +25,7 @@ class SiblingFetcher
} }
// Page in book or chapter // Page in book or chapter
if (($entity instanceof Page && !$entity->chapter) || $entity->isA('chapter')) { if (($entity instanceof Page && !$entity->chapter) || $entity instanceof Chapter) {
$entities = $entity->book->getDirectChildren(); $entities = $entity->book->getDirectChildren();
} }

View File

@ -235,13 +235,15 @@ class TrashCan
{ {
$shouldRestore = true; $shouldRestore = true;
$restoreCount = 0; $restoreCount = 0;
$parent = $deletion->deletable->getParent();
if ($deletion->deletable instanceof Entity) {
$parent = $deletion->deletable->getParent();
if ($parent && $parent->trashed()) { if ($parent && $parent->trashed()) {
$shouldRestore = false; $shouldRestore = false;
} }
}
if ($shouldRestore) { if ($deletion->deletable instanceof Entity && $shouldRestore) {
$restoreCount = $this->restoreEntity($deletion->deletable); $restoreCount = $this->restoreEntity($deletion->deletable);
} }

View File

@ -58,6 +58,7 @@ class RecycleBinController extends Controller
$searching = false; $searching = false;
} }
} }
/** @var ?Deletion $parentDeletion */ /** @var ?Deletion $parentDeletion */
$parentDeletion = ($currentDeletable === $deletion->deletable) ? null : $currentDeletable->deletions()->first(); $parentDeletion = ($currentDeletable === $deletion->deletable) ? null : $currentDeletable->deletions()->first();

View File

@ -0,0 +1,14 @@
<?php
namespace BookStack\Interfaces;
use Illuminate\Database\Eloquent\Relations\MorphMany;
/**
* A model that can be deleted in a manner that deletions
* are tracked to be part of the recycle bin system.
*/
interface Deletable
{
public function deletions(): MorphMany;
}