From 768711ebd1fdbc5530b01bfc1f7aaba785176d0a Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Mon, 18 Jan 2021 13:55:07 -0500 Subject: [PATCH] Proposal: var_representation($value, int $flags=0): string as alternative for var_export RFC: https://wiki.php.net/rfc/readable_var_representation Prior RFCs have proposed adding to the option list of var_export and been rejected or had development stop: - A lot of code (php-src tests, PECL tests, and applications written in PHP) is already using var_export, and may be relying on the old behaviors for unit tests (asserting exact representation), etc. ~~(Changing var_export would result in thousands of failures in php-src alone)~~ (haven't counted yet) - Code compatibility with PHP 5 has also been brought up as a reason to keep var_export output the same. - See GH-2699 and linked PRs. - Extending `var_export` by adding a third argument is impossible to polyfill in older php versions, and would result in a ArgumentCountError in php 8.0 - See https://externals.io/message/109415 This has the following differences from var_export: 1. var_representation() unconditionally returns a string 2. Use `null` instead of `NULL` - the former is recommended by more coding guidelines (https://www.php-fig.org/psr/psr-2/). 3. Change the way indentation is done for arrays/objects. See ext/standard/tests/general_functions/short_var_export1.phpt (e.g. always add 2 spaces, never 3 in objects, and put the array start on the same line as the key) 4. Render lists as `"['item1']"` rather than `"array(\n 0 => 'item1',\n)"` Always render empty lists on a single line, render multiline by default when there are 1 or more elements 5. Prepend `\` to class names so that generated code snippets can be used in namespaces without any issues. 6. Support `VAR_REPRESENTATION_SINGLE_LINE` in $flags. This will use a single-line representation for arrays/objects, though strings with embedded newlines will still cause newlines in the output. 7. Escape control characters("\x00"-"\x1f" and "\x7f"(backspace)) inside of double quoted strings instead of single quoted strings with unescaped control characters mixed with ` . "\0" . `. Having this available in php-src is useful for the following reasons: 1. It's convenient to be able to dump a snippet and use that in your source code with less reformatting, updating namespaces, etc. (or to dump values to see in utilities) 2. This often saves a few bytes (except for some binary strings) if the output of var_export() for an object/array is saved to disk, used as an array key, etc. 3. This is possibly more readable for list-like arrays 4. This can start being used in short, self-contained scripts if available. 5. The checks for infinite recursion are possible to polyfill with ReflectionReference or other approaches, but error prone to implement from scratch. Example short_var_export output (see ext/standard/tests/general_functions/var_representation1.phpt for more examples): ```php \ArrayObject::__set_state([ 'a', [ "x\r\ny", ], [], ]) oneline: \ArrayObject::__set_state(['a', ["x\r\ny"], []]) ``` Double quoted strings are used if the string contains \x00-\x1f or \x7f (control characters including `\r`, `\n`, `\t`, and backspace) The ascii range 0x00-0x7f is encoded as follows if double quoted strings are used (`\r`, `\n`, `\t`, `\$`, `\\`, `\"`, in addition to other control characters and backspace encoded as hex escaped characters) ```php // 0x00-0x1f "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" // 0x20-0x7f " !\"#\$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f" ``` --- ext/standard/basic_functions.stub.php | 2 + ext/standard/basic_functions_arginfo.h | 9 +- ext/standard/php_var.h | 1 + .../var_representation1.phpt | 150 +++++++++ ext/standard/var.c | 308 ++++++++++++++++++ 5 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/general_functions/var_representation1.phpt diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 99d0d3c63db7a..f1903893e9712 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -1475,6 +1475,8 @@ function var_export(mixed $value, bool $return = false): ?string {} function debug_zval_dump(mixed $value, mixed ...$values): void {} +function var_representation(mixed $value, int $flags = 0): string {} + function serialize(mixed $value): string {} function unserialize(string $data, array $options = []): mixed {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 5eafa59b50bd7..69235645b9018 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7540039937587f05584660bc1a1a8a80aa5ccbd1 */ + * Stub hash: 533808ec59e5d4713134fe8c76a5a4df2ee872cb */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -2157,6 +2157,11 @@ ZEND_END_ARG_INFO() #define arginfo_debug_zval_dump arginfo_var_dump +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_var_representation, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + #define arginfo_serialize arginfo_gettype ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_unserialize, 0, 1, IS_MIXED, 0) @@ -2811,6 +2816,7 @@ ZEND_FUNCTION(convert_uudecode); ZEND_FUNCTION(var_dump); ZEND_FUNCTION(var_export); ZEND_FUNCTION(debug_zval_dump); +ZEND_FUNCTION(var_representation); ZEND_FUNCTION(serialize); ZEND_FUNCTION(unserialize); ZEND_FUNCTION(memory_get_usage); @@ -3463,6 +3469,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(var_dump, arginfo_var_dump) ZEND_FE(var_export, arginfo_var_export) ZEND_FE(debug_zval_dump, arginfo_debug_zval_dump) + ZEND_FE(var_representation, arginfo_var_representation) ZEND_FE(serialize, arginfo_serialize) ZEND_FE(unserialize, arginfo_unserialize) ZEND_FE(memory_get_usage, arginfo_memory_get_usage) diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h index 2d9f5464f53b9..726860d5e6288 100644 --- a/ext/standard/php_var.h +++ b/ext/standard/php_var.h @@ -25,6 +25,7 @@ PHP_MINIT_FUNCTION(var); PHPAPI void php_var_dump(zval *struc, int level); PHPAPI void php_var_export(zval *struc, int level); PHPAPI void php_var_export_ex(zval *struc, int level, smart_str *buf); +PHPAPI void php_var_representation_ex(zval *struc, int level, smart_str *buf); PHPAPI void php_debug_zval_dump(zval *struc, int level); diff --git a/ext/standard/tests/general_functions/var_representation1.phpt b/ext/standard/tests/general_functions/var_representation1.phpt new file mode 100644 index 0000000000000..ac60333fe52a1 --- /dev/null +++ b/ext/standard/tests/general_functions/var_representation1.phpt @@ -0,0 +1,150 @@ +--TEST-- +Test var_representation() function +--FILE-- + "\$value\tsecond"]); +dump([]); +dump([1,2,3]); +dump(new ArrayObject(['a', ['b']])); +dump("Content-Length: 42\r\n"); +// use octal +dump(["Foo\0\r\n\r\001\x19test quotes and special characters\$b'\"\\" => "\0"]); +// does not escape or check validity of bytes "\80-\ff" (e.g. utf-8 data) +dump("▜"); +dump((object)["Foo\0\r\n\r\001" => true]); +echo "STDIN is dumped as null, like var_export\n"; +dump(STDIN); +echo "The ascii range is encoded as follows:\n"; +echo var_representation(implode('', array_map('chr', range(0, 0x7f)))), "\n"; +echo "Recursive objects cause a warning, like var_export\n"; +$x = new stdClass(); +$x->x = $x; +dump($x); +echo "Recursive arrays cause a warning, like var_export\n"; + +$a = []; +$a[0] = &$a; +$a[1] = 'other'; +dump($a); +$ref = 'ref shown as value like var_export'; +dump([(object)['key' => (object)['inner' => [1.0]], 'other' => &$ref]]); +class Example { + public $untyped1; + public $untyped2 = null; + public $untyped3 = 3; + public int $typed1; + public array $typed2 = []; + // rendering does not depend on existence of public function __set_state(), like var_export. +} +$x = new Example(); +unset($x->untyped1); // unset properties/uninitialized typed properties are not shown, like var_export +$x->typed2 = [new Example()]; +dump($x); +?> +--EXPECTF-- +null +oneline: null +false +oneline: false +true +oneline: true +0 +oneline: 0 +0.0 +oneline: 0.0 +(object) [] +oneline: (object) [] +(object) [ + 'key' => "\$value\tsecond", +] +oneline: (object) ['key' => "\$value\tsecond"] +[] +oneline: [] +[ + 1, + 2, + 3, +] +oneline: [1, 2, 3] +\ArrayObject::__set_state([ + 'a', + [ + 'b', + ], +]) +oneline: \ArrayObject::__set_state(['a', ['b']]) +"Content-Length: 42\r\n" +oneline: "Content-Length: 42\r\n" +[ + "Foo\x00\r\n\r\x01\x19test quotes and special characters\$b'\"\\" => "\x00", +] +oneline: ["Foo\x00\r\n\r\x01\x19test quotes and special characters\$b'\"\\" => "\x00"] +'▜' +oneline: '▜' +(object) [ + "Foo\x00\r\n\r\x01" => true, +] +oneline: (object) ["Foo\x00\r\n\r\x01" => true] +STDIN is dumped as null, like var_export + +Warning: var_representation does not handle resources in %s on line 3 +null + +Warning: var_representation does not handle resources in %s on line 4 +oneline: null +The ascii range is encoded as follows: +"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#\$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f" +Recursive objects cause a warning, like var_export + +Warning: var_representation does not handle circular references in %s on line 3 +(object) [ + 'x' => null, +] + +Warning: var_representation does not handle circular references in %s on line 4 +oneline: (object) ['x' => null] +Recursive arrays cause a warning, like var_export + +Warning: var_representation does not handle circular references in %s on line 3 +[ + null, + 'other', +] + +Warning: var_representation does not handle circular references in %s on line 4 +oneline: [null, 'other'] +[ + (object) [ + 'key' => (object) [ + 'inner' => [ + 1.0, + ], + ], + 'other' => 'ref shown as value like var_export', + ], +] +oneline: [(object) ['key' => (object) ['inner' => [1.0]], 'other' => 'ref shown as value like var_export']] +\Example::__set_state([ + 'untyped2' => null, + 'untyped3' => 3, + 'typed2' => [ + \Example::__set_state([ + 'untyped1' => null, + 'untyped2' => null, + 'untyped3' => 3, + 'typed2' => [], + ]), + ], +]) +oneline: \Example::__set_state(['untyped2' => null, 'untyped3' => 3, 'typed2' => [\Example::__set_state(['untyped1' => null, 'untyped2' => null, 'untyped3' => 3, 'typed2' => []])]]) \ No newline at end of file diff --git a/ext/standard/var.c b/ext/standard/var.c index 5c13846a82ae2..03bd59b29de05 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -35,6 +35,8 @@ struct php_serialize_data { #define COMMON (is_ref ? "&" : "") +static const int VAR_REPRESENTATION_SINGLE_LINE = 1; + static void php_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */ { if (key == NULL) { /* numeric key */ @@ -468,6 +470,142 @@ static void php_object_element_export(zval *zv, zend_ulong index, zend_string *k } /* }}} */ +static zend_string* php_var_representation_string_double_quotes(const char *str, size_t len) { /* {{{ */ + /* NOTE: This needs to be thread safe, which is why I gave up and used a constant */ + /* '\1'(0x01) means to use the hexadecimal representation, others mean to use a backslash followed by that character */ + static const char lookup[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1,'t','n', 1, 1,'r', 1, 1, /* 0x00-0x0f '\r', '\n', '\t' */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x10-0x1f */ + 0, 0,'"', 0,'$', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x2f '"', '$' */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30-0x3f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40-0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, /* 0x50-0x5f - '\\' */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0x70-0x7f backspace (\x7f) */ + /* don't escape 0x80-0xff for now in case it's valid in whatever encoding the application is using, + the missing values are filled with zeroes by the compiler */ + }; + /* Copied from php_bin2hex */ + ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef"; + + /* based on php_addcslashes_str - allocate a buffer to encode the worst case of 4 bytes per byte */ + zend_string *new_str = zend_string_safe_alloc(4, len, 0, 0); + const char *source, *end; + char *target; + for (source = str, end = source + len, target = ZSTR_VAL(new_str); source < end; source++) { + const char c = *source; + const char replacement = lookup[(unsigned char) c]; + if (replacement) { + *target++ = '\\'; + if (replacement == '\1') { + /* encode \x00-\x1f and \x7f excluding \r, \n, \t */ + target[0] = 'x'; + target[1] = hexconvtab[((unsigned char) c) >> 4]; + target[2] = hexconvtab[((unsigned char) c) & 15]; + target += 3; + } else { + /* encode \r, \n, \t, \$, \", \\ */ + *target++ = replacement; + } + continue; + } + *target++ = c; + } + *target = 0; + size_t newlen = target - ZSTR_VAL(new_str); + if (newlen < len * 4) { + new_str = zend_string_truncate(new_str, newlen, 0); + } + return new_str; +} +/* }}} */ + + +static void php_var_representation_string(smart_str *buf, const char *str, size_t len) /* {{{ */ +{ + for (size_t i = 0; i < len; i++) { + const unsigned char c = (unsigned char)str[i]; + /* escape using double quotes if the string contains control characters such as newline/tab and backspaces(\x7f) */ + if (c < 0x20 || c == 0x7f) { + /* This needs to escape the previously scanned characters because this didn't check for $ and \\ */ + smart_str_appendc(buf, '"'); + zend_string *ztmp = php_var_representation_string_double_quotes(str, len); + smart_str_append(buf, ztmp); + smart_str_appendc(buf, '"'); + zend_string_free(ztmp); + return; + } + } + /* guaranteed not to have '\0' characters at this point. */ + zend_string *ztmp = php_addcslashes_str(str, len, "'\\", 2); + + smart_str_appendc(buf, '\''); + smart_str_append(buf, ztmp); + smart_str_appendc(buf, '\''); + + zend_string_free(ztmp); +} +/* }}} */ + +static void php_object_element_var_representation(zval *zv, zend_ulong index, zend_string *key, int level, smart_str *buf, bool is_list) /* {{{ */ +{ + bool multiline = level >= 0; + if (multiline) { + buffer_append_spaces(buf, level + 1); + } + if (is_list) { + ZEND_ASSERT(!key); + } else { + if (key != NULL) { + const char *class_name, *prop_name; + size_t prop_name_len; + + zend_unmangle_property_name_ex(key, &class_name, &prop_name, &prop_name_len); + php_var_representation_string(buf, prop_name, prop_name_len); + } else { + smart_str_append_long(buf, (zend_long) index); + } + smart_str_appendl(buf, " => ", 4); + } + + php_var_representation_ex(zv, multiline ? level + 2 : -1, buf); + if (multiline) { + smart_str_appendc(buf, ','); + smart_str_appendc(buf, '\n'); + } +} +/* }}} */ + +static void php_array_element_var_representation(zval *zv, zend_ulong index, zend_string *key, int level, smart_str *buf, bool is_list) /* {{{ */ +{ + bool multiline = level >= 0; + if (key == NULL) { /* numeric key */ + if (multiline) { + buffer_append_spaces(buf, level+1); + } + if (!is_list) { + smart_str_append_long(buf, (zend_long) index); + smart_str_appendl(buf, " => ", 4); + } + } else { /* string key */ + ZEND_ASSERT(!is_list); + + if (multiline) { + buffer_append_spaces(buf, level+1); + } + + php_var_representation_string(buf, ZSTR_VAL(key), ZSTR_LEN(key)); + smart_str_appendl(buf, " => ", 4); + } + php_var_representation_ex(zv, multiline ? level + 2 : -1, buf); + + if (multiline) { + smart_str_appendc(buf, ','); + smart_str_appendc(buf, '\n'); + } +} +/* }}} */ + PHPAPI void php_var_export_ex(zval *struc, int level, smart_str *buf) /* {{{ */ { HashTable *myht; @@ -605,6 +743,155 @@ PHPAPI void php_var_export_ex(zval *struc, int level, smart_str *buf) /* {{{ */ } /* }}} */ +PHPAPI void php_var_representation_ex(zval *struc, int level, smart_str *buf) /* {{{ */ +{ + HashTable *myht; + char tmp_str[PHP_DOUBLE_MAX_LENGTH]; + zend_ulong index; + zend_string *key; + zval *val; + bool first, is_list; + +again: + switch (Z_TYPE_P(struc)) { + case IS_FALSE: + smart_str_appendl(buf, "false", 5); + break; + case IS_TRUE: + smart_str_appendl(buf, "true", 4); + break; + case IS_NULL: + smart_str_appendl(buf, "null", 4); + break; + case IS_LONG: + /* INT_MIN as a literal will be parsed as a float. Emit something like + * -9223372036854775807-1 to avoid this. */ + if (Z_LVAL_P(struc) == ZEND_LONG_MIN) { + smart_str_append_long(buf, ZEND_LONG_MIN+1); + smart_str_appends(buf, "-1"); + break; + } + smart_str_append_long(buf, Z_LVAL_P(struc)); + break; + case IS_DOUBLE: + php_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str); + smart_str_appends(buf, tmp_str); + /* Without a decimal point, PHP treats a number literal as an int. + * This check even works for scientific notation, because the + * mantissa always contains a decimal point. + * We need to check for finiteness, because INF, -INF and NAN + * must not have a decimal point added. + */ + if (zend_finite(Z_DVAL_P(struc)) && NULL == strchr(tmp_str, '.')) { + smart_str_appendl(buf, ".0", 2); + } + break; + case IS_STRING: + php_var_representation_string(buf, Z_STRVAL_P(struc), Z_STRLEN_P(struc)); + break; + case IS_ARRAY: { + myht = Z_ARRVAL_P(struc); + if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) { + if (GC_IS_RECURSIVE(myht)) { + smart_str_appendl(buf, "null", 4); + zend_error(E_WARNING, "var_representation does not handle circular references"); + return; + } + GC_ADDREF(myht); + GC_PROTECT_RECURSION(myht); + } + smart_str_appendc(buf, '['); + first = true; + is_list = zend_array_is_list(myht); + ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, val) { + if (level < 0) { + if (first) { + first = false; + } else { + smart_str_appendl(buf, ", ", 2); + } + } else if (first) { + smart_str_appendc(buf, '\n'); + first = false; + } + php_array_element_var_representation(val, index, key, level, buf, is_list); + } ZEND_HASH_FOREACH_END(); + if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) { + GC_UNPROTECT_RECURSION(myht); + GC_DELREF(myht); + } + if (level > 1 && !first) { + buffer_append_spaces(buf, level - 1); + } + smart_str_appendc(buf, ']'); + + break; + } + case IS_OBJECT: + myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_VAR_EXPORT); + if (myht) { + if (GC_IS_RECURSIVE(myht)) { + smart_str_appendl(buf, "null", 4); + zend_error(E_WARNING, "var_representation does not handle circular references"); + zend_release_properties(myht); + return; + } else { + GC_TRY_PROTECT_RECURSION(myht); + } + } + + /* stdClass has no __set_state method, but can be casted to */ + if (Z_OBJCE_P(struc) == zend_standard_class_def) { + smart_str_appendl(buf, "(object) [", 10); + } else { + smart_str_appendc(buf, '\\'); + smart_str_append(buf, Z_OBJCE_P(struc)->name); + smart_str_appendl(buf, "::__set_state([", 15); + } + + first = true; + if (myht) { + is_list = zend_array_is_list(myht); + ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) { + if (level < 0) { + if (first) { + first = false; + } else { + smart_str_appendl(buf, ", ", 2); + } + } else if (first) { + smart_str_appendc(buf, '\n'); + first = false; + } + php_object_element_var_representation(val, index, key, level, buf, is_list); + } ZEND_HASH_FOREACH_END(); + GC_TRY_UNPROTECT_RECURSION(myht); + zend_release_properties(myht); + } + if (level > 1 && !first) { + buffer_append_spaces(buf, level - 1); + } + if (Z_OBJCE_P(struc) == zend_standard_class_def) { + smart_str_appendc(buf, ']'); + } else { + smart_str_appendl(buf, "])", 2); + } + + break; + case IS_REFERENCE: + struc = Z_REFVAL_P(struc); + goto again; + break; + case IS_RESOURCE: + zend_error(E_WARNING, "var_representation does not handle resources"); + /* fall through */ + default: + smart_str_appendl(buf, "null", 4); + break; + } +} +/* }}} */ + /* FOR BC reasons, this will always perform and then print */ PHPAPI void php_var_export(zval *struc, int level) /* {{{ */ { @@ -641,6 +928,26 @@ PHP_FUNCTION(var_export) } /* }}} */ +/* {{{ Returns a short, readable string representation of a variable */ +PHP_FUNCTION(var_representation) +{ + zval *var; + zend_long flags = 0; + smart_str buf = {0}; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(var) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + php_var_representation_ex(var, (flags & 1) ? -1 : 1, &buf); + smart_str_0 (&buf); + + RETURN_NEW_STR(buf.s); +} +/* }}} */ + static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash); static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var) /* {{{ */ @@ -1340,5 +1647,6 @@ PHP_INI_END() PHP_MINIT_FUNCTION(var) { REGISTER_INI_ENTRIES(); + REGISTER_LONG_CONSTANT("VAR_REPRESENTATION_SINGLE_LINE", VAR_REPRESENTATION_SINGLE_LINE, CONST_CS|CONST_PERSISTENT); return SUCCESS; }