Компонент BrowserKit

Дата обновления перевода 2021-12-25

Компонент BrowserKit

Компонент BrowserKit симулирует поведение веб-браузера, позволяя вам программно делать запросы, нажимать на ссылки и отправлять формы.

Note

В версиях Symfony до 4.3, компонент BrowserKit мог делать только внутренние запросы к вашему приложению, Начиная с Symfony 4.3, этот компонент может также делать HTTP-запросы к любому публичному сайту при использовании его в комбинации с компонентом HttpClient component.

Установка

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)
    {
        // ... преобразовать запрос в ответ

        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']);

5.3

Метод jsonRequest() был представлен в Symfony 5.3.

Метод 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);

Отправка форм

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

Обработка пользовательских заголовков

5.2

Метод getHeaders() был представлен в Symfony 5.2.

Необязательные 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 по имени
$cookie = $cookieJar->get('name_of_the_cookie');

// Получите данные 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
$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 и добавлять их в cookie jar, которая может быть внедрена в конструктор клиента:

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

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

//создайте клиент и установите cookie
$client = new Client(array(), null, $cookieJar);
// ...

История

Клиент хранит все ваши запросы, позволяя вам перемещаться по вашей истории вперёд и назад:

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', '/');

// перезапустите клиент (история и cookie также очищаются)
$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());