Bring in stricter CS Fixer rules.

pull/150/head
Anthony Birkett 4 years ago
parent 48b89017c4
commit 0e4d7d4a6b

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'@PSR1' => true,
'@PSR12' => true,
'@PSR12:risky' => true,
'@PSR2' => true,
'@PhpCsFixer' => true,
'@PhpCsFixer:risky' => true,
'@Symfony' => true,
'@Symfony:risky' => true,
'concat_space' => false,
'native_constant_invocation' => false,
'native_function_invocation' => false,
'php_unit_fqcn_annotation' => false,
])
->setFinder(PhpCsFixer\Finder::create()
->exclude('vendor')
->in('Examples')
->in('SourceQuery')
->in('Tests')
)
;

@ -4,8 +4,8 @@ declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../vendor/autoload.php';
use xPaw\SourceQuery\SourceQuery;
use xPaw\SourceQuery\Socket\SourceSocket; use xPaw\SourceQuery\Socket\SourceSocket;
use xPaw\SourceQuery\SourceQuery;
// For the sake of this example // For the sake of this example
header('Content-Type: text/plain'); header('Content-Type: text/plain');

@ -4,8 +4,8 @@ declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../vendor/autoload.php';
use xPaw\SourceQuery\SourceQuery;
use xPaw\SourceQuery\Socket\SourceSocket; use xPaw\SourceQuery\Socket\SourceSocket;
use xPaw\SourceQuery\SourceQuery;
// For the sake of this example // For the sake of this example
header('Content-Type: text/plain'); header('Content-Type: text/plain');

