Skip to content

PHP Client

Introduction

The Client encapsulates features such as request signing, obtaining access_token, refreshing access_token, etc. With the PHP Client, you can quickly initiate requests to the FastMoss API. NOTE: cacheToken and getCachedToken methods must be implemented.

request example

PHP
<?php
use FastMossClient;
$client = new FastMossClient('client_id', 'client_secret');
$result = $client->test(['hello'=>'world'])

PHP Client Code:

php
<?php
// FastMossClient.php - A PHP client for FastMoss API

/**
 * FastMossClient provides a complete implementation for:
 * - Token management (acquisition and refresh)
 * - API request signing
 * - Local token caching
 * - Standardized API calls
 * 
 * Example usage:
 * $client = new FastMossClient("your_id", "your_secret");
 * $response = $client->doApiCall("/v1/videos", ["page" => 1]);
 * 
 * NOTE: cacheToken and getCachedToken methods must be implemented.
 */

class FastMossError extends Exception
{
    // Base exception for FastMoss API errors
}

class FastMossClient
{
    private $clientId;
    private $clientSecret;
    private $baseUrl;
    private $timeout;
    private $httpClient;

    /**
     * Initialize the FastMoss API client
     *
     * @param string $clientId API client identifier
     * @param string $clientSecret API client secret key
     * @param string $baseUrl Base API URL (defaults to test environment)
     * @param int $timeout Request timeout in seconds
     */
    public function __construct(
        string $clientId,
        string $clientSecret,
        string $baseUrl = "https://openapi.test.fastmoss.com",
        int $timeout = 15
    ) {
        $this->clientId = $clientId;
        $this->clientSecret = $clientSecret;
        $this->baseUrl = rtrim($baseUrl, "/");
        $this->timeout = $timeout;
        $this->httpClient = curl_init();
        
        // Configure logging
        error_log(date('Y-m-d H:i:s') . " - FastMossClient - INFO - Initialized client\n", 3, 'fastmoss.log');
    }

