kanalumaddela/laravel-steam-login

View on GitHub
src/SteamUser.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php
/*
 * Laravel Steam Login.
 *
 * @link      https://www.maddela.org
 * @link      https://github.com/kanalumaddela/laravel-steam-login
 *
 * @author    kanalumaddela <git@maddela.org>
 * @copyright Copyright (c) 2018-2021 Maddela
 * @license   MIT
 */

namespace kanalumaddela\LaravelSteamLogin;

use function array_merge;
use function config;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Response;
use Illuminate\Support\Fluent;
use function json_decode;
use const JSON_ERROR_NONE;
use function json_last_error;
use function simplexml_load_string;
use function sprintf;
use SteamID;
use function ucfirst;

/**
 * @property string steamId
 * @property string steamId2
 * @property string steamId3
 * @property string accountUrl
 * @property string profileDataUrl
 * @property int    accountId
 * @property string name
 * @property string avatar
 */
class SteamUser extends Fluent
{
    /**
     * Steam Community URL using 64bit steamId.
     *
     * @var string
     */
    const STEAM_PROFILE = 'https://steamcommunity.com/profiles/%s';

    /**
     * Steam Community URL using custom id.
     *
     * @var string
     */
    const STEAM_PROFILE_ID = 'https://steamcommunity.com/id/%s';

    /**
     * Steam API GetPlayerSummaries URL.
     *
     * @var string
     */
    const STEAM_PLAYER_API = 'https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=%s&steamids=%s';

    /**
     * personaStates.
     */
    protected static $personaStates = [
        'Offline',
        'Online',
        'Busy',
        'Away',
        'Snooze',
        'Looking to trade',
        'Looking to play',
    ];

    /**
     * Profile data retrieval method to use.
     *
     * @var string
     */
    protected $method = 'xml';

    /**
     * URL to use when retrieving a user's profile.
     *
     * @var string
     */
    protected $profileDataUrl;

    /**
     * xPaw instance.
     *
     * @var \SteamID
     */
    protected $xPawSteamId;

    /**
     * Guzzle instance.
     *
     * @var \GuzzleHttp\Client
     */
    protected $guzzle;

    /**
     * Guzzle response.
     *
     * @var \GuzzleHttp\Psr7\Response
     */
    protected $response;

    /**
     * SteamUser constructor. Extends SteamID and constructs that first.
     *
     * @param string|int        $steamId
     * @param GuzzleClient|null $guzzle
     */
    public function __construct($steamId, GuzzleClient $guzzle = null)
    {
        if (PHP_INT_SIZE !== 8) {
            trigger_error('64 bit PHP is required to handle SteamID conversions', E_USER_ERROR);
        }

        $xPawSteamId = new SteamID($steamId);

        $this->attributes['steamId'] = $this->attributes['steam64'] = $xPawSteamId->ConvertToUInt64();
        $this->attributes['steamId2'] = $xPawSteamId->RenderSteam2();
        $this->attributes['steamId3'] = $xPawSteamId->RenderSteam3();
        $this->attributes['accountId'] = $xPawSteamId->GetAccountID();
        $this->attributes['accountUrl'] = sprintf(self::STEAM_PROFILE, $this->attributes['steamId']);
        $this->attributes['profileDataUrl'] = sprintf(self::STEAM_PROFILE.'/?xml=1', $this->attributes['steamId']);
        $this->xPawSteamId = $xPawSteamId;

        unset($steamId);
        unset($xPawSteamId);

        $this->guzzle = $guzzle ?? new GuzzleClient();
        $this->method = config('steam-login.method', 'xml') === 'api' ? 'api' : 'xml';
        $this->profileDataUrl = $this->method === 'xml' ? $this->attributes['profileDataUrl'] : sprintf(self::STEAM_PLAYER_API, config('steam-login.api_key'), $this->attributes['steamId']);

        parent::__construct($this->attributes);
    }

    /**
     * magic method __toString using Fluent toJson().
     *
     * @return string
     */
    public function __toString(): string
    {
        return $this->toJson();
    }

