• 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// 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}
305