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