Skip to content

Commit 0629297

Browse files
author
Gwynne Raskind
committed
Add unescaped Unicode encoding to json_encode(). Closes bug #53946. Patch by Irker and Gwynne.
1 parent 58747df commit 0629297

File tree

4 files changed

+26
-17
lines changed

4 files changed

+26
-17
lines changed

ext/json/json.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ static PHP_MINIT_FUNCTION(json)
9595
REGISTER_LONG_CONSTANT("JSON_NUMERIC_CHECK", PHP_JSON_NUMERIC_CHECK, CONST_CS | CONST_PERSISTENT);
9696
REGISTER_LONG_CONSTANT("JSON_UNESCAPED_SLASHES", PHP_JSON_UNESCAPED_SLASHES, CONST_CS | CONST_PERSISTENT);
9797
REGISTER_LONG_CONSTANT("JSON_PRETTY_PRINT", PHP_JSON_PRETTY_PRINT, CONST_CS | CONST_PERSISTENT);
98+
REGISTER_LONG_CONSTANT("JSON_UNESCAPED_UNICODE", PHP_JSON_UNESCAPED_UNICODE, CONST_CS | CONST_PERSISTENT);
9899

99100
REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT);
100101
REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT);
@@ -346,7 +347,7 @@ static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC)
346347

347348
static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
348349
{
349-
int pos = 0;
350+
int pos = 0, ulen = 0;
350351
unsigned short us;
351352
unsigned short *utf16;
352353

@@ -378,15 +379,14 @@ static void json_escape_string(smart_str *buf, char *s, int len, int options TSR
378379
}
379380

380381
}
381-
382-
utf16 = (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
383-
384-
len = utf8_to_utf16(utf16, s, len);
385-
if (len <= 0) {
382+
383+
utf16 = (options & PHP_JSON_UNESCAPED_UNICODE) ? NULL : (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
384+
ulen = utf8_to_utf16(utf16, s, len);
385+
if (ulen <= 0) {
386386
if (utf16) {
387387
efree(utf16);
388388
}
389-
if (len < 0) {
389+
if (ulen < 0) {
390390
JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
391391
if (!PG(display_errors)) {
392392
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");
@@ -397,12 +397,15 @@ static void json_escape_string(smart_str *buf, char *s, int len, int options TSR
397397
}
398398
return;
399399
}
400+
if (!(options & PHP_JSON_UNESCAPED_UNICODE)) {
401+
len = ulen;
402+
}
400403

401404
smart_str_appendc(buf, '"');
402405

403406
while (pos < len)
404407
{
405-
us = utf16[pos++];
408+
us = (options & PHP_JSON_UNESCAPED_UNICODE) ? s[pos++] : utf16[pos++];
406409

407410
switch (us)
408411
{
@@ -479,7 +482,7 @@ static void json_escape_string(smart_str *buf, char *s, int len, int options TSR
479482
break;
480483

481484
default:
482-
if (us >= ' ' && (us & 127) == us) {
485+
if (us >= ' ' && ((options & PHP_JSON_UNESCAPED_UNICODE) || (us & 127) == us)) {
483486
smart_str_appendc(buf, (unsigned char) us);
484487
} else {
485488
smart_str_appendl(buf, "\\u", 2);
@@ -498,7 +501,9 @@ static void json_escape_string(smart_str *buf, char *s, int len, int options TSR
498501
}
499502

500503
smart_str_appendc(buf, '"');
501-
efree(utf16);
504+
if (utf16) {
505+
efree(utf16);
506+
}
502507
}
503508
/* }}} */
504509

ext/json/php_json.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ extern zend_class_entry *php_json_serializable_ce;
6262
#define PHP_JSON_NUMERIC_CHECK (1<<5)
6363
#define PHP_JSON_UNESCAPED_SLASHES (1<<6)
6464
#define PHP_JSON_PRETTY_PRINT (1<<7)
65+
#define PHP_JSON_UNESCAPED_UNICODE (1<<8)
6566

6667
/* Internal flags */
6768
#define PHP_JSON_OUTPUT_ARRAY 0

ext/json/utf8_to_utf16.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ SOFTWARE.
3030
#include "utf8_decode.h"
3131

3232
int
33-
utf8_to_utf16(unsigned short w[], char p[], int length)
33+
utf8_to_utf16(unsigned short *w, char p[], int length)
3434
{
3535
int c;
3636
int the_index = 0;
@@ -43,14 +43,17 @@ utf8_to_utf16(unsigned short w[], char p[], int length)
4343
return (c == UTF8_END) ? the_index : UTF8_ERROR;
4444
}
4545
if (c < 0x10000) {
46-
w[the_index] = (unsigned short)c;
46+
if (w) {
47+
w[the_index] = (unsigned short)c;
48+
}
4749
the_index += 1;
4850
} else {
4951
c -= 0x10000;
50-
w[the_index] = (unsigned short)(0xD800 | (c >> 10));
51-
the_index += 1;
52-
w[the_index] = (unsigned short)(0xDC00 | (c & 0x3FF));
53-
the_index += 1;
52+
if (w) {
53+
w[the_index] = (unsigned short)(0xD800 | (c >> 10));
54+
w[the_index + 1] = (unsigned short)(0xDC00 | (c & 0x3FF));
55+
}
56+
the_index += 2;
5457
}
5558
}
5659
}

ext/json/utf8_to_utf16.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
/* utf8_to_utf16.h */
22

3-
extern int utf8_to_utf16(unsigned short w[], char p[], int length);
3+
extern int utf8_to_utf16(unsigned short *w, char p[], int length);

0 commit comments

Comments
 (0)