sgd_dashboard-1.0.0-beta1/src/Services/EndOfLifeClientService.php
src/Services/EndOfLifeClientService.php
<?php
namespace Drupal\sgd_dashboard\Services;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\sgd_dashboard\Exception\EndOfLifeClientException;
use GuzzleHttp\Client as httpClient;
use Psr\Log\LoggerInterface;
/**
* SiteGuardian dashboard End of life Client service.
*
* Provides all interaction between the dashboard and the end of life
* client web service (https://endoflife.date)
*/
class EndOfLifeClientService implements EndOfLifeClientServiceInterface {
use StringTranslationTrait;
/**
* The http client.
*
* @var \GuzzleHttp\Client
*/
protected $httpClient;
/**
* A cache backend interface.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* Logger.
*
* @var Psr\Log\LoggerInterface
*/
protected $logger;
/**
* The messenger service.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* {@inheritDoc}
*/
public function __construct(httpClient $httpClient, CacheBackendInterface $cache, LoggerInterface $logger, MessengerInterface $messenger) {
$this->httpClient = $httpClient;
$this->cache = $cache;
$this->logger = $logger;
$this->messenger = $messenger;
}
/**
* Gets the End of Life data for PHP.
*
* @return array
* Info about Drupal version, PHP version, and more.
*/
public function getSingleCycle($product, $cycle): array | bool {
// Throw an error no product or cycle.
if (empty($product) || empty($cycle)) {
$message = sprintf("Invalid product or cycle (%s, %s) for endoflife.date request.", $product, $cycle);
throw new EndOfLifeClientException($message);
}
// Get the eol for all cycles of the product.
$eolStatus = $this->getProduct($product);
// Nothing returned so return false.
if (empty($eolStatus)) {
return FALSE;
}
// See if we have the cycle requested.
$cycleIndex = array_search($cycle, array_column($eolStatus, 'cycle'));
// If the requested cycle is not found just return.
if ($cycleIndex === FALSE) {
return FALSE;
}
// Return the cycle info.
return $eolStatus[$cycleIndex];
}
/**
* Gets the End of Life data for a product.
*
* @return array
* Information about the cycles for the requested product
*/
private function getProduct($product): array {
// If we had an exception accessing the eol api recently then just error.
if ($this->cache->get("endoflife:exception")) {
$this->messenger->addWarning($this->t('An issue was encountered accessing the End of life service. API access is temporarily disabled.'));
$message = sprintf("An issue was encountered accessing the End of life service. API access is temporarily disabled.");
throw new EndOfLifeClientException($message);
}
// Cache ID to use for the product end of life data.
$cacheId = "endoflife:" . $product;
// If we have cached data then use it otherwise we need to ask the API.
if (!($cachedEolData = $this->cache->get($cacheId))) {
$url = "https://endoflife.date/api/" . $product . ".json";
try {
$request = $this->httpClient->get($url);
}
catch (\Exception $e) {
// If we had an exception then set a value in the cache
// that will stop us trying again for a while.
$this->cache->set("endoflife:exception", time(), time() + 30);
$this->logger->error("Getting endoflife status for %product: @error", [
'%product' => $product,
'@error' => $e->getMessage(),
]);
$message = sprintf("An unexpected error occured whilst accessing endoflife.date api for product %s.", $product);
throw new EndOfLifeClientException($message);
}
// Now we have the API response we can decode it and then cache it
// (for 1 day).
$eolStatusData = json_decode($request->getBody(), TRUE);
$this->cache->set($cacheId, $eolStatusData, time() + 86400);
}
// Data was in cache so return just the EOL data.
else {
$eolStatusData = $cachedEolData->data;
}
return $eolStatusData;
}
}
