795 lines
24 KiB
PHP
Executable file
795 lines
24 KiB
PHP
Executable file
<?php
|
|
require_once __DIR__ . "/session_bootstrap.php";
|
|
|
|
define("ESI_CLIENT_ID", "YOUR-EVE-CLIENT-ID");
|
|
// Replace with your ESI client secret from EVE Developer Portal
|
|
define("ESI_CLIENT_SECRET", "YOUR-EVE-CLIENT-SECRET");
|
|
|
|
function fetch_character_jobs($character_id, &$access_token)
|
|
{
|
|
// Check cache first
|
|
$cache_key = "character_jobs_{$character_id}";
|
|
$cache = get_cache_data($cache_key);
|
|
|
|
if ($cache !== null) {
|
|
return $cache;
|
|
}
|
|
|
|
$esi_url = "https://esi.evetech.net/latest/characters/{$character_id}/industry/jobs/?include_completed=false";
|
|
$headers = [
|
|
"Authorization: Bearer $access_token",
|
|
"Accept: application/json",
|
|
];
|
|
|
|
$response = esi_call($esi_url, $headers);
|
|
|
|
// Token expired? Try to refresh it.
|
|
if (
|
|
$response["http_code"] === 401 &&
|
|
isset($_SESSION["characters"][$character_id]["refresh_token"])
|
|
) {
|
|
$new_tokens = refresh_token(
|
|
$_SESSION["characters"][$character_id]["refresh_token"]
|
|
);
|
|
|
|
if (!empty($new_tokens["access_token"])) {
|
|
// Save refreshed token
|
|
$_SESSION["characters"][$character_id]["access_token"] =
|
|
$new_tokens["access_token"];
|
|
$access_token = $new_tokens["access_token"];
|
|
$headers[0] = "Authorization: Bearer $access_token";
|
|
|
|
// Retry the API call
|
|
$response = esi_call($esi_url, $headers);
|
|
} else {
|
|
error_log(
|
|
"ERROR: Token refresh failed for character ID $character_id."
|
|
);
|
|
return []; // Avoid crashing page
|
|
}
|
|
}
|
|
|
|
// Still bad? Bail out
|
|
if ($response["http_code"] !== 200) {
|
|
error_log(
|
|
"ERROR: ESI call failed after token refresh for character ID $character_id. HTTP {$response["http_code"]}"
|
|
);
|
|
return [];
|
|
}
|
|
|
|
$char_jobs = json_decode($response["body"], true);
|
|
if (!is_array($char_jobs)) {
|
|
$char_jobs = [];
|
|
}
|
|
|
|
// Skip corporation jobs if no character jobs (faster load)
|
|
if (empty($char_jobs)) {
|
|
// Cache empty results for 60 seconds (shorter time for empty results)
|
|
set_cache_data($cache_key, [], 60);
|
|
return [];
|
|
}
|
|
|
|
// Fetch corporation jobs if possible
|
|
$corp_jobs = [];
|
|
$character_info = fetch_character_info($character_id, $access_token);
|
|
if (isset($character_info["corporation_id"])) {
|
|
$corp_jobs = fetch_corporation_jobs(
|
|
$character_info["corporation_id"],
|
|
$access_token
|
|
);
|
|
}
|
|
|
|
// Filter out corp jobs where character_id is not in the $_SESSION["characters"]
|
|
$filtered_corp_jobs = [];
|
|
foreach ($corp_jobs as $job) {
|
|
if (
|
|
isset($job["installer_id"]) &&
|
|
$job["installer_id"] == $character_id
|
|
) {
|
|
$filtered_corp_jobs[] = $job;
|
|
}
|
|
}
|
|
|
|
$all_jobs = array_merge($char_jobs, $filtered_corp_jobs);
|
|
|
|
// If still no jobs after merging, return empty results
|
|
if (empty($all_jobs)) {
|
|
// Cache empty results for 60 seconds (shorter time for empty results)
|
|
set_cache_data($cache_key, [], 60);
|
|
return [];
|
|
}
|
|
|
|
// Extract IDs and fetch names in batch
|
|
$blueprint_ids = array_unique(array_column($all_jobs, "blueprint_type_id"));
|
|
$system_ids = array_unique(array_column($all_jobs, "system_id"));
|
|
|
|
// Fetch names in parallel using promises
|
|
$blueprint_names = [];
|
|
$system_names = [];
|
|
|
|
// Optimize by caching character name
|
|
$character_name = $_SESSION["characters"][$character_id]["name"] ?? "Unknown";
|
|
|
|
// Pre-define activity mapping
|
|
$activity_map = [
|
|
1 => "Manufacturing",
|
|
3 => "TE Research",
|
|
4 => "ME Research",
|
|
5 => "Copying",
|
|
7 => "Reverse Engineering",
|
|
8 => "Invention",
|
|
9 => "Reaction",
|
|
11 => "Reaction",
|
|
];
|
|
|
|
// Process blueprint and system IDs in smaller batches for better performance
|
|
if (!empty($blueprint_ids)) {
|
|
$blueprint_names = fetch_type_names($blueprint_ids);
|
|
}
|
|
|
|
if (!empty($system_ids)) {
|
|
$system_names = fetch_system_names($system_ids);
|
|
}
|
|
|
|
$results = [];
|
|
$current_time = time();
|
|
|
|
foreach ($all_jobs as $job) {
|
|
$start_time = format_time($job["start_date"]);
|
|
$end_time = isset($job["end_date"])
|
|
? format_time($job["end_date"])
|
|
: "";
|
|
|
|
$end_timestamp = isset($job["end_date"])
|
|
? strtotime($job["end_date"])
|
|
: null;
|
|
|
|
$time_left =
|
|
$end_timestamp && $job["status"] !== "delivered"
|
|
? max(0, $end_timestamp - $current_time)
|
|
: "Completed";
|
|
|
|
$system_name = $system_names[$job["system_id"]] ?? "Private Structure";
|
|
$blueprint_name =
|
|
$blueprint_names[$job["blueprint_type_id"]] ??
|
|
$job["blueprint_type_id"];
|
|
|
|
$results[] = [
|
|
"character" =>
|
|
$character_name .
|
|
(isset($job["installer_id"]) &&
|
|
$job["installer_id"] != $character_id
|
|
? " (Corp)"
|
|
: ""),
|
|
"blueprint" => $blueprint_name,
|
|
"activity" =>
|
|
$activity_map[$job["activity_id"]] ??
|
|
"Activity {$job["activity_id"]}",
|
|
"status" => $job["status"],
|
|
"location" => $system_name,
|
|
"start_time" => $start_time,
|
|
"end_time" => $end_time,
|
|
"end_time_unix" => $end_timestamp,
|
|
"time_left" => is_numeric($time_left)
|
|
? gmdate("H:i:s", $time_left)
|
|
: $time_left,
|
|
];
|
|
}
|
|
|
|
// Cache results with dynamic TTL based on the nearest job completion
|
|
$min_time_left = PHP_INT_MAX;
|
|
foreach ($results as $job) {
|
|
if (is_numeric($job["end_time_unix"]) && $job["end_time_unix"] > $current_time) {
|
|
$time_until_completion = $job["end_time_unix"] - $current_time;
|
|
if ($time_until_completion < $min_time_left) {
|
|
$min_time_left = $time_until_completion;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cache until the next job completes (min 60 seconds, max 300 seconds)
|
|
$cache_ttl = ($min_time_left < PHP_INT_MAX)
|
|
? min(max(60, $min_time_left), 300)
|
|
: 300;
|
|
|
|
set_cache_data($cache_key, $results, $cache_ttl);
|
|
|
|
return $results;
|
|
}
|
|
|
|
function fetch_corporation_jobs($corporation_id, $access_token)
|
|
{
|
|
// Check cache first
|
|
$cache_key = "corporation_jobs_{$corporation_id}";
|
|
$cache = get_cache_data($cache_key);
|
|
|
|
if ($cache !== null) {
|
|
return $cache;
|
|
}
|
|
|
|
$esi_url = "https://esi.evetech.net/latest/corporations/{$corporation_id}/industry/jobs/?include_completed=false";
|
|
$headers = [
|
|
"Authorization: Bearer $access_token",
|
|
"Accept: application/json",
|
|
];
|
|
|
|
$response = esi_call($esi_url, $headers);
|
|
|
|
if ($response["http_code"] !== 200) {
|
|
error_log("Corp job fetch failed: " . $response["body"]);
|
|
return [];
|
|
}
|
|
|
|
$jobs = json_decode($response["body"], true);
|
|
$results = is_array($jobs) ? $jobs : [];
|
|
|
|
// Cache the results for 5 minutes
|
|
set_cache_data($cache_key, $results, 300);
|
|
|
|
return $results;
|
|
}
|
|
|
|
function fetch_character_info($character_id, $access_token)
|
|
{
|
|
// Check cache first
|
|
$cache_key = "character_info_{$character_id}";
|
|
$cache = get_cache_data($cache_key);
|
|
|
|
if ($cache !== null) {
|
|
return $cache;
|
|
}
|
|
|
|
$url = "https://esi.evetech.net/latest/characters/{$character_id}/";
|
|
$headers = [
|
|
"Authorization: Bearer $access_token",
|
|
"Accept: application/json",
|
|
];
|
|
|
|
$response = esi_call($url, $headers);
|
|
$info = json_decode($response["body"], true);
|
|
|
|
if (is_array($info)) {
|
|
// Cache character info for 1 hour (rarely changes)
|
|
set_cache_data($cache_key, $info, 3600);
|
|
}
|
|
|
|
return $info;
|
|
}
|
|
|
|
function format_time($iso_string)
|
|
{
|
|
static $cache = [];
|
|
|
|
// Use cached result if available
|
|
if (isset($cache[$iso_string])) {
|
|
return $cache[$iso_string];
|
|
}
|
|
|
|
$dt = DateTime::createFromFormat(DateTime::ATOM, $iso_string);
|
|
$result = $dt ? $dt->format("M j, Y H:i") : $iso_string;
|
|
|
|
// Cache the result
|
|
$cache[$iso_string] = $result;
|
|
|
|
return $result;
|
|
}
|
|
|
|
function fetch_type_names($type_ids) {
|
|
if (empty($type_ids)) {
|
|
return [];
|
|
}
|
|
|
|
// Check if cleanup is needed
|
|
$last_cleanup = get_last_cleanup_time();
|
|
$thirty_days_ago = strtotime("-30 days");
|
|
if (!$last_cleanup || strtotime($last_cleanup) < $thirty_days_ago) {
|
|
// Call populate_cache.php to refresh the cache
|
|
$populate_cache_script = __DIR__ . "/populate_cache.php";
|
|
if (file_exists($populate_cache_script)) {
|
|
exec("php " . escapeshellarg($populate_cache_script));
|
|
}
|
|
update_last_cleanup_time(); // Update the last cleanup timestamp
|
|
}
|
|
|
|
// Load cache
|
|
$cache_file = __DIR__ . "/cache/blueprint_cache.json";
|
|
$cache = file_exists($cache_file) ? json_decode(file_get_contents($cache_file), true) : [];
|
|
|
|
// Check cache first
|
|
$cached_names = [];
|
|
$ids_to_fetch = [];
|
|
foreach ($type_ids as $id) {
|
|
if (isset($cache[$id])) {
|
|
$cached_names[$id] = $cache[$id];
|
|
} else {
|
|
$ids_to_fetch[] = $id;
|
|
}
|
|
}
|
|
|
|
// Fetch missing IDs from API
|
|
if (!empty($ids_to_fetch)) {
|
|
$url = "https://esi.evetech.net/latest/universe/names/";
|
|
$chunks = array_chunk($ids_to_fetch, 1000); // Split into chunks of 1000 IDs
|
|
foreach ($chunks as $chunk) {
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array_values($chunk)));
|
|
$response = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($http_code === 200) {
|
|
$data = json_decode($response, true);
|
|
foreach ($data as $entry) {
|
|
if (isset($entry["id"], $entry["name"])) {
|
|
$cached_names[$entry["id"]] = $entry["name"];
|
|
$cache[$entry["id"]] = $entry["name"]; // Update cache
|
|
}
|
|
}
|
|
} else {
|
|
error_log("Failed to fetch type names. HTTP Code: $http_code");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save updated cache
|
|
file_put_contents($cache_file, json_encode($cache, JSON_PRETTY_PRINT));
|
|
|
|
return $cached_names;
|
|
}
|
|
|
|
function populate_blueprint_cache() {
|
|
echo "Fetching all type IDs...\n";
|
|
$all_type_ids = get_all_type_ids();
|
|
|
|
if (empty($all_type_ids)) {
|
|
echo "No type IDs retrieved.\n";
|
|
return;
|
|
}
|
|
|
|
echo "Filtering blueprint IDs...\n";
|
|
$blueprint_ids = filter_blueprint_ids($all_type_ids);
|
|
|
|
if (empty($blueprint_ids)) {
|
|
echo "No blueprint IDs found.\n";
|
|
return;
|
|
}
|
|
|
|
echo "Fetching blueprint names...\n";
|
|
$cached_data = [];
|
|
$chunks = array_chunk($blueprint_ids, 1000);
|
|
foreach ($chunks as $chunk) {
|
|
$batch_names = fetch_type_names($chunk);
|
|
foreach ($batch_names as $id => $name) {
|
|
$cached_data[$id] = $name;
|
|
}
|
|
usleep(250000);
|
|
}
|
|
|
|
// Save to JSON cache file
|
|
$cache_dir = __DIR__ . "/cache";
|
|
if (!is_dir($cache_dir)) {
|
|
mkdir($cache_dir, 0775, true);
|
|
}
|
|
$cache_file = $cache_dir . "/blueprint_cache.json";
|
|
file_put_contents(
|
|
$cache_file,
|
|
json_encode($cached_data, JSON_PRETTY_PRINT)
|
|
);
|
|
|
|
echo "Cache populated with " . count($cached_data) . " blueprint names.\n";
|
|
}
|
|
|
|
function get_all_type_ids() {
|
|
$all_ids = [];
|
|
$page = 1;
|
|
|
|
do {
|
|
$url = "https://esi.evetech.net/latest/universe/types/?page=$page";
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
$response = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($http_code !== 200) {
|
|
echo "Failed to fetch type IDs on page $page. HTTP Code: $http_code\n";
|
|
break;
|
|
}
|
|
|
|
$ids = json_decode($response, true);
|
|
if (empty($ids)) {
|
|
break;
|
|
}
|
|
|
|
$all_ids = array_merge($all_ids, $ids);
|
|
$page++;
|
|
usleep(250000); // Avoid API rate limit
|
|
} while (true);
|
|
|
|
return $all_ids;
|
|
}
|
|
|
|
function filter_blueprint_ids($ids) {
|
|
$blueprint_ids = [];
|
|
$chunks = array_chunk($ids, 1000);
|
|
|
|
foreach ($chunks as $chunk) {
|
|
$names = fetch_type_names($chunk);
|
|
foreach ($names as $id => $name) {
|
|
if (str_ends_with($name, "Blueprint")) {
|
|
$blueprint_ids[] = $id;
|
|
}
|
|
}
|
|
usleep(250000);
|
|
}
|
|
|
|
return $blueprint_ids;
|
|
}
|
|
|
|
function fetch_system_names($system_ids)
|
|
{
|
|
if (empty($system_ids)) {
|
|
return [];
|
|
}
|
|
|
|
// Generate a cache key based on the sorted system IDs
|
|
sort($system_ids);
|
|
$cache_key = "system_names_" . md5(json_encode($system_ids));
|
|
$cache = get_cache_data($cache_key);
|
|
|
|
if ($cache !== null) {
|
|
return $cache;
|
|
}
|
|
|
|
// Check if we already have some of these system names cached individually
|
|
$names = [];
|
|
$ids_to_fetch = [];
|
|
|
|
foreach ($system_ids as $id) {
|
|
$individual_cache_key = "system_name_$id";
|
|
$cached_name = get_cache_data($individual_cache_key);
|
|
|
|
if ($cached_name !== null) {
|
|
$names[$id] = $cached_name;
|
|
} else {
|
|
$ids_to_fetch[] = $id;
|
|
}
|
|
}
|
|
|
|
// If we have all system names from individual caches, return them
|
|
if (empty($ids_to_fetch)) {
|
|
// Still cache the combined result
|
|
set_cache_data($cache_key, $names, 86400);
|
|
return $names;
|
|
}
|
|
|
|
// Only fetch the IDs we don't already have cached
|
|
$url = "https://esi.evetech.net/latest/universe/names/";
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
|
|
curl_setopt(
|
|
$ch,
|
|
CURLOPT_POSTFIELDS,
|
|
json_encode(array_values($ids_to_fetch))
|
|
);
|
|
$response = curl_exec($ch);
|
|
curl_close($ch);
|
|
|
|
$data = json_decode($response, true);
|
|
|
|
if (is_array($data)) {
|
|
foreach ($data as $entry) {
|
|
if (isset($entry["id"]) && isset($entry["name"])) {
|
|
$names[$entry["id"]] = $entry["name"];
|
|
|
|
// Cache individual system names
|
|
$individual_cache_key = "system_name_{$entry["id"]}";
|
|
set_cache_data($individual_cache_key, $entry["name"], 86400 * 7); // Cache for a week
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cache system names for 24 hours (they don't change)
|
|
set_cache_data($cache_key, $names, 86400);
|
|
|
|
return $names;
|
|
}
|
|
|
|
function refresh_token($refresh_token)
|
|
{
|
|
// Initialize cURL
|
|
$ch = curl_init("https://login.eveonline.com/v2/oauth/token");
|
|
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
"Authorization: Basic " . base64_encode(ESI_CLIENT_ID . ":" . ESI_CLIENT_SECRET),
|
|
"Content-Type: application/x-www-form-urlencoded",
|
|
]);
|
|
curl_setopt(
|
|
$ch,
|
|
CURLOPT_POSTFIELDS,
|
|
http_build_query([
|
|
"grant_type" => "refresh_token",
|
|
"refresh_token" => $refresh_token,
|
|
])
|
|
);
|
|
|
|
// Execute the request
|
|
$response = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); // Get HTTP status code
|
|
$curl_error = curl_error($ch); // Capture cURL error if any
|
|
curl_close($ch);
|
|
|
|
// Handle cURL errors
|
|
if ($response === false) {
|
|
error_log("cURL error: $curl_error");
|
|
return [];
|
|
}
|
|
|
|
// Handle HTTP errors
|
|
if ($http_code !== 200) {
|
|
error_log("ERROR: Token refresh failed. HTTP $http_code. Response: $response");
|
|
|
|
// Specific handling for common HTTP errors
|
|
if ($http_code === 400) {
|
|
error_log("Bad Request: Check the refresh token or request parameters.");
|
|
} elseif ($http_code === 401) {
|
|
error_log("Unauthorized: Check the client ID and secret.");
|
|
} elseif ($http_code === 429) {
|
|
error_log("Rate limit exceeded. Retry after delay.");
|
|
} elseif ($http_code >= 500) {
|
|
error_log("Server error. Retry after a delay.");
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
// Decode the response
|
|
$data = json_decode($response, true);
|
|
|
|
// Validate the response
|
|
if (!isset($data["access_token"])) {
|
|
error_log("ERROR: Token refresh failed. Invalid response: $response");
|
|
return [];
|
|
}
|
|
|
|
// Save the new refresh token if provided
|
|
if (isset($data["refresh_token"])) {
|
|
$_SESSION["characters"][$character_id]["refresh_token"] = $data["refresh_token"];
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
function esi_call($url, $headers)
|
|
{
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
|
curl_setopt($ch, CURLOPT_HEADER, true);
|
|
$response = curl_exec($ch);
|
|
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
$body = substr($response, $header_size);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
return [
|
|
"http_code" => $http_code,
|
|
"body" => $body,
|
|
];
|
|
}
|
|
|
|
function is_token_expired($access_token, $buffer_time = 300)
|
|
{
|
|
// Split the token into its three parts (header, payload, signature)
|
|
$parts = explode(".", $access_token);
|
|
|
|
// Validate that the token has exactly three parts
|
|
if (count($parts) !== 3) {
|
|
error_log("Invalid JWT format: $access_token");
|
|
return true; // Assume expired if the format is invalid
|
|
}
|
|
|
|
// Decode the payload (second part of the JWT)
|
|
$payload = json_decode(base64_decode($parts[1]), true);
|
|
|
|
// Validate that the payload is a valid JSON object
|
|
if (!is_array($payload)) {
|
|
error_log("Malformed JWT payload: " . base64_decode($parts[1]));
|
|
return true; // Assume expired if the payload is malformed
|
|
}
|
|
|
|
// Check if the "exp" (expiration) claim exists
|
|
if (!isset($payload["exp"])) {
|
|
error_log("JWT missing 'exp' claim: " . json_encode($payload));
|
|
return true; // Assume expired if the expiration claim is missing
|
|
}
|
|
|
|
// Compare the expiration time with the current time, considering the buffer time
|
|
// This prevents tokens from expiring during a user session by refreshing them early
|
|
return $payload["exp"] < (time() + $buffer_time);
|
|
}
|
|
function fetch_character_blueprints($character_id, $access_token)
|
|
{
|
|
// Check cache first
|
|
$cache_key = "character_blueprints_{$character_id}";
|
|
$cache = get_cache_data($cache_key);
|
|
|
|
if ($cache !== null) {
|
|
return $cache;
|
|
}
|
|
|
|
$esi_url = "https://esi.evetech.net/latest/characters/{$character_id}/blueprints/";
|
|
$headers = [
|
|
"Authorization: Bearer $access_token",
|
|
"Accept: application/json",
|
|
];
|
|
|
|
$response = esi_call($esi_url, $headers);
|
|
|
|
if ($response["http_code"] !== 200) {
|
|
error_log(
|
|
"Blueprint fetch failed for character ID $character_id: " .
|
|
$response["body"]
|
|
);
|
|
return [];
|
|
}
|
|
|
|
$blueprints = json_decode($response["body"], true);
|
|
$results = [];
|
|
|
|
foreach ($blueprints as $bp) {
|
|
$results[] = [
|
|
"blueprint_type_id" => $bp["type_id"],
|
|
"blueprint_name" => $bp["type_id"], // Placeholder, replace with actual name lookup if needed
|
|
"material_efficiency" => $bp["material_efficiency"],
|
|
"time_efficiency" => $bp["time_efficiency"],
|
|
"runs" => $bp["runs"],
|
|
"quantity" => $bp["quantity"] ?? 1,
|
|
];
|
|
}
|
|
|
|
// Cache the results for 10 minutes (blueprints change less frequently)
|
|
set_cache_data($cache_key, $results, 600);
|
|
|
|
return $results;
|
|
}
|
|
function get_cached_name($id) {
|
|
$cache_file = __DIR__ . "/cache/blueprint_cache.json";
|
|
if (!file_exists($cache_file)) {
|
|
return null;
|
|
}
|
|
|
|
$cache = json_decode(file_get_contents($cache_file), true);
|
|
return $cache[$id]["name"] ?? null;
|
|
}
|
|
|
|
function cache_name($id, $name) {
|
|
$cache_file = __DIR__ . "/cache/blueprint_cache.json";
|
|
$cache = file_exists($cache_file) ? json_decode(file_get_contents($cache_file), true) : [];
|
|
$cache[$id] = [
|
|
"name" => $name,
|
|
"last_updated" => gmdate("Y-m-d\TH:i:s\Z")
|
|
];
|
|
file_put_contents($cache_file, json_encode($cache, JSON_PRETTY_PRINT));
|
|
}
|
|
|
|
function clean_cache($expiry_days = 30) {
|
|
$cache_file = __DIR__ . "/cache/blueprint_cache.json";
|
|
if (!file_exists($cache_file)) {
|
|
return;
|
|
}
|
|
|
|
$cache = json_decode(file_get_contents($cache_file), true);
|
|
$expiry_time = strtotime("-$expiry_days days");
|
|
|
|
foreach ($cache as $id => $entry) {
|
|
if (strtotime($entry["last_updated"]) < $expiry_time) {
|
|
unset($cache[$id]);
|
|
}
|
|
}
|
|
|
|
file_put_contents($cache_file, json_encode($cache, JSON_PRETTY_PRINT));
|
|
}
|
|
function get_last_cleanup_time() {
|
|
$file = __DIR__ . "/cache/last_cleanup.json";
|
|
if (!file_exists($file)) {
|
|
return null;
|
|
}
|
|
|
|
$data = json_decode(file_get_contents($file), true);
|
|
return $data["last_cleanup"] ?? null;
|
|
}
|
|
|
|
function update_last_cleanup_time() {
|
|
$file = __DIR__ . "/cache/last_cleanup.json";
|
|
$data = ["last_cleanup" => gmdate("Y-m-d\TH:i:s\Z")];
|
|
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT));
|
|
}
|
|
// Cache functions
|
|
function get_cache_data($key) {
|
|
static $cache_data = null;
|
|
static $loaded = false;
|
|
|
|
$cache_dir = __DIR__ . "/cache";
|
|
$cache_file = $cache_dir . "/api_cache.json";
|
|
|
|
// Load cache data only once per request
|
|
if (!$loaded) {
|
|
if (file_exists($cache_file)) {
|
|
$cache_data = json_decode(file_get_contents($cache_file), true) ?: [];
|
|
} else {
|
|
$cache_data = [];
|
|
}
|
|
$loaded = true;
|
|
}
|
|
|
|
if (!isset($cache_data[$key]) || $cache_data[$key]['expires'] < time()) {
|
|
return null;
|
|
}
|
|
|
|
return $cache_data[$key]['data'];
|
|
}
|
|
|
|
function set_cache_data($key, $data, $ttl = 300) {
|
|
static $cache_data = null;
|
|
static $cache_modified = false;
|
|
static $loaded = false;
|
|
|
|
$cache_dir = __DIR__ . "/cache";
|
|
|
|
if (!is_dir($cache_dir)) {
|
|
mkdir($cache_dir, 0755, true);
|
|
}
|
|
|
|
$cache_file = $cache_dir . "/api_cache.json";
|
|
|
|
// Load cache data only once per request
|
|
if (!$loaded) {
|
|
if (file_exists($cache_file)) {
|
|
$cache_data = json_decode(file_get_contents($cache_file), true) ?: [];
|
|
} else {
|
|
$cache_data = [];
|
|
}
|
|
$loaded = true;
|
|
}
|
|
|
|
if (!is_array($cache_data)) {
|
|
$cache_data = [];
|
|
}
|
|
|
|
// Clean expired cache entries (only occasionally to improve performance)
|
|
if (rand(1, 10) === 1) {
|
|
foreach ($cache_data as $cache_key => $cache_entry) {
|
|
if ($cache_entry['expires'] < time()) {
|
|
unset($cache_data[$cache_key]);
|
|
$cache_modified = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update cache with new data
|
|
$cache_data[$key] = [
|
|
'data' => $data,
|
|
'expires' => time() + $ttl
|
|
];
|
|
$cache_modified = true;
|
|
|
|
// Use register_shutdown_function to write cache only once at the end of the request
|
|
if ($cache_modified && !isset($GLOBALS['cache_shutdown_registered'])) {
|
|
$GLOBALS['cache_shutdown_registered'] = true;
|
|
register_shutdown_function(function() use ($cache_file) {
|
|
global $cache_data;
|
|
if (is_array($cache_data)) {
|
|
file_put_contents($cache_file, json_encode($cache_data));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
?>
|