Skip to content

Commit 86ffde3

Browse files
committed
Improve ini number handling with INI_SCANNER_TYPED
Fixes phpGH-11010 Closes phpGH-11014
1 parent 471dcf6 commit 86ffde3

File tree

7 files changed

+65
-34
lines changed

7 files changed

+65
-34
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ PHP NEWS
170170
. Fix GH-10239 (proc_close after proc_get_status always returns -1). (nielsdos)
171171
. Improve the warning message for unpack() in case not enough values were
172172
provided. (nielsdos)
173+
. Fix GH-11010 (parse_ini_string() now preserves formatting of unquoted
174+
strings starting with numbers when the INI_SCANNER_TYPED flag is
175+
specified). (ilutov)
173176

174177
- Streams:
175178
. Fixed bug #51056: blocking fread() will block even if data is available.

Zend/zend_ini_parser.y

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ int ini_parse(void);
4343
#endif
4444

4545
#define ZEND_SYSTEM_INI CG(ini_parser_unbuffered_errors)
46+
#define INI_ZVAL_IS_NUMBER 1
4647

4748
static int get_int_val(zval *op) {
4849
switch (Z_TYPE_P(op)) {
@@ -92,8 +93,12 @@ static void zend_ini_do_op(char type, zval *result, zval *op1, zval *op2)
9293
break;
9394
}
9495

95-
str_len = sprintf(str_result, "%d", i_result);
96-
ZVAL_NEW_STR(result, zend_string_init(str_result, str_len, ZEND_SYSTEM_INI));
96+
if (INI_SCNG(scanner_mode) != ZEND_INI_SCANNER_TYPED) {
97+
str_len = sprintf(str_result, "%d", i_result);
98+
ZVAL_NEW_STR(result, zend_string_init(str_result, str_len, ZEND_SYSTEM_INI));
99+
} else {
100+
ZVAL_LONG(result, i_result);
101+
}
97102
}
98103
/* }}} */
99104

@@ -276,6 +281,41 @@ static void zval_ini_dtor(zval *zv)
276281
}
277282
/* }}} */
278283

284+
static inline zend_result convert_to_number(zval *retval, const char *str, const int str_len)
285+
{
286+
uint8_t type;
287+
int overflow;
288+
zend_long lval;
289+
double dval;
290+
291+
if ((type = is_numeric_string_ex(str, str_len, &lval, &dval, 0, &overflow, NULL)) != 0) {
292+
if (type == IS_LONG) {
293+
ZVAL_LONG(retval, lval);
294+
return SUCCESS;
295+
} else if (type == IS_DOUBLE && !overflow) {
296+
ZVAL_DOUBLE(retval, dval);
297+
return SUCCESS;
298+
}
299+
}
300+
301+
return FAILURE;
302+
}
303+
304+
static void normalize_value(zval *zv)
305+
{
306+
if (INI_SCNG(scanner_mode) != ZEND_INI_SCANNER_TYPED) {
307+
return;
308+
}
309+
310+
if (Z_EXTRA_P(zv) == INI_ZVAL_IS_NUMBER && Z_TYPE_P(zv) == IS_STRING) {
311+
zval number_rv;
312+
if (convert_to_number(&number_rv, Z_STRVAL_P(zv), Z_STRLEN_P(zv)) == SUCCESS) {
313+
zval_ptr_dtor(zv);
314+
ZVAL_COPY_VALUE(zv, &number_rv);
315+
}
316+
}
317+
}
318+
279319
%}
280320

