baseUrl, '/') . '/store-api/search'; $sanitizedCriteria = $this->sanitizeValue($criteria); $body = json_encode( $sanitizedCriteria, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE ); if (!is_string($body)) { throw new RuntimeException('Failed to encode Store API criteria.'); } $response = $this->httpClient->request('POST', $url, [ 'headers' => [ 'Content-Type' => 'application/json; charset=utf-8', 'Accept' => 'application/json', 'sw-access-key' => $this->salesChannelAccessKey, ], 'body' => $body, 'timeout' => $this->timeoutSeconds, ]); $statusCode = $response->getStatusCode(); $content = $response->getContent(false); $content = $this->sanitizeString($content); if ($statusCode < 200 || $statusCode >= 300) { throw $this->buildHttpFailure($statusCode, $content); } $data = json_decode($content, true); if (!is_array($data)) { throw new StoreApiException( 'Shopware Store API returned invalid JSON.', $statusCode, true, $this->containsUtf8FailureSignal($content), true ); } return $data; } private function buildHttpFailure(int $statusCode, string $content): StoreApiException { $preview = mb_substr(trim($content), 0, 1000); $utf8Failure = $this->containsUtf8FailureSignal($preview); $serverFailure = $statusCode >= 500; return new StoreApiException( sprintf( 'Shopware Store API request failed with status %d. Response: %s', $statusCode, $preview ), $statusCode, $serverFailure, $utf8Failure, $serverFailure || $utf8Failure ); } private function containsUtf8FailureSignal(string $content): bool { $normalized = mb_strtolower($content, 'UTF-8'); return str_contains($normalized, 'malformed utf-8') || str_contains($normalized, 'malformed utf8') || str_contains($normalized, 'invalid utf-8') || str_contains($normalized, 'invalid utf8') || str_contains($normalized, 'possibly incorrectly encoded'); } private function sanitizeValue(mixed $value): mixed { if (is_array($value)) { $out = []; foreach ($value as $key => $item) { $out[$key] = $this->sanitizeValue($item); } return $out; } if (!is_string($value)) { return $value; } return $this->sanitizeString($value); } private function sanitizeString(string $value): string { if (preg_match('//u', $value) === 1) { return $value; } if (function_exists('mb_convert_encoding')) { $value = mb_convert_encoding($value, 'UTF-8', 'UTF-8'); } if (preg_match('//u', $value) === 1) { return $value; } if (function_exists('iconv')) { $converted = @iconv('UTF-8', 'UTF-8//IGNORE', $value); if (is_string($converted) && $converted !== '') { return $converted; } } return ''; } }