HTTP requests with file_get_contents, getting the response code

111,105

Solution 1

http://php.net/manual/en/reserved.variables.httpresponseheader.php

$context = stream_context_create(['http' => ['ignore_errors' => true]]);
$result = file_get_contents("http://example.com", false, $context);
var_dump($http_response_header);

Solution 2

None of the answers (including the one accepted by OP) actually satisfy the two requirements:

  • suppress a warning (I'm planning to throw my own exception in case of failure)
  • obtain the error information (at least, the response code) from the stream

Here's my take:

function fetch(string $method, string $url, string $body, array $headers = []) {
    $context = stream_context_create([
        "http" => [
            // http://docs.php.net/manual/en/context.http.php
            "method"        => $method,
            "header"        => implode("\r\n", $headers),
            "content"       => $body,
            "ignore_errors" => true,
        ],
    ]);

    $response = file_get_contents($url, false, $context);

    /**
     * @var array $http_response_header materializes out of thin air
     */

    $status_line = $http_response_header[0];

    preg_match('{HTTP\/\S*\s(\d{3})}', $status_line, $match);

    $status = $match[1];

    if ($status !== "200") {
        throw new RuntimeException("unexpected response status: {$status_line}\n" . $response);
    }

    return $response;
}

This will throw for a non-200 response, but you can easily work from there, e.g. add a simple Response class and return new Response((int) $status, $response); if that fits your use-case better.

For example, to do a JSON POST to an API endpoint:

$response = fetch(
    "POST",
    "http://example.com/",
    json_encode([
        "foo" => "bar",
    ]),
    [
        "Content-Type: application/json",
        "X-API-Key: 123456789",
    ]
);

Note the use of "ignore_errors" => true in the http context map - this will prevent the function from throwing errors for non-2xx status codes.

This is most likely the "right" amount of error-suppression for most use-cases - I do not recommend using the @ error-suppression operator, as this will also suppress errors like simply passing the wrong arguments, which could inadvertently hide a bug in calling code.

Solution 3

Adding few more lines to the accepted response to get the http code

function getHttpCode($http_response_header)
{
    if(is_array($http_response_header))
    {
        $parts=explode(' ',$http_response_header[0]);
        if(count($parts)>1) //HTTP/1.0 <code> <text>
            return intval($parts[1]); //Get code
    }
    return 0;
}

@file_get_contents("http://example.com");
$code=getHttpCode($http_response_header);

to hide the error output both comments are ok, ignore_errors = true or @ (I prefer @)

Share:
111,105
georg
Author by

georg

Hi, I'm Georg. I do mostly python (geodata / bigdata), (type | java)script and some (objective) C. work :: gws | datafeedr fun :: copytables | chartreux | slon | jadzia

Updated on July 08, 2022

Comments

  • georg
    georg almost 2 years

    I'm trying to use file_get_contents together with stream_context_create to make POST requests. My code so far:

        $options = array('http' => array(
            'method'  => 'POST',
            'content' => $data,
            'header'  => 
                "Content-Type: text/plain\r\n" .
                "Content-Length: " . strlen($data) . "\r\n"
        ));
        $context  = stream_context_create($options);
        $response = file_get_contents($url, false, $context);
    

    It works fine, however, when an HTTP error occurs, it spits out a warning:

    file_get_contents(...): failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request
    

    and returns false. Is there a way to:

    • suppress a warning (I'm planning to throw my own exception in case of failure)
    • obtain the error information (at least, the response code) from the stream
  • georg
    georg about 11 years
    For those wondering, the answer to the first question is to add 'ignore_errors' => TRUE to $options.
  • Nico Haase
    Nico Haase almost 6 years
    And how does this solve the question? Can you explain how to get the response code?
  • Admin
    Admin almost 6 years
    @Nico What do you think about my solution?
  • Nico Haase
    Nico Haase almost 6 years
    It's still a bad one, as you parse a JSON result not and read a random field named status - it has no connection to the HTTP status code for the API call
  • Admin
    Admin almost 6 years
    It's obvious that the RESTful API I connect to returns a JSON response and that what I need to know for my purpose (200 or 400) is contained in the "status" field. That's all I need and that's all I get. If you need to know the HTTP status code, just do this: return $http_response_header[0]; But note that it doesn't work with @file_get_contents.
  • Nico Haase
    Nico Haase almost 6 years
    And how does this answer the original question? Why do you think that $http_response_header[0], a highly upvoted answer, does not work with file_get_contents?
  • Admin
    Admin almost 6 years
    It doesn't work with @file_get_contents (NOT with file_get_contents). Just try it!
  • MLK.DEV
    MLK.DEV almost 5 years
    @Besen the ->status is not always an option as it's not part of the spec. That is a property being exposed by whatever stream you are tapping as a courtesy (which is nice) but not standard. If the stream does not explicitly define this property your answer does not provide a viable solution.
  • at54321
    at54321 almost 3 years
    That's great! But I wonder: isn't there a less-hacky way to directly get the status code as an integer value?
  • mindplay.dk
    mindplay.dk almost 3 years
    @at54321 not with file_get_contents, that's how it was designed (back in the deep, dark ages of PHP) but in my own client I ended up using fopen instead, which avoids this weird, magical API - you can see here how to do that.
  • GPHemsley
    GPHemsley over 2 years
    Documentation for $http_response_header, which does indeed materialize itself after the call to file_get_contents().
  • Liglo App
    Liglo App about 2 years
    "$http_response_header materializes out of thin air" deserves an upvote, PHP for allowing this a downvote for the language design.
  • mindplay.dk
    mindplay.dk about 2 years
    @LigloApp "language design" 😂
  • Your Common Sense
    Your Common Sense almost 2 years
    @FluorescentGreen5 with your recommendation you are ruining many programmers experience. Please do not recommend anything that will result in the very bad experience.