    /**
     * Request a new access token from FastMoss API
     *
     * @return array Token information
     * @throws FastMossError
     */
    private function getNewToken(): array
    {
        $url = $this->baseUrl . "/v1/token";
        $payload = [
            "client_id" => $this->clientId,
            "client_secret" => $this->clientSecret
        ];

        try {
            $response = $this->makeHttpRequest($url, $payload, "POST");
            $responseData = json_decode($response, true);

            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new FastMossError("Failed to decode token response");
            }

            if ($responseData["code"] === 0) {
                return $responseData["data"] ?? [];
            } else {
                $errorMsg = $responseData["message"] ?? "Unknown error";
                error_log(date('Y-m-d H:i:s') . " - FastMossClient - ERROR - Token request failed: $errorMsg\n", 3, 'fastmoss.log');
                throw new FastMossError($errorMsg);
            }
        } catch (Exception $e) {
            error_log(date('Y-m-d H:i:s') . " - FastMossClient - ERROR - Token request exception: {$e->getMessage()}\n", 3, 'fastmoss.log');
            throw new FastMossError("Token request failed: {$e->getMessage()}");
        }
    }

    /**
     * Refresh an expired access token
     *
     * @param string $refreshToken The refresh token
     * @return array New token information
     * @throws FastMossError
     */
    private function refreshToken(string $refreshToken): array
    {
        $url = $this->baseUrl . "/v1/refreshToken";
        $payload = [
            "client_id" => $this->clientId,
            "refresh_token" => $refreshToken
        ];

        try {
            $response = $this->makeHttpRequest($url, $payload, "POST");
            $responseData = json_decode($response, true);

            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new FastMossError("Failed to decode refresh token response");
            }

            if ($responseData["code"] === 0) {
                return $responseData["data"] ?? [];
            } else {
                $errorMsg = $responseData["message"] ?? "Unknown error";
                error_log(date('Y-m-d H:i:s') . " - FastMossClient - ERROR - Token refresh failed: $errorMsg\n", 3, 'fastmoss.log');
                throw new FastMossError($errorMsg);
            }
        } catch (Exception $e) {
            error_log(date('Y-m-d H:i:s') . " - FastMossClient - ERROR - Token refresh exception: {$e->getMessage()}\n", 3, 'fastmoss.log');
            throw new FastMossError("Token refresh failed: {$e->getMessage()}");
        }
    }

    /**
     * Retrieve cached token from local storage
     * 
     * @return ?array Cached token if available, null otherwise
     */
    private function getCachedToken(): ?array
    {
         // TODO: Implement actual storage/retrieval logic
        //  $key = 'fm_token:' . $this->clientId;
        // $cachedData = Cache::get($key);
        // if(empty($cachedData)) {
        //     return null;
        // }
        //return json_decode($cachedData,true);
        return null;
    }

    /**
     * Cache token data in local storage
     *
     * @param array $tokenData Token information to cache
     * @return bool True if caching succeeded
     */
    private function cacheToken(array $tokenData): bool
    {
        // TODO: Implement actual storage logic
        //  $key = 'fm_token:' . $this->clientId;
        // Cache::set($key,json_encode($tokenData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), $tokenData['refresh_expires_in'] ?? 86400)
        return true;
    }

    /**
     * Check if token is still valid
     *
     * @param array $tokenData Token information
     * @return bool True if valid
     */
    private function isTokenValid(array $tokenData): bool
    {
        $expiresAt = $tokenData["expire_at"] ?? 0;
        $currentTime = time();
        return $expiresAt > $currentTime + 600; // 10 minute buffer
    }

    /**
     * Check if refresh token is still valid
     *
     * @param array $tokenData Token information
     * @return bool True if valid
     */
    private function isRefreshTokenValid(array $tokenData): bool
    {
        $refreshExpiresAt = $tokenData["refresh_expire_at"] ?? 0;
        $currentTime = time();
        return $refreshExpiresAt > $currentTime + 600; // 10 minute buffer
    }

    /**
     * Get a valid access token, using cache if available
     *
     * @return array Token information
     * @throws FastMossError
     */
    public function getToken(): array
    {
        $tokenData = $this->getCachedToken();

        if ($tokenData) {
            if ($this->isTokenValid($tokenData)) {
                return $tokenData;
            } elseif ($this->isRefreshTokenValid($tokenData)) {
                try {
                    $newToken = $this->refreshToken($tokenData["refresh_token"]);
                    $this->cacheToken($newToken);
                    return $newToken;
                } catch (FastMossError $e) {
                    error_log(date('Y-m-d H:i:s') . " - FastMossClient - WARNING - Token refresh failed, getting new token\n", 3, 'fastmoss.log');
                }
            }
        }

        try {
            $newToken = $this->getNewToken();
            $this->cacheToken($newToken);
            return $newToken;
        } catch (FastMossError $e) {
            error_log(date('Y-m-d H:i:s') . " - FastMossClient - ERROR - Failed to get new token\n", 3, 'fastmoss.log');
            throw new FastMossError("Failed to acquire valid token: {$e->getMessage()}");
        }
    }

    /**
     * Generate API request signature
     *
     * @param string $uri API endpoint URI
     * @param string $jsonStr JSON string of request payload
     * @return string SHA256 signature
     */
    private function generateSignature(string $uri, string $jsonStr): string
    {
        $signData = "{$this->clientSecret}|{$uri}|{$jsonStr}|{$this->clientSecret}";
        return hash('sha256', $signData);
    }

    /**
     * Make HTTP request using curl
     *
     * @param string $url Request URL
     * @param array $postData Request payload
     * @param string $method HTTP method
     * @return string Response body
     * @throws Exception
     */
    private function makeHttpRequest(string $url, array $postData, string $method): string
    {
        $ch = $this->httpClient;
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json; charset=utf-8'
        ]);

        if ($method === "POST") {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        if ($response === false || $httpCode >= 400) {
            $error = curl_error($ch) ?: "HTTP $httpCode";
            throw new Exception("Request failed: $error");
        }

        return $response;
    }

    /**
     * Make an authenticated API call to FastMoss
     *
     * @param string $uri API endpoint URI
     * @param array $postData Request parameters
     * @param string $method HTTP method
     * @return array API response data
     * @throws FastMossError
     */
    public function doApiCall(string $uri, array $postData, string $method = "POST"): array
    {
        try {
            $tokenInfo = $this->getToken();
            $accessToken = $tokenInfo["access_token"] ?? null;
            if (!$accessToken) {
                throw new FastMossError("Access token not available");
            }

            $postDataStr = json_encode($postData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            $signature = $this->generateSignature($uri, $postDataStr);
            $timestamp = time();

            $url = $this->baseUrl . $uri . "?" . http_build_query([
                "access_token" => $accessToken,
                "sign" => $signature,
                "client_id" => $this->clientId,
                "timestamp" => $timestamp,
                "signature_version" => 2
            ]);

            $response = $this->makeHttpRequest($url, $postData, $method);
            $responseData = json_decode($response, true);

            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new FastMossError("Failed to decode API response");
            }

            return $responseData;
        } catch (Exception $e) {
            error_log(date('Y-m-d H:i:s') . " - FastMossClient - ERROR - API request failed: {$e->getMessage()}\n", 3, 'fastmoss.log');
            throw new FastMossError("API request failed: {$e->getMessage()}");
        }
    }

    /**
     * FastMoss Test API
     *
     * @param array $params Query parameters
     * @return array Video list data
     * @throws FastMossError
     */
    public function test(array $params): array
    {
        try {
            $response = $this->doApiCall("/test", $params);
            error_log(date('Y-m-d H:i:s') . " - FastMossClient - INFO - Successfully called test\n", 3, 'fastmoss.log');
            return $response;
        } catch (FastMossError $e) {
            error_log(date('Y-m-d H:i:s') . " - FastMossClient - ERROR - Failed to call test: {$e->getMessage()}\n", 3, 'fastmoss.log');
            throw $e;
        }
    }
}