blob: 43f4745dd1907b35eac4854f3def6b9c328035ca [file] [log] [blame]
avm9996399bb77c2020-01-27 03:15:08 +01001<?php
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc. All rights reserved.
5// https://developers.google.com/protocol-buffers/
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
10//
11// * Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// * Redistributions in binary form must reproduce the above
14// copyright notice, this list of conditions and the following disclaimer
15// in the documentation and/or other materials provided with the
16// distribution.
17// * Neither the name of Google Inc. nor the names of its
18// contributors may be used to endorse or promote products derived from
19// this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33namespace Google\Protobuf\Internal;
34
35class GPBJsonWire
36{
37
38 public static function serializeFieldToStream(
39 $value,
40 $field,
41 &$output, $has_field_name = true)
42 {
43 if ($has_field_name) {
44 $output->writeRaw("\"", 1);
45 $field_name = GPBJsonWire::formatFieldName($field);
46 $output->writeRaw($field_name, strlen($field_name));
47 $output->writeRaw("\":", 2);
48 }
49 return static::serializeFieldValueToStream(
50 $value,
51 $field,
52 $output,
53 !$has_field_name);
54 }
55
56 public static function serializeFieldValueToStream(
57 $values,
58 $field,
59 &$output,
60 $is_well_known = false)
61 {
62 if ($field->isMap()) {
63 $output->writeRaw("{", 1);
64 $first = true;
65 $map_entry = $field->getMessageType();
66 $key_field = $map_entry->getFieldByNumber(1);
67 $value_field = $map_entry->getFieldByNumber(2);
68
69 switch ($key_field->getType()) {
70 case GPBType::STRING:
71 case GPBType::SFIXED64:
72 case GPBType::INT64:
73 case GPBType::SINT64:
74 case GPBType::FIXED64:
75 case GPBType::UINT64:
76 $additional_quote = false;
77 break;
78 default:
79 $additional_quote = true;
80 }
81
82 foreach ($values as $key => $value) {
83 if ($first) {
84 $first = false;
85 } else {
86 $output->writeRaw(",", 1);
87 }
88 if ($additional_quote) {
89 $output->writeRaw("\"", 1);
90 }
91 if (!static::serializeSingularFieldValueToStream(
92 $key,
93 $key_field,
94 $output,
95 $is_well_known)) {
96 return false;
97 }
98 if ($additional_quote) {
99 $output->writeRaw("\"", 1);
100 }
101 $output->writeRaw(":", 1);
102 if (!static::serializeSingularFieldValueToStream(
103 $value,
104 $value_field,
105 $output,
106 $is_well_known)) {
107 return false;
108 }
109 }
110 $output->writeRaw("}", 1);
111 return true;
112 } elseif ($field->isRepeated()) {
113 $output->writeRaw("[", 1);
114 $first = true;
115 foreach ($values as $value) {
116 if ($first) {
117 $first = false;
118 } else {
119 $output->writeRaw(",", 1);
120 }
121 if (!static::serializeSingularFieldValueToStream(
122 $value,
123 $field,
124 $output,
125 $is_well_known)) {
126 return false;
127 }
128 }
129 $output->writeRaw("]", 1);
130 return true;
131 } else {
132 return static::serializeSingularFieldValueToStream(
133 $values,
134 $field,
135 $output,
136 $is_well_known);
137 }
138 }
139
140 private static function serializeSingularFieldValueToStream(
141 $value,
142 $field,
143 &$output, $is_well_known = false)
144 {
145 switch ($field->getType()) {
146 case GPBType::SFIXED32:
147 case GPBType::SINT32:
148 case GPBType::INT32:
149 $str_value = strval($value);
150 $output->writeRaw($str_value, strlen($str_value));
151 break;
152 case GPBType::FIXED32:
153 case GPBType::UINT32:
154 if ($value < 0) {
155 $value = bcadd($value, "4294967296");
156 }
157 $str_value = strval($value);
158 $output->writeRaw($str_value, strlen($str_value));
159 break;
160 case GPBType::FIXED64:
161 case GPBType::UINT64:
162 if ($value < 0) {
163 $value = bcadd($value, "18446744073709551616");
164 }
165 // Intentional fall through.
166 case GPBType::SFIXED64:
167 case GPBType::INT64:
168 case GPBType::SINT64:
169 $output->writeRaw("\"", 1);
170 $str_value = strval($value);
171 $output->writeRaw($str_value, strlen($str_value));
172 $output->writeRaw("\"", 1);
173 break;
174 case GPBType::FLOAT:
175 if (is_nan($value)) {
176 $str_value = "\"NaN\"";
177 } elseif ($value === INF) {
178 $str_value = "\"Infinity\"";
179 } elseif ($value === -INF) {
180 $str_value = "\"-Infinity\"";
181 } else {
182 $str_value = sprintf("%.8g", $value);
183 }
184 $output->writeRaw($str_value, strlen($str_value));
185 break;
186 case GPBType::DOUBLE:
187 if (is_nan($value)) {
188 $str_value = "\"NaN\"";
189 } elseif ($value === INF) {
190 $str_value = "\"Infinity\"";
191 } elseif ($value === -INF) {
192 $str_value = "\"-Infinity\"";
193 } else {
194 $str_value = sprintf("%.17g", $value);
195 }
196 $output->writeRaw($str_value, strlen($str_value));
197 break;
198 case GPBType::ENUM:
199 $enum_desc = $field->getEnumType();
200 if ($enum_desc->getClass() === "Google\Protobuf\NullValue") {
201 $output->writeRaw("null", 4);
202 break;
203 }
204 $enum_value_desc = $enum_desc->getValueByNumber($value);
205 if (!is_null($enum_value_desc)) {
206 $str_value = $enum_value_desc->getName();
207 $output->writeRaw("\"", 1);
208 $output->writeRaw($str_value, strlen($str_value));
209 $output->writeRaw("\"", 1);
210 } else {
211 $str_value = strval($value);
212 $output->writeRaw($str_value, strlen($str_value));
213 }
214 break;
215 case GPBType::BOOL:
216 if ($value) {
217 $output->writeRaw("true", 4);
218 } else {
219 $output->writeRaw("false", 5);
220 }
221 break;
222 case GPBType::BYTES:
223 $bytes_value = base64_encode($value);
224 $output->writeRaw("\"", 1);
225 $output->writeRaw($bytes_value, strlen($bytes_value));
226 $output->writeRaw("\"", 1);
227 break;
228 case GPBType::STRING:
229 $value = json_encode($value, JSON_UNESCAPED_UNICODE);
230 $output->writeRaw($value, strlen($value));
231 break;
232 // case GPBType::GROUP:
233 // echo "GROUP\xA";
234 // trigger_error("Not implemented.", E_ERROR);
235 // break;
236 case GPBType::MESSAGE:
237 $value->serializeToJsonStream($output);
238 break;
239 default:
240 user_error("Unsupported type.");
241 return false;
242 }
243 return true;
244 }
245
246 private static function formatFieldName($field)
247 {
248 return $field->getJsonName();
249 }
250
251 // Used for escaping control chars in strings.
252 private static $k_control_char_limit = 0x20;
253
254 private static function jsonNiceEscape($c)
255 {
256 switch ($c) {
257 case '"': return "\\\"";
258 case '\\': return "\\\\";
259 case '/': return "\\/";
260 case '\b': return "\\b";
261 case '\f': return "\\f";
262 case '\n': return "\\n";
263 case '\r': return "\\r";
264 case '\t': return "\\t";
265 default: return NULL;
266 }
267 }
268
269 private static function isJsonEscaped($c)
270 {
271 // See RFC 4627.
272 return $c < chr($k_control_char_limit) || $c === "\"" || $c === "\\";
273 }
274
275 public static function escapedJson($value)
276 {
277 $escaped_value = "";
278 $unescaped_run = "";
279 for ($i = 0; $i < strlen($value); $i++) {
280 $c = $value[$i];
281 // Handle escaping.
282 if (static::isJsonEscaped($c)) {
283 // Use a "nice" escape, like \n, if one exists for this
284 // character.
285 $escape = static::jsonNiceEscape($c);
286 if (is_null($escape)) {
287 $escape = "\\u00" . bin2hex($c);
288 }
289 if ($unescaped_run !== "") {
290 $escaped_value .= $unescaped_run;
291 $unescaped_run = "";
292 }
293 $escaped_value .= $escape;
294 } else {
295 if ($unescaped_run === "") {
296 $unescaped_run .= $c;
297 }
298 }
299 }
300 $escaped_value .= $unescaped_run;
301 return $escaped_value;
302 }
303
304}