summaryrefslogtreecommitdiff
path: root/Zend/zend_compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r--Zend/zend_compile.c91
1 files changed, 84 insertions, 7 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 83942be570..b87f79446c 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -2675,6 +2675,21 @@ static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */
}
/* }}} */
+static bool is_globals_fetch(const zend_ast *ast)
+{
+ if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) {
+ zval *name = zend_ast_get_zval(ast->child[0]);
+ return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "GLOBALS");
+ }
+
+ return 0;
+}
+
+static bool is_global_var_fetch(zend_ast *ast)
+{
+ return ast->kind == ZEND_AST_DIM && is_globals_fetch(ast->child[0]);
+}
+
static zend_bool this_guaranteed_exists() /* {{{ */
{
zend_op_array *op_array = CG(active_op_array);
@@ -2695,6 +2710,13 @@ static zend_op *zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t t
}
CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS;
return opline;
+ } else if (is_globals_fetch(ast)) {
+ zend_op *opline = zend_emit_op(result, ZEND_FETCH_GLOBALS, NULL, NULL);
+ if (type == BP_VAR_R || type == BP_VAR_IS) {
+ opline->result_type = IS_TMP_VAR;
+ result->op_type = IS_TMP_VAR;
+ }
+ return opline;
} else if (zend_try_compile_cv(result, ast) == FAILURE) {
return zend_compile_simple_var_no_cv(result, ast, type, delayed);
}
@@ -2740,10 +2762,22 @@ static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t
znode var_node, dim_node;
- zend_short_circuiting_mark_inner(var_ast);
- opline = zend_delayed_compile_var(&var_node, var_ast, type, 0);
- if (opline && type == BP_VAR_W && (opline->opcode == ZEND_FETCH_STATIC_PROP_W || opline->opcode == ZEND_FETCH_OBJ_W)) {
- opline->extended_value |= ZEND_FETCH_DIM_WRITE;
+ if (is_globals_fetch(var_ast)) {
+ zend_compile_expr(&dim_node, dim_ast);
+ if (dim_node.op_type == IS_CONST) {
+ convert_to_string(&dim_node.u.constant);
+ }
+
+ opline = zend_delayed_emit_op(result, ZEND_FETCH_R, &dim_node, NULL);
+ opline->extended_value = ZEND_FETCH_GLOBAL;
+ zend_adjust_for_fetch_type(opline, result, type);
+ return opline;
+ } else {
+ zend_short_circuiting_mark_inner(var_ast);
+ opline = zend_delayed_compile_var(&var_node, var_ast, type, 0);
+ if (opline && type == BP_VAR_W && (opline->opcode == ZEND_FETCH_STATIC_PROP_W || opline->opcode == ZEND_FETCH_OBJ_W)) {
+ opline->extended_value |= ZEND_FETCH_DIM_WRITE;
+ }
}
zend_separate_if_call_and_write(&var_node, var_ast, type);
@@ -3021,6 +3055,10 @@ static void zend_ensure_writable_variable(const zend_ast *ast) /* {{{ */
if (zend_ast_is_short_circuited(ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Can't use nullsafe operator in write context");
}
+ if (is_globals_fetch(ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "$GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax");
+ }
}
/* }}} */
@@ -3064,7 +3102,9 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
zend_ensure_writable_variable(var_ast);
- switch (var_ast->kind) {
+ /* Treat $GLOBALS['x'] assignment like assignment to variable. */
+ zend_ast_kind kind = is_global_var_fetch(var_ast) ? ZEND_AST_VAR : var_ast->kind;
+ switch (kind) {
case ZEND_AST_VAR:
offset = zend_delayed_compile_begin();
zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W, 0);
@@ -3172,6 +3212,9 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
if (zend_ast_is_short_circuited(source_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot take reference of a nullsafe chain");
}
+ if (is_globals_fetch(source_ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot acquire reference to $GLOBALS");
+ }
offset = zend_delayed_compile_begin();
zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W, 1);
@@ -3241,7 +3284,9 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
zend_ensure_writable_variable(var_ast);
- switch (var_ast->kind) {
+ /* Treat $GLOBALS['x'] assignment like assignment to variable. */
+ zend_ast_kind kind = is_global_var_fetch(var_ast) ? ZEND_AST_VAR : var_ast->kind;
+ switch (kind) {
case ZEND_AST_VAR:
offset = zend_delayed_compile_begin();
zend_delayed_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
@@ -3411,7 +3456,9 @@ uint32_t zend_compile_args(
arg_count++;
}
- if (zend_is_call(arg)) {
+ /* Treat passing of $GLOBALS the same as passing a call.
+ * This will error at runtime if the argument is by-ref. */
+ if (zend_is_call(arg) || is_globals_fetch(arg)) {
zend_compile_var(&arg_node, arg, BP_VAR_R, 0);
if (arg_node.op_type & (IS_CONST|IS_TMP_VAR)) {
/* Function call was converted into builtin instruction */
@@ -4558,6 +4605,7 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */
convert_to_string(&name_node.u.constant);
}
+ // TODO(GLOBALS) Forbid "global $GLOBALS"?
if (is_this_fetch(var_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as global variable");
} else if (zend_try_compile_cv(&result, var_ast) == SUCCESS) {
@@ -4629,6 +4677,17 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
zend_ensure_writable_variable(var_ast);
+ if (is_global_var_fetch(var_ast)) {
+ zend_compile_expr(&var_node, var_ast->child[1]);
+ if (var_node.op_type == IS_CONST) {
+ convert_to_string(&var_node.u.constant);
+ }
+
+ opline = zend_emit_op(NULL, ZEND_UNSET_VAR, &var_node, NULL);
+ opline->extended_value = ZEND_FETCH_GLOBAL;
+ return;
+ }
+
switch (var_ast->kind) {
case ZEND_AST_VAR:
if (is_this_fetch(var_ast)) {
@@ -8711,6 +8770,24 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
}
}
+ if (is_globals_fetch(var_ast)) {
+ result->op_type = IS_CONST;
+ ZVAL_BOOL(&result->u.constant, ast->kind == ZEND_AST_ISSET);
+ return;
+ }
+
+ if (is_global_var_fetch(var_ast)) {
+ zend_compile_expr(&var_node, var_ast->child[1]);
+ if (var_node.op_type == IS_CONST) {
+ convert_to_string(&var_node.u.constant);
+ }
+
+ opline = zend_emit_op_tmp(result, ZEND_ISSET_ISEMPTY_VAR, &var_node, NULL);
+ opline->extended_value =
+ ZEND_FETCH_GLOBAL | (ast->kind == ZEND_AST_EMPTY ? ZEND_ISEMPTY : 0);
+ return;
+ }
+
zend_short_circuiting_mark_inner(var_ast);
switch (var_ast->kind) {
case ZEND_AST_VAR: