Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fallback value syntax for ini variables #11351

Merged
merged 3 commits into from Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Zend/tests/bug70748.phpt
Expand Up @@ -15,6 +15,6 @@ var_dump(parse_ini_file($ini_file));
unlink(__DIR__ . "/bug70748.ini");
?>
--EXPECTF--
Warning: syntax error, unexpected end of file, expecting '}' in %sbug70748.ini on line %d
Warning: syntax error, unexpected end of file, expecting TC_FALLBACK or '}' in %s on line %d
in %sbug70748.php on line %d
bool(false)
16 changes: 14 additions & 2 deletions Zend/zend_ini_parser.y
Expand Up @@ -170,7 +170,7 @@ static void zend_ini_get_constant(zval *result, zval *name)
/* }}} */

/* {{{ zend_ini_get_var() */
static void zend_ini_get_var(zval *result, zval *name)
static void zend_ini_get_var(zval *result, zval *name, zval *fallback)
{
zval *curval;
char *envvar;
Expand All @@ -182,9 +182,13 @@ static void zend_ini_get_var(zval *result, zval *name)
} else if ((envvar = zend_getenv(Z_STRVAL_P(name), Z_STRLEN_P(name))) != NULL ||
(envvar = getenv(Z_STRVAL_P(name))) != NULL) {
ZVAL_NEW_STR(result, zend_string_init(envvar, strlen(envvar), ZEND_SYSTEM_INI));
/* ..or if not defined, try fallback value */
} else if (fallback) {
ZVAL_NEW_STR(result, zend_string_init(Z_STRVAL_P(fallback), strlen(Z_STRVAL_P(fallback)), ZEND_SYSTEM_INI));
Misterio77 marked this conversation as resolved.
Show resolved Hide resolved
} else {
zend_ini_init_string(result);
}

}
/* }}} */

Expand Down Expand Up @@ -338,6 +342,7 @@ static void normalize_value(zval *zv)
%token TC_DOLLAR_CURLY
%token TC_VARNAME
%token TC_QUOTED_STRING
%token TC_FALLBACK
%token BOOL_TRUE
%token BOOL_FALSE
%token NULL_NULL
Expand Down Expand Up @@ -440,7 +445,14 @@ expr:
;

cfg_var_ref:
TC_DOLLAR_CURLY TC_VARNAME '}' { zend_ini_get_var(&$$, &$2); zend_string_free(Z_STR($2)); }
TC_DOLLAR_CURLY TC_VARNAME '}' { zend_ini_get_var(&$$, &$2, NULL); zend_string_free(Z_STR($2)); }
| TC_DOLLAR_CURLY TC_VARNAME TC_FALLBACK fallback '}' { zend_ini_get_var(&$$, &$2, &$4); zend_string_free(Z_STR($2)); zend_string_free(Z_STR($4)); }
;


fallback:
var_string_list { $$ = $1; }
| %empty { zend_ini_init_string(&$$); }
;