    /**
     * Get xpaw SteamID instance.
     *
     * @return \SteamID
     */
    public function getXpawSteamID(): ?SteamID
    {
        return $this->xPawSteamId;
    }

    /**
     * Retrieve a user's steam info set its attributes.
     *
     * @return $this
     */
    public function getUserInfo(): self
    {
        $this->userInfo();

        return $this;
    }

    /**
     * Retrieve a user's profile info from Steam via API or XML data.
     */
    protected function userInfo()
    {
        try {
            $this->response = $this->guzzle->get($this->profileDataUrl, ['connect_timeout' => config('steam-login.timeout')]);
            $body = $this->response->getBody()->getContents();

            switch ($this->method) {
                case 'api':
                    $data = $this->parseApiProfileData($body);
                    break;
                case 'xml':
                    $data = $this->parseXmlProfileData($body);
                    break;
                default:
                    $data = [];
                    break;
            }

            $this->attributes = array_merge($this->attributes, $data);
        } catch (GuzzleException $e) {
        }
    }

    protected static function parseApiProfileData($body): array
    {
        $json = @json_decode($body, true);
        $json = $json['response']['players'][0] ?? null;

        if (empty($body) || $json === null || json_last_error() !== JSON_ERROR_NONE) {
            return [];
        }

        $offlineStates = [
            0 => true,
            4 => true,
        ];

        return [
            'name'            => $json['personaname'],
            'realName'        => $json['realname'] ?? null,
            'profileUrl'      => $json['profileurl'],
            'isPublic'        => $json['communityvisibilitystate'] === 3,
            'privacyState'    => $json['communityvisibilitystate'] === 3 ? 'Public' : 'Private',
            'visibilityState' => $json['communityvisibilitystate'],
            'isOnline'        => !isset($offlineStates[$json['personastate']]),
            'onlineState'     => isset($data['gameid']) ? 'In-Game' : (!isset($offlineStates[$json['personastate']]) ? 'Online' : 'Offline'),
            'joined'          => $json['timecreated'] ?? null,
            'avatarIcon'      => $json['avatar'],
            'avatarSmall'     => $json['avatar'],
            'avatarMedium'    => $json['avatarmedium'],
            'avatarFull'      => $json['avatarfull'],
            'avatarLarge'     => $json['avatarfull'],
            'avatar'          => $json['avatarfull'],
        ];
    }

    protected static function parseXmlProfileData($body): array
    {
        $xml = simplexml_load_string($body, 'SimpleXMLElement', LIBXML_NOCDATA);

        if (empty($body) || $xml === false || isset($xml->error)) {
            return [];
        }

        return [
            'name'            => (string) $xml->steamID,
            'realName'        => isset($xml->realName) ? (string) $xml->realName : null,
            'profileUrl'      => sprintf((isset($xml->customURL) ? static::STEAM_PROFILE : static::STEAM_PROFILE_ID), $xml->customURL ?? $xml->steamID64),
            'isPublic'        => $xml->privacyState === 'public',
            'privacyState'    => $xml->privacyState === 'public' ? 'Public' : 'Private',
            'visibilityState' => (int) $xml->visibilityState,
            'isOnline'        => $xml->onlineState !== 'offline',
            'onlineState'     => $xml->onlineState === 'in-game' ? 'In-Game' : ucfirst($xml->onlineState),
            'joined'          => isset($xml->memberSince) ? strtotime($xml->memberSince) : null,
            'avatarIcon'      => (string) $xml->avatarIcon,
            'avatarSmall'     => (string) $xml->avatarIcon,
            'avatarMedium'    => (string) $xml->avatarMedium,
            'avatarFull'      => (string) $xml->avatarFull,
            'avatarLarge'     => (string) $xml->avatarFull,
            'avatar'          => (string) $xml->avatarFull,
        ];
    }

    /**
     * Return Guzzle response of retrieving player's profile data.
     *
     * @return Response
     */
    public function getResponse(): Response
    {
        return $this->response;
    }
}