Skip to content

Add http_(get|clear)_last_reponse_headers() functions #12500

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

Merged
merged 1 commit into from
Feb 29, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ PHP NEWS
. Changed return type of long2ip to string from string|false. (Jorg Sowa)
. Fix GH-12143 (Extend the maximum precision round can handle by one digit).
(SakiTakamachi)
. Added the http_get_last_response_headers() and
http_clear_last_response_headers() that allows retrieving the same content
as the magic $http_response_header variable.

- XML:
. Added XML_OPTION_PARSE_HUGE parser option. (nielsdos)
Expand Down
5 changes: 5 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,11 @@ PHP 8.4 UPGRADE NOTES
. sodium_crypto_aead_aes256gcm_*() functions are now enabled on aarch64 CPUs
with the ARM cryptographic extensions.

- Standard:
. Added the http_get_last_response_headers() and
http_clear_last_response_headers() that allows retrieving the same content
as the magic $http_response_header variable.

- XSL:
. Added XSLTProcessor::registerPhpFunctionNS().
RFC: https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl
Expand Down
6 changes: 6 additions & 0 deletions ext/standard/basic_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,9 @@ PHP_RINIT_FUNCTION(basic) /* {{{ */
BASIC_RINIT_SUBMODULE(dir)
BASIC_RINIT_SUBMODULE(url_scanner_ex)

/* Initialize memory for last http headers */
ZVAL_UNDEF(&BG(last_http_headers));

/* Setup default context */
FG(default_context) = NULL;

Expand Down Expand Up @@ -482,6 +485,9 @@ PHP_RSHUTDOWN_FUNCTION(basic) /* {{{ */
BASIC_RSHUTDOWN_SUBMODULE(user_filters)
BASIC_RSHUTDOWN_SUBMODULE(browscap)

/* Free last http headers */
zval_ptr_dtor(&BG(last_http_headers));

