HTTP-запросы с file_get_contents, получение кода ответа


87

Я пытаюсь использовать file_get_contentsвместе с stream_context_createдля выполнения запросов POST. Мой код на данный момент:

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

Он работает нормально, однако при возникновении ошибки HTTP выдает предупреждение:

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

и возвращает false. Есть ли способ:

  • подавить предупреждение (я планирую выбросить собственное исключение в случае сбоя)
  • получить информацию об ошибке (как минимум, код ответа) из потока

Ответы:


150

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

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

35
Для тех , кому интересно, ответ на первый вопрос , чтобы добавить 'ignore_errors' => TRUEк $options.
georg

3
@georg Так же можно поставить @в начале строки.
FluorescentGreen5

19

Ни один из ответов (включая тот, который был принят OP) фактически не удовлетворяет двум требованиям:

  • подавить предупреждение (я планирую выбросить собственное исключение в случае сбоя)
  • получить информацию об ошибке (как минимум, код ответа) из потока

Вот мой вывод:

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

Это приведет к отсутствию 200ответа, но вы можете легко работать оттуда, например, добавить простой Responseкласс, и return new Response((int) $status, $response);если он лучше подходит для вашего варианта использования.

Например, чтобы передать JSON POSTв конечную точку API:

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

Обратите внимание на использование "ignore_errors" => trueв httpконтекстной карте - это предотвратит выдачу функцией ошибок для кодов состояния, отличных от 2xx.

Скорее всего, это «правильный» уровень подавления ошибок для большинства случаев использования - я не рекомендую использовать @оператор подавления ошибок, так как это также подавит такие ошибки, как простая передача неправильных аргументов, которые могут непреднамеренно скрыть ошибку в код вызова.


5

Добавляем еще несколько строк к принятому ответу, чтобы получить 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);

чтобы скрыть вывод ошибки, оба комментария в порядке, ignore_errors = true или @ (я предпочитаю @)


-1

Я перехожу на эту страницу с другой проблемой, поэтому отправляю свой ответ. Моя проблема заключалась в том, что я просто пытался подавить предупреждение и отобразить настраиваемое предупреждающее сообщение для пользователя, поэтому мне помогло это простое и очевидное исправление:

// Suppress the warning messages
error_reporting(0);

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

И, если необходимо, после этого верните отчет об ошибках:

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

-2

@file_get_contentsи ignore_errors = trueне то же самое: первое ничего не возвращает; второй подавляет сообщения об ошибках, но возвращает ответ сервера (например, 400 Bad request).

Я использую такую ​​функцию:

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

Возвращает 200 (нормально) или 400 (неверный запрос).

Он отлично работает и проще, чем cURL.


И как это решает вопрос? Вы можете объяснить, как получить код ответа?
Нико Хаасе

@Nico Что вы думаете о моем решении?
Besen 02

Это по-прежнему плохой, поскольку вы не анализируете результат JSON и читаете случайное поле с именем status- у него нет связи с кодом состояния HTTP для вызова API
Нико Хаасе

Очевидно, что RESTful API, к которому я подключаюсь, возвращает ответ JSON и что то, что мне нужно знать для моей цели (200 или 400), содержится в поле «status». Это все, что мне нужно, и это все, что я получаю. Если вам нужно знать код состояния HTTP, просто сделайте следующее: return $ http_response_header [0]; Но учтите, что это не работает с @file_get_contents.
Besen 02

И как это отвечает на исходный вопрос? Как вы думаете $http_response_header[0], почему этот ответ, получивший большое количество голосов, не работает file_get_contents?
Нико Хаасе
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.