Skip to content

Commit 20977ad

Browse files
committed
Enhance zend_dump_op_array to Properly Represent Non-Printable Characters (GH-15680)
This change enhances `zend_dump_op_array` to properly represent non-printable characters in strings. This is useful for debugging purposes, as it allows developers to see the actual content of strings that contain non-printable characters.
1 parent 8a20154 commit 20977ad

File tree

7 files changed

+107
-13
lines changed

7 files changed

+107
-13
lines changed

Zend/Optimizer/zend_dump.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,8 @@ void zend_dump_const(const zval *zv)
6767
fprintf(stderr, " float(%g)", Z_DVAL_P(zv));
6868
break;
6969
case IS_STRING:;
70-
zend_string *escaped_string = php_addcslashes(Z_STR_P(zv), "\"\\", 2);
71-
70+
zend_string *escaped_string = php_repr_str(Z_STR_P(zv)->val, Z_STR_P(zv)->len);
7271
fprintf(stderr, " string(\"%s\")", ZSTR_VAL(escaped_string));
73-
7472
zend_string_release(escaped_string);
7573
break;
7674
case IS_ARRAY:

ext/opcache/tests/match/002.phpt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,14 @@ test:
4444
; (lines=2, args=0, vars=0, tmps=0)
4545
; (after optimizer)
4646
; %s
47-
0000 ECHO string("No match
48-
")
47+
0000 ECHO string("No match\n")
4948
0001 RETURN null
5049

5150
test2:
5251
; (lines=2, args=0, vars=0, tmps=0)
5352
; (after optimizer)
5453
; %s
55-
0000 ECHO string("No match
56-
")
54+
0000 ECHO string("No match\n")
5755
0001 RETURN null
5856
No match
5957
No match

ext/opcache/tests/match/005.phpt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
Match expression mixed jump table
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.opt_debug_level=0x20000
7+
zend_test.observer.enabled=0
8+
--EXTENSIONS--
9+
opcache
10+
--FILE--
11+
<?php
12+
return <<<EOT
13+
\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f
14+
\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f
15+
\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f
16+
\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f
17+
\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f
18+
\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f
19+
\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f
20+
\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f
21+
\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f
22+
\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f
23+
\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf
24+
\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf
25+
\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf
26+
\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf
27+
\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef
28+
\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff
29+
EOT;
30+
?>
31+
--EXPECTF--
32+
$_main:
33+
; (lines=1, args=0, vars=0, tmps=0)
34+
; (after optimizer)
35+
; %s.php:1-21
36+
0000 RETURN string("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\n\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\n !\"#$%&'()*+,-./\n0123456789:;<=>?\n@ABCDEFGHIJKLMNO\nPQRSTUVWXYZ[\\]^_\n`abcdefghijklmno\npqrstuvwxyz{|}~\x7f\n\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\n\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\n\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\n\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\n\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\n\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\n\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\n\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")

ext/opcache/tests/opt/dce_009.phpt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,8 @@ Loop::test:
5050
; (lines=3, args=0, vars=0, tmps=0)
5151
; (after optimizer)
5252
; %sdce_009.php:4-10
53-
0000 ECHO string("Start
54-
")
55-
0001 ECHO string("Done
56-
")
53+
0000 ECHO string("Start\n")
54+
0001 ECHO string("Done\n")
5755
0002 RETURN null
5856

5957
Loop::test2:

ext/opcache/tests/opt/sccp_032.phpt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ $_main:
3636
0004 INIT_FCALL 1 %d string("var_export")
3737
0005 SEND_VAR CV0($x) 1
3838
0006 DO_ICALL
39-
0007 ECHO string("
40-
")
39+
0007 ECHO string("\n")
4140
0008 JMP 0003
4241
0009 FE_FREE V1
4342
0010 RETURN int(1)

ext/standard/php_string.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ PHPAPI char *php_strtr(char *str, size_t len, const char *str_from, const char *
4242
PHPAPI zend_string *php_addslashes(zend_string *str);
4343
PHPAPI void php_stripslashes(zend_string *str);
4444
PHPAPI zend_string *php_addcslashes_str(const char *str, size_t len, const char *what, size_t what_len);
45+
PHPAPI zend_string *php_repr_str(const char *str, size_t len);
4546
PHPAPI zend_string *php_addcslashes(zend_string *str, const char *what, size_t what_len);
4647
PHPAPI void php_stripcslashes(zend_string *str);
4748
PHPAPI zend_string *php_basename(const char *s, size_t len, const char *suffix, size_t sufflen);

ext/standard/string.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
#include "scanf.h"
3333
#include "zend_API.h"
3434
#include "zend_execute.h"
35+
#include "zend_string.h"
36+
#include "php_globals.h"
3537
#include "basic_functions.h"
3638
#include "zend_smart_str.h"
3739
#include <Zend/zend_exceptions.h>
@@ -50,6 +52,19 @@
5052
#include "Zend/zend_bitset.h"
5153
#endif
5254

55+
typedef struct {
56+
char c;
57+
const char *repr;
58+
} char_repr_t;
59+
60+
static const char_repr_t char_reprs[] = {
61+
{'\t', "\\t"},
62+
{'\n', "\\n"},
63+
{'\r', "\\r"},
64+
{'"', "\\\""},
65+
{'\\', "\\\\"},
66+
};
67+
5368
/* this is read-only, so it's ok */
5469
ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef";
5570

@@ -3863,6 +3878,55 @@ PHPAPI zend_string *php_addcslashes_str(const char *str, size_t len, const char
38633878
}
38643879
/* }}} */
38653880

3881+
static const char *get_char_repr(char c) {
3882+
for (size_t i = 0; i < sizeof(char_reprs) / sizeof(char_reprs[0]); i++) {
3883+
if (char_reprs[i].c == c) {
3884+
return char_reprs[i].repr;
3885+
}
3886+
}
3887+
return NULL;
3888+
}
3889+
3890+
/* {{{ php_repr_str */
3891+
PHPAPI zend_string *php_repr_str(const char *str, size_t len) {
3892+
size_t new_len = 0;
3893+
char *repr_str;
3894+
3895+
for (size_t i = 0; i < len; i++) {
3896+
const char *repr = get_char_repr(str[i]);
3897+
if (repr) {
3898+
new_len += strlen(repr);
3899+
} else if (isprint((unsigned char)str[i])) {
3900+
new_len += 1;
3901+
} else {
3902+
new_len += 4;
3903+
}
3904+
}
3905+
3906+
repr_str = emalloc(new_len + 1);
3907+
3908+
size_t pos = 0;
3909+
for (size_t i = 0; i < len; i++) {
3910+
const char *repr = get_char_repr(str[i]);
3911+
if (repr) {
3912+
size_t len_repr = strlen(repr);
3913+
memcpy(repr_str + pos, repr, len_repr);
3914+
pos += len_repr;
3915+
} else if (isprint((unsigned char)str[i])) {
3916+
repr_str[pos++] = str[i];
3917+
} else {
3918+
pos += snprintf(repr_str + pos, 5, "\\x%02x", (unsigned char)str[i]);
3919+
}
3920+
}
3921+
3922+
repr_str[pos] = '\0';
3923+
zend_string *zend_str = zend_string_init(repr_str, pos, 0);
3924+
efree(repr_str);
3925+
3926+
return zend_str;
3927+
}
3928+
3929+
38663930
/* {{{ php_addcslashes */
38673931
PHPAPI zend_string *php_addcslashes(zend_string *str, const char *what, size_t wlength)
38683932
{

0 commit comments

Comments
 (0)