Solicitações HTTP com file_get_contents, obtendo o código de resposta


87

Estou tentando usar em file_get_contentsconjunto com stream_context_createpara fazer solicitações POST. Meu código até agora:

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

Funciona bem, no entanto, quando ocorre um erro de HTTP, ele exibe um aviso:

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

e retorna falso. Existe uma maneira de:

  • suprimir um aviso (estou planejando lançar minha própria exceção em caso de falha)
  • obter as informações de erro (pelo menos, o código de resposta) do fluxo

Respostas:



19

Nenhuma das respostas (incluindo a aceita pelo OP) realmente satisfaz os dois requisitos:

  • suprimir um aviso (estou planejando lançar minha própria exceção em caso de falha)
  • obter as informações de erro (pelo menos, o código de resposta) do fluxo

Aqui está minha opinião:

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;
}

Isso resultará em uma não 200resposta, mas você pode facilmente trabalhar a partir daí, por exemplo, adicionar uma Responseclasse simples e return new Response((int) $status, $response);se isso se adequar melhor ao seu caso de uso.

Por exemplo, para fazer um JSON POSTpara um endpoint de API:

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

Observe o uso de "ignore_errors" => trueno httpmapa de contexto - isso evitará que a função lance erros para códigos de status não 2xx.

Esta é provavelmente a quantidade "certa" de supressão de erros para a maioria dos casos de uso - eu não recomendo usar o @operador de supressão de erros, pois isso também suprime erros como simplesmente passar os argumentos errados, que poderiam inadvertidamente ocultar um bug no código de chamada.


5

Adicionando mais algumas linhas à resposta aceita para obter o código http

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

para ocultar a saída de erro, ambos os comentários estão ok, ignore_errors = true ou @ (eu prefiro @)


-1

Eu vou para esta página com um problema diferente, então postando minha resposta. Meu problema era que eu estava apenas tentando suprimir a notificação de aviso e exibir uma mensagem de aviso personalizada para o usuário, então essa correção simples e óbvia me ajudou:

// Suppress the warning messages
error_reporting(0);

$contents = file_get_contents($url);
if ($contents === false) {
  print 'My warning message';
}

E se necessário, retorne o relatório de erros depois disso:

// Enable warning messages again
error_reporting(-1);

-2

@file_get_contentse ignore_errors = truenão são iguais: o primeiro não retorna nada; o segundo suprime as mensagens de erro, mas retorna a resposta do servidor (por exemplo, 400 Bad request).

Eu uso uma função como esta:

$result = file_get_contents(
  $url_of_API, 
  false, 
  stream_context_create([
    'http' => [
      'content' => json_encode(['value1' => $value1, 'value2' => $value2]), 
      'header' => 'Authorization: Basic XXXXXXXXXXXXXXX', 
      'ignore_errors' => 1, 
      'method' => 'POST', 
      'timeout' => 10
    ]
  ])
);

return json_decode($result)->status;

Ele retorna 200 (Ok) ou 400 (Solicitação inválida).

Funciona perfeitamente e é mais fácil do que cURL.


E como isso resolve a questão? Você pode explicar como obter o código de resposta?
Nico Haase

@Nico O que você acha da minha solução?
Besen

Ainda é ruim, já que você não analisa um resultado JSON e lê um campo aleatório chamado status- ele não tem conexão com o código de status HTTP para a chamada de API
Nico Haase

É óbvio que a API RESTful à qual me conecto retorna uma resposta JSON e que o que preciso saber para o meu propósito (200 ou 400) está contido no campo "status". É tudo que preciso e é tudo que consigo. Se você precisa saber o código de status HTTP, faça o seguinte: return $ http_response_header [0]; Mas observe que não funciona com @file_get_contents.
Besen

E como isso responde à pergunta original? Por que você acha que $http_response_header[0]uma resposta altamente votada não funciona file_get_contents?
Nico Haase
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.