Компонент BrowserKit

Дата обновления перевода 2024-07-03

Компонент 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 отображает cookie (если они есть) через класс CookieJar, который позволяет вам хранить и извлекать любой cookie во время выполнения запросов с клиентом:

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

Эти методы возвращают только cookie, срок действия которых не истёк.

Закольцовывание через куки

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(). Это также удалит все cookie:

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