Компонент BrowserKit

Дата оновлення перекладу 2024-04-30

Компонент BrowserKit

Компонент BrowserKit симулює поведінку веб-брайзера, дозволяючи вам програмно робити запити, натискати на посилання та відправляти форми.

Установка

1
$ composer require symfony/browser-kit

Note

Якщо ви встановлюєте цей компонент поза додатком Symfony, вам потрібно підключити файл vendor/autoload.phpу вашому коді для включення механізму автозавантаження класів, наданих Composer. Детальніше можна прочитати у цій статті.

Базове застосування

See also

Ця стаття поясннює, як використовувати функції BrowserKit в якості незалежного компоненту в будь-якому PHP-додатку. Прочитайте статтю Функціональні тести Symfony , щоб зрозуміти, як використовувати його у додатках Symfony.

Створення клієнта

Компонент адає лише абстрактного клієнта, і не надає готового до використання бекенду для рівня HTTP. Щоб створити вашого власного клієнта, вам потрібно розширити клас AbstractBrowser та реалізувати метод doRequest(). Цей метод приймає запит та повинен повернути відповідь:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace Acme;

use Symfony\Component\BrowserKit\AbstractBrowser;
use Symfony\Component\BrowserKit\Response;

class Client extends AbstractBrowser
{
    protected function doRequest($request): Response
    {
        // ... перетворити запит у відповідь

        return new Response($content, $status, $headers);
    }
}

Для простої реалізації браузера, заснованого на шарі HTTP, подивіться на HttpBrowser, наданий цим компонентом . Для реалізації, заснованої на HttpKernelInterface, подивіться на HttpClientKernel, наданий компонентом HttpKernel.

Як робити запити

Використовуйте метод request(), щоб робити HTTP-запити. Перші два аргументів - HTTP-метод та запитуваний URL:

1
2
3
4
use Acme\Client;

$client = new Client();
$crawler = $client->request('GET', '/');

Значення, повернене методом request() - це екземпляр класу Crawler, наданий DomCrawler component, який дозволяє програмний доступ та траверсування HTML елементів.

Метод jsonRequest(), який визначає ті ж аргументи, що і метод request(), є ярликом для перетворення параметрів запиту в JSON-рядок та встановлення необхідних HTTP-заголовків:

1
2
3
4
5
use Acme\Client;

$client = new Client();
// це зашифровує параметри як JSON і встановлює необхідні заголовки CONTENT_TYPE та HTTP_ACCEPT
$crawler = $client->jsonRequest('GET', '/', ['some_parameter' => 'some_value']);

Метод xmlHttpRequest(), який визначає ті ж аргументи, що і метод request(), є ярликом для створення AJAX-запитів:

1
2
3
4
5
use Acme\Client;

$client = new Client();
// необхідний заголовок HTTP_X_REQUESTED_WITH додається автоматично
$crawler = $client->xmlHttpRequest('GET', '/');

Натискання на посилання

AbstractBrowser здатний симулювати натискання на посилання. Передайте зміст тексту посиланння, а клієнт виконає необхідний запит HTTP GET, щоб симулювати натискання на посилання:

1
2
3
4
5
6
use Acme\Client;

$client = new Client();
$client->request('GET', '/product/123');

$crawler = $client->clickLink('Go elsewhere...');

Якщо вам потрібен обʼєкт Link, який надає доступ до властивостей посилання (наприклад, $link->getMethod(), $link->getUri()), використайте цей другий метод:

1
2
3
4
// ...
$crawler = $client->request('GET', '/product/123');
$link = $crawler->selectLink('Go elsewhere...')->link();
$client->click($link);

Методи click() та clickLink()
можуть прийняти опціональний аргумент serverParameters. Цей параметр дозволяє вам додавати додаткову інформацію на кшталт заголовків при натисканні на посилання:

1
2
3
4
5
6
7
8
9
10
11
use Acme\Client;

$client = new Client();
$client->request('GET', '/product/123');

// працює як з `click()`...
$link = $crawler->selectLink('Go elsewhere...')->link();
$client->click($link, ['X-Custom-Header' => 'Some data']);

// ... так і з `clickLink()`
$crawler = $client->clickLink('Go elsewhere...', ['X-Custom-Header' => 'Some data']);

