1
0
Fork 0
mirror of https://codeberg.org/demostf/api.git synced 2026-06-03 18:04:08 +02:00

type hint all the things

This commit is contained in:
Robin Appelman 2020-11-28 23:37:02 +01:00
commit 3f9e613e77
34 changed files with 287 additions and 309 deletions

View file

@ -5,7 +5,7 @@ name: CI
jobs: jobs:
php-cs-fixer: php-cs-fixer:
name: PHP-CS-Fixer name: PHP-CS-Fixer
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- name: PHP-CS-Fixer - name: PHP-CS-Fixer
@ -15,7 +15,7 @@ jobs:
phpstan: phpstan:
name: PHPStan Static Analysis name: PHPStan Static Analysis
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -23,10 +23,10 @@ jobs:
- uses: php-actions/phpstan@v1 - uses: php-actions/phpstan@v1
with: with:
error_format: github error_format: github
args: --level 5 src args: --level 6 src
phpunit: phpunit:
runs-on: ubuntu-latest runs-on: ubuntu-20.04
name: Unit tests name: Unit tests
services: services:

View file

@ -7,7 +7,9 @@
"koraktor/steam-condenser": "^1.3", "koraktor/steam-condenser": "^1.3",
"guzzlehttp/guzzle": "^6.5", "guzzlehttp/guzzle": "^6.5",
"doctrine/dbal": "^2.12", "doctrine/dbal": "^2.12",
"ext-pdo": "*" "ext-pdo": "*",
"ext-json": "*",
"ext-apcu": "*"
}, },
"autoload": { "autoload": {
"files": [ "files": [

View file

@ -24,17 +24,17 @@ use flight\net\Response;
use RandomLib\Generator; use RandomLib\Generator;
class Container { class Container {
private $connection; private Connection $connection;
private $generator; private Generator $generator;
private $baseUrl; private string $baseUrl;
private $parserPath; private string $parserPath;
private $storeRoot; private string $storeRoot;
private $storeUrl; private string $storeUrl;
private $apiRoot; private string $apiRoot;
private $editKey; private string $editKey;
private $request; private Request $request;
private $response; private Response $response;
private $uploadKey; private string $uploadKey;
public function __construct( public function __construct(
Request $request, Request $request,

View file

@ -13,20 +13,10 @@ use flight\net\Response;
use SteamId; use SteamId;
class AuthController extends BaseController { class AuthController extends BaseController {
/** private UserProvider $userProvider;
* @var UserProvider private AuthProvider $authProvider;
*/ private string $host;
private $userProvider; private string $apiRoot;
/**
* @var AuthProvider
*/
private $authProvider;
/** @var string */
private $host;
private $apiRoot;
public function __construct( public function __construct(
Request $request, Request $request,
@ -43,11 +33,11 @@ class AuthController extends BaseController {
$this->apiRoot = $apiRoot; $this->apiRoot = $apiRoot;
} }
public function token() { public function token(): void {
echo $this->authProvider->generateToken(); echo $this->authProvider->generateToken();
} }
public function get($token) { public function get(string $token): void {
$userData = $this->authProvider->getUser($token); $userData = $this->authProvider->getUser($token);
Flight::json([ Flight::json([
'token' => $token, 'token' => $token,
@ -57,14 +47,14 @@ class AuthController extends BaseController {
]); ]);
} }
public function login($token) { public function login(string $token): void {
$_SESSION['return'] = $this->query('return', 'https://' . $this->host); $_SESSION['return'] = $this->query('return', 'https://' . $this->host);
$steam = new SteamLogin(); $steam = new SteamLogin();
$url = $steam->url($this->apiRoot . '/auth/handle/' . urlencode($token), $this->apiRoot); $url = $steam->url($this->apiRoot . '/auth/handle/' . urlencode($token), $this->apiRoot);
Flight::redirect(str_replace('&', '&', $url)); // headers make no sense Flight::redirect(str_replace('&', '&', $url)); // headers make no sense
} }
public function logout($token) { public function logout(string $token): void {
$this->authProvider->logout($token); $this->authProvider->logout($token);
Flight::json([ Flight::json([
'token' => $token, 'token' => $token,
@ -74,7 +64,7 @@ class AuthController extends BaseController {
]); ]);
} }
public function handle($token) { public function handle(string $token): void {
$return = $_SESSION['return'] ?? 'https://' . $this->host; $return = $_SESSION['return'] ?? 'https://' . $this->host;
unset($_SESSION['return']); unset($_SESSION['return']);
$steam = new SteamLogin(); $steam = new SteamLogin();

View file

@ -8,32 +8,48 @@ use flight\net\Request;
use flight\net\Response; use flight\net\Response;
class BaseController { class BaseController {
private $request; private Request $request;
private $response; private Response $response;
public function __construct(Request $request, Response $response) { public function __construct(Request $request, Response $response) {
$this->request = $request; $this->request = $request;
$this->response = $response; $this->response = $response;
} }
protected function query($name, $default) { /**
* @return string[]|string
*/
protected function query(string $name, string $default) {
return $this->request->query[$name] ?? $default; return $this->request->query[$name] ?? $default;
} }
protected function file($name) { /**
* @return string[]|null
*/
protected function file(string $name): ?array {
return $this->request->files[$name]; return $this->request->files[$name];
} }
protected function post($name, $default = null) { protected function post(string $name, string $default): string {
return $this->request->data[$name] ?? $default; return $this->request->data[$name] ?? $default;
} }
protected function json($data, $code = 200, $encode = true, $charset = 'utf-8', $option = 0) { /**
* @param mixed $data
*
* @throws \Exception
*/
protected function json(
$data,
int $code = 200,
bool $encode = true,
string $charset = 'utf-8',
int $option = 0
): void {
$json = ($encode) ? json_encode($data, $option) : $data; $json = ($encode) ? json_encode($data, $option) : $data;
$this->response $this->response->status($code);
->status($code) $this->response->header('Content-Type', 'application/json; charset=' . $charset)
->header('Content-Type', 'application/json; charset=' . $charset)
->write($json) ->write($json)
->send(); ->send();
} }

View file

@ -14,17 +14,11 @@ use flight\net\Request;
use flight\net\Response; use flight\net\Response;
class DemoController extends BaseController { class DemoController extends BaseController {
/** @var DemoProvider */ private DemoProvider $demoProvider;
private $demoProvider; private ChatProvider $chatProvider;
private DemoListProvider $demoListProvider;
/** @var ChatProvider */ private string $editKey;
private $chatProvider; private DemoStore $store;
private $demoListProvider;
private $editKey;
private $store;
public function __construct( public function __construct(
Request $request, Request $request,
@ -43,14 +37,14 @@ class DemoController extends BaseController {
$this->editKey = $editKey; $this->editKey = $editKey;
} }
/** public function get(string $id): void {
* @param string $id
*/
public function get($id) {
$this->json($this->demoProvider->get(\intval($id, 10))); $this->json($this->demoProvider->get(\intval($id, 10)));
} }
protected function getFilter() { /**
* @return array<mixed>
*/
protected function getFilter(): array {
$map = $this->query('map', ''); $map = $this->query('map', '');
$players = $this->query('players', ''); $players = $this->query('players', '');
$type = $this->query('type', ''); $type = $this->query('type', '');
@ -110,29 +104,29 @@ class DemoController extends BaseController {
return $filter; return $filter;
} }
public function listDemos() { public function listDemos(): void {
$page = $this->query('page', 1); $page = (int) $this->query('page', '1');
$order = 'ASC' === $this->query('order', 'DESC') ? 'ASC' : 'DESC'; $order = 'ASC' === $this->query('order', 'DESC') ? 'ASC' : 'DESC';
$this->json($this->demoListProvider->listDemos((int) $page, $this->getFilter(), $order)); $this->json($this->demoListProvider->listDemos((int) $page, $this->getFilter(), $order));
} }
public function listProfile($steamId) { public function listProfile(string $steamId): void {
$page = $this->query('page', 1); $page = (int) $this->query('page', '1');
$where = $this->getFilter(); $where = $this->getFilter();
$where['players'][] = $steamId; $where['players'][] = $steamId;
$this->json($this->demoListProvider->listProfile((int) $page, $where)); $this->json($this->demoListProvider->listProfile((int) $page, $where));
} }
public function listUploads($steamId) { public function listUploads(string $steamId): void {
$page = $this->query('page', 1); $page = (int) $this->query('page', '1');
$this->json($this->demoListProvider->listUploads($steamId, (int) $page, $this->getFilter())); $this->json($this->demoListProvider->listUploads($steamId, (int) $page, $this->getFilter()));
} }
public function chat($demoId) { public function chat(string $demoId): void {
$this->json($this->chatProvider->getChat((int) $demoId)); $this->json($this->chatProvider->getChat((int) $demoId));
} }
public function setDemoUrl($id) { public function setDemoUrl(string $id): void {
$hash = (string) $this->post('hash', ''); $hash = (string) $this->post('hash', '');
$backend = (string) $this->post('backend', ''); $backend = (string) $this->post('backend', '');
$path = (string) $this->post('path', ''); $path = (string) $this->post('path', '');

View file

@ -10,19 +10,18 @@ use flight\net\Request;
use flight\net\Response; use flight\net\Response;
class InfoController extends BaseController { class InfoController extends BaseController {
/** @var InfoProvider */ private InfoProvider $infoProvider;
private $infoProvider;
public function __construct(Request $request, Response $response, InfoProvider $infoProvider) { public function __construct(Request $request, Response $response, InfoProvider $infoProvider) {
parent::__construct($request, $response); parent::__construct($request, $response);
$this->infoProvider = $infoProvider; $this->infoProvider = $infoProvider;
} }
public function listMaps() { public function listMaps(): void {
Flight::json($this->infoProvider->listMaps()); Flight::json($this->infoProvider->listMaps());
} }
public function stats() { public function stats(): void {
Flight::json($this->infoProvider->getStats()); Flight::json($this->infoProvider->getStats());
} }
} }

View file

@ -4,22 +4,19 @@ declare(strict_types=1);
namespace Demostf\API\Controllers; namespace Demostf\API\Controllers;
use Demostf\API\Error\InvalidKeyException;
use Demostf\API\Providers\UploadProvider; use Demostf\API\Providers\UploadProvider;
use Exception;
use Flight;
use flight\net\Request; use flight\net\Request;
use flight\net\Response; use flight\net\Response;
class UploadController extends BaseController { class UploadController extends BaseController {
private $uploadProvider; private UploadProvider $uploadProvider;
public function __construct(Request $request, Response $response, UploadProvider $uploadProvider) { public function __construct(Request $request, Response $response, UploadProvider $uploadProvider) {
parent::__construct($request, $response); parent::__construct($request, $response);
$this->uploadProvider = $uploadProvider; $this->uploadProvider = $uploadProvider;
} }
public function upload() { public function upload(): void {
$key = (string) $this->post('key', ''); $key = (string) $this->post('key', '');
$red = (string) $this->post('red', 'RED'); $red = (string) $this->post('red', 'RED');
$blu = (string) $this->post('blu', 'BLU'); $blu = (string) $this->post('blu', 'BLU');

View file

@ -12,17 +12,14 @@ use flight\net\Response;
use InvalidArgumentException; use InvalidArgumentException;
class UserController extends BaseController { class UserController extends BaseController {
/** private UserProvider $userProvider;
* @var UserProvider
*/
private $userProvider;
public function __construct(Request $request, Response $response, UserProvider $userProvider) { public function __construct(Request $request, Response $response, UserProvider $userProvider) {
parent::__construct($request, $response); parent::__construct($request, $response);
$this->userProvider = $userProvider; $this->userProvider = $userProvider;
} }
public function get($steamId) { public function get(string $steamId): void {
if (!is_numeric($steamId)) { if (!is_numeric($steamId)) {
try { try {
$steamId = Parser::convertSteamIdToCommunityId($steamId); $steamId = Parser::convertSteamIdToCommunityId($steamId);
@ -36,7 +33,7 @@ class UserController extends BaseController {
Flight::json($this->userProvider->get($steamId)); Flight::json($this->userProvider->get($steamId));
} }
public function search() { public function search(): void {
$query = $this->query('query', ''); $query = $this->query('query', '');
Flight::json($this->userProvider->search($query)); Flight::json($this->userProvider->search($query));
} }

View file

@ -7,26 +7,16 @@ namespace Demostf\API\Data;
use JsonSerializable; use JsonSerializable;
class DemoPlayer implements JsonSerializable { class DemoPlayer implements JsonSerializable {
/** @var int */ private int $id;
private $id; private int $userId;
/** @var int */ private string $name;
private $userId; private string $team;
/** @var string */ private string $class;
private $name; private string $steamId;
/** @var string */ private string $avatar;
private $team; private int $kills;
/** @var string */ private int $assists;
private $class; private int $deaths;
/** @var string */
private $steamId;
/** @var string */
private $avatar;
/** @var int */
private $kills;
/** @var int */
private $assists;
/** @var int */
private $deaths;
public function __construct(int $id, int $userId, string $name, string $team, string $class, string $steamId, string $avatar, int $kills, int $assists, int $deaths) { public function __construct(int $id, int $userId, string $name, string $team, string $class, string $steamId, string $avatar, int $kills, int $assists, int $deaths) {
$this->id = $id; $this->id = $id;
@ -81,7 +71,12 @@ class DemoPlayer implements JsonSerializable {
return $this->deaths; return $this->deaths;
} }
public static function fromRow($row): self { /**
* @param mixed[] $row
*
* @return DemoPlayer
*/
public static function fromRow(array $row): self {
return new self( return new self(
$row['id'], $row['id'],
$row['user_id'], $row['user_id'],

View file

@ -5,17 +5,12 @@ declare(strict_types=1);
namespace Demostf\API\Data; namespace Demostf\API\Data;
class Kill { class Kill {
private $id; private int $id;
private int $demoId;
private $demoId; private int $attackerId;
private int $assisterId;
private $attackerId; private int $victimId;
private string $weapon;
private $assisterId;
private $victimId;
private $weapon;
public function __construct(int $id, int $demoId, int $attackerId, int $assisterId, int $victimId, string $weapon) { public function __construct(int $id, int $demoId, int $attackerId, int $assisterId, int $victimId, string $weapon) {
$this->id = $id; $this->id = $id;

View file

@ -7,16 +7,14 @@ namespace Demostf\API\Data;
use Demostf\API\Demo\ChatMessage; use Demostf\API\Demo\ChatMessage;
class ParsedDemo { class ParsedDemo {
/** @var int */ private int $redScore;
private $redScore; private int $blueScore;
/** @var int */
private $blueScore;
/** @var ChatMessage[] */ /** @var ChatMessage[] */
private $chat; private array $chat;
/** @var ParsedPlayer[] */ /** @var ParsedPlayer[] */
private $players; private array $players;
/** @var ParsedKill[] */ /** @var ParsedKill[] */
private $kills; private array $kills;
/** /**
* ParsedDemo constructor. * ParsedDemo constructor.

View file

@ -5,13 +5,10 @@ declare(strict_types=1);
namespace Demostf\API\Data; namespace Demostf\API\Data;
class ParsedKill { class ParsedKill {
private $attackerDemoId; private int $attackerDemoId;
private int $assisterDemoId;
private $assisterDemoId; private int $victimDemoId;
private string $weapon;
private $victimDemoId;
private $weapon;
public function __construct(int $attackerDemoId, int $assisterDemoId, int $victimDemoId, string $weapon) { public function __construct(int $attackerDemoId, int $assisterDemoId, int $victimDemoId, string $weapon) {
$this->attackerDemoId = $attackerDemoId; $this->attackerDemoId = $attackerDemoId;

View file

@ -5,16 +5,11 @@ declare(strict_types=1);
namespace Demostf\API\Data; namespace Demostf\API\Data;
class ParsedPlayer { class ParsedPlayer {
/** @var string */ private string $name;
private $name; private int $demoUserId;
/** @var int */ private string $steamId;
private $demoUserId; private string $team;
/** @var string */ private string $class;
private $steamId;
/** @var string */
private $team;
/** @var string` */
private $class;
public function __construct(string $name, int $demoUserId, string $steamId, string $team, string $class) { public function __construct(string $name, int $demoUserId, string $steamId, string $team, string $class) {
$this->name = $name; $this->name = $name;

View file

@ -5,26 +5,13 @@ declare(strict_types=1);
namespace Demostf\API\Data; namespace Demostf\API\Data;
class Player { class Player {
/** @var int */ private int $id;
private $id; private int $demoId;
private int $demoUserId;
/** @var int */ private int $userId;
private $demoId; private string $name;
private string $team;
/** @var int */ private string $class;
private $demoUserId;
/** @var int */
private $userId;
/** @var string */
private $name;
/** @var string */
private $team;
/** @var string */
private $class;
public function __construct(int $id, int $demoId, int $demoUserId, int $userId, string $name, string $team, string $class) { public function __construct(int $id, int $demoId, int $demoUserId, int $userId, string $name, string $team, string $class) {
$this->id = $id; $this->id = $id;

View file

@ -40,6 +40,11 @@ class SteamUser implements JsonSerializable {
]; ];
} }
/**
* @param mixed[] $row
*
* @return SteamUser
*/
public static function fromRow(array $row): self { public static function fromRow(array $row): self {
return new self( return new self(
(int) $row['id'], (int) $row['id'],

View file

@ -5,12 +5,9 @@ declare(strict_types=1);
namespace Demostf\API\Data; namespace Demostf\API\Data;
class StoredDemo { class StoredDemo {
/** @var string */ private string $url;
private $url; private string $backend;
/** @var string */ private string $path;
private $backend;
/** @var string */
private $path;
public function __construct(string $url, string $backend, string $path) { public function __construct(string $url, string $backend, string $path) {
$this->url = $url; $this->url = $url;

View file

@ -5,16 +5,11 @@ declare(strict_types=1);
namespace Demostf\API\Data; namespace Demostf\API\Data;
class Upload { class Upload {
/** @var string */ private string $name;
private $name; private string $red;
/** @var string */ private string $blue;
private $red; private int $uploaderId;
/** @var string */ private string $hash;
private $blue;
/** @var int */
private $uploaderId;
/** @var string */
private $hash;
public function __construct(string $name, string $red, string $blue, int $uploaderId, string $hash) { public function __construct(string $name, string $red, string $blue, int $uploaderId, string $hash) {
$this->name = $name; $this->name = $name;

View file

@ -7,16 +7,11 @@ namespace Demostf\API\Data;
use JsonSerializable; use JsonSerializable;
class User implements JsonSerializable { class User implements JsonSerializable {
/** @var int */ private int $id;
private $id; private string $steamId;
/** @var string */ private string $name;
private $steamId; private string $avatar;
/** @var string */ private string $token;
private $name;
/** @var string */
private $avatar;
/** @var string */
private $token;
public function __construct(int $id, string $steamId, string $name, string $avatar, string $token) { public function __construct(int $id, string $steamId, string $name, string $avatar, string $token) {
$this->id = $id; $this->id = $id;
@ -55,6 +50,11 @@ class User implements JsonSerializable {
]; ];
} }
/**
* @param mixed[] $row
*
* @return User
*/
public static function fromRow(array $row): self { public static function fromRow(array $row): self {
return new self( return new self(
(int) $row['id'], (int) $row['id'],

View file

@ -10,44 +10,26 @@ use Demostf\API\Data\User;
use JsonSerializable; use JsonSerializable;
class Demo implements JsonSerializable { class Demo implements JsonSerializable {
/** @var int */ private int $id;
private $id; private string $url;
/** @var string */ private string $name;
private $url; private string $server;
/** @var string */ private float $duration;
private $name; private string $nick;
/** @var string */ private string $map;
private $server; private DateTime $time;
/** @var float */ private string $red;
private $duration; private string $blue;
/** @var string */ private int $redScore;
private $nick; private int $blueScore;
/** @var string */ private int $playerCount;
private $map; private int $uploader;
/** @var DateTime */ private ?User $uploaderUser;
private $time;
/** @var string */
private $red;
/** @var string */
private $blue;
/** @var int */
private $redScore;
/** @var int */
private $blueScore;
/** @var int */
private $playerCount;
/** @var int */
private $uploader;
/** @var User|null */
private $uploaderUser;
/** @var DemoPlayer[] */ /** @var DemoPlayer[] */
private $players; private array $players;
/** @var string */ private string $hash;
private $hash; private string $backend;
/** @var string */ private string $path;
private $backend;
/** @var string */
private $path;
public function __construct( public function __construct(
int $id, int $id,
@ -85,6 +67,7 @@ class Demo implements JsonSerializable {
$this->hash = $hash; $this->hash = $hash;
$this->backend = $backend; $this->backend = $backend;
$this->path = $path; $this->path = $path;
$this->players = [];
} }
public function getId(): int { public function getId(): int {
@ -147,11 +130,16 @@ class Demo implements JsonSerializable {
return $this->uploaderUser; return $this->uploaderUser;
} }
public function setUploaderUser(User $uploaderUser) { public function setUploaderUser(User $uploaderUser): void {
$this->uploaderUser = $uploaderUser; $this->uploaderUser = $uploaderUser;
} }
public static function fromRow($row): self { /**
* @param mixed[] $row
*
* @return Demo
*/
public static function fromRow(array $row): self {
return new self( return new self(
(int) $row['id'], (int) $row['id'],
$row['url'], $row['url'],
@ -180,7 +168,10 @@ class Demo implements JsonSerializable {
return $this->players; return $this->players;
} }
public function setPlayers(array $players) { /**
* @param DemoPlayer[] $players
*/
public function setPlayers(array $players): void {
$this->players = $players; $this->players = $players;
} }

View file

@ -18,17 +18,12 @@ use Demostf\API\Providers\UserProvider;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
class DemoSaver { class DemoSaver {
/** @var KillProvider */ private KillProvider $killProvider;
private $killProvider; private PlayerProvider $playerProvider;
/** @var PlayerProvider */ private ChatProvider $chatProvider;
private $playerProvider; private UserProvider $userProvider;
/** @var ChatProvider */ private DemoProvider $demoProvider;
private $chatProvider; private Connection $connection;
/** @var UserProvider */
private $userProvider;
/** @var DemoProvider */
private $demoProvider;
private $connection;
public function __construct( public function __construct(
KillProvider $killProvider, KillProvider $killProvider,

View file

@ -40,7 +40,7 @@ class DemoStore {
return 'https://' . $this->webRoot . $this->getPrefix($name) . $name; return 'https://' . $this->webRoot . $this->getPrefix($name) . $name;
} }
public function remove(Demo $demo) { public function remove(Demo $demo): void {
if (file_exists($demo->getPath())) { if (file_exists($demo->getPath())) {
unlink($demo->getPath()); unlink($demo->getPath());
} }

View file

@ -133,7 +133,12 @@ class Header {
return $this->version; return $this->version;
} }
public static function fromArray(array $info) { /**
* @param mixed[] $info
*
* @return Header
*/
public static function fromArray(array $info): self {
return new self( return new self(
$info['type'], $info['type'],
$info['version'], $info['version'],

View file

@ -44,6 +44,11 @@ class Parser {
} }
} }
/**
* @param mixed[] $data
*
* @throws Exception
*/
private function handleData(array $data): ParsedDemo { private function handleData(array $data): ParsedDemo {
$intervalPerTick = $data['intervalPerTick']; $intervalPerTick = $data['intervalPerTick'];
$red = 0; $red = 0;
@ -159,12 +164,12 @@ class Parser {
} }
if (preg_match('/^STEAM_[0-1]:[0-1]:[0-9]+$/', $steamId)) { if (preg_match('/^STEAM_[0-1]:[0-1]:[0-9]+$/', $steamId)) {
$steamParts = explode(':', substr($steamId, 8)); $steamParts = explode(':', substr($steamId, 8));
$steamId = (int)$steamParts[0] + (int)$steamParts[1] * 2 + 1197960265728; $steamId = (int) $steamParts[0] + (int) $steamParts[1] * 2 + 1197960265728;
return '7656' . $steamId; return '7656' . $steamId;
} elseif (preg_match('/^\[U:[0-1]:[0-9]+\]$/', $steamId)) { } elseif (preg_match('/^\[U:[0-1]:[0-9]+\]$/', $steamId)) {
$steamParts = explode(':', substr($steamId, 3, -1)); $steamParts = explode(':', substr($steamId, 3, -1));
$steamId = (int)$steamParts[0] + (int)$steamParts[1] + 1197960265727; $steamId = (int) $steamParts[0] + (int) $steamParts[1] + 1197960265727;
return '7656' . $steamId; return '7656' . $steamId;
} else { } else {

View file

@ -22,6 +22,8 @@ class RawParser {
/** /**
* @throws Exception * @throws Exception
*
* @return mixed[]|null
*/ */
public function parse(string $path): ?array { public function parse(string $path): ?array {
try { try {

View file

@ -23,7 +23,7 @@ class AuthProvider extends BaseProvider {
return $this->generator->generateString(32, Generator::CHAR_ALNUM); return $this->generator->generateString(32, Generator::CHAR_ALNUM);
} }
public function setUser(string $token, SteamId $steamid, string $key) { public function setUser(string $token, SteamId $steamid, string $key): void {
apcu_store($token, [ apcu_store($token, [
'name' => $steamid->getNickname(), 'name' => $steamid->getNickname(),
'steamid' => $steamid->getSteamId64(), 'steamid' => $steamid->getSteamId64(),
@ -31,14 +31,17 @@ class AuthProvider extends BaseProvider {
]); ]);
} }
public function getUser(string $token) { /**
* @return (string|null)[]
*/
public function getUser(string $token): array {
$found = true; $found = true;
$result = apcu_fetch($token, $found); $result = apcu_fetch($token, $found);
return $found ? $result : ['name' => null, 'steamid' => null, 'key' => null]; return $found ? $result : ['name' => null, 'steamid' => null, 'key' => null];
} }
public function logout($token) { public function logout(string $token): void {
apcu_delete($token); apcu_delete($token);
} }
} }

View file

@ -8,10 +8,7 @@ use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Query\QueryBuilder;
class BaseProvider { class BaseProvider {
/** protected Connection $connection;
* @var Connection
*/
protected $connection;
/** /**
* BaseProvider constructor. * BaseProvider constructor.
@ -27,13 +24,6 @@ class BaseProvider {
$this->connection = $connection; $this->connection = $connection;
} }
protected function query(string $sql, array $params = []) {
$query = $this->connection->prepare($sql);
$query->execute($params);
return $query;
}
/** /**
* @return QueryBuilder * @return QueryBuilder
*/ */

View file

@ -8,7 +8,10 @@ use Demostf\API\Demo\ChatMessage;
use PDO; use PDO;
class ChatProvider extends BaseProvider { class ChatProvider extends BaseProvider {
public function getChat(int $demoId) { /**
* @return ChatMessage[]
*/
public function getChat(int $demoId): array {
$query = $this->getQueryBuilder(); $query = $this->getQueryBuilder();
$query->select('text', '"from"', 'time') $query->select('text', '"from"', 'time')
->from('chat') ->from('chat')
@ -26,7 +29,7 @@ class ChatProvider extends BaseProvider {
}, $result->fetchAll()); }, $result->fetchAll());
} }
public function storeChatMessage(int $demoId, ChatMessage $message) { public function storeChatMessage(int $demoId, ChatMessage $message): void {
$query = $this->getQueryBuilder(); $query = $this->getQueryBuilder();
$query->insert('chat') $query->insert('chat')
->values([ ->values([

View file

@ -10,6 +10,13 @@ use Doctrine\DBAL\Query\QueryBuilder;
use PDO; use PDO;
class DemoListProvider extends BaseProvider { class DemoListProvider extends BaseProvider {
/**
* @param mixed[] $where
*
* @throws \Doctrine\DBAL\Exception
*
* @return Demo[]
*/
public function listUploads(string $steamId, int $page, array $where = []) { public function listUploads(string $steamId, int $page, array $where = []) {
$query = $this->getQueryBuilder(); $query = $this->getQueryBuilder();
$query->select('id') $query->select('id')
@ -25,11 +32,19 @@ class DemoListProvider extends BaseProvider {
return $this->listDemos($page, $where); return $this->listDemos($page, $where);
} }
public function listProfile(int $page, array $where = []) { /**
* @param mixed[] $where
*
* @throws \Doctrine\DBAL\Exception
*
* @return Demo[]
*/
public function listProfile(int $page, array $where = []): array {
$query = $this->getQueryBuilder(); $query = $this->getQueryBuilder();
$query->select('id') $query->select('id')
->from('users') ->from('users')
->where($query->expr()->in('steamid', $query->createNamedParameter($where['players'], Connection::PARAM_STR_ARRAY))); ->where($query->expr()->in('steamid',
$query->createNamedParameter($where['players'], Connection::PARAM_STR_ARRAY)));
unset($where['players']); unset($where['players']);
$result = $query->execute(); $result = $query->execute();
$userIds = $result->fetchAll(PDO::FETCH_COLUMN); $userIds = $result->fetchAll(PDO::FETCH_COLUMN);
@ -73,7 +88,10 @@ class DemoListProvider extends BaseProvider {
return $this->formatList($query->execute()->fetchAll()); return $this->formatList($query->execute()->fetchAll());
} }
private function addWhere(QueryBuilder $query, array $where = []) { /**
* @param mixed[] $where
*/
private function addWhere(QueryBuilder $query, array $where = []): void {
if (isset($where['map'])) { if (isset($where['map'])) {
$query->andWhere($query->expr()->orX( $query->andWhere($query->expr()->orX(
$query->expr()->eq('clean_map_name(map)', $query->createNamedParameter($where['map'])), $query->expr()->eq('clean_map_name(map)', $query->createNamedParameter($where['map'])),
@ -103,9 +121,13 @@ class DemoListProvider extends BaseProvider {
} }
/** /**
* @param mixed[] $where
*
* @throws \Doctrine\DBAL\Exception
*
* @return Demo[] * @return Demo[]
*/ */
public function listDemos(int $page, array $where = [], string $order = 'DESC') { public function listDemos(int $page, array $where = [], string $order = 'DESC'): array {
if (isset($where['players']) and \is_array($where['players']) and \count($where['players']) > 0) { if (isset($where['players']) and \is_array($where['players']) and \count($where['players']) > 0) {
return $this->listProfile($page, $where); return $this->listProfile($page, $where);
} }
@ -127,7 +149,12 @@ class DemoListProvider extends BaseProvider {
return $this->formatList($demos); return $this->formatList($demos);
} }
protected function formatList(array $rows) { /**
* @param array[] $rows
*
* @return Demo[]
*/
protected function formatList(array $rows): array {
return array_map(function ($row) { return array_map(function ($row) {
return Demo::fromRow($row); return Demo::fromRow($row);
}, $rows); }, $rows);

View file

@ -13,7 +13,7 @@ use PDO;
class DemoProvider extends BaseProvider { class DemoProvider extends BaseProvider {
const VERSION = 4; const VERSION = 4;
private $userProvider; private UserProvider $userProvider;
public function __construct(Connection $connection, UserProvider $userProvider) { public function __construct(Connection $connection, UserProvider $userProvider) {
parent::__construct($connection); parent::__construct($connection);
@ -49,7 +49,7 @@ class DemoProvider extends BaseProvider {
if ($fetchDetails) { if ($fetchDetails) {
$uploader = $this->userProvider->getById($demo->getUploader()); $uploader = $this->userProvider->getById($demo->getUploader());
$playerQuery = $this->query($sql, [$demo->getId(), $demo->getId()]); $playerQuery = $this->connection->executeQuery($sql, [$demo->getId(), $demo->getId()]);
$players = $playerQuery->fetchAll(PDO::FETCH_ASSOC); $players = $playerQuery->fetchAll(PDO::FETCH_ASSOC);
$demo->setUploaderUser($uploader); $demo->setUploaderUser($uploader);
@ -105,7 +105,7 @@ class DemoProvider extends BaseProvider {
return (int) $this->connection->lastInsertId(); return (int) $this->connection->lastInsertId();
} }
public function setDemoUrl(int $id, string $backend, string $url, string $path) { public function setDemoUrl(int $id, string $backend, string $url, string $path): void {
$query = $this->getQueryBuilder(); $query = $this->getQueryBuilder();
$query->update('demos') $query->update('demos')
->set('backend', $query->createNamedParameter($backend)) ->set('backend', $query->createNamedParameter($backend))

View file

@ -7,7 +7,10 @@ namespace Demostf\API\Providers;
use PDO; use PDO;
class InfoProvider extends BaseProvider { class InfoProvider extends BaseProvider {
public function listMaps() { /**
* @return string[]
*/
public function listMaps(): array {
$query = $this->getQueryBuilder(); $query = $this->getQueryBuilder();
$query->select('map', 'count') $query->select('map', 'count')
->from('map_list'); ->from('map_list');
@ -24,7 +27,10 @@ class InfoProvider extends BaseProvider {
return $query->execute()->fetch(PDO::FETCH_COLUMN); return $query->execute()->fetch(PDO::FETCH_COLUMN);
} }
public function getStats() { /**
* @return int[]
*/
public function getStats(): array {
$demoCount = $this->count('demos'); $demoCount = $this->count('demos');
$playerCount = $this->count('users'); $playerCount = $this->count('users');

View file

@ -13,25 +13,16 @@ use Demostf\API\Demo\HeaderParser;
use Demostf\API\Demo\Parser; use Demostf\API\Demo\Parser;
use Demostf\API\Error\InvalidKeyException; use Demostf\API\Error\InvalidKeyException;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use RandomLib\Generator;
class UploadProvider extends BaseProvider { class UploadProvider extends BaseProvider {
/** @var Generator */ private HeaderParser $headerParser;
private $generator; private Parser $parser;
/** @var HeaderParser */ private DemoStore $store;
private $headerParser; private UserProvider $userProvider;
/** @var Parser */ private DemoProvider $demoProvider;
private $parser; private DemoSaver $demoSaver;
/** @var DemoStore */ private string $baseUrl;
private $store; private string $uploadKey;
/** @var UserProvider */
private $userProvider;
/** @var DemoProvider */
private $demoProvider;
/** @var DemoSaver */
private $demoSaver;
private $baseUrl;
private $uploadKey;
public function __construct( public function __construct(
Connection $db, Connection $db,
@ -97,7 +88,7 @@ class UploadProvider extends BaseProvider {
return 'STV available at: ' . $this->baseUrl . '/' . $id; return 'STV available at: ' . $this->baseUrl . '/' . $id;
} }
public function validateHeader(int $size, Header $header) { public function validateHeader(int $size, Header $header): ?string {
if ($size < 1024) { if ($size < 1024) {
return 'Demos needs to be at least 1KB is size'; return 'Demos needs to be at least 1KB is size';
} }
@ -113,7 +104,7 @@ class UploadProvider extends BaseProvider {
return null; return null;
} }
public function validateParsed(Header $header, ParsedDemo $parsedDemo) { public function validateParsed(Header $header, ParsedDemo $parsedDemo): ?string {
$rounds = $parsedDemo->getRedScore() + $parsedDemo->getBlueScore(); $rounds = $parsedDemo->getRedScore() + $parsedDemo->getBlueScore();
if (0 === $rounds && $header->getDuration() < (15 * 60)) { if (0 === $rounds && $header->getDuration() < (15 * 60)) {
return 'Demos must be at least 5 minutes long'; return 'Demos must be at least 5 minutes long';

View file

@ -171,7 +171,7 @@ class UserProvider extends BaseProvider {
return $row ? User::fromRow($row) : null; return $row ? User::fromRow($row) : null;
} }
public function getUserId(string $steamId) { public function getUserId(string $steamId): int {
$existing = $this->get($steamId); $existing = $this->get($steamId);
if ($existing) { if ($existing) {
return $existing->getId(); return $existing->getId();

View file

@ -22,6 +22,7 @@ $connectionParams = [
if ('pgsql' === $connectionParams['driver']) { if ('pgsql' === $connectionParams['driver']) {
$connectionParams['driver'] = 'pdo_pgsql'; $connectionParams['driver'] = 'pdo_pgsql';
} }
/** @phpstan-ignore-next-line */
$db = DriverManager::getConnection($connectionParams); $db = DriverManager::getConnection($connectionParams);
$host = getenv('BASE_HOST') ?: ''; $host = getenv('BASE_HOST') ?: '';
$storeRoot = getenv('DEMO_ROOT') ?: ''; $storeRoot = getenv('DEMO_ROOT') ?: '';