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