diff --git a/Examples/View.php b/Examples/View.php index fb21e20..9ecaa47 100644 --- a/Examples/View.php +++ b/Examples/View.php @@ -39,7 +39,7 @@ $Timer = number_format(microtime(true) - $Timer, 4, '.', ''); ?> - +
"; print_r($InfoValue); echo ""; + } elseif ($InfoValue === true) { + echo 'true'; + } elseif ($InfoValue === false) { + echo 'false'; } else { - if ($InfoValue === true) { - echo 'true'; - } elseif ($InfoValue === false) { - echo 'false'; - } else { - echo htmlspecialchars($InfoValue); - } + echo htmlspecialchars($InfoValue); } ?> diff --git a/SourceQuery/BaseSocket.php b/SourceQuery/BaseSocket.php index 5909f48..f1a98dc 100644 --- a/SourceQuery/BaseSocket.php +++ b/SourceQuery/BaseSocket.php @@ -23,29 +23,82 @@ use xPaw\SourceQuery\Exception\SocketException; * * @package xPaw\SourceQuery * - * @uses xPaw\SourceQuery\Exception\InvalidPacketException - * @uses xPaw\SourceQuery\Exception\SocketException + * @uses InvalidPacketException + * @uses SocketException */ abstract class BaseSocket { - /** @var ?resource */ + /** + * @var resource + */ public $Socket; - public int $Engine; - - public string $Address; - public int $Port; - public int $Timeout; + /** + * @var int $Engine + */ + public int $Engine = SourceQuery::SOURCE; + + /** + * @var string $Address + */ + public string $Address = ''; + + /** + * @var int $Port + */ + public int $Port = 0; + + /** + * @var int $Timeout + */ + public int $Timeout = 0; + + /** + * Destructor + */ public function __destruct() { $this->Close(); } + /** + * Close + */ abstract public function Close(): void; + + /** + * @param string $Address + * @param int $Port + * @param int $Timeout + * @param int $Engine + */ abstract public function Open(string $Address, int $Port, int $Timeout, int $Engine): void; + + /** + * @param int $Header + * @param string $String + * + * @return bool + */ abstract public function Write(int $Header, string $String = ''): bool; + + /** + * @param int $Length + * + * @return Buffer + */ abstract public function Read(int $Length = 1400): Buffer; + /** + * @param Buffer $Buffer + * @param int $Length + * @param callable $SherlockFunction + * + * @return Buffer + * + * @throws InvalidPacketException + * @throws SocketException + */ protected function ReadInternal(Buffer $Buffer, int $Length, callable $SherlockFunction): Buffer { if ($Buffer->Remaining() === 0) { @@ -54,12 +107,14 @@ abstract class BaseSocket $Header = $Buffer->GetLong(); - if ($Header === -1) { // Single packet - // We don't have to do anything - } elseif ($Header === -2) { // Split packet + // Single packet, do nothing. + if ($Header === -1) { + return $Buffer; + } + + if ($Header === -2) { // Split packet $Packets = []; $IsCompressed = false; - $ReadMore = false; $PacketChecksum = null; do { @@ -98,18 +153,13 @@ abstract class BaseSocket $Packets[ $PacketNumber ] = $Buffer->Get(); - $ReadMore = $PacketCount > sizeof($Packets); + $ReadMore = $PacketCount > count($Packets); } while ($ReadMore && $SherlockFunction($Buffer, $Length)); $Data = implode($Packets); // TODO: Test this if ($IsCompressed) { - // Let's make sure this function exists, it's not included in PHP by default - if (!function_exists('bzdecompress')) { - throw new \RuntimeException('Received compressed packet, PHP doesn\'t have Bzip2 library installed, can\'t decompress.'); - } - $Data = bzdecompress($Data); if (!is_string($Data) || crc32($Data) !== $PacketChecksum) { diff --git a/SourceQuery/Buffer.php b/SourceQuery/Buffer.php index 1896261..7eb911e 100644 --- a/SourceQuery/Buffer.php +++ b/SourceQuery/Buffer.php @@ -22,7 +22,7 @@ use xPaw\SourceQuery\Exception\InvalidPacketException; * * @package xPaw\SourceQuery * - * @uses xPaw\SourceQuery\Exception\InvalidPacketException + * @uses InvalidPacketException */ final class Buffer { @@ -43,6 +43,8 @@ final class Buffer /** * Sets buffer + * + * @param string $Buffer */ public function Set(string $Buffer): void { @@ -61,10 +63,20 @@ final class Buffer return $this->Length - $this->Position; } + /** + * @return bool + */ + public function isEmpty(): bool + { + return $this->Remaining() <= 0; + } + /** * Gets data from buffer * * @param int $Length Bytes to read + * + * @return string */ public function Get(int $Length = -1): string { @@ -97,6 +109,8 @@ final class Buffer /** * Get short from buffer + * + * @throws InvalidPacketException */ public function GetShort(): int { @@ -111,6 +125,8 @@ final class Buffer /** * Get long from buffer + * + * @throws InvalidPacketException */ public function GetLong(): int { @@ -125,6 +141,8 @@ final class Buffer /** * Get float from buffer + * + * @throws InvalidPacketException */ public function GetFloat(): float { @@ -139,6 +157,8 @@ final class Buffer /** * Get unsigned long from buffer + * + * @throws InvalidPacketException */ public function GetUnsignedLong(): int { diff --git a/SourceQuery/Exception/SourceQueryException.php b/SourceQuery/Exception/SourceQueryException.php index 7c8bce0..965968a 100644 --- a/SourceQuery/Exception/SourceQueryException.php +++ b/SourceQuery/Exception/SourceQueryException.php @@ -15,7 +15,9 @@ declare(strict_types=1); namespace xPaw\SourceQuery\Exception; -abstract class SourceQueryException extends \Exception +use Exception; + +abstract class SourceQueryException extends Exception { // Base exception class } diff --git a/SourceQuery/GoldSourceRcon.php b/SourceQuery/GoldSourceRcon.php index f3cd2c2..dd43294 100644 --- a/SourceQuery/GoldSourceRcon.php +++ b/SourceQuery/GoldSourceRcon.php @@ -33,28 +33,48 @@ final class GoldSourceRcon * * @var BaseSocket */ - private $Socket; + private BaseSocket $Socket; + /** + * @var string $RconPassword + */ private string $RconPassword = ''; + + /** + * @var string $RconChallenge + */ private string $RconChallenge = ''; + /** + * @param BaseSocket $Socket + */ public function __construct(BaseSocket $Socket) { $this->Socket = $Socket; } + /** + * Close + */ public function Close(): void { $this->RconChallenge = ''; $this->RconPassword = ''; } + /** + * Open + */ public function Open(): void { - // } - public function Write(int $Header, string $String = ''): bool + /** + * @param string $String + * + * @return bool + */ + public function Write(string $String = ''): bool { $Command = pack('cccca*', 0xFF, 0xFF, 0xFF, 0xFF, $String); $Length = strlen($Command); @@ -63,17 +83,17 @@ final class GoldSourceRcon } /** - * @param int $Length * @throws AuthenticationException + * @throws InvalidPacketException + * * @return Buffer */ - public function Read(int $Length = 1400): Buffer + public function Read(): Buffer { // GoldSource RCON has same structure as Query $Buffer = $this->Socket->Read(); $StringBuffer = ''; - $ReadMore = false; // There is no indentifier of the end, so we just need to continue reading do { @@ -110,23 +130,36 @@ final class GoldSourceRcon return $Buffer; } + /** + * @param string $Command + * + * @return string + * + * @throws AuthenticationException + * @throws InvalidPacketException + */ public function Command(string $Command): string { if (!$this->RconChallenge) { throw new AuthenticationException('Tried to execute a RCON command before successful authorization.', AuthenticationException::BAD_PASSWORD); } - $this->Write(0, 'rcon ' . $this->RconChallenge . ' "' . $this->RconPassword . '" ' . $Command . "\0"); + $this->Write('rcon ' . $this->RconChallenge . ' "' . $this->RconPassword . '" ' . $Command . "\0"); $Buffer = $this->Read(); return $Buffer->Get(); } + /** + * @param string $Password + * + * @throws AuthenticationException + */ public function Authorize(string $Password): void { $this->RconPassword = $Password; - $this->Write(0, 'challenge rcon'); + $this->Write('challenge rcon'); $Buffer = $this->Socket->Read(); if ($Buffer->Get(14) !== 'challenge rcon') { diff --git a/SourceQuery/Socket.php b/SourceQuery/Socket.php index f5d8bb5..05489e3 100644 --- a/SourceQuery/Socket.php +++ b/SourceQuery/Socket.php @@ -28,15 +28,26 @@ use xPaw\SourceQuery\Exception\SocketException; */ final class Socket extends BaseSocket { + /** + * Close + */ public function Close(): void { if ($this->Socket !== null) { fclose($this->Socket); - $this->Socket = null; + $this->Socket = 0; } } + /** + * @param string $Address + * @param int $Port + * @param int $Timeout + * @param int $Engine + * + * @throws SocketException + */ public function Open(string $Address, int $Port, int $Timeout, int $Engine): void { $this->Timeout = $Timeout; @@ -55,6 +66,12 @@ final class Socket extends BaseSocket stream_set_blocking($this->Socket, true); } + /** + * @param int $Header + * @param string $String + * + * @return bool + */ public function Write(int $Header, string $String = ''): bool { $Command = pack('ccccca*', 0xFF, 0xFF, 0xFF, 0xFF, $Header, $String); @@ -66,9 +83,13 @@ final class Socket extends BaseSocket /** * Reads from socket and returns Buffer. * - * @throws InvalidPacketException + * @param int $Length * * @return Buffer Buffer + * + * @throws InvalidPacketException + * @throws SocketException + * */ public function Read(int $Length = 1400): Buffer { @@ -80,6 +101,14 @@ final class Socket extends BaseSocket return $Buffer; } + /** + * @param Buffer $Buffer + * @param int $Length + * + * @return bool + * + * @throws InvalidPacketException + */ public function Sherlock(Buffer $Buffer, int $Length): bool { $Data = fread($this->Socket, $Length); diff --git a/SourceQuery/SourceQuery.php b/SourceQuery/SourceQuery.php index f1a895b..4d0d037 100644 --- a/SourceQuery/SourceQuery.php +++ b/SourceQuery/SourceQuery.php @@ -15,6 +15,7 @@ declare(strict_types=1); namespace xPaw\SourceQuery; +use RuntimeException; use xPaw\SourceQuery\Exception\AuthenticationException; use xPaw\SourceQuery\Exception\InvalidArgumentException; use xPaw\SourceQuery\Exception\InvalidPacketException; @@ -98,11 +99,17 @@ final class SourceQuery */ private bool $UseOldGetChallengeMethod = false; + /** + * @param BaseSocket|null $Socket + */ public function __construct(BaseSocket $Socket = null) { $this->Socket = $Socket ?: new Socket(); } + /** + * Destructor + */ public function __destruct() { $this->Disconnect(); @@ -137,7 +144,7 @@ final class SourceQuery * * @param bool $Value Set to true to force old method * - * @returns bool Previous value + * @return bool Previous value */ public function SetUseOldGetChallengeMethod(bool $Value): bool { @@ -299,7 +306,6 @@ final class SourceQuery if ($Flags & 0x10) { $SteamIDLower = $Buffer->GetUnsignedLong(); $SteamIDInstance = $Buffer->GetUnsignedLong(); // This gets shifted by 32 bits, which should be steamid instance - $SteamID = 0; if (PHP_INT_SIZE === 4) { if (extension_loaded('gmp')) { @@ -307,7 +313,7 @@ final class SourceQuery $SteamIDInstance = gmp_abs($SteamIDInstance); $SteamID = gmp_strval(gmp_or($SteamIDLower, gmp_mul($SteamIDInstance, gmp_pow(2, 32)))); } else { - throw new \RuntimeException('Either 64-bit PHP installation or "gmp" module is required to correctly parse server\'s steamid.'); + throw new RuntimeException('Either 64-bit PHP installation or "gmp" module is required to correctly parse server\'s steamid.'); } } else { $SteamID = $SteamIDLower | ($SteamIDInstance << 32); @@ -334,7 +340,7 @@ final class SourceQuery $Server[ 'GameID' ] = $Buffer->GetUnsignedLong() | ($Buffer->GetUnsignedLong() << 32); } - if ($Buffer->Remaining() > 0) { + if (!$Buffer->isEmpty()) { throw new InvalidPacketException( 'GetInfo: unread data? ' . $Buffer->Remaining() . ' bytes remaining in the buffer. Please report it to the library developer.', InvalidPacketException::BUFFER_NOT_EMPTY @@ -431,7 +437,11 @@ final class SourceQuery /** * Get challenge (used for players/rules packets) * + * @param int $Header + * @param int $ExpectedResult + * * @throws InvalidPacketException + * @throws SocketException */ private function GetChallenge(int $Header, int $ExpectedResult): void { @@ -488,13 +498,13 @@ final class SourceQuery } switch ($this->Socket->Engine) { - case SourceQuery::GOLDSOURCE: + case self::GOLDSOURCE: { $this->Rcon = new GoldSourceRcon($this->Socket); break; } - case SourceQuery::SOURCE: + case self::SOURCE: { $this->Rcon = new SourceRcon($this->Socket); diff --git a/SourceQuery/SourceRcon.php b/SourceQuery/SourceRcon.php index 88625b9..d080db7 100644 --- a/SourceQuery/SourceRcon.php +++ b/SourceQuery/SourceRcon.php @@ -35,15 +35,27 @@ final class SourceRcon */ private BaseSocket $Socket; - /** @var ?resource */ + /** + * @var ?resource + */ private $RconSocket; + + /** + * @var int $RconRequestId + */ private int $RconRequestId = 0; + /** + * @param BaseSocket $Socket + */ public function __construct(BaseSocket $Socket) { $this->Socket = $Socket; } + /** + * Close + */ public function Close(): void { if ($this->RconSocket) { @@ -55,6 +67,9 @@ final class SourceRcon $this->RconRequestId = 0; } + /** + * @throws SocketException + */ public function Open(): void { if (!$this->RconSocket) { @@ -70,6 +85,12 @@ final class SourceRcon } } + /** + * @param int $Header + * @param string $String + * + * @return bool + */ public function Write(int $Header, string $String = ''): bool { // Pack the packet together @@ -82,6 +103,11 @@ final class SourceRcon return $Length === fwrite($this->RconSocket, $Command, $Length); } + /** + * @return Buffer + * + * @throws InvalidPacketException + */ public function Read(): Buffer { $Buffer = new Buffer(); @@ -117,6 +143,14 @@ final class SourceRcon return $Buffer; } + /** + * @param string $Command + * + * @return string + * + * @throws AuthenticationException + * @throws InvalidPacketException + */ public function Command(string $Command): string { $this->Write(SourceQuery::SERVERDATA_EXECCOMMAND, $Command); @@ -161,6 +195,12 @@ final class SourceRcon return rtrim($Data, "\0"); } + /** + * @param string $Password + * + * @throws AuthenticationException + * @throws InvalidPacketException + */ public function Authorize(string $Password): void { $this->Write(SourceQuery::SERVERDATA_AUTH, $Password); diff --git a/Tests/Tests.php b/Tests/Tests.php index 556bd47..f1c7d6e 100644 --- a/Tests/Tests.php +++ b/Tests/Tests.php @@ -6,28 +6,48 @@ use PHPUnit\Framework\TestCase; use xPaw\SourceQuery\BaseSocket; use xPaw\SourceQuery\SourceQuery; use xPaw\SourceQuery\Buffer; +use xPaw\SourceQuery\Exception\AuthenticationException; +use xPaw\SourceQuery\Exception\InvalidArgumentException; +use xPaw\SourceQuery\Exception\InvalidPacketException; +use xPaw\SourceQuery\Exception\SocketException; final class TestableSocket extends BaseSocket { - /** @var \SplQueue