syntaxerrors/Steam

View on GitHub
src/Syntax/SteamApi/Client.php

Summary

Maintainability
A
0 mins
Test Coverage
B
88%
<?php

namespace Syntax\SteamApi;

use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Config;
use stdClass;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Psr7\Request;
use Exception;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ServerException;
use Syntax\SteamApi\Exceptions\ApiArgumentRequired;
use Syntax\SteamApi\Exceptions\ApiCallFailedException;
use Syntax\SteamApi\Exceptions\ClassNotFoundException;
use Syntax\SteamApi\Exceptions\InvalidApiKeyException;
use Syntax\SteamApi\Exceptions\UnrecognizedId;
use Syntax\SteamApi\Steam\App;
use Syntax\SteamApi\Steam\Group;
use Syntax\SteamApi\Steam\Item;
use Syntax\SteamApi\Steam\News;
use Syntax\Steamapi\Steam\Package;
use Syntax\SteamApi\Steam\Player;
use Syntax\SteamApi\Steam\User;
use Syntax\SteamApi\Steam\User\Stats;

/**
 * @method News       news()
 * @method Player     player($steamId)
 * @method User       user($steamId)
 * @method Stats      userStats($steamId)
 * @method App        app()
 * @method Package    package()
 * @method Group      group()
 * @method Item       item()
 */
class Client
{
    use SteamId;

    public array $validFormats = ['json', 'xml', 'vdf'];

    protected string $url = 'http://api.steampowered.com/';

    protected GuzzleClient $client;

    protected ?string $interface;

    protected string $method;

    protected ?string $version = 'v0002';

    protected string $apiKey;

    protected string $apiFormat = 'json';

    protected $steamId;

    protected bool $isService = false;

    /**
     * @throws InvalidApiKeyException
     */
    public function __construct()
    {
        $apiKey = $this->getApiKey();

        $this->client = new GuzzleClient();
        $this->apiKey = $apiKey;

        // Set up the Ids
        $this->setUpFormatted();
    }

    public function get(): static
    {
        return $this;
    }

    public function getSteamId()
    {
        return $this->steamId;
    }

    /**
     * @param null $arguments
     *
     * @return stdClass
     *
     * @throws ApiArgumentRequired
     * @throws ApiCallFailedException
     * @throws GuzzleException
     */
    protected function setUpService($arguments = null): stdClass
    {
        // Services have a different url syntax
        if ($arguments == null) {
            throw new ApiArgumentRequired;
        }

        $parameters = [
            'key'        => $this->apiKey,
            'format'     => $this->apiFormat,
            'input_json' => $arguments,
        ];

        $steamUrl = $this->buildUrl(true);

        // Build the query string
        $parameters = http_build_query($parameters);

        // Send the request and get the results
        $request  = new Request('GET', $steamUrl . '?' . $parameters);
        $response = $this->sendRequest($request);

        // Pass the results back
        return $response->body;
    }

    /**
     * @throws GuzzleException
     * @throws ApiCallFailedException
     */
    protected function setUpClient(array $arguments = [])
    {
        $versionFlag = ! is_null($this->version);
        $steamUrl    = $this->buildUrl($versionFlag);

        $parameters = [
            'key'    => $this->apiKey,
            'format' => $this->apiFormat,
        ];

        if (! empty($arguments)) {
            $parameters = array_merge($arguments, $parameters);
        }

        // Build the query string
        $parameters = http_build_query($parameters);

        $headers = [];
        if (array_key_exists('l', $arguments)) {
            $headers = [
                'Accept-Language' => $arguments['l'],
            ];
        }

        // Send the request and get the results
        $request  = new Request('GET', $steamUrl . '?' . $parameters, $headers);
        $response = $this->sendRequest($request);

        // Pass the results back
        return $response->body;
    }

    protected function setUpXml(array $arguments = []): \SimpleXMLElement|null
    {
        $steamUrl = $this->buildUrl();

        // Build the query string
        $parameters = http_build_query($arguments);

        // Pass the results back
        libxml_use_internal_errors(true);
        $result = simplexml_load_file($steamUrl . '?' . $parameters);

        if (! $result) {
            return null;
        }

        return $result;
    }

