HTTP API WordPress: отправка запросов и кэширование результатов

# HTTP API WordPress

В PHP есть несколько способов отправки HTTP-запросов, но в WordPress доступен только один. Этот единственный способ включает в себя все поддерживаемые PHP опции — это API, который является стандартом и удобен!

## Содержание

- Введение в HTTP API
- Функции HTTP API и отправка запросов
- Примеры использования WP HTTP API
  - #1 Передача параметров запроса в GET и POST
  - #2 Изменение параметров запроса
  - #3 Получение тела ответа (наш любимый)
  - #4 Получение заголовков ответа
  - #5 Отладка: данные запроса
  - #6 Получение кода ответа
  - #7 Пользовательс��ий тип запроса
  - #8 Получение данных о фильмах с КиноПоиск
  - #9 Управление кэшированием
  - #10 Хук: разрешить внутренние запросы с htpasswd
  - #11 Пример использования прокси
- Сохранение результата запроса (кэширование)
- Кэширование в временных опциях (транзиенты)
- Удаление временной опции
- Кэширование в метаполях
- Коды ответа HTTP

## Введение в HTTP API

Для абсолютных новичков, давайте объясним, что такое HTTP-запрос. Это запрос от браузера к серверу, или от одного сервера к другому. Пример диалога такой:

1. Привет, сервер, можешь показать файл: file.html?
2. Привет! Могу, вот он...

При создании HTTP-запросов в PHP обычно используют одну из следующих опций: библиотеку cURL или встроенные потоки PHP. Чтобы упростить и стандартизировать различные способы отправки запросов, в WordPress с версии 2.7 появилась класс WP_Http, который стал основой HTTP API.

### Зачем нам HTTP API

Удобство и необходимость такого API связаны с тем, что разные хосты поддерживают разные способы отправки запросов, и некоторые не поддерживают никаких. Задача HTTP API — создать единый стандарт для использования запросов в WordPress и гарантировать их работу. Если один способ передачи запроса не поддерживается, будет найден альтернативный.

Также HTTP API упрощает разработку. Авторам плагинов нужно писать много кода и изобретать велосипеды, чтобы их плагины работали с любым хостом. С HTTP API эта задача сводится к нескольким встроенным функциям WordPress.

Кроме того, HTTP API предоставляет единый стандарт для указания данных при работе с разными типами передачи запросов. Например, мы всегда указываем одни и те же параметры и передаем их в функцию HTTP API. Класс уже выбирает подходящий тип транспорта, модифицирует наши параметры и отправляет запрос.

С версии 2.7 HTTP API работал только с основными элементами запроса: заголовком, телом и ответом. С версии 2.8 были добавлены:

- сжатие
- поддержка кук
- поддержка прокси

Некоторые функции работают автоматически, без настройки дополнительных параметров запроса.

### Библиотека запросов

С версии WP 4.6 HTTP API работает на основе библиотеки Requests, которая разрабатывается и поддерживается WordPress.

Таким образом, основной класс WP_Http был полностью заменён библиотекой PHP Requests, и теперь все запросы выполняются через него.

HTTP API WordPress сегодня — это полноценный API, �� котором учтено множество деталей и исправлено сотни ошибок. Стоит отметить, что до версии WP 4.4 HTTP API значительно отличался от того, что есть сейчас, поэтому некоторые моменты из этого руководства могут не работать в версиях до 4.4.

Практически все возможности транспорта можно изменять через опции или фильтры. Например, с помощью фильтра http_api_transports можно добавить свой класс транспорта.

## Функции HTTP API и отправка запросов

Использовать HTTP API очень просто, есть специальные функции:

### Функции отправки запросов:

- wp_remote_get( $url, $args ) — отправляет HTTP GET запрос.
- wp_remote_post( $url, $args ) — отправляет HTTP POST запрос.
- wp_remote_head( $url, $args ) — отправляет HTTP HEAD запрос.
- wp_get_http_headers( $url ) — получает только HTTP заголовки указанного URL.
- wp_remote_request( $url, $args ) — отправляет запрос любого типа: GET, POST, HEAD, PUT, DELETE.
- wp_safe_remote_request( $url, $args ) — аналогично wp_remote_request(), но избегает небезопасных редиректов и перенаправлений.

Все функции возвращают: массив или WP_Error. Если запрос успешен, возвращается массив с данными ответа. Если возникает ошибка, возвращается WP_Error.

### Функции обработки ответа:

- wp_remote_retrieve_body( $response ) — получает только тело ответа.
- wp_remote_retrieve_headers( $response ) — получает весь массив заголовков ответа.
- wp_remote_retrieve_header( $response, $header ) — получает один заголовок; $header — это имя заголовка.
- wp_remote_retrieve_response_code( $response ) — получает код HTTP ответа (статус ответа).
- wp_remote_retrieve_response_message( $response ) — получает сообщение ответа, соответствующее коду ответа.
- wp_remote_retrieve_cookies( $response ) — получает все куки ответа.
- wp_remote_retrieve_cookie( $response, $name ) — получает все данные указанного куки, где $name — это имя куки.
- wp_remote_retrieve_cookie_value( $response, $name ) — получает значение указанного куки.

### Параметры функции

$url (строка) (обязательный) — URL, на который будет отправлен запрос.  
$args (массив) — параметры запроса. Смотрите описание wp_remote_request() для списка параметров.  
$response (массив) — результат запроса, возвращаемый любой функцией запроса.

### Простой пример использования

Теперь, когда мы знаем все функции запроса, давайте попробуем создать простой запрос.

```php
$response = wp_remote_get('http://httpbin.org/get?a=b&c=d');

Здесь мы отправили запрос на URL: http://httpbin.org/get и добавили два параметра запроса. В результате $response будет содержать следующий массив:

Array (
    [headers] => Array (
        [server] => nginx
        [date] => Sun, 19 Jun 2016 08:18:20 GMT
        [content-type] => application/json
        [content-length] => 316
        [connection] => close
        [access-control-allow-origin] => *
        [access-control-allow-credentials] => true
    )
    [body] => {
        "args": {
            "a": "b",
            "c": "d"
        },
        // Остальные данные
    }
    [response] => Array (
         => 200
        [message] => OK
    )
    [cookies] => Array()
    [filename] =>
    [http_response] => WP_HTTP_Requests_Response Object (
        // ...
    )
)

Каждый элемент ответа можно получить, выбрав его из массива. Например, чтобы получить заголовки ответа:

$response = wp_remote_get('http://httpbin.org/get?a=b&c=d');
$headers = $response['headers'];

echo $headers['server']; // nginx
echo $headers['content-type']; // application/json
echo $headers['content-length']; // 316

Однако рекомендуется использовать специальные функции для получения частей ответа, чтобы избежать ошибок.

Примеры использования WP HTTP API

1 Передача параметров запроса в GET и POST

Для GET-запроса параметры передаются непосредственно в URL:

$url = 'http://httpbin.org/get';
$params = array(
    'foo' => 'значение 1',
    'bar' => 10
);
$params = urlencode_deep($params);
$url = add_query_arg($params, $url);
$response = wp_remote_get($url);
$body = wp_remote_retrieve_body($response);
print_r($body);

Для POST-запроса параметры передаются в теле:

$url = 'http://httpbin.org/post';
$args = [
    'body' => [
        'foo' => 'значение 1',
        'bar' => 10,
    ],
    'timeout' => '5',
];
$response = wp_remote_post($url, $args);

2 Изменение параметров запроса

Перед отправкой запроса можно изменить настройки по умолчанию:

$url = 'http://httpbin.org/get';
$args = array(
    'timeout' => 5, // Время в секундах ожидания ответа
    'user-agent' => 'Mozilla/5.0',
);
$response = wp_remote_get($url, $args);

3 Получение тела ответа

Как передать заголовок в запросе и получить тело ответа:

$url = 'http://httpbin.org/get?a=b&c=d';
$args = array(
    'headers' => array( "Content-type" => "application/json" )
);
$response = wp_remote_get($url, $args);
$body = wp_remote_retrieve_body($response);

4 Получение заголовков ответа

1. Полный запрос и получение только заголовков

$response = wp_remote_get('http://httpbin.org/get?a=b&c=d');
$response_headers = wp_remote_retrieve_headers($response);
$content_type = wp_remote_retrieve_header($response, 'content-type');

2. Запрос только заголовков

$response = wp_remote_head('http://httpbin.org/get?a=b&c=d');
$response_headers = wp_remote_retrieve_headers($response);

5 Отладка: данные запроса

Добавьте следующий код, чтобы отладить запросы:

add_action('http_api_debug', function($response, $type, $class, $args, $url) {
    echo sprintf("Запрос URL: %sn", print_r($url, 1));
    echo sprintf("Параметры запроса: %sn", print_r($args, 1));
    echo sprintf("Ответ запроса: %sn", print_r($response, 1));
}, 10, 5);

6 Получение кода ответа

$response = wp_remote_get('http://httpbin.org/get?a=b&c=d');
echo wp_remote_retrieve_response_code($response); // 200
echo wp_remote_retrieve_response_message($response); // OK

7 Пользовательский тип запроса

Вы можете отправлять запросы других типов, например PUT или DELETE:

$url = 'http://httpbin.org';
$args = array(
    'method' => 'PUT',
);
$response = wp_remote_request($url, $args);

8 Получение данных о фильмах с КиноПоиск

Пример функции для получения информации о фильме:

function kinopoisk_get_movie($id) {
    $params = array('filmID' => absint($id));
    $url = 'http://api.kinopoisk.cf/getFilm';
    $url = add_query_arg($params, esc_url_raw($url));

    $response = wp_remote_get($url);
    $response_code = wp_remote_retrieve_response_code($response);
    $response_message = wp_remote_retrieve_response_message($response);
    $response_body = json_decode(wp_remote_retrieve_body($response));

    if (200 != $response_code) {
        return new WP_Error($response_code, $response_message);
    }
    return $response_body;
}

9 Управление кэшированием

Чтобы управлять кэшированием, нужно указать заголовок Cache-Control:

$readme_url = 'http://example.com/foo';
$http_params = [
    'headers' => [
        'Authorization' => "token $auth_token",
        'Cache-Control' => 'no-cache',
    ],
];
$res = wp_remote_get($readme_url, $http_params);

10 Хук: разрешить внутренние запросы с Htpasswd

Если на сервере установлена защита с использованием .htpasswd:

if ('production' !== wp_get_environment_type()) {
    add_filter('http_request_args', function($parsed_args, $url) {
        if (str_contains($url, parse_url(home_url(), PHP_URL_HOST))) {
            $log_pass = 'логин:пароль';
            $auth = &$parsed_args['headers']['Authorization'];
            if (!$auth) {
                $auth = 'Basic ' . base64_encode($log_pass);
            }
        }
        return $parsed_args;
    }, 10, 2);
}

11 Пример использования прокси

Для работы с прокси:

$proxy = '180.168.232.64:8001';
$proxyauth = 'k7yQJa:R9Cdeu';
$url = 'https://example.com';

[$host, $port] = explode(':', $proxy);
[$log, $pass] = explode(':', $proxyauth);

define('WP_PROXY_HOST', $host);
define('WP_PROXY_PORT', $port);
define('WP_PROXY_USERNAME', $log);
define('WP_PROXY_PASSWORD', $pass);

$resp = wp_remote_get($url);
$headers = wp_remote_retrieve_headers($resp);
$code = wp_remote_retrieve_response_code($resp);
$body = wp_remote_retrieve_body($resp);

var_dump($code);
print_r($headers);
echo $body;

После этого мы можем использовать кэширование результатов запросов.

Кэширование в временных опциях (транзиенты)

Чтобы кэшировать результаты HTTP-запросов, используйте функции WordPress:

  • set_transient( $transient, $value, $expiration ) — устанавливает/обновляет временную опцию.
  • get_transient( $transient ) — получает значение временной опции.
  • delete_transient( $transient ) — удаляет временную опцию.

Пример кэширования HTTP-запроса в временной опции

$transient = 'курс_usd'; // название временной опции
$usd_in_rub = get_transient($transient);

if (!$usd_in_rub) {
    $url = 'http://www.cbr.ru/scripts/XML_daily.asp';
    $expiration = DAY_IN_SECONDS / 2; // срок кэширования - полдня

    $resp = wp_remote_get($url); // получить данные
    if (wp_remote_retrieve_response_code($resp) === 200) {
        // Обработать ответ
        $body = wp_remote_retrieve_body($resp);
        $usd_in_rub = // обработка данных...
    }
    set_transient($transient, $usd_in_rub, $expiration);
}

echo 'Курс доллара к рублю сегодня: $1 = ' . $usd_in_rub . ' руб.';

Удаление временной опции

Если временная опция больше не требуется, её можно удалить:

delete_transient('имя_временной_опции');

Кэширование в метаполях

Для более целесообразного кэширования используйте метаполя для хранения данных, например, при получении рейтинга фильмов с КиноПоиск.

function get_film_rating($post_id = 0) {
    if (!$post_id) $post_id = $GLOBALS['post']->ID;

    $rating = get_post_meta($post_id, 'kinopoisk_rating', true);
    $up_time = get_post_meta($post_id, 'kinopoisk_rating_uptime', true);

    if (time() > $up_time + WEEK_IN_SECONDS) {
        $film_id = get_post_meta($post_id, 'kinopoisk_id', true);
        $res = kinopoisk_get_movie($film_id);

        if (!is_wp_error($res) && $res->ratingData->rating)
            $rating = $res->ratingData->rating;
        else
            $rating = '-1';

        update_post_meta($post_id, 'kinopoisk_rating_uptime', time());
        update_post_meta($post_id, 'kinopoisk_rating', $rating);
    }

    return $rating;
}

echo get_film_rating();

Коды ответа HTTP

Стандартные коды состояния HTTP и ответы на них:

$phrases = [
    200 => 'OK',
    404 => 'Not Found',
    500 => 'Internal Server Error',
    // И другие...
];

Такой список поможет быстро разобраться с ответами сервера.

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *