• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<?php
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc.  All rights reserved.
5//
6// Use of this source code is governed by a BSD-style
7// license that can be found in the LICENSE file or at
8// https://developers.google.com/open-source/licenses/bsd
9
10namespace Google\Protobuf\Internal;
11
12class GPBJsonWire
13{
14
15    public static function serializeFieldToStream(
16        $value,
17        $field,
18        &$output, $has_field_name = true)
19    {
20        if ($has_field_name) {
21            $output->writeRaw("\"", 1);
22            $field_name = GPBJsonWire::formatFieldName($field);
23            $output->writeRaw($field_name, strlen($field_name));
24            $output->writeRaw("\":", 2);
25        }
26        return static::serializeFieldValueToStream(
27            $value,
28            $field,
29            $output,
30            !$has_field_name);
31    }
32
33    public static function serializeFieldValueToStream(
34        $values,
35        $field,
36        &$output,
37        $is_well_known = false)
38    {
39        if ($field->isMap()) {
40            $output->writeRaw("{", 1);
41            $first = true;
42            $map_entry = $field->getMessageType();
43            $key_field = $map_entry->getFieldByNumber(1);
44            $value_field = $map_entry->getFieldByNumber(2);
45
46            switch ($key_field->getType()) {
47            case GPBType::STRING:
48            case GPBType::SFIXED64:
49            case GPBType::INT64:
50            case GPBType::SINT64:
51            case GPBType::FIXED64:
52            case GPBType::UINT64:
53                $additional_quote = false;
54                break;
55            default:
56                $additional_quote = true;
57            }
58
59            foreach ($values as $key => $value) {
60                if ($first) {
61                    $first = false;
62                } else {
63                    $output->writeRaw(",", 1);
64                }
65                if ($additional_quote) {
66                    $output->writeRaw("\"", 1);
67                }
68                if (!static::serializeSingularFieldValueToStream(
69                    $key,
70                    $key_field,
71                    $output,
72                    $is_well_known)) {
73                    return false;
74                }
75                if ($additional_quote) {
76                    $output->writeRaw("\"", 1);
77                }
78                $output->writeRaw(":", 1);
79                if (!static::serializeSingularFieldValueToStream(
80                    $value,
81                    $value_field,
82                    $output,
83                    $is_well_known)) {
84                    return false;
85                }
86            }
87            $output->writeRaw("}", 1);
88            return true;
89        } elseif ($field->isRepeated()) {
90            $output->writeRaw("[", 1);
91            $first = true;
92            foreach ($values as $value) {
93                if ($first) {
94                    $first = false;
95                } else {
96                    $output->writeRaw(",", 1);
97                }
98                if (!static::serializeSingularFieldValueToStream(
99                    $value,
100                    $field,
101                    $output,
102                    $is_well_known)) {
103                    return false;
104                }
105            }
106            $output->writeRaw("]", 1);
107            return true;
108        } else {
109            return static::serializeSingularFieldValueToStream(
110                $values,
111                $field,
112                $output,
113                $is_well_known);
114        }
115    }
116
117    private static function serializeSingularFieldValueToStream(
118        $value,
119        $field,
120        &$output, $is_well_known = false)
121    {
122        switch ($field->getType()) {
123            case GPBType::SFIXED32:
124            case GPBType::SINT32:
125            case GPBType::INT32:
126                $str_value = strval($value);
127                $output->writeRaw($str_value, strlen($str_value));
128                break;
129            case GPBType::FIXED32:
130            case GPBType::UINT32:
131                if ($value < 0) {
132                    $value = bcadd($value, "4294967296");
133                }
134                $str_value = strval($value);
135                $output->writeRaw($str_value, strlen($str_value));
136                break;
137            case GPBType::FIXED64:
138            case GPBType::UINT64:
139                if ($value < 0) {
140                    $value = bcadd($value, "18446744073709551616");
141                }
142                // Intentional fall through.
143            case GPBType::SFIXED64:
144            case GPBType::INT64:
145            case GPBType::SINT64:
146                $output->writeRaw("\"", 1);
147                $str_value = strval($value);
148                $output->writeRaw($str_value, strlen($str_value));
149                $output->writeRaw("\"", 1);
150                break;
151            case GPBType::FLOAT:
152                if (is_nan($value)) {
153                    $str_value = "\"NaN\"";
154                } elseif ($value === INF) {
155                    $str_value = "\"Infinity\"";
156                } elseif ($value === -INF) {
157                    $str_value = "\"-Infinity\"";
158                } else {
159                    $str_value = sprintf("%.8g", $value);
160                }
161                $output->writeRaw($str_value, strlen($str_value));
162                break;
163            case GPBType::DOUBLE:
164                if (is_nan($value)) {
165                    $str_value = "\"NaN\"";
166                } elseif ($value === INF) {
167                    $str_value = "\"Infinity\"";
168                } elseif ($value === -INF) {
169                    $str_value = "\"-Infinity\"";
170                } else {
171                    $str_value = sprintf("%.17g", $value);
172                }
173                $output->writeRaw($str_value, strlen($str_value));
174                break;
175            case GPBType::ENUM:
176                $enum_desc = $field->getEnumType();
177                if ($enum_desc->getClass() === "Google\Protobuf\NullValue") {
178                    $output->writeRaw("null", 4);
179                    break;
180                }
181                $enum_value_desc = $enum_desc->getValueByNumber($value);
182                if (!is_null($enum_value_desc)) {
183                    $str_value = $enum_value_desc->getName();
184                    $output->writeRaw("\"", 1);
185                    $output->writeRaw($str_value, strlen($str_value));
186                    $output->writeRaw("\"", 1);
187                } else {
188                    $str_value = strval($value);
189                    $output->writeRaw($str_value, strlen($str_value));
190                }
191                break;
192            case GPBType::BOOL:
193                if ($value) {
194                    $output->writeRaw("true", 4);
195                } else {
196                    $output->writeRaw("false", 5);
197                }
198                break;
199            case GPBType::BYTES:
200                $bytes_value = base64_encode($value);
201                $output->writeRaw("\"", 1);
202                $output->writeRaw($bytes_value, strlen($bytes_value));
203                $output->writeRaw("\"", 1);
204                break;
205            case GPBType::STRING:
206                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
207                $output->writeRaw($value, strlen($value));
208                break;
209            //    case GPBType::GROUP:
210            //      echo "GROUP\xA";
211            //      trigger_error("Not implemented.", E_ERROR);
212            //      break;
213            case GPBType::MESSAGE:
214                $value->serializeToJsonStream($output);
215                break;
216            default:
217                user_error("Unsupported type.");
218                return false;
219        }
220        return true;
221    }
222
223    private static function formatFieldName($field)
224    {
225        return $field->getJsonName();
226    }
227
228    // Used for escaping control chars in strings.
229    private static $k_control_char_limit = 0x20;
230
231    private static function jsonNiceEscape($c)
232    {
233      switch ($c) {
234          case '"':  return "\\\"";
235          case '\\': return "\\\\";
236          case '/': return "\\/";
237          case '\b': return "\\b";
238          case '\f': return "\\f";
239          case '\n': return "\\n";
240          case '\r': return "\\r";
241          case '\t': return "\\t";
242          default:   return NULL;
243      }
244    }
245
246    private static function isJsonEscaped($c)
247    {
248        // See RFC 4627.
249        return $c < chr($k_control_char_limit) || $c === "\"" || $c === "\\";
250    }
251
252    public static function escapedJson($value)
253    {
254        $escaped_value = "";
255        $unescaped_run = "";
256        for ($i = 0; $i < strlen($value); $i++) {
257            $c = $value[$i];
258            // Handle escaping.
259            if (static::isJsonEscaped($c)) {
260                // Use a "nice" escape, like \n, if one exists for this
261                // character.
262                $escape = static::jsonNiceEscape($c);
263                if (is_null($escape)) {
264                    $escape = "\\u00" . bin2hex($c);
265                }
266                if ($unescaped_run !== "") {
267                    $escaped_value .= $unescaped_run;
268                    $unescaped_run = "";
269                }
270                $escaped_value .= $escape;
271            } else {
272              if ($unescaped_run === "") {
273                $unescaped_run .= $c;
274              }
275            }
276        }
277        $escaped_value .= $unescaped_run;
278        return $escaped_value;
279    }
280
281}
282