Відправка форм

AbstractBrowser також здатний відправлляти форми. Спочатку, оберіть форму, використовуючи будь-яку її кнопку, а потім перевизначте будь-яку з її властивостей (метод, значення поля та ін.), перед її відправкою:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use Acme\Client;

$client = new Client();
$crawler = $client->request('GET', 'https://github.com/login');

// знайти форму з кнопкою 'Log in' та відправити її
// 'Log in' може бути текстовим змістом, id, значенням або імʼям <button> чи <input type="submit">
$client->submitForm('Log in');

// другий необовʼязковий аргумент дозволяє вам перевизначати значення полів форми за замовчуванням
$client->submitForm('Log in', [
    'login' => 'my_user',
    'password' => 'my_pass',
    // щоб завантажити файл, значення має бути абсолютнним шляхом файлу
    'file' => __FILE__,
]);

// ви можете також перевизначити інші опції форми
$client->submitForm(
    'Log in',
    ['login' => 'my_user', 'password' => 'my_pass'],
    // перевизначити HTTP-метод форми за замовчуванням
    'PUT',
    // перевизначити деякі параметри $_SERVER (наприклад, HTTP-заголовки)
    ['HTTP_ACCEPT_LANGUAGE' => 'es']
);

Якщо вам потрібен обʼєкт Form, який надає доступ до властивостей форми (наприклад, $form->getUri(), $form->getValues(), $form->getFields()), використайте цей інший метод:

1
2
3
4
5
6
7
8
9
// ...

// обрати форму та заповнити якісь значення
$form = $crawler->selectButton('Log in')->form();
$form['login'] = 'symfonyfan';
$form['password'] = 'anypass';

// відправити цю форму
$crawler = $client->submit($form);

Обробка користувацьких заголовків

Необовʼязкові HTTP-заголовки, передані методу request(), слідують формату запиту FastCGI (верхній регістр, нижні підкреслення замість дефісів та префікс HTTP_). До збереження цих заголовків у запит, вони переводяться у нижний регістр, HTTP_ прибирається, а нижні підкреслення перетворюються на дефіси.

Якщо ви робите запит до додатку, який має спеціальні правила про регістр або пунктуацію заголовків, перевизначте метод getHeaders(), який має повертати асоційований масив заголовків:

1
2
3
4
5
6
7
8
9
protected function getHeaders(Request $request): array
{
    $headers = parent::getHeaders($request);
    if (isset($request->getServer()['api_key'])) {
        $headers['api_key'] = $request->getServer()['api_key'];
    }

    return $headers;
}

Куки

Вилучення куки

Реалізація AbstractBrowser відображає куки (якщо вони є) через клас CookieJar, який дозволяє вам зберігати та вилучати будь-який куки під час виконання запитів з клієнтом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use Acme\Client;

// Зробіть запит
$client = new Client();
$crawler = $client->request('GET', '/');

// Отримайте cookie Jar
$cookieJar = $client->getCookieJar();

// Отримайте куки за імʼям
$cookie = $cookieJar->get('name_of_the_cookie');

// Отримайте дані куки
$name       = $cookie->getName();
$value      = $cookie->getValue();
$rawValue   = $cookie->getRawValue();
$isSecure   = $cookie->isSecure();
$isHttpOnly = $cookie->isHttpOnly();
$isExpired  = $cookie->isExpired();
$expires    = $cookie->getExpiresTime();
$path       = $cookie->getPath();
$domain     = $cookie->getDomain();
$sameSite   = $cookie->getSameSite();

Note

Ці методи повертають лише куки, строк дії яких не закінчився.

Кільцювання через куки

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use Acme\Client;

// Зробіть запит
$client = new Client();
$crawler = $client->request('GET', '/');

// Отримайте cookie Jar
$cookieJar = $client->getCookieJar();

// Отримайте масив з усіма куки
$cookies = $cookieJar->all();
foreach ($cookies as $cookie) {
    // ...
}

// Отримайте всі значення
$values = $cookieJar->allValues('http://symfony.com');
foreach ($values as $value) {
    // ...
}

// Отримайте всі сирі значення
$rawValues = $cookieJar->allRawValues('http://symfony.com');
foreach ($rawValues as $rawValue) {
    // ...
}

