1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

ext/standard: Deprecate $http_response_header (#19464)

RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_http_response_header_predefined_variable
This commit is contained in:
Gina Peter Banyard
2025-08-25 22:43:26 +01:00
committed by GitHub
parent e87767686c
commit 8a5972fd83
27 changed files with 189 additions and 27 deletions

View File

@@ -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(context).has_assigned_to_http_response_header = false;
CG(context).brk_cont_array = NULL;
CG(context).labels = NULL;
CG(context).in_jmp_frameless_branch = false;
@@ -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) {
@@ -2884,6 +2885,16 @@ static zend_result zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */
return FAILURE;
}
if (!CG(context).has_assigned_to_http_response_header && zend_string_equals_literal(name, "http_response_header")) {
if (type == BP_VAR_R) {
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(context).has_assigned_to_http_response_header = true;
}
}
result->op_type = IS_CV;
result->u.op.var = lookup_cv(name);
@@ -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;
}
@@ -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;
@@ -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);
@@ -3507,7 +3526,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);
@@ -3822,7 +3841,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;
}
@@ -5427,7 +5446,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 {
@@ -5553,7 +5572,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);
@@ -6116,7 +6135,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;
@@ -10842,7 +10861,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);

View File

@@ -208,6 +208,7 @@ typedef struct _zend_oparray_context {
zend_string *active_property_info_name;
zend_property_hook_kind active_property_hook_kind;
bool in_jmp_frameless_branch;
bool has_assigned_to_http_response_header;
} zend_oparray_context;
/* Class, property and method flags class|meth.|prop.|const*/

View File

@@ -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);

View File

@@ -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]=>

View File

@@ -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

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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__);

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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

View File

@@ -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) {

View File

@@ -0,0 +1,40 @@
--TEST--
Ensure suggested cross-version compatible code for $http_reponse_header provided by the RFC works
--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"
}

View File

@@ -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"
}

View File

@@ -0,0 +1,13 @@
<?php
$responses = array(
"data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header1\r\n\r\nBody11",
);
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
$f = file_get_contents($uri);
var_dump($f);
var_dump($http_response_header);
http_server_kill($pid);

View File

@@ -0,0 +1,13 @@
<?php
$responses = array(
"data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header2\r\n\r\nBody2",
);
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
$f = file_get_contents($uri);
var_dump($f);
var_dump($http_response_header);
http_server_kill($pid);

View File

@@ -0,0 +1,20 @@
--TEST--
$http_reponse_header should warn once per oparray
--FILE--
<?php
function foo() {
var_dump($http_response_header);
}
class C {
public function bar() {
var_dump($http_response_header);
}
}
?>
--EXPECTF--
Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 4
Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 9

View File

@@ -0,0 +1,16 @@
--TEST--
$http_reponse_header should warn once per oparray even if nested
--FILE--
<?php
function foo() {
$http_response_header = "foo";
function nested() {
echo $http_response_header;
}
echo $http_response_header;
}
?>
--EXPECTF--
Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 6