diff --git a/composer.json b/composer.json index 39f80afd..e8037b52 100644 --- a/composer.json +++ b/composer.json @@ -60,6 +60,7 @@ "clean": "vendor/bin/robo clean", "coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build", "phpstan": "phpstan analyse -c phpstan.neon", + "psalm": "vendor/bin/psalm", "watch:css": "cd public && npm run watch:css", "watch:js": "cd public && npm run watch:js", "test": "vendor/bin/phpunit -c build --no-coverage", diff --git a/phpstan.neon b/phpstan.neon index 46125efa..7f460fb1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,12 +1,12 @@ parameters: checkGenericClassInNonGenericObjectType: false + checkMissingIterableValueType: false inferPrivatePropertyTypeFromConstructor: true level: 7 autoload_files: - %rootDir%/../../../tests/mocks.php paths: - src - - tests - ./console - index.php ignoreErrors: diff --git a/psalm.xml b/psalm.xml index e2cedae2..42f355b2 100644 --- a/psalm.xml +++ b/psalm.xml @@ -15,5 +15,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/API/Kitsu/Transformer/CharacterTransformer.php b/src/API/Kitsu/Transformer/CharacterTransformer.php index 963fcf03..32a90bfa 100644 --- a/src/API/Kitsu/Transformer/CharacterTransformer.php +++ b/src/API/Kitsu/Transformer/CharacterTransformer.php @@ -28,8 +28,9 @@ final class CharacterTransformer extends AbstractTransformer { /** * @param array $characterData + * @return Character */ - public function transform(array $characterData): Character + public function transform($characterData): Character { $data = JsonAPI::organizeData($characterData); $attributes = $data[0]['attributes']; diff --git a/src/API/Kitsu/Transformer/PersonTransformer.php b/src/API/Kitsu/Transformer/PersonTransformer.php index e07876f0..52ee5f60 100644 --- a/src/API/Kitsu/Transformer/PersonTransformer.php +++ b/src/API/Kitsu/Transformer/PersonTransformer.php @@ -25,7 +25,11 @@ use Aviat\Ion\Transformer\AbstractTransformer; */ final class PersonTransformer extends AbstractTransformer { - public function transform(array $personData): Person + /** + * @param array|object $personData + * @return Person + */ + public function transform($personData): Person { $data = JsonAPI::organizeData($personData); $included = JsonAPI::organizeIncludes($personData['included']); diff --git a/src/API/Kitsu/Transformer/UserTransformer.php b/src/API/Kitsu/Transformer/UserTransformer.php index 96e58578..3ad8f242 100644 --- a/src/API/Kitsu/Transformer/UserTransformer.php +++ b/src/API/Kitsu/Transformer/UserTransformer.php @@ -24,9 +24,12 @@ use Aviat\Ion\Transformer\AbstractTransformer; /** * Transform user profile data for display + * + * @param array|object $profileData + * @return User */ final class UserTransformer extends AbstractTransformer { - public function transform(array $profileData): User + public function transform($profileData): User { $orgData = JsonAPI::organizeData($profileData)[0]; $attributes = $orgData['attributes']; diff --git a/src/Model/AnimeCollection.php b/src/Model/AnimeCollection.php index 3fa620b8..3cb63220 100644 --- a/src/Model/AnimeCollection.php +++ b/src/Model/AnimeCollection.php @@ -75,6 +75,11 @@ final class AnimeCollection extends Collection { */ public function getMediaTypeList(): array { + if ($this->validDatabase === FALSE) + { + return []; + } + $output = []; $query = $this->db->select('id, type') @@ -136,6 +141,11 @@ final class AnimeCollection extends Collection { */ public function add($data): void { + if ($this->validDatabase === FALSE) + { + return; + } + $id = $data['id']; // Check that the anime doesn't already exist @@ -166,13 +176,16 @@ final class AnimeCollection extends Collection { /** * Verify that an item was added * - * @param $data * @param array|null|object $data - * * @return bool */ public function wasAdded($data): bool { + if ($this->validDatabase === FALSE) + { + return FALSE; + } + $row = $this->get($data['id']); return ! empty($row); @@ -186,6 +199,11 @@ final class AnimeCollection extends Collection { */ public function update($data): void { + if ($this->validDatabase === FALSE) + { + return; + } + // If there's no id to update, don't update if ( ! array_key_exists('hummingbird_id', $data)) { @@ -206,13 +224,17 @@ final class AnimeCollection extends Collection { /** * Verify that the collection item was updated * - * @param $data * @param array|null|object $data * * @return bool */ public function wasUpdated($data): bool { + if ($this->validDatabase === FALSE) + { + return FALSE; + } + $row = $this->get($data['hummingbird_id']); foreach ($data as $key => $value) @@ -234,6 +256,11 @@ final class AnimeCollection extends Collection { */ public function delete($data): void { + if ($this->validDatabase === FALSE) + { + return; + } + // If there's no id to update, don't delete if ( ! array_key_exists('hummingbird_id', $data)) { @@ -249,9 +276,15 @@ final class AnimeCollection extends Collection { /** * @param array|null|object $data + * @return bool */ public function wasDeleted($data): bool { + if ($this->validDatabase === FALSE) + { + return FALSE; + } + $animeRow = $this->get($data['hummingbird_id']); return empty($animeRow); @@ -265,6 +298,11 @@ final class AnimeCollection extends Collection { */ public function get($kitsuId) { + if ($this->validDatabase === FALSE) + { + return FALSE; + } + $query = $this->db->from('anime_set') ->where('hummingbird_id', $kitsuId) ->get(); @@ -280,6 +318,11 @@ final class AnimeCollection extends Collection { */ public function getGenreList(array $filter = []): array { + if ($this->validDatabase === FALSE) + { + return []; + } + $output = []; // Catch the missing table PDOException @@ -337,6 +380,11 @@ final class AnimeCollection extends Collection { */ private function updateGenre($animeId): void { + if ($this->validDatabase === FALSE) + { + return; + } + // Get api information $anime = $this->animeModel->getAnimeById($animeId); @@ -379,6 +427,11 @@ final class AnimeCollection extends Collection { */ private function addNewGenres(array $genres): void { + if ($this->validDatabase === FALSE) + { + return; + } + $existingGenres = $this->getExistingGenres(); $newGenres = array_diff($genres, $existingGenres); @@ -416,6 +469,11 @@ final class AnimeCollection extends Collection { private function getExistingGenres(): array { + if ($this->validDatabase === FALSE) + { + return []; + } + $genres = []; // Get existing genres @@ -435,6 +493,11 @@ final class AnimeCollection extends Collection { private function getExistingGenreLinkEntries(): array { + if ($this->validDatabase === FALSE) + { + return []; + } + $links = []; $query = $this->db->select('hummingbird_id, genre_id') diff --git a/src/Model/Collection.php b/src/Model/Collection.php index 79cae2cf..27641518 100644 --- a/src/Model/Collection.php +++ b/src/Model/Collection.php @@ -19,6 +19,7 @@ namespace Aviat\AnimeClient\Model; use Aviat\Ion\Di\ContainerInterface; use PDOException; +use Query\Query_Builder_Interface; use function Query; /** @@ -28,7 +29,7 @@ class Collection extends DB { /** * The query builder object - * @var \Query\Query_Builder_Interface + * @var Query_Builder_Interface */ protected $db; @@ -52,7 +53,13 @@ class Collection extends DB { $this->db = Query($this->dbConfig); $this->validDatabase = TRUE; } - catch (PDOException $e) {} + catch (PDOException $e) + { + $this->db = Query([ + 'type' => 'sqlite', + 'file' => ':memory:', + ]); + } // Is database valid? If not, set a flag so the // app can be run without a valid database diff --git a/src/Model/MangaCollection.php b/src/Model/MangaCollection.php index d652ee0b..1f5a4c65 100644 --- a/src/Model/MangaCollection.php +++ b/src/Model/MangaCollection.php @@ -17,7 +17,6 @@ namespace Aviat\AnimeClient\Model; use Aviat\Ion\Di\ContainerInterface; -use PDO; /** * Model for getting anime collection data @@ -40,249 +39,5 @@ final class MangaCollection extends Collection { parent::__construct($container); $this->mangaModel = $container->get('manga-model'); } - - /** - * Get collection from the database, and organize by media type - * - * @return array - */ - public function getCollection(): array - { - $rawCollection = $this->getCollectionFromDatabase(); - - $collection = []; - - foreach ($rawCollection as $row) - { - if (array_key_exists($row['media'], $collection)) - { - $collection[$row['media']][] = $row; - } - else - { - $collection[$row['media']] = [$row]; - } - } - - return $collection; - } - - /** - * Get list of media types - * - * @return array - */ - public function getMediaTypeList(): array - { - $output = []; - - $query = $this->db->select('id, type') - ->from('media') - ->get(); - - foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) - { - $output[$row['id']] = $row['type']; - } - - return $output; - } - - /** - * Get full collection from the database - * - * @return array - */ - private function getCollectionFromDatabase(): array - { - if ( ! $this->validDatabase) - { - return []; - } - - $query = $this->db->select('hummingbird_id, slug, title, alternate_title, show_type, - age_rating, episode_count, episode_length, cover_image, notes, media.type as media') - ->from('manga_set a') - ->join('media', 'media.id=a.media_id', 'inner') - ->order_by('media') - ->order_by('title') - ->get(); - - return $query->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * Add an item to the anime collection - * - * @param array $data - * @return void - */ - public function add($data): void - { - $anime = (object)$this->mangaModel->getMangaById($data['id']); - $this->db->set([ - 'hummingbird_id' => $data['id'], - 'slug' => $anime->slug, - 'title' => array_shift($anime->titles), - 'alternate_title' => implode('
', $anime->titles), - 'show_type' => $anime->show_type, - 'age_rating' => $anime->age_rating, - 'cover_image' => $anime->cover_image, - 'episode_count' => $anime->episode_count, - 'episode_length' => $anime->episode_length, - 'media_id' => $data['media_id'], - 'notes' => $data['notes'] - ])->insert('manga_set'); - - $this->updateGenre($data['id']); - } - - /** - * Update a collection item - * - * @param array $data - * @return void - */ - public function update($data): void - { - // If there's no id to update, don't update - if ( ! array_key_exists('hummingbird_id', $data)) - { - return; - } - - $id = $data['hummingbird_id']; - unset($data['hummingbird_id']); - - $this->db->set($data) - ->where('hummingbird_id', $id) - ->update('manga_set'); - } - - /** - * Remove a collection item - * - * @param array $data - * @return void - */ - public function delete($data): void - { - // If there's no id to update, don't delete - if ( ! array_key_exists('hummingbird_id', $data)) - { - return; - } - - $this->db->where('hummingbird_id', $data['hummingbird_id']) - ->delete('genre_manga_set_link'); - - $this->db->where('hummingbird_id', $data['hummingbird_id']) - ->delete('manga_set'); - } - - /** - * Get the details of a collection item - * - * @param string $kitsuId - * @return array - */ - public function get($kitsuId): array - { - $query = $this->db->from('manga_set') - ->where('hummingbird_id', $kitsuId) - ->get(); - - return $query->fetch(PDO::FETCH_ASSOC); - } - - /** - * Update genre information for selected manga - * - * @param string $mangaId The current manga - * @return void - */ - private function updateGenre($mangaId): void - { - $genreInfo = $this->getGenreData(); - $genres = $genreInfo['genres']; - $links = $genreInfo['links']; - - // Get api information - $manga = $this->mangaModel->getMangaById($mangaId); - - foreach ($manga['genres'] as $genre) - { - // Add genres that don't currently exist - if ( ! \in_array($genre, $genres, TRUE)) - { - $this->db->set('genre', $genre) - ->insert('genres'); - - $genres[] = $genre; - } - - // Update link table - // Get id of genre to put in link table - $flippedGenres = array_flip($genres); - - $insertArray = [ - 'hummingbird_id' => $mangaId, - 'genre_id' => $flippedGenres[$genre] - ]; - - if (array_key_exists($mangaId, $links)) - { - if ( ! \in_array($flippedGenres[$genre], $links[$mangaId], TRUE)) - { - $this->db->set($insertArray)->insert('genre_manga_set_link'); - } - } - else - { - $this->db->set($insertArray)->insert('genre_manga_set_link'); - } - } - } - - /** - * Get list of existing genres - * - * @return array - */ - private function getGenreData(): array - { - $genres = []; - $links = []; - - // Get existing genres - $query = $this->db->select('id, genre') - ->from('genres') - ->get(); - foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $genre) - { - $genres[$genre['id']] = $genre['genre']; - } - - // Get existing link table entries - $query = $this->db->select('hummingbird_id, genre_id') - ->from('genre_manga_set_link') - ->get(); - foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $link) - { - if (array_key_exists($link['hummingbird_id'], $links)) - { - $links[$link['hummingbird_id']][] = $link['genre_id']; - } - else - { - $links[$link['hummingbird_id']] = [$link['genre_id']]; - } - } - - return [ - 'genres' => $genres, - 'links' => $links - ]; - } } // End of MangaCollectionModel.php \ No newline at end of file diff --git a/src/Types/AbstractType.php b/src/Types/AbstractType.php index 6f66eb93..e12aae00 100644 --- a/src/Types/AbstractType.php +++ b/src/Types/AbstractType.php @@ -179,7 +179,7 @@ abstract class AbstractType implements ArrayAccess, Countable { /** * Recursively cast properties to an array * - * @param null $parent + * @param mixed $parent * @return mixed */ public function toArray($parent = null) diff --git a/src/Types/Anime.php b/src/Types/Anime.php index f49af8e4..9b2b517d 100644 --- a/src/Types/Anime.php +++ b/src/Types/Anime.php @@ -38,12 +38,12 @@ class Anime extends AbstractType { public $cover_image; /** - * @var string|number + * @var string|int */ public $episode_count; /** - * @var string|number + * @var string|int */ public $episode_length; diff --git a/src/Types/AnimeListItem.php b/src/Types/AnimeListItem.php index 4449ab84..ece92c86 100644 --- a/src/Types/AnimeListItem.php +++ b/src/Types/AnimeListItem.php @@ -74,12 +74,12 @@ final class AnimeListItem extends AbstractType { public $rewatching; /** - * @var number + * @var int */ public $rewatched; /** - * @var number + * @var int */ public $user_rating; diff --git a/src/Types/Character.php b/src/Types/Character.php index 906d58cc..a97dd974 100644 --- a/src/Types/Character.php +++ b/src/Types/Character.php @@ -41,7 +41,7 @@ final class Character extends AbstractType { public $included; /** - * @var array + * @var Media */ public $media; @@ -62,9 +62,6 @@ final class Character extends AbstractType { public function setMedia ($media): void { - $this->media = new class($media) extends AbstractType { - public $anime; - public $manga; - }; + $this->media = new Media($media); } } \ No newline at end of file diff --git a/src/Types/Characters.php b/src/Types/Characters.php new file mode 100644 index 00000000..390549ed --- /dev/null +++ b/src/Types/Characters.php @@ -0,0 +1,29 @@ + + * @copyright 2015 - 2019 Timothy J. Warren + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 4.2 + * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient + */ + +namespace Aviat\AnimeClient\Types; + +final class Characters extends AbstractType { + /** + * @var array + */ + public $main; + + /** + * @var array + */ + public $supporting; +} \ No newline at end of file diff --git a/src/Types/Config/Database.php b/src/Types/Config/Database.php index 3f761ead..ddf97de1 100644 --- a/src/Types/Config/Database.php +++ b/src/Types/Config/Database.php @@ -40,7 +40,7 @@ class Database extends AbstractType { public $pass; /** - * @var string|number + * @var string|int */ public $port; diff --git a/src/Types/Media.php b/src/Types/Media.php new file mode 100644 index 00000000..241de436 --- /dev/null +++ b/src/Types/Media.php @@ -0,0 +1,29 @@ + + * @copyright 2015 - 2019 Timothy J. Warren + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 4.2 + * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient + */ + +namespace Aviat\AnimeClient\Types; + +final class Media extends AbstractType { + /** + * @var array + */ + public $anime = []; + + /** + * @var array + */ + public $manga = []; +} \ No newline at end of file diff --git a/src/Types/Person.php b/src/Types/Person.php index 71984222..b1baac67 100644 --- a/src/Types/Person.php +++ b/src/Types/Person.php @@ -31,7 +31,7 @@ final class Person extends AbstractType { public $name; /** - * @var object + * @var Characters */ public $characters; @@ -42,9 +42,6 @@ final class Person extends AbstractType { public function setCharacters($characters): void { - $this->characters = new class($characters) extends AbstractType { - public $main; - public $supporting; - }; + $this->characters = new Characters($characters); } } \ No newline at end of file