Testing WordPress headers in CLI mode

A WordPress specific solution to the empty headers in CLI mode issue.

What’s the problem with headers?

While developing the WordPress functional module for wp-browser I came across the need to make requests to WordPress and test its output headers.
The typical case is the one where the test does not see the user log in but still hitting the /wp-admin/index.php file: the auth_redirect function will fire and in turn the wp_redirect one will set a new Location header to redirect the user to the /wp-logig.php file.
Then auth_redirect will stop the request handling with an exit() call.
The issue with this is that functional tests are meant to run in the console and calls to the headers_list() function will always return an empty array when called in CLI mode.
The code below will have different outcomes depending on it running in CLI mode or not:

header('One: one');
header('Two: two');
header('Three: three');

// in CLI mode `$headers` will be empty!
$headers = headers_list();

So the problems lies in tests running, by definition, in CLI mode…

A workaround specific to WordPress

While WordPress reliance on calls to the header, exit and die functions makes tracking headers in tests impossible the uniformity of the calls are makes it easy to work around the issue.
All the calls to the header() function will happen in the wp_redirect function and that is a “pluggable” function; in WordPress terms it means the function is wrapped in a conditional block:

if( !function_exists('wp_redirect')) {
    function wp_redirect( $location = '', $status = 302) {
        // ...

I’ve copied the function body to a file included before WordPress is loaded and added a line taking care of storing the just set header in a variable that I will be later able to read even in CLI mode:

global $cli_headers;
 * An associative array in the [header => status] format.
$cli_headers = [];

 * Copy and paste of WordPress original function where headers are but stored
 * before sending to avoid CLI limitations.
 * @param $location
 * @param int $status
 * @return bool
function wp_redirect($location, $status = 302)
    // [original function code omitted]

    // this is the call the original function will do 
    header("Location: $location", true, $status);

    // store the just set header in the global to retrieve it later
    global $cli_headers;
    $cli_headers["Location: $location"] = $status;

    return true;

Later in my test code headers can now be fetched accessing the $cli_headers global variable.