Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context, zend_op_arra
CG(context).try_catch_offset = -1;
CG(context).current_brk_cont = -1;
CG(context).last_brk_cont = 0;
CG(has_assigned_to_http_response_header) = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, doesn't this need to be on the op array context instead? Otherwise we only warn once per compilation instead of once per file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added tests and it seems to work as I expect it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah okay I see I actually think I wanted to say "once per op_array", but if that's the intention that's fine I guess?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it seems to be once per op_array 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll check this again tomorrow when I'm more awake

Copy link
Member

@nielsdos nielsdos Aug 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you reset the variable when a new op context starts, but they can be nested. So the following emits 2 deprecations even though it should only emit 1 (artificial example I know, but a simple one):

<?php
function foo() {
    $http_response_header = "foo";
    function nested() {
        echo $http_response_header;
    }
    echo $http_response_header;
}

You can remove the code from "nested" and it will still emit a deprecation that should not be happening.

CG(context).brk_cont_array = NULL;
CG(context).labels = NULL;
CG(context).in_jmp_frameless_branch = false;
Expand Down Expand Up @@ -2867,7 +2868,7 @@ static void zend_compile_class_ref(znode *result, zend_ast *name_ast, uint32_t f
}
/* }}} */

static zend_result zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */
static zend_result zend_try_compile_cv(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
{
zend_ast *name_ast = ast->child[0];
if (name_ast->kind == ZEND_AST_ZVAL) {
Expand All @@ -2884,6 +2885,16 @@ static zend_result zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */
return FAILURE;
}

if (zend_string_equals_literal(name, "http_response_header")) {
if (type == BP_VAR_R && !CG(has_assigned_to_http_response_header)) {
zend_error(E_DEPRECATED,
"The predefined locally scoped $http_response_header variable is deprecated,"
" call http_get_last_response_headers() instead");
} else if (type == BP_VAR_W) {
CG(has_assigned_to_http_response_header) = true;
}
}

result->op_type = IS_CV;
result->u.op.var = lookup_cv(name);

Expand Down Expand Up @@ -2920,6 +2931,14 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint

opline->extended_value = ZEND_FETCH_GLOBAL;
} else {
// TODO: Have a test case for this?
if (name_node.op_type == IS_CONST
&& type == BP_VAR_R
&& zend_string_equals_literal(Z_STR(name_node.u.constant), "http_response_header")) {
zend_error(E_DEPRECATED,
"The predefined locally scoped $http_response_header variable is deprecated,"
" call http_get_last_response_headers() instead");
}
opline->extended_value = ZEND_FETCH_LOCAL;
}

Expand Down Expand Up @@ -2991,7 +3010,7 @@ static zend_op *zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t t
result->op_type = IS_TMP_VAR;
}
return opline;
} else if (zend_try_compile_cv(result, ast) == FAILURE) {
} else if (zend_try_compile_cv(result, ast, type) == FAILURE) {
return zend_compile_simple_var_no_cv(result, ast, type, delayed);
}
return NULL;
Expand Down Expand Up @@ -3417,7 +3436,7 @@ static void zend_compile_expr_with_potential_assign_to_self(
/* $a[0] = $a should evaluate the right $a first */
znode cv_node;

if (zend_try_compile_cv(&cv_node, expr_ast) == FAILURE) {
if (zend_try_compile_cv(&cv_node, expr_ast, BP_VAR_R) == FAILURE) {
zend_compile_simple_var_no_cv(expr_node, expr_ast, BP_VAR_R, 0);
} else {
zend_emit_op_tmp(expr_node, ZEND_QM_ASSIGN, &cv_node, NULL);
Expand All @@ -3438,6 +3457,7 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
if (is_this_fetch(var_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
}
// TODO: Mark assignment to http_response_header?

zend_ensure_writable_variable(var_ast);

Expand Down Expand Up @@ -3507,7 +3527,7 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
/* list($a, $b) = $a should evaluate the right $a first */
znode cv_node;

if (zend_try_compile_cv(&cv_node, expr_ast) == FAILURE) {
if (zend_try_compile_cv(&cv_node, expr_ast, BP_VAR_R) == FAILURE) {
zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0);
} else {
zend_emit_op_tmp(&expr_node, ZEND_QM_ASSIGN, &cv_node, NULL);
Expand Down Expand Up @@ -3822,7 +3842,7 @@ static uint32_t zend_compile_args(
opcode = ZEND_SEND_VAR_EX;
CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS;
break;
} else if (zend_try_compile_cv(&arg_node, arg) == SUCCESS) {
} else if (zend_try_compile_cv(&arg_node, arg, BP_VAR_R) == SUCCESS) {
opcode = ZEND_SEND_VAR_EX;
break;
}
Expand Down Expand Up @@ -5427,7 +5447,7 @@ static void zend_compile_global_var(zend_ast *ast) /* {{{ */
// 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) {
} else if (zend_try_compile_cv(&result, var_ast, BP_VAR_R) == SUCCESS) {
zend_op *opline = zend_emit_op(NULL, ZEND_BIND_GLOBAL, &result, &name_node);
opline->extended_value = zend_alloc_cache_slot();
} else {
Expand Down Expand Up @@ -5553,7 +5573,7 @@ static void zend_compile_unset(zend_ast *ast) /* {{{ */
case ZEND_AST_VAR:
if (is_this_fetch(var_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot unset $this");
} else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) {
} else if (zend_try_compile_cv(&var_node, var_ast, BP_VAR_UNSET) == SUCCESS) {
opline = zend_emit_op(NULL, ZEND_UNSET_CV, &var_node, NULL);
} else {
opline = zend_compile_simple_var_no_cv(NULL, var_ast, BP_VAR_UNSET, 0);
Expand Down Expand Up @@ -6116,7 +6136,7 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
if (is_this_fetch(value_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
} else if (value_ast->kind == ZEND_AST_VAR &&
zend_try_compile_cv(&value_node, value_ast) == SUCCESS) {
zend_try_compile_cv(&value_node, value_ast, BP_VAR_R) == SUCCESS) {
SET_NODE(opline->op2, &value_node);
} else {
opline->op2_type = IS_VAR;
Expand Down Expand Up @@ -10834,7 +10854,7 @@ static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
if (is_this_fetch(var_ast)) {
opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_THIS, NULL, NULL);
CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS;
} else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) {
} else if (zend_try_compile_cv(&var_node, var_ast, BP_VAR_IS) == SUCCESS) {
opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_CV, &var_node, NULL);
} else {
opline = zend_compile_simple_var_no_cv(result, var_ast, BP_VAR_IS, 0);
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ struct _zend_compiler_globals {

bool ini_parser_unbuffered_errors;

bool has_assigned_to_http_response_header;

zend_llist open_files;

struct _zend_ini_parser_param *ini_parser_param;
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/bug75535.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ $responses = array(
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);

var_dump(file_get_contents($uri));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());

http_server_kill($pid);

Expand Down
3 changes: 2 additions & 1 deletion ext/standard/tests/http/bug80838.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ var_dump(http_get_last_response_headers());
http_server_kill($pid);

?>
--EXPECT--
--EXPECTF--
Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 23
NULL
array(3) {
[0]=>
Expand Down
3 changes: 2 additions & 1 deletion ext/standard/tests/http/gh9316.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ for ($i = 0; $i < count($responses); ++$i) {
http_server_kill($pid);

?>
--EXPECT--
--EXPECTF--
Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 17
http_get_last_response_headers() before stream layer call:
NULL
$http_response_header
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ $clientCode = <<<'CODE'
$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ $clientCode = <<<'CODE'
$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ $clientCode = <<<'CODE'
];
$ctx = stream_context_create($opts);
var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ $clientCode = <<<'CODE'
];
$ctx = stream_context_create($opts);
var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ $clientCode = <<<'CODE'
];
$ctx = stream_context_create($opts);
var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ $clientCode = <<<'CODE'
$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ $clientCode = <<<'CODE'
$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ $clientCode = <<<'CODE'
$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ $clientCode = <<<'CODE'
$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ $clientCode = <<<'CODE'
$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ $clientCode = <<<'CODE'
$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ $clientCode = <<<'CODE'
$ctx = stream_context_create();
stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
var_dump($http_response_header);
var_dump(http_get_last_response_headers());
CODE;

include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
Expand Down
3 changes: 2 additions & 1 deletion ext/standard/tests/http/http_response_header_01.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ var_dump(http_get_last_response_headers());
http_server_kill($pid);

?>
--EXPECT--
--EXPECTF--
Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 14
NULL
string(4) "Body"
array(3) {
Expand Down
3 changes: 2 additions & 1 deletion ext/standard/tests/http/http_response_header_02.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ var_dump(http_get_last_response_headers());
http_server_kill($pid);

?>
--EXPECT--
--EXPECTF--
Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 16
NULL
string(4) "Body"
array(5) {
Expand Down
1 change: 1 addition & 0 deletions ext/standard/tests/http/http_response_header_03.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ http_server_kill($pid);

?>
--EXPECTF--
Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 16
NULL

Warning: file_get_contents(http://%s:%d): Failed to open stream: HTTP request failed! HTTP/1.0 404 Not Found%a
Expand Down
4 changes: 3 additions & 1 deletion ext/standard/tests/http/http_response_header_04.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ var_dump(http_get_last_response_headers());
http_server_kill($pid);

?>
--EXPECT--

--EXPECTF--
Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 14
NULL
string(4) "Body"
array(2) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
$http_reponse_header using suggested cross-compatible version workaround
--SKIPIF--
<?php require 'server.inc'; http_server_skipif(); ?>
--INI--
allow_url_fopen=1
--FILE--
<?php
require 'server.inc';

$responses = array(
"data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\nSome: Header\r\n\r\nBody",
);

['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);

var_dump(http_get_last_response_headers());

$f = file_get_contents($uri);
var_dump($f);

if (function_exists('http_get_last_response_headers')) {
$http_response_header = http_get_last_response_headers();
}
var_dump($http_response_header);

http_server_kill($pid);

?>
--EXPECT--
NULL
string(4) "Body"
array(3) {
[0]=>
string(15) "HTTP/1.0 200 Ok"
[1]=>
string(12) "Some: Header"
[2]=>
string(12) "Some: Header"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
$http_reponse_header should warn once per file
--SKIPIF--
<?php require 'server.inc'; http_server_skipif(); ?>
--INI--
allow_url_fopen=1
--FILE--
<?php
require 'server.inc';

include 'http_response_header_deprecated_multiple_files_1.inc';

include 'http_response_header_deprecated_multiple_files_2.inc';

?>
--EXPECTF--
Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %shttp_response_header_deprecated_multiple_files_1.inc on line %d
string(6) "Body11"
array(2) {
[0]=>
string(15) "HTTP/1.0 200 Ok"
[1]=>
string(13) "Some: Header1"
}

Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %shttp_response_header_deprecated_multiple_files_2.inc on line %d
string(5) "Body2"
array(2) {
[0]=>
string(15) "HTTP/1.0 200 Ok"
[1]=>
string(13) "Some: Header2"
}
Loading