1: <?php
2:
3: declare(strict_types=1);
4:
5: /**
6: * Copyright OpenSearch Contributors
7: * SPDX-License-Identifier: Apache-2.0
8: *
9: * OpenSearch PHP client
10: *
11: * @link https://github.com/opensearch-project/opensearch-php/
12: * @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
13: * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
14: * @license https://www.gnu.org/licenses/lgpl-2.1.html GNU Lesser General Public License, Version 2.1
15: *
16: * Licensed to Elasticsearch B.V under one or more agreements.
17: * Elasticsearch B.V licenses this file to you under the Apache 2.0 License or
18: * the GNU Lesser General Public License, Version 2.1, at your option.
19: * See the LICENSE file in the project root for more information.
20: */
21:
22: namespace OpenSearch\Serializers;
23:
24: use OpenSearch\Exception\JsonException;
25: use OpenSearch\Exception\RuntimeException;
26:
27: if (!defined('JSON_INVALID_UTF8_SUBSTITUTE')) {
28: //PHP < 7.2 Define it as 0 so it does nothing
29: define('JSON_INVALID_UTF8_SUBSTITUTE', 0);
30: }
31:
32: class SmartSerializer implements SerializerInterface
33: {
34: /**
35: * {@inheritdoc}
36: */
37: public function serialize($data): string
38: {
39: if (is_string($data) === true) {
40: return $data;
41: } else {
42: $data = json_encode($data, JSON_PRESERVE_ZERO_FRACTION + JSON_INVALID_UTF8_SUBSTITUTE);
43: if ($data === false) {
44: throw new RuntimeException("Failed to JSON encode: ".json_last_error_msg());
45: }
46: if ($data === '[]') {
47: return '{}';
48: } else {
49: return $data;
50: }
51: }
52: }
53:
54: /**
55: * {@inheritdoc}
56: */
57: public function deserialize(?string $data, array $headers)
58: {
59: if ($this->isJson($headers)) {
60: return $this->decode($data);
61: }
62: return $data;
63: }
64:
65: /**
66: * Decode JSON data.
67: *
68: * @throws \OpenSearch\Exception\JsonException
69: */
70: private function decode(?string $data): array
71: {
72: if ($data === null || strlen($data) === 0) {
73: return [];
74: }
75:
76: try {
77: return json_decode($data, true, 512, JSON_THROW_ON_ERROR);
78: } catch (\JsonException $e) {
79: throw new JsonException($e->getCode(), $data, $e);
80: }
81: }
82:
83: /**
84: * Check the response content type to see if it is JSON.
85: *
86: * @param array<string,mixed> $headers
87: */
88: private function isJson(array $headers): bool
89: {
90: // Legacy support for 'transfer_stats'.
91: if (array_key_exists('content_type', $headers)) {
92: return str_contains($headers['content_type'], 'json');
93: }
94:
95: // Check PSR-7 headers.
96: $lowercaseHeaders = array_change_key_case($headers, CASE_LOWER);
97: if (array_key_exists('content-type', $lowercaseHeaders)) {
98: foreach ($lowercaseHeaders['content-type'] as $type) {
99: if (str_contains($type, 'json')) {
100: return true;
101: }
102: }
103: return false;
104: }
105:
106: // No content type header, so assume it is JSON.
107: return true;
108: }
109: }
110: