app/classes/ReleaseInsights/Json.php (72 lines of code) (raw):
<?php
declare(strict_types=1);
namespace ReleaseInsights;
use Cache\Cache;
class Json
{
/**
* @param array<string, string> $data
*/
public function __construct(
public array $data = [],
public mixed $jsonp = false,
public bool $pretty_print = false,
)
{
}
/**
* Return a JSON/JSONP representation of data with the right HTTP headers
*/
public function output(): string
{
$json = $this->pretty_print ? json_encode($this->data, JSON_PRETTY_PRINT) : json_encode($this->data);
$mime = 'application/json';
if (is_string($this->jsonp)) {
$mime = 'application/javascript';
$json = $this->jsonp . '(' . $json . ')';
}
ob_start();
header("access-control-allow-origin: *"); // * is OK as our Json API is public and readonly
header("Content-type: {$mime}; charset=UTF-8");
header("Content-Length: " . strlen($json));
echo $json;
$json = ob_get_contents();
ob_end_clean();
return $json;
}
/**
* Return HTTP code 400 and an error message if an API call is incorrect
*/
public function outputError(): string
{
$this->pretty_print = true;
http_response_code(400);
return $this->output();
}
/**
* Output Json data
*/
public function render(): void
{
if (array_key_exists('error', $this->data)) {
echo $this->outputError();
} else {
$this->jsonp = $_GET['callback'] ?? false;
echo $this->output();
}
}
// Below are static methods imported from the Utils class, refactoring in progress
/**
* @return array<mixed> $template_data
*/
public static function load(string $url, int $ttl = 0): array
{
if (! $data = Cache::getKey($url, $ttl)) {
$data = Utils::getFile($url);
// Error fetching external data, don't cache. Safety net
// @codeCoverageIgnoreStart
if ($data === false) {
return ['error' => 'URL triggered an error'];
}
// @codeCoverageIgnoreEnd
// No data returned, bug or incorrect data, don't cache.
if (empty($data)) {
return ['error' => 'URL provided no data'];
}
// Invalid Json, don't cache.
if (! json_validate($data)) {
return ['error' => 'Invalid JSON source'];
}
Cache::setKey($url, $data, $ttl);
}
return self::toArray($data);
}
/**
* Return an Array from a Json string
* This is an utility function as we use json_decode in multiple places,
* always with the same options. That will make these calls shorter,
* with a more explicit function name and will allow to change default
* values at the app level.
*
* @return array<mixed> Associative array from a Json string
*/
public static function toArray(string $data): array
{
$data = json_decode(
json: $data,
associative: true,
depth: 512,
);
return is_null($data) ? [] : $data;
}
}