281321
%expect 0
@@ -351,7 +391,7 @@ section_string_or_value:
351391
;
352392
353393
string_or_value:
354-
expr { $$ = $1; }
394+
expr { $$ = $1; normalize_value(&$$); }
355395
| BOOL_TRUE { $$ = $1; }
356396
| BOOL_FALSE { $$ = $1; }
357397
| NULL_NULL { $$ = $1; }
@@ -412,7 +452,11 @@ constant_literal:
412452
constant_string:
413453
TC_CONSTANT { zend_ini_get_constant(&$$, &$1); }
414454
| TC_RAW { $$ = $1; /*printf("TC_RAW: '%s'\n", Z_STRVAL($1));*/ }
415-
| TC_NUMBER { $$ = $1; /*printf("TC_NUMBER: '%s'\n", Z_STRVAL($1));*/ }
455+
| TC_NUMBER {
456+
$$ = $1;
457+
Z_EXTRA($$) = INI_ZVAL_IS_NUMBER;
458+
/*printf("TC_NUMBER: '%s'\n", Z_STRVAL($1));*/
459+
}
416460
| TC_STRING { $$ = $1; /*printf("TC_STRING: '%s'\n", Z_STRVAL($1));*/ }
417461
| TC_WHITESPACE { $$ = $1; /*printf("TC_WHITESPACE: '%s'\n", Z_STRVAL($1));*/ }
418462
;

Zend/zend_ini_scanner.l

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -151,26 +151,6 @@ ZEND_API zend_ini_scanner_globals ini_scanner_globals;
151151
return type; \
152152
}
153153

154-
static inline zend_result convert_to_number(zval *retval, const char *str, const int str_len)
155-
{
156-
uint8_t type;
157-
int overflow;
158-
zend_long lval;
159-
double dval;
160-
161-
if ((type = is_numeric_string_ex(str, str_len, &lval, &dval, 0, &overflow, NULL)) != 0) {
162-
if (type == IS_LONG) {
163-
ZVAL_LONG(retval, lval);
164-
return SUCCESS;
165-
} else if (type == IS_DOUBLE && !overflow) {
166-
ZVAL_DOUBLE(retval, dval);
167-
return SUCCESS;
168-
}
169-
}
170-
171-
return FAILURE;
172-
}
173-
174154
static void zend_ini_copy_typed_value(zval *retval, const int type, const char *str, int len)
175155
{
176156
switch (type) {
@@ -183,11 +163,6 @@ static void zend_ini_copy_typed_value(zval *retval, const int type, const char *
183163
ZVAL_NULL(retval);
184164
break;
185165

186-
case TC_NUMBER:
187-
if (convert_to_number(retval, str, len) == SUCCESS) {
188-
break;
189-
}
190-
ZEND_FALLTHROUGH;
191166
default:
192167
zend_ini_copy_value(retval, str, len);
193168
}

ext/standard/tests/general_functions/bug70157.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ array(%d) {
2121
["foo"]=>
2222
array(%d) {
2323
[123]=>
24-
string(%d) "24575"
24+
int(24575)
2525
[456]=>
2626
int(123)
2727
}

ext/standard/tests/general_functions/bug77844.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ var_dump(parse_ini_string($ini, true, INI_SCANNER_TYPED));
1313
--EXPECT--
1414
array(2) {
1515
["val1"]=>
16-
string(1) "2"
16+
int(2)
1717
["val2"]=>
18-
string(1) "2"
18+
int(2)
1919
}

ext/standard/tests/general_functions/parse_ini_string_bug76068.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ array(1) {
2121
["foo"]=>
2222
array(1) {
2323
["bar"]=>
24-
string(1) "1"
24+
int(1)
2525
}
2626
}
2727
array(1) {
@@ -42,6 +42,6 @@ array(1) {
4242
["foo"]=>
4343
array(1) {
4444
["bar"]=>
45-
string(2) "42"
45+
int(42)
4646
}
4747
}

ext/standard/tests/gh11010.phpt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
GH-11010: Preserve ini formatting of concatenated numbers
3+
--FILE--
4+
<?php
5+
$result = parse_ini_string('variable = -00 20 30', false, INI_SCANNER_TYPED);
6+
var_dump($result['variable']);
7+
?>
8+
--EXPECT--
9+
string(9) "-00 20 30"

0 commit comments

Comments
 (0)