• 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 
33 namespace Google\Protobuf\Internal;
34 
35 class 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