    public function getRedirectUrl(): void
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->url);
        curl_setopt($ch, CURLOPT_HEADER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        curl_exec($ch);
        $this->url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
        curl_close($ch);
    }

    /**
     *
     * @param Request $request
     * @return stdClass
     * @throws ApiCallFailedException
     * @throws GuzzleException
     */
    protected function sendRequest(Request $request): stdClass
    {
        // Try to get the result.  Handle the possible exceptions that can arise
        try {
            $response = $this->client->send($request);

            $result       = new stdClass();
            $result->code = $response->getStatusCode();
            $result->body = json_decode((string) $response->getBody(), null, 512, JSON_THROW_ON_ERROR);
        } catch (ClientException $e) {
            throw new ApiCallFailedException($e->getMessage(), $e->getResponse()->getStatusCode(), $e);
        } catch (ServerException $e) {
            throw new ApiCallFailedException('Api call failed to complete due to a server error.', $e->getResponse()->getStatusCode(), $e);
        } catch (Exception $e) {
            throw new ApiCallFailedException($e->getMessage(), $e->getCode(), $e);
        }
        
        // For some resources, the Steam API responds with an empty response
        if (empty($result->body)) {
            throw new ApiCallFailedException('API call failed due to empty response', $result->code);
        }

        if (empty((array)$result->body)) {
            throw new ApiCallFailedException('Api call failed to complete due to an empty response.', $result->code);
        }

        // If all worked out, return the result
        return $result;
    }

    private function buildUrl($version = false): string
    {
        // Set up the basic url
        $url = $this->url . ($this->interface ?? '') . '/' . $this->method . '/';

        // If we have a version, add it
        if ($version) {
            return $url . $this->version . '/';
        }

        return $url;
    }

    /**
     * @throws ClassNotFoundException
     * @throws UnrecognizedId
     */
    public function __call($name, $arguments)
    {
        // Handle a steamId being passed
        if (! empty($arguments) && count($arguments) == 1) {
            $this->steamId = $arguments[0];

            $this->convertSteamIdTo64();
        }

        // Inside the root steam directory
        $class      = ucfirst((string) $name);
        $steamClass = '\Syntax\SteamApi\Steam\\' . $class;

        if (class_exists($steamClass)) {
            return new $steamClass($this->steamId);
        }

        // Inside a nested directory
        $class      = implode('\\', preg_split('/(?=[A-Z])/', $class, -1, PREG_SPLIT_NO_EMPTY));
        $steamClass = '\Syntax\SteamApi\Steam\\' . $class;

        if (class_exists($steamClass)) {
            return new $steamClass($this->steamId);
        }

        // Nothing found
        throw new ClassNotFoundException($name);
    }

    /**
     * @param Collection $objects
     *
     * @return Collection
     */
    protected function sortObjects(Collection $objects): Collection
    {
        return $objects->sortBy(fn($object) => $object->name);
    }

    /**
     * @param string $method
     * @param string $version
     */
    protected function setApiDetails(string $method, string $version): void
    {
        $this->method  = $method;
        $this->version = $version;
    }

    /**
     * @throws ApiArgumentRequired
     * @throws ApiCallFailedException
     * @throws GuzzleException
     * @throws \JsonException
     */
    protected function getServiceResponse($arguments)
    {
        $arguments = json_encode($arguments, JSON_THROW_ON_ERROR);

        // Get the client
        $body = $this->setUpService($arguments);

        if (!isset($body->response) || empty((array)$body->response)) {
            throw new ApiCallFailedException('Api call failed to complete due to an empty response.', 500);
        }

        return $body->response;
    }

    /**
     * @throws ApiCallFailedException
     * @throws GuzzleException
     */
    protected function getClientResponse($arguments)
    {
        // Get the client
        $body = $this->setUpClient($arguments);

        if (!isset($body->response) || empty((array)$body->response)) {
            throw new ApiCallFailedException('Api call failed to complete due to an empty response.', 500);
        }

        return $body->response;
    }

    /**
     * @return string
     * @throws Exceptions\InvalidApiKeyException
     */
    protected function getApiKey(): string
    {
        $apiKey = Config::get('steam-api.steamApiKey');

        if ($apiKey == 'YOUR-API-KEY') {
            throw new Exceptions\InvalidApiKeyException();
        }

        if (is_null($apiKey) || $apiKey === '' || $apiKey == []) {
            $apiKey = getenv('apiKey');
        }

        return $apiKey;
    }

    /**
     * @throws UnrecognizedId
     */
    private function convertSteamIdTo64(): void
    {
        if (is_array($this->steamId)) {
            array_walk(
                /**
                 * @throws UnrecognizedId
                 */
                $this->steamId, function (&$id) {
                // Convert the id to all types and grab the 64 bit version
                $id = $this->convertToAll($id)->id64;
            });
        } else {
            // Convert the id to all types and grab the 64 bit version
            $this->steamId = $this->convertToAll($this->steamId)->id64;
        }
    }
}