diff --git a/src/ManticoreSearch/Client.php b/src/ManticoreSearch/Client.php index 2a53987..173308f 100644 --- a/src/ManticoreSearch/Client.php +++ b/src/ManticoreSearch/Client.php @@ -414,7 +414,7 @@ public function getSettings(): Settings { protected function fetchSettings(): Settings { $resp = $this->sendRequest('SHOW SETTINGS'); /** @var array{0:array{columns:array,data:array{Setting_name:string,Value:string}}} */ - $data = (array)json_decode($resp->getBody(), true); + $data = (array)simdjson_decode($resp->getBody(), true); $settings = new Vector(); foreach ($data[0]['data'] as ['Setting_name' => $key, 'Value' => $value]) { // If the key is plugin_dir check env first and after choose @@ -442,7 +442,7 @@ protected function fetchSettings(): Settings { // Gather variables also $resp = $this->sendRequest('SHOW VARIABLES'); /** @var array{0:array{columns:array,data:array{Setting_name:string,Value:string}}} */ - $data = (array)json_decode($resp->getBody(), true); + $data = (array)simdjson_decode($resp->getBody(), true); foreach ($data[0]['data'] as ['Variable_name' => $key, 'Value' => $value]) { $settings->push( new Map( diff --git a/src/Network/Request.php b/src/Network/Request.php index 7b7bdf0..ce3a99e 100644 --- a/src/Network/Request.php +++ b/src/Network/Request.php @@ -19,6 +19,7 @@ use Manticoresearch\Buddy\Core\ManticoreSearch\RequestFormat; use Manticoresearch\Buddy\Core\ManticoreSearch\Settings; use Manticoresearch\Buddy\Core\Tool\Buddy; +use SimdJsonException; final class Request { const PAYLOAD_FIELDS = [ @@ -81,6 +82,7 @@ public static function default(string $id = '0'): static { * @param string $id * @return static */ + public static function fromString(string $data, string $id = '0'): static { $self = new static; $self->id = $id; @@ -149,17 +151,21 @@ public static function validateOrFail(string $data): array { if ($data === '') { throw new InvalidNetworkRequestError('The payload is missing'); } + try { + $result = simdjson_decode($data, true, 512); + if (!is_array($result)) { + throw new InvalidNetworkRequestError('Invalid request payload is passed'); + } + } catch (SimdJsonException) { + throw new InvalidNetworkRequestError('Failed to parse request payload'); + } + /** @var array{ * type:string, * error:array{message:string,body?:array{error:string}}, * message:array{path_query:string,body:string}, * version:int} $result */ - $result = json_decode($data, true, 512, JSON_INVALID_UTF8_SUBSTITUTE); - if (!is_array($result)) { - throw new InvalidNetworkRequestError('Invalid request payload is passed'); - } - return $result; } @@ -234,11 +240,11 @@ protected function parseOrFail(array $payload): static { $this->path = $path; $this->format = $format; $this->endpointBundle = $endpointBundle; - $this->mySQLTool = static::detectMySQLTool($payload['message']['body']); + $this->mySQLTool = $format === RequestFormat::SQL ? static::detectMySQLTool($payload['message']['body']) : null; $this->payload = (in_array($endpointBundle, [Endpoint::Elastic, Endpoint::Bulk])) ? trim($payload['message']['body']) : static::removeComments($payload['message']['body']); - $this->command = strtok(strtolower($this->payload), ' ') ?: ''; + $this->command = strtolower(strtok($this->payload, ' ') ?: ''); $this->error = $payload['error']['message']; $this->errorBody = $payload['error']['body'] ?? []; $this->version = match ($payload['version']) { diff --git a/src/Network/Struct.php b/src/Network/Struct.php index ac233e7..ec6ce36 100644 --- a/src/Network/Struct.php +++ b/src/Network/Struct.php @@ -111,9 +111,7 @@ public function addBigIntField(string $field): void { * @return bool */ public static function isValid(string $json): bool { - // TODO: replace with json_validate once we more to 8.3 - $result = json_decode($json, true, static::JSON_DEPTH, static::JSON_FLAGS); - return !!$result; + return simdjson_is_valid($json, static::JSON_DEPTH); } /** @@ -123,9 +121,10 @@ public static function isValid(string $json): bool { */ public static function fromJson(string $json): self { /** @var array */ - $result = (array)json_decode($json, true, static::JSON_DEPTH, static::JSON_FLAGS); + $result = (array)simdjson_decode($json, true, static::JSON_DEPTH); $bigIntFields = []; if (static::hasBigInt($json)) { + // We need here to keep original json decode cuzit has bigIntFields /** @var array */ $modified = json_decode($json, true, static::JSON_DEPTH, static::JSON_FLAGS | JSON_BIGINT_AS_STRING); static::traverseAndTrack($modified, $result, $bigIntFields); diff --git a/src/Plugin/Pluggable.php b/src/Plugin/Pluggable.php index 6912679..6032040 100644 --- a/src/Plugin/Pluggable.php +++ b/src/Plugin/Pluggable.php @@ -219,7 +219,7 @@ public function getList(): array { return []; } /** @var array{require?:array} $composerJson */ - $composerJson = json_decode($composerContent, true); + $composerJson = simdjson_decode($composerContent, true); if (!isset($composerJson['require'])) { return []; } @@ -274,7 +274,7 @@ public function getClassNamespaceByFullName(string $name): string { throw new Exception("Failed to get contents of composer.json for plugin: $name"); } - $composerJson = json_decode($composerContent, true); + $composerJson = simdjson_decode($composerContent, true); if (!$composerJson) { throw new Exception("Failed to decode contents of composer.json file for plugin: $name"); } @@ -465,7 +465,7 @@ public function fetchLocalPlugins(): array { throw new Exception("Failed to read composer file for plugin: $shortName"); } /** @var array{name:?string} */ - $composer = json_decode($composerContent, true); + $composer = simdjson_decode($composerContent, true); if (!isset($composer['name'])) { throw new Exception("Failed to detect local plugin name from file: $composerFile"); } diff --git a/src/Tool/KeyboardLayout.php b/src/Tool/KeyboardLayout.php index 47d7739..e3ec89c 100644 --- a/src/Tool/KeyboardLayout.php +++ b/src/Tool/KeyboardLayout.php @@ -156,7 +156,7 @@ protected static function getLangMap(): array { } /** @var array> $langMap */ - $langMap = json_decode($configContent, true); + $langMap = simdjson_decode($configContent, true); if (!is_array($langMap)) { throw new Exception("Invalid keyboard layout config file at '$configPath'"); } diff --git a/src/Tool/SqlQueryParser.php b/src/Tool/SqlQueryParser.php index 7f079c4..9f7e994 100644 --- a/src/Tool/SqlQueryParser.php +++ b/src/Tool/SqlQueryParser.php @@ -82,7 +82,6 @@ private static function getCreator(): PHPSQLCreator { * @throws GenericError */ public static function parse(string $payload, Closure $preProcessorCallback, mixed $args): ?array { - $result = (bool)call_user_func($preProcessorCallback, $args); if ($result === false) { return null; diff --git a/test/BuddyCore/Network/RequestTest.php b/test/BuddyCore/Network/RequestTest.php index 864c699..1de39c6 100644 --- a/test/BuddyCore/Network/RequestTest.php +++ b/test/BuddyCore/Network/RequestTest.php @@ -169,14 +169,19 @@ public function testManticoreQueryValidationFail(): void { $this->assertEquals(InvalidNetworkRequestError::class, $exCls); $this->assertEquals('The payload is missing', $exMsg); - $query = "Invalid query\nis passed\nagain"; + $query = '"payload as a string"'; [$exCls, $exMsg] = self::getExceptionInfo(Request::class, 'fromString', [$query]); $this->assertEquals(InvalidNetworkRequestError::class, $exCls); $this->assertEquals('Invalid request payload is passed', $exMsg); + $query = "Invalid query\nis passed\nagain"; + [$exCls, $exMsg] = self::getExceptionInfo(Request::class, 'fromString', [$query]); + $this->assertEquals(InvalidNetworkRequestError::class, $exCls); + $this->assertEquals('Failed to parse request payload', $exMsg); + $query = 'Query\nwith unvalid\n\n{"request_body"}'; [$exCls, $exMsg] = self::getExceptionInfo(Request::class, 'fromString', [$query]); $this->assertEquals(InvalidNetworkRequestError::class, $exCls); - $this->assertEquals('Invalid request payload is passed', $exMsg); + $this->assertEquals('Failed to parse request payload', $exMsg); } } diff --git a/test/src/Trait/TestFunctionalTrait.php b/test/src/Trait/TestFunctionalTrait.php index 06c30f5..2c17ca9 100644 --- a/test/src/Trait/TestFunctionalTrait.php +++ b/test/src/Trait/TestFunctionalTrait.php @@ -318,13 +318,13 @@ protected static function runHttpQuery( /** @var array>,total?:string,columns?:string}> $result */ $result = match ($path) { - 'cli_json', 'sql', 'sql?mode=raw' => (array)json_decode(implode(PHP_EOL, $output), true), + 'cli_json', 'sql', 'sql?mode=raw' => (array)simdjson_decode(implode(PHP_EOL, $output), true), 'cli' => [ ['columns' => implode(PHP_EOL, $output), 'data' => [], 'error' => ''], ], // assuming Elastic-like endpoint is passed default => [ - ['data' => [(array)json_decode($output[0] ?? '{}', true)], 'error' => ''], + ['data' => [(array)simdjson_decode($output[0] ?? '{}', true)], 'error' => ''], ], }; print_r($output); @@ -361,7 +361,7 @@ protected static function runHttpBuddyRequest( $redirect = $redirectOutput ? '2>&1' : ''; exec("curl -s 127.0.0.1:$port -H 'Content-type: application/json' -d @$payloadFile $redirect", $output); /** @var array{version:int,type:string,message:array,data:array>}>} $result */ - $result = (array)json_decode($output[0] ?? '{}', true); + $result = (array)simdjson_decode($output[0] ?? '{}', true); return $result; }