``` <?php namespace src\Integration; use src\Integlration\APIException; class DataProvider { /** * @var string */ private $host; /** * @var string */ private $user; /** * @var string */ private $password; /** * @param string $host * @param string $user * @param string $password */ public function __construct($host, $user, $password) { $this->host = $host; $this->user = $user; $this->password = $password; } /** * @param array $request * @return array * @throws APIException */ public function get(array $request) { // returns a response from external service } } ``` ``` <?php namespace src\Data; use Exception; use Library\HttpLibraryException; use Psr\Log\LoggerInterface; use src\Cache\Cache; use src\Cache\CacheException; use src\Integlration\APIException; use src\Integration\DataProvider; class DataManager { /** * @var DataProvider */ private $dataProvider; /** * @var Cache */ private $cache; /** * @var LoggerInterface */ private $logger; /** * @param DataProvider $dataProvider * @param Cache $cache * @param LoggerInterface $logger */ public function __construct(DataProvider $dataProvider, Cache $cache, LoggerInterface $logger) { $this->dataProvider = $dataProvider; $this->cache = $cache; $this->logger = $logger; } /** * @param array $input * @return array * @throws CacheException */ public function getResponse(array $input) { $result = null; $cacheKey = $this->cache->getCacheKey(json_encode($input)); try { $result = $this->dataProvider->get($input); $this->checkResult($result); $this->cache->set($cacheKey, $result); } catch (CacheException $cacheException) { $this->logger->critical( $cacheException->getMessage(), $this->getErrorLogContext($input, $result, $cacheException) ); } catch (APIException | HttpLibraryException | InvalidResponseDataException | Exception $exception) { $this->logger->error( $exception->getMessage(), $this->getErrorLogContext($input, $result, $exception) ); } if ($result === null) { $result = $this->cache->get($cacheKey) ?: []; } return $result; } /** * @param mixed $result * @throws InvalidResponseDataException */ private function checkResult($result) { // check response data with some json schema and other invalid or dangerous data if (/** not valid */) { throw new InvalidResponseDataException(/** message which data is invalid */); } } /** * @param array $input * @param array|null $result * @param Exception $exception * @return array */ private function getErrorLogContext($input, $result, Exception $exception) { $logContext = [ 'request' => $input, 'response' => $result, 'exception' => $exception, ]; if ($exception instanceof APIException) { $logContext['http_code'] = $exception->getHttpCode(); $logContext['response_body'] = $exception->getResponseBody(); } return $logContext; } } ``` ``` <?php namespace src\Cache; use DateTime; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; class Cache { /** * @var CacheItemPoolInterface */ private $cache; /** * @var CacheItemInterface[] */ private $cacheItem = []; public function __construct(CacheItemPoolInterface $cache) { $this->cache = $cache; } /** * @param string $keyString * @return string */ public function getCacheKey($keyString) { return sha1($keyString); } /** * @param STRING $cacheKey * @return string|null * @throws CacheException */ public function get($cacheKey) { $cacheItem = $this->cache->getItem($cacheKey); return $cacheItem->isHit() ? $cacheItem->get() : null; } /** * @param string $cacheKey * @param array $value * @throws CacheException */ public function set($cacheKey, $value) { $cacheItem = $this->cache->getItem($cacheKey); if (!$cacheItem->isHit() || $cacheItem->get() !== $value) { $cacheItem ->set($value) ->expiresAt( (new DateTime())->modify('+1 day') ); } } } ``` ``` <?php namespace src\Cache; class CacheException extends \Exception { } ``` ``` <?php namespace src\Integlration; use Throwable; class APIException extends \Exception { /** * @var int */ private $httpCode; /** * @var string */ private $responseBody; public function __construct($message = "", $httpCode = 500, $responseBody = '', $code = 0, Throwable $previous = null) { parent::__construct($message, $code, $previous); $this->httpCode = $httpCode; $this->responseBody = $responseBody; } /** * @return int */ public function getHttpCode() { return $this->httpCode; } /** * @return string */ public function getResponseBody() { return $this->responseBody; } } ``` ``` <?php namespace src\Data; class InvalidResponseDataException extends \Exception { } ```