Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/coverage/coverage-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions src/Adapter/MockServerQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PlanetTeamSpeak\TeamSpeak3Framework\Exception\TransportException;
use PlanetTeamSpeak\TeamSpeak3Framework\Helper\Profiler;
use PlanetTeamSpeak\TeamSpeak3Framework\Helper\Signal;
use PlanetTeamSpeak\TeamSpeak3Framework\Helper\StringHelper;
use PlanetTeamSpeak\TeamSpeak3Framework\TeamSpeak3;
use PlanetTeamSpeak\TeamSpeak3Framework\Transport\MockTCP;

Expand All @@ -27,11 +28,77 @@ protected function syn(): void
Profiler::init(spl_object_hash($this));

$rdy = $this->getTransport()->readLine();
$rdy = StringHelper::factory($rdy ?? '');

if (! $rdy->startsWith(TeamSpeak3::TS3_PROTO_IDENT) && ! $rdy->startsWith(TeamSpeak3::TEA_PROTO_IDENT)) {
throw new AdapterException('invalid reply from the server ('.$rdy.')');
}

Signal::getInstance()->emit('serverqueryConnected', $this);
}

/**
* Return the underlying MockTCP transport.
*/
public function getMockTransport(): MockTCP
{
/** @var MockTCP $transport */
$transport = $this->getTransport();

return $transport;
}

/**
* Register a custom response on the mock transport.
*/
public function registerResponse(string $cmd, string $reply): self
{
$this->getMockTransport()->registerResponse($cmd, $reply);

return $this;
}

/**
* Queue a response on the mock transport.
*/
public function queueResponse(string $reply): self
{
$this->getMockTransport()->queueResponse($reply);

return $this;
}

/**
* Set a custom dynamic command handler.
*/
public function setCommandHandler(callable $handler): self
{
$this->getMockTransport()->setCommandHandler($handler);

return $this;
}

/**
* Get sent commands.
*/
public function getSentCommands(): array
{
return $this->getMockTransport()->getSentCommands();
}

/**
* Get the last sent command.
*/
public function getLastCommand(): ?string
{
return $this->getMockTransport()->getLastCommand();
}

/**
* Check if a command was sent.
*/
public function hasSentCommand(string $command): bool
{
return $this->getMockTransport()->hasSentCommand($command);
}
}
36 changes: 34 additions & 2 deletions src/Helper/Char.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,23 +162,55 @@ public function toAscii(): int
*/
public function toUnicode(): int
{
$length = strlen($this->char);
$h = ord($this->char[0]);

if ($h <= 0x7F) {
return $h;
return $length === 1 ? $h : -1;
} elseif ($h < 0xC2) {
return -1;
} elseif ($h <= 0xDF) {
if ($length !== 2 || ! $this->isContinuationByte(1)) {
return -1;
}

return ($h & 0x1F) << 6 | (ord($this->char[1]) & 0x3F);
} elseif ($h <= 0xEF) {
if ($length !== 3 || ! $this->isContinuationByte(1) || ! $this->isContinuationByte(2)) {
return -1;
}

return ($h & 0x0F) << 12 | (ord($this->char[1]) & 0x3F) << 6 | (ord($this->char[2]) & 0x3F);
} elseif ($h <= 0xF4) {
return ($h & 0x0F) << 18 | (ord($this->char[1]) & 0x3F) << 12 | (ord($this->char[2]) & 0x3F) << 6 | (ord($this->char[3]) & 0x3F);
if (
$length !== 4
|| ! $this->isContinuationByte(1)
|| ! $this->isContinuationByte(2)
|| ! $this->isContinuationByte(3)
) {
return -1;
}

return ($h & 0x07) << 18
| (ord($this->char[1]) & 0x3F) << 12
| (ord($this->char[2]) & 0x3F) << 6
| (ord($this->char[3]) & 0x3F);
} else {
return -1;
}
}

private function isContinuationByte(int $offset): bool
{
if (! isset($this->char[$offset])) {
return false;
}

$byte = ord($this->char[$offset]);

return $byte >= 0x80 && $byte <= 0xBF;
}

/**
* Returns the hexadecimal value of the char.
*
Expand Down
4 changes: 4 additions & 0 deletions src/Helper/Signal.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ public function emit(string $signal, mixed $params = null): mixed
*/
public function getCallbackHash(mixed $callback): string
{
if ($callback instanceof \Closure) {
return spl_object_hash($callback);
}

if (! is_callable($callback, true, $callable_name)) {
throw new SignalException('invalid callback specified');
}
Expand Down
22 changes: 15 additions & 7 deletions src/Node/Host.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,15 @@ public function version(): array
if ($this->version === null) {
$raw = $this->request('version')->toList();

// Find the first array that contains real data
foreach ($raw as $item) {
if (is_array($item) && isset($item['version'])) {
$this->version = $item;
break;
if (isset($raw['version'])) {
$this->version = $raw;
} elseif (is_array($raw)) {
// Find the first array that contains real data
foreach ($raw as $item) {
if (is_array($item) && isset($item['version'])) {
$this->version = $item;
break;
}
}
}

Expand Down Expand Up @@ -889,8 +893,12 @@ public function whoami(): array
// Execute server request
$response = $this->request('whoami')->toList();

// response[1] contains the actual data
$data = $response[1] ?? [];
// Check if single-row associative array or nested list
if (isset($response['client_id']) || isset($response['virtualserver_id']) || isset($response['client_nickname'])) {
$data = $response;
} else {
$data = $response[1] ?? ($response[0] ?? []);
}

// Automatically convert StringHelper to strings
foreach ($data as $key => $val) {
Expand Down
21 changes: 13 additions & 8 deletions src/Node/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,19 @@ public function channelCreate(array $properties): int

$cid = null;

// Go backwards through the lines – the last line usually contains the new channel ID.
for ($i = count($result) - 1; $i >= 0; $i--) {
foreach ($result[$i] as $key => $value) {
if (stripos($key, 'cid') !== false) {
// Extract only the leading digit
if (preg_match('/\d+/', $value, $matches)) {
$cid = (int) $matches[0];
break 2;
if (isset($result['cid'])) {
$cid = (int) $result['cid'];
} else {
$lines = isset($result[0]) ? $result : [$result];
for ($i = count($lines) - 1; $i >= 0; $i--) {
if (is_array($lines[$i])) {
foreach ($lines[$i] as $key => $value) {
if (stripos($key, 'cid') !== false) {
if (preg_match('/\d+/', (string) $value, $matches)) {
$cid = (int) $matches[0];
break 2;
}
}
}
}
}
Expand Down
Loading
Loading