From 58353b249af81a80d11e8524786f88b954fbca8c Mon Sep 17 00:00:00 2001 From: Keith Solomon Date: Sun, 8 Feb 2026 23:25:50 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8feature:=20Initial=20info=20gathering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + agent-info.php | 222 ++++++++++++++ config.php | 94 ++++++ index.php | 42 +++ lib/project-config.php | 13 + lib/spacetraders-api-exception.php | 52 ++++ lib/spacetraders-api.php | 465 +++++++++++++++++++++++++++++ lib/spacetraders-storage.php | 245 +++++++++++++++ phpcs.xml | 27 +- 9 files changed, 1153 insertions(+), 8 deletions(-) create mode 100644 agent-info.php create mode 100644 config.php create mode 100644 index.php create mode 100644 lib/project-config.php create mode 100644 lib/spacetraders-api-exception.php create mode 100644 lib/spacetraders-api.php create mode 100644 lib/spacetraders-storage.php diff --git a/.gitignore b/.gitignore index faf4484..21eb6cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ Notes/ .vscode/ phpcs-results.txt +data/*.sqlite diff --git a/agent-info.php b/agent-info.php new file mode 100644 index 0000000..95192f3 --- /dev/null +++ b/agent-info.php @@ -0,0 +1,222 @@ + + * @license MIT License + * @link https://git.keithsolomon.net/keith/Spacetraders + */ + +require_once __DIR__ . '/lib/spacetraders-api.php'; +require_once __DIR__ . '/lib/spacetraders-storage.php'; + +$config = require __DIR__ . '/lib/project-config.php'; + +$storage = new SpacetradersStorage( $config['db_path'] ); +$token = $storage->getAgentToken(); +$statusMessage = ''; +$errorMessage = ''; + +if (! is_string( $token ) || trim( $token ) === '') { + $envToken = getenv( 'SPACETRADERS_TOKEN' ); + if (is_string( $envToken ) && trim( $envToken ) !== '' ) { + $token = trim( $envToken ); + $storage->setAgentToken( $token ); + } +} + +if (! is_string( $token ) || trim( $token ) === '') { + $tokenError = 'No token found. Set one in config.php or SPACETRADERS_TOKEN.'; +} + +$agent = array(); +$ships = array(); +$contracts = array(); + +if (! isset( $tokenError ) ) { + $client = new SpacetradersApi( + trim( $token ), + $config['api_base_url'], + (int) $config['api_timeout'], + $storage, + (int) $config['cache_ttl'] + ); + + if (isset( $_GET['accept_contract'] ) && is_string( $_GET['accept_contract'] ) && trim( $_GET['accept_contract'] ) !== '') { + try { + $client->acceptContract( trim( $_GET['accept_contract'] ) ); + $storage->clearAllCache(); + $statusMessage = 'Contract accepted.'; + } catch (SpacetradersApiException $e) { + $errorMessage = 'Unable to accept contract: ' . $e->getMessage(); + } + } +} + +try { + if (! isset( $tokenError ) ) { + $agentResponse = $client->getMyAgent(); + $shipsResponse = $client->listMyShips(); + $contractsResponse = $client->listMyContracts(); + + $agent = $agentResponse['data'] ?? $agentResponse; + $ships = $shipsResponse['data'] ?? $shipsResponse; + $contracts = $contractsResponse['data'] ?? $contractsResponse; + } +} catch (SpacetradersApiException $e) { + $error = array( + 'error' => $e->getMessage(), + 'code' => $e->getCode(), + 'payload' => $e->getErrorPayload(), + ); + + if (PHP_SAPI === 'cli' ) { + fwrite( STDERR, json_encode( $error, JSON_PRETTY_PRINT ) . PHP_EOL ); + exit( 1 ); + } + + http_response_code( 500 ); + header( 'Content-Type: application/json; charset=utf-8' ); + echo json_encode( $error, JSON_PRETTY_PRINT ); +} +?> + + + + + + + Spacetraders - Agent Information + + + + + + +
+

+ Open Configuration Page +
+ + + + + +
+ Configuration +
+ + +
+ +
+ + + +
+ +
+ + +

Spacetraders Agent and Ships

+ +

+ Agent:
+ Credits: +

+ +

+ Headquarters:
+ Faction:
+ Ship Count:
+ System: +

+ +

Ships

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameRoleTypeStatusFlight ModeRoute
N/AN/A
+ +