constant_literal:
Expand Down
71 changes: 63 additions & 8 deletions Zend/zend_ini_scanner.l
Expand Up @@ -359,7 +359,8 @@ NEWLINE ("\r"|"\n"|"\r\n")
TABS_AND_SPACES [ \t]
WHITESPACE [ \t]+
CONSTANT [a-zA-Z_][a-zA-Z0-9_]*
LABEL [^=\n\r\t;&|^$~(){}!"\[]+
LABEL_CHAR [^=\n\r\t;&|^$~(){}!"\[\]\x00]
LABEL ({LABEL_CHAR}+)
TOKENS [:,.\[\]"'()&|^+-/*=%$!~<>?@{}]
OPERATORS [&|^~()!]
DOLLAR_CURLY "${"
Expand All @@ -370,6 +371,7 @@ RAW_VALUE_CHARS [^\n\r;\000]

LITERAL_DOLLAR ("$"([^{\000]|("\\"{ANY_CHAR})))
VALUE_CHARS ([^$= \t\n\r;&|^~()!"'\000]|{LITERAL_DOLLAR})
FALLBACK_CHARS ([^$\n\r;"'}\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR})
SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR})

<!*> := yyleng = YYCURSOR - SCNG(yy_text);
Expand Down Expand Up @@ -417,12 +419,61 @@ SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR})
return ']';
}

<ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{DOLLAR_CURLY} { /* Variable start */
<ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_VALUE,ST_OFFSET,ST_VAR_FALLBACK>{DOLLAR_CURLY} { /* Variable start */
yy_push_state(ST_VARNAME);
return TC_DOLLAR_CURLY;
}

<ST_VARNAME>{LABEL} { /* Variable name */
<ST_VARNAME>":-" { /* End Variable name, fallback start */
fallback_lexing:
yy_pop_state();
yy_push_state(ST_VAR_FALLBACK);
return TC_FALLBACK;
}

<ST_VARNAME>{LABEL_CHAR} { /* Variable name */
if (YYCURSOR[0] == ':' && YYCURSOR[1] == '-') {
YYCURSOR++;
goto fallback_lexing;
}

while (YYCURSOR < YYLIMIT) {
switch (*YYCURSOR++) {
case '=':
case '\n':
case '\r':
case '\t':
case ';':
case '&':
case '|':
case '^':
case '$':
case '~':
case '(':
case ')':
case '{':
case '}':
case '!':
case '"':
case '[':
case ']':
break;
/* ':' is only allowed if it isn't followed by '-'. */
case ':':
if (YYCURSOR[0] == '-') {
break;
} else {
continue;
}
default:
continue;
}

YYCURSOR--;
yyleng = YYCURSOR - SCNG(yy_text);
break;
}

/* Eat leading whitespace */
EAT_LEADING_WHITESPACE();

Expand All @@ -432,7 +483,7 @@ SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR})
RETURN_TOKEN(TC_VARNAME, yytext, yyleng);
}

<ST_VARNAME>"}" { /* Variable end */
<ST_VARNAME,ST_VAR_FALLBACK>"}" { /* Variable/fallback end */
yy_pop_state();
return '}';
}
Expand Down Expand Up @@ -522,11 +573,11 @@ end_raw_value_chars:
return END_OF_LINE;
}

<ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{CONSTANT} { /* Get constant option value */
<ST_SECTION_VALUE,ST_VALUE,ST_VAR_FALLBACK,ST_OFFSET>{CONSTANT} { /* Get constant option value */
RETURN_TOKEN(TC_CONSTANT, yytext, yyleng);
}

<ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{NUMBER} { /* Get number option value as string */
<ST_SECTION_VALUE,ST_VALUE,ST_VAR_FALLBACK,ST_OFFSET>{NUMBER} { /* Get number option value as string */
RETURN_TOKEN(TC_NUMBER, yytext, yyleng);
}

Expand All @@ -548,11 +599,15 @@ end_raw_value_chars:
RETURN_TOKEN(TC_STRING, yytext, yyleng);
}

<ST_VAR_FALLBACK>{FALLBACK_CHARS}+ { /* Same as below, but excluding '}' */
RETURN_TOKEN(TC_STRING, yytext, yyleng);
}

<ST_SECTION_VALUE,ST_OFFSET>{SECTION_VALUE_CHARS}+ { /* Get rest as section/offset value */
RETURN_TOKEN(TC_STRING, yytext, yyleng);
}

<ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{TABS_AND_SPACES}*["] { /* Double quoted '"' string start */
<ST_SECTION_VALUE,ST_VALUE,ST_VAR_FALLBACK,ST_OFFSET>{TABS_AND_SPACES}*["] { /* Double quoted '"' string start */
yy_push_state(ST_DOUBLE_QUOTES);
return '"';
}
Expand Down Expand Up @@ -603,7 +658,7 @@ end_raw_value_chars:
return TC_QUOTED_STRING;
}

<ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{WHITESPACE} {
<ST_SECTION_VALUE,ST_VALUE,ST_OFFSET,ST_VAR_FALLBACK>{WHITESPACE} {
RETURN_TOKEN(TC_WHITESPACE, yytext, yyleng);
}

Expand Down
23 changes: 23 additions & 0 deletions ext/standard/tests/general_functions/parse_ini_basic.data
Expand Up @@ -76,11 +76,13 @@ novalue_option4[]=
["Quoted strings and variables in sections"]

[${basicval}]
[${undefinedval:-foo}]
[${basicval}/foo]
[foo/${basicval}]
[foo/${basicval}/foo]

["${basicqval}"]
["${undefinedval:-foo}"]
["${basicqval}/foo"]
["foo/${basicqval}"]
["foo/${basicqval}/foo"]
Expand Down Expand Up @@ -141,3 +143,24 @@ double_quoted_multiline = "Lorem \"ipsum\"""
dolor"
dollar_test = "\${test}"
unescaped ="\n\r\t"

[variable-fallback]
defined1 = "Hello, ${basicval:-world}!"
defined2 = ${basicval:-123}
falsy = ${falsyval:-hi}
undefined = "Hello, ${undefined_var:-world}!"
empty = ${emptyval:-foo}
spaces = ${undefined_var:-hi there}
exclamation = ${hi_is_this_undefined:-"no, this is fallback!"}
nested = ${undefined_var:-${also_undefined:-${undefined_too:-hello}}}
hostname = localhost:${undefined_var:-8080}
outer_quotes = "foo:${undefined_var:-bar}"
number_value = ${undefined_var:-12345}
true_value = ${undefined_var:-true}
false_value = ${undefined_var:-false}
null_value = ${undefined_var:-null}
empty1 = ${undefined_var:-}
empty2 = ${undefined}
constant = ${undefined:-TEST_CONSTANT}
INI:WITH:COLON = ${INI:WITH:COLON:-}
NONEXISTENT:INI:WITH:COLON = ${NONEXISTENT:INI:WITH:COLON:-fallback}
50 changes: 49 additions & 1 deletion ext/standard/tests/general_functions/parse_ini_basic.phpt
Expand Up @@ -3,6 +3,10 @@ parse_ini_file() tests
--ENV--
basicval=FUBAR_VARIABLE
basicqval=FUBAR_QUOTES_VARIABLE
falsyval=false
emptyval=
--INI--
INI:WITH:COLON=ini_with_colon
--FILE--
<?php

Expand All @@ -15,7 +19,7 @@ var_dump(parse_ini_file($ini_file, 1));
echo "Done.\n";
?>
--EXPECT--
array(27) {
array(29) {
["basic"]=>
array(15) {
["basicval"]=>
Expand Down Expand Up @@ -179,6 +183,9 @@ array(27) {
["FUBAR_VARIABLE"]=>
array(0) {
}
["foo"]=>
array(0) {
}
["FUBAR_VARIABLE/foo"]=>
array(0) {
}
Expand Down Expand Up @@ -295,5 +302,46 @@ array(27) {
["unescaped"]=>
string(6) "\n\r\t"
}
["variable-fallback"]=>
array(19) {
["defined1"]=>
string(22) "Hello, FUBAR_VARIABLE!"
["defined2"]=>
string(14) "FUBAR_VARIABLE"
["falsy"]=>
string(5) "false"
["undefined"]=>
string(13) "Hello, world!"
["empty"]=>
string(3) "foo"
["spaces"]=>
string(8) "hi there"
["exclamation"]=>
string(21) "no, this is fallback!"
["nested"]=>
string(5) "hello"
["hostname"]=>
string(14) "localhost:8080"
["outer_quotes"]=>
string(7) "foo:bar"
["number_value"]=>
string(5) "12345"
["true_value"]=>
string(1) "1"
["false_value"]=>
string(0) ""
["null_value"]=>
string(0) ""
["empty1"]=>
string(0) ""
["empty2"]=>
string(0) ""
["constant"]=>
string(21) "this_is_test_constant"
["INI:WITH:COLON"]=>
string(14) "ini_with_colon"
["NONEXISTENT:INI:WITH:COLON"]=>
string(8) "fallback"
}
}
Done.
Expand Up @@ -3,8 +3,18 @@ Ini parsing errors should not result in memory leaks
--FILE--
<?php
var_dump(parse_ini_string('a="b'));
var_dump(parse_ini_string('a=${b'));
var_dump(parse_ini_string('a=${b:-a'));
?>
--EXPECTF--
Warning: syntax error, unexpected end of file, expecting TC_DOLLAR_CURLY or TC_QUOTED_STRING or '"' in Unknown on line 1
in %s on line %d
bool(false)

Warning: syntax error, unexpected end of file, expecting TC_FALLBACK or '}' in Unknown on line 1
in %s on line %d
bool(false)

Warning: syntax error, unexpected TC_FALLBACK, expecting TC_VARNAME in Unknown on line 1
in %s on line %d
bool(false)