From e4018c269f3cd2c7ca04fd285e76b2c37acec2e3 Mon Sep 17 00:00:00 2001 From: Timothy J Warren Date: Fri, 8 May 2020 19:15:21 -0400 Subject: [PATCH] Simplify caching --- .gitignore | 4 +- app/bootstrap.php | 9 +- app/config/cache.toml.example | 2 +- composer.json | 2 +- index.php | 2 +- src/AnimeClient/API/CacheTrait.php | 45 ++++- src/AnimeClient/API/Kitsu.php | 2 + src/AnimeClient/API/Kitsu/Auth.php | 107 ++++------ src/AnimeClient/API/Kitsu/Model.php | 79 +++++--- .../Transformer/CharacterTransformer.php | 2 +- .../Kitsu/Transformer/MangaTransformer.php | 8 +- src/AnimeClient/AnimeClient.php | 36 +++- src/AnimeClient/Command/BaseCommand.php | 189 +++++++++--------- src/AnimeClient/Command/CacheClear.php | 14 +- src/AnimeClient/Command/CachePrime.php | 25 +-- src/AnimeClient/Controller.php | 23 +-- 16 files changed, 300 insertions(+), 249 deletions(-) diff --git a/.gitignore b/.gitignore index cab47bdd..9056ec31 100644 --- a/.gitignore +++ b/.gitignore @@ -146,4 +146,6 @@ public/images/manga/** public/images/characters/** public/images/people/** public/mal_mappings.json -.phpunit.result.cache \ No newline at end of file +.phpunit.result.cache + +.is-dev \ No newline at end of file diff --git a/app/bootstrap.php b/app/bootstrap.php index 02dd99c7..34068f1b 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -25,10 +25,11 @@ use Aviat\AnimeClient\API\{ Kitsu\KitsuRequestBuilder }; use Aviat\AnimeClient\Model; -use Aviat\Banker\Pool; +use Aviat\Banker\Teller; use Aviat\Ion\Config; use Aviat\Ion\Di\Container; use Aviat\Ion\Di\ContainerInterface; +use Psr\SimpleCache\CacheInterface; use Laminas\Diactoros\{Response, ServerRequestFactory}; use Monolog\Handler\RotatingFileHandler; use Monolog\Logger; @@ -64,10 +65,10 @@ return static function (array $configArray = []): Container { $container->set('config', fn () => new Config($configArray)); // Create Cache Object - $container->set('cache', static function(ContainerInterface $container): Pool { + $container->set('cache', static function(ContainerInterface $container): CacheInterface { $logger = $container->getLogger(); $config = $container->get('config')->get('cache'); - return new Pool($config, $logger); + return new Teller($config, $logger); }); // Create Aura Router Object @@ -113,7 +114,7 @@ return static function (array $configArray = []): Container { // Models $container->set('kitsu-model', static function(ContainerInterface $container): Kitsu\Model { - $requestBuilder = new KitsuRequestBuilder(); + $requestBuilder = new KitsuRequestBuilder($container); $requestBuilder->setLogger($container->getLogger('kitsu-request')); $listItem = new Kitsu\ListItem(); diff --git a/app/config/cache.toml.example b/app/config/cache.toml.example index 6de18499..74935015 100644 --- a/app/config/cache.toml.example +++ b/app/config/cache.toml.example @@ -4,7 +4,7 @@ # See https://git.timshomepage.net/aviat/banker for more information -# Available drivers are apcu, memcache, memcached, redis or null +# Available drivers are memcached, redis or null # Null cache driver means no caching driver = "redis" diff --git a/composer.json b/composer.json index b4b2c23c..cea18548 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "aura/html": "^2.5.0", "aura/router": "^3.1.0", "aura/session": "^2.1.0", - "aviat/banker": "^2.0.0", + "aviat/banker": "^3.1.1", "aviat/query": "^3.0.0", "danielstjules/stringy": "^3.1.0", "ext-dom": "*", diff --git a/index.php b/index.php index 93f533d9..be33f4aa 100644 --- a/index.php +++ b/index.php @@ -27,7 +27,7 @@ setlocale(LC_CTYPE, 'en_US'); // Load composer autoloader require_once __DIR__ . '/vendor/autoload.php'; -if (array_key_exists('ENV', $_SERVER) && $_SERVER['ENV'] === 'development') +if (file_exists('.is-dev')) { $whoops = new Run; $whoops->pushHandler(new PrettyPageHandler); diff --git a/src/AnimeClient/API/CacheTrait.php b/src/AnimeClient/API/CacheTrait.php index bc1e945d..dbb01271 100644 --- a/src/AnimeClient/API/CacheTrait.php +++ b/src/AnimeClient/API/CacheTrait.php @@ -16,7 +16,8 @@ namespace Aviat\AnimeClient\API; -use Aviat\Banker\Pool; +use Psr\SimpleCache\CacheInterface; +use Psr\SimpleCache\InvalidArgumentException; /** * Helper methods for dealing with the Cache @@ -24,17 +25,17 @@ use Aviat\Banker\Pool; trait CacheTrait { /** - * @var Pool + * @var CacheInterface */ - protected Pool $cache; + protected CacheInterface $cache; /** * Inject the cache object * - * @param Pool $cache + * @param CacheInterface $cache * @return $this */ - public function setCache(Pool $cache): self + public function setCache(CacheInterface $cache): self { $this->cache = $cache; return $this; @@ -43,13 +44,41 @@ trait CacheTrait { /** * Get the cache object if it exists * - * @return Pool + * @return CacheInterface */ - public function getCache(): Pool + public function getCache(): CacheInterface { return $this->cache; } + /** + * Get the cached value if it exists, otherwise set the cache value + * and return it. + * + * @param string $key + * @param callable $primer + * @param array $primeArgs + * @return mixed|null + * @throws InvalidArgumentException + */ + public function getCached(string $key, callable $primer, ?array $primeArgs = []) + { + $value = $this->cache->get($key, NULL); + + if ($value === NULL) + { + $value = $primer(...$primeArgs); + if ($value === NULL) + { + return NULL; + } + + $this->cache->set($key, $value); + } + + return $value; + } + /** * Generate a hash as a cache key from the current method call * @@ -61,7 +90,7 @@ trait CacheTrait { public function getHashForMethodCall($object, string $method, array $args = []): string { $keyObj = [ - 'class' => \get_class($object), + 'class' => get_class($object), 'method' => $method, 'args' => $args, ]; diff --git a/src/AnimeClient/API/Kitsu.php b/src/AnimeClient/API/Kitsu.php index 40268466..409c905a 100644 --- a/src/AnimeClient/API/Kitsu.php +++ b/src/AnimeClient/API/Kitsu.php @@ -28,6 +28,8 @@ final class Kitsu { public const AUTH_TOKEN_CACHE_KEY = 'kitsu-auth-token'; public const AUTH_TOKEN_EXP_CACHE_KEY = 'kitsu-auth-token-expires'; public const AUTH_TOKEN_REFRESH_CACHE_KEY = 'kitsu-auth-token-refresh'; + public const ANIME_HISTORY_LIST_CACHE_KEY = 'kitsu-anime-history-list'; + public const MANGA_HISTORY_LIST_CACHE_KEY = 'kitsu-manga-history-list'; /** * Determine whether an anime is airing, finished airing, or has not yet aired diff --git a/src/AnimeClient/API/Kitsu/Auth.php b/src/AnimeClient/API/Kitsu/Auth.php index 729aa5a7..cfb5c38b 100644 --- a/src/AnimeClient/API/Kitsu/Auth.php +++ b/src/AnimeClient/API/Kitsu/Auth.php @@ -18,7 +18,6 @@ namespace Aviat\AnimeClient\API\Kitsu; use Aura\Session\Segment; -use Aviat\Banker\Exception\InvalidArgumentException; use const Aviat\AnimeClient\SESSION_SEGMENT; use Aviat\AnimeClient\API\{ @@ -29,8 +28,9 @@ use Aviat\Ion\Di\{ContainerAware, ContainerInterface}; use Aviat\Ion\Di\Exception\{ContainerException, NotFoundException}; use Aviat\Ion\Event; +use Psr\SimpleCache\InvalidArgumentException; + use Throwable; -use const PHP_SAPI; /** * Kitsu API Authentication @@ -77,7 +77,6 @@ final class Auth { * * @param string $password * @return boolean - * @throws InvalidArgumentException * @throws Throwable */ public function authenticate(string $password): bool @@ -95,12 +94,11 @@ final class Auth { * * @param string $refreshToken * @return boolean - * @throws InvalidArgumentException - * @throws Throwable + * @throws Throwable|InvalidArgumentException */ public function reAuthenticate(?string $refreshToken): bool { - $refreshToken ??= $this->getAuthToken(); + $refreshToken ??= $this->getRefreshToken(); if (empty($refreshToken)) { @@ -116,6 +114,7 @@ final class Auth { * Check whether the current user is authenticated * * @return boolean + * @throws InvalidArgumentException */ public function isAuthenticated(): bool { @@ -136,87 +135,59 @@ final class Auth { * Retrieve the authentication token from the session * * @return string + * @throws InvalidArgumentException */ - private function getAuthToken(): ?string + public function getAuthToken(): ?string { - $now = time(); - - if (PHP_SAPI === 'cli') - { - $token = $this->cacheGet(K::AUTH_TOKEN_CACHE_KEY, NULL); - $refreshToken = $this->cacheGet(K::AUTH_TOKEN_REFRESH_CACHE_KEY, NULL); - $expireTime = $this->cacheGet(K::AUTH_TOKEN_EXP_CACHE_KEY); - $isExpired = $now > $expireTime; - } - else - { - $token = $this->segment->get('auth_token', NULL); - $refreshToken = $this->segment->get('refresh_token', NULL); - $isExpired = $now > $this->segment->get('auth_token_expires', $now + 5000); - } - - // Attempt to re-authenticate with refresh token - /* if ($isExpired === TRUE && $refreshToken !== NULL) - { - if ($this->reAuthenticate($refreshToken) !== NULL) - { - return (PHP_SAPI === 'cli') - ? $this->cacheGet(K::AUTH_TOKEN_CACHE_KEY, NULL) - : $this->segment->get('auth_token', NULL); - } - - return NULL; - }*/ - - return $token; + return $this->segment->get('auth_token', NULL) + ?? $this->cache->get(K::AUTH_TOKEN_CACHE_KEY, NULL); } + /** + * Retrieve the refresh token + * + * @return string|null + * @throws InvalidArgumentException + */ private function getRefreshToken(): ?string { - return (PHP_SAPI === 'cli') - ? $this->cacheGet(K::AUTH_TOKEN_REFRESH_CACHE_KEY, NULL) - : $this->segment->get('refresh_token'); + return $this->segment->get('refresh_token') + ?? $this->cache->get(K::AUTH_TOKEN_REFRESH_CACHE_KEY, NULL); } - private function storeAuth(bool $auth): bool + /** + * Save the new authentication information + * + * @param $auth + * @return bool + * @throws InvalidArgumentException + */ + private function storeAuth($auth): bool { if (FALSE !== $auth) { - // Set the token in the cache for command line operations - $cacheItem = $this->cache->getItem(K::AUTH_TOKEN_CACHE_KEY); - $cacheItem->set($auth['access_token']); - $cacheItem->save(); - - // Set the token expiration in the cache $expire_time = $auth['created_at'] + $auth['expires_in']; - $cacheItem = $this->cache->getItem(K::AUTH_TOKEN_EXP_CACHE_KEY); - $cacheItem->set($expire_time); - $cacheItem->save(); + // Set the token in the cache for command line operations + // Set the token expiration in the cache // Set the refresh token in the cache - $cacheItem = $this->cache->getItem(K::AUTH_TOKEN_REFRESH_CACHE_KEY); - $cacheItem->set($auth['refresh_token']); - $cacheItem->save(); + $saved = $this->cache->setMultiple([ + K::AUTH_TOKEN_CACHE_KEY => $auth['access_token'], + K::AUTH_TOKEN_EXP_CACHE_KEY => $expire_time, + K::AUTH_TOKEN_REFRESH_CACHE_KEY => $auth['refresh_token'], + ]); // Set the session values - $this->segment->set('auth_token', $auth['access_token']); - $this->segment->set('auth_token_expires', $expire_time); - $this->segment->set('refresh_token', $auth['refresh_token']); - return TRUE; + if ($saved) + { + $this->segment->set('auth_token', $auth['access_token']); + $this->segment->set('auth_token_expires', $expire_time); + $this->segment->set('refresh_token', $auth['refresh_token']); + return TRUE; + } } return FALSE; } - - private function cacheGet(string $key, $default = NULL) - { - $cacheItem = $this->cache->getItem($key); - if ( ! $cacheItem->isHit()) - { - return $default; - } - - return $cacheItem->get(); - } } // End of KitsuAuth.php \ No newline at end of file diff --git a/src/AnimeClient/API/Kitsu/Model.php b/src/AnimeClient/API/Kitsu/Model.php index ad02b958..5c5a90e4 100644 --- a/src/AnimeClient/API/Kitsu/Model.php +++ b/src/AnimeClient/API/Kitsu/Model.php @@ -190,11 +190,8 @@ final class Model { $username = $this->getUsername(); } - $cacheItem = $this->cache->getItem(K::AUTH_USER_ID_KEY); - - if ( ! $cacheItem->isHit()) - { - $data = $this->getRequest('users', [ + return $this->getCached(K::AUTH_USER_ID_KEY, function(string $username) { + $data = $this->requestBuilder->getRequest('users', [ 'query' => [ 'filter' => [ 'name' => $username @@ -202,11 +199,8 @@ final class Model { ] ]); - $cacheItem->set($data['data'][0]['id']); - $cacheItem->save(); - } - - return $cacheItem->get(); + return $data['data'][0]['id'] ?? NULL; + }, [$username]); } /** @@ -398,11 +392,23 @@ final class Model { */ public function getAnimeHistory(): array { - $raw = $this->getRawHistoryList('anime'); - $organized = JsonAPI::organizeData($raw); - $organized = array_filter($organized, fn ($item) => array_key_exists('relationships', $item)); + $key = K::ANIME_HISTORY_LIST_CACHE_KEY; + $list = $this->cache->get($key, NULL); - return (new AnimeHistoryTransformer())->transform($organized); + if ($list === NULL) + { + $raw = $this->getRawHistoryList('anime'); + + $organized = JsonAPI::organizeData($raw); + $organized = array_filter($organized, fn ($item) => array_key_exists('relationships', $item)); + + $list = (new AnimeHistoryTransformer())->transform($organized); + + $this->cache->set($key, $list); + + } + + return $list; } /** @@ -426,9 +432,11 @@ final class Model { */ public function getAnimeList(string $status): array { - $cacheItem = $this->cache->getItem("kitsu-anime-list-{$status}"); + $key = "kitsu-anime-list-{$status}"; - if ( ! $cacheItem->isHit()) + $list = $this->cache->get($key, NULL); + + if ($list === NULL) { $data = $this->getRawAnimeList($status) ?? []; @@ -454,11 +462,11 @@ final class Model { $keyed[$item['id']] = $item; } - $cacheItem->set($keyed); - $cacheItem->save(); + $list = $keyed; + $this->cache->set($key, $list); } - return $cacheItem->get(); + return $list; } /** @@ -650,11 +658,21 @@ final class Model { */ public function getMangaHistory(): array { - $raw = $this->getRawHistoryList('manga'); - $organized = JsonAPI::organizeData($raw); - $organized = array_filter($organized, fn ($item) => array_key_exists('relationships', $item)); + $key = K::MANGA_HISTORY_LIST_CACHE_KEY; + $list = $this->cache->get($key, NULL); - return (new MangaHistoryTransformer())->transform($organized); + if ($list === NULL) + { + $raw = $this->getRawHistoryList('manga'); + $organized = JsonAPI::organizeData($raw); + $organized = array_filter($organized, fn ($item) => array_key_exists('relationships', $item)); + + $list = (new MangaHistoryTransformer())->transform($organized); + + $this->cache->set($key, $list); + } + + return $list; } /** @@ -696,11 +714,13 @@ final class Model { ] ]; - $cacheItem = $this->cache->getItem("kitsu-manga-list-{$status}"); + $key = "kitsu-manga-list-{$status}"; - if ( ! $cacheItem->isHit()) + $list = $this->cache->get($key, NULL); + + if ($list === NULL) { - $data = $this->getRequest('library-entries', $options) ?? []; + $data = $this->requestBuilder->getRequest('library-entries', $options) ?? []; // Bail out on no data if (empty($data) || ( ! array_key_exists('included', $data))) @@ -717,13 +737,12 @@ final class Model { } unset($item); - $transformed = $this->mangaListTransformer->transformCollection($data['data']); + $list = $this->mangaListTransformer->transformCollection($data['data']); - $cacheItem->set($transformed); - $cacheItem->save(); + $this->cache->set($key, $list); } - return $cacheItem->get(); + return $list; } /** diff --git a/src/AnimeClient/API/Kitsu/Transformer/CharacterTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/CharacterTransformer.php index 17a259fc..49fb45f7 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/CharacterTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/CharacterTransformer.php @@ -149,7 +149,7 @@ final class CharacterTransformer extends AbstractTransformer { $person = $p['attributes']; $person['id'] = $pid; - $person['image'] = $person['image']['original']; + $person['image'] = $person['image']['original'] ?? ''; uasort($role['relationships']['media']['anime'], static function ($a, $b) { return $a['attributes']['canonicalTitle'] <=> $b['attributes']['canonicalTitle']; diff --git a/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php index c5018318..b697d4d0 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php @@ -98,16 +98,12 @@ final class MangaTransformer extends AbstractTransformer { if ( ! empty($characters['main'])) { - uasort($characters['main'], static function ($a, $b) { - return $a['name'] <=> $b['name']; - }); + uasort($characters['main'], fn ($a, $b) => $a['name'] <=> $b['anime']); } if ( ! empty($characters['supporting'])) { - uasort($characters['supporting'], static function ($a, $b) { - return $a['name'] <=> $b['name']; - }); + uasort($characters['supporting'], fn ($a, $b) => $a['name'] <=> $b['anime']); } ksort($characters); diff --git a/src/AnimeClient/AnimeClient.php b/src/AnimeClient/AnimeClient.php index 4fca5075..42eadf73 100644 --- a/src/AnimeClient/AnimeClient.php +++ b/src/AnimeClient/AnimeClient.php @@ -16,6 +16,9 @@ namespace Aviat\AnimeClient; +use Aviat\AnimeClient\API\Kitsu; +use Psr\SimpleCache\CacheInterface; +use Psr\SimpleCache\InvalidArgumentException; use function Amp\Promise\wait; use Amp\Http\Client\Request; @@ -26,6 +29,8 @@ use Amp\Http\Client\HttpClientBuilder; use Aviat\Ion\ConfigInterface; use Yosymfony\Toml\{Toml, TomlBuilder}; +use Throwable; + // ---------------------------------------------------------------------------- //! TOML Functions // ---------------------------------------------------------------------------- @@ -232,7 +237,7 @@ function getApiClient (): HttpClient * * @param string|Request $request * @return Response - * @throws \Throwable + * @throws Throwable */ function getResponse ($request): Response { @@ -256,7 +261,7 @@ function getResponse ($request): Response */ function getLocalImg ($kitsuUrl, $webp = TRUE): string { - if ( ! is_string($kitsuUrl)) + if (empty($kitsuUrl) || ( ! is_string($kitsuUrl))) { return 'images/placeholder.webp'; } @@ -345,4 +350,31 @@ function col_not_empty(array $search, string $key): bool { $items = array_filter(array_column($search, $key), fn ($x) => ( ! empty($x))); return count($items) > 0; +} + +/** + * Clear the cache, but save user auth data + * + * @param CacheInterface $cache + * @return bool + * @throws InvalidArgumentException + */ +function clearCache(CacheInterface $cache): bool +{ + // Save the user data, if it exists, for priming the cache + $userData = $cache->getMultiple([ + Kitsu::AUTH_USER_ID_KEY, + Kitsu::AUTH_TOKEN_CACHE_KEY, + Kitsu::AUTH_TOKEN_EXP_CACHE_KEY, + Kitsu::AUTH_TOKEN_REFRESH_CACHE_KEY, + ], NULL); + + $userData = array_filter((array)$userData, fn ($value) => $value !== NULL); + $cleared = $cache->clear(); + + $saved = ( ! empty($userData)) + ? $cache->setMultiple($userData) + : TRUE; + + return $cleared && $saved; } \ No newline at end of file diff --git a/src/AnimeClient/Command/BaseCommand.php b/src/AnimeClient/Command/BaseCommand.php index 5f6002cf..7f03656d 100644 --- a/src/AnimeClient/Command/BaseCommand.php +++ b/src/AnimeClient/Command/BaseCommand.php @@ -24,7 +24,7 @@ use Aura\Session\SessionFactory; use Aviat\AnimeClient\{Model, UrlGenerator, Util}; use Aviat\AnimeClient\API\{Anilist, CacheTrait, Kitsu}; use Aviat\AnimeClient\API\Kitsu\KitsuRequestBuilder; -use Aviat\Banker\Pool; +use Aviat\Banker\Teller; use Aviat\Ion\Config; use Aviat\Ion\Di\{Container, ContainerInterface, ContainerAware}; use ConsoleKit\{Colors, Command, ConsoleException}; @@ -129,99 +129,7 @@ abstract class BaseCommand extends Command { $configArray = array_replace_recursive($baseConfig, $config, $overrideConfig); - $di = static function (array $configArray) use ($APP_DIR): Container { - $container = new Container(); - - // ------------------------------------------------------------------------- - // Logging - // ------------------------------------------------------------------------- - - $app_logger = new Logger('animeclient'); - $app_logger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/app-cli.log', Logger::NOTICE)); - - $kitsu_request_logger = new Logger('kitsu-request'); - $kitsu_request_logger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/kitsu_request-cli.log', Logger::NOTICE)); - - $anilistRequestLogger = new Logger('anilist-request'); - $anilistRequestLogger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/anilist_request-cli.log', Logger::NOTICE)); - - $container->setLogger($app_logger); - $container->setLogger($anilistRequestLogger, 'anilist-request'); - $container->setLogger($kitsu_request_logger, 'kitsu-request'); - - // Create Config Object - $container->set('config', fn () => new Config($configArray)); - - // Create Cache Object - $container->set('cache', static function($container) { - $logger = $container->getLogger(); - $config = $container->get('config')->get('cache'); - return new Pool($config, $logger); - }); - - // Create Aura Router Object - $container->set('aura-router', fn () => new RouterContainer); - - // Create Request/Response Objects - $container->set('request', fn () => ServerRequestFactory::fromGlobals( - $_SERVER, - $_GET, - $_POST, - $_COOKIE, - $_FILES - )); - $container->set('response', fn () => new Response); - - // Create session Object - $container->set('session', fn () => (new SessionFactory())->newInstance($_COOKIE)); - - // Models - $container->set('kitsu-model', static function($container): Kitsu\Model { - $requestBuilder = new KitsuRequestBuilder(); - $requestBuilder->setLogger($container->getLogger('kitsu-request')); - - $listItem = new Kitsu\ListItem(); - $listItem->setContainer($container); - $listItem->setRequestBuilder($requestBuilder); - - $model = new Kitsu\Model($listItem); - $model->setContainer($container); - $model->setRequestBuilder($requestBuilder); - - $cache = $container->get('cache'); - $model->setCache($cache); - return $model; - }); - $container->set('anilist-model', static function ($container): Anilist\Model { - $requestBuilder = new Anilist\AnilistRequestBuilder(); - $requestBuilder->setLogger($container->getLogger('anilist-request')); - - $listItem = new Anilist\ListItem(); - $listItem->setContainer($container); - $listItem->setRequestBuilder($requestBuilder); - - $model = new Anilist\Model($listItem); - $model->setContainer($container); - $model->setRequestBuilder($requestBuilder); - - return $model; - }); - $container->set('settings-model', static function($container): Model\Settings { - $model = new Model\Settings($container->get('config')); - $model->setContainer($container); - return $model; - }); - - $container->set('auth', fn ($container) => new Kitsu\Auth($container)); - - $container->set('url-generator', fn ($container) => new UrlGenerator($container)); - - $container->set('util', fn ($container) => new Util($container)); - - return $container; - }; - - return $di($configArray); + return $this->_di($configArray, $APP_DIR); } private function _line(string $message, $fgColor = NULL, $bgColor = NULL): void @@ -229,4 +137,97 @@ abstract class BaseCommand extends Command { $message = Colors::colorize($message, $fgColor, $bgColor); $this->getConsole()->writeln($message); } + + private function _di(array $configArray, string $APP_DIR): ContainerInterface + { + $container = new Container(); + + // ------------------------------------------------------------------------- + // Logging + // ------------------------------------------------------------------------- + + $app_logger = new Logger('animeclient'); + $app_logger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/app-cli.log', Logger::NOTICE)); + + $kitsu_request_logger = new Logger('kitsu-request'); + $kitsu_request_logger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/kitsu_request-cli.log', Logger::NOTICE)); + + $anilistRequestLogger = new Logger('anilist-request'); + $anilistRequestLogger->pushHandler(new RotatingFileHandler($APP_DIR . '/logs/anilist_request-cli.log', Logger::NOTICE)); + + $container->setLogger($app_logger); + $container->setLogger($anilistRequestLogger, 'anilist-request'); + $container->setLogger($kitsu_request_logger, 'kitsu-request'); + + // Create Config Object + $container->set('config', fn () => new Config($configArray)); + + // Create Cache Object + $container->set('cache', static function($container) { + $logger = $container->getLogger(); + $config = $container->get('config')->get('cache'); + return new Teller($config, $logger); + }); + + // Create Aura Router Object + $container->set('aura-router', fn () => new RouterContainer); + + // Create Request/Response Objects + $container->set('request', fn () => ServerRequestFactory::fromGlobals( + $_SERVER, + $_GET, + $_POST, + $_COOKIE, + $_FILES + )); + $container->set('response', fn () => new Response); + + // Create session Object + $container->set('session', fn () => (new SessionFactory())->newInstance($_COOKIE)); + + // Models + $container->set('kitsu-model', static function($container): Kitsu\Model { + $requestBuilder = new KitsuRequestBuilder($container); + $requestBuilder->setLogger($container->getLogger('kitsu-request')); + + $listItem = new Kitsu\ListItem(); + $listItem->setContainer($container); + $listItem->setRequestBuilder($requestBuilder); + + $model = new Kitsu\Model($listItem); + $model->setContainer($container); + $model->setRequestBuilder($requestBuilder); + + $cache = $container->get('cache'); + $model->setCache($cache); + return $model; + }); + $container->set('anilist-model', static function ($container): Anilist\Model { + $requestBuilder = new Anilist\AnilistRequestBuilder(); + $requestBuilder->setLogger($container->getLogger('anilist-request')); + + $listItem = new Anilist\ListItem(); + $listItem->setContainer($container); + $listItem->setRequestBuilder($requestBuilder); + + $model = new Anilist\Model($listItem); + $model->setContainer($container); + $model->setRequestBuilder($requestBuilder); + + return $model; + }); + $container->set('settings-model', static function($container): Model\Settings { + $model = new Model\Settings($container->get('config')); + $model->setContainer($container); + return $model; + }); + + $container->set('auth', fn ($container) => new Kitsu\Auth($container)); + + $container->set('url-generator', fn ($container) => new UrlGenerator($container)); + + $container->set('util', fn ($container) => new Util($container)); + + return $container; + } } \ No newline at end of file diff --git a/src/AnimeClient/Command/CacheClear.php b/src/AnimeClient/Command/CacheClear.php index 2d3495e5..f56a93ac 100644 --- a/src/AnimeClient/Command/CacheClear.php +++ b/src/AnimeClient/Command/CacheClear.php @@ -18,6 +18,7 @@ namespace Aviat\AnimeClient\Command; use Aviat\Ion\Di\Exception\ContainerException; use Aviat\Ion\Di\Exception\NotFoundException; +use function Aviat\AnimeClient\clearCache; /** * Clears the API Cache @@ -36,8 +37,17 @@ final class CacheClear extends BaseCommand { { $this->setContainer($this->setupContainer()); - $this->container->get('cache')->clear(); + $cache = $this->container->get('cache'); - $this->echoBox('API Cache has been cleared.'); + $cleared = clearCache($cache); + + if ($cleared) + { + $this->echoBox('API Cache has been cleared.'); + } + else + { + $this->echoErrorBox('Failed to clear cache.'); + } } } diff --git a/src/AnimeClient/Command/CachePrime.php b/src/AnimeClient/Command/CachePrime.php index 2b25cca7..7072fda9 100644 --- a/src/AnimeClient/Command/CachePrime.php +++ b/src/AnimeClient/Command/CachePrime.php @@ -16,8 +16,10 @@ namespace Aviat\AnimeClient\Command; +use Aviat\AnimeClient\API\Kitsu; use Aviat\Ion\Di\Exception\ContainerException; use Aviat\Ion\Di\Exception\NotFoundException; +use function Aviat\AnimeClient\clearCache; /** * Clears the API Cache @@ -35,30 +37,25 @@ final class CachePrime extends BaseCommand { public function execute(array $args, array $options = []): void { $this->setContainer($this->setupContainer()); - $cache = $this->container->get('cache'); - // Save the user id, if it exists, for priming the cache - $userIdItem = $cache->getItem('kitsu-auth-token'); - $userId = $userIdItem->isHit() ? $userIdItem->get() : null; - - $cache->clear(); + $cleared = clearCache($cache); + if ( ! $cleared) + { + $this->echoErrorBox('Failed to clear cache.'); + return; + } $this->echoBox('Cache cleared, re-priming...'); - if ($userId !== NULL) - { - $userIdItem = $cache->getItem('kitsu-auth-token'); - $userIdItem->set($userId); - $userIdItem->save(); - } - $kitsuModel = $this->container->get('kitsu-model'); - // Prime anime list cache + // Prime anime list and history cache + $kitsuModel->getAnimeHistory(); $kitsuModel->getFullOrganizedAnimeList(); // Prime manga list cache + $kitsuModel->getMangaHistory(); $kitsuModel->getFullOrganizedMangaList(); $this->echoBox('API Cache has been primed.'); diff --git a/src/AnimeClient/Controller.php b/src/AnimeClient/Controller.php index 5fc6c034..b15ed34f 100644 --- a/src/AnimeClient/Controller.php +++ b/src/AnimeClient/Controller.php @@ -16,16 +16,16 @@ namespace Aviat\AnimeClient; -use Aviat\AnimeClient\Enum\EventType; use function Aviat\Ion\_dir; +use Aviat\AnimeClient\Enum\EventType; use Aura\Router\Generator; use Aura\Session\Segment; use Aviat\AnimeClient\API\Kitsu\Auth; use Aviat\Ion\ConfigInterface; -use Psr\Cache\CacheItemPoolInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Psr\SimpleCache\CacheInterface; use Aviat\Ion\Di\{ ContainerAware, @@ -53,9 +53,9 @@ class Controller { /** * Cache manager - * @var CacheItemPoolInterface + * @var CacheInterface */ - protected CacheItemPoolInterface $cache; + protected CacheInterface $cache; /** * The global configuration object @@ -134,8 +134,9 @@ class Controller { 'urlGenerator' => $urlGenerator, ]; - Event::on(EventType::CLEAR_CACHE, fn () => $this->emptyCache()); - Event::on(EventType::RESET_CACHE_KEY, fn (string $key) => $this->removeCacheItem($key)); + // Set up 'global' events + Event::on(EventType::CLEAR_CACHE, fn () => clearCache($this->cache)); + Event::on(EventType::RESET_CACHE_KEY, fn (string $key) => $this->cache->delete($key)); } /** @@ -435,15 +436,5 @@ class Controller { (new HttpView($this->container))->redirect($url, $code); exit(); } - - private function emptyCache(): void - { - $this->cache->emptyCache(); - } - - private function removeCacheItem(string $key): void - { - $this->cache->deleteItem($key); - } } // End of BaseController.php \ No newline at end of file