Contracts

+
+ +
+

Contract : -

+ +

+ Delivery Details: units delivered to +

+ +

+ Payment: on Accept, on Fulfill +

+ +

Deadline To Accept:

+

Deadline:

+ +

+ Status: + + + + Accept? + + + +

+
+ + +
+ + diff --git a/config.php b/config.php new file mode 100644 index 0000000..8b42974 --- /dev/null +++ b/config.php @@ -0,0 +1,94 @@ + + * @license MIT License + * @link https://git.keithsolomon.net/keith/Spacetraders + */ + +require_once __DIR__ . '/lib/spacetraders-storage.php'; + +$config = require __DIR__ . '/lib/project-config.php'; + +$storage = new SpacetradersStorage( $config['db_path'] ); +$token = $storage->getAgentToken(); +$statusMessage = ''; + +if ($_SERVER['REQUEST_METHOD'] === 'POST' ) { + if (isset( $_POST['agent_token'] ) ) { + $submittedToken = trim( (string) $_POST['agent_token'] ); + if ($submittedToken !== '' ) { + $storage->setAgentToken( $submittedToken ); + $token = $submittedToken; + $statusMessage = 'Agent token saved to SQLite settings.'; + } + } + + if (isset( $_POST['clear_cache'] ) ) { + $storage->clearAllCache(); + $statusMessage = 'API cache cleared.'; + } +} + +if (! is_string( $token ) || trim( $token ) === '') { + $envToken = getenv( 'SPACETRADERS_TOKEN' ); + if (is_string( $envToken ) && trim( $envToken ) !== '' ) { + $token = trim( $envToken ); + $storage->setAgentToken( $token ); + $statusMessage = 'Agent token imported from SPACETRADERS_TOKEN and saved.'; + } +} + +$hasToken = is_string( $token ) && trim( $token ) !== ''; +?> + + + + + + + Spacetraders - Configuration + + + + + +

Spacetraders Configuration

+ +
+

+ API responses are cached in SQLite for + minutes. +

+ + +

+ + + +

No token is currently stored.

+ +

Token is configured.

+ + +
+
+ + +
+ + +
+
+ + Back to Agent Info + + diff --git a/index.php b/index.php new file mode 100644 index 0000000..d1ec175 --- /dev/null +++ b/index.php @@ -0,0 +1,42 @@ + + * @license MIT License + * @link https://git.keithsolomon.net/keith/Spacetraders + */ +?> + + + + + + + Spacetraders - Agent Information + + + + + + + +

Spacetraders Playground

+ +
    + ' . $file . ''; + } + ?> +