@ -4,8 +4,8 @@ declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../vendor/autoload.php';
use xPaw\SourceQuery\SourceQuery;
use xPaw\SourceQuery\Socket\SourceSocket; use xPaw\SourceQuery\Socket\SourceSocket;
use xPaw\SourceQuery\SourceQuery;
$timer = microtime(true); $timer = microtime(true);
@ -37,28 +37,28 @@ $timer = number_format(microtime(true) - $timer, 4, '.', '');
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Source Query PHP Library</title> <title>Source Query PHP Library</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<style> <style>
.table { .table {
table-layout: fixed; table-layout: fixed;
border-top-color: #428BCA; border-top-color: #428BCA;
} }
.table td { .table td {
overflow-x: auto; overflow-x: auto;
} }
.table thead th { .table thead th {
background-color: #428BCA; background-color: #428BCA;
border-color: #428BCA !important; border-color: #428BCA !important;
color: #FFF; color: #FFF;
} }
.info-column { .info-column {
width: 120px; width: 120px;
} }
.frags-column { .frags-column {
width: 80px; width: 80px;
} }
@ -69,9 +69,9 @@ $timer = number_format(microtime(true) - $timer, 4, '.', '');
<div class="jumbotron"> <div class="jumbotron">
<div class="container"> <div class="container">
<h1>Source Query PHP Library</h1> <h1>Source Query PHP Library</h1>
<p class="lead">This library was created to query game server which use the Source (Steamworks) query protocol.</p> <p class="lead">This library was created to query game server which use the Source (Steamworks) query protocol.</p>
<p> <p>
<a class="btn btn-large btn-primary" href="https://xpaw.me">Made by xPaw</a> <a class="btn btn-large btn-primary" href="https://xpaw.me">Made by xPaw</a>
<a class="btn btn-large btn-primary" href="https://github.com/xPaw/PHP-Source-Query">View on GitHub</a> <a class="btn btn-large btn-primary" href="https://github.com/xPaw/PHP-Source-Query">View on GitHub</a>
@ -79,13 +79,13 @@ $timer = number_format(microtime(true) - $timer, 4, '.', '');
</p> </p>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
<?php if ($exception !== null): ?> <?php if (null !== $exception) { ?>
<div class="panel panel-error"> <div class="panel panel-error">
<pre class="panel-body"><?php echo htmlspecialchars($exception->__toString()); ?></pre> <pre class="panel-body"><?php echo htmlspecialchars($exception->__toString()); ?></pre>
</div> </div>
<?php endif; ?> <?php } ?>
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<table class="table table-bordered table-striped"> <table class="table table-bordered table-striped">
@ -96,18 +96,18 @@ $timer = number_format(microtime(true) - $timer, 4, '.', '');
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php if (!empty($info)): ?> <?php if (!empty($info)) { ?>
<?php foreach ($info as $infoKey => $infoValue): ?> <?php foreach ($info as $infoKey => $infoValue) { ?>
<tr> <tr>
<td><?php echo htmlspecialchars($infoKey); ?></td> <td><?php echo htmlspecialchars($infoKey); ?></td>
<td><?php <td><?php
if (is_array($infoValue)) { if (is_array($infoValue)) {
echo "<pre>"; echo '<pre>';
print_r($infoValue); print_r($infoValue);
echo "</pre>"; echo '</pre>';
} elseif ($infoValue === true) { } elseif (true === $infoValue) {
echo 'true'; echo 'true';
} elseif ($infoValue === false) { } elseif (false === $infoValue) {
echo 'false'; echo 'false';
} elseif (is_int($infoValue)) { } elseif (is_int($infoValue)) {
echo $infoValue; echo $infoValue;
@ -116,12 +116,12 @@ $timer = number_format(microtime(true) - $timer, 4, '.', '');
} }
?></td> ?></td>
</tr> </tr>
<?php endforeach; ?> <?php } ?>
<?php else: ?> <?php } else { ?>
<tr> <tr>
<td colspan="2">No information received</td> <td colspan="2">No information received</td>
</tr> </tr>
<?php endif; ?> <?php } ?>
</tbody> </tbody>
</table> </table>
</div> </div>
@ -135,19 +135,19 @@ $timer = number_format(microtime(true) - $timer, 4, '.', '');
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php if (!empty($players)): ?> <?php if (!empty($players)) { ?>
<?php foreach ($players as $player): ?> <?php foreach ($players as $player) { ?>
<tr> <tr>
<td><?php echo htmlspecialchars($player[ 'Name' ]); ?></td> <td><?php echo htmlspecialchars($player['Name']); ?></td>
<td><?php echo $player[ 'Frags' ]; ?></td> <td><?php echo $player['Frags']; ?></td>
<td><?php echo $player[ 'TimeF' ]; ?></td> <td><?php echo $player['TimeF']; ?></td>
</tr> </tr>
<?php endforeach; ?> <?php } ?>
<?php else: ?> <?php } else { ?>
<tr> <tr>
<td colspan="3">No players received</td> <td colspan="3">No players received</td>
</tr> </tr>
<?php endif; ?> <?php } ?>
</tbody> </tbody>
</table> </table>
</div> </div>
@ -161,18 +161,18 @@ $timer = number_format(microtime(true) - $timer, 4, '.', '');
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php if (!empty($rules)): ?> <?php if (!empty($rules)) { ?>
<?php foreach ($rules as $ruleKey => $ruleValue): ?> <?php foreach ($rules as $ruleKey => $ruleValue) { ?>
<tr> <tr>
<td><?php echo htmlspecialchars($ruleKey); ?></td> <td><?php echo htmlspecialchars($ruleKey); ?></td>
<td><?php echo htmlspecialchars($ruleValue); ?></td> <td><?php echo htmlspecialchars($ruleValue); ?></td>
</tr> </tr>
<?php endforeach; ?> <?php } ?>
<?php else: ?> <?php } else { ?>
<tr> <tr>
<td colspan="2">No rules received</td> <td colspan="2">No rules received</td>
</tr> </tr>
<?php endif; ?> <?php } ?>
</tbody> </tbody>
</table> </table>
</div> </div>

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *
@ -20,24 +20,22 @@ use xPaw\SourceQuery\Exception\InvalidPacketException;
final class Buffer final class Buffer
{ {
/** /**
* Buffer * Buffer.
*/ */
private string $buffer = ''; private string $buffer = '';
/** /**
* Buffer length * Buffer length.
*/ */
private int $length = 0; private int $length = 0;
/** /**
* Current position in buffer * Current position in buffer.
*/ */
private int $position = 0; private int $position = 0;
/** /**
* Sets buffer * Sets buffer.
*
* @param string $buffer
*/ */
public function set(string $buffer): void public function set(string $buffer): void
{ {
@ -47,7 +45,7 @@ final class Buffer
} }
/** /**
* Get remaining bytes * Get remaining bytes.
* *
* @return int Remaining bytes in buffer * @return int Remaining bytes in buffer
*/ */
@ -56,30 +54,25 @@ final class Buffer
return $this->length - $this->position; return $this->length - $this->position;
} }
/**
* @return bool
*/
public function isEmpty(): bool public function isEmpty(): bool
{ {
return $this->remaining() <= 0; return $this->remaining() <= 0;
} }
/** /**
* Gets data from buffer * Gets data from buffer.
* *
* @param int $length Bytes to read * @param int $length Bytes to read
*
* @return string
*/ */
public function get(int $length = -1): string public function get(int $length = -1): string
{ {
if ($length === 0) { if (0 === $length) {
return ''; return '';
} }
$remaining = $this->remaining(); $remaining = $this->remaining();
if ($length === -1) { if (-1 === $length) {
$length = $remaining; $length = $remaining;
} elseif ($length > $remaining) { } elseif ($length > $remaining) {
return ''; return '';
@ -93,7 +86,7 @@ final class Buffer
} }
/** /**
* Get byte from buffer * Get byte from buffer.
*/ */
public function getByte(): int public function getByte(): int
{ {
@ -101,7 +94,7 @@ final class Buffer
} }
/** /**
* Get short from buffer * Get short from buffer.
* *
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
@ -121,7 +114,7 @@ final class Buffer
} }
/** /**
* Get long from buffer * Get long from buffer.
* *
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
@ -141,7 +134,7 @@ final class Buffer
} }
/** /**
* Get float from buffer * Get float from buffer.
* *
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
@ -161,7 +154,7 @@ final class Buffer
} }
/** /**
* Get unsigned long from buffer * Get unsigned long from buffer.
* *
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
@ -181,19 +174,19 @@ final class Buffer
} }
/** /**
* Read one string from buffer ending with null byte * Read one string from buffer ending with null byte.
*/ */
public function getString(): string public function getString(): string
{ {
$zeroBytePosition = strpos($this->buffer, "\0", $this->position); $zeroBytePosition = strpos($this->buffer, "\0", $this->position);
if ($zeroBytePosition === false) { if (false === $zeroBytePosition) {
return ''; return '';
} }
$string = $this->get($zeroBytePosition - $this->position); $string = $this->get($zeroBytePosition - $this->position);
$this->position++; ++$this->position;
return $string; return $string;
} }

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *
@ -24,16 +24,8 @@ abstract class AbstractRcon implements RconInterface
/** /**
* @throws AuthenticationException * @throws AuthenticationException
* @throws InvalidPacketException * @throws InvalidPacketException
*
* @return Buffer
*/ */
abstract protected function read(): Buffer; abstract protected function read(): Buffer;
/**
* @param int|null $header
* @param string $string
*
* @return bool
*/
abstract protected function write(?int $header, string $string = ''): bool; abstract protected function write(?int $header, string $string = ''): bool;
} }

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *
@ -24,49 +24,36 @@ use xPaw\SourceQuery\SourceQuery;
final class GoldSourceRcon extends AbstractRcon final class GoldSourceRcon extends AbstractRcon
{ {
/** /**
* Points to socket class * Points to socket class.
*
* @var SocketInterface
*/ */
private SocketInterface $socket; private SocketInterface $socket;
/**
* @var string
*/
private string $rconPassword = ''; private string $rconPassword = '';
/**
* @var string
*/
private string $rconChallenge = ''; private string $rconChallenge = '';
/**
* @param SocketInterface $socket
*/
public function __construct(SocketInterface $socket) public function __construct(SocketInterface $socket)
{ {
$this->socket = $socket; $this->socket = $socket;
} }
/** /**
* Open * Open.
*/ */
public function open(): void public function open(): void
{ {
} }
/** /**
* Close * Close.
*/ */
public function close(): void public function close(): void
{ {
$this->rconChallenge = ''; $this->rconChallenge = '';
$this->rconPassword = ''; $this->rconPassword = '';
} }
/** /**
* @param string $password
*
* @throws AuthenticationException * @throws AuthenticationException
*/ */
public function authorize(string $password): void public function authorize(string $password): void
@ -76,7 +63,7 @@ final class GoldSourceRcon extends AbstractRcon
$this->write(null, 'challenge rcon'); $this->write(null, 'challenge rcon');
$buffer = $this->socket->read(); $buffer = $this->socket->read();
if ($buffer->get(14) !== 'challenge rcon') { if ('challenge rcon' !== $buffer->get(14)) {
throw new AuthenticationException('Failed to get RCON challenge.', AuthenticationException::BAD_PASSWORD); throw new AuthenticationException('Failed to get RCON challenge.', AuthenticationException::BAD_PASSWORD);
} }
@ -84,10 +71,6 @@ final class GoldSourceRcon extends AbstractRcon
} }
/** /**
* @param string $command
*
* @return string
*
* @throws AuthenticationException * @throws AuthenticationException
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
@ -106,8 +89,6 @@ final class GoldSourceRcon extends AbstractRcon
/** /**
* @throws AuthenticationException * @throws AuthenticationException
* @throws InvalidPacketException * @throws InvalidPacketException
*
* @return Buffer
*/ */
protected function read(): Buffer protected function read(): Buffer
{ {
@ -121,7 +102,7 @@ final class GoldSourceRcon extends AbstractRcon
$readMore = !$buffer->isEmpty(); $readMore = !$buffer->isEmpty();
if ($readMore) { if ($readMore) {
if ($buffer->getByte() !== SourceQuery::S2A_RCON) { if (SourceQuery::S2A_RCON !== $buffer->getByte()) {
throw new InvalidPacketException('Invalid rcon response.', InvalidPacketException::PACKET_HEADER_MISMATCH); throw new InvalidPacketException('Invalid rcon response.', InvalidPacketException::PACKET_HEADER_MISMATCH);
} }
@ -140,9 +121,10 @@ final class GoldSourceRcon extends AbstractRcon
$trimmed = trim($stringBuffer); $trimmed = trim($stringBuffer);
if ($trimmed === 'Bad rcon_password.') { if ('Bad rcon_password.' === $trimmed) {
throw new AuthenticationException($trimmed, AuthenticationException::BAD_PASSWORD); throw new AuthenticationException($trimmed, AuthenticationException::BAD_PASSWORD);
} elseif ($trimmed === 'You have been banned from this server.') { }
if ('You have been banned from this server.' === $trimmed) {
throw new AuthenticationException($trimmed, AuthenticationException::BANNED); throw new AuthenticationException($trimmed, AuthenticationException::BANNED);
} }
@ -151,12 +133,6 @@ final class GoldSourceRcon extends AbstractRcon
return $buffer; return $buffer;
} }
/**
* @param int|null $header
* @param string $string
*
* @return bool
*/
protected function write(?int $header, string $string = ''): bool protected function write(?int $header, string $string = ''): bool
{ {
$command = pack('cccca*', 0xFF, 0xFF, 0xFF, 0xFF, $string); $command = pack('cccca*', 0xFF, 0xFF, 0xFF, 0xFF, $string);

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *
@ -21,33 +21,24 @@ use xPaw\SourceQuery\Socket\SocketInterface;
interface RconInterface interface RconInterface
{ {
/**
* @param SocketInterface $socket
*/
public function __construct(SocketInterface $socket); public function __construct(SocketInterface $socket);
/** /**
* Open * Open.
*/ */
public function open(): void; public function open(): void;
/** /**
* Close * Close.
*/ */
public function close(): void; public function close(): void;
/** /**
* @param string $password
*
* @throws AuthenticationException * @throws AuthenticationException
*/ */
public function authorize(string $password): void; public function authorize(string $password): void;
/** /**
* @param string $command
*
* @return string
*
* @throws AuthenticationException * @throws AuthenticationException
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *
@ -25,7 +25,7 @@ use xPaw\SourceQuery\SourceQuery;
final class SourceRcon extends AbstractRcon final class SourceRcon extends AbstractRcon
{ {
/** /**
* Points to socket class * Points to socket class.
*/ */
private SocketInterface $socket; private SocketInterface $socket;
@ -36,14 +36,8 @@ final class SourceRcon extends AbstractRcon
*/ */
private $rconSocket; private $rconSocket;
/**
* @var int
*/
private int $rconRequestId = 0; private int $rconRequestId = 0;
/**
* @param SocketInterface $socket
*/
public function __construct(SocketInterface $socket) public function __construct(SocketInterface $socket)
{ {
$this->socket = $socket; $this->socket = $socket;
@ -74,7 +68,7 @@ final class SourceRcon extends AbstractRcon
} }
/** /**
* Close * Close.
*/ */
public function close(): void public function close(): void
{ {
@ -88,8 +82,6 @@ final class SourceRcon extends AbstractRcon
} }
/** /**
* @param string $password
*
* @throws AuthenticationException * @throws AuthenticationException
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
@ -104,23 +96,19 @@ final class SourceRcon extends AbstractRcon
// If we receive SERVERDATA_RESPONSE_VALUE, then we need to read again. // If we receive SERVERDATA_RESPONSE_VALUE, then we need to read again.
// More info: https://developer.valvesoftware.com/wiki/Source_RCON_Protocol#Additional_Comments // More info: https://developer.valvesoftware.com/wiki/Source_RCON_Protocol#Additional_Comments
if ($type === SourceQuery::SERVERDATA_RESPONSE_VALUE) { if (SourceQuery::SERVERDATA_RESPONSE_VALUE === $type) {
$buffer = $this->read(); $buffer = $this->read();
$requestId = $buffer->getLong(); $requestId = $buffer->getLong();
$type = $buffer->getLong(); $type = $buffer->getLong();
} }
if ($requestId === -1 || $type !== SourceQuery::SERVERDATA_AUTH_RESPONSE) { if (-1 === $requestId || SourceQuery::SERVERDATA_AUTH_RESPONSE !== $type) {
throw new AuthenticationException('RCON authorization failed.', AuthenticationException::BAD_PASSWORD); throw new AuthenticationException('RCON authorization failed.', AuthenticationException::BAD_PASSWORD);
} }
} }
/** /**
* @param string $command
*
* @return string
*
* @throws AuthenticationException * @throws AuthenticationException
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
@ -133,9 +121,10 @@ final class SourceRcon extends AbstractRcon
$type = $buffer->getLong(); $type = $buffer->getLong();
if ($type === SourceQuery::SERVERDATA_AUTH_RESPONSE) { if (SourceQuery::SERVERDATA_AUTH_RESPONSE === $type) {
throw new AuthenticationException('Bad rcon_password.', AuthenticationException::BAD_PASSWORD); throw new AuthenticationException('Bad rcon_password.', AuthenticationException::BAD_PASSWORD);
} elseif ($type !== SourceQuery::SERVERDATA_RESPONSE_VALUE) { }
if (SourceQuery::SERVERDATA_RESPONSE_VALUE !== $type) {
throw new InvalidPacketException('Invalid rcon response.', InvalidPacketException::PACKET_HEADER_MISMATCH); throw new InvalidPacketException('Invalid rcon response.', InvalidPacketException::PACKET_HEADER_MISMATCH);
} }
@ -151,13 +140,13 @@ final class SourceRcon extends AbstractRcon
$buffer->getLong(); // RequestID. $buffer->getLong(); // RequestID.
if ($buffer->getLong() !== SourceQuery::SERVERDATA_RESPONSE_VALUE) { if (SourceQuery::SERVERDATA_RESPONSE_VALUE !== $buffer->getLong()) {
break; break;
} }
$data2 = $buffer->get(); $data2 = $buffer->get();
if ($data2 === "\x00\x01\x00\x00\x00\x00") { if ("\x00\x01\x00\x00\x00\x00" === $data2) {
break; break;
} }
@ -169,8 +158,6 @@ final class SourceRcon extends AbstractRcon
} }
/** /**
* @return Buffer
*
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
protected function read(): Buffer protected function read(): Buffer
@ -229,11 +216,6 @@ final class SourceRcon extends AbstractRcon
} }
/** /**
* @param int|null $header
* @param string $string
*
* @return bool
*
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
protected function write(?int $header, string $string = ''): bool protected function write(?int $header, string $string = ''): bool
@ -247,7 +229,7 @@ final class SourceRcon extends AbstractRcon
// Prepend packet length. // Prepend packet length.
$command = pack('V', strlen($command)) . $command; $command = pack('V', strlen($command)) . $command;
$length = strlen($command); $length = strlen($command);
return $length === fwrite($this->rconSocket, $command, $length); return $length === fwrite($this->rconSocket, $command, $length);
} }

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *
@ -22,14 +22,8 @@ use xPaw\SourceQuery\Exception\SocketException;
abstract class AbstractSocket implements SocketInterface abstract class AbstractSocket implements SocketInterface
{ {
/**
* @var string $address
*/
public string $address = ''; public string $address = '';
/**
* @var int $port
*/
public int $port = 0; public int $port = 0;
/** /**
@ -39,39 +33,30 @@ abstract class AbstractSocket implements SocketInterface
*/ */
public $socket; public $socket;
/**
* @var int $timeout
*/
public int $timeout = 0; public int $timeout = 0;
/** /**
* Destructor * Destructor.
*/ */
public function __destruct() public function __destruct()
{ {
$this->close(); $this->close();
} }
/**
* @return string
*/
public function getAddress(): string public function getAddress(): string
{ {
return $this->address; return $this->address;
} }
/**
* @return int
*/
public function getPort(): int public function getPort(): int
{ {
return $this->port; return $this->port;
} }
/** /**
* @return resource
*
* @throws InvalidArgumentException * @throws InvalidArgumentException
*
* @return resource
*/ */
public function getSocket() public function getSocket()
{ {
@ -82,30 +67,23 @@ abstract class AbstractSocket implements SocketInterface
return $this->socket; return $this->socket;
} }
/**
* @return int
*/
public function getTimeout(): int public function getTimeout(): int
{ {
return $this->timeout; return $this->timeout;
} }
/** /**
* @param string $address
* @param int $port
* @param int $timeout
*
* @throws SocketException * @throws SocketException
*/ */
public function open(string $address, int $port, int $timeout): void public function open(string $address, int $port, int $timeout): void
{ {
$this->timeout = $timeout; $this->timeout = $timeout;
$this->port = $port; $this->port = $port;
$this->address = $address; $this->address = $address;
$socket = @fsockopen('udp://' . $address, $port, $errNo, $errStr, $timeout); $socket = @fsockopen('udp://' . $address, $port, $errNo, $errStr, $timeout);
if ($errNo || $socket === false) { if ($errNo || false === $socket) {
throw new SocketException('Could not create socket: ' . $errStr, SocketException::COULD_NOT_CREATE_SOCKET); throw new SocketException('Could not create socket: ' . $errStr, SocketException::COULD_NOT_CREATE_SOCKET);
} }
@ -115,7 +93,7 @@ abstract class AbstractSocket implements SocketInterface
} }
/** /**
* Close * Close.
*/ */
public function close(): void public function close(): void
{ {
@ -129,13 +107,10 @@ abstract class AbstractSocket implements SocketInterface
/** /**
* Reads from socket and returns Buffer. * Reads from socket and returns Buffer.
* *
* @param int $length
*
* @return Buffer Buffer
*
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
* *
* @return Buffer Buffer
*/ */
public function read(int $length = 1400): Buffer public function read(int $length = 1400): Buffer
{ {
@ -152,17 +127,12 @@ abstract class AbstractSocket implements SocketInterface
$buffer->set($data); $buffer->set($data);
$this->readInternal($buffer, $length, [ $this, 'sherlock' ]); $this->readInternal($buffer, $length, [$this, 'sherlock']);
return $buffer; return $buffer;
} }
/** /**
* @param int $header
* @param string $string
*
* @return bool
*
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
public function write(int $header, string $string = ''): bool public function write(int $header, string $string = ''): bool
@ -172,17 +142,12 @@ abstract class AbstractSocket implements SocketInterface
} }
$command = pack('ccccca*', 0xFF, 0xFF, 0xFF, 0xFF, $header, $string); $command = pack('ccccca*', 0xFF, 0xFF, 0xFF, 0xFF, $header, $string);
$length = strlen($command); $length = strlen($command);
return $length === fwrite($this->socket, $command, $length); return $length === fwrite($this->socket, $command, $length);
} }
/** /**
* @param Buffer $buffer
* @param int $length
*
* @return bool
*
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
public function sherlock(Buffer $buffer, int $length): bool public function sherlock(Buffer $buffer, int $length): bool
@ -203,18 +168,11 @@ abstract class AbstractSocket implements SocketInterface
$buffer->set($data); $buffer->set($data);
return $buffer->getLong() === -2; return -2 === $buffer->getLong();
} }
/** /**
*
* Get packet data (count, number, checksum) from the buffer. Different for goldsrc/src. * Get packet data (count, number, checksum) from the buffer. Different for goldsrc/src.
*
* @param Buffer $buffer
* @param int $count
* @param int $number
* @param bool $isCompressed
* @param int|null $checksum
*/ */
abstract protected function readInternalPacketData( abstract protected function readInternalPacketData(
Buffer $buffer, Buffer $buffer,
@ -225,12 +183,6 @@ abstract class AbstractSocket implements SocketInterface
): void; ): void;
/** /**
* @param Buffer $buffer
* @param int $length
* @param callable $sherlockFunction
*
* @return Buffer
*
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
protected function readInternal(Buffer $buffer, int $length, callable $sherlockFunction): Buffer protected function readInternal(Buffer $buffer, int $length, callable $sherlockFunction): Buffer
@ -242,11 +194,11 @@ abstract class AbstractSocket implements SocketInterface
$header = $buffer->getLong(); $header = $buffer->getLong();
// Single packet, do nothing. // Single packet, do nothing.
if ($header === -1) { if (-1 === $header) {
return $buffer; return $buffer;
} }
if ($header === -2) { // Split packet if (-2 === $header) { // Split packet
$packets = []; $packets = [];
$packetCount = 0; $packetCount = 0;
$packetNumber = 0; $packetNumber = 0;
@ -269,7 +221,7 @@ abstract class AbstractSocket implements SocketInterface
$readMore = $packetCount > count($packets); $readMore = $packetCount > count($packets);
} while ($readMore && $sherlockFunction($buffer, $length)); } while ($readMore && $sherlockFunction($buffer, $length));
$data = implode($packets); $data = implode('', $packets);
// TODO: Test this // TODO: Test this
if ($isCompressed) { if ($isCompressed) {

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *
@ -19,21 +19,11 @@ use xPaw\SourceQuery\Buffer;
final class GoldSourceSocket extends AbstractSocket final class GoldSourceSocket extends AbstractSocket
{ {
/**
* @return int
*/
public function getType(): int public function getType(): int
{ {
return SocketType::GOLDSOURCE; return SocketType::GOLDSOURCE;
} }
/**
* @param Buffer $buffer
* @param int $count
* @param int $number
* @param bool $isCompressed
* @param int|null $checksum
*/
protected function readInternalPacketData( protected function readInternalPacketData(
Buffer $buffer, Buffer $buffer,
int &$count, int &$count,

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *
@ -18,20 +18,12 @@ namespace xPaw\SourceQuery\Socket;
use xPaw\SourceQuery\Buffer; use xPaw\SourceQuery\Buffer;
/** /**
* Base socket interface * Base socket interface.
*
* @package xPaw\SourceQuery\Socket
*/ */
interface SocketInterface interface SocketInterface
{ {
/**
* @return string
*/
public function getAddress(): string; public function getAddress(): string;
/**
* @return int
*/
public function getPort(): int; public function getPort(): int;
/** /**
@ -39,9 +31,6 @@ interface SocketInterface
*/ */
public function getSocket(); public function getSocket();
/**
* @return int
*/
public function getTimeout(): int; public function getTimeout(): int;
/** /**
@ -49,30 +38,14 @@ interface SocketInterface
*/ */
public function getType(): int; public function getType(): int;
/**
* @param string $address
* @param int $port
* @param int $timeout
*/
public function open(string $address, int $port, int $timeout): void; public function open(string $address, int $port, int $timeout): void;
/** /**
* Close * Close.
*/ */
public function close(): void; public function close(): void;
/**
* @param int $length
*
* @return Buffer
*/
public function read(int $length = 1400): Buffer; public function read(int $length = 1400): Buffer;
/**
* @param int $header
* @param string $string
*
* @return bool
*/
public function write(int $header, string $string = ''): bool; public function write(int $header, string $string = ''): bool;
} }

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *
@ -20,21 +20,12 @@ use xPaw\SourceQuery\Exception\InvalidPacketException;
final class SourceSocket extends AbstractSocket final class SourceSocket extends AbstractSocket
{ {
/**
* @return int
*/
public function getType(): int public function getType(): int
{ {
return SocketType::SOURCE; return SocketType::SOURCE;
} }
/** /**
* @param Buffer $buffer
* @param int $count
* @param int $number
* @param bool $isCompressed
* @param int|null $checksum
*
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
protected function readInternalPacketData( protected function readInternalPacketData(

@ -5,8 +5,8 @@ declare(strict_types=1);
/** /**
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
* *
@ -25,15 +25,10 @@ final class TestableSocket extends AbstractSocket
*/ */
private array $packetQueue; private array $packetQueue;
/**
* @var int
*/
private int $type; private int $type;
/** /**
* TestableSocket constructor. * TestableSocket constructor.
*
* @param int $type
*/ */
public function __construct(int $type) public function __construct(int $type)
{ {
@ -41,31 +36,20 @@ final class TestableSocket extends AbstractSocket
$this->type = $type; $this->type = $type;
} }
/**
* @return int
*/
public function getType(): int public function getType(): int
{ {
return $this->type; return $this->type;
} }
/**
* @param string $data
*/
public function queue(string $data): void public function queue(string $data): void
{ {
$this->packetQueue[] = $data; $this->packetQueue[] = $data;
} }
/**
* @param string $address
* @param int $port
* @param int $timeout
*/
public function open(string $address, int $port, int $timeout): void public function open(string $address, int $port, int $timeout): void
{ {
$this->timeout = $timeout; $this->timeout = $timeout;
$this->port = $port; $this->port = $port;
$this->address = $address; $this->address = $address;
} }
@ -77,10 +61,6 @@ final class TestableSocket extends AbstractSocket
} }
/** /**
* @param int $length
*
* @return Buffer
*
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
public function read(int $length = 1400): Buffer public function read(int $length = 1400): Buffer
@ -95,48 +75,31 @@ final class TestableSocket extends AbstractSocket
$buffer->set($packet); $buffer->set($packet);
$this->readInternal($buffer, $length, [ $this, 'sherlock' ]); $this->readInternal($buffer, $length, [$this, 'sherlock']);
return $buffer; return $buffer;
} }
/**
* @param int $header
* @param string $string
*
* @return bool
*/
public function write(int $header, string $string = ''): bool public function write(int $header, string $string = ''): bool
{ {
return true; return true;
} }
/** /**
* @param Buffer $buffer
* @param int $length
*
* @return bool
*
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
public function sherlock(Buffer $buffer, int $length): bool public function sherlock(Buffer $buffer, int $length): bool
{ {
if (count($this->packetQueue) === 0) { if (0 === count($this->packetQueue)) {
return false; return false;
} }
$buffer->set(array_shift($this->packetQueue)); $buffer->set(array_shift($this->packetQueue));
return $buffer->getLong() === -2; return -2 === $buffer->getLong();
} }
/** /**
* @param Buffer $buffer
* @param int $count
* @param int $number
* @param bool $isCompressed
* @param int|null $checksum
*
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
protected function readInternalPacketData( protected function readInternalPacketData(
@ -172,12 +135,6 @@ final class TestableSocket extends AbstractSocket
/** /**
* Same as GoldSourceSocket::readInternalPacketData. * Same as GoldSourceSocket::readInternalPacketData.
*
* @param Buffer $buffer
* @param int $count
* @param int $number
* @param bool $isCompressed
* @param int|null $checksum
*/ */
private function readInternalPacketDataGoldSource( private function readInternalPacketDataGoldSource(
Buffer $buffer, Buffer $buffer,
@ -195,12 +152,6 @@ final class TestableSocket extends AbstractSocket
/** /**
* Same as SourceSocket::readInternalPacketData. * Same as SourceSocket::readInternalPacketData.
* *
* @param Buffer $buffer
* @param int $count
* @param int $number
* @param bool $isCompressed
* @param int|null $checksum
*
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
private function readInternalPacketDataSource( private function readInternalPacketDataSource(

@ -7,8 +7,8 @@ declare(strict_types=1);
* *
* @author Pavel Djundik * @author Pavel Djundik
* *
* @link https://xpaw.me * @see https://xpaw.me
* @link https://github.com/xPaw/PHP-Source-Query * @see https://github.com/xPaw/PHP-Source-Query
* *
* @license GNU Lesser General Public License, version 2.1 * @license GNU Lesser General Public License, version 2.1
*/ */
@ -29,68 +29,63 @@ use xPaw\SourceQuery\Socket\SocketType;
final class SourceQuery final class SourceQuery
{ {
/** /**
* Packets sent * Packets sent.
*/ */
private const A2A_PING = 0x69; private const A2A_PING = 0x69;
private const A2S_INFO = 0x54; private const A2S_INFO = 0x54;
private const A2S_PLAYER = 0x55; private const A2S_PLAYER = 0x55;
private const A2S_RULES = 0x56; private const A2S_RULES = 0x56;
private const A2S_SERVERQUERY_GETCHALLENGE = 0x57; private const A2S_SERVERQUERY_GETCHALLENGE = 0x57;
/** /**
* Packets received * Packets received.
*/ */
private const A2A_ACK = 0x6A; private const A2A_ACK = 0x6A;
private const S2C_CHALLENGE = 0x41; private const S2C_CHALLENGE = 0x41;
private const S2A_INFO_SRC = 0x49; private const S2A_INFO_SRC = 0x49;
private const S2A_INFO_OLD = 0x6D; // Old GoldSource, HLTV uses it (actually called S2A_INFO_DETAILED). private const S2A_INFO_OLD = 0x6D; // Old GoldSource, HLTV uses it (actually called S2A_INFO_DETAILED).
private const S2A_PLAYER = 0x44; private const S2A_PLAYER = 0x44;
private const S2A_RULES = 0x45; private const S2A_RULES = 0x45;
public const S2A_RCON = 0x6C; public const S2A_RCON = 0x6C;
/** /**
* Source rcon sent * Source rcon sent.
*/ */
public const SERVERDATA_REQUESTVALUE = 0; public const SERVERDATA_REQUESTVALUE = 0;
public const SERVERDATA_EXECCOMMAND = 2; public const SERVERDATA_EXECCOMMAND = 2;
public const SERVERDATA_AUTH = 3; public const SERVERDATA_AUTH = 3;
/** /**
* Source rcon received * Source rcon received.
*/ */
public const SERVERDATA_RESPONSE_VALUE = 0; public const SERVERDATA_RESPONSE_VALUE = 0;
public const SERVERDATA_AUTH_RESPONSE = 2; public const SERVERDATA_AUTH_RESPONSE = 2;
/** /**
* Points to rcon class * Points to rcon class.
*
* @var RconInterface|null
*/ */
private ?RconInterface $rcon; private ?RconInterface $rcon;
/** /**
* Points to socket class * Points to socket class.
*/ */
private SocketInterface $socket; private SocketInterface $socket;
/** /**
* True if connection is open, false if not * True if connection is open, false if not.
*/ */
private bool $connected = false; private bool $connected = false;
/** /**
* Contains challenge * Contains challenge.
*/ */
private string $challenge = ''; private string $challenge = '';
/** /**
* Use old method for getting challenge number * Use old method for getting challenge number.
*/ */
private bool $useOldGetChallengeMethod = false; private bool $useOldGetChallengeMethod = false;
/**
* @param SocketInterface $socket
*/
public function __construct(SocketInterface $socket) public function __construct(SocketInterface $socket)
{ {
$this->socket = $socket; $this->socket = $socket;
@ -98,7 +93,7 @@ final class SourceQuery
} }
/** /**
* Destructor * Destructor.
*/ */
public function __destruct() public function __destruct()
{ {
@ -106,11 +101,11 @@ final class SourceQuery
} }
/** /**
* Opens connection to server * Opens connection to server.
* *
* @param string $address Server ip * @param string $address Server ip
* @param int $port Server port * @param int $port Server port
* @param int $timeout Timeout period * @param int $timeout Timeout period
* *
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
@ -128,7 +123,7 @@ final class SourceQuery
} }
/** /**
* Closes all open connections * Closes all open connections.
*/ */
public function disconnect(): void public function disconnect(): void
{ {
@ -145,7 +140,7 @@ final class SourceQuery
} }
/** /**
* Forces GetChallenge to use old method for challenge retrieval because some games use outdated protocol (e.g Starbound) * Forces GetChallenge to use old method for challenge retrieval because some games use outdated protocol (e.g Starbound).
* *
* @param bool $value Set to true to force old method * @param bool $value Set to true to force old method
* *
@ -155,14 +150,14 @@ final class SourceQuery
{ {
$previous = $this->useOldGetChallengeMethod; $previous = $this->useOldGetChallengeMethod;
$this->useOldGetChallengeMethod = $value === true; $this->useOldGetChallengeMethod = true === $value;
return $previous; return $previous;
} }
/** /**
* Sends ping packet to the server * Sends ping packet to the server
* NOTE: This may not work on some games (TF2 for example) * NOTE: This may not work on some games (TF2 for example).
* *
* @throws SocketException * @throws SocketException
* *
@ -177,11 +172,11 @@ final class SourceQuery
$this->socket->write(self::A2A_PING); $this->socket->write(self::A2A_PING);
$buffer = $this->socket->read(); $buffer = $this->socket->read();
return $buffer->getByte() === self::A2A_ACK; return self::A2A_ACK === $buffer->getByte();
} }
/** /**
* Get server information * Get server information.
* *
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
@ -204,7 +199,7 @@ final class SourceQuery
$type = $buffer->getByte(); $type = $buffer->getByte();
$server = []; $server = [];
if ($type === self::S2C_CHALLENGE) { if (self::S2C_CHALLENGE === $type) {
$this->challenge = $buffer->get(4); $this->challenge = $buffer->get(4);
$this->socket->write(self::A2S_INFO, "Source Engine Query\0" . $this->challenge); $this->socket->write(self::A2S_INFO, "Source Engine Query\0" . $this->challenge);
@ -213,78 +208,78 @@ final class SourceQuery
} }
// Old GoldSource protocol, HLTV still uses it. // Old GoldSource protocol, HLTV still uses it.
if ($type === self::S2A_INFO_OLD && $this->socket->getType() === SocketType::GOLDSOURCE) { if (self::S2A_INFO_OLD === $type && SocketType::GOLDSOURCE === $this->socket->getType()) {
/** /*
* If we try to read data again, and we get the result with type S2A_INFO (0x49) * If we try to read data again, and we get the result with type S2A_INFO (0x49)
* That means this server is running dproto, * That means this server is running dproto,
* Because it sends answer for both protocols * Because it sends answer for both protocols
*/ */
$server[ 'Address' ] = $buffer->getString(); $server['Address'] = $buffer->getString();
$server[ 'HostName' ] = $buffer->getString(); $server['HostName'] = $buffer->getString();
$server[ 'Map' ] = $buffer->getString(); $server['Map'] = $buffer->getString();
$server[ 'ModDir' ] = $buffer->getString(); $server['ModDir'] = $buffer->getString();
$server[ 'ModDesc' ] = $buffer->getString(); $server['ModDesc'] = $buffer->getString();
$server[ 'Players' ] = $buffer->getByte(); $server['Players'] = $buffer->getByte();
$server[ 'MaxPlayers' ] = $buffer->getByte(); $server['MaxPlayers'] = $buffer->getByte();
$server[ 'Protocol' ] = $buffer->getByte(); $server['Protocol'] = $buffer->getByte();
$server[ 'Dedicated' ] = chr($buffer->getByte()); $server['Dedicated'] = chr($buffer->getByte());
$server[ 'Os' ] = chr($buffer->getByte()); $server['Os'] = chr($buffer->getByte());
$server[ 'Password' ] = $buffer->getByte() === 1; $server['Password'] = 1 === $buffer->getByte();
$server[ 'IsMod' ] = $buffer->getByte() === 1; $server['IsMod'] = 1 === $buffer->getByte();
if ($server[ 'IsMod' ]) { if ($server['IsMod']) {
$Mod = []; $Mod = [];
$Mod[ 'Url' ] = $buffer->getString(); $Mod['Url'] = $buffer->getString();
$Mod[ 'Download' ] = $buffer->getString(); $Mod['Download'] = $buffer->getString();
$buffer->get(1); // NULL byte $buffer->get(1); // NULL byte
$Mod[ 'Version' ] = $buffer->getLong(); $Mod['Version'] = $buffer->getLong();
$Mod[ 'Size' ] = $buffer->getLong(); $Mod['Size'] = $buffer->getLong();
$Mod[ 'ServerSide' ] = $buffer->getByte() === 1; $Mod['ServerSide'] = 1 === $buffer->getByte();
$Mod[ 'CustomDLL' ] = $buffer->getByte() === 1; $Mod['CustomDLL'] = 1 === $buffer->getByte();
$server[ 'Mod' ] = $Mod; $server['Mod'] = $Mod;
} }
$server[ 'Secure' ] = $buffer->getByte() === 1; $server['Secure'] = 1 === $buffer->getByte();
$server[ 'Bots' ] = $buffer->getByte(); $server['Bots'] = $buffer->getByte();
return $server; return $server;
} }
if ($type !== self::S2A_INFO_SRC) { if (self::S2A_INFO_SRC !== $type) {
throw new InvalidPacketException('GetInfo: Packet header mismatch. (0x' . dechex($type) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH); throw new InvalidPacketException('GetInfo: Packet header mismatch. (0x' . dechex($type) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH);
} }
$server[ 'Protocol' ] = $buffer->getByte(); $server['Protocol'] = $buffer->getByte();
$server[ 'HostName' ] = $buffer->getString(); $server['HostName'] = $buffer->getString();
$server[ 'Map' ] = $buffer->getString(); $server['Map'] = $buffer->getString();
$server[ 'ModDir' ] = $buffer->getString(); $server['ModDir'] = $buffer->getString();
$server[ 'ModDesc' ] = $buffer->getString(); $server['ModDesc'] = $buffer->getString();
$server[ 'AppID' ] = $buffer->getShort(); $server['AppID'] = $buffer->getShort();
$server[ 'Players' ] = $buffer->getByte(); $server['Players'] = $buffer->getByte();
$server[ 'MaxPlayers' ] = $buffer->getByte(); $server['MaxPlayers'] = $buffer->getByte();
$server[ 'Bots' ] = $buffer->getByte(); $server['Bots'] = $buffer->getByte();
$server[ 'Dedicated' ] = chr($buffer->getByte()); $server['Dedicated'] = chr($buffer->getByte());
$server[ 'Os' ] = chr($buffer->getByte()); $server['Os'] = chr($buffer->getByte());
$server[ 'Password' ] = $buffer->getByte() === 1; $server['Password'] = 1 === $buffer->getByte();
$server[ 'Secure' ] = $buffer->getByte() === 1; $server['Secure'] = 1 === $buffer->getByte();
// The Ship (they violate query protocol spec by modifying the response) // The Ship (they violate query protocol spec by modifying the response)
if ($server[ 'AppID' ] === 2400) { if (2400 === $server['AppID']) {
$server[ 'GameMode' ] = $buffer->getByte(); $server['GameMode'] = $buffer->getByte();
$server[ 'WitnessCount' ] = $buffer->getByte(); $server['WitnessCount'] = $buffer->getByte();
$server[ 'WitnessTime' ] = $buffer->getByte(); $server['WitnessTime'] = $buffer->getByte();
} }
$server[ 'Version' ] = $buffer->getString(); $server['Version'] = $buffer->getString();
// Extra Data Flags. // Extra Data Flags.
if ($buffer->remaining() > 0) { if ($buffer->remaining() > 0) {
$server[ 'ExtraDataFlags' ] = $Flags = $buffer->getByte(); $server['ExtraDataFlags'] = $Flags = $buffer->getByte();
// S2A_EXTRA_DATA_HAS_GAME_PORT - Next 2 bytes include the game port. // S2A_EXTRA_DATA_HAS_GAME_PORT - Next 2 bytes include the game port.
if ($Flags & 0x80) { if ($Flags & 0x80) {
$server[ 'GamePort' ] = $buffer->getShort(); $server['GamePort'] = $buffer->getShort();
} }
// S2A_EXTRA_DATA_HAS_STEAMID - Next 8 bytes are the steamID. // S2A_EXTRA_DATA_HAS_STEAMID - Next 8 bytes are the steamID.
@ -306,32 +301,29 @@ final class SourceQuery
$steamId = $steamIdLower | ($steamIdInstance << 32); $steamId = $steamIdLower | ($steamIdInstance << 32);
} }
$server[ 'SteamID' ] = $steamId; $server['SteamID'] = $steamId;
unset($steamIdLower, $steamIdInstance, $steamId); unset($steamIdLower, $steamIdInstance, $steamId);
} }
// S2A_EXTRA_DATA_HAS_SPECTATOR_DATA - Next 2 bytes include the spectator port, then the spectator server name. // S2A_EXTRA_DATA_HAS_SPECTATOR_DATA - Next 2 bytes include the spectator port, then the spectator server name.
if ($Flags & 0x40) { if ($Flags & 0x40) {
$server[ 'SpecPort' ] = $buffer->getShort(); $server['SpecPort'] = $buffer->getShort();
$server[ 'SpecName' ] = $buffer->getString(); $server['SpecName'] = $buffer->getString();
} }
// S2A_EXTRA_DATA_HAS_GAMETAG_DATA - Next bytes are the game tag string. // S2A_EXTRA_DATA_HAS_GAMETAG_DATA - Next bytes are the game tag string.
if ($Flags & 0x20) { if ($Flags & 0x20) {
$server[ 'GameTags' ] = $buffer->getString(); $server['GameTags'] = $buffer->getString();
} }
// S2A_EXTRA_DATA_GAMEID - Next 8 bytes are the gameID of the server. // S2A_EXTRA_DATA_GAMEID - Next 8 bytes are the gameID of the server.
if ($Flags & 0x01) { if ($Flags & 0x01) {
$server[ 'GameID' ] = $buffer->getUnsignedLong() | ($buffer->getUnsignedLong() << 32); $server['GameID'] = $buffer->getUnsignedLong() | ($buffer->getUnsignedLong() << 32);
} }
if (!$buffer->isEmpty()) { if (!$buffer->isEmpty()) {
throw new InvalidPacketException( throw new InvalidPacketException('GetInfo: unread data? ' . $buffer->remaining() . ' bytes remaining in the buffer. Please report it to the library developer.', InvalidPacketException::BUFFER_NOT_EMPTY);
'GetInfo: unread data? ' . $buffer->remaining() . ' bytes remaining in the buffer. Please report it to the library developer.',
InvalidPacketException::BUFFER_NOT_EMPTY
);
} }
} }
@ -339,7 +331,7 @@ final class SourceQuery
} }
/** /**
* Get players on the server * Get players on the server.
* *
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
@ -360,7 +352,7 @@ final class SourceQuery
$type = $buffer->getByte(); $type = $buffer->getByte();
if ($type !== self::S2A_PLAYER) { if (self::S2A_PLAYER !== $type) {
throw new InvalidPacketException('GetPlayers: Packet header mismatch. (0x' . dechex($type) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH); throw new InvalidPacketException('GetPlayers: Packet header mismatch. (0x' . dechex($type) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH);
} }
@ -369,11 +361,11 @@ final class SourceQuery
while ($count-- > 0 && !$buffer->isEmpty()) { while ($count-- > 0 && !$buffer->isEmpty()) {
$player = []; $player = [];
$player[ 'Id' ] = $buffer->getByte(); // PlayerID, is it just always 0? $player['Id'] = $buffer->getByte(); // PlayerID, is it just always 0?
$player[ 'Name' ] = $buffer->getString(); $player['Name'] = $buffer->getString();
$player[ 'Frags' ] = $buffer->getLong(); $player['Frags'] = $buffer->getLong();
$player[ 'Time' ] = (int)$buffer->getFloat(); $player['Time'] = (int) $buffer->getFloat();
$player[ 'TimeF' ] = gmdate(($player[ 'Time' ] > 3600 ? 'H:i:s' : 'i:s'), $player[ 'Time' ]); $player['TimeF'] = gmdate(($player['Time'] > 3600 ? 'H:i:s' : 'i:s'), $player['Time']);
$players[] = $player; $players[] = $player;
} }
@ -382,7 +374,7 @@ final class SourceQuery
} }
/** /**
* Get rules (cvars) from the server * Get rules (cvars) from the server.
* *
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
@ -402,7 +394,7 @@ final class SourceQuery
$type = $buffer->getByte(); $type = $buffer->getByte();
if ($type !== self::S2A_RULES) { if (self::S2A_RULES !== $type) {
throw new InvalidPacketException('GetRules: Packet header mismatch. (0x' . dechex($type) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH); throw new InvalidPacketException('GetRules: Packet header mismatch. (0x' . dechex($type) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH);
} }
@ -422,7 +414,7 @@ final class SourceQuery
} }
/** /**
* Sets rcon password, for future use in Rcon() * Sets rcon password, for future use in Rcon().
* *
* @param string $password Rcon Password * @param string $password Rcon Password
* *
@ -438,21 +430,17 @@ final class SourceQuery
switch ($this->socket->getType()) { switch ($this->socket->getType()) {
case SocketType::GOLDSOURCE: case SocketType::GOLDSOURCE:
{
$this->rcon = new GoldSourceRcon($this->socket); $this->rcon = new GoldSourceRcon($this->socket);
break; break;
}
case SocketType::SOURCE: case SocketType::SOURCE:
{
$this->rcon = new SourceRcon($this->socket); $this->rcon = new SourceRcon($this->socket);
break; break;
}
default: default:
{
throw new SocketException('Unknown engine.', SocketException::INVALID_ENGINE); throw new SocketException('Unknown engine.', SocketException::INVALID_ENGINE);
}
} }
$this->rcon->open(); $this->rcon->open();
@ -464,11 +452,11 @@ final class SourceQuery
* *
* @param string $command Command to execute * @param string $command Command to execute
* *
* @return string Answer from server in string
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
*
* @throws AuthenticationException * @throws AuthenticationException
*
* @return string Answer from server in string
*/ */
public function rcon(string $command): string public function rcon(string $command): string
{ {
@ -476,7 +464,7 @@ final class SourceQuery
throw new SocketException('Not connected.', SocketException::NOT_CONNECTED); throw new SocketException('Not connected.', SocketException::NOT_CONNECTED);
} }
if ($this->rcon === null) { if (null === $this->rcon) {
throw new SocketException('You must set a RCON password before trying to execute a RCON command.', SocketException::NOT_CONNECTED); throw new SocketException('You must set a RCON password before trying to execute a RCON command.', SocketException::NOT_CONNECTED);
} }
@ -484,10 +472,7 @@ final class SourceQuery
} }
/** /**
* Get challenge (used for players/rules packets) * Get challenge (used for players/rules packets).
*
* @param int $header
* @param int $expectedResult
* *
* @throws InvalidPacketException * @throws InvalidPacketException
*/ */
@ -508,24 +493,18 @@ final class SourceQuery
switch ($type) { switch ($type) {
case self::S2C_CHALLENGE: case self::S2C_CHALLENGE:
{
$this->challenge = $buffer->get(4); $this->challenge = $buffer->get(4);
return; return;
}
case $expectedResult: case $expectedResult:
{
// Goldsource (HLTV). // Goldsource (HLTV).
return; return;
}
case 0: case 0:
{
throw new InvalidPacketException('GetChallenge: Failed to get challenge.'); throw new InvalidPacketException('GetChallenge: Failed to get challenge.');
}
default: default:
{
throw new InvalidPacketException('GetChallenge: Packet header mismatch. (0x' . dechex($type) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH); throw new InvalidPacketException('GetChallenge: Packet header mismatch. (0x' . dechex($type) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH);
}
} }
} }
} }