Установка куки

Ви також можете створювати куки та додавати їх у cookie jar, яка може бути впроваджена у контруктор клієнта:

1
2
3
4
5
6
7
8
9
10
use Acme\Client;

// створіть куки та додайте в cookie jar
$cookie = new Cookie('flavor', 'chocolate', strtotime('+1 day'));
$cookieJar = new CookieJar();
$cookieJar->set($cookie);

// створіть клієнта та встановіть куки
$client = new Client(array(), null, $cookieJar);
// ...

Відправка куки

Запити можуть містити куки. Щоб зробити це, використайте аргумент serverParameters методу request(), щоб встановити значення заголовку Cookie:

1
2
3
4
5
6
$client->request('GET', '/', [], [], [
    'HTTP_COOKIE' => new Cookie('flavor', 'chocolate', strtotime('+1 day')),

    // ви також можете передати зміст куки в якості рядка
    'HTTP_COOKIE' => 'flavor=chocolate; expires=Sat, 11 Feb 2023 12:18:13 GMT; Max-Age=86400; path=/'
]);

Note

Всі HTTP-заголовки, встановлені з аргументом serverParameters, повинні мати префікс HTTP_.

Історія

Клієнт зберігає всі ваши запити, дозволяючи вам переміщуватися по вашій історії вперед та назад:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use Acme\Client;

$client = new Client();
$client->request('GET', '/');

// оберіть та натисніть на посилання
$link = $crawler->selectLink('Documentation')->link();
$client->click($link);

// перейдіть назад до домашньої сторінки
$crawler = $client->back();

// перейдіть вперео до сторінки документації
$crawler = $client->forward();

Ви можете видаляти історію клієнта методом restart(). Це також видалить усі куки:

1
2
3
4
5
6
7
use Acme\Client;

$client = new Client();
$client->request('GET', '/');

// перезапустіть клієнта (історія та куки також очищуються)
$client->restart();

Зовнішні HTTP-запити

До цих пір, всі приклади у цій статті припускали, що ви робите внутрішні запити до вашого власного додатку. Однак, ви можете виконати точно такі ж приклади під час HTTP-запитів до зовнішніх веб-сайтів та додатків.

Спочатку встановіть та сконфігуруйте компонент HttpClient. Потім, використайте HttpBrowser, щоб створити клієнта, який буде робити зовнішні HTTP-запити:

1
2
3
4
use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;

$browser = new HttpBrowser(HttpClient::create());

Тепер ви можете використовувати будь-який з методів, показаних у цій статті, щоб вилучати інформацію, натискати на посилання, відправляти форми і т.д. Це означає, що вам більше не потрібно використовувати відповідний веб-краулер або скрейпер, на кшталт Goutte:

1
2
3
4
5
6
7
8
$browser = new HttpBrowser(HttpClient::create());

$browser->request('GET', 'https://github.com');
$browser->clickLink('Sign in');
$browser->submitForm('Sign in', ['login' => '...', 'password' => '...']);
$openPullRequests = trim($browser->clickLink('Pull requests')->filter(
    '.table-list-header-toggle a:nth-child(1)'
)->text());

Tip

Ви також можете використовувати опції клієнта HTTP типу ciphers, auth_basic та query. Вони мають бути передані як аргумент опції за замовчуванням клієнту, який використовується HTTP-браузером.

Робота з HTTP-відповідями

При використанні компонента BrowserKit, вам може знадобитися працювати з відповідями на запити, які ви робили. Щоб зробити це, викличте метод getResponse() обʼєкта HttpBrowser. Цей метод повертає останню відповідь, яку отримав браузер:

1
2
3
4
$browser = new HttpBrowser(HttpClient::create());

$browser->request('GET', 'https://foo.com');
$response = $browser->getResponse();

Якщо ви робите запити, які призводять до відповідей JSON, ви можете використати метод toArray(), щоб перетворити документ JSON на PHP-масив, без необхідності викликати json_decode() чітко:

1
2
3
4
5
$browser = new HttpBrowser(HttpClient::create());

$browser->request('GET', 'https://api.foo.com');
$response = $browser->getResponse()->toArray();
// $response - це PHP-масив дешифрованого змісту JSON