From 03505518d66c3a54d52bec2474ae1f6a80dffd34 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 5 Feb 2021 17:19:11 -0500 Subject: [PATCH 01/37] Update docs and CI --- CHANGELOG.md | 3 +++ Jenkinsfile | 22 +++++++++++----------- README.md | 4 ++-- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f20fc455..21bb4913 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## Version 5.2 +* Updated PHP requirement to 8 + ## Version 5.1 * Added session check, so when coming back to a page, if the session is expired, the page will refresh. * Updated logging config so that much fewer, much smaller files are generated. diff --git a/Jenkinsfile b/Jenkinsfile index 48232ea7..466e9600 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -34,16 +34,16 @@ pipeline { sh 'php ./vendor/bin/phpunit --colors=never' } } - stage('Coverage') { - agent any - steps { - sh 'php composer.phar run-script coverage' - step([ - $class: 'CloverPublisher', - cloverReportDir: '', - cloverReportFileName: 'build/logs/clover.xml', - ]) - } - } + } + post { + success { + sh 'php composer.phar run-script coverage' + step([ + $class: 'CloverPublisher', + cloverReportDir: '', + cloverReportFileName: 'build/logs/clover.xml', + ]) + junit 'build/logs/junit.xml' + } } } \ No newline at end of file diff --git a/README.md b/README.md index 4bf489cf..062c4a97 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Update your anime/manga list on Kitsu.io and Anilist [![Build Status](https://travis-ci.com/timw4mail/HummingBirdAnimeClient.svg?branch=master)](https://travis-ci.com/github/timw4mail/HummingBirdAnimeClient) -[![Build Status](https://jenkins.timshomepage.net/buildStatus/icon?job=timw4mail/HummingBirdAnimeClient/develop)](https://jenkins.timshomepage.net/job/timw4mail/HummingBirdAnimeClient/develop) +[![Build Status](https://jenkins.timshome.page/buildStatus/icon?job=timw4mail/HummingBirdAnimeClient/develop)](https://jenkins.timshome.page/job/timw4mail/job/HummingBirdAnimeClient/job/develop/) [[Hosted Example](https://list.timshomepage.net)] @@ -31,7 +31,7 @@ Update your anime/manga list on Kitsu.io and Anilist ### Requirements -* PHP 7.4+ +* PHP 8 * PDO SQLite or PDO PostgreSQL (For collection tab) * GD extension for caching images From 3841bba92380c231d4ddf940c2ab494768ce9ab1 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 5 Feb 2021 17:49:19 -0500 Subject: [PATCH 02/37] Fix the CI build? --- Jenkinsfile | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 466e9600..7351ec62 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,13 +37,15 @@ pipeline { } post { success { - sh 'php composer.phar run-script coverage' - step([ - $class: 'CloverPublisher', - cloverReportDir: '', - cloverReportFileName: 'build/logs/clover.xml', - ]) - junit 'build/logs/junit.xml' + agent any { + sh 'php composer.phar run-script coverage' + step([ + $class: 'CloverPublisher', + cloverReportDir: '', + cloverReportFileName: 'build/logs/clover.xml', + ]) + junit 'build/logs/junit.xml' + } } } } \ No newline at end of file From f88b8edeed15f50f4472da37c9d447f511e9c3e4 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 5 Feb 2021 20:05:33 -0500 Subject: [PATCH 03/37] Fix the CI build? --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7351ec62..26a1b7fa 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,7 +37,7 @@ pipeline { } post { success { - agent any { + node('Coverage') { sh 'php composer.phar run-script coverage' step([ $class: 'CloverPublisher', From 86c7812f470ca5ea27e56c2e5925fb97c5a3decb Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 5 Feb 2021 20:16:55 -0500 Subject: [PATCH 04/37] Fix the CI build? --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 26a1b7fa..9bc755fa 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,7 +37,8 @@ pipeline { } post { success { - node('Coverage') { + agent any + node { sh 'php composer.phar run-script coverage' step([ $class: 'CloverPublisher', From eb2f4d95b63b6f9136d28bb2d56b5ff29ee2f7b4 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 5 Feb 2021 20:21:05 -0500 Subject: [PATCH 05/37] Fix the CI build? --- Jenkinsfile | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 9bc755fa..40ad8172 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -34,11 +34,9 @@ pipeline { sh 'php ./vendor/bin/phpunit --colors=never' } } - } - post { - success { - agent any - node { + stage('Coverage') { + agent any + steps { sh 'php composer.phar run-script coverage' step([ $class: 'CloverPublisher', @@ -46,7 +44,7 @@ pipeline { cloverReportFileName: 'build/logs/clover.xml', ]) junit 'build/logs/junit.xml' - } - } + } + } } } \ No newline at end of file From ff5503af16393e1ee1f8d00972bc7696f95c9cff Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Sun, 7 Feb 2021 08:54:20 -0500 Subject: [PATCH 06/37] Fix the CI build? --- build/phpunit.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/phpunit.xml b/build/phpunit.xml index 02c0a25e..a27b5804 100644 --- a/build/phpunit.xml +++ b/build/phpunit.xml @@ -17,7 +17,9 @@ ../tests/Ion - + + + From cb7a4c28e5e9d3abb3642da161b5650317caba6d Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Mon, 8 Feb 2021 17:03:04 -0500 Subject: [PATCH 07/37] Handle null values better in title uniqueness check --- src/AnimeClient/Kitsu.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AnimeClient/Kitsu.php b/src/AnimeClient/Kitsu.php index f4162dc2..426cf1ff 100644 --- a/src/AnimeClient/Kitsu.php +++ b/src/AnimeClient/Kitsu.php @@ -412,11 +412,11 @@ final class Kitsu { /** * Determine if an alternate title is unique enough to list * - * @param string $title + * @param string|null $title * @param array $existingTitles * @return bool */ - private static function titleIsUnique(string $title = '', array $existingTitles = []): bool + private static function titleIsUnique(?string $title = '', array $existingTitles = []): bool { if (empty($title)) { From b7a2eafc0d5b314b45832e743c6b6e2024231741 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Wed, 10 Feb 2021 10:59:15 -0500 Subject: [PATCH 08/37] Add tests for title uniqueness check --- src/AnimeClient/Kitsu.php | 2 +- tests/AnimeClient/KitsuTest.php | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/AnimeClient/Kitsu.php b/src/AnimeClient/Kitsu.php index 426cf1ff..03e53991 100644 --- a/src/AnimeClient/Kitsu.php +++ b/src/AnimeClient/Kitsu.php @@ -416,7 +416,7 @@ final class Kitsu { * @param array $existingTitles * @return bool */ - private static function titleIsUnique(?string $title = '', array $existingTitles = []): bool + protected static function titleIsUnique(?string $title = '', array $existingTitles = []): bool { if (empty($title)) { diff --git a/tests/AnimeClient/KitsuTest.php b/tests/AnimeClient/KitsuTest.php index 143defa6..7a099a6f 100644 --- a/tests/AnimeClient/KitsuTest.php +++ b/tests/AnimeClient/KitsuTest.php @@ -122,4 +122,36 @@ class KitsuTest extends TestCase { $this->assertEquals($expected, $actual); } + + public function testFilterLocalizedTitles() + { + $input = [ + 'canonical' => 'foo', + 'localized' => [ + 'en' => 'Foo the Movie', + 'fr' => '', + 'jp' => NULL, + ], + 'alternatives' => [], + ]; + + $actual = Kitsu::filterLocalizedTitles($input); + + $this->assertEquals(['Foo the Movie'], $actual); + } + + public function testGetFilteredTitles() + { + $input = [ + 'canonical' => 'foo', + 'localized' => [ + 'en' => 'Foo the Movie' + ], + 'alternatives' => [], + ]; + + $actual = Kitsu::getFilteredTitles($input); + + $this->assertEquals(['Foo the Movie'], $actual); + } } \ No newline at end of file From ebd7f811ee4432579babb07ca24585a3520f32eb Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Wed, 10 Feb 2021 13:59:37 -0500 Subject: [PATCH 09/37] Code style fixes --- src/AnimeClient/API/APIRequestBuilder.php | 68 ------------------- .../API/Anilist/RequestBuilder.php | 7 +- src/AnimeClient/API/Kitsu/RequestBuilder.php | 68 +++++++++++++++++++ src/AnimeClient/AnimeClient.php | 5 ++ 4 files changed, 77 insertions(+), 71 deletions(-) diff --git a/src/AnimeClient/API/APIRequestBuilder.php b/src/AnimeClient/API/APIRequestBuilder.php index 99d6274b..ae714548 100644 --- a/src/AnimeClient/API/APIRequestBuilder.php +++ b/src/AnimeClient/API/APIRequestBuilder.php @@ -293,74 +293,6 @@ abstract class APIRequestBuilder { return $this; } - /** - * Create a GraphQL query and return the Request object - * - * @param string $name - * @param array $variables - * @return Request - */ - public function queryRequest(string $name, array $variables = []): Request - { - $file = realpath("{$this->filePath}/Queries/{$name}.graphql"); - if ( ! file_exists($file)) - { - throw new LogicException('GraphQL query file does not exist.'); - } - - $query = file_get_contents($file); - $body = [ - 'query' => $query - ]; - - if ( ! empty($variables)) - { - $body['variables'] = []; - foreach($variables as $key => $val) - { - $body['variables'][$key] = $val; - } - } - - return $this->setUpRequest('POST', $this->baseUrl, [ - 'body' => $body, - ]); - } - - /** - * Create a GraphQL mutation request, and return the Request object - * - * @param string $name - * @param array $variables - * @return Request - * @throws Throwable - */ - public function mutateRequest (string $name, array $variables = []): Request - { - $file = "{$this->filePath}/Mutations/{$name}.graphql"; - if ( ! file_exists($file)) - { - throw new LogicException('GraphQL mutation file does not exist.'); - } - - $query = file_get_contents($file); - $body = [ - 'query' => $query - ]; - - if (!empty($variables)) { - $body['variables'] = []; - foreach ($variables as $key => $val) - { - $body['variables'][$key] = $val; - } - } - - return $this->setUpRequest('POST', $this->baseUrl, [ - 'body' => $body, - ]); - } - /** * Create the full request url * diff --git a/src/AnimeClient/API/Anilist/RequestBuilder.php b/src/AnimeClient/API/Anilist/RequestBuilder.php index a6958a12..a7f4c1e8 100644 --- a/src/AnimeClient/API/Anilist/RequestBuilder.php +++ b/src/AnimeClient/API/Anilist/RequestBuilder.php @@ -31,6 +31,7 @@ use const Aviat\AnimeClient\USER_AGENT; use Aviat\AnimeClient\API\APIRequestBuilder; use LogicException; +use Throwable; final class RequestBuilder extends APIRequestBuilder { use ContainerAware; @@ -117,7 +118,7 @@ final class RequestBuilder extends APIRequestBuilder { */ public function runQuery(string $name, array $variables = []): array { - $file = realpath(__DIR__ . "/Queries/{$name}.graphql"); + $file = __DIR__ . "/Queries/{$name}.graphql"; if ( ! file_exists($file)) { throw new LogicException('GraphQL query file does not exist.'); @@ -150,8 +151,8 @@ final class RequestBuilder extends APIRequestBuilder { */ public function mutateRequest (string $name, array $variables = []): Request { - $file = realpath(__DIR__ . "/Mutations/{$name}.graphql"); - if (!file_exists($file)) + $file = __DIR__ . "/Mutations/{$name}.graphql"; + if ( ! file_exists($file)) { throw new LogicException('GraphQL mutation file does not exist.'); } diff --git a/src/AnimeClient/API/Kitsu/RequestBuilder.php b/src/AnimeClient/API/Kitsu/RequestBuilder.php index a69b6853..74c02f8f 100644 --- a/src/AnimeClient/API/Kitsu/RequestBuilder.php +++ b/src/AnimeClient/API/Kitsu/RequestBuilder.php @@ -200,6 +200,74 @@ final class RequestBuilder extends APIRequestBuilder { return $response; } + /** + * Create a GraphQL query and return the Request object + * + * @param string $name + * @param array $variables + * @return Request + */ + public function queryRequest(string $name, array $variables = []): Request + { + $file = realpath("{$this->filePath}/Queries/{$name}.graphql"); + if ( ! file_exists($file)) + { + throw new LogicException('GraphQL query file does not exist.'); + } + + $query = file_get_contents($file); + $body = [ + 'query' => $query + ]; + + if ( ! empty($variables)) + { + $body['variables'] = []; + foreach($variables as $key => $val) + { + $body['variables'][$key] = $val; + } + } + + return $this->setUpRequest('POST', $this->baseUrl, [ + 'body' => $body, + ]); + } + + /** + * Create a GraphQL mutation request, and return the Request object + * + * @param string $name + * @param array $variables + * @return Request + * @throws Throwable + */ + public function mutateRequest (string $name, array $variables = []): Request + { + $file = "{$this->filePath}/Mutations/{$name}.graphql"; + if ( ! file_exists($file)) + { + throw new LogicException('GraphQL mutation file does not exist.'); + } + + $query = file_get_contents($file); + $body = [ + 'query' => $query + ]; + + if (!empty($variables)) { + $body['variables'] = []; + foreach ($variables as $key => $val) + { + $body['variables'][$key] = $val; + } + } + + return $this->setUpRequest('POST', $this->baseUrl, [ + 'body' => $body, + ]); + } + /** * Make a request * diff --git a/src/AnimeClient/AnimeClient.php b/src/AnimeClient/AnimeClient.php index 1b3d42b7..df1ce722 100644 --- a/src/AnimeClient/AnimeClient.php +++ b/src/AnimeClient/AnimeClient.php @@ -48,6 +48,11 @@ function loadConfig(string $path): array $output = []; $files = glob("{$path}/*.toml"); + if ( ! is_array($files)) + { + return []; + } + foreach ($files as $file) { $key = str_replace('.toml', '', basename($file)); From 9224751d2dcb751d751a313b441ed0b3bef38c1d Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Wed, 10 Feb 2021 17:17:51 -0500 Subject: [PATCH 10/37] Add a bumch of soundness checks suggested by PHPStan --- .../Transformer/AnimeListTransformer.php | 1 - src/AnimeClient/Kitsu.php | 8 +- src/AnimeClient/MenuGenerator.php | 2 +- src/AnimeClient/Model/AnimeCollection.php | 88 +++++++++++++++---- src/AnimeClient/Model/Collection.php | 7 +- src/AnimeClient/Model/Settings.php | 4 +- src/AnimeClient/Types/AbstractType.php | 57 ++++++------ src/AnimeClient/Types/Anime.php | 4 +- src/AnimeClient/Types/AnimeListItem.php | 9 +- src/AnimeClient/Types/Character.php | 4 +- src/AnimeClient/Types/Config.php | 21 +++-- src/AnimeClient/Types/FormItem.php | 10 +-- src/AnimeClient/Types/MangaPage.php | 20 ++--- src/AnimeClient/Types/Person.php | 2 +- src/AnimeClient/UrlGenerator.php | 2 +- src/AnimeClient/Util.php | 6 +- src/Ion/Di/Container.php | 29 +++--- src/Ion/Json.php | 21 +++-- src/Ion/Type/StringType.php | 2 +- src/Ion/View/HtmlView.php | 3 +- src/Ion/View/HttpView.php | 8 +- src/Ion/View/JsonView.php | 5 +- ...st__testUntransform with data set 0__1.yml | 1 - ...st__testUntransform with data set 1__1.yml | 1 - ...st__testUntransform with data set 2__1.yml | 1 - 25 files changed, 190 insertions(+), 126 deletions(-) diff --git a/src/AnimeClient/API/Kitsu/Transformer/AnimeListTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/AnimeListTransformer.php index bc2992d3..0e493bf3 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/AnimeListTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/AnimeListTransformer.php @@ -122,7 +122,6 @@ final class AnimeListTransformer extends AbstractTransformer { $untransformed = FormItem::from([ 'id' => $item['id'], - 'anilist_item_id' => $item['anilist_item_id'] ?? NULL, 'mal_id' => $item['mal_id'] ?? NULL, 'data' => [ 'status' => $item['watching_status'], diff --git a/src/AnimeClient/Kitsu.php b/src/AnimeClient/Kitsu.php index 03e53991..a1d0d18a 100644 --- a/src/AnimeClient/Kitsu.php +++ b/src/AnimeClient/Kitsu.php @@ -166,7 +166,7 @@ final class Kitsu { */ public static function parseStreamingLinks(array $nodes): array { - if (count($nodes) === 0) + if (empty($nodes)) { return []; } @@ -179,12 +179,16 @@ final class Kitsu { // 'Fix' links that start with the hostname, // rather than a protocol - if (strpos($url, '//') === FALSE) + if ( ! str_contains($url, '//')) { $url = '//' . $url; } $host = parse_url($url, \PHP_URL_HOST); + if ($host === FALSE) + { + return []; + } $links[] = [ 'meta' => self::getServiceMetaData($host), diff --git a/src/AnimeClient/MenuGenerator.php b/src/AnimeClient/MenuGenerator.php index 3951dacc..d8dfd23d 100644 --- a/src/AnimeClient/MenuGenerator.php +++ b/src/AnimeClient/MenuGenerator.php @@ -45,7 +45,7 @@ final class MenuGenerator extends UrlGenerator { /** * @param ContainerInterface $container - * @return static + * @return self */ public static function new(ContainerInterface $container): self { diff --git a/src/AnimeClient/Model/AnimeCollection.php b/src/AnimeClient/Model/AnimeCollection.php index 74301911..e489dc5a 100644 --- a/src/AnimeClient/Model/AnimeCollection.php +++ b/src/AnimeClient/Model/AnimeCollection.php @@ -92,6 +92,11 @@ final class AnimeCollection extends Collection { $genres = $this->getGenreList(); $media = $this->getMediaList(); + if ($rows === FALSE) + { + return []; + } + foreach($rows as &$row) { $id = $row['hummingbird_id']; @@ -128,7 +133,13 @@ final class AnimeCollection extends Collection { ->from('media') ->get(); - foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) + $rows = $query->fetchAll(PDO::FETCH_ASSOC); + if ($rows === FALSE) + { + return []; + } + + foreach ($rows as $row) { $flatList[$row['id']] = $row['type']; } @@ -158,10 +169,10 @@ final class AnimeCollection extends Collection { /** * Add an item to the anime collection * - * @param array $data + * @param mixed $data * @return void */ - public function add($data): void + public function add(mixed $data): void { if ($this->validDatabase === FALSE) { @@ -197,10 +208,10 @@ final class AnimeCollection extends Collection { /** * Verify that an item was added * - * @param array|null|object $data + * @param array $data * @return bool */ - public function wasAdded($data): bool + public function wasAdded(array $data): bool { if ($this->validDatabase === FALSE) { @@ -218,7 +229,7 @@ final class AnimeCollection extends Collection { * @param array $data * @return void */ - public function update($data): void + public function update(array $data): void { if ($this->validDatabase === FALSE) { @@ -253,11 +264,11 @@ final class AnimeCollection extends Collection { /** * Verify that the collection item was updated * - * @param array|null|object $data + * @param array $data * * @return bool */ - public function wasUpdated($data): bool + public function wasUpdated(array $data): bool { if ($this->validDatabase === FALSE) { @@ -288,7 +299,7 @@ final class AnimeCollection extends Collection { * @param array $data * @return void */ - public function delete($data): void + public function delete(array $data): void { if ($this->validDatabase === FALSE) { @@ -316,10 +327,10 @@ final class AnimeCollection extends Collection { } /** - * @param array|null|object $data + * @param array $data * @return bool */ - public function wasDeleted($data): bool + public function wasDeleted(array $data): bool { if ($this->validDatabase === FALSE) { @@ -335,7 +346,7 @@ final class AnimeCollection extends Collection { * @param int|string $kitsuId * @return array */ - public function get($kitsuId): array + public function get(int|string $kitsuId): array { if ($this->validDatabase === FALSE) { @@ -348,6 +359,11 @@ final class AnimeCollection extends Collection { ->get() ->fetch(PDO::FETCH_ASSOC); + if ($row === FALSE) + { + return []; + } + // Get the media ids $mediaRows = $this->db->select('media_id') ->from('anime_set_media_link') @@ -355,6 +371,11 @@ final class AnimeCollection extends Collection { ->get() ->fetchAll(PDO::FETCH_ASSOC); + if ($mediaRows === FALSE) + { + return []; + } + $row['media_id'] = array_column($mediaRows, 'media_id'); return $row; @@ -366,7 +387,7 @@ final class AnimeCollection extends Collection { * @param int|string $kitsuId * @return bool */ - public function has($kitsuId): bool + public function has(int|string $kitsuId): bool { if ($this->validDatabase === FALSE) { @@ -416,7 +437,13 @@ final class AnimeCollection extends Collection { ->orderBy('genre') ->get(); - foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) + $rows = $query->fetchAll(PDO::FETCH_ASSOC); + if ($rows === FALSE) + { + return []; + } + + foreach ($rows as $row) { $id = $row['hummingbird_id']; $genre = $row['genre']; @@ -437,7 +464,7 @@ final class AnimeCollection extends Collection { } } } - catch (PDOException $e) {} + catch (PDOException) {} $this->db->resetQuery(); @@ -478,7 +505,13 @@ final class AnimeCollection extends Collection { ->orderBy('media') ->get(); - foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) + $rows = $query->fetchAll(PDO::FETCH_ASSOC); + if ($rows === FALSE) + { + return []; + } + + foreach ($rows as $row) { $id = $row['hummingbird_id']; $media = $row['media']; @@ -609,7 +642,7 @@ final class AnimeCollection extends Collection { { $this->db->insertBatch('genres', $insert); } - catch (PDOException $e) + catch (PDOException) { // dump($e); } @@ -642,7 +675,13 @@ final class AnimeCollection extends Collection { ->from('genres') ->get(); - foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $genre) + $rows = $query->fetchAll(PDO::FETCH_ASSOC); + if ($rows === FALSE) + { + return []; + } + + foreach ($rows as $genre) { $genres[$genre['id']] = $genre['genre']; } @@ -665,7 +704,13 @@ final class AnimeCollection extends Collection { ->from('anime_set_genre_link') ->get(); - foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $link) + $rows = $query->fetchAll(PDO::FETCH_ASSOC); + if ($rows === FALSE) + { + return []; + } + + foreach ($rows as $link) { if (array_key_exists($link['hummingbird_id'], $links)) { @@ -706,6 +751,11 @@ final class AnimeCollection extends Collection { // Add genres associated with each item $rows = $query->fetchAll(PDO::FETCH_ASSOC); + if ($rows === FALSE) + { + return []; + } + $genres = $this->getGenreList(); foreach($rows as &$row) diff --git a/src/AnimeClient/Model/Collection.php b/src/AnimeClient/Model/Collection.php index 9d0bcb59..ae63d8f2 100644 --- a/src/AnimeClient/Model/Collection.php +++ b/src/AnimeClient/Model/Collection.php @@ -67,10 +67,11 @@ class Collection extends DB { { $dbFileName = $this->dbConfig['file']; - if ($dbFileName !== ':memory:' && file_exists($dbFileName)) + if ($dbFileName !== ':memory:') { - $dbFile = file_get_contents($dbFileName); - $this->validDatabase = (strpos($dbFile, 'SQLite format 3') === 0); + $rawFile = file_get_contents($dbFileName); + $dbFile = ($rawFile !== FALSE) ? $rawFile : ''; + $this->validDatabase = str_starts_with($dbFile, 'SQLite format 3'); } else { diff --git a/src/AnimeClient/Model/Settings.php b/src/AnimeClient/Model/Settings.php index 7d413cf5..fc7e600b 100644 --- a/src/AnimeClient/Model/Settings.php +++ b/src/AnimeClient/Model/Settings.php @@ -69,9 +69,7 @@ final class Settings { { $output = []; - $settings = $this->getSettings(); - - foreach($settings as $file => $values) + foreach($this->getSettings() as $file => $values) { $values = $values ?? []; diff --git a/src/AnimeClient/Types/AbstractType.php b/src/AnimeClient/Types/AbstractType.php index 7a3b6407..13f6bd73 100644 --- a/src/AnimeClient/Types/AbstractType.php +++ b/src/AnimeClient/Types/AbstractType.php @@ -23,10 +23,10 @@ abstract class AbstractType implements ArrayAccess, Countable { /** * Populate values for un-serializing data * - * @param $properties + * @param mixed $properties * @return self */ - public static function __set_state($properties): self + public static function __set_state(mixed $properties): self { return new static($properties); } @@ -43,7 +43,8 @@ abstract class AbstractType implements ArrayAccess, Countable { if (get_parent_class($currentClass) !== FALSE) { - return (new $currentClass($data))->toArray(); + $output = static::class::from($data)->toArray(); + return (is_array($output)) ? $output : []; } return NULL; @@ -55,7 +56,7 @@ abstract class AbstractType implements ArrayAccess, Countable { * @param mixed $data * @return static */ - final public static function from($data): self + final public static function from(mixed $data): static { return new static($data); } @@ -65,7 +66,7 @@ abstract class AbstractType implements ArrayAccess, Countable { * * @param mixed $data */ - final private function __construct($data = []) + final private function __construct(mixed $data = []) { $typeKeys = array_keys((array)$this); $dataKeys = array_keys((array)$data); @@ -87,10 +88,10 @@ abstract class AbstractType implements ArrayAccess, Countable { /** * See if a property is set * - * @param $name + * @param string $name * @return bool */ - final public function __isset($name): bool + final public function __isset(string $name): bool { return property_exists($this, $name) && isset($this->$name); } @@ -102,7 +103,7 @@ abstract class AbstractType implements ArrayAccess, Countable { * @param mixed $value * @return void */ - final public function __set($name, $value): void + final public function __set(string $name, mixed $value): void { $setterMethod = 'set' . ucfirst($name); @@ -128,7 +129,7 @@ abstract class AbstractType implements ArrayAccess, Countable { * @param string $name * @return mixed */ - final public function __get($name) + final public function __get(string $name): mixed { // Be a bit more lenient here, so that you can easily typecast missing // values to reasonable defaults, and not have to resort to array indexes @@ -148,46 +149,47 @@ abstract class AbstractType implements ArrayAccess, Countable { /** * Implementing ArrayAccess * - * @param $offset + * @param mixed $offset * @return bool */ - final public function offsetExists($offset): bool + final public function offsetExists(mixed $offset): bool { - return $this->__isset($offset); + return $this->__isset((string)$offset); } /** * Implementing ArrayAccess * - * @param $offset + * @param mixed $offset * @return mixed */ - final public function offsetGet($offset) + final public function offsetGet(mixed $offset): mixed { - return $this->__get($offset); + return $this->__get((string)$offset); } /** * Implementing ArrayAccess * - * @param $offset - * @param $value + * @param mixed $offset + * @param mixed $value */ - final public function offsetSet($offset, $value): void + final public function offsetSet(mixed $offset, mixed $value): void { - $this->__set($offset, $value); + $this->__set((string)$offset, $value); } /** * Implementing ArrayAccess * - * @param $offset + * @param mixed $offset */ - final public function offsetUnset($offset): void + final public function offsetUnset(mixed $offset): void { if ($this->offsetExists($offset)) { - unset($this->$offset); + $strOffset = (string)$offset; + unset($this->$strOffset); } } @@ -198,17 +200,19 @@ abstract class AbstractType implements ArrayAccess, Countable { */ final public function count(): int { - $keys = array_keys($this->toArray()); + $keys = array_keys((array)$this->toArray()); return count($keys); } /** * Recursively cast properties to an array * + * Returns early on primitive values to work recursively. + * * @param mixed $parent - * @return mixed + * @return float|bool|null|int|array|string */ - final public function toArray($parent = null) + final public function toArray(mixed $parent = null): float|null|bool|int|array|string { $object = $parent ?? $this; @@ -236,7 +240,8 @@ abstract class AbstractType implements ArrayAccess, Countable { */ final public function isEmpty(): bool { - foreach ($this as $value) + $self = (array)$this->toArray(); + foreach ($self as $value) { if ( ! empty($value)) { diff --git a/src/AnimeClient/Types/Anime.php b/src/AnimeClient/Types/Anime.php index 357345a7..115be3c5 100644 --- a/src/AnimeClient/Types/Anime.php +++ b/src/AnimeClient/Types/Anime.php @@ -35,9 +35,9 @@ class Anime extends AbstractType { public array $genres = []; /** - * @var string|int + * @var string */ - public $id = ''; + public string $id = ''; public array $included = []; diff --git a/src/AnimeClient/Types/AnimeListItem.php b/src/AnimeClient/Types/AnimeListItem.php index 2a2c484f..56d41c2d 100644 --- a/src/AnimeClient/Types/AnimeListItem.php +++ b/src/AnimeClient/Types/AnimeListItem.php @@ -24,11 +24,6 @@ final class AnimeListItem extends AbstractType { public ?string $mal_id; - /** - * @var string - */ - public $anilist_item_id; - public array $episodes = [ 'length' => 0, 'total' => 0, @@ -54,14 +49,14 @@ final class AnimeListItem extends AbstractType { /** * @var string|int */ - public $user_rating = ''; + public string|int $user_rating = ''; /** * One of Aviat\AnimeClient\API\Enum\AnimeWatchingStatus */ public string $watching_status; - public function setAnime($anime): void + public function setAnime(mixed $anime): void { $this->anime = Anime::from($anime); } diff --git a/src/AnimeClient/Types/Character.php b/src/AnimeClient/Types/Character.php index d9500914..e68c4259 100644 --- a/src/AnimeClient/Types/Character.php +++ b/src/AnimeClient/Types/Character.php @@ -27,7 +27,7 @@ final class Character extends AbstractType { /** * @var string */ - public $id; + public string $id; public array $included = []; @@ -39,7 +39,7 @@ final class Character extends AbstractType { public array $otherNames = []; - public function setMedia ($media): void + public function setMedia (mixed $media): void { $this->media = Media::from($media); } diff --git a/src/AnimeClient/Types/Config.php b/src/AnimeClient/Types/Config.php index d3f2d7d7..9de10ffd 100644 --- a/src/AnimeClient/Types/Config.php +++ b/src/AnimeClient/Types/Config.php @@ -47,8 +47,9 @@ class Config extends AbstractType { /** * The list to redirect to from the root url + * 'anime' or 'manga' * - * @var 'anime' | 'manga' + * @var string|null */ public ?string $default_list; @@ -59,7 +60,10 @@ class Config extends AbstractType { public ?string $default_manga_list_path; /** - * @var 'cover_view' | 'list_view' + * Default list view type + * 'cover_view' or 'list_view' + * + * @var string */ public ?string $default_view_type; @@ -70,17 +74,18 @@ class Config extends AbstractType { /** * @var string|bool */ - public $show_anime_collection = FALSE; + public string|bool $show_anime_collection = FALSE; /** * @var string|bool */ - public $show_manga_collection = FALSE; + public string|bool $show_manga_collection = FALSE; /** * CSS theme: light, dark, or auto-switching + * 'auto', 'light', or 'dark' * - * @var 'auto' | 'light' | 'dark' + * @var string|null */ public ?string $theme = 'auto'; @@ -116,17 +121,17 @@ class Config extends AbstractType { public ?string $view_path; - public function setAnilist ($data): void + public function setAnilist (mixed $data): void { $this->anilist = Config\Anilist::from($data); } - public function setCache ($data): void + public function setCache (mixed $data): void { $this->cache = Config\Cache::from($data); } - public function setDatabase ($data): void + public function setDatabase (mixed $data): void { $this->database = Config\Database::from($data); } diff --git a/src/AnimeClient/Types/FormItem.php b/src/AnimeClient/Types/FormItem.php index b8d1130a..47a0eb5a 100644 --- a/src/AnimeClient/Types/FormItem.php +++ b/src/AnimeClient/Types/FormItem.php @@ -23,18 +23,16 @@ class FormItem extends AbstractType { /** * @var string|int */ - public $id; - - public ?string $anilist_item_id; + public string|int $id; /** - * @var string|int + * @var string|int|null */ - public $mal_id; + public string|int|null $mal_id; public ?FormItemData $data; - public function setData($value): void + public function setData(mixed $value): void { $this->data = FormItemData::from($value); } diff --git a/src/AnimeClient/Types/MangaPage.php b/src/AnimeClient/Types/MangaPage.php index a889e120..08972090 100644 --- a/src/AnimeClient/Types/MangaPage.php +++ b/src/AnimeClient/Types/MangaPage.php @@ -23,12 +23,12 @@ use Aviat\AnimeClient\API\Kitsu\Enum\MangaPublishingStatus; */ final class MangaPage extends AbstractType { /** - * @var string + * @var string|null */ public ?string $age_rating; /** - * @var string + * @var string|null */ public ?string $age_rating_guide; @@ -38,14 +38,14 @@ final class MangaPage extends AbstractType { public array $characters; /** - * @var int + * @var int|null */ - public $chapter_count; + public ?int $chapter_count; /** - * @var string + * @var string|null */ - public $cover_image; + public ?string $cover_image; /** * @var array @@ -60,15 +60,15 @@ final class MangaPage extends AbstractType { /** * @var string */ - public $id; + public string $id; /** * @var string */ - public $manga_type; + public string $manga_type; /** - * @var MangaPublishingStatus + * @var string */ public string $status = MangaPublishingStatus::FINISHED; @@ -103,7 +103,7 @@ final class MangaPage extends AbstractType { public string $url; /** - * @var int + * @var int|null */ public ?int $volume_count; } diff --git a/src/AnimeClient/Types/Person.php b/src/AnimeClient/Types/Person.php index 675f2b56..d9afd8ee 100644 --- a/src/AnimeClient/Types/Person.php +++ b/src/AnimeClient/Types/Person.php @@ -21,7 +21,7 @@ namespace Aviat\AnimeClient\Types; */ final class Person extends AbstractType { - public $id; + public string $id; public ?string $name; diff --git a/src/AnimeClient/UrlGenerator.php b/src/AnimeClient/UrlGenerator.php index b4c99b56..77731bbf 100644 --- a/src/AnimeClient/UrlGenerator.php +++ b/src/AnimeClient/UrlGenerator.php @@ -49,7 +49,7 @@ class UrlGenerator extends RoutingBase { /** * Get the base url for css/js/images * - * @param array $args + * @param string ...$args * @return string */ public function assetUrl(string ...$args): string diff --git a/src/AnimeClient/Util.php b/src/AnimeClient/Util.php index 940b976e..fcb77751 100644 --- a/src/AnimeClient/Util.php +++ b/src/AnimeClient/Util.php @@ -57,11 +57,11 @@ class Util { /** * Absolutely equal? * - * @param $left - * @param $right + * @param mixed $left + * @param mixed $right * @return bool */ - public static function eq($left, $right): bool + public static function eq(mixed $left, mixed $right): bool { return $left === $right; } diff --git a/src/Ion/Di/Container.php b/src/Ion/Di/Container.php index cecafd1f..ac545d21 100644 --- a/src/Ion/Di/Container.php +++ b/src/Ion/Di/Container.php @@ -29,21 +29,21 @@ class Container implements ContainerInterface { * * @var Callable[] */ - protected $container = []; + protected array $container = []; /** * Array of object instances * * @var array */ - protected $instances = []; + protected array $instances = []; /** * Map of logger instances * * @var array */ - protected $loggers = []; + protected array $loggers = []; /** * Constructor @@ -66,7 +66,7 @@ class Container implements ContainerInterface { * * @return mixed Entry. */ - public function get($id) + public function get($id): mixed { if ( ! \is_string($id)) { @@ -99,7 +99,7 @@ class Container implements ContainerInterface { * @throws ContainerException - Error while retrieving the entry. * @return mixed */ - public function getNew($id, array $args = NULL) + public function getNew($id, array $args = NULL): mixed { if ( ! \is_string($id)) { @@ -141,7 +141,7 @@ class Container implements ContainerInterface { * @throws NotFoundException - No entry was found for this identifier. * @return ContainerInterface */ - public function setInstance(string $id, $value): ContainerInterface + public function setInstance(string $id, mixed $value): ContainerInterface { if ( ! $this->has($id)) { @@ -209,15 +209,20 @@ class Container implements ContainerInterface { * @param mixed $obj * @return mixed */ - private function applyContainer($obj) + private function applyContainer(mixed $obj): mixed { - $trait_name = ContainerAware::class; - $interface_name = ContainerAwareInterface::class; + $traitName = ContainerAware::class; + $interfaceName = ContainerAwareInterface::class; - $uses_trait = \in_array($trait_name, class_uses($obj), TRUE); - $implements_interface = \in_array($interface_name, class_implements($obj), TRUE); + $traits = class_uses($obj); + $traitsUsed = (is_array($traits)) ? $traits : []; + $usesTrait = in_array($traitName, $traitsUsed, TRUE); - if ($uses_trait || $implements_interface) + $interfaces = class_implements($obj); + $implemented = (is_array($interfaces)) ? $interfaces : []; + $implementsInterface = in_array($interfaceName, $implemented, TRUE); + + if ($usesTrait || $implementsInterface) { $obj->setContainer($this); } diff --git a/src/Ion/Json.php b/src/Ion/Json.php index 3711c2bb..fbfc5f3f 100644 --- a/src/Ion/Json.php +++ b/src/Ion/Json.php @@ -32,11 +32,12 @@ class Json { * @throws JsonException * @return string */ - public static function encode($data, $options = 0, $depth = 512): string + public static function encode(mixed $data, int $options = 0, int $depth = 512): string { $json = json_encode($data, $options, $depth); self::check_json_error(); - return $json; + + return ($json !== FALSE) ? $json : ''; } /** @@ -47,12 +48,14 @@ class Json { * @param int $jsonOptions - Options to pass to json_encode * @param int $fileOptions - Options to pass to file_get_contents * @throws JsonException - * @return int + * @return bool */ - public static function encodeFile(string $filename, $data, int $jsonOptions = 0, int $fileOptions = 0): int + public static function encodeFile(string $filename, mixed $data, int $jsonOptions = 0, int $fileOptions = 0): bool { $json = self::encode($data, $jsonOptions); - return file_put_contents($filename, $json, $fileOptions); + + $res = file_put_contents($filename, $json, $fileOptions); + return $res !== FALSE; } /** @@ -65,7 +68,7 @@ class Json { * @throws JsonException * @return mixed */ - public static function decode($json, bool $assoc = TRUE, int $depth = 512, int $options = 0) + public static function decode(?string $json, bool $assoc = TRUE, int $depth = 512, int $options = 0): mixed { // Don't try to decode null if ($json === NULL) @@ -89,9 +92,11 @@ class Json { * @throws JsonException * @return mixed */ - public static function decodeFile(string $filename, bool $assoc = TRUE, int $depth = 512, int $options = 0) + public static function decodeFile(string $filename, bool $assoc = TRUE, int $depth = 512, int $options = 0): mixed { - $json = file_get_contents($filename); + $rawJson = file_get_contents($filename); + $json = ($rawJson !== FALSE) ? $rawJson : ''; + return self::decode($json, $assoc, $depth, $options); } diff --git a/src/Ion/Type/StringType.php b/src/Ion/Type/StringType.php index a90da7d6..4a61c380 100644 --- a/src/Ion/Type/StringType.php +++ b/src/Ion/Type/StringType.php @@ -27,7 +27,7 @@ class StringType extends Stringy { * Alias for `create` static constructor * * @param string $str - * @return $this + * @return self */ public static function from(string $str): self { diff --git a/src/Ion/View/HtmlView.php b/src/Ion/View/HtmlView.php index bcc813d0..bfd1eb05 100644 --- a/src/Ion/View/HtmlView.php +++ b/src/Ion/View/HtmlView.php @@ -69,7 +69,8 @@ class HtmlView extends HttpView { ob_start(); extract($data, EXTR_OVERWRITE); include_once $path; - $buffer = ob_get_clean(); + $rawBuffer = ob_get_clean(); + $buffer = ($rawBuffer === FALSE) ? '' : $rawBuffer; // Very basic html minify, that won't affect content between html tags diff --git a/src/Ion/View/HttpView.php b/src/Ion/View/HttpView.php index 21a4b041..dd683a3b 100644 --- a/src/Ion/View/HttpView.php +++ b/src/Ion/View/HttpView.php @@ -103,9 +103,9 @@ class HttpView implements ViewInterface{ * Set the output string * * @param mixed $string - * @return HttpView + * @return ViewInterface */ - public function setOutput($string): self + public function setOutput($string): ViewInterface { $this->response->getBody()->write($string); @@ -117,9 +117,9 @@ class HttpView implements ViewInterface{ * Append additional output. * * @param string $string - * @return HttpView + * @return ViewInterface */ - public function appendOutput(string $string): self + public function appendOutput(string $string): ViewInterface { return $this->setOutput($string); } diff --git a/src/Ion/View/JsonView.php b/src/Ion/View/JsonView.php index 8f27801d..09856d47 100644 --- a/src/Ion/View/JsonView.php +++ b/src/Ion/View/JsonView.php @@ -17,6 +17,7 @@ namespace Aviat\Ion\View; use Aviat\Ion\Json; +use Aviat\Ion\ViewInterface; /** * View class to serialize Json @@ -34,9 +35,9 @@ class JsonView extends HttpView { * Set the output string * * @param mixed $string - * @return JsonView + * @return ViewInterface */ - public function setOutput($string): self + public function setOutput($string): ViewInterface { if ( ! is_string($string)) { diff --git a/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 0__1.yml b/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 0__1.yml index 22fb6164..814b33e9 100644 --- a/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 0__1.yml +++ b/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 0__1.yml @@ -1,6 +1,5 @@ empty: false id: 14047981 -anilist_item_id: null mal_id: null data: empty: false diff --git a/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 1__1.yml b/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 1__1.yml index 6769276d..6f631375 100644 --- a/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 1__1.yml +++ b/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 1__1.yml @@ -1,6 +1,5 @@ empty: false id: 14047981 -anilist_item_id: null mal_id: '12345' data: empty: false diff --git a/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 2__1.yml b/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 2__1.yml index adc5fcbe..98e0c061 100644 --- a/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 2__1.yml +++ b/tests/AnimeClient/API/Kitsu/Transformer/__snapshots__/AnimeListTransformerTest__testUntransform with data set 2__1.yml @@ -1,6 +1,5 @@ empty: false id: 14047983 -anilist_item_id: null mal_id: '12347' data: empty: false From 103f95c07bae410c89bf8ae21bc9fe509ab9eeb3 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Wed, 10 Feb 2021 17:31:20 -0500 Subject: [PATCH 11/37] A few more PHPStan fixes --- phpstan.neon | 10 +++--- src/AnimeClient/Types/AbstractType.php | 47 ++++++++++++++------------ 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 7f460fb1..8390c43c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,16 +3,14 @@ parameters: checkMissingIterableValueType: false inferPrivatePropertyTypeFromConstructor: true level: 7 - autoload_files: - - %rootDir%/../../../tests/mocks.php paths: - src - ./console - index.php ignoreErrors: - - '#Access to an undefined property Aviat\\\Ion\\\Friend::\$[a-zA-Z0-9_]+#' - - '#Call to an undefined method Aviat\\\Ion\\\Friend::[a-zA-Z0-9_]+\(\)#' + - "#Offset 'fields' does not exist on array#" - '#Call to an undefined method Aura\\\Html\\\HelperLocator::[a-zA-Z0-9_]+\(\)#' - - '#Property Amp\\Artax\\Internal\\RequestCycle::\$[a-zA-Z0-9_]+#' + - '#Call to an undefined method Query\\QueryBuilderInterface::[a-zA-Z0-9_]+\(\)#' excludes_analyse: - - tests/mocks.php \ No newline at end of file + - tests/mocks.php + - vendor \ No newline at end of file diff --git a/src/AnimeClient/Types/AbstractType.php b/src/AnimeClient/Types/AbstractType.php index 13f6bd73..32766d2e 100644 --- a/src/AnimeClient/Types/AbstractType.php +++ b/src/AnimeClient/Types/AbstractType.php @@ -43,8 +43,7 @@ abstract class AbstractType implements ArrayAccess, Countable { if (get_parent_class($currentClass) !== FALSE) { - $output = static::class::from($data)->toArray(); - return (is_array($output)) ? $output : []; + return static::class::from($data)->toArray(); } return NULL; @@ -210,27 +209,12 @@ abstract class AbstractType implements ArrayAccess, Countable { * Returns early on primitive values to work recursively. * * @param mixed $parent - * @return float|bool|null|int|array|string + * @return array */ - final public function toArray(mixed $parent = null): float|null|bool|int|array|string + final public function toArray(mixed $parent = null): array { - $object = $parent ?? $this; - - if (is_scalar($object) || $object === NULL) - { - return $object; - } - - $output = []; - - foreach ($object as $key => $value) - { - $output[$key] = (is_scalar($value) || empty($value)) - ? $value - : $this->toArray((array) $value); - } - - return $output; + $fromObject = $this->fromObject($parent); + return (is_array($fromObject)) ? $fromObject : []; } /** @@ -251,4 +235,25 @@ abstract class AbstractType implements ArrayAccess, Countable { return TRUE; } + + final protected function fromObject(mixed $parent = null): float|null|bool|int|array|string + { + $object = $parent ?? $this; + + if (is_scalar($object) || $object === NULL) + { + return $object; + } + + $output = []; + + foreach ($object as $key => $value) + { + $output[$key] = (is_scalar($value) || empty($value)) + ? $value + : $this->fromObject((array) $value); + } + + return $output; + } } From c100105fbc43e4a2631c9c669d51f365b5416baa Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Thu, 11 Feb 2021 19:54:22 -0500 Subject: [PATCH 12/37] Yet more PHPStan fixes --- index.php | 2 +- phpstan.neon | 1 + src/AnimeClient/AnimeClient.php | 4 +- src/AnimeClient/Controller.php | 13 +++---- src/AnimeClient/Controller/Anime.php | 8 ++-- .../Controller/AnimeCollection.php | 10 ++--- src/AnimeClient/Controller/Images.php | 13 +++++-- src/AnimeClient/Controller/Manga.php | 16 +++----- src/AnimeClient/Controller/Misc.php | 8 +++- src/AnimeClient/Controller/Settings.php | 12 ++++-- src/AnimeClient/Dispatcher.php | 37 ++++++++++++------- src/AnimeClient/FormGenerator.php | 2 +- src/AnimeClient/MenuGenerator.php | 6 +-- src/AnimeClient/Model/MediaTrait.php | 2 +- src/AnimeClient/RoutingBase.php | 19 ++++------ src/Ion/HttpViewInterface.php | 32 ++++++++++++++++ src/Ion/View/HtmlView.php | 9 +---- src/Ion/View/HttpView.php | 12 +++--- src/Ion/View/JsonView.php | 6 +-- 19 files changed, 126 insertions(+), 86 deletions(-) create mode 100644 src/Ion/HttpViewInterface.php diff --git a/index.php b/index.php index 2e72e99e..ca9a24a5 100644 --- a/index.php +++ b/index.php @@ -64,7 +64,7 @@ if (array_key_exists('timezone', $checkedConfig) && ! empty($checkedConfig['time { date_default_timezone_set($checkedConfig['timezone']); } -else if ($timezone !== '') +else if (is_string($timezone) && $timezone !== '') { date_default_timezone_set($timezone); } diff --git a/phpstan.neon b/phpstan.neon index 8390c43c..a5bf6603 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -8,6 +8,7 @@ parameters: - ./console - index.php ignoreErrors: + - '#Access to an undefined property Aviat\\Ion\\Friend#' - "#Offset 'fields' does not exist on array#" - '#Call to an undefined method Aura\\\Html\\\HelperLocator::[a-zA-Z0-9_]+\(\)#' - '#Call to an undefined method Query\\QueryBuilderInterface::[a-zA-Z0-9_]+\(\)#' diff --git a/src/AnimeClient/AnimeClient.php b/src/AnimeClient/AnimeClient.php index df1ce722..481e34c2 100644 --- a/src/AnimeClient/AnimeClient.php +++ b/src/AnimeClient/AnimeClient.php @@ -91,7 +91,7 @@ function loadTomlFile(string $filename): array return Toml::parseFile($filename); } -function _iterateToml(TomlBuilder $builder, iterable $data, $parentKey = NULL): void +function _iterateToml(TomlBuilder $builder, iterable $data, mixed $parentKey = NULL): void { foreach ($data as $key => $value) { @@ -359,7 +359,7 @@ function colNotEmpty(array $search, string $key): bool * * @param CacheInterface $cache * @return bool - * @throws InvalidArgumentException + * @throws Throwable */ function clearCache(CacheInterface $cache): bool { diff --git a/src/AnimeClient/Controller.php b/src/AnimeClient/Controller.php index c0827ee4..3f6a1cc3 100644 --- a/src/AnimeClient/Controller.php +++ b/src/AnimeClient/Controller.php @@ -203,7 +203,7 @@ class Controller { * @throws NotFoundException * @return string */ - protected function loadPartial($view, string $template, array $data = []): string + protected function loadPartial(HtmlView $view, string $template, array $data = []): string { $router = $this->container->get('dispatcher'); @@ -236,7 +236,7 @@ class Controller { * @throws ContainerException * @throws NotFoundException */ - protected function renderFullPage($view, string $template, array $data): HtmlView + protected function renderFullPage(HtmlView $view, string $template, array $data): HtmlView { $csp = [ "default-src 'self'", @@ -360,7 +360,7 @@ class Controller { * @throws NotFoundException * @return string */ - protected function showMessage($view, string $type, string $message): string + protected function showMessage(HtmlView $view, string $type, string $message): string { return $this->loadPartial($view, 'message', [ 'message_type' => $type, @@ -399,7 +399,7 @@ class Controller { * @throws DoubleRenderException * @return void */ - protected function outputJSON($data, int $code): void + protected function outputJSON(mixed $data, int $code): void { (new JsonView()) ->setOutput($data) @@ -420,10 +420,7 @@ class Controller { { (new HttpView())->redirect($url, $code)->send(); } - catch (\Throwable $e) - { - - } + catch (\Throwable) {} } } // End of BaseController.php \ No newline at end of file diff --git a/src/AnimeClient/Controller/Anime.php b/src/AnimeClient/Controller/Anime.php index 04bd7862..e011e426 100644 --- a/src/AnimeClient/Controller/Anime.php +++ b/src/AnimeClient/Controller/Anime.php @@ -145,7 +145,7 @@ final class Anime extends BaseController { { $this->checkAuth(); - $data = $this->request->getParsedBody(); + $data = (array)$this->request->getParsedBody(); if (empty($data['mal_id'])) { @@ -220,7 +220,7 @@ final class Anime extends BaseController { { $this->checkAuth(); - $data = $this->request->getParsedBody(); + $data = (array)$this->request->getParsedBody(); // Do some minor data manipulation for // large form-based updates @@ -257,7 +257,7 @@ final class Anime extends BaseController { } else { - $data = $this->request->getParsedBody(); + $data = (array)$this->request->getParsedBody(); } if (empty($data)) @@ -282,7 +282,7 @@ final class Anime extends BaseController { { $this->checkAuth(); - $body = $this->request->getParsedBody(); + $body = (array)$this->request->getParsedBody(); $response = $this->model->deleteLibraryItem($body['id'], $body['mal_id']); if ($response === TRUE) diff --git a/src/AnimeClient/Controller/AnimeCollection.php b/src/AnimeClient/Controller/AnimeCollection.php index 0935d181..97de11d4 100644 --- a/src/AnimeClient/Controller/AnimeCollection.php +++ b/src/AnimeClient/Controller/AnimeCollection.php @@ -154,7 +154,7 @@ final class AnimeCollection extends BaseController { public function edit(): void { $this->checkAuth(); - $this->update($this->request->getParsedBody()); + $this->update((array)$this->request->getParsedBody()); } /** @@ -169,7 +169,7 @@ final class AnimeCollection extends BaseController { { $this->checkAuth(); - $data = $this->request->getParsedBody(); + $data = (array)$this->request->getParsedBody(); if (array_key_exists('id', $data)) { // Check for existing entry @@ -218,7 +218,7 @@ final class AnimeCollection extends BaseController { { $this->checkAuth(); - $data = $this->request->getParsedBody(); + $data = (array)$this->request->getParsedBody(); if ( ! array_key_exists('hummingbird_id', $data)) { $this->setFlashMessage("Can't delete item that doesn't exist", 'error'); @@ -238,11 +238,11 @@ final class AnimeCollection extends BaseController { /** * Update a collection item * - * @param $data + * @param array $data * @throws ContainerException * @throws NotFoundException */ - protected function update($data): void + protected function update(array $data): void { if (array_key_exists('hummingbird_id', $data)) { diff --git a/src/AnimeClient/Controller/Images.php b/src/AnimeClient/Controller/Images.php index dbceec27..1f526d7a 100644 --- a/src/AnimeClient/Controller/Images.php +++ b/src/AnimeClient/Controller/Images.php @@ -37,9 +37,7 @@ final class Images extends BaseController { * @param string $file The filename to look for * @param bool $display Whether to output the image to the server * @return void - * @throws NotFoundException * @throws Throwable - * @throws ContainerException */ public function cache(string $type, string $file, $display = TRUE): void { @@ -134,7 +132,16 @@ final class Images extends BaseController { [$origWidth] = getimagesizefromstring($data); $gdImg = imagecreatefromstring($data); + if ($gdImg === FALSE) + { + return; + } + $resizedImg = imagescale($gdImg, $width ?? $origWidth); + if ($resizedImg === FALSE) + { + return; + } if ($ext === 'gif') { @@ -161,7 +168,7 @@ final class Images extends BaseController { ? 'image/webp' : $response->getHeader('content-type')[0]; - $outputFile = (strpos($file, '-original') !== FALSE) + $outputFile = (str_contains($file, '-original')) ? "{$filePrefix}-original.{$ext}" : "{$filePrefix}.{$ext}"; diff --git a/src/AnimeClient/Controller/Manga.php b/src/AnimeClient/Controller/Manga.php index fd26aded..049be5a1 100644 --- a/src/AnimeClient/Controller/Manga.php +++ b/src/AnimeClient/Controller/Manga.php @@ -135,15 +135,13 @@ final class Manga extends Controller { * Add an manga to the list * * @return void - * @throws NotFoundException * @throws Throwable - * @throws ContainerException */ public function add(): void { $this->checkAuth(); - $data = $this->request->getParsedBody(); + $data = (array)$this->request->getParsedBody(); if ( ! array_key_exists('id', $data)) { $this->redirect('manga/add', 303); @@ -163,7 +161,7 @@ final class Manga extends Controller { } else { - $this->setFlashMessage('Failed to add new manga to list' . $result['body'], 'error'); + $this->setFlashMessage('Failed to add new manga to list:' . print_r($data, TRUE), 'error'); } $this->sessionRedirect(); @@ -180,7 +178,7 @@ final class Manga extends Controller { * @throws InvalidArgumentException * @return void */ - public function edit($id, $status = 'All'): void + public function edit(string $id, string $status = 'All'): void { $this->checkAuth(); @@ -218,14 +216,12 @@ final class Manga extends Controller { * * @return void * @throws Throwable - * @throws NotFoundException - * @throws ContainerException */ public function formUpdate(): void { $this->checkAuth(); - $data = $this->request->getParsedBody(); + $data = (array)$this->request->getParsedBody(); // Do some minor data manipulation for // large form-based updates @@ -275,8 +271,6 @@ final class Manga extends Controller { /** * Remove an manga from the list * - * @throws ContainerException - * @throws NotFoundException * @throws Throwable * @return void */ @@ -284,7 +278,7 @@ final class Manga extends Controller { { $this->checkAuth(); - $body = $this->request->getParsedBody(); + $body = (array)$this->request->getParsedBody(); $response = $this->model->deleteLibraryItem($body['id'], $body['mal_id']); if ($response) diff --git a/src/AnimeClient/Controller/Misc.php b/src/AnimeClient/Controller/Misc.php index 208bb29a..6d4a72b7 100644 --- a/src/AnimeClient/Controller/Misc.php +++ b/src/AnimeClient/Controller/Misc.php @@ -74,7 +74,7 @@ final class Misc extends BaseController { */ public function loginAction(): void { - $post = $this->request->getParsedBody(); + $post = (array)$this->request->getParsedBody(); if ($this->auth->authenticate($post['password'])) { @@ -83,7 +83,11 @@ final class Misc extends BaseController { } $this->setFlashMessage('Invalid username or password.'); - $this->redirect($this->url->generate('login'), 303); + + $redirectUrl = $this->url->generate('login'); + $redirectUrl = ($redirectUrl !== FALSE) ? $redirectUrl : ''; + + $this->redirect($redirectUrl, 303); } /** diff --git a/src/AnimeClient/Controller/Settings.php b/src/AnimeClient/Controller/Settings.php index c564e9db..2e78e9f5 100644 --- a/src/AnimeClient/Controller/Settings.php +++ b/src/AnimeClient/Controller/Settings.php @@ -84,7 +84,7 @@ final class Settings extends BaseController { */ public function update(): void { - $post = $this->request->getParsedBody(); + $post = (array)$this->request->getParsedBody(); unset($post['settings-tabs']); $saved = $this->settingsModel->saveSettingsFile($post); @@ -93,7 +93,10 @@ final class Settings extends BaseController { ? $this->setFlashMessage('Saved config settings.', 'success') : $this->setFlashMessage('Failed to save config file.', 'error'); - $this->redirect($this->url->generate('settings'), 303); + $redirectUrl = $this->url->generate('settings'); + $redirectUrl = ($redirectUrl !== FALSE) ? $redirectUrl : ''; + + $this->redirect($redirectUrl, 303); } /** @@ -152,6 +155,9 @@ final class Settings extends BaseController { ? $this->setFlashMessage('Linked Anilist Account', 'success') : $this->setFlashMessage('Error Linking Anilist Account', 'error'); - $this->redirect($this->url->generate('settings'), 303); + $redirectUrl = $this->url->generate('settings'); + $redirectUrl = ($redirectUrl !== FALSE) ? $redirectUrl : ''; + + $this->redirect($redirectUrl, 303); } } \ No newline at end of file diff --git a/src/AnimeClient/Dispatcher.php b/src/AnimeClient/Dispatcher.php index 6a6d53f3..0be584fb 100644 --- a/src/AnimeClient/Dispatcher.php +++ b/src/AnimeClient/Dispatcher.php @@ -16,19 +16,23 @@ namespace Aviat\AnimeClient; -use Aviat\AnimeClient\Enum\EventType; -use function Aviat\Ion\_dir; - -use Aura\Router\{Map, Matcher, Route, Rule}; - +use Aviat\Ion\Json; +use Aura\Router\{ + Map, + Matcher, + Route, + Rule, +}; use Aviat\AnimeClient\API\FailedResponseException; use Aviat\Ion\Di\ContainerInterface; -use Aviat\Ion\Event; use Aviat\Ion\Friend; use Aviat\Ion\Type\StringType; +use JetBrains\PhpStorm\ArrayShape; use LogicException; use ReflectionException; +use function Aviat\Ion\_dir; + /** * Basic routing/ dispatch */ @@ -78,7 +82,7 @@ final class Dispatcher extends RoutingBase { * * @return Route|false */ - public function getRoute() + public function getRoute(): Route | false { $logger = $this->container->getLogger(); @@ -132,7 +136,7 @@ final class Dispatcher extends RoutingBase { { // If not route was matched, return an appropriate http // error message - $errorRoute = $this->getErrorParams(); + $errorRoute = (array)$this->getErrorParams(); $controllerName = DEFAULT_CONTROLLER; $actionMethod = $errorRoute['action_method']; $params = $errorRoute['params']; @@ -152,11 +156,11 @@ final class Dispatcher extends RoutingBase { * Parse out the arguments for the appropriate controller for * the current route * - * @param Route $route + * @param Friend $route * @throws LogicException * @return array */ - protected function processRoute($route): array + protected function processRoute(Friend $route): array { if ( ! array_key_exists('controller', $route->attributes)) { @@ -166,7 +170,7 @@ final class Dispatcher extends RoutingBase { $controllerName = $route->attributes['controller']; // Get the full namespace for a controller if a short name is given - if (strpos($controllerName, '\\') === FALSE) + if ( ! str_contains($controllerName, '\\')) { $map = $this->getControllerList(); $controllerName = $map[$controllerName]; @@ -191,7 +195,7 @@ final class Dispatcher extends RoutingBase { $logger = $this->container->getLogger(); if ($logger !== NULL) { - $logger->info(json_encode($params)); + $logger->info(Json::encode($params)); } return [ @@ -244,6 +248,10 @@ final class Dispatcher extends RoutingBase { $path = trim($path, '/'); $actualPath = realpath(_dir(SRC_DIR, $path)); $classFiles = glob("{$actualPath}/*.php"); + if ($classFiles === FALSE) + { + return []; + } $controllers = []; @@ -282,9 +290,10 @@ final class Dispatcher extends RoutingBase { $logger->debug('Dispatcher - controller arguments', $params); } - call_user_func_array([$controller, $method], array_values($params)); + $params = array_values($params); + $controller->$method(...$params); } - catch (FailedResponseException $e) + catch (FailedResponseException) { $controllerName = DEFAULT_CONTROLLER; $controller = new $controllerName($this->container); diff --git a/src/AnimeClient/FormGenerator.php b/src/AnimeClient/FormGenerator.php index c0a50ce3..a659e119 100644 --- a/src/AnimeClient/FormGenerator.php +++ b/src/AnimeClient/FormGenerator.php @@ -48,7 +48,7 @@ final class FormGenerator { * Create a new FormGenerator * * @param ContainerInterface $container - * @return $this + * @return self */ public static function new(ContainerInterface $container): self { diff --git a/src/AnimeClient/MenuGenerator.php b/src/AnimeClient/MenuGenerator.php index d8dfd23d..98e27f72 100644 --- a/src/AnimeClient/MenuGenerator.php +++ b/src/AnimeClient/MenuGenerator.php @@ -22,7 +22,7 @@ use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Exception\ConfigException; use Aviat\Ion\Type\ArrayType; use Aviat\Ion\Type\StringType; -use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ServerRequestInterface; /** * Helper object to manage menu creation and selection @@ -39,9 +39,9 @@ final class MenuGenerator extends UrlGenerator { /** * Request object * - * @var RequestInterface + * @var ServerRequestInterface */ - protected RequestInterface $request; + protected ServerRequestInterface $request; /** * @param ContainerInterface $container diff --git a/src/AnimeClient/Model/MediaTrait.php b/src/AnimeClient/Model/MediaTrait.php index aac8fabe..a844115c 100644 --- a/src/AnimeClient/Model/MediaTrait.php +++ b/src/AnimeClient/Model/MediaTrait.php @@ -85,7 +85,7 @@ trait MediaTrait { * @param string $itemId * @return AnimeListItem|MangaListItem */ - public function getLibraryItem(string $itemId) + public function getLibraryItem(string $itemId): AnimeListItem|MangaListItem { return $this->kitsuModel->getListItem($itemId); } diff --git a/src/AnimeClient/RoutingBase.php b/src/AnimeClient/RoutingBase.php index 1d91fd6d..e4cd80f7 100644 --- a/src/AnimeClient/RoutingBase.php +++ b/src/AnimeClient/RoutingBase.php @@ -22,7 +22,7 @@ use Aviat\Ion\Di\Exception\ContainerException; use Aviat\Ion\Di\Exception\NotFoundException; use Aviat\Ion\Exception\ConfigException; use Aviat\Ion\Type\StringType; -use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ServerRequestInterface; /** * Base for routing/url classes @@ -43,9 +43,9 @@ class RoutingBase { /** * Class wrapper for input superglobals - * @var RequestInterface + * @var ServerRequestInterface */ - protected RequestInterface $request; + protected ServerRequestInterface $request; /** * Constructor @@ -64,8 +64,7 @@ class RoutingBase { /** * Get the current url path - * @throws ContainerException - * @throws NotFoundException + * * @return string */ public function path(): string @@ -82,8 +81,7 @@ class RoutingBase { /** * Get the url segments - * @throws ContainerException - * @throws NotFoundException + * * @return array */ public function segments(): array @@ -96,11 +94,10 @@ class RoutingBase { * Get a segment of the current url * * @param int $num - * @throws ContainerException - * @throws NotFoundException + * * @return string|null */ - public function getSegment($num): ?string + public function getSegment(int $num): ?string { $segments = $this->segments(); return $segments[$num] ?? NULL; @@ -109,8 +106,6 @@ class RoutingBase { /** * Retrieve the last url segment * - * @throws ContainerException - * @throws NotFoundException * @return string */ public function lastSegment(): string diff --git a/src/Ion/HttpViewInterface.php b/src/Ion/HttpViewInterface.php new file mode 100644 index 00000000..e817b3c7 --- /dev/null +++ b/src/Ion/HttpViewInterface.php @@ -0,0 +1,32 @@ + + * @copyright 2015 - 2021 Timothy J. Warren + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 5.2 + * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient + */ + +namespace Aviat\Ion; + +/** + * View Interface abstracting an HTTP Response + */ +interface HttpViewInterface extends ViewInterface { + + /** + * Set the status code of the request + * + * @param int $code + * @throws \InvalidArgumentException + * @return self + */ + public function setStatusCode(int $code): self; +} \ No newline at end of file diff --git a/src/Ion/View/HtmlView.php b/src/Ion/View/HtmlView.php index bfd1eb05..541089a1 100644 --- a/src/Ion/View/HtmlView.php +++ b/src/Ion/View/HtmlView.php @@ -18,8 +18,6 @@ namespace Aviat\Ion\View; use Aviat\Ion\Di\ContainerAware; use Aviat\Ion\Di\ContainerInterface; -use Aviat\Ion\Di\Exception\ContainerException; -use Aviat\Ion\Di\Exception\NotFoundException; use Laminas\Diactoros\Response\HtmlResponse; use const EXTR_OVERWRITE; @@ -40,8 +38,6 @@ class HtmlView extends HttpView { * Create the Html View * * @param ContainerInterface $container - * @throws ContainerException - * @throws NotFoundException */ public function __construct(ContainerInterface $container) { @@ -57,6 +53,7 @@ class HtmlView extends HttpView { * @param string $path * @param array $data * @return string + * @throws \Throwable */ public function renderTemplate(string $path, array $data): string { @@ -74,9 +71,7 @@ class HtmlView extends HttpView { // Very basic html minify, that won't affect content between html tags - $buffer = preg_replace('/>\s+ <', $buffer); - - return $buffer; + return preg_replace('/>\s+ <', $buffer); } } // End of HtmlView.php \ No newline at end of file diff --git a/src/Ion/View/HttpView.php b/src/Ion/View/HttpView.php index dd683a3b..c0b65913 100644 --- a/src/Ion/View/HttpView.php +++ b/src/Ion/View/HttpView.php @@ -16,7 +16,7 @@ namespace Aviat\Ion\View; -use Aviat\Ion\ViewInterface; +use Aviat\Ion\HttpViewInterface; use Laminas\Diactoros\Response; use Laminas\HttpHandlerRunner\Emitter\SapiEmitter; @@ -26,7 +26,7 @@ use Psr\Http\Message\ResponseInterface; /** * Base view class for Http output */ -class HttpView implements ViewInterface{ +class HttpView implements HttpViewInterface{ /** * HTTP response Object @@ -103,9 +103,9 @@ class HttpView implements ViewInterface{ * Set the output string * * @param mixed $string - * @return ViewInterface + * @return HttpViewInterface */ - public function setOutput($string): ViewInterface + public function setOutput($string): HttpViewInterface { $this->response->getBody()->write($string); @@ -117,9 +117,9 @@ class HttpView implements ViewInterface{ * Append additional output. * * @param string $string - * @return ViewInterface + * @return HttpViewInterface */ - public function appendOutput(string $string): ViewInterface + public function appendOutput(string $string): HttpViewInterface { return $this->setOutput($string); } diff --git a/src/Ion/View/JsonView.php b/src/Ion/View/JsonView.php index 09856d47..470076d4 100644 --- a/src/Ion/View/JsonView.php +++ b/src/Ion/View/JsonView.php @@ -17,7 +17,7 @@ namespace Aviat\Ion\View; use Aviat\Ion\Json; -use Aviat\Ion\ViewInterface; +use Aviat\Ion\HttpViewInterface; /** * View class to serialize Json @@ -35,9 +35,9 @@ class JsonView extends HttpView { * Set the output string * * @param mixed $string - * @return ViewInterface + * @return HttpViewInterface */ - public function setOutput($string): ViewInterface + public function setOutput(mixed $string): HttpViewInterface { if ( ! is_string($string)) { From 0c592b493245ad3bc0fe011abf342c19b8f2895b Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 09:39:28 -0500 Subject: [PATCH 13/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 40ad8172..b43ccb76 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -34,6 +34,13 @@ pipeline { sh 'php ./vendor/bin/phpunit --colors=never' } } + stage('Code Cleanliness') { + agent any + steps { + sh 'php ./vendor/bin/phpstan analyse src/ -l max -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{$1=$1;print}' > build/logs/checkstyle.xml' + checkstyle 'build/logs/checkstyle.xml' + } + } stage('Coverage') { agent any steps { From dabb6be291c1790a5101945a407dce56164c6ffa Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 09:42:54 -0500 Subject: [PATCH 14/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index b43ccb76..a32a7cf0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,7 +37,7 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh 'php ./vendor/bin/phpstan analyse src/ -l max -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{$1=$1;print}' > build/logs/checkstyle.xml' + sh "php ./vendor/bin/phpstan analyse src/ -l max -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" checkstyle 'build/logs/checkstyle.xml' } } From 7a41a5d9a97757405f5cb54d540babac949f39bc Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 09:51:52 -0500 Subject: [PATCH 15/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a32a7cf0..e490bbe4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,8 +37,8 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php ./vendor/bin/phpstan analyse src/ -l max -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" - checkstyle 'build/logs/checkstyle.xml' + sh "php ./vendor/bin/phpstan analyse src/ -l max -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle \\| awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" + recordIssues(tools: [checkstyle(reportEncoding: 'UTF-8')]) } } stage('Coverage') { From 39641572127cb7667cf1e24d3c8d3eb3b4309843 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 09:59:29 -0500 Subject: [PATCH 16/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index e490bbe4..f0510b91 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,7 +37,7 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php ./vendor/bin/phpstan analyse src/ -l max -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle \\| awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" + sh "php ./vendor/bin/phpstan analyse src/ -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle > build/logs/checkstyle.xml" recordIssues(tools: [checkstyle(reportEncoding: 'UTF-8')]) } } From 442e59f6cfc6d91fc844dd21aa52165797c56b27 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 10:04:13 -0500 Subject: [PATCH 17/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index f0510b91..e66efb67 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -35,7 +35,12 @@ pipeline { } } stage('Code Cleanliness') { - agent any + agent { + docker { + image 'php:8-cli-alpine' + args '-u root --privileged' + } + } steps { sh "php ./vendor/bin/phpstan analyse src/ -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle > build/logs/checkstyle.xml" recordIssues(tools: [checkstyle(reportEncoding: 'UTF-8')]) From 39b1d02862442a34d8e9813d632fa5218668e209 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 10:07:55 -0500 Subject: [PATCH 18/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index e66efb67..6814daed 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -35,15 +35,10 @@ pipeline { } } stage('Code Cleanliness') { - agent { - docker { - image 'php:8-cli-alpine' - args '-u root --privileged' - } - } + agent any steps { - sh "php ./vendor/bin/phpstan analyse src/ -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle > build/logs/checkstyle.xml" - recordIssues(tools: [checkstyle(reportEncoding: 'UTF-8')]) + sh "php8 ./vendor/bin/phpstan analyse src/ -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" + recordIssues(tools: [checkstyle(reportEncoding: 'UTF-8', reportFileName: 'build/logs/checkstyle.xml')]) } } stage('Coverage') { From 0ad9a5094b1c543bf4a9f5005d6b4439dca4ea59 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 10:19:58 -0500 Subject: [PATCH 19/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 6814daed..5ff92685 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,8 +37,8 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php8 ./vendor/bin/phpstan analyse src/ -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" - recordIssues(tools: [checkstyle(reportEncoding: 'UTF-8', reportFileName: 'build/logs/checkstyle.xml')]) + sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" + recordIssues(tools: [checkstyle(reportEncoding: 'UTF-8')]) } } stage('Coverage') { From 486e7dc982f82dc59ab021fef6d5a0e300fec77e Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 10:34:08 -0500 Subject: [PATCH 20/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5ff92685..6c2877bf 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -38,7 +38,7 @@ pipeline { agent any steps { sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" - recordIssues(tools: [checkstyle(reportEncoding: 'UTF-8')]) + recordIssues(tools: [phpstan(reportEncoding: 'UTF-8')]) } } stage('Coverage') { From 42004ed735ba13a847ca064c398ce8075aed3389 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 10:38:16 -0500 Subject: [PATCH 21/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 6c2877bf..345b7daa 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -38,7 +38,8 @@ pipeline { agent any steps { sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" - recordIssues(tools: [phpstan(reportEncoding: 'UTF-8')]) + // recordIssues(tools: [checkStyle(reportEncoding: 'UTF-8')]) + checkStyle 'build/logs/checkstyle.xml' } } stage('Coverage') { From dc02627a345db9b6fad1eae1bbb29c2b1c7963f5 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 10:40:03 -0500 Subject: [PATCH 22/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 345b7daa..73fb4286 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -38,8 +38,8 @@ pipeline { agent any steps { sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" - // recordIssues(tools: [checkStyle(reportEncoding: 'UTF-8')]) - checkStyle 'build/logs/checkstyle.xml' + recordIssues(tools: [checkStyle(reportEncoding: 'UTF-8')]) + // checkStyle 'build/logs/checkstyle.xml' } } stage('Coverage') { From 05b6d297217cdf925af33c2300b7ce6f8978dd18 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 10:42:51 -0500 Subject: [PATCH 23/37] Attempt to add PHPStan messages to CI --- Jenkinsfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 73fb4286..0abe3756 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,9 +37,8 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle.xml" + sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle-result.xml" recordIssues(tools: [checkStyle(reportEncoding: 'UTF-8')]) - // checkStyle 'build/logs/checkstyle.xml' } } stage('Coverage') { From d60b1dd776a48633061c7e42000eb938368b5f71 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 10:53:07 -0500 Subject: [PATCH 24/37] Fix code warnings for 'src/AnimeClient/AnimeClient.php' --- src/AnimeClient/AnimeClient.php | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/AnimeClient/AnimeClient.php b/src/AnimeClient/AnimeClient.php index 481e34c2..9dd179ba 100644 --- a/src/AnimeClient/AnimeClient.php +++ b/src/AnimeClient/AnimeClient.php @@ -124,7 +124,7 @@ function _iterateToml(TomlBuilder $builder, iterable $data, mixed $parentKey = N /** * Serialize config data into a Toml file * - * @param mixed $data + * @param iterable $data * @return string */ function arrayToToml(iterable $data): string @@ -157,7 +157,7 @@ function tomlToArray(string $toml): array * @param mixed $array * @return bool */ -function isSequentialArray($array): bool +function isSequentialArray(mixed $array): bool { if ( ! is_array($array)) { @@ -270,7 +270,7 @@ function getLocalImg (string $kitsuUrl, $webp = TRUE): string $parts = parse_url($kitsuUrl); - if ($parts === FALSE) + if ($parts === FALSE || ! array_key_exists('path', $parts)) { return 'images/placeholder.webp'; } @@ -296,22 +296,35 @@ function getLocalImg (string $kitsuUrl, $webp = TRUE): string * @param int|null $width * @param int|null $height * @param string $text + * @return bool */ -function createPlaceholderImage (string $path, ?int $width, ?int $height, $text = 'Image Unavailable'): void +function createPlaceholderImage (string $path, ?int $width, ?int $height, $text = 'Image Unavailable'): bool { $width = $width ?? 200; $height = $height ?? 200; $img = imagecreatetruecolor($width, $height); + if ($img === FALSE) + { + return FALSE; + } imagealphablending($img, TRUE); $path = rtrim($path, '/'); // Background is the first color by default $fillColor = imagecolorallocatealpha($img, 255, 255, 255, 127); + if ($fillColor === FALSE) + { + return FALSE; + } imagefill($img, 0, 0, $fillColor); $textColor = imagecolorallocate($img, 64, 64, 64); + if ($textColor === FALSE) + { + return FALSE; + } imagealphablending($img, TRUE); @@ -333,12 +346,18 @@ function createPlaceholderImage (string $path, ?int $width, ?int $height, $text imagedestroy($img); $pngImage = imagecreatefrompng($path . '/placeholder.png'); + if ($pngImage === FALSE) + { + return FALSE; + } imagealphablending($pngImage, TRUE); imagesavealpha($pngImage, TRUE); imagewebp($pngImage, $path . '/placeholder.webp'); imagedestroy($pngImage); + + return TRUE; } /** From eb03679579019e05f54fc305c5b8efbb67e29f37 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 11:14:45 -0500 Subject: [PATCH 25/37] Solve more PHPStan issues --- phpstan.neon | 6 +++-- src/AnimeClient/API/Kitsu/Auth.php | 8 ++++--- .../Transformer/LibraryEntryTransformer.php | 5 +++-- src/AnimeClient/AnimeClient.php | 3 ++- src/AnimeClient/Command/BaseCommand.php | 22 +++++++++++++++++-- src/AnimeClient/Command/SyncLists.php | 4 ++-- 6 files changed, 36 insertions(+), 12 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index a5bf6603..53b8e218 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -8,10 +8,12 @@ parameters: - ./console - index.php ignoreErrors: - - '#Access to an undefined property Aviat\\Ion\\Friend#' - "#Offset 'fields' does not exist on array#" - '#Call to an undefined method Aura\\\Html\\\HelperLocator::[a-zA-Z0-9_]+\(\)#' - '#Call to an undefined method Query\\QueryBuilderInterface::[a-zA-Z0-9_]+\(\)#' excludes_analyse: - tests/mocks.php - - vendor \ No newline at end of file + - vendor + # These are objects that basically can return anything + universalObjectCratesClasses: + - Aviat\Ion\Friend diff --git a/src/AnimeClient/API/Kitsu/Auth.php b/src/AnimeClient/API/Kitsu/Auth.php index 54d5de7c..67a1f7f3 100644 --- a/src/AnimeClient/API/Kitsu/Auth.php +++ b/src/AnimeClient/API/Kitsu/Auth.php @@ -66,7 +66,7 @@ final class Auth { ->getSegment(SESSION_SEGMENT); $this->model = $container->get('kitsu-model'); - Event::on('::unauthorized::', [$this, 'reAuthenticate'], []); + Event::on('::unauthorized::', [$this, 'reAuthenticate']); } /** @@ -76,6 +76,7 @@ final class Auth { * @param string $password * @return boolean * @throws Throwable + * @throws InvalidArgumentException */ public function authenticate(string $password): bool { @@ -90,9 +91,10 @@ final class Auth { /** * Make the call to re-authenticate with the existing refresh token * - * @param string $refreshToken + * @param string|null $refreshToken * @return boolean - * @throws Throwable|InvalidArgumentException + * @throws InvalidArgumentException + * @throws Throwable */ public function reAuthenticate(?string $refreshToken = NULL): bool { diff --git a/src/AnimeClient/API/Kitsu/Transformer/LibraryEntryTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/LibraryEntryTransformer.php index b18409bd..2a9b4105 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/LibraryEntryTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/LibraryEntryTransformer.php @@ -28,6 +28,7 @@ final class LibraryEntryTransformer extends AbstractTransformer { public function transform($item) { + $item = (array)$item; $type = $item['media']['type'] ?? ''; $genres = []; @@ -50,7 +51,7 @@ final class LibraryEntryTransformer extends AbstractTransformer } } - private function animeTransform($item, array $genres): AnimeListItem + private function animeTransform(array $item, array $genres): AnimeListItem { $animeId = $item['media']['id']; $anime = $item['media']; @@ -119,7 +120,7 @@ final class LibraryEntryTransformer extends AbstractTransformer ]); } - private function mangaTransform($item, array $genres): MangaListItem + private function mangaTransform(array $item, array $genres): MangaListItem { $mangaId = $item['media']['id']; $manga = $item['media']; diff --git a/src/AnimeClient/AnimeClient.php b/src/AnimeClient/AnimeClient.php index 9dd179ba..1a903d89 100644 --- a/src/AnimeClient/AnimeClient.php +++ b/src/AnimeClient/AnimeClient.php @@ -413,5 +413,6 @@ function renderTemplate(string $path, array $data): string ob_start(); extract($data, EXTR_OVERWRITE); include $path; - return ob_get_clean(); + $rawOutput = ob_get_clean(); + return (is_string($rawOutput)) ? $rawOutput : ''; } \ No newline at end of file diff --git a/src/AnimeClient/Command/BaseCommand.php b/src/AnimeClient/Command/BaseCommand.php index f043d9e2..b538e2cd 100644 --- a/src/AnimeClient/Command/BaseCommand.php +++ b/src/AnimeClient/Command/BaseCommand.php @@ -52,13 +52,22 @@ abstract class BaseCommand extends Command { * @param string|int|null $bgColor * @return void */ - public function echoBox($message, $fgColor = NULL, $bgColor = NULL): void + public function echoBox(string|array $message, string|int|null $fgColor = NULL, string|int|null $bgColor = NULL): void { if (is_array($message)) { $message = implode("\n", $message); } + if ($fgColor !== NULL) + { + $fgColor = (string)$fgColor; + } + if ($bgColor !== NULL) + { + $bgColor = (string)$bgColor; + } + // color message $message = Colors::colorize($message, $fgColor, $bgColor); @@ -129,8 +138,17 @@ abstract class BaseCommand extends Command { return $this->_di($configArray, $APP_DIR); } - private function _line(string $message, $fgColor = NULL, $bgColor = NULL): void + private function _line(string $message, int|string|null $fgColor = NULL, int|string|null $bgColor = NULL): void { + if ($fgColor !== NULL) + { + $fgColor = (string)$fgColor; + } + if ($bgColor !== NULL) + { + $bgColor = (string)$bgColor; + } + $message = Colors::colorize($message, $fgColor, $bgColor); $this->getConsole()->writeln($message); } diff --git a/src/AnimeClient/Command/SyncLists.php b/src/AnimeClient/Command/SyncLists.php index 8eb01e0f..427ec829 100644 --- a/src/AnimeClient/Command/SyncLists.php +++ b/src/AnimeClient/Command/SyncLists.php @@ -218,7 +218,7 @@ final class SyncLists extends BaseCommand { * @param array $data * @throws Throwable */ - protected function update(string $type, array $data) + protected function update(string $type, array $data): void { if ( ! empty($data['addToAnilist'])) { @@ -259,7 +259,7 @@ final class SyncLists extends BaseCommand { // ------------------------------------------------------------------------ // Fetch helpers // ------------------------------------------------------------------------ - private function fetchAnilistCount(string $type) + private function fetchAnilistCount(string $type): int { $list = $this->fetchAnilist($type); From 21f1ae0502c7d5a10299c03e62f858249e75a419 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 11:24:57 -0500 Subject: [PATCH 26/37] Try to report phpStan errors more directly --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 0abe3756..27a6c1ac 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -38,7 +38,7 @@ pipeline { agent any steps { sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle-result.xml" - recordIssues(tools: [checkStyle(reportEncoding: 'UTF-8')]) + recordIssues(tools: [phpStan(reportEncoding: 'UTF-8')]) } } stage('Coverage') { From 35d8db08b7039591230150282a1b229640d65111 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 11:27:20 -0500 Subject: [PATCH 27/37] Try to report phpStan errors more directly --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 27a6c1ac..6b418c23 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,7 +37,7 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle-result.xml" + sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon" recordIssues(tools: [phpStan(reportEncoding: 'UTF-8')]) } } From d23e6ef95f274b0e5ffda13ea13a3ac6f2d056f0 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 11:40:25 -0500 Subject: [PATCH 28/37] Try to report phpStan errors more directly --- Jenkinsfile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 6b418c23..3103379d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,8 +37,11 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon" - recordIssues(tools: [phpStan(reportEncoding: 'UTF-8')]) + sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon --no-progress --no-ansi " + recordIssues( + failOnError: false, + tools: [phpStan(reportEncoding: 'UTF-8')] + ) } } stage('Coverage') { From ede4ab437b48f23bd3a55c2d81a020bfc5b37930 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 11:43:25 -0500 Subject: [PATCH 29/37] Try to report phpStan errors more directly --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3103379d..7212327b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,10 +37,10 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon --no-progress --no-ansi " + sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon --no-progress --no-ansi > build/logs/phpstan.log" recordIssues( failOnError: false, - tools: [phpStan(reportEncoding: 'UTF-8')] + tools: [phpStan(reportEncoding: 'UTF-8', pattern: 'build/logs/phpstan.log')] ) } } From aa3e73d184a96fe416e3b33e2449b4d9505984ce Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 11:48:24 -0500 Subject: [PATCH 30/37] Try to report phpStan errors more directly --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7212327b..65b7af91 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,10 +37,10 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon --no-progress --no-ansi > build/logs/phpstan.log" + sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-progress --no-ansi" recordIssues( failOnError: false, - tools: [phpStan(reportEncoding: 'UTF-8', pattern: 'build/logs/phpstan.log')] + tools: [phpStan(reportEncoding: 'UTF-8')] ) } } From 26118e627fff26f67cb081031a961080d504d1b8 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 11:54:22 -0500 Subject: [PATCH 31/37] Try to report phpStan errors more directly --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 65b7af91..f79edb82 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,10 +37,10 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-progress --no-ansi" + sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-progress --no-ansi -vvv > build/logs/phpstan.log 2> /dev/null" recordIssues( failOnError: false, - tools: [phpStan(reportEncoding: 'UTF-8')] + tools: [phpStan(reportEncoding: 'UTF-8', pattern: 'build/logs/phpstan.log')] ) } } From 8a32fc4e27a9e77e62cc9bb94f00439c036d2a06 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 11:57:36 -0500 Subject: [PATCH 32/37] Try to report phpStan errors more directly --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index f79edb82..a58d8f41 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,7 +37,7 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-progress --no-ansi -vvv > build/logs/phpstan.log 2> /dev/null" + sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-progress --no-ansi -vvv 2> build/logs/phpstan.log" recordIssues( failOnError: false, tools: [phpStan(reportEncoding: 'UTF-8', pattern: 'build/logs/phpstan.log')] From e7ec57eec5a54192415f832e5551c34168774aa6 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 12:00:44 -0500 Subject: [PATCH 33/37] Just go back to CheckStyle --- Jenkinsfile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a58d8f41..0abe3756 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -37,11 +37,8 @@ pipeline { stage('Code Cleanliness') { agent any steps { - sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-progress --no-ansi -vvv 2> build/logs/phpstan.log" - recordIssues( - failOnError: false, - tools: [phpStan(reportEncoding: 'UTF-8', pattern: 'build/logs/phpstan.log')] - ) + sh "php8 ./vendor/bin/phpstan analyse -c phpstan.neon -n --no-ansi --no-progress --error-format=checkstyle | awk '{\$1=\$1;print}' > build/logs/checkstyle-result.xml" + recordIssues(tools: [checkStyle(reportEncoding: 'UTF-8')]) } } stage('Coverage') { From be74cfacebee07ec779c232cc96e825be09d5d41 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 13:09:57 -0500 Subject: [PATCH 34/37] Fix a bunch more phpstan errors --- phpstan.neon | 1 + src/AnimeClient/API/APIRequestBuilder.php | 4 +--- .../Transformer/AnimeListTransformer.php | 2 +- .../Transformer/MangaListTransformer.php | 2 +- src/AnimeClient/API/CacheTrait.php | 5 ++-- src/AnimeClient/API/Kitsu/Model.php | 23 ++----------------- src/AnimeClient/API/Kitsu/RequestBuilder.php | 13 +++-------- .../Transformer/AnimeListTransformer.php | 7 +++--- .../Kitsu/Transformer/AnimeTransformer.php | 5 ++-- .../Transformer/CharacterTransformer.php | 7 +++--- .../Kitsu/Transformer/HistoryTransformer.php | 13 +++++++---- .../Transformer/LibraryEntryTransformer.php | 17 +++++--------- .../Transformer/MangaListTransformer.php | 5 ++-- .../Kitsu/Transformer/MangaTransformer.php | 5 ++-- .../Kitsu/Transformer/PersonTransformer.php | 7 +++--- .../API/Kitsu/Transformer/UserTransformer.php | 7 +++--- src/AnimeClient/Command/BaseCommand.php | 2 +- src/Ion/Transformer/AbstractTransformer.php | 2 +- src/Ion/Transformer/TransformerInterface.php | 2 +- 19 files changed, 54 insertions(+), 75 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 53b8e218..04161bca 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -9,6 +9,7 @@ parameters: - index.php ignoreErrors: - "#Offset 'fields' does not exist on array#" + - '#Function imagepalletetotruecolor not found#' - '#Call to an undefined method Aura\\\Html\\\HelperLocator::[a-zA-Z0-9_]+\(\)#' - '#Call to an undefined method Query\\QueryBuilderInterface::[a-zA-Z0-9_]+\(\)#' excludes_analyse: diff --git a/src/AnimeClient/API/APIRequestBuilder.php b/src/AnimeClient/API/APIRequestBuilder.php index ae714548..8b832090 100644 --- a/src/AnimeClient/API/APIRequestBuilder.php +++ b/src/AnimeClient/API/APIRequestBuilder.php @@ -116,10 +116,9 @@ abstract class APIRequestBuilder { * Set the request body * * @param FormBody|string $body - * @throws \TypeError * @return self */ - public function setBody($body): self + public function setBody(FormBody|string $body): self { $this->request->setBody($body); return $this; @@ -129,7 +128,6 @@ abstract class APIRequestBuilder { * Set body as form fields * * @param array $fields Mapping of field names to values - * @throws \TypeError * @return self */ public function setFormFields(array $fields): self diff --git a/src/AnimeClient/API/Anilist/Transformer/AnimeListTransformer.php b/src/AnimeClient/API/Anilist/Transformer/AnimeListTransformer.php index b09ecb9e..ffb82c13 100644 --- a/src/AnimeClient/API/Anilist/Transformer/AnimeListTransformer.php +++ b/src/AnimeClient/API/Anilist/Transformer/AnimeListTransformer.php @@ -27,7 +27,7 @@ use DateTime; class AnimeListTransformer extends AbstractTransformer { - public function transform($item): AnimeListItem + public function transform(array|object $item): AnimeListItem { return AnimeListItem::from([]); } diff --git a/src/AnimeClient/API/Anilist/Transformer/MangaListTransformer.php b/src/AnimeClient/API/Anilist/Transformer/MangaListTransformer.php index da2112d3..e1e47a79 100644 --- a/src/AnimeClient/API/Anilist/Transformer/MangaListTransformer.php +++ b/src/AnimeClient/API/Anilist/Transformer/MangaListTransformer.php @@ -28,7 +28,7 @@ use DateTime; class MangaListTransformer extends AbstractTransformer { - public function transform($item) + public function transform(array|object $item): MangaListItem { return MangaListItem::from([]); } diff --git a/src/AnimeClient/API/CacheTrait.php b/src/AnimeClient/API/CacheTrait.php index e8d940ad..a7c0d111 100644 --- a/src/AnimeClient/API/CacheTrait.php +++ b/src/AnimeClient/API/CacheTrait.php @@ -58,10 +58,9 @@ trait CacheTrait { * @param string $key * @param callable $primer * @param array|null $primeArgs - * @return mixed|null - * @throws InvalidArgumentException + * @return mixed */ - public function getCached(string $key, callable $primer, ?array $primeArgs = []) + public function getCached(string $key, callable $primer, ?array $primeArgs = []): mixed { $value = $this->cache->get($key, NULL); diff --git a/src/AnimeClient/API/Kitsu/Model.php b/src/AnimeClient/API/Kitsu/Model.php index acfef90f..c91e7633 100644 --- a/src/AnimeClient/API/Kitsu/Model.php +++ b/src/AnimeClient/API/Kitsu/Model.php @@ -37,12 +37,11 @@ use Aviat\AnimeClient\Enum\MediaType; use Aviat\AnimeClient\Kitsu as K; use Aviat\AnimeClient\Types\Anime; use Aviat\AnimeClient\Types\MangaPage; -use Aviat\Banker\Exception\InvalidArgumentException; use Aviat\Ion\{ Di\ContainerAware, Json }; -use Throwable; +use Generator; use function Amp\Promise\wait; use function Aviat\AnimeClient\getApiClient; @@ -91,7 +90,6 @@ final class Model { * @param string $username * @param string $password * @return bool|array - * @throws Throwable */ public function authenticate(string $username, string $password) { @@ -134,7 +132,6 @@ final class Model { * * @param string $token * @return bool|array - * @throws Throwable */ public function reAuthenticate(string $token) { @@ -174,7 +171,6 @@ final class Model { * * @param string|null $username * @return string - * @throws \Psr\SimpleCache\InvalidArgumentException */ public function getUserIdByUsername(string $username = NULL): string { @@ -210,7 +206,6 @@ final class Model { * * @param string $slug * @return array - * @throws InvalidArgumentException */ public function getPerson(string $slug): array { @@ -289,8 +284,6 @@ final class Model { * Retrieve the data for the anime watch history page * * @return array - * @throws InvalidArgumentException - * @throws Throwable */ public function getAnimeHistory(): array { @@ -315,7 +308,6 @@ final class Model { * * @param string $status - The watching status to filter the list with * @return array - * @throws InvalidArgumentException */ public function getAnimeList(string $status): array { @@ -354,7 +346,6 @@ final class Model { * * @param string $status - Optional status to filter by * @return int - * @throws InvalidArgumentException */ public function getAnimeListCount(string $status = '') : int { @@ -365,8 +356,6 @@ final class Model { * Get all the anime entries, that are organized for output to html * * @return array - * @throws ReflectionException - * @throws InvalidArgumentException */ public function getFullOrganizedAnimeList(): array { @@ -434,8 +423,6 @@ final class Model { * Retrieve the data for the manga read history page * * @return array - * @throws InvalidArgumentException - * @throws Throwable */ public function getMangaHistory(): array { @@ -458,7 +445,6 @@ final class Model { * * @param string $status - The reading status by which to filter the list * @return array - * @throws InvalidArgumentException */ public function getMangaList(string $status): array { @@ -497,7 +483,6 @@ final class Model { * * @param string $status - Optional status to filter by * @return int - * @throws InvalidArgumentException */ public function getMangaListCount(string $status = '') : int { @@ -508,8 +493,6 @@ final class Model { * Get all Manga lists * * @return array - * @throws ReflectionException - * @throws InvalidArgumentException */ public function getFullOrganizedMangaList(): array { @@ -638,8 +621,6 @@ final class Model { * * @param string $type * @return array - * @throws InvalidArgumentException - * @throws Throwable */ public function getSyncList(string $type): array { @@ -831,7 +812,7 @@ final class Model { }); } - private function getPages(callable $method, ...$args): ?\Generator + private function getPages(callable $method, mixed ...$args): ?Generator { $items = $method(...$args); diff --git a/src/AnimeClient/API/Kitsu/RequestBuilder.php b/src/AnimeClient/API/Kitsu/RequestBuilder.php index 74c02f8f..999b0794 100644 --- a/src/AnimeClient/API/Kitsu/RequestBuilder.php +++ b/src/AnimeClient/API/Kitsu/RequestBuilder.php @@ -26,7 +26,6 @@ use Amp\Http\Client\Request; use Amp\Http\Client\Response; use Aviat\AnimeClient\Kitsu as K; use Aviat\AnimeClient\API\APIRequestBuilder; -use Aviat\AnimeClient\API\FailedResponseException; use Aviat\AnimeClient\Enum\EventType; use Aviat\Ion\Di\ContainerAware; use Aviat\Ion\Di\ContainerInterface; @@ -157,7 +156,6 @@ final class RequestBuilder extends APIRequestBuilder { * @param string $name * @param array $variables * @return array - * @throws Throwable */ public function mutate(string $name, array $variables = []): array { @@ -181,7 +179,6 @@ final class RequestBuilder extends APIRequestBuilder { * @param string $url * @param array $options * @return Response - * @throws \Throwable */ public function getResponse(string $type, string $url, array $options = []): Response { @@ -210,7 +207,7 @@ final class RequestBuilder extends APIRequestBuilder { public function queryRequest(string $name, array $variables = []): Request { $file = realpath("{$this->filePath}/Queries/{$name}.graphql"); - if ( ! file_exists($file)) + if ($file === FALSE || ! file_exists($file)) { throw new LogicException('GraphQL query file does not exist.'); } @@ -240,12 +237,11 @@ final class RequestBuilder extends APIRequestBuilder { * @param string $name * @param array $variables * @return Request - * @throws Throwable */ public function mutateRequest (string $name, array $variables = []): Request { - $file = "{$this->filePath}/Mutations/{$name}.graphql"; - if ( ! file_exists($file)) + $file = realpath("{$this->filePath}/Mutations/{$name}.graphql"); + if ($file === FALSE || ! file_exists($file)) { throw new LogicException('GraphQL mutation file does not exist.'); } @@ -274,9 +270,6 @@ final class RequestBuilder extends APIRequestBuilder { * @param string $type * @param string $url * @param array $options - * @throws JsonException - * @throws FailedResponseException - * @throws Throwable * @return array */ private function request(string $type, string $url, array $options = []): array diff --git a/src/AnimeClient/API/Kitsu/Transformer/AnimeListTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/AnimeListTransformer.php index 0e493bf3..847efcf2 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/AnimeListTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/AnimeListTransformer.php @@ -33,11 +33,12 @@ final class AnimeListTransformer extends AbstractTransformer { * Convert raw api response to a more * logical and workable structure * - * @param array $item API library item + * @param array|object $item API library item * @return AnimeListItem */ - public function transform($item): AnimeListItem + public function transform(array|object $item): AnimeListItem { + $item = (array)$item; $animeId = $item['media']['id']; $anime = $item['media']; @@ -115,7 +116,7 @@ final class AnimeListTransformer extends AbstractTransformer { * @param array $item Transformed library item * @return FormItem API library item */ - public function untransform($item): FormItem + public function untransform(array $item): FormItem { $privacy = (array_key_exists('private', $item) && $item['private']); $rewatching = (array_key_exists('rewatching', $item) && $item['rewatching']); diff --git a/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php index d98117e9..53c62e27 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/AnimeTransformer.php @@ -29,11 +29,12 @@ final class AnimeTransformer extends AbstractTransformer { * Convert raw api response to a more * logical and workable structure * - * @param array $item API library item + * @param array|object $item API library item * @return AnimePage */ - public function transform($item): AnimePage + public function transform(array|object $item): AnimePage { + $item = (array)$item; $base = $item['data']['findAnimeBySlug'] ?? $item['data']['findAnimeById'] ?? $item['data']['randomMedia']; $characters = []; $links = []; diff --git a/src/AnimeClient/API/Kitsu/Transformer/CharacterTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/CharacterTransformer.php index 3cf43d6e..87cb5f9f 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/CharacterTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/CharacterTransformer.php @@ -28,12 +28,13 @@ use Locale; final class CharacterTransformer extends AbstractTransformer { /** - * @param array $characterData + * @param array|object $item * @return Character */ - public function transform($characterData): Character + public function transform(array|object $item): Character { - $data = $characterData['data']['findCharacterBySlug'] ?? []; + $item = (array)$item; + $data = $item['data']['findCharacterBySlug'] ?? []; $castings = []; $media = [ 'anime' => [], diff --git a/src/AnimeClient/API/Kitsu/Transformer/HistoryTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/HistoryTransformer.php index ad0974ac..1a37f9f0 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/HistoryTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/HistoryTransformer.php @@ -65,7 +65,7 @@ abstract class HistoryTransformer { foreach ($base as $entry) { - if ( ! (strtolower($entry['media']['__typename']) === $this->type)) + if (strtolower($entry['media']['__typename']) !== $this->type) { continue; } @@ -210,7 +210,7 @@ abstract class HistoryTransformer { ]); } - protected function transformUpdated($entry): HistoryItem + protected function transformUpdated(array $entry): HistoryItem { $id = $entry['media']['id']; $data = $entry['media']; @@ -242,7 +242,7 @@ abstract class HistoryTransformer { ]); } - return $entry; + return HistoryItem::from($entry); } protected function linkTitle (array $data): string @@ -257,6 +257,11 @@ abstract class HistoryTransformer { $date ); + if ($dateTime === FALSE) + { + return new DateTimeImmutable(); + } + return $dateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); } @@ -265,7 +270,7 @@ abstract class HistoryTransformer { return "/{$this->type}/details/{$data['slug']}"; } - protected function isReconsuming ($entry): bool + protected function isReconsuming (array $entry): bool { return $entry['libraryEntry']['reconsuming']; } diff --git a/src/AnimeClient/API/Kitsu/Transformer/LibraryEntryTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/LibraryEntryTransformer.php index 2a9b4105..5a5212e8 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/LibraryEntryTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/LibraryEntryTransformer.php @@ -26,7 +26,7 @@ use Aviat\Ion\Type\StringType; */ final class LibraryEntryTransformer extends AbstractTransformer { - public function transform($item) + public function transform(array|object $item): AnimeListItem|MangaListItem { $item = (array)$item; $type = $item['media']['type'] ?? ''; @@ -38,17 +38,12 @@ final class LibraryEntryTransformer extends AbstractTransformer sort($genres); } - switch (strtolower($type)) + return match (strtolower($type)) { - case 'anime': - return $this->animeTransform($item, $genres); - - case 'manga': - return $this->mangaTransform($item, $genres); - - default: - return []; - } + 'anime' => $this->animeTransform($item, $genres), + 'manga' => $this->mangaTransform($item, $genres), + default => AnimeListItem::from([]), + }; } private function animeTransform(array $item, array $genres): AnimeListItem diff --git a/src/AnimeClient/API/Kitsu/Transformer/MangaListTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/MangaListTransformer.php index 929f75bc..c8f97ba5 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/MangaListTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/MangaListTransformer.php @@ -31,11 +31,12 @@ final class MangaListTransformer extends AbstractTransformer { /** * Remap zipped anime data to a more logical form * - * @param array $item manga entry item + * @param array|object $item manga entry item * @return MangaListItem */ - public function transform($item): MangaListItem + public function transform(array|object $item): MangaListItem { + $item = (array)$item; $mangaId = $item['media']['id']; $manga = $item['media']; diff --git a/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php index 18041491..7c01a4d6 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php @@ -29,11 +29,12 @@ final class MangaTransformer extends AbstractTransformer { * Convert raw api response to a more * logical and workable structure * - * @param array $item API library item + * @param array|object $item API library item * @return MangaPage */ - public function transform($item): MangaPage + public function transform(array|object $item): MangaPage { + $item = (array)$item; $base = $item['data']['findMangaBySlug'] ?? $item['data']['findMangaById'] ?? $item['data']['randomMedia']; $characters = []; $links = []; diff --git a/src/AnimeClient/API/Kitsu/Transformer/PersonTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/PersonTransformer.php index 9d6752bb..16d235ab 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/PersonTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/PersonTransformer.php @@ -26,12 +26,13 @@ use Aviat\Ion\Transformer\AbstractTransformer; final class PersonTransformer extends AbstractTransformer { /** - * @param array|object $personData + * @param array|object $item * @return Person */ - public function transform($personData): Person + public function transform(array|object $item): Person { - $data = $personData['data']['findPersonBySlug'] ?? []; + $item = (array)$item; + $data = $item['data']['findPersonBySlug'] ?? []; $canonicalName = $data['names']['localized'][$data['names']['canonical']] ?? array_shift($data['names']['localized']); diff --git a/src/AnimeClient/API/Kitsu/Transformer/UserTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/UserTransformer.php index 00764cbb..236779f6 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/UserTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/UserTransformer.php @@ -29,9 +29,10 @@ use Aviat\Ion\Transformer\AbstractTransformer; * @return User */ final class UserTransformer extends AbstractTransformer { - public function transform($profileData): User + public function transform(array|object $item): User { - $base = $profileData['data']['findProfileBySlug'] ?? []; + $item = (array)$item; + $base = $item['data']['findProfileBySlug'] ?? []; $favorites = $base['favorites']['nodes'] ?? []; $stats = $base['stats'] ?? []; $waifu = (array_key_exists('waifu', $base)) ? [ @@ -71,7 +72,7 @@ final class UserTransformer extends AbstractTransformer { return $output; } - private function organizeStats(array $stats, $data = []): array + private function organizeStats(array $stats, array $data = []): array { $animeStats = []; $mangaStats = []; diff --git a/src/AnimeClient/Command/BaseCommand.php b/src/AnimeClient/Command/BaseCommand.php index b538e2cd..f05d2a60 100644 --- a/src/AnimeClient/Command/BaseCommand.php +++ b/src/AnimeClient/Command/BaseCommand.php @@ -148,7 +148,7 @@ abstract class BaseCommand extends Command { { $bgColor = (string)$bgColor; } - + $message = Colors::colorize($message, $fgColor, $bgColor); $this->getConsole()->writeln($message); } diff --git a/src/Ion/Transformer/AbstractTransformer.php b/src/Ion/Transformer/AbstractTransformer.php index 2beeee19..c65a2ab9 100644 --- a/src/Ion/Transformer/AbstractTransformer.php +++ b/src/Ion/Transformer/AbstractTransformer.php @@ -28,7 +28,7 @@ abstract class AbstractTransformer implements TransformerInterface { * @param array|object $item * @return mixed */ - abstract public function transform($item); + abstract public function transform(array|object $item): mixed; /** * Transform a set of structures diff --git a/src/Ion/Transformer/TransformerInterface.php b/src/Ion/Transformer/TransformerInterface.php index 1c7d0160..72e94c17 100644 --- a/src/Ion/Transformer/TransformerInterface.php +++ b/src/Ion/Transformer/TransformerInterface.php @@ -27,5 +27,5 @@ interface TransformerInterface { * @param array|object $item * @return mixed */ - public function transform($item); + public function transform(array|object $item): mixed; } \ No newline at end of file From e59b1ffc57eae7698da9352bfcb17ae923ff4101 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 13:14:58 -0500 Subject: [PATCH 35/37] Fix failing test --- tests/Ion/mocks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Ion/mocks.php b/tests/Ion/mocks.php index 0a28b385..c0a2f755 100644 --- a/tests/Ion/mocks.php +++ b/tests/Ion/mocks.php @@ -66,7 +66,7 @@ class FriendTestClass extends FriendParentTestClass { class TestTransformer extends AbstractTransformer { - public function transform($item) + public function transform(array|object $item): array { $out = []; $genre_list = (array) $item; From c03bd4c0406d9f17f1274504519ac0674185400b Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 17:52:58 -0500 Subject: [PATCH 36/37] Increase warning level of PHPStan --- phpstan.neon | 2 +- src/AnimeClient/API/Kitsu/Auth.php | 18 ++---------------- src/Ion/View/HtmlView.php | 2 +- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 04161bca..2fa40512 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,7 +2,7 @@ parameters: checkGenericClassInNonGenericObjectType: false checkMissingIterableValueType: false inferPrivatePropertyTypeFromConstructor: true - level: 7 + level: 8 paths: - src - ./console diff --git a/src/AnimeClient/API/Kitsu/Auth.php b/src/AnimeClient/API/Kitsu/Auth.php index 67a1f7f3..0d0b5c5e 100644 --- a/src/AnimeClient/API/Kitsu/Auth.php +++ b/src/AnimeClient/API/Kitsu/Auth.php @@ -26,10 +26,6 @@ use Aviat\Ion\Di\{ContainerAware, ContainerInterface}; use Aviat\Ion\Di\Exception\{ContainerException, NotFoundException}; use Aviat\Ion\Event; -use Psr\SimpleCache\InvalidArgumentException; - -use Throwable; - /** * Kitsu API Authentication */ @@ -55,8 +51,6 @@ final class Auth { * Constructor * * @param ContainerInterface $container - * @throws ContainerException - * @throws NotFoundException */ public function __construct(ContainerInterface $container) { @@ -75,8 +69,6 @@ final class Auth { * * @param string $password * @return boolean - * @throws Throwable - * @throws InvalidArgumentException */ public function authenticate(string $password): bool { @@ -93,8 +85,6 @@ final class Auth { * * @param string|null $refreshToken * @return boolean - * @throws InvalidArgumentException - * @throws Throwable */ public function reAuthenticate(?string $refreshToken = NULL): bool { @@ -114,7 +104,6 @@ final class Auth { * Check whether the current user is authenticated * * @return boolean - * @throws InvalidArgumentException */ public function isAuthenticated(): bool { @@ -135,7 +124,6 @@ final class Auth { * Retrieve the authentication token from the session * * @return string - * @throws InvalidArgumentException */ public function getAuthToken(): ?string { @@ -152,7 +140,6 @@ final class Auth { * Retrieve the refresh token * * @return string|null - * @throws InvalidArgumentException */ private function getRefreshToken(): ?string { @@ -168,11 +155,10 @@ final class Auth { /** * Save the new authentication information * - * @param $auth + * @param array|false $auth * @return bool - * @throws InvalidArgumentException */ - private function storeAuth($auth): bool + private function storeAuth(array|false $auth): bool { if (FALSE !== $auth) { diff --git a/src/Ion/View/HtmlView.php b/src/Ion/View/HtmlView.php index 541089a1..ad24c6e5 100644 --- a/src/Ion/View/HtmlView.php +++ b/src/Ion/View/HtmlView.php @@ -71,7 +71,7 @@ class HtmlView extends HttpView { // Very basic html minify, that won't affect content between html tags - return preg_replace('/>\s+ <', $buffer); + return preg_replace('/>\s+ <', $buffer) ?? $buffer; } } // End of HtmlView.php \ No newline at end of file From c71ff7f38ebd80028c93cadda586e5191e35c0c8 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 12 Feb 2021 19:17:39 -0500 Subject: [PATCH 37/37] Fix more PHPStan issues --- src/AnimeClient/API/Kitsu/MutationTrait.php | 3 +- src/AnimeClient/Kitsu.php | 2 +- src/AnimeClient/Model/AnimeCollection.php | 48 +++++++++++++-------- src/AnimeClient/Model/Collection.php | 23 ++-------- src/AnimeClient/Model/MediaTrait.php | 8 +++- src/AnimeClient/Model/Settings.php | 4 ++ src/AnimeClient/UrlGenerator.php | 4 +- src/Ion/Type/ArrayType.php | 20 ++++----- 8 files changed, 58 insertions(+), 54 deletions(-) diff --git a/src/AnimeClient/API/Kitsu/MutationTrait.php b/src/AnimeClient/API/Kitsu/MutationTrait.php index 846b04a3..0fbc30b4 100644 --- a/src/AnimeClient/API/Kitsu/MutationTrait.php +++ b/src/AnimeClient/API/Kitsu/MutationTrait.php @@ -32,8 +32,7 @@ trait MutationTrait { * Create a list item * * @param array $data - * @return Request - * @throws InvalidArgumentException + * @return Request|null */ public function createListItem(array $data): ?Request { diff --git a/src/AnimeClient/Kitsu.php b/src/AnimeClient/Kitsu.php index a1d0d18a..ddd3c717 100644 --- a/src/AnimeClient/Kitsu.php +++ b/src/AnimeClient/Kitsu.php @@ -405,7 +405,7 @@ final class Kitsu { if (empty($parts)) { - return $last; + return ($last !== NULL) ? $last : ''; } return (count($parts) > 1) diff --git a/src/AnimeClient/Model/AnimeCollection.php b/src/AnimeClient/Model/AnimeCollection.php index e489dc5a..6802d0d8 100644 --- a/src/AnimeClient/Model/AnimeCollection.php +++ b/src/AnimeClient/Model/AnimeCollection.php @@ -50,6 +50,11 @@ final class AnimeCollection extends Collection { */ public function getCollection(): array { + if ($this->db === NULL) + { + return []; + } + $rawCollection = $this->getCollectionFromDatabase(); $collection = []; @@ -76,7 +81,7 @@ final class AnimeCollection extends Collection { */ public function getFlatCollection(): array { - if ( ! $this->validDatabase) + if ($this->db === NULL) { return []; } @@ -122,7 +127,7 @@ final class AnimeCollection extends Collection { */ public function getMediaTypeList(): array { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return []; } @@ -174,7 +179,7 @@ final class AnimeCollection extends Collection { */ public function add(mixed $data): void { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return; } @@ -213,7 +218,7 @@ final class AnimeCollection extends Collection { */ public function wasAdded(array $data): bool { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return FALSE; } @@ -231,7 +236,7 @@ final class AnimeCollection extends Collection { */ public function update(array $data): void { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return; } @@ -270,7 +275,7 @@ final class AnimeCollection extends Collection { */ public function wasUpdated(array $data): bool { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return FALSE; } @@ -301,7 +306,7 @@ final class AnimeCollection extends Collection { */ public function delete(array $data): void { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return; } @@ -332,7 +337,7 @@ final class AnimeCollection extends Collection { */ public function wasDeleted(array $data): bool { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return FALSE; } @@ -348,7 +353,7 @@ final class AnimeCollection extends Collection { */ public function get(int|string $kitsuId): array { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return []; } @@ -389,7 +394,7 @@ final class AnimeCollection extends Collection { */ public function has(int|string $kitsuId): bool { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return FALSE; } @@ -411,7 +416,7 @@ final class AnimeCollection extends Collection { */ public function getGenreList(array $filter = []): array { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return []; } @@ -479,7 +484,7 @@ final class AnimeCollection extends Collection { */ public function getMediaList(array $filter = []): array { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return []; } @@ -541,6 +546,11 @@ final class AnimeCollection extends Collection { private function updateMediaLink(string $animeId, array $media): void { + if ($this->db === NULL) + { + return; + } + $this->db->beginTransaction(); // Delete the old entries @@ -570,7 +580,7 @@ final class AnimeCollection extends Collection { */ private function updateGenres($animeId): void { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return; } @@ -604,13 +614,13 @@ final class AnimeCollection extends Collection { } } - if ( ! empty($linksToInsert)) + if ($this->db !== NULL && ! empty($linksToInsert)) { try { $this->db->insertBatch('anime_set_genre_link', $linksToInsert); } - catch (PDOException $e) {} + catch (PDOException) {} } } @@ -621,7 +631,7 @@ final class AnimeCollection extends Collection { */ private function addNewGenres(array $genres): void { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return; } @@ -663,7 +673,7 @@ final class AnimeCollection extends Collection { private function getExistingGenres(): array { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return []; } @@ -693,7 +703,7 @@ final class AnimeCollection extends Collection { private function getExistingGenreLinkEntries(): array { - if ($this->validDatabase === FALSE) + if ($this->db === NULL) { return []; } @@ -734,7 +744,7 @@ final class AnimeCollection extends Collection { */ private function getCollectionFromDatabase(): array { - if ( ! $this->validDatabase) + if ($this->db === NULL) { return []; } diff --git a/src/AnimeClient/Model/Collection.php b/src/AnimeClient/Model/Collection.php index ae63d8f2..d369af3f 100644 --- a/src/AnimeClient/Model/Collection.php +++ b/src/AnimeClient/Model/Collection.php @@ -29,15 +29,9 @@ class Collection extends DB { /** * The query builder object - * @var QueryBuilderInterface + * @var QueryBuilderInterface|null */ - protected QueryBuilderInterface $db; - - /** - * Whether the database is valid for querying - * @var boolean - */ - protected bool $validDatabase = FALSE; + protected ?QueryBuilderInterface $db; /** * Create a new collection object @@ -51,9 +45,8 @@ class Collection extends DB { try { $this->db = Query($this->dbConfig); - $this->validDatabase = TRUE; } - catch (PDOException $e) + catch (PDOException) { $this->db = Query([ 'type' => 'sqlite', @@ -71,16 +64,8 @@ class Collection extends DB { { $rawFile = file_get_contents($dbFileName); $dbFile = ($rawFile !== FALSE) ? $rawFile : ''; - $this->validDatabase = str_starts_with($dbFile, 'SQLite format 3'); + $this->db = (str_starts_with($dbFile, 'SQLite format 3')) ? $this->db : NULL; } - else - { - $this->validDatabase = FALSE; - } - } - else if ($this->db === NULL) - { - $this->validDatabase = FALSE; } } } diff --git a/src/AnimeClient/Model/MediaTrait.php b/src/AnimeClient/Model/MediaTrait.php index a844115c..14e2535c 100644 --- a/src/AnimeClient/Model/MediaTrait.php +++ b/src/AnimeClient/Model/MediaTrait.php @@ -100,7 +100,13 @@ trait MediaTrait { public function createLibraryItem(array $data): bool { $requester = new ParallelAPIRequest(); - $requester->addRequest($this->kitsuModel->createListItem($data), 'kitsu'); + $kitsuRequest = $this->kitsuModel->createListItem($data); + if ($kitsuRequest === NULL) + { + return FALSE; + } + + $requester->addRequest($kitsuRequest, 'kitsu'); if ($this->anilistEnabled && $data['mal_id'] !== null) { diff --git a/src/AnimeClient/Model/Settings.php b/src/AnimeClient/Model/Settings.php index fc7e600b..13bacb76 100644 --- a/src/AnimeClient/Model/Settings.php +++ b/src/AnimeClient/Model/Settings.php @@ -124,6 +124,10 @@ final class Settings { public function validateSettings(array $settings): array { $cfg = Config::check($settings); + if ( ! is_iterable($cfg)) + { + return []; + } $looseConfig = []; $keyedConfig = []; diff --git a/src/AnimeClient/UrlGenerator.php b/src/AnimeClient/UrlGenerator.php index 77731bbf..19cb7262 100644 --- a/src/AnimeClient/UrlGenerator.php +++ b/src/AnimeClient/UrlGenerator.php @@ -72,7 +72,7 @@ class UrlGenerator extends RoutingBase { { $path = trim($path, '/'); - $path = preg_replace('`{/.*?}`i', '', $path); + $path = preg_replace('`{/.*?}`i', '', $path) ?? ""; // Remove any optional parameters from the route // and replace them with existing route parameters, if they exist @@ -87,7 +87,7 @@ class UrlGenerator extends RoutingBase { $segments[$i + 1] = ''; } - $pathSegments[$i] = preg_replace('`{.*?}`', $segments[$i + 1], $pathSegments[$i]); + $pathSegments[$i] = preg_replace('`{.*?}`', $segments[$i + 1], $pathSegments[$i] ?? ''); } $path = implode('/', $pathSegments); diff --git a/src/Ion/Type/ArrayType.php b/src/Ion/Type/ArrayType.php index cb3a9353..03050dbe 100644 --- a/src/Ion/Type/ArrayType.php +++ b/src/Ion/Type/ArrayType.php @@ -101,7 +101,7 @@ class ArrayType { * @return mixed * @throws InvalidArgumentException */ - public function __call(string $method, array $args) + public function __call(string $method, array $args): mixed { // Simple mapping for the majority of methods if (array_key_exists($method, $this->nativeMethods)) @@ -128,7 +128,7 @@ class ArrayType { * @param int|string|array $key * @return bool */ - public function hasKey($key): bool + public function hasKey(int|string|array $key): bool { if (\is_array($key)) { @@ -158,7 +158,7 @@ class ArrayType { * @param mixed $value * @return array */ - public function fill(int $start_index, int $num, $value): array + public function fill(int $start_index, int $num, mixed $value): array { return array_fill($start_index, $num, $value); } @@ -179,9 +179,9 @@ class ArrayType { * * @param mixed $value * @param bool $strict - * @return false|integer|string + * @return false|integer|string|null */ - public function search($value, bool $strict = TRUE) + public function search(mixed $value, bool $strict = TRUE): int|string|false|null { return array_search($value, $this->arr, $strict); } @@ -193,7 +193,7 @@ class ArrayType { * @param bool $strict * @return bool */ - public function has($value, bool $strict = TRUE): bool + public function has(mixed $value, bool $strict = TRUE): bool { return \in_array($value, $this->arr, $strict); } @@ -204,7 +204,7 @@ class ArrayType { * @param string|integer|null $key * @return mixed */ - public function &get($key = NULL) + public function &get(string|int|null $key = NULL): mixed { $value = NULL; if ($key === NULL) @@ -229,7 +229,7 @@ class ArrayType { * @param mixed $value * @return ArrayType */ - public function set($key, $value): ArrayType + public function set(mixed $key, mixed $value): ArrayType { $this->arr[$key] = $value; return $this; @@ -244,7 +244,7 @@ class ArrayType { * @param array $key An array of keys of the array * @return mixed */ - public function &getDeepKey(array $key) + public function &getDeepKey(array $key): mixed { $pos =& $this->arr; @@ -273,7 +273,7 @@ class ArrayType { * @param mixed $value * @return array */ - public function setDeepKey(array $key, $value): array + public function setDeepKey(array $key, mixed $value): array { $pos =& $this->arr;