|
Expand Up
|
@@ -114,9 +114,8 @@ static void cli_readline_init_globals(zend_cli_readline_globals *rg) |
|
|
PHP_INI_BEGIN() |
|
|
STD_PHP_INI_ENTRY("cli.pager", "", PHP_INI_ALL, OnUpdateString, pager, zend_cli_readline_globals, cli_readline_globals) |
|
|
STD_PHP_INI_ENTRY("cli.prompt", DEFAULT_PROMPT, PHP_INI_ALL, OnUpdateString, prompt, zend_cli_readline_globals, cli_readline_globals) |
|
|
PHP_INI_END() |
|
|
|
|
|
|
|
|
STD_PHP_INI_BOOLEAN("cli.enable_interactive_shell_result_function", "1", PHP_INI_SYSTEM, OnUpdateBool, enable_interactive_shell_result_function, zend_cli_readline_globals, cli_readline_globals) |
|
|
PHP_INI_END(); |
|
|
|
|
|
typedef enum { |
|
|
body, |
|
Expand All
|
@@ -131,6 +130,133 @@ typedef enum { |
|
|
outside, |
|
|
} php_code_type; |
|
|
|
|
|
/* Generate a snippet to try to convert single-statement statement lists to a statement returning an expression. |
|
|
* If this fails to parse, the passed-in statements are used instead. */ |
|
|
static zend_string *php_readline_create_return_expression_string(const char* str, size_t str_len) /* {{{ */ |
|
|
{ |
|
|
/* E.g. convert "2+2; \t" to "return (2+2);" */ |
|
|
zend_string *result; |
|
|
while (str_len > 0) { |
|
|
char c = str[str_len - 1]; |
|
|
/* Remove trailing whitespace and semicolon(s) from statements. |
|
|
* TODO: Could tokenize to remove trailing comments as well. */ |
|
|
if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == ';') { |
|
|
str_len--; |
|
|
} else { |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
result = zend_string_alloc(str_len + sizeof("return ();")-1, 0); |
|
|
memcpy(ZSTR_VAL(result), "return (", sizeof("return (") - 1); |
|
|
memcpy(ZSTR_VAL(result) + sizeof("return (") - 1, str, str_len); |
|
|
memcpy(ZSTR_VAL(result) + sizeof("return (") - 1 + str_len, ");", sizeof(");") - 1); |
|
|
ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0'; |
|
|
return result; |
|
|
} |
|
|
|
|
|
static int php_readline_eval_stringl(const char *str, size_t str_len, zval *retval_ptr, const char *string_name) /* {{{ */ |
|
|
{ |
|
|
zend_string *pv; |
|
|
zend_op_array *new_op_array; |
|
|
uint32_t original_compiler_options = CG(compiler_options); |
|
|
int retval; |
|
|
|
|
|
if (retval_ptr) { |
|
|
pv = php_readline_create_return_expression_string(str, str_len); |
|
|
} else { |
|
|
recompile_without_block_expression: |
|
|
pv = zend_string_init(str, str_len, 0); |
|
|
} |
|
|
|
|
|
CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL; |
|
|
if (retval_ptr) { |
|
|
int original_error_reporting = EG(error_reporting); |
|
|
EG(error_reporting) &= ~(E_PARSE); |
|
|
new_op_array = zend_compile_string(pv, string_name); |
|
|
EG(error_reporting) = original_error_reporting; |
|
|
if (!new_op_array) { |
|
|
zend_string_release(pv); |
|
|
if (!EG(exception) || EG(exception)->ce != zend_ce_parse_error) { |
|
|
/* Don't retry if this is any exception other than ParseError (even CompileError). |
|
|
* If we could parse but not compile `return (expr);`, we likely couldn't compile `expr;` either */ |
|
|
return FAILURE; |
|
|
} |
|
|
retval_ptr = NULL; |
|
|
/* TODO: Only retry if the error in question was a ParseError, not a compile error. */ |
|
|
/* Code such as `return 123;;` with too many semicolons doesn't parse with the mechanism this uses to get the value of the last expression/statement. */ |
|
|
zend_clear_exception(); |
|
|
goto recompile_without_block_expression; |
|
|
} |
|
|
} else { |
|
|
new_op_array = zend_compile_string(pv, string_name); |
|
|
} |
|
|
CG(compiler_options) = original_compiler_options; |
|
|
|
|
|
if (new_op_array) { |
|
|
zval local_retval; |
|
|
|
|
|
EG(no_extensions)=1; |
|
|
|
|
|
new_op_array->scope = zend_get_executed_scope(); |
|
|
|
|
|
zend_try { |
|
|
ZVAL_UNDEF(&local_retval); |
|
|
zend_execute(new_op_array, &local_retval); |
|
|
} zend_catch { |
|
|
destroy_op_array(new_op_array); |
|
|
efree_size(new_op_array, sizeof(zend_op_array)); |
|
|
zend_bailout(); |
|
|
} zend_end_try(); |
|
|
|
|
|
if (Z_TYPE(local_retval) != IS_UNDEF) { |
|
|
if (retval_ptr) { |
|
|
ZVAL_COPY_VALUE(retval_ptr, &local_retval); |
|
|
} else { |
|
|
zval_ptr_dtor(&local_retval); |
|
|
} |
|
|
} else { |
|
|
if (retval_ptr) { |
|
|
ZVAL_NULL(retval_ptr); |
|
|
} |
|
|
} |
|
|
|
|
|
EG(no_extensions)=0; |
|
|
destroy_op_array(new_op_array); |
|
|
efree_size(new_op_array, sizeof(zend_op_array)); |
|
|
retval = SUCCESS; |
|
|
} else { |
|
|
retval = FAILURE; |
|
|
} |
|
|
zend_string_release(pv); |
|
|
return retval; |
|
|
} |
|
|
/* }}} */ |
|
|
|
|
|
static void php_readline_try_interactive_eval(const char* code, const size_t codelen, const char* string_name) /* {{{ */ |
|
|
{ |
|
|
zval returned_zv; |
|
|
/* The snippet that's evaluated depends on whether the result would be dumped */ |
|
|
const bool should_dump = CLIR_G(enable_interactive_shell_result_function) && php_readline_should_dump_interactive_result(); |
|
|
ZVAL_UNDEF(&returned_zv); |
|
|
zend_try { |
|
|
php_readline_eval_stringl(code, codelen, should_dump ? &returned_zv : NULL, string_name); |
|
|
if (!EG(exception) && should_dump && !Z_ISUNDEF(returned_zv)) { |
|
|
/* If the evaluated expression caused output, and that output did not add in a newline, |
|
|
* append a newline before possibly dumping the result expression */ |
|
|
if (!pager_pipe && php_last_char != '\0' && php_last_char != '\n') { |
|
|
PHPWRITE("\n", 1); |
|
|
} |
|
|
php_readline_dump_interactive_result(code, codelen, &returned_zv); |
|
|
} |
|
|
} zend_end_try(); |
|
|
if (should_dump) { |
|
|
zend_try { |
|
|
zval_ptr_dtor(&returned_zv); |
|
|
} zend_end_try(); |
|
|
} |
|
|
} |
|
|
|
|
|
static zend_string *cli_get_prompt(char *block, char prompt) /* {{{ */ |
|
|
{ |
|
|
smart_str retval = {0}; |
|
Expand Down
Expand Up
|
@@ -185,9 +311,7 @@ static zend_string *cli_get_prompt(char *block, char prompt) /* {{{ */ |
|
|
code = estrndup(prompt_spec + 1, prompt_end - prompt_spec - 1); |
|
|
|
|
|
CLIR_G(prompt_str) = &retval; |
|
|
zend_try { |
|
|
zend_eval_stringl(code, prompt_end - prompt_spec - 1, NULL, "php prompt code"); |
|
|
} zend_end_try(); |
|
|
php_readline_try_interactive_eval(prompt_spec + 1, prompt_end - (prompt_spec + 1), "php prompt code"); |
|
|
CLIR_G(prompt_str) = NULL; |
|
|
efree(code); |
|
|
prompt_spec = prompt_end; |
|
Expand Down
Expand Up
|
@@ -682,9 +806,7 @@ static int readline_shell_run(void) /* {{{ */ |
|
|
history_lines_to_write = 0; |
|
|
} |
|
|
|
|
|
zend_try { |
|
|
zend_eval_stringl(code, pos, NULL, "php shell code"); |
|
|
} zend_end_try(); |
|
|
php_readline_try_interactive_eval(code, pos, "php shell code"); |
|
|
|
|
|
pos = 0; |
|
|
|
|
Expand Down
|
|