BrowserKit 组件
BrowserKit 组件模拟 Web 浏览器的行为,允许你以编程方式发送请求、点击链接和提交表单。
安装
1
$ composer require symfony/browser-kit
注意
如果在 Symfony 应用程序之外安装此组件,则必须在代码中引入 vendor/autoload.php
文件,以启用 Composer 提供的类自动加载机制。阅读 这篇文章了解更多详情。
基本用法
另请参阅
本文介绍了如何在任何 PHP 应用程序中将 BrowserKit 功能用作独立组件。阅读 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
{
// ... convert request into a 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 组件 提供,允许以编程方式访问和遍历 HTML 元素。
jsonRequest() 方法定义了与 request()
方法相同的参数,是一个将请求参数转换为 JSON 字符串并设置所需 HTTP 标头的快捷方式。
1 2 3 4 5
use Acme\Client;
$client = new Client();
// this encodes parameters as JSON and sets the required CONTENT_TYPE and HTTP_ACCEPT headers
$crawler = $client->jsonRequest('GET', '/', ['some_parameter' => 'some_value']);
xmlHttpRequest() 方法定义了与 request()
方法相同的参数,是一个发送 AJAX 请求的快捷方式。
1 2 3 4 5
use Acme\Client;
$client = new Client();
// the required HTTP_X_REQUESTED_WITH header is added automatically
$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
参数。此参数允许在点击链接时发送附加信息,例如 headers。
1 2 3 4 5 6 7 8 9 10 11
use Acme\Client;
$client = new Client();
$client->request('GET', '/product/123');
// works both with `click()`...
$link = $crawler->selectLink('Go elsewhere...')->link();
$client->click($link, ['X-Custom-Header' => 'Some data']);
// ... and `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');
// find the form with the 'Log in' button and submit it
// 'Log in' can be the text content, id, value or name of a <button> or <input type="submit">
$client->submitForm('Log in');
// the second optional argument lets you override the default form field values
$client->submitForm('Log in', [
'login' => 'my_user',
'password' => 'my_pass',
// to upload a file, the value must be the absolute file path
'file' => __FILE__,
]);
// you can override other form options too
$client->submitForm(
'Log in',
['login' => 'my_user', 'password' => 'my_pass'],
// override the default form HTTP method
'PUT',
// override some $_SERVER parameters (e.g. HTTP headers)
['HTTP_ACCEPT_LANGUAGE' => 'es']
);
如果你需要 Form 对象,它可以访问表单属性(例如 $form->getUri()
, $form->getValues()
, $form->getFields()
),请使用此其他方法。
1 2 3 4 5 6 7 8 9
// ...
// select the form and fill in some values
$form = $crawler->selectButton('Log in')->form();
$form['login'] = 'symfonyfan';
$form['password'] = 'anypass';
// submit that form
$crawler = $client->submit($form);
自定义 Header 处理
传递给 request()
方法的可选 HTTP headers 遵循 FastCGI 请求格式(大写,下划线代替短划线,并以 HTTP_
为前缀)。在将这些 headers 保存到请求之前,它们会被转换为小写,HTTP_
被剥离,下划线转换为短划线。
如果你正在向对 header 大小写或标点符号有特殊规则的应用程序发送请求,请覆盖 getHeaders()
方法,该方法必须返回 headers 的关联数组。
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;
}
Cookies
检索 Cookies
AbstractBrowser
实现通过 CookieJar 公开 cookies(如果有),它允许你在使用客户端发送请求时存储和检索任何 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;
// Make a request
$client = new Client();
$crawler = $client->request('GET', '/');
// Get the cookie Jar
$cookieJar = $client->getCookieJar();
// Get a cookie by name
$cookie = $cookieJar->get('name_of_the_cookie');
// Get cookie data
$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();
注意
这些方法仅返回尚未过期的 cookies。
循环遍历 Cookies
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;
// Make a request
$client = new Client();
$crawler = $client->request('GET', '/');
// Get the cookie Jar
$cookieJar = $client->getCookieJar();
// Get array with all cookies
$cookies = $cookieJar->all();
foreach ($cookies as $cookie) {
// ...
}
// Get all values
$values = $cookieJar->allValues('http://symfony.ac.cn');
foreach ($values as $value) {
// ...
}
// Get all raw values
$rawValues = $cookieJar->allRawValues('http://symfony.ac.cn');
foreach ($rawValues as $rawValue) {
// ...
}
设置 Cookies
你还可以创建 cookies 并将其添加到 cookie jar 中,该 cookie jar 可以注入到客户端构造函数中。
1 2 3 4 5 6 7 8 9 10
use Acme\Client;
// create cookies and add to cookie jar
$cookie = new Cookie('flavor', 'chocolate', strtotime('+1 day'));
$cookieJar = new CookieJar();
$cookieJar->set($cookie);
// create a client and set the cookies
$client = new Client([], null, $cookieJar);
// ...
发送 Cookies
请求可以包含 cookies。为此,请使用 request() 方法的 serverParameters
参数来设置 Cookie
header 值。
1 2 3 4 5 6
$client->request('GET', '/', [], [], [
'HTTP_COOKIE' => new Cookie('flavor', 'chocolate', strtotime('+1 day')),
// you can also pass the cookie contents as a string
'HTTP_COOKIE' => 'flavor=chocolate; expires=Sat, 11 Feb 2023 12:18:13 GMT; Max-Age=86400; path=/'
]);
注意
使用 serverParameters
参数设置的所有 HTTP headers 都必须以 HTTP_
为前缀。
历史记录
客户端存储你的所有请求,允许你在历史记录中前进和后退。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
use Acme\Client;
$client = new Client();
$client->request('GET', '/');
// select and click on a link
$link = $crawler->selectLink('Documentation')->link();
$client->click($link);
// go back to home page
$crawler = $client->back();
// go forward to documentation page
$crawler = $client->forward();
你可以使用 restart()
方法删除客户端的历史记录。这也将删除所有 cookies。
1 2 3 4 5 6 7
use Acme\Client;
$client = new Client();
$client->request('GET', '/');
// reset the client (history and cookies are cleared too)
$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());
提示
你还可以使用 HTTP 客户端选项,如 ciphers
、auth_basic
和 query
。它们必须作为默认选项参数传递给 HTTP 浏览器使用的客户端。
处理 HTTP 响应
当使用 BrowserKit 组件时,你可能需要处理你发送的请求的响应。为此,调用 HttpBrowser
对象的 getResponse()
方法。此方法返回浏览器收到的最后一个响应。
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 is a PHP array of the decoded JSON contents