HTTP API WordPress
У PHP є кілька способів відправити HTTP запит, WordPress тільки один. Але цей один спосіб включає всі варіанти підтримувані PHP – це і є API, це стандарт і це зручно!
Коротке знайомство з HTTP API
Для зовсім новачків, мабуть, варто пояснити, що таке HTTP запит. Це запит браузера до сервера, або одного сервера до іншого, де відбувається подібний діалог:
- Привіт сервере, можеш мені показати файл: file.html?
- Вітання! Можу, ось він…
Виглядає такий діалог ось так (тільки на місці клієнта в даному випадку виступає наш сервер, який запитує на інший сервер):
При створенні HTTP запитів у PHP, як правило, використовуються один з варіантів: бібліотека cURL або вбудовані в PHP потоки (streams) . Щоб спростити та стандартизувати різні способи надсилання запитів, з версії 2.7. WordPress з’явився клас WP_Http , який і ліг в основу HTTP API.
З версії WP 4.6 ядро WP_Http повністю замінили PHP бібліотекою Requests for PHP і всі запити виконуються через неї. Вищезгадані класи WP_Http_Curl та WP_Http_Streams вже не використовуються. Таким чином, технічно HTTP API WordPress кардинально змінився, але залишився незмінним зовні: все працює як і працювало. Також з впровадженням бібліотеки Requests очікуються нові можливості, деякі з них вже з’явилися (нечутливі до регістру заголовки, підтримка міжнародних доменів на кшталт böcean.ch), а інші (паралельні запити) з’являться у наступних релізах і поки що можливі лише з використанням бібліотеки Requests безпосередньо.
Застаріло із WP 4.6. WP_Http визначає тип транспорту, і викликає інший клас, що відповідає цьому типу. Викликаний клас створює сам запит. За промовчанням у WordPress два таких класи, для різних типів: WP_Http_Curl та WP_Http_Streams
Зручність та необхідність такого API полягає в тому, що різні хостинги підтримують різні варіанти відправки запитів, а деякі не підтримують жодного. Завдання HTTP API створити єдиний стандарт використання запитів WordPress, при цьому щоб запити працювали завжди, якщо не підтримується один спосіб транспортування запиту, то буде знайдений альтернативний.
Інше завдання – спростити розробку. Авторам плагінів доводиться писати купу коду, винаходити велосипеди та припускатися помилок. І все це, щоб їхній плагін умів працювати з будь-яким хостингом. З HTTP API це завдання має спроститися до кількох вбудованих у WordPress функцій.
Ще один плюс HTTP API у цьому, що має єдиний стандарт вказаних даних, під час роботи з різними типами транспортування запитів, тобто. ми завжди вказуємо однакові параметри та передаємо їх у функцію HTTP API, а клас вже вибирає відповідний тип транспорту, наприклад cURL, змінює наші параметри під зрозумілі для поточного типу транспорту та надсилає запит.
З версії 2.7. HTTP API працював тільки з базовими елементами запиту: header , body та response . З версії 2.8. з’явилися: стиснення (compression), куки (cookies) та підтримка проксі (proxy). Деякі з функцій пасивні, тобто. працюють автоматично без встановлення додаткових параметрів запиту.
HTTP API WordPress сьогодні – це повноцінне API, в якому враховано багато дрібниць та виправлено сотні помилок. Також слід зауважити, що до версії WP 4.4 HTTP API значно відрізнявся від того, який він зараз, тому деякі моменти з цього мануалу можуть не працювати на версіях до 4.4.
Майже всі можливості транспорту можна змінювати через опції або фільтри. Наприклад, через фільтр http_api_transports можна додати ще один свій клас транспорту. Або через установку констант у файл wp-config.php можна увімкнути режим проксі:
define('WP_PROXY_HOST', '192.168.84.101'); define('WP_PROXY_PORT', '8080'); define('WP_PROXY_BYPASS_HOSTS', 'localhost, www.example.com, *.wordpress.org'); define('WP_PROXY_USERNAME', 'login'); define('WP_PROXY_PASSWORD', 'pass');
Щоб розібратися, як працює проксі, дивіться клас WP_HTTP_Proxy{}
Ну, і нарешті, HTTP API можна розширити для роботи з Twitter API, Google Maps API і т.д.
Функції HTTP API та надсилання запиту
Використовувати HTTP API дуже просто, для цього існують спеціальні функції 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() , але ще й дозволяє уникнути небезпечних редиректів і переадресацій. Потрібно при запитах HTTP на заздалегідь невідомі URL, наприклад, коли URL передається користувачем. Для «safe» також є функції обгортки:
Усі функції повертають: масив або WP_Error.
- Результат повертається у вигляді масиву та містить усі дані відповіді: контент (тіло) відповіді
- WP_Error повертається у разі невдалого запиту. Якщо сервер повернув нестандартну помилку: 500, 404 і т.д., то ви отримаєте дані відповіді, а не WP_Error.
Функції обробки відповіді:
wp_remote_retrieve_body( $response ) – отримує лише тіло (body) відповіді.
wp_remote_retrieve_headers($response) – отримує весь масив заголовків відповіді.
wp_remote_retrieve_header( $response, $header ) – отримує окремий заголовок, де $header – ім’я заголовка.
wp_remote_retrieve_response_message( $response ) – отримує повідомлення відповіді відповідне коду відповіді (статусу відповіді). Наприклад, при статусі “200”, повідомлення буде “OK”.
wp_remote_retrieve_cookies( $response ) – отримує всі куки відповіді.
wp_remote_retrieve_cookie( $response, $name ) – отримує всі дані зазначеної cookie, де $name – це назва cookie.
- wp_remote_retrieve_cookie_value( $response, $name ) – отримує значення вказаної cookie, де $name – це назва cookie.
Параметри функцій
-
$url
(рядок) (обов’язковий) - URL куди буде надіслано запит (з якого будуть отримані дані).
-
$args
(масив) -
Параметри запиту. Список параметрів дивіться в описі
wp_remote_request()
За замовчуванням: array() (передустановки) -
$response
(масив) - Результат запиту, що повертається будь-якою функцією запиту.
Базовий приклад використання
Тепер, коли ми знаємо всі функції запитів, спробуємо створити простий запит.
$response = wp_remote_get('http://httpbin.org/get?a=b&c=d');
Тут ми надіслали запит на URL: http://httpbin.org/get
і додали два параметри запиту ?a=b&c=d
. В результаті $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" }, "headers": { "Accept": "*/*", "Accept-Encoding": "deflate;q=1.0, compress;q=0.5, gzip;q=0.5", "Host": "httpbin.org", "User-Agent": "WordPress/4.5.2; http://wp-kama.ru" }, "origin": "5.101.156.80", "url": "http://httpbin.org/get?a=b&c=d" } [response] => Array ( [code] => 200 [message] => OK ) [cookies] => Array() [filename] => [http_response] => WP_HTTP_Requests_Response Object ( ... ) )
Як видно, результат містить ключі, кожен ключ – це частина відповіді сервера, де:
headers
– Містить заголовки HTTP відповіді сервера.body
– Тіло відповіді. Якщо ми запитуємо HTML сторінку, то тут буде HTML код. Якщо повертаються JSON дані, то тут буде рядок JSON. І т.д.response
– Містить статус код HTTP відповіді. Наприклад 200 – OK.cookies
– Містить куки, які просить встановити сервер у відповіді на запит.filename
– Містить розташування або шлях файлу який був відправлений на сервер і був там розміщений. Файл можна надіслати запитом POST.http_response
– З версії WP 4.6. Містить весь об’єкт результату запиту бібліотеки Requests.
Кожну частину відповіді можна отримати, вибравши її з масиву. Наприклад, давайте отримаємо заголовки відповіді:
$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
Однак отримувати частини відповіді таким чином не рекомендується! Тому що відповідь може бути різна і її потрібно перевірити або якось додатково обробити.
Для отримання кожної частини відповіді рекомендується використовувати спеціальні функції, зазначені вище. Давайте отримаємо потрібні нам заголовки відповіді правильно:
$response = wp_remote_get('http://httpbin.org/get?a=b&c=d'); echo wp_remote_retrieve_header($response, 'server'); //> nginx echo wp_remote_retrieve_header($response, 'content-type'); //> application/json echo wp_remote_retrieve_header($response, 'content-length'); //> 316
На цьому теоретичну частину HTTP API можна закінчити та перейти до прикладів.
Приклади використання WP HTTP API
#1 Передача параметрів запиту до GET і POST
Для GET запиту аргументи передаються в URL:
$url = 'http://httpbin.org/get'; // Параметри запиту $params = array( 'foo' => 'значення 1', 'bar' => 10 ); // щоб кирилиця правильно передалася $params = urlencode_deep($params); // Додамо параметр до URL $url = add_query_arg($params, $url); // В результаті отримаємо такий URL: // http://httpbin.org/get?foo=%D0%B7%D0%B0%D0%BD%D1%87%D0%B5%D0%BD%D0%B8%D0%B5+1&bar=10 // надсилаємо запит $ Response = wp_remote_get ($ Url); $body = wp_remote_retrieve_body( $response ); print_r($body);
Для запиту POST, параметри запиту передаються в опції body
:
$url = 'http://httpbin.org/post'; // Параметри запиту $args = [ // Параметри запиту 'body' => [ 'foo' => 'значення 1', 'bar' => 10, ], 'timeout' => '5', ]; $response = wp_remote_post( $url, $args );
#2 Зміна параметрів запиту
Перед надсиланням запиту можна змінити дефолтні налаштування запиту. Наприклад, встановити timeout (максимальний час) з’єднання або вказати user agent.
Повний список параметрів (налаштувань) див. у описі wp_remote_request() .
А цей приклад демонструє, як передавати такі налаштування:
$url = 'http://httpbin.org/get'; $ args = array ( 'timeout' => 5, // Час у секундах отримання даних. 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, як Gecko) Chrome/51.0.2704.103 Safari/537.36', ); $ 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' ); //> application/json
#2 Запитуємо тільки заголовки
У цьому прикладі ми не створюватимемо повний запит, а запросимо лише заголовки. Для цього скористаються функцією запиту wp_remote_head() :
$response = wp_remote_head('http://httpbin.org/get?a=b&c=d'); $response_headers = wp_remote_retrieve_headers($response); //> масив з усіма заголовками
Відповідь на такий запит ми отримаємо швидше, ніж за «повного запиту». Тому, якщо вам не потрібне тіло відповіді, користуйтеся таким запитом.
#3 Запитуємо лише заголовки (спосіб 2)
Використовуючи функцію wp_get_http_headers() , ми також можемо не створювати повний запит, а запросити і відразу отримати лише заголовки:
$response_headers = wp_get_http_headers('http://wordpress.org'); print_r($response_headers); /* Array ( [server] => nginx [date] => Wed, 22 Jun 2016 02:03:20 GMT [content-type] => application/json [content-length] => 316 [connection] => close [access-control-allow-origin] => * [access-control-allow-credentials] => true ) */
2 і 3 приклад, ніколи не містити тіло відповіді та їх використання може стати в нагоді для високо-навантажених API, які обмежують кількість запитів у період часу. Так, перед тим як отримати тіло запиту, потрібно перевірити: а чи були змінені дані, чи сервер відповідає на наш запит і т.д. У таких випадках, рекомендується спочатку відправити HEAD запит, обробити відповідь, де перевірити, чи є нові дані і тільки потім робити повний запит з отриманням тіла відповіді.
#4 Дебаг: дані запиту
Variant 1:
Щоб покопатися в WordPress HTTP API, я рекомендую наступний фрагмент за допомогою події http_api_debug :
add_action( 'http_api_debug', function( $response, $type, $class, $args, $url ) { // Ви можете змінити цей від error_log() до var_dump() але це може перешкодити AJAX requests echo sprintf( "Request URL: %sn", print_r( $url, 1 ) ); echo sprintf("Request Args: %sn", print_r($args, 1)); echo sprintf("Request Response: %sn", print_r($response, 1)); }, 10, 5);
Variant 2:
Іноді потрібно одержати дані запиту, які будуть надіслані безпосередньо перед запитом. Найпізніший хук який мені вдалося знайти, відразу після якого робиться сам запит на основі $transport це хук requests-(hook) – requests-requests.before_request
:
add_action( "requests-requests.before_request", function( $parameters, $request, $url ){ /* $parameters https://example.com/wp-json/wp/v1/post/456? */ /* $request Array ( [Authorization] => Basic a2FtYTp1a2RDFTHWjQgYTRVRyBYVjJzIFU5STIgeW5oiu== [Cookie] => my_cookie=cookie_value ) */ /* $url null */ }, 10, 3);
Далі після того, як буде отримано транспорт – Requests::get_transport() і зроблено запит, відпрацює хук requests-requests.before_parse
, на якому можна отримати дані відповіді (втім дані відповіді можна отримати і стандартним чином):
add_action( "requests-requests.before_parse", function( $parameters, $request, $url ){ // $parameters /* HTTP/1.1 200 OK Date: Mon, 23 May 2022 10:02:26 GMT Server: Apache/2.4.41 (Ubuntu) Upgrade: h2,h2c Connection: Upgrade, close X-Cache: miss X-Robots-Tag: noindex Link: ; rel="https://api.w.org/" X-Content-Type-Options: nosniff Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages, Link Access-Control-Allow-Headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type Expires: Wed, 11 Jan 1984 05:00:00 GMT Cache-Control: no-cache, must-revalidate, max-age=0 Allow: GET, POST Content-Length: 1630 Content-Type: application/json; charset=UTF-8 RESPONSE Content ... */ // $request /* https://example.com/wp-json/wp/v1/post/456? */ // $url /* Array ( [Authorization] => Basic a2FtYTp1a2RDFTHWjQgYTRVRyBYVjJzIFU5STIgeW5oiu== [Cookie] => my_cookie=cookie_value ) */ }, 10, 3);
#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 Довільний тип запиту
Крім GET та POST типів запитів, ми можемо відправляти будь-який інший, наприклад, PUT чи DELETE.
Для цього використовуємо функцію wp_remote_request(), яка є основою всіх функцій запитів:
$url = 'http://httpbin.org'; $ args = array ( 'method' => 'PUT', //'method' => 'DELETE' ); $response = wp_remote_request( $url, $args );
#8 Отримання даних про фільм з кінопошуку
Цей приклад показує, як отримати дані фільму з Кінопошуку за вказаним ID фільму.
/** * Отримаємо інформацію про фільм із сайту Kinopoisk * Працює на базі сервісу http://docs.kinopoiskapi.apiary.io/. * * @param int $id ID фільму на сайті * @return string|WP_Error Отримує тіло відповіді, у разі успіху та об'єкт WP_Error при невдачі */ function kinopoisk_get_movie( $id ) { // Параметри GET запиту $params = array( 'filmID' => absint( $id ), ); // Створимо URL з параметрами $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 && ! empty( $response_message ) ) return new WP_Error( $response_code, $response_message ); elseif ( 200 != $response_code ) return new WP_Error( $response_code, 'Невідома помилка'); elseif(! $response_body) return new WP_Error( 'nodata', 'Немає даних про фільм або такого фільму немає в базі' ); else return $response_body; } // Приклад використання функції --- // Запит $res = kinopoisk_get_movie(714888); // Виводимо помилку чи інформацію if ( is_wp_error( $res ) ) echo 'Помилка запиту на IMDB: '. wp_strip_all_tags( $res->get_error_message() ); else echo 'Фільм: "' . esc_html( $res->nameRU ) .'" ('. (int) $res->year .' рік). Рейтинг: '. $res->ratingData->rating; // у результаті отримаємо: // Фільм: "Зоряні війни: Пробудження сили" (2015). Рейтинг: 7.3
#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, див . У цьому випадку, крон завдання перестануть працювати і, можливо, інші внутрішні запити. Щоб вирішити цю проблему, можна встановити дефолтні параметри авторизації для всіх запитів WP HTTP API:
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 = 'login:password'; $auth = & $parsed_args['headers']['Authorization']; if( ! $auth ){ $auth = 'Basic'. base64_encode($log_pass); } } return $parsed_args; }, 10, 2); }
Цей код можна розмістити, наприклад, в must-use плагіни .
#11 Приклад використання проксі
Проксі буде працювати і для https, головне, щоб сам проксі підтримував цей протокол.
$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 ); //define( 'WP_PROXY_BYPASS_HOSTS', 'example.com, *.wordpress.org'); $ 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;
Щоб працювати за протоколами socks5, socks4 потрібно, щоб вони були включені (їх підтримував) CURL сервери, а також потрібно буде хаком вказати їх форсовано. Приклад як це зробити:
add_action( 'http_api_curl', 'wpkama_curl_before_send', 100, 3); function wpkama_curl_before_send( $handle, $request, $url ) { curl_setopt ($ handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); // curl_setopt ($ handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); // curl_setopt ($ handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4A); }
Якщо робити запит стандартно через CURL, він виглядатиме так:
$proxy = '180.168.232.64:8001'; $proxyauth = 'k7yQJa:R9Cdeu'; $url = 'https://example.com'; $ ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt ($ ch, CURLOPT_PROXY, $ proxy); curl_setopt ($ ch, CURLOPT_PROXYUSERPWD, $ proxyauth); curl_setopt ($ ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($ ch, CURLOPT_HEADER, 1); $ curl_scraped_page = curl_exec ($ ch); curl_close($ch); echo $curl_scraped_page;
Чи не дорогі проксі можна купити тут: proxy6.net . Введіть партнерський код RgJ8zx8ycP
та отримайте 5% знижку.
Збереження результату запиту (кешування)
Кешувати результат HTTP запиту потрібно завжди, винятків, мабуть, немає.
Дуже важливим моментом є кешування результату – збереження його кудись. Важливо, тому що без кешу, сторінки сайту будуть генеруватися повільно. Щоб усвідомити всю важливість кешування, потрібно зрозуміти, як все відбувається і чому запити HTTP будуть гальмувати завантаження сторінки.
Справа в тому, що при використанні однієї з функцій HTTP API наш сервер надсилає запит на інший сервер і чекає на відповідь. У період очікування, ніякі PHP операції на нашому сервері не відбуваються – він просто зупиняє виконання скрипту та чекає. Таке очікування може бути досить тривалим. Як правило, таке очікування триває від 0,5 до 5 секунд.
Картина виглядає приблизно так: нам на сервер надійшов HTTP запит: http://example.com/page , і наш сервер має показати сторінку page. Для цього наш сервер починає генерувати цю сторінку, при цьому до виконання будь-яких операцій із генерації сторінки підключаються різні модулі: nginx, apach, PHP, модулі PHP і т.д. Потім починається генерація сторінки, і що складніше сторінка, то довше вона генеруватися. Але при цьому сервер використовує свої дані з оперативної пам’яті, з диска, з бази даних і т.д. всі ці дані він отримує за частки секунди. А тепер уявімо, що в якийсь момент збору даних та генерації сторінки потрібно отримати дані з іншого сервера. У цей момент збирається запит і відправляється, і наш сервер чекає на відповідь: поки запит дійде до іншого сервера, поки там зберуться всі модулі, поки сторінка (відповідь) згенерується і поки він повернеться до нас через всі вузли інтернет з’єднання. Все це за мірками сервера, дуже довго – від 0,5 до 5 секунд.
А тепер відповіді самі собі, чи потрібно зберігати дані запиту на свій сервер, де він може отримати їх за частки секунди?
Інша проблема: «пропускна спроможність»
Теоретично можна обійтися без кешування, якщо у вас на сторінці всього один HTTP запит і сервер, що обробляє цей запит, дозволяє відправляти йому необмежену кількість запитів. Але як правило, якщо ви використовуєте якийсь API, на сервері де обробляються запити, стоїть обмеження кількості запитів у проміжок часу, так звана «пропускна здатність» API. І в якийсь момент віддалений сервер просто не відповість на ваш запит, а ви не отримаєте потрібних даних.
На основі всього сказаного вище можна зробити єдино правильний висновок – кешувати HTTP запити потрібно завжди, а зробити це зовсім не складно.
Кешування в часові налаштування (transients)
Отже, ми надіслали запит, отримали результат і тепер нам потрібно його зберегти. Варіантів куди зберігати результат багато. Наприклад, ми можемо записати результат у файл і наступного разу брати його від туди, або можна зберегти результат у базі даних. Але це вимагатиме додаткового коду, який, як правило, не потрібен, тому що в WordPress вже є відповідні функції:
- set_transient( $transient, $value, $expiration ) – встановлює/оновлює тимчасову опцію.
- get_transient($transient) – отримує значення часової опції.
- delete_transient( $transient ) – Видаляє тимчасову опцію.
Де:
-
$transient
(рядок) (обов’язковий) - Назва часової опції. Максимальна довжина 172 знаки. Очікуються дані з екранованими слешами, очищення слєшів робиться автоматично перед записом до БД. Функція автоматично захищає від ін’єкцій SQL.
-
$value
(змішаний) (обов’язковий) - Значення часової опції. Очікуються дані з екранованими слешами, очищення слєшів робиться автоматично перед записом до БД. Функція захищає від ін’єкцій SQL. Серіалізація, якщо передається масив, також виконується автоматично.
-
$expiration
(рядок/масив/число/об’єкт/логічний) Час життя опції в секундах, починаючи з моменту. Наприклад, щоб записати опцію на день, потрібно вказати
60 * 60 * 24
.Якщо вказати 0 – опція ніколи не відійде і не матиме терміну давності. Виходить це буде звичайна опція, тільки з префіксом
_transient_
у її назві – це означатиме, що вона тимчасова і при чищенні таблиці wp_options її можна буде сміливо видаляти, без ризику, щось зіпсувати.
За замовчуванням: 0
Важливий час для тимчасові настройки (transients). Вони можуть працювати з об’єктним кешуванням. Тобто. вони записуються тимчасово базу даних wp_options . Але якщо на сайті встановлено модуль (плагін) об’єктного кешування, то вони не записуються в БД, а зберігаються у файли об’єктного кешу.
#1 Приклади кешування HTTP запиту до тимчасової опції
Допустимо, через HTTP запит ми отримуємо курс рубля зі стороннього сайту. Щоб прискорити завантаження сторінки, потрібно кешувати отриманий результат на 12 годин.
$transient = 'one_usd_in_rub'; // Назва тимчасової опції $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); // отримаємо дані // якщо статус відповіді 200 – ОК if( wp_remote_retrieve_response_code($resp) === 200 ){ $body = wp_remote_retrieve_body( $resp ); $xml = simplexml_load_string($body); // Парсим XML дані $data = json_decode(json_encode($xml)); // перетворимо на звичайний об'єкт //print_r ($ data); $USD = wp_list_filter( $data->Valute, array('CharCode'=>'USD') ); // Отримаємо лише дані USD $USD = array_shift($USD); /* stdClass Object ( [NumCode] => 840 [CharCode] => USD [Nominal] => 1 [Name] => Долар США [Value] => 64,0165 ) */ // echo "$USD->Nominal $USD->Name дорівнює $USD->Value руб."; //> 1 Долар США дорівнює 64,0165 руб. $usd_in_rub = $USD->Value; } // статус відповіді не 200 – ОК else $usd_in_rub = 'немає даних'; // збережемо курс долара у тимчасову опцію set_transient ($ transient, $ usd_in_rub, $ expiration); } echo 'Курс долара до рубля сьогодні: $1 = '. $usd_in_rub .' руб.'; //> Курс долара до рубля сьогодні: $1 = 64,0165 крб.
Результатом роботи цього коду буде отримання свіжого курсу долара кожні півдня.
Видалення тимчасової опції
Допустимо, ви вже не використовуєте тимчасову опцію і вона не оновлюється, і більше не потрібна. І тут її краще видалити. Тимчасова опція сама по собі не видаляється і буде знаходитись у таблиці wp_options доти, доки ви не очистите всі тимчасові опції спеціальним плагіном або якось ще. Тому її рекомендується видалити вручну, якщо вона вже не використовується. Зробити це можна викликавши один раз такий рядок:
delete_transient('назва_тимчасової_опції');
Кешування в метаполі
Для кешування тимчасові опції підходять завжди, але не завжди це розумно. Для пояснення, представимо таку ситуацію: у вас на сайті є записи, допустимо це фільми і для кожного фільму ви парсіте дані із зовнішнього джерела, наприклад, рейтинг Кінопошуку. Такі дані також потрібно іноді оновлювати, наприклад, раз на тиждень.
У цьому випадку буде логічніше зберігати отримані дані не в таблицю опцій, а в метаполі запису. Тому що так їх буде зручніше отримувати, ми отримаємо можливість сортувати записи за рейтингом (по метаполі), до того ж це працюватиме швидше та грамотніше за кодом. Тому що при встановленні параметра $expiration у часових опцій, для їх отримання робиться 2 простих запити до бази даних, тоді як з метаполями WordPress працює набагато ефективніше.
Але при використанні метаполів вам потрібно вручну перевіряти час життя даних і оновлювати їх при необхідності. Час життя даних можна записувати у схоже за назвою метаполе. До речі, у тимчасових опціях у таблиці wp_options робиться те саме – створюється тимчасова опція і поруч ще одна опція зі схожою назвою та значенням часу, коли опція застаріває.
#1 Приклади кешування HTTP запиту в метаполі
Для прикладу, візьмемо аналогію, де записи фільмів і для отримання даних фільму з Кінопошуку використовується функція kinopoisk_get_movie( $id ) з прикладу вище. Також, припустимо, що для кожного запису ми вже маємо ID фільму на Кінопошуку і знаходиться він у метаполі kinopoisk_id
.
Тепер давайте напишемо код для щотижневого оновлення рейтингу кожного фільму. Для цього створимо функцію, яка виводить рейтинг і одночасно перевіряє, чи не потрібно оновити або отримати дані:
function get_film_rating( $post_id = 0 ){ if( ! $post_id ) $post_id = $GLOBALS['post']->ID; $rating = get_post_meta( $post_id, 'kinopoisk_rating', 1); $up_time = get_post_meta( $post_id, 'kinopoisk_rating_uptime', 1); // Перевіримо, чи потрібно оновити дані if( time() > $up_time + WEEK_IN_SECONDS ){ // час прострочено чи ні даних у метаполях $film_id = get_post_meta( $post_id, 'kinopoisk_id', 1); // пробуємо отримати дані з Кінопошуку $res = kinopoisk_get_movie( $film_id ); // Вдалось отримати дані! if( ! is_wp_error($res) && $res->ratingData->rating ) $rating = $res->ratingData->rating; // Помилка при отриманні даних. Залишимо рейтинг який був або встановимо -1 (немає рейтингу), якщо до цього не було рейтингу else $rating = $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(s) запитів:
/** @var array Map of standard HTTP status code/reason phrases */ $phrases = [ 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', 200 => 'ОК', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-status', 208 => 'Already Reported', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Switch Proxy', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', 418 => 'I'ma teapot', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Unordered Collection', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 451 => 'Unavailable For Legal Reasons', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version no supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 511 => 'Network Authentication Required', ];