Skip to content

PHP 8.5: reading $http_response_header emits a compile-time deprecation on include #155

Description

@Baffos

Environment

  • matomo/matomo-php-tracker: 4.0.0 (and current master, cf976bf)
  • PHP: 8.5.4 (CLI, NTS)

What happens

Since PHP 8.5, $http_response_header is deprecated, and the diagnostic is emitted at
compile time — merely including MatomoTracker.php triggers it, without any code
being executed:

$ php -r "include 'vendor/matomo/matomo-php-tracker/MatomoTracker.php';"
Deprecated: The predefined locally scoped $http_response_header variable is deprecated,
call http_get_last_response_headers() instead in .../MatomoTracker.php on line 2419

The offending line is the legacy fallback in sendRequest():

} elseif ($response !== false) {
    $responseHeaders = $http_response_header;
}

Why this is a problem

Because it fires on include rather than on use, the notice cannot be avoided by the
function_exists('http_get_last_response_headers') guard, and it cannot be suppressed
with @. Any tool that autoloads the class with error_reporting(E_ALL) and a
throwing error handler aborts. Concrete example — Psalm 6.16.1 crashes while scanning
the package, because it evaluates the class_exists('\MatomoTracker') call in
PiwikTracker.php:17, which triggers the autoloader:

Uncaught RuntimeException: PHP Error: The predefined locally scoped
$http_response_header variable is deprecated ... in .../MatomoTracker.php:2419
#4 .../PiwikTracker.php(17): class_exists('\\MatomoTracker')
(Psalm 6.16.1 crashed due to an uncaught Throwable)

Applications that log deprecations also get one entry per include.

Version 3.4.0 was not affected, because it assigned to the variable before reading it.

Suggested fix

Assigning the variable before reading it silences the compile-time diagnostic while
keeping the PHP < 8.5 fallback working (this is exactly the shape 3.4.0 used):

$responseHeaders = [];
$http_response_header = [];   // avoid the PHP 8.5 compile-time deprecation
$response = file_get_contents($url, false, $ctx);

if (function_exists('http_get_last_response_headers')) {
    $headers = http_get_last_response_headers();
    if (is_array($headers)) {
        $responseHeaders = $headers;
    }
} elseif ($response !== false) {
    $responseHeaders = $http_response_header;
}

Verified on PHP 8.5.4:

function a() { $r = @file_get_contents('http://x'); return $http_response_header; }
// -> Deprecated: The predefined locally scoped $http_response_header variable is deprecated

function b() { $http_response_header = []; $r = @file_get_contents('http://x'); return $http_response_header; }
// -> no diagnostic

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions