dolibarr  20.0.0-alpha
json.lib.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2011-2012 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2011-2012 Regis Houssin <regis.houssin@inodbox.com>
4  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  * or see https://www.gnu.org/
19  */
20 
27 if (!function_exists('json_encode')) {
35  function json_encode($elements)
36  {
37  return dol_json_encode($elements);
38  }
39 }
40 
41 
52 function dol_json_encode($elements)
53 {
54  dol_syslog("For better performance, enable the native json in your PHP", LOG_WARNING);
55 
56  $num = 0;
57  if (is_object($elements)) { // Count number of properties for an object
58  foreach ($elements as $key => $value) {
59  $num++;
60  }
61  } else {
62  $num = count($elements);
63  }
64  //var_dump($num);
65 
66  // determine type
67  if (is_numeric(key($elements)) && key($elements) == 0) {
68  // indexed (list)
69  $keysofelements = array_keys($elements); // Elements array mus have key that does not start with 0 and end with num-1, so we will use this later.
70  $output = '[';
71  for ($i = 0, $last = ($num - 1); $i < $num; $i++) {
72  if (!isset($elements[$keysofelements[$i]])) {
73  continue;
74  }
75  if (is_array($elements[$keysofelements[$i]]) || is_object($elements[$keysofelements[$i]])) {
76  $output .= json_encode($elements[$keysofelements[$i]]);
77  } else {
78  $output .= _val($elements[$keysofelements[$i]]);
79  }
80  if ($i !== $last) {
81  $output .= ',';
82  }
83  }
84  $output .= ']';
85  } else {
86  // associative (object)
87  $output = '{';
88  $last = $num - 1;
89  $i = 0;
90  $tmpelements = array();
91  if (is_array($elements)) {
92  $tmpelements = $elements;
93  }
94  if (is_object($elements)) {
95  $tmpelements = get_object_vars($elements);
96  }
97  foreach ($tmpelements as $key => $value) {
98  $output .= '"'.$key.'":';
99  if (is_array($value)) {
100  $output .= json_encode($value);
101  } else {
102  $output .= _val($value);
103  }
104  if ($i !== $last) {
105  $output .= ',';
106  }
107  ++$i;
108  }
109  $output .= '}';
110  }
111 
112  // return
113  return $output;
114 }
115 
122 function _val($val)
123 {
124  if (is_string($val)) {
125  // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
126  $ascii = '';
127  $strlen_var = strlen($val);
128 
129  /*
130  * Iterate over every character in the string,
131  * escaping with a slash or encoding to UTF-8 where necessary
132  */
133  for ($c = 0; $c < $strlen_var; ++$c) {
134  $ord_var_c = ord($val[$c]);
135 
136  switch (true) {
137  case $ord_var_c == 0x08:
138  $ascii .= '\b';
139  break;
140  case $ord_var_c == 0x09:
141  $ascii .= '\t';
142  break;
143  case $ord_var_c == 0x0A:
144  $ascii .= '\n';
145  break;
146  case $ord_var_c == 0x0C:
147  $ascii .= '\f';
148  break;
149  case $ord_var_c == 0x0D:
150  $ascii .= '\r';
151  break;
152 
153  case $ord_var_c == 0x22:
154  case $ord_var_c == 0x2F:
155  case $ord_var_c == 0x5C:
156  // double quote, slash, slosh
157  $ascii .= '\\'.$val[$c];
158  break;
159 
160  case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
161  // characters U-00000000 - U-0000007F (same as ASCII)
162  $ascii .= $val[$c];
163  break;
164 
165  case (($ord_var_c & 0xE0) == 0xC0):
166  // characters U-00000080 - U-000007FF, mask 110XXXXX
167  // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
168  $char = pack('C*', $ord_var_c, ord($val[$c + 1]));
169  $c += 1;
170  $utf16 = utf82utf16($char);
171  $ascii .= sprintf('\u%04s', bin2hex($utf16));
172  break;
173 
174  case (($ord_var_c & 0xF0) == 0xE0):
175  // characters U-00000800 - U-0000FFFF, mask 1110XXXX
176  // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
177  $char = pack('C*', $ord_var_c, ord($val[$c + 1]), ord($val[$c + 2]));
178  $c += 2;
179  $utf16 = utf82utf16($char);
180  $ascii .= sprintf('\u%04s', bin2hex($utf16));
181  break;
182 
183  case (($ord_var_c & 0xF8) == 0xF0):
184  // characters U-00010000 - U-001FFFFF, mask 11110XXX
185  // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
186  $char = pack('C*', $ord_var_c, ord($val[$c + 1]), ord($val[$c + 2]), ord($val[$c + 3]));
187  $c += 3;
188  $utf16 = utf82utf16($char);
189  $ascii .= sprintf('\u%04s', bin2hex($utf16));
190  break;
191 
192  case (($ord_var_c & 0xFC) == 0xF8):
193  // characters U-00200000 - U-03FFFFFF, mask 111110XX
194  // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
195  $char = pack('C*', $ord_var_c, ord($val[$c + 1]), ord($val[$c + 2]), ord($val[$c + 3]), ord($val[$c + 4]));
196  $c += 4;
197  $utf16 = utf82utf16($char);
198  $ascii .= sprintf('\u%04s', bin2hex($utf16));
199  break;
200 
201  case (($ord_var_c & 0xFE) == 0xFC):
202  // characters U-04000000 - U-7FFFFFFF, mask 1111110X
203  // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
204  $char = pack('C*', $ord_var_c, ord($val[$c + 1]), ord($val[$c + 2]), ord($val[$c + 3]), ord($val[$c + 4]), ord($val[$c + 5]));
205  $c += 5;
206  $utf16 = utf82utf16($char);
207  $ascii .= sprintf('\u%04s', bin2hex($utf16));
208  break;
209  }
210  }
211 
212  return '"'.$ascii.'"';
213  } elseif (is_int($val)) {
214  return sprintf('%d', $val);
215  } elseif (is_float($val)) {
216  return sprintf('%F', $val);
217  } elseif (is_bool($val)) {
218  return ($val ? 'true' : 'false');
219  } else {
220  return 'null';
221  }
222 }
223 
224 if (!function_exists('json_decode')) {
233  function json_decode($json, $assoc = false)
234  {
235  return dol_json_decode($json, $assoc);
236  }
237 }
238 
248 function dol_json_decode($json, $assoc = false)
249 {
250  dol_syslog("For better performance, enable the native json in your PHP", LOG_WARNING);
251 
252  $comment = false;
253 
254  $out = '';
255  $strLength = strlen($json); // Must stay strlen and not dol_strlen because we want technical length, not visible length
256  for ($i = 0; $i < $strLength; $i++) {
257  if (!$comment) {
258  if (($json[$i] == '{') || ($json[$i] == '[')) {
259  $out .= 'array(';
260  } elseif (($json[$i] == '}') || ($json[$i] == ']')) {
261  $out .= ')';
262  } elseif ($json[$i] == ':') {
263  $out .= ' => ';
264  } else {
265  $out .= $json[$i];
266  }
267  } else {
268  $out .= $json[$i];
269  }
270  // @phan-suppress-next-line PhanCompatibleNegativeStringOffset
271  if ($i >= 1 && $json[$i] == '"' && $json[$i - 1] != "\\") {
272  $comment = !$comment;
273  }
274  }
275 
276  $out = _unval($out);
277 
278  $array = array();
279 
280  // Return an array
281  if ($out != '') {
282  try {
283  // @phan-suppress-next-line PhanPluginUnsafeEval
284  eval('$array = '.$out.';');
285  } catch (Exception $e) {
286  $array = array(); // @phan-suppress-current-line PhanPluginRedundantAssignment
287  }
288  }
289 
290  // Return an object
291  if (!$assoc) {
292  if (!empty($array)) {
293  $object = false;
294  if (count($array) > 0) {
295  $object = (object) array();
296  }
297  foreach ($array as $key => $value) {
298  if ($key) {
299  $object->{$key} = $value;
300  }
301  }
302 
303  return $object;
304  }
305 
306  return false;
307  }
308 
309  return $array;
310 }
311 
318 function _unval($val)
319 {
320  $reg = array();
321  while (preg_match('/\\\u([0-9A-F]{2})([0-9A-F]{2})/i', $val, $reg)) {
322  // single, escaped unicode character
323  $utf16 = chr(hexdec($reg[1])).chr(hexdec($reg[2]));
324  $utf8 = utf162utf8($utf16);
325  $val = preg_replace('/\\\u'.$reg[1].$reg[2].'/i', $utf8, $val);
326  }
327  return $val;
328 }
329 
340 function utf162utf8($utf16)
341 {
342  // oh please oh please oh please oh please oh please
343  if (function_exists('mb_convert_encoding')) {
344  return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
345  }
346 
347  $bytes = (ord($utf16[0]) << 8) | ord($utf16[1]);
348 
349  switch (true) {
350  case ((0x7F & $bytes) == $bytes):
351  // this case should never be reached, because we are in ASCII range
352  // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
353  return chr($bytes);
354 
355  case (0x07FF & $bytes) == $bytes:
356  // return a 2-byte UTF-8 character
357  // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
358  return chr(0xC0 | (($bytes >> 6) & 0x1F))
359  . chr(0x80 | ($bytes & 0x3F));
360 
361  case (0xFFFF & $bytes) == $bytes:
362  // return a 3-byte UTF-8 character
363  // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
364  return chr(0xE0 | (($bytes >> 12) & 0x0F))
365  . chr(0x80 | (($bytes >> 6) & 0x3F))
366  . chr(0x80 | ($bytes & 0x3F));
367  }
368 
369  // ignoring UTF-32 for now, sorry
370  return '';
371 }
372 
383 function utf82utf16($utf8)
384 {
385  // oh please oh please oh please oh please oh please
386  if (function_exists('mb_convert_encoding')) {
387  return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
388  }
389 
390  switch (strlen($utf8)) {
391  case 1:
392  // this case should never be reached, because we are in ASCII range
393  // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
394  return $utf8;
395 
396  case 2:
397  // return a UTF-16 character from a 2-byte UTF-8 char
398  // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
399  return chr(0x07 & (ord($utf8[0]) >> 2)).chr((0xC0 & (ord($utf8[0]) << 6)) | (0x3F & ord($utf8[1])));
400 
401  case 3:
402  // return a UTF-16 character from a 3-byte UTF-8 char
403  // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
404  return chr((0xF0 & (ord($utf8[0]) << 4)) | (0x0F & (ord($utf8[1]) >> 2))).chr((0xC0 & (ord($utf8[1]) << 6)) | (0x7F & ord($utf8[2])));
405  }
406 
407  // ignoring UTF-32 for now, sorry
408  return '';
409 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
_val($val)
Return text according to type.
Definition: json.lib.php:122
if(!function_exists('json_decode')) dol_json_decode($json, $assoc=false)
Implement json_decode for PHP that does not support it Use json_encode and json_decode in your code !
Definition: json.lib.php:248
utf162utf8($utf16)
Convert a string from one UTF-16 char to one UTF-8 char.
Definition: json.lib.php:340
_unval($val)
Return text according to type.
Definition: json.lib.php:318
if(!function_exists('json_encode')) dol_json_encode($elements)
Implement json_encode for PHP that does not support it.
Definition: json.lib.php:52
utf82utf16($utf8)
Convert a string from one UTF-8 char to one UTF-16 char.
Definition: json.lib.php:383