@ -3,30 +3,28 @@
declare(strict_types=1); declare(strict_types=1);
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use xPaw\SourceQuery\SourceQuery;
use xPaw\SourceQuery\Exception\AuthenticationException; use xPaw\SourceQuery\Exception\AuthenticationException;
use xPaw\SourceQuery\Exception\InvalidArgumentException; use xPaw\SourceQuery\Exception\InvalidArgumentException;
use xPaw\SourceQuery\Exception\InvalidPacketException; use xPaw\SourceQuery\Exception\InvalidPacketException;
use xPaw\SourceQuery\Exception\SocketException; use xPaw\SourceQuery\Exception\SocketException;
use xPaw\SourceQuery\Socket\SocketType; use xPaw\SourceQuery\Socket\SocketType;
use xPaw\SourceQuery\Socket\TestableSocket; use xPaw\SourceQuery\Socket\TestableSocket;
use xPaw\SourceQuery\SourceQuery;
/**
* @internal
* @coversNothing
*/
final class Tests extends TestCase final class Tests extends TestCase
{ {
/**
* @var TestableSocket
*/
private TestableSocket $socket; private TestableSocket $socket;
/**
* @var SourceQuery
*/
private SourceQuery $sourceQuery; private SourceQuery $sourceQuery;
/** /**
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function setUp(): void protected function setUp(): void
{ {
$this->socket = new TestableSocket(SocketType::SOURCE); $this->socket = new TestableSocket(SocketType::SOURCE);
$this->sourceQuery = new SourceQuery($this->socket); $this->sourceQuery = new SourceQuery($this->socket);
@ -34,13 +32,14 @@ final class Tests extends TestCase
} }
/** /**
* tearDown * tearDown.
*/ */
public function tearDown(): void protected function tearDown(): void
{ {
$this->sourceQuery->disconnect(); $this->sourceQuery->disconnect();
unset($this->socket, $this->sourceQuery); $this->socket = null;
$this->sourceQuery = null;
} }
/** /**
@ -132,9 +131,6 @@ final class Tests extends TestCase
} }
/** /**
* @param string $rawInput
* @param array $expectedOutput
*
* @throws InvalidArgumentException * @throws InvalidArgumentException
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
@ -143,7 +139,7 @@ final class Tests extends TestCase
*/ */
public function testGetInfo(string $rawInput, array $expectedOutput): void public function testGetInfo(string $rawInput, array $expectedOutput): void
{ {
if (isset($expectedOutput[ 'IsMod' ])) { if (isset($expectedOutput['IsMod'])) {
$this->socket = new TestableSocket(SocketType::GOLDSOURCE); $this->socket = new TestableSocket(SocketType::GOLDSOURCE);
$this->sourceQuery = new SourceQuery($this->socket); $this->sourceQuery = new SourceQuery($this->socket);
$this->sourceQuery->connect('', 2); $this->sourceQuery->connect('', 2);
@ -153,12 +149,10 @@ final class Tests extends TestCase
$realOutput = $this->sourceQuery->getInfo(); $realOutput = $this->sourceQuery->getInfo();
self::assertEquals($expectedOutput, $realOutput); static::assertSame($expectedOutput, $realOutput);
} }
/** /**
* @return array
*
* @throws JsonException * @throws JsonException
*/ */
public function infoProvider(): array public function infoProvider(): array
@ -167,8 +161,6 @@ final class Tests extends TestCase
} }
/** /**
* @param string $data
*
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
* *
@ -183,8 +175,6 @@ final class Tests extends TestCase
} }
/** /**
* @param string $data
*
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
* *
@ -199,8 +189,6 @@ final class Tests extends TestCase
} }
/** /**
* @param string $data
*
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
* *
@ -216,8 +204,6 @@ final class Tests extends TestCase
} }
/** /**
* @param string $data
*
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
* *
@ -239,13 +225,13 @@ final class Tests extends TestCase
{ {
return return
[ [
[ '' ], [''],
[ "\xff\xff\xff\xff" ], // No type. ["\xff\xff\xff\xff"], // No type.
[ "\xff\xff\xff\xff\x49" ], // Correct type, but no data after. ["\xff\xff\xff\xff\x49"], // Correct type, but no data after.
[ "\xff\xff\xff\xff\x6D" ], // Old info packet, but tests are done for source. ["\xff\xff\xff\xff\x6D"], // Old info packet, but tests are done for source.
[ "\xff\xff\xff\xff\x11" ], // Wrong type. ["\xff\xff\xff\xff\x11"], // Wrong type.
[ "\x11\x11\x11\x11" ], // Wrong header. ["\x11\x11\x11\x11"], // Wrong header.
[ "\xff" ], // Should be 4 bytes, but it's 1. ["\xff"], // Should be 4 bytes, but it's 1.
]; ];
} }
@ -257,15 +243,14 @@ final class Tests extends TestCase
{ {
$this->socket->queue("\xFF\xFF\xFF\xFF\x41\x11\x11\x11\x11"); $this->socket->queue("\xFF\xFF\xFF\xFF\x41\x11\x11\x11\x11");
$this->socket->queue("\xFF\xFF\xFF\xFF\x45\x01\x00ayy\x00lmao\x00"); $this->socket->queue("\xFF\xFF\xFF\xFF\x45\x01\x00ayy\x00lmao\x00");
self::assertEquals([ 'ayy' => 'lmao' ], $this->sourceQuery->getRules()); static::assertSame(['ayy' => 'lmao'], $this->sourceQuery->getRules());
$this->socket->queue("\xFF\xFF\xFF\xFF\x45\x01\x00wow\x00much\x00"); $this->socket->queue("\xFF\xFF\xFF\xFF\x45\x01\x00wow\x00much\x00");
self::assertEquals([ 'wow' => 'much' ], $this->sourceQuery->getRules()); static::assertSame(['wow' => 'much'], $this->sourceQuery->getRules());
} }
/** /**
* @param string[] $rawInput * @param string[] $rawInput
* @param array $expectedOutput
* *
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
@ -294,12 +279,10 @@ final class Tests extends TestCase
$realOutput = $this->sourceQuery->getRules(); $realOutput = $this->sourceQuery->getRules();
self::assertEquals($expectedOutput, $realOutput); static::assertSame($expectedOutput, $realOutput);
} }
/** /**
* @return array
*
* @throws JsonException * @throws JsonException
*/ */
public function rulesProvider(): array public function rulesProvider(): array
@ -309,7 +292,6 @@ final class Tests extends TestCase
/** /**
* @param string[] $rawInput * @param string[] $rawInput
* @param array $expectedOutput
* *
* @throws InvalidPacketException * @throws InvalidPacketException
* @throws SocketException * @throws SocketException
@ -338,12 +320,10 @@ final class Tests extends TestCase
$realOutput = $this->sourceQuery->getPlayers(); $realOutput = $this->sourceQuery->getPlayers();
self::assertEquals($expectedOutput, $realOutput); static::assertSame($expectedOutput, $realOutput);
} }
/** /**
* @return array
*
* @throws JsonException * @throws JsonException
*/ */
public function playersProvider(): array public function playersProvider(): array
@ -357,18 +337,13 @@ final class Tests extends TestCase
public function testPing(): void public function testPing(): void
{ {
$this->socket->queue("\xFF\xFF\xFF\xFF\x6A\x00"); $this->socket->queue("\xFF\xFF\xFF\xFF\x6A\x00");
self::assertTrue($this->sourceQuery->ping()); static::assertTrue($this->sourceQuery->ping());
$this->socket->queue("\xFF\xFF\xFF\xFF\xEE"); $this->socket->queue("\xFF\xFF\xFF\xFF\xEE");
self::assertFalse($this->sourceQuery->ping()); static::assertFalse($this->sourceQuery->ping());
} }
/** /**
* @param string $path
* @param bool $hexToBin
*
* @return array
*
* @throws JsonException * @throws JsonException
*/ */
private function getData(string $path, bool $hexToBin = false): array private function getData(string $path, bool $hexToBin = false): array
@ -410,7 +385,7 @@ final class Tests extends TestCase
true, true,
512, 512,
JSON_THROW_ON_ERROR JSON_THROW_ON_ERROR
) ),
]; ];
} }

@ -36,5 +36,11 @@
{ {
"xPaw\\SourceQuery\\": "SourceQuery/" "xPaw\\SourceQuery\\": "SourceQuery/"
} }
},
"scripts": {
"psalm": "psalm",
"phpstan": "phpstan",
"phpunit": "phpunit Tests/Tests.php",
"php-cs-fixer": "php-cs-fixer fix --config=.php-cs-fixer.php"
} }
} }

Loading…
Cancel
Save