+ + diff --git a/lib/project-config.php b/lib/project-config.php new file mode 100644 index 0000000..49418fa --- /dev/null +++ b/lib/project-config.php @@ -0,0 +1,13 @@ + 'https://api.spacetraders.io/v2', + 'api_timeout' => 30, + 'cache_ttl' => 600, + 'db_path' => dirname( __DIR__ ) . '/data/spacetraders.sqlite', +); diff --git a/lib/spacetraders-api-exception.php b/lib/spacetraders-api-exception.php new file mode 100644 index 0000000..0885257 --- /dev/null +++ b/lib/spacetraders-api-exception.php @@ -0,0 +1,52 @@ + + * @license MIT License + * @link https://github.com/your-repo/spacetraders + */ + +/** + * Custom exception class for Spacetraders API related errors. + * + * This exception is thrown when API calls to the Spacetraders service + * encounter errors such as network issues, invalid responses, authentication + * failures, or other API-specific problems. + * + * @extends RuntimeException + */ +class SpacetradersApiException extends RuntimeException { + /** + * Error payload data from the API response. + * + * @var array + */ + protected array $errorPayload = array(); + + /** + * Constructor for SpacetradersApiException. + * + * @param string $message The exception message. + * @param int $code The exception code (default: 0). + * @param array $errorPayload The error payload from API response (default: empty array). + */ + public function __construct( string $message, int $code = 0, array $errorPayload = array() ) { + parent::__construct( $message, $code ); + $this->errorPayload = $errorPayload; + } + + /** + * Get the error payload from the API response. + * + * @return array + */ + public function getErrorPayload(): array { + return $this->errorPayload; + } +} diff --git a/lib/spacetraders-api.php b/lib/spacetraders-api.php new file mode 100644 index 0000000..34346c4 --- /dev/null +++ b/lib/spacetraders-api.php @@ -0,0 +1,465 @@ + + * @license MIT License + * @version GIT: + * @link https://spacetraders.io + */ + +require_once __DIR__ . '/spacetraders-api-exception.php'; +require_once __DIR__ . '/spacetraders-storage.php'; + +/** + * Spacetraders API Client + * + * Main client class for interacting with the Spacetraders API. Provides methods + * for authentication, managing agents, ships, contracts, systems, and performing + * various game actions like navigation, trading, and resource extraction. + * + * @package SpacetradersAPI + */ +class SpacetradersApi { + /** + * The base URL for the Spacetraders API. + * + * @var string + */ + private string $baseUrl; + + /** + * The authentication token for API requests. + * + * @var string|null + */ + private ?string $token; + + /** + * The timeout for HTTP requests in seconds. + * + * @var int + */ + private int $timeout; + /** + * Optional SQLite storage for settings and cache. + * + * @var SpacetradersStorage|null + */ + private ?SpacetradersStorage $storage; + + /** + * Default cache lifetime in seconds. + * + * @var int + */ + private int $cacheTtl; + + /** + * Constructor for SpacetradersApi. + * + * @param string|null $token The authentication token for API requests ( default: null ). + * @param string $baseUrl The base URL for the Spacetraders API ( default: 'https://api.spacetraders.io/v2' ). + * @param int $timeout The timeout for HTTP requests in seconds ( default: 30 ). + * @param SpacetradersStorage|null $storage Optional SQLite storage object for settings and caching. + * @param int $cacheTtl API cache lifetime in seconds (default: 600). + */ + public function __construct( + ?string $token = null, + string $baseUrl = 'https://api.spacetraders.io/v2', + int $timeout = 30, + ?SpacetradersStorage $storage = null, + int $cacheTtl = 600 + ) { + $this->baseUrl = rtrim( $baseUrl, '/' ); + $this->token = $token; + $this->timeout = $timeout; + $this->storage = $storage; + $this->cacheTtl = max( 1, $cacheTtl ); + } + + /** + * Set the authentication token for API requests. + * + * @param string $token The authentication token to set. + * + * @return void + */ + public function setToken( string $token ): void { + $this->token = trim( $token ); + } + + /** + * Get the current authentication token. + * + * @return string|null The authentication token or null if not set. + */ + public function getToken(): ?string { + return $this->token; + } + + /** + * Attach storage for API caching and persisted settings. + * + * @param SpacetradersStorage|null $storage Storage handler. + * + * @return void + */ + public function setStorage( ?SpacetradersStorage $storage ): void { + $this->storage = $storage; + } + + /** + * Set cache lifetime for cached API calls in seconds. + * + * @param int $cacheTtl Cache lifetime in seconds. + * + * @return void + */ + public function setCacheTtl( int $cacheTtl ): void { + $this->cacheTtl = max( 1, $cacheTtl ); + } + + /** + * Make an HTTP request to the Spacetraders API. + * + * @param string $method The HTTP method (GET, POST, etc.). + * @param string $endpoint The API endpoint to call. + * @param array $payload The request payload data. + * @param array $query The query parameters. + * + * @return array + * @throws SpacetradersApiException When cURL fails to initialize, JSON encoding fails, network errors occur, or API returns an error response. + */ + public function request( + string $method, + string $endpoint, + array $payload = array(), + array $query = array() + ): array { + $url = $this->_buildUrl( $endpoint, $query ); + $method = strtoupper( $method ); + $cacheKey = $this->_buildCacheKey( $method, $url, $payload ); + + if ($method === 'GET' && $this->storage !== null ) { + $cachedResponse = $this->storage->getCache( $cacheKey ); + if (is_array( $cachedResponse ) ) { + return $cachedResponse; + } + } + + $ch = curl_init( $url ); + + if ($ch === false ) { + throw new SpacetradersApiException( 'Unable to initialize cURL.' ); + } + + $headers = array( 'Accept: application/json' ); + $jsonPayload = null; + + if (! empty( $this->token ) ) { + $headers[] = 'Authorization: Bearer ' . $this->token; + } + + if (! empty( $payload ) ) { + $jsonPayload = json_encode( $payload ); + if ($jsonPayload === false ) { + throw new SpacetradersApiException( 'Unable to encode request payload as JSON.' ); + } + + $headers[] = 'Content-Type: application/json'; + } + + curl_setopt_array( + $ch, + array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => $method, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_TIMEOUT => $this->timeout, + ) + ); + + if ($jsonPayload !== null ) { + curl_setopt( $ch, CURLOPT_POSTFIELDS, $jsonPayload ); + } + + $rawResponse = curl_exec( $ch ); + + if ($rawResponse === false ) { + $error = curl_error( $ch ); + curl_close( $ch ); + throw new SpacetradersApiException( 'Network error: ' . $error ); + } + + $statusCode = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE ); + curl_close( $ch ); + + $decoded = json_decode( $rawResponse, true ); + if (! is_array( $decoded ) ) { + throw new SpacetradersApiException( + 'API returned an invalid JSON response.', + $statusCode + ); + } + + if ($statusCode >= 400 ) { + $message = $decoded['error']['message'] ?? 'Unknown API error.'; + $errorCode = (int) ( $decoded['error']['code'] ?? $statusCode ); + throw new SpacetradersApiException( $message, $errorCode, $decoded ); + } + + if ($method === 'GET' && $this->storage !== null ) { + $this->storage->setCache( $cacheKey, $decoded, $this->cacheTtl ); + $this->storage->purgeExpiredCache(); + } + + return $decoded; + } + + /** + * Register a new agent and returns the API response. + * + * @param array $options Optional registration options: `symbol`, `faction`, and `email`. + * + * @return array + */ + public function registerAgent( array $options ): array { + return $this->request( 'POST', '/register', $options ); + } + + /** + * Get the current agent's information. + * + * @return array + */ + public function getMyAgent(): array { + return $this->request( 'GET', '/my/agent' ); + } + + /** + * Get a list of all ships owned by the current agent. + * + * @param array $query + * + * @return array + */ + public function listMyShips( array $query = array() ): array { + return $this->request( 'GET', '/my/ships', array(), $query ); + } + + /** + * Get information about a specific ship owned by the current agent. + * + * @param string $shipSymbol The symbol of the ship to retrieve. + * + * @return array + */ + public function getShip( string $shipSymbol ): array { + return $this->request( 'GET', '/my/ships/' . rawurlencode( $shipSymbol ) ); + } + + /** + * Get a list of all contracts for the current agent. + * + * @param array $query + * + * @return array + */ + public function listMyContracts( array $query = array() ): array { + return $this->request( 'GET', '/my/contracts', array(), $query ); + } + + /** + * Accept a contract for the current agent. + * + * @param string $contractId The ID of the contract to accept. + * + * @return array + */ + public function acceptContract( string $contractId ): array { + return $this->request( + 'POST', + '/my/contracts/' . rawurlencode( $contractId ) . '/accept' + ); + } + + /** + * Get a list of all systems in the universe. + * + * @param array $query + * + * @return array + */ + public function listSystems( array $query = array() ): array { + return $this->request( 'GET', '/systems', array(), $query ); + } + + /** + * Get information about a specific system. + * + * @param string $systemSymbol The symbol of the system to retrieve. + * + * @return array + */ + public function getSystem( string $systemSymbol ): array { + return $this->request( 'GET', '/systems/' . rawurlencode( $systemSymbol ) ); + } + + /** + * Get a list of waypoints in a specific system. + * + * @param string $systemSymbol The symbol of the system to retrieve waypoints from. + * @param array $query + * + * @return array + */ + public function listWaypoints( string $systemSymbol, array $query = array() ): array { + return $this->request( + 'GET', + '/systems/' . rawurlencode( $systemSymbol ) . '/waypoints', + array(), + $query + ); + } + + /** + * Navigate a ship to a specific waypoint. + * + * @param string $shipSymbol The symbol of the ship to navigate. + * @param string $waypointSymbol The symbol of the waypoint to navigate to. + * + * @return array + */ + public function navigateShip( string $shipSymbol, string $waypointSymbol ): array { + return $this->request( + 'POST', + '/my/ships/' . rawurlencode( $shipSymbol ) . '/navigate', + array( 'waypointSymbol' => $waypointSymbol ) + ); + } + + /** + * Put a ship into orbit around its current waypoint. + * + * @param string $shipSymbol The symbol of the ship to put into orbit. + * + * @return array + */ + public function orbitShip( string $shipSymbol ): array { + return $this->request( + 'POST', + '/my/ships/' . rawurlencode( $shipSymbol ) . '/orbit' + ); + } + + /** + * Dock a ship at its current waypoint. + * + * @param string $shipSymbol The symbol of the ship to dock. + * + * @return array + */ + public function dockShip( string $shipSymbol ): array { + return $this->request( + 'POST', + '/my/ships/' . rawurlencode( $shipSymbol ) . '/dock' + ); + } + + /** + * Extract resources from the current waypoint using the specified ship. + * + * @param string $shipSymbol The symbol of the ship to use for resource extraction. + * + * @return array + */ + public function extractResources( string $shipSymbol ): array { + return $this->request( + 'POST', + '/my/ships/' . rawurlencode( $shipSymbol ) . '/extract' + ); + } + + /** + * Purchase cargo for a specific ship at its current waypoint. + * + * @param string $shipSymbol The symbol of the ship to purchase cargo for. + * @param string $tradeSymbol The symbol of the trade good to purchase. + * @param int $units The number of units to purchase. + * + * @return array + */ + public function purchaseCargo( string $shipSymbol, string $tradeSymbol, int $units ): array { + return $this->request( + 'POST', + '/my/ships/' . rawurlencode( $shipSymbol ) . '/purchase', + array( + 'symbol' => $tradeSymbol, + 'units' => $units, + ) + ); + } + + /** + * Sell cargo from a specific ship at its current waypoint. + * + * @param string $shipSymbol The symbol of the ship to sell cargo from. + * @param string $tradeSymbol The symbol of the trade good to sell. + * @param int $units The number of units to sell. + * + * @return array + */ + public function sellCargo( string $shipSymbol, string $tradeSymbol, int $units ): array { + return $this->request( + 'POST', + '/my/ships/' . rawurlencode( $shipSymbol ) . '/sell', + array( + 'symbol' => $tradeSymbol, + 'units' => $units, + ) + ); + } + + /** + * Build the full URL for an API endpoint with optional query parameters. + * + * @param string $endpoint The API endpoint path. + * @param array $query The query parameters to append. + * + * @return string The complete URL. + */ + private function _buildUrl( string $endpoint, array $query = array() ): string { + $url = $this->baseUrl . '/' . ltrim( $endpoint, '/' ); + + if (! empty( $query ) ) { + $url .= '?' . http_build_query( $query ); + } + + return $url; + } + + /** + * Build a deterministic cache key for an API request. + * + * @param string $method HTTP method. + * @param string $url Full URL. + * @param array $payload Request payload. + * + * @return string + */ + private function _buildCacheKey( string $method, string $url, array $payload ): string { + $payloadJson = json_encode( $payload ); + if ($payloadJson === false ) { + $payloadJson = ''; + } + + return hash( 'sha256', $method . '|' . $url . '|' . $payloadJson ); + } +} diff --git a/lib/spacetraders-storage.php b/lib/spacetraders-storage.php new file mode 100644 index 0000000..8885ea5 --- /dev/null +++ b/lib/spacetraders-storage.php @@ -0,0 +1,245 @@ + + * @license MIT License + * @link https://git.keithsolomon.net/keith/Spacetraders + */ + +/** + * Spacetraders SQLite storage for configuration and API cache. + * + * @package SpacetradersAPI + */ +class SpacetradersStorage { + /** + * Database handle. + * + * @var PDO + */ + private PDO $db; + + /** + * Create storage and initialize schema. + * + * @param string $dbPath Absolute or project-relative SQLite database path. + */ + public function __construct( string $dbPath ) { + $directory = dirname( $dbPath ); + if (! is_dir( $directory ) ) { + mkdir( $directory, 0777, true ); + } + + $this->db = new PDO( 'sqlite:' . $dbPath ); + $this->db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); + $this->db->setAttribute( PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC ); + + $this->_initializeSchema(); + } + + /** + * Create required tables if they do not exist. + * + * @return void + */ + private function _initializeSchema(): void { + $this->db->exec( + 'CREATE TABLE IF NOT EXISTS settings ( + setting_key TEXT PRIMARY KEY, + setting_value TEXT NOT NULL, + updated_at INTEGER NOT NULL + )' + ); + + $this->db->exec( + 'CREATE TABLE IF NOT EXISTS api_cache ( + cache_key TEXT PRIMARY KEY, + response_json TEXT NOT NULL, + created_at INTEGER NOT NULL, + expires_at INTEGER NOT NULL + )' + ); + + $this->db->exec( + 'CREATE INDEX IF NOT EXISTS idx_api_cache_expires + ON api_cache (expires_at)' + ); + } + + /** + * Save a generic setting. + * + * @param string $key Setting key. + * @param string $value Setting value. + * + * @return void + */ + public function setSetting( string $key, string $value ): void { + $stmt = $this->db->prepare( + 'INSERT INTO settings (setting_key, setting_value, updated_at) + VALUES (:key, :value, :updated_at) + ON CONFLICT(setting_key) DO UPDATE SET + setting_value = excluded.setting_value, + updated_at = excluded.updated_at' + ); + + $stmt->execute( + array( + ':key' => $key, + ':value' => $value, + ':updated_at' => time(), + ) + ); + } + + /** + * Load a generic setting. + * + * @param string $key Setting key. + * + * @return string|null + */ + public function getSetting( string $key ): ?string { + $stmt = $this->db->prepare( + 'SELECT setting_value FROM settings WHERE setting_key = :key LIMIT 1' + ); + $stmt->execute( array( ':key' => $key ) ); + $row = $stmt->fetch(); + + if (! is_array( $row ) || ! isset( $row['setting_value'] ) ) { + return null; + } + + return (string) $row['setting_value']; + } + + /** + * Save the agent token in settings. + * + * @param string $token Agent token. + * + * @return void + */ + public function setAgentToken( string $token ): void { + $this->setSetting( 'agent_token', $token ); + } + + /** + * Get the stored agent token, if available. + * + * @return string|null + */ + public function getAgentToken(): ?string { + return $this->getSetting( 'agent_token' ); + } + + /** + * Fetch a valid cached response by cache key. + * + * @param string $cacheKey Cache key. + * + * @return array|null + */ + public function getCache( string $cacheKey ): ?array { + $stmt = $this->db->prepare( + 'SELECT response_json, expires_at + FROM api_cache + WHERE cache_key = :cache_key + LIMIT 1' + ); + $stmt->execute( array( ':cache_key' => $cacheKey ) ); + $row = $stmt->fetch(); + + if (! is_array( $row ) ) { + return null; + } + + if ((int) $row['expires_at'] <= time() ) { + $this->deleteCache( $cacheKey ); + return null; + } + + $decoded = json_decode( (string) $row['response_json'], true ); + if (! is_array( $decoded ) ) { + $this->deleteCache( $cacheKey ); + return null; + } + + return $decoded; + } + + /** + * Store API response in cache. + * + * @param string $cacheKey Cache key. + * @param array $response API response payload. + * @param int $ttl Time to live in seconds. + * + * @return void + */ + public function setCache( string $cacheKey, array $response, int $ttl = 600 ): void { + $json = json_encode( $response ); + if ($json === false ) { + return; + } + + $createdAt = time(); + $expiresAt = $createdAt + max( 1, $ttl ); + + $stmt = $this->db->prepare( + 'INSERT INTO api_cache (cache_key, response_json, created_at, expires_at) + VALUES (:cache_key, :response_json, :created_at, :expires_at) + ON CONFLICT(cache_key) DO UPDATE SET + response_json = excluded.response_json, + created_at = excluded.created_at, + expires_at = excluded.expires_at' + ); + + $stmt->execute( + array( + ':cache_key' => $cacheKey, + ':response_json' => $json, + ':created_at' => $createdAt, + ':expires_at' => $expiresAt, + ) + ); + } + + /** + * Remove one cache row. + * + * @param string $cacheKey Cache key. + * + * @return void + */ + public function deleteCache( string $cacheKey ): void { + $stmt = $this->db->prepare( + 'DELETE FROM api_cache WHERE cache_key = :cache_key' + ); + $stmt->execute( array( ':cache_key' => $cacheKey ) ); + } + + /** + * Remove all expired cache rows. + * + * @return void + */ + public function purgeExpiredCache(): void { + $stmt = $this->db->prepare( + 'DELETE FROM api_cache WHERE expires_at <= :now' + ); + $stmt->execute( array( ':now' => time() ) ); + } + + /** + * Remove all cached API rows. + * + * @return void + */ + public function clearAllCache(): void { + $this->db->exec( 'DELETE FROM api_cache' ); + } +} diff --git a/phpcs.xml b/phpcs.xml index bf766bd..d887674 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -12,25 +12,36 @@ node_modules/ - - - - - - - + + + + + + + + + + + - 0 + + + 0 + + + + 0 +