Optimized code of Hyperf\SocketIOServer\Parser\Decoder::decode(). (#6455)

Co-authored-by: 李铭昕 <715557344@qq.com>
This commit is contained in:
pandaLIU 2024-01-11 13:58:44 +08:00 committed by GitHub
parent 0a0d1e6ac6
commit b8d6f7529a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 34 deletions

View File

@ -1,4 +1,10 @@
# v3.0.47 - TBD
# v3.0.48 - TBD
# v3.0.47 - 2024-01-11
## Optimized
- [#6455](https://github.com/hyperf/hyperf/pull/6455) Optimized code of Hyperf\SocketIOServer\Parser\Decoder::decode().
# v3.0.46 - 2023-11-30

View File

@ -67,7 +67,7 @@
"php-amqplib/php-amqplib": "^3.5",
"php-di/phpdoc-reader": "^2.2",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan": "1.10.47",
"phpunit/phpunit": "^9.5",
"predis/predis": "^1.1",
"promphp/prometheus_client_php": "2.2.*",

View File

@ -1,5 +1,11 @@
# Changelogs
# v3.0.47 - 2024-01-11
## Optimized
- [#6455](https://github.com/hyperf/hyperf/pull/6455) Optimized code of Hyperf\SocketIOServer\Parser\Decoder::decode().
# v3.0.46 - 2023-11-30
## Fixed

View File

@ -1,5 +1,11 @@
# 版本更新记录
# v3.0.47 - 2024-01-11
## 优化
- [#6455](https://github.com/hyperf/hyperf/pull/6455) 优化 `Hyperf\SocketIOServer\Parser\Decoder::decode()` 的代码实现。
# v3.0.46 - 2023-11-30
## Fixed

View File

@ -11,49 +11,80 @@ declare(strict_types=1);
*/
namespace Hyperf\SocketIOServer\Parser;
use InvalidArgumentException;
use Throwable;
class Decoder
{
public function decode($payload): Packet
/**
* @param string $payload such as `2/ws?foo=xxx,2["event","hellohyperf"]`
*/
public function decode(string $payload): Packet
{
// type
$i = 0;
$type = $payload[$i];
if (! $payload) {
throw new InvalidArgumentException('Empty packet');
}
$length = strlen($payload);
$type = $payload[0];
if (! in_array($type, [Packet::OPEN, Packet::CLOSE, Packet::EVENT, Packet::ACK], true)) {
throw new InvalidArgumentException('Unknown packet type ' . $type);
}
if ($length === 1) {
return $this->makePacket($type);
}
$nsp = '/';
$query = [];
++$i;
// TODO: Support attachment
$payload = substr($payload, 1);
if ($payload[0] === '/') {
// parse url
$exploded = explode(',', $payload, 2);
$parsedUrl = parse_url($exploded[0]);
$nsp = $parsedUrl['path'];
if (! empty($parsedUrl['query'])) {
parse_str($parsedUrl['query'], $query);
}
// namespace
if (isset($payload[$i]) && $payload[$i] === '/') {
++$i;
while ($payload[$i] !== ',' && $payload[$i] !== '?') {
$nsp .= $payload[$i];
++$i;
}
if ($payload[$i] === '?') {
++$i;
$query = '';
while ($payload[$i] !== ',') {
$query .= $payload[$i];
++$i;
}
$result = [];
parse_str($query, $result);
$query = $result;
}
++$i;
$payload = $exploded[1] ?? null;
}
// id
$id = '';
while (mb_strlen($payload) > $i && filter_var($payload[$i], FILTER_VALIDATE_INT) !== false) {
$id .= $payload[$i];
++$i;
if (! $payload) {
return $this->makePacket($type, $nsp, $query);
}
// data
$data = json_decode(mb_substr($payload, $i), true) ?? [];
$offset = 0;
while (true) {
$char = $payload[$offset] ?? null;
if ($char === null) {
break;
}
if (is_numeric($char)) {
++$offset;
} else {
break;
}
}
$id = substr($payload, 0, $offset);
$payload = substr($payload, $offset);
$data = [];
if ($payload) {
try {
$data = json_decode($payload, true, flags: JSON_THROW_ON_ERROR);
} catch (Throwable $exception) {
throw new InvalidArgumentException('Invalid data', (int) $exception->getCode(), $exception);
}
}
return $this->makePacket($type, $nsp, $query, $id, $data);
}
public function makePacket(string $type, string $nsp = '/', array $query = [], string $id = '', array $data = []): Packet
{
return Packet::create([
'type' => $type,
'nsp' => $nsp,

View File

@ -12,6 +12,8 @@ declare(strict_types=1);
namespace HyperfTest\SocketIOServer\Cases;
use Hyperf\SocketIOServer\Parser\Decoder;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\CoversNothing;
/**
* @internal
@ -57,5 +59,38 @@ class DecoderTest extends AbstractTestCase
$this->assertEquals('1', $packet['type']);
$this->assertEquals('/ws', $packet['nsp']);
$this->assertEquals(['foo' => 'bar', 'baz' => '1'], $packet['query']);
$result = $decoder->decode('2/1');
$this->assertEquals(2, $result->type);
$this->assertEquals('/1', $result->nsp);
$result = $decoder->decode('2/1,2["event","hellohyperf"]');
$this->assertEquals(2, $result->type);
$this->assertEquals('/1', $result->nsp);
$this->assertEquals('2', $result->id);
$this->assertEquals([
], $result->query);
$result = $decoder->decode('2/1?foo=xxx,2["event","hellohyperf"]');
$this->assertEquals(2, $result->type);
$this->assertEquals('/1', $result->nsp);
$this->assertEquals('2', $result->id);
$this->assertEquals([
'foo' => 'xxx',
], $result->query);
$this->assertEquals(json_decode('["event","hellohyperf"]', true), $result->data);
$result = $decoder->decode('2/1?foo=xxx,2{"event": "JOIN"}');
$this->assertEquals(2, $result->type);
$this->assertEquals('/1', $result->nsp);
$this->assertEquals('2', $result->id);
$this->assertEquals([
'foo' => 'xxx',
], $result->query);
$this->assertEquals(['event' => 'JOIN'], $result->data);
try {
$decoder->decode('2/1?2["event","hellohyperf"]');
$this->assertTrue(false);
} catch (InvalidArgumentException $e) {
$this->assertEquals('Invalid data', $e->getMessage());
}
}
}