BG(page_uid) = -1;
BG(page_gid) = -1;
return SUCCESS;
Expand Down
3 changes: 3 additions & 0 deletions ext/standard/basic_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ typedef struct _php_basic_globals {

zval active_ini_file_section;

/* http_fopen_wrapper.c */
zval last_http_headers;

/* pageinfo.c */
zend_long page_uid;
zend_long page_gid;
Expand Down
4 changes: 4 additions & 0 deletions ext/standard/basic_functions.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -3025,6 +3025,10 @@ function pfsockopen(string $hostname, int $port = -1, &$error_code = null, &$err
/** @refcount 1 */
function http_build_query(array|object $data, string $numeric_prefix = "", ?string $arg_separator = null, int $encoding_type = PHP_QUERY_RFC1738): string {}

function http_get_last_response_headers(): ?array {}

function http_clear_last_response_headers(): void {}

/**
* @param array|null $options
* @return array<int, array>
Expand Down
10 changes: 9 additions & 1 deletion ext/standard/basic_functions_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions ext/standard/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "SAPI.h"
#include "zend_exceptions.h"
#include "ext/spl/spl_exceptions.h"
#include "basic_functions.h"

static void php_url_encode_scalar(zval *scalar, smart_str *form_str,
int encoding_type, zend_ulong index_int,
Expand Down Expand Up @@ -360,3 +361,26 @@ PHP_FUNCTION(request_parse_body)
SG(request_parse_body_context).throw_exceptions = false;
memset(&SG(request_parse_body_context).options_cache, 0, sizeof(SG(request_parse_body_context).options_cache));
}

PHP_FUNCTION(http_get_last_response_headers)
{
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}

if (!Z_ISUNDEF(BG(last_http_headers))) {
RETURN_COPY(&BG(last_http_headers));
} else {
RETURN_NULL();
}
}

PHP_FUNCTION(http_clear_last_response_headers)
{
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}

zval_ptr_dtor(&BG(last_http_headers));
ZVAL_UNDEF(&BG(last_http_headers));
}
6 changes: 6 additions & 0 deletions ext/standard/http_fopen_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -982,13 +982,19 @@ php_stream *php_stream_url_wrap_http(php_stream_wrapper *wrapper, const char *pa
{
php_stream *stream;
zval headers;

ZVAL_UNDEF(&headers);

zval_ptr_dtor(&BG(last_http_headers));
ZVAL_UNDEF(&BG(last_http_headers));

stream = php_stream_url_wrap_http_ex(
wrapper, path, mode, options, opened_path, context,
PHP_URL_REDIRECT_MAX, HTTP_WRAPPER_HEADER_INIT, &headers STREAMS_CC);

if (!Z_ISUNDEF(headers)) {
ZVAL_COPY(&BG(last_http_headers), &headers);

if (FAILURE == zend_set_local_var_str(
"http_response_header", sizeof("http_response_header")-1, &headers, 0)) {
zval_ptr_dtor(&headers);
Expand Down
10 changes: 10 additions & 0 deletions ext/standard/tests/http/bug75535.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,27 @@ $responses = array(

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

var_dump(http_get_last_response_headers());

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

http_server_kill($pid);

?>
--EXPECT--
NULL
string(0) ""
array(2) {
[0]=>
string(15) "HTTP/1.0 200 Ok"
[1]=>
string(14) "Content-Length"
}
array(2) {
[0]=>
string(15) "HTTP/1.0 200 Ok"
[1]=>
string(14) "Content-Length"
}
12 changes: 12 additions & 0 deletions ext/standard/tests/http/bug80838.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,26 @@ $options = [

$ctx = stream_context_create($options);

var_dump(http_get_last_response_headers());

$fd = fopen($uri, 'rb', false, $ctx);
fclose($fd);
var_dump($http_response_header);
var_dump(http_get_last_response_headers());

http_server_kill($pid);

?>
--EXPECT--
NULL
array(3) {
[0]=>
string(32) "HTTP/1.1 101 Switching Protocols"
[1]=>
string(15) "Header1: Value1"
[2]=>
string(15) "Header2: Value2"
}
array(3) {
[0]=>
string(32) "HTTP/1.1 101 Switching Protocols"
Expand Down
31 changes: 31 additions & 0 deletions ext/standard/tests/http/gh9316.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,52 @@ $responses = array(
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);

for ($i = 0; $i < count($responses); ++$i) {
echo 'http_get_last_response_headers() before stream layer call:', PHP_EOL;
var_dump(http_get_last_response_headers());

$f = @fopen($uri, "r");
echo '$http_response_header', PHP_EOL;
var_dump($http_response_header);
echo 'http_get_last_response_headers() after stream layer call:', PHP_EOL;
var_dump(http_get_last_response_headers());
fclose($f);
}

http_server_kill($pid);

?>
--EXPECT--
http_get_last_response_headers() before stream layer call:
NULL
$http_response_header
array(2) {
[0]=>
string(126) "HTTP/1.1 200 Some very long reason-phrase to test that this is properly handled by our code without adding a new header like "
[1]=>
string(12) "Good: Header"
}
http_get_last_response_headers() after stream layer call:
array(2) {
[0]=>
string(126) "HTTP/1.1 200 Some very long reason-phrase to test that this is properly handled by our code without adding a new header like "
[1]=>
string(12) "Good: Header"
}
http_get_last_response_headers() before stream layer call:
array(2) {
[0]=>
string(126) "HTTP/1.1 200 Some very long reason-phrase to test that this is properly handled by our code without adding a new header like "
[1]=>
string(12) "Good: Header"
}
$http_response_header
array(2) {
[0]=>
string(13) "HTTP/1.1 200 "
[1]=>
string(12) "Good: Header"
}
http_get_last_response_headers() after stream layer call:
array(2) {
[0]=>
string(13) "HTTP/1.1 200 "
Expand Down
38 changes: 38 additions & 0 deletions ext/standard/tests/http/http_clear_last_response_headers.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
Verify that http_clear_last_response_headers() clears the headers.
--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);

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

// Clear headers
http_clear_last_response_headers();
var_dump(http_get_last_response_headers());

http_server_kill($pid);

?>
--EXPECT--
string(4) "Body"
array(3) {
[0]=>
string(15) "HTTP/1.0 200 Ok"
[1]=>
string(12) "Some: Header"
[2]=>
string(12) "Some: Header"
}
NULL
12 changes: 12 additions & 0 deletions ext/standard/tests/http/http_response_header_01.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ $responses = array(

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

var_dump(http_get_last_response_headers());

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

http_server_kill($pid);

?>
--EXPECT--
NULL
string(4) "Body"
array(3) {
[0]=>
Expand All @@ -31,3 +35,11 @@ array(3) {
[2]=>
string(12) "Some: Header"
}
array(3) {
[0]=>
string(15) "HTTP/1.0 200 Ok"
[1]=>
string(12) "Some: Header"
[2]=>
string(12) "Some: Header"
}
16 changes: 16 additions & 0 deletions ext/standard/tests/http/http_response_header_02.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ $responses = array(

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

var_dump(http_get_last_response_headers());

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

http_server_kill($pid);

?>
--EXPECT--
NULL
string(4) "Body"
array(5) {
[0]=>
Expand All @@ -37,3 +41,15 @@ array(5) {
[4]=>
string(12) "Some: Header"
}
array(5) {
[0]=>
string(18) "HTTP/1.0 302 Found"
[1]=>
string(12) "Some: Header"
[2]=>
string(20) "Location: /try-again"
[3]=>
string(15) "HTTP/1.0 200 Ok"
[4]=>
string(12) "Some: Header"
}
Loading