From efb7d21b52eb033530e80e5e49d175e6e3b031f4 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 2 Oct 2020 17:50:59 +0200 Subject: [PATCH] Add strict types for bookmarks management Parameters typing and using strict types overall increase the codebase quality by enforcing the a given parameter will have the expected type. It also removes the need to unnecessary unit tests checking methods behavior with invalid input. --- application/api/ApiUtils.php | 6 +- application/api/controllers/Links.php | 19 +-- application/bookmark/Bookmark.php | 121 +++++++++--------- application/bookmark/BookmarkArray.php | 14 +- application/bookmark/BookmarkFileService.php | 66 ++++------ application/bookmark/BookmarkFilter.php | 47 ++++--- application/bookmark/BookmarkIO.php | 6 +- application/bookmark/BookmarkInitializer.php | 6 +- .../bookmark/BookmarkServiceInterface.php | 72 ++++++----- application/feed/FeedBuilder.php | 2 +- .../controller/admin/ThumbnailsController.php | 2 +- application/security/LoginManager.php | 2 +- tests/HistoryTest.php | 8 -- tests/bookmark/BookmarkArrayTest.php | 13 -- tests/bookmark/BookmarkFileServiceTest.php | 44 ------- tests/bookmark/BookmarkTest.php | 38 ------ .../DeleteBookmarkTest.php | 4 + .../SaveBookmarkTest.php | 20 ++- .../admin/ThumbnailsControllerTest.php | 4 +- 19 files changed, 209 insertions(+), 285 deletions(-) diff --git a/application/api/ApiUtils.php b/application/api/ApiUtils.php index 4a6326f0..eb1ca9bc 100644 --- a/application/api/ApiUtils.php +++ b/application/api/ApiUtils.php @@ -89,12 +89,12 @@ class ApiUtils * If no URL is provided, it will generate a local note URL. * If no title is provided, it will use the URL as title. * - * @param array $input Request Link. - * @param bool $defaultPrivate Request Link. + * @param array|null $input Request Link. + * @param bool $defaultPrivate Setting defined if a bookmark is private by default. * * @return Bookmark instance. */ - public static function buildBookmarkFromRequest($input, $defaultPrivate): Bookmark + public static function buildBookmarkFromRequest(?array $input, bool $defaultPrivate): Bookmark { $bookmark = new Bookmark(); $url = ! empty($input['url']) ? cleanup_url($input['url']) : ''; diff --git a/application/api/controllers/Links.php b/application/api/controllers/Links.php index 778097fd..73a1b84e 100644 --- a/application/api/controllers/Links.php +++ b/application/api/controllers/Links.php @@ -96,11 +96,12 @@ class Links extends ApiController */ public function getLink($request, $response, $args) { - if (!$this->bookmarkService->exists($args['id'])) { + $id = is_integer_mixed($args['id']) ? (int) $args['id'] : null; + if ($id === null || ! $this->bookmarkService->exists($id)) { throw new ApiLinkNotFoundException(); } $index = index_url($this->ci['environment']); - $out = ApiUtils::formatLink($this->bookmarkService->get($args['id']), $index); + $out = ApiUtils::formatLink($this->bookmarkService->get($id), $index); return $response->withJson($out, 200, $this->jsonStyle); } @@ -115,7 +116,7 @@ class Links extends ApiController */ public function postLink($request, $response) { - $data = $request->getParsedBody(); + $data = (array) ($request->getParsedBody() ?? []); $bookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links')); // duplicate by URL, return 409 Conflict if (! empty($bookmark->getUrl()) @@ -148,7 +149,8 @@ class Links extends ApiController */ public function putLink($request, $response, $args) { - if (! $this->bookmarkService->exists($args['id'])) { + $id = is_integer_mixed($args['id']) ? (int) $args['id'] : null; + if ($id === null || !$this->bookmarkService->exists($id)) { throw new ApiLinkNotFoundException(); } @@ -159,7 +161,7 @@ class Links extends ApiController // duplicate URL on a different link, return 409 Conflict if (! empty($requestBookmark->getUrl()) && ! empty($dup = $this->bookmarkService->findByUrl($requestBookmark->getUrl())) - && $dup->getId() != $args['id'] + && $dup->getId() != $id ) { return $response->withJson( ApiUtils::formatLink($dup, $index), @@ -168,7 +170,7 @@ class Links extends ApiController ); } - $responseBookmark = $this->bookmarkService->get($args['id']); + $responseBookmark = $this->bookmarkService->get($id); $responseBookmark = ApiUtils::updateLink($responseBookmark, $requestBookmark); $this->bookmarkService->set($responseBookmark); @@ -189,10 +191,11 @@ class Links extends ApiController */ public function deleteLink($request, $response, $args) { - if (! $this->bookmarkService->exists($args['id'])) { + $id = is_integer_mixed($args['id']) ? (int) $args['id'] : null; + if ($id === null || !$this->bookmarkService->exists($id)) { throw new ApiLinkNotFoundException(); } - $bookmark = $this->bookmarkService->get($args['id']); + $bookmark = $this->bookmarkService->get($id); $this->bookmarkService->remove($bookmark); return $response->withStatus(204); diff --git a/application/bookmark/Bookmark.php b/application/bookmark/Bookmark.php index 1beb8be2..fa45d2fc 100644 --- a/application/bookmark/Bookmark.php +++ b/application/bookmark/Bookmark.php @@ -1,5 +1,7 @@ id = $data['id']; - $this->shortUrl = $data['shorturl']; - $this->url = $data['url']; - $this->title = $data['title']; - $this->description = $data['description']; - $this->thumbnail = isset($data['thumbnail']) ? $data['thumbnail'] : null; - $this->sticky = isset($data['sticky']) ? $data['sticky'] : false; - $this->created = $data['created']; + $this->id = $data['id'] ?? null; + $this->shortUrl = $data['shorturl'] ?? null; + $this->url = $data['url'] ?? null; + $this->title = $data['title'] ?? null; + $this->description = $data['description'] ?? null; + $this->thumbnail = $data['thumbnail'] ?? null; + $this->sticky = $data['sticky'] ?? false; + $this->created = $data['created'] ?? null; if (is_array($data['tags'])) { $this->tags = $data['tags']; } else { - $this->tags = preg_split('/\s+/', $data['tags'], -1, PREG_SPLIT_NO_EMPTY); + $this->tags = preg_split('/\s+/', $data['tags'] ?? '', -1, PREG_SPLIT_NO_EMPTY); } if (! empty($data['updated'])) { $this->updated = $data['updated']; } - $this->private = $data['private'] ? true : false; + $this->private = ($data['private'] ?? false) ? true : false; return $this; } @@ -95,13 +97,12 @@ class Bookmark * * @throws InvalidBookmarkException */ - public function validate() + public function validate(): void { if ($this->id === null || ! is_int($this->id) || empty($this->shortUrl) || empty($this->created) - || ! $this->created instanceof DateTimeInterface ) { throw new InvalidBookmarkException($this); } @@ -119,11 +120,11 @@ class Bookmark * - created: with the current datetime * - shortUrl: with a generated small hash from the date and the given ID * - * @param int $id + * @param int|null $id * * @return Bookmark */ - public function setId($id) + public function setId(?int $id): Bookmark { $this->id = $id; if (empty($this->created)) { @@ -139,9 +140,9 @@ class Bookmark /** * Get the Id. * - * @return int + * @return int|null */ - public function getId() + public function getId(): ?int { return $this->id; } @@ -149,9 +150,9 @@ class Bookmark /** * Get the ShortUrl. * - * @return string + * @return string|null */ - public function getShortUrl() + public function getShortUrl(): ?string { return $this->shortUrl; } @@ -159,9 +160,9 @@ class Bookmark /** * Get the Url. * - * @return string + * @return string|null */ - public function getUrl() + public function getUrl(): ?string { return $this->url; } @@ -171,7 +172,7 @@ class Bookmark * * @return string */ - public function getTitle() + public function getTitle(): ?string { return $this->title; } @@ -181,7 +182,7 @@ class Bookmark * * @return string */ - public function getDescription() + public function getDescription(): string { return ! empty($this->description) ? $this->description : ''; } @@ -191,7 +192,7 @@ class Bookmark * * @return DateTimeInterface */ - public function getCreated() + public function getCreated(): ?DateTimeInterface { return $this->created; } @@ -201,7 +202,7 @@ class Bookmark * * @return DateTimeInterface */ - public function getUpdated() + public function getUpdated(): ?DateTimeInterface { return $this->updated; } @@ -209,11 +210,11 @@ class Bookmark /** * Set the ShortUrl. * - * @param string $shortUrl + * @param string|null $shortUrl * * @return Bookmark */ - public function setShortUrl($shortUrl) + public function setShortUrl(?string $shortUrl): Bookmark { $this->shortUrl = $shortUrl; @@ -223,14 +224,14 @@ class Bookmark /** * Set the Url. * - * @param string $url - * @param array $allowedProtocols + * @param string|null $url + * @param string[] $allowedProtocols * * @return Bookmark */ - public function setUrl($url, $allowedProtocols = []) + public function setUrl(?string $url, array $allowedProtocols = []): Bookmark { - $url = trim($url); + $url = $url !== null ? trim($url) : ''; if (! empty($url)) { $url = whitelist_protocols($url, $allowedProtocols); } @@ -242,13 +243,13 @@ class Bookmark /** * Set the Title. * - * @param string $title + * @param string|null $title * * @return Bookmark */ - public function setTitle($title) + public function setTitle(?string $title): Bookmark { - $this->title = trim($title); + $this->title = $title !== null ? trim($title) : ''; return $this; } @@ -256,11 +257,11 @@ class Bookmark /** * Set the Description. * - * @param string $description + * @param string|null $description * * @return Bookmark */ - public function setDescription($description) + public function setDescription(?string $description): Bookmark { $this->description = $description; @@ -271,11 +272,11 @@ class Bookmark * Set the Created. * Note: you shouldn't set this manually except for special cases (like bookmark import) * - * @param DateTimeInterface $created + * @param DateTimeInterface|null $created * * @return Bookmark */ - public function setCreated($created) + public function setCreated(?DateTimeInterface $created): Bookmark { $this->created = $created; @@ -285,11 +286,11 @@ class Bookmark /** * Set the Updated. * - * @param DateTimeInterface $updated + * @param DateTimeInterface|null $updated * * @return Bookmark */ - public function setUpdated($updated) + public function setUpdated(?DateTimeInterface $updated): Bookmark { $this->updated = $updated; @@ -301,7 +302,7 @@ class Bookmark * * @return bool */ - public function isPrivate() + public function isPrivate(): bool { return $this->private ? true : false; } @@ -309,11 +310,11 @@ class Bookmark /** * Set the Private. * - * @param bool $private + * @param bool|null $private * * @return Bookmark */ - public function setPrivate($private) + public function setPrivate(?bool $private): Bookmark { $this->private = $private ? true : false; @@ -323,9 +324,9 @@ class Bookmark /** * Get the Tags. * - * @return array + * @return string[] */ - public function getTags() + public function getTags(): array { return is_array($this->tags) ? $this->tags : []; } @@ -333,13 +334,13 @@ class Bookmark /** * Set the Tags. * - * @param array $tags + * @param string[]|null $tags * * @return Bookmark */ - public function setTags($tags) + public function setTags(?array $tags): Bookmark { - $this->setTagsString(implode(' ', $tags)); + $this->setTagsString(implode(' ', $tags ?? [])); return $this; } @@ -357,11 +358,11 @@ class Bookmark /** * Set the Thumbnail. * - * @param string|bool $thumbnail Thumbnail's URL - false if no thumbnail could be found + * @param string|bool|null $thumbnail Thumbnail's URL - false if no thumbnail could be found * * @return Bookmark */ - public function setThumbnail($thumbnail) + public function setThumbnail($thumbnail): Bookmark { $this->thumbnail = $thumbnail; @@ -373,7 +374,7 @@ class Bookmark * * @return bool */ - public function isSticky() + public function isSticky(): bool { return $this->sticky ? true : false; } @@ -381,11 +382,11 @@ class Bookmark /** * Set the Sticky. * - * @param bool $sticky + * @param bool|null $sticky * * @return Bookmark */ - public function setSticky($sticky) + public function setSticky(?bool $sticky): Bookmark { $this->sticky = $sticky ? true : false; @@ -395,7 +396,7 @@ class Bookmark /** * @return string Bookmark's tags as a string, separated by a space */ - public function getTagsString() + public function getTagsString(): string { return implode(' ', $this->getTags()); } @@ -403,7 +404,7 @@ class Bookmark /** * @return bool */ - public function isNote() + public function isNote(): bool { // We check empty value to get a valid result if the link has not been saved yet return empty($this->url) || startsWith($this->url, '/shaare/') || $this->url[0] === '?'; @@ -416,14 +417,14 @@ class Bookmark * - multiple spaces will be removed * - trailing dash in tags will be removed * - * @param string $tags + * @param string|null $tags * * @return $this */ - public function setTagsString($tags) + public function setTagsString(?string $tags): Bookmark { // Remove first '-' char in tags. - $tags = preg_replace('/(^| )\-/', '$1', $tags); + $tags = preg_replace('/(^| )\-/', '$1', $tags ?? ''); // Explode all tags separted by spaces or commas $tags = preg_split('/[\s,]+/', $tags); // Remove eventual empty values @@ -440,7 +441,7 @@ class Bookmark * @param string $fromTag * @param string $toTag */ - public function renameTag($fromTag, $toTag) + public function renameTag(string $fromTag, string $toTag): void { if (($pos = array_search($fromTag, $this->tags)) !== false) { $this->tags[$pos] = trim($toTag); @@ -452,7 +453,7 @@ class Bookmark * * @param string $tag */ - public function deleteTag($tag) + public function deleteTag(string $tag): void { if (($pos = array_search($tag, $this->tags)) !== false) { unset($this->tags[$pos]); diff --git a/application/bookmark/BookmarkArray.php b/application/bookmark/BookmarkArray.php index 3bd5eb20..67bb3b73 100644 --- a/application/bookmark/BookmarkArray.php +++ b/application/bookmark/BookmarkArray.php @@ -1,5 +1,7 @@ ids[$id])) { + if ($id !== null && isset($this->ids[$id])) { return $this->ids[$id]; } return null; @@ -205,7 +207,7 @@ class BookmarkArray implements \Iterator, \Countable, \ArrayAccess * * @return int next ID. */ - public function getNextId() + public function getNextId(): int { if (!empty($this->ids)) { return max(array_keys($this->ids)) + 1; @@ -214,11 +216,11 @@ class BookmarkArray implements \Iterator, \Countable, \ArrayAccess } /** - * @param $url + * @param string $url * * @return Bookmark|null */ - public function getByUrl($url) + public function getByUrl(string $url): ?Bookmark { if (! empty($url) && isset($this->urls[$url]) diff --git a/application/bookmark/BookmarkFileService.php b/application/bookmark/BookmarkFileService.php index 1ba00712..804b2520 100644 --- a/application/bookmark/BookmarkFileService.php +++ b/application/bookmark/BookmarkFileService.php @@ -1,9 +1,10 @@ conf = $conf; $this->history = $history; @@ -96,7 +97,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function findByHash($hash) + public function findByHash(string $hash): Bookmark { $bookmark = $this->bookmarkFilter->filter(BookmarkFilter::$FILTER_HASH, $hash); // PHP 7.3 introduced array_key_first() to avoid this hack @@ -111,7 +112,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function findByUrl($url) + public function findByUrl(string $url): ?Bookmark { return $this->bookmarks->getByUrl($url); } @@ -120,10 +121,10 @@ class BookmarkFileService implements BookmarkServiceInterface * @inheritDoc */ public function search( - $request = [], - $visibility = null, - $caseSensitive = false, - $untaggedOnly = false, + array $request = [], + string $visibility = null, + bool $caseSensitive = false, + bool $untaggedOnly = false, bool $ignoreSticky = false ) { if ($visibility === null) { @@ -131,8 +132,8 @@ class BookmarkFileService implements BookmarkServiceInterface } // Filter bookmark database according to parameters. - $searchtags = isset($request['searchtags']) ? $request['searchtags'] : ''; - $searchterm = isset($request['searchterm']) ? $request['searchterm'] : ''; + $searchTags = isset($request['searchtags']) ? $request['searchtags'] : ''; + $searchTerm = isset($request['searchterm']) ? $request['searchterm'] : ''; if ($ignoreSticky) { $this->bookmarks->reorder('DESC', true); @@ -140,7 +141,7 @@ class BookmarkFileService implements BookmarkServiceInterface return $this->bookmarkFilter->filter( BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT, - [$searchtags, $searchterm], + [$searchTags, $searchTerm], $caseSensitive, $visibility, $untaggedOnly @@ -150,7 +151,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function get($id, $visibility = null) + public function get(int $id, string $visibility = null): Bookmark { if (! isset($this->bookmarks[$id])) { throw new BookmarkNotFoundException(); @@ -173,20 +174,17 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function set($bookmark, $save = true) + public function set(Bookmark $bookmark, bool $save = true): Bookmark { if (true !== $this->isLoggedIn) { throw new Exception(t('You\'re not authorized to alter the datastore')); } - if (! $bookmark instanceof Bookmark) { - throw new Exception(t('Provided data is invalid')); - } if (! isset($this->bookmarks[$bookmark->getId()])) { throw new BookmarkNotFoundException(); } $bookmark->validate(); - $bookmark->setUpdated(new \DateTime()); + $bookmark->setUpdated(new DateTime()); $this->bookmarks[$bookmark->getId()] = $bookmark; if ($save === true) { $this->save(); @@ -198,15 +196,12 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function add($bookmark, $save = true) + public function add(Bookmark $bookmark, bool $save = true): Bookmark { if (true !== $this->isLoggedIn) { throw new Exception(t('You\'re not authorized to alter the datastore')); } - if (! $bookmark instanceof Bookmark) { - throw new Exception(t('Provided data is invalid')); - } - if (! empty($bookmark->getId())) { + if (!empty($bookmark->getId())) { throw new Exception(t('This bookmarks already exists')); } $bookmark->setId($this->bookmarks->getNextId()); @@ -223,14 +218,11 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function addOrSet($bookmark, $save = true) + public function addOrSet(Bookmark $bookmark, bool $save = true): Bookmark { if (true !== $this->isLoggedIn) { throw new Exception(t('You\'re not authorized to alter the datastore')); } - if (! $bookmark instanceof Bookmark) { - throw new Exception('Provided data is invalid'); - } if ($bookmark->getId() === null) { return $this->add($bookmark, $save); } @@ -240,14 +232,11 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function remove($bookmark, $save = true) + public function remove(Bookmark $bookmark, bool $save = true): void { if (true !== $this->isLoggedIn) { throw new Exception(t('You\'re not authorized to alter the datastore')); } - if (! $bookmark instanceof Bookmark) { - throw new Exception(t('Provided data is invalid')); - } if (! isset($this->bookmarks[$bookmark->getId()])) { throw new BookmarkNotFoundException(); } @@ -262,7 +251,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function exists($id, $visibility = null) + public function exists(int $id, string $visibility = null): bool { if (! isset($this->bookmarks[$id])) { return false; @@ -285,7 +274,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function count($visibility = null) + public function count(string $visibility = null): int { return count($this->search([], $visibility)); } @@ -293,7 +282,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function save() + public function save(): void { if (true !== $this->isLoggedIn) { // TODO: raise an Exception instead @@ -308,7 +297,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function bookmarksCountPerTag($filteringTags = [], $visibility = null) + public function bookmarksCountPerTag(array $filteringTags = [], string $visibility = null): array { $bookmarks = $this->search(['searchtags' => $filteringTags], $visibility); $tags = []; @@ -344,13 +333,14 @@ class BookmarkFileService implements BookmarkServiceInterface $keys = array_keys($tags); $tmpTags = array_combine($keys, $keys); array_multisort($tags, SORT_DESC, $tmpTags, SORT_ASC, $tags); + return $tags; } /** * @inheritDoc */ - public function days() + public function days(): array { $bookmarkDays = []; foreach ($this->search() as $bookmark) { @@ -365,7 +355,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function filterDay($request) + public function filterDay(string $request) { $visibility = $this->isLoggedIn ? BookmarkFilter::$ALL : BookmarkFilter::$PUBLIC; @@ -375,7 +365,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function initialize() + public function initialize(): void { $initializer = new BookmarkInitializer($this); $initializer->initialize(); @@ -388,7 +378,7 @@ class BookmarkFileService implements BookmarkServiceInterface /** * Handles migration to the new database format (BookmarksArray). */ - protected function migrate() + protected function migrate(): void { $bookmarkDb = new LegacyLinkDB( $this->conf->get('resource.datastore'), diff --git a/application/bookmark/BookmarkFilter.php b/application/bookmark/BookmarkFilter.php index 6636bbfe..4232f114 100644 --- a/application/bookmark/BookmarkFilter.php +++ b/application/bookmark/BookmarkFilter.php @@ -1,5 +1,7 @@ bookmarks; @@ -151,11 +158,11 @@ class BookmarkFilter * * @param string $smallHash permalink hash. * - * @return array $filtered array containing permalink data. + * @return Bookmark[] $filtered array containing permalink data. * - * @throws \Shaarli\Bookmark\Exception\BookmarkNotFoundException if the smallhash doesn't match any link. + * @throws BookmarkNotFoundException if the smallhash doesn't match any link. */ - private function filterSmallHash($smallHash) + private function filterSmallHash(string $smallHash) { foreach ($this->bookmarks as $key => $l) { if ($smallHash == $l->getShortUrl()) { @@ -186,9 +193,9 @@ class BookmarkFilter * @param string $searchterms search query. * @param string $visibility Optional: return only all/private/public bookmarks. * - * @return array search results. + * @return Bookmark[] search results. */ - private function filterFulltext($searchterms, $visibility = 'all') + private function filterFulltext(string $searchterms, string $visibility = 'all') { if (empty($searchterms)) { return $this->noFilter($visibility); @@ -268,7 +275,7 @@ class BookmarkFilter * * @return string generated regex fragment */ - private static function tag2regex($tag) + private static function tag2regex(string $tag): string { $len = strlen($tag); if (!$len || $tag === "-" || $tag === "*") { @@ -314,13 +321,13 @@ class BookmarkFilter * You can specify one or more tags, separated by space or a comma, e.g. * print_r($mydb->filterTags('linux programming')); * - * @param string $tags list of tags separated by commas or blank spaces. - * @param bool $casesensitive ignore case if false. - * @param string $visibility Optional: return only all/private/public bookmarks. + * @param string|array $tags list of tags, separated by commas or blank spaces if passed as string. + * @param bool $casesensitive ignore case if false. + * @param string $visibility Optional: return only all/private/public bookmarks. * - * @return array filtered bookmarks. + * @return Bookmark[] filtered bookmarks. */ - public function filterTags($tags, $casesensitive = false, $visibility = 'all') + public function filterTags($tags, bool $casesensitive = false, string $visibility = 'all') { // get single tags (we may get passed an array, even though the docs say different) $inputTags = $tags; @@ -396,9 +403,9 @@ class BookmarkFilter * * @param string $visibility return only all/private/public bookmarks. * - * @return array filtered bookmarks. + * @return Bookmark[] filtered bookmarks. */ - public function filterUntagged($visibility) + public function filterUntagged(string $visibility) { $filtered = []; foreach ($this->bookmarks as $key => $link) { @@ -427,11 +434,11 @@ class BookmarkFilter * @param string $day day to filter. * @param string $visibility return only all/private/public bookmarks. - * @return array all link matching given day. + * @return Bookmark[] all link matching given day. * * @throws Exception if date format is invalid. */ - public function filterDay($day, $visibility) + public function filterDay(string $day, string $visibility) { if (!checkDateFormat('Ymd', $day)) { throw new Exception('Invalid date format'); @@ -460,9 +467,9 @@ class BookmarkFilter * @param string $tags string containing a list of tags. * @param bool $casesensitive will convert everything to lowercase if false. * - * @return array filtered tags string. + * @return string[] filtered tags string. */ - public static function tagsStrToArray($tags, $casesensitive) + public static function tagsStrToArray(string $tags, bool $casesensitive): array { // We use UTF-8 conversion to handle various graphemes (i.e. cyrillic, or greek) $tagsOut = $casesensitive ? $tags : mb_convert_case($tags, MB_CASE_LOWER, 'UTF-8'); diff --git a/application/bookmark/BookmarkIO.php b/application/bookmark/BookmarkIO.php index 099653b0..f40fa476 100644 --- a/application/bookmark/BookmarkIO.php +++ b/application/bookmark/BookmarkIO.php @@ -1,5 +1,7 @@ bookmarkService = $bookmarkService; } @@ -31,7 +33,7 @@ class BookmarkInitializer /** * Initialize the data store with default bookmarks */ - public function initialize() + public function initialize(): void { $bookmark = new Bookmark(); $bookmark->setTitle('quicksilver (loop) on Vimeo ' . t('(private bookmark with thumbnail demo)')); diff --git a/application/bookmark/BookmarkServiceInterface.php b/application/bookmark/BookmarkServiceInterface.php index 638cfa5f..37a54d03 100644 --- a/application/bookmark/BookmarkServiceInterface.php +++ b/application/bookmark/BookmarkServiceInterface.php @@ -1,7 +1,8 @@ bookmarksCount */ - public function bookmarksCountPerTag($filteringTags = [], $visibility = 'all'); + public function bookmarksCountPerTag(array $filteringTags = [], ?string $visibility = null): array; /** * Returns the list of days containing articles (oldest first) * * @return array containing days (in format YYYYMMDD). */ - public function days(); + public function days(): array; /** * Returns the list of articles for a given day. @@ -166,10 +170,10 @@ interface BookmarkServiceInterface * * @throws BookmarkNotFoundException */ - public function filterDay($request); + public function filterDay(string $request); /** * Creates the default database after a fresh install. */ - public function initialize(); + public function initialize(): void; } diff --git a/application/feed/FeedBuilder.php b/application/feed/FeedBuilder.php index f6def630..f70fce4f 100644 --- a/application/feed/FeedBuilder.php +++ b/application/feed/FeedBuilder.php @@ -102,7 +102,7 @@ class FeedBuilder } // Optionally filter the results: - $linksToDisplay = $this->linkDB->search($userInput, null, false, false, true); + $linksToDisplay = $this->linkDB->search($userInput ?? [], null, false, false, true); $nblinksToDisplay = $this->getNbLinks(count($linksToDisplay), $userInput); diff --git a/application/front/controller/admin/ThumbnailsController.php b/application/front/controller/admin/ThumbnailsController.php index 81c87ed0..4dc09d38 100644 --- a/application/front/controller/admin/ThumbnailsController.php +++ b/application/front/controller/admin/ThumbnailsController.php @@ -52,7 +52,7 @@ class ThumbnailsController extends ShaarliAdminController } try { - $bookmark = $this->container->bookmarkService->get($id); + $bookmark = $this->container->bookmarkService->get((int) $id); } catch (BookmarkNotFoundException $e) { return $response->withStatus(404); } diff --git a/application/security/LoginManager.php b/application/security/LoginManager.php index d74c3118..65048f10 100644 --- a/application/security/LoginManager.php +++ b/application/security/LoginManager.php @@ -118,7 +118,7 @@ class LoginManager * * @return true when the user is logged in, false otherwise */ - public function isLoggedIn() + public function isLoggedIn(): bool { if ($this->openShaarli) { return true; diff --git a/tests/HistoryTest.php b/tests/HistoryTest.php index 6dc0e5b7..e810104e 100644 --- a/tests/HistoryTest.php +++ b/tests/HistoryTest.php @@ -89,14 +89,6 @@ class HistoryTest extends \Shaarli\TestCase $this->assertEquals(History::CREATED, $actual['event']); $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); $this->assertEquals(1, $actual['id']); - - $history = new History(self::$historyFilePath); - $bookmark = (new Bookmark())->setId('str'); - $history->addLink($bookmark); - $actual = $history->getHistory()[0]; - $this->assertEquals(History::CREATED, $actual['event']); - $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); - $this->assertEquals('str', $actual['id']); } // /** diff --git a/tests/bookmark/BookmarkArrayTest.php b/tests/bookmark/BookmarkArrayTest.php index ebed9bfc..1953078c 100644 --- a/tests/bookmark/BookmarkArrayTest.php +++ b/tests/bookmark/BookmarkArrayTest.php @@ -90,19 +90,6 @@ class BookmarkArrayTest extends TestCase $array['nope'] = $bookmark; } - /** - * Test adding a bad entry: invalid ID type - */ - public function testArrayAccessAddBadEntryIdType() - { - $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class); - - $array = new BookmarkArray(); - $bookmark = (new Bookmark())->setId('nope'); - $bookmark->validate(); - $array[] = $bookmark; - } - /** * Test adding a bad entry: ID/offset not consistent */ diff --git a/tests/bookmark/BookmarkFileServiceTest.php b/tests/bookmark/BookmarkFileServiceTest.php index 6c56dfaa..59c0608c 100644 --- a/tests/bookmark/BookmarkFileServiceTest.php +++ b/tests/bookmark/BookmarkFileServiceTest.php @@ -264,17 +264,6 @@ class BookmarkFileServiceTest extends TestCase $this->publicLinkDB->add(new Bookmark()); } - /** - * Test add() method with an entry which is not a bookmark instance - */ - public function testAddNotABookmark() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Provided data is invalid'); - - $this->privateLinkDB->add(['title' => 'hi!']); - } - /** * Test add() method with a Bookmark already containing an ID */ @@ -412,17 +401,6 @@ class BookmarkFileServiceTest extends TestCase $this->publicLinkDB->set(new Bookmark()); } - /** - * Test set() method with an entry which is not a bookmark instance - */ - public function testSetNotABookmark() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Provided data is invalid'); - - $this->privateLinkDB->set(['title' => 'hi!']); - } - /** * Test set() method with a Bookmark without an ID defined. */ @@ -496,17 +474,6 @@ class BookmarkFileServiceTest extends TestCase $this->publicLinkDB->addOrSet(new Bookmark()); } - /** - * Test addOrSet() method with an entry which is not a bookmark instance - */ - public function testAddOrSetNotABookmark() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Provided data is invalid'); - - $this->privateLinkDB->addOrSet(['title' => 'hi!']); - } - /** * Test addOrSet() method for a bookmark without any field set and without writing the data store */ @@ -564,17 +531,6 @@ class BookmarkFileServiceTest extends TestCase $this->publicLinkDB->remove($bookmark); } - /** - * Test remove() method with an entry which is not a bookmark instance - */ - public function testRemoveNotABookmark() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Provided data is invalid'); - - $this->privateLinkDB->remove(['title' => 'hi!']); - } - /** * Test remove() method with a Bookmark with an unknown ID */ diff --git a/tests/bookmark/BookmarkTest.php b/tests/bookmark/BookmarkTest.php index afec2440..4c7ae4c0 100644 --- a/tests/bookmark/BookmarkTest.php +++ b/tests/bookmark/BookmarkTest.php @@ -153,25 +153,6 @@ class BookmarkTest extends TestCase $this->assertContainsPolyfill('- ID: '. PHP_EOL, $exception->getMessage()); } - /** - * Test validate() with a a bookmark with a non integer ID. - */ - public function testValidateNotValidStringId() - { - $bookmark = new Bookmark(); - $bookmark->setId('str'); - $bookmark->setShortUrl('abc'); - $bookmark->setCreated(\DateTime::createFromFormat('Ymd_His', '20190514_200102')); - $exception = null; - try { - $bookmark->validate(); - } catch (InvalidBookmarkException $e) { - $exception = $e; - } - $this->assertNotNull($exception); - $this->assertContainsPolyfill('- ID: str'. PHP_EOL, $exception->getMessage()); - } - /** * Test validate() with a a bookmark without short url. */ @@ -210,25 +191,6 @@ class BookmarkTest extends TestCase $this->assertContainsPolyfill('- Created: '. PHP_EOL, $exception->getMessage()); } - /** - * Test validate() with a a bookmark with a bad created datetime. - */ - public function testValidateNotValidBadCreated() - { - $bookmark = new Bookmark(); - $bookmark->setId(1); - $bookmark->setShortUrl('abc'); - $bookmark->setCreated('hi!'); - $exception = null; - try { - $bookmark->validate(); - } catch (InvalidBookmarkException $e) { - $exception = $e; - } - $this->assertNotNull($exception); - $this->assertContainsPolyfill('- Created: Not a DateTime object'. PHP_EOL, $exception->getMessage()); - } - /** * Test setId() and make sure that default fields are generated. */ diff --git a/tests/front/controller/admin/ManageShaareControllerTest/DeleteBookmarkTest.php b/tests/front/controller/admin/ManageShaareControllerTest/DeleteBookmarkTest.php index ba774e21..83bbee7c 100644 --- a/tests/front/controller/admin/ManageShaareControllerTest/DeleteBookmarkTest.php +++ b/tests/front/controller/admin/ManageShaareControllerTest/DeleteBookmarkTest.php @@ -356,6 +356,10 @@ class DeleteBookmarkTest extends TestCase ; $response = new Response(); + $this->container->bookmarkService->method('get')->with('123')->willReturn( + (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123') + ); + $this->container->formatterFactory = $this->createMock(FormatterFactory::class); $this->container->formatterFactory ->expects(static::once()) diff --git a/tests/front/controller/admin/ManageShaareControllerTest/SaveBookmarkTest.php b/tests/front/controller/admin/ManageShaareControllerTest/SaveBookmarkTest.php index f7a68226..37542c26 100644 --- a/tests/front/controller/admin/ManageShaareControllerTest/SaveBookmarkTest.php +++ b/tests/front/controller/admin/ManageShaareControllerTest/SaveBookmarkTest.php @@ -66,23 +66,27 @@ class SaveBookmarkTest extends TestCase $this->container->bookmarkService ->expects(static::once()) ->method('addOrSet') - ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): void { + ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark { static::assertFalse($save); $checkBookmark($bookmark); $bookmark->setId($id); + + return $bookmark; }) ; $this->container->bookmarkService ->expects(static::once()) ->method('set') - ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): void { + ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark { static::assertTrue($save); $checkBookmark($bookmark); static::assertSame($id, $bookmark->getId()); + + return $bookmark; }) ; @@ -155,21 +159,25 @@ class SaveBookmarkTest extends TestCase $this->container->bookmarkService ->expects(static::once()) ->method('addOrSet') - ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): void { + ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark { static::assertFalse($save); $checkBookmark($bookmark); + + return $bookmark; }) ; $this->container->bookmarkService ->expects(static::once()) ->method('set') - ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): void { + ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark { static::assertTrue($save); $checkBookmark($bookmark); static::assertSame($id, $bookmark->getId()); + + return $bookmark; }) ; @@ -230,8 +238,10 @@ class SaveBookmarkTest extends TestCase $this->container->bookmarkService ->expects(static::once()) ->method('addOrSet') - ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($thumb): void { + ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($thumb): Bookmark { static::assertSame($thumb, $bookmark->getThumbnail()); + + return $bookmark; }) ; diff --git a/tests/front/controller/admin/ThumbnailsControllerTest.php b/tests/front/controller/admin/ThumbnailsControllerTest.php index f4a8acff..e5749654 100644 --- a/tests/front/controller/admin/ThumbnailsControllerTest.php +++ b/tests/front/controller/admin/ThumbnailsControllerTest.php @@ -89,8 +89,10 @@ class ThumbnailsControllerTest extends TestCase $this->container->bookmarkService ->expects(static::once()) ->method('set') - ->willReturnCallback(function (Bookmark $bookmark) use ($thumb) { + ->willReturnCallback(function (Bookmark $bookmark) use ($thumb): Bookmark { static::assertSame($thumb, $bookmark->getThumbnail()); + + return $bookmark; }) ; -- 2.41.0