跳到主要內容

BrowserKit 組件

編輯此頁面

BrowserKit 組件模擬網頁瀏覽器的行為,讓您能以程式化的方式發出請求、點擊連結和提交表單。

安裝

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 的實作,請參閱 HttpKernel 組件提供的 HttpClientKernel

發出請求

使用 request() 方法發出 HTTP 請求。前兩個引數是 HTTP 方法和請求的 URL。

1
2
3
4
use Acme\Client;

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

request() 方法傳回的值是 DomCrawler 組件提供的 Crawler 類別的實例,可讓您以程式化的方式存取和遍歷 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 引數。此參數允許在點擊連結時發送額外資訊,例如標頭。

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

自訂標頭處理

傳遞給 request() 方法的選用 HTTP 標頭遵循 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;
}

Cookie

取得 Cookie

AbstractBrowser 實作透過 CookieJar 公開 Cookie(如果有的話),讓您在透過用戶端發出請求時,可以儲存和取得任何 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();

注意

這些方法僅傳回尚未過期的 Cookie。

迴圈處理 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;

// 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('https://symfony.dev.org.tw');
foreach ($values as $value) {
    // ...
}

// Get all raw values
$rawValues = $cookieJar->allRawValues('https://symfony.dev.org.tw');
foreach ($rawValues as $rawValue) {
    // ...
}

設定 Cookie

您也可以建立 Cookie,並將其新增至可注入用戶端建構子的 Cookie 容器中。

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

發送 Cookie

請求可以包含 Cookie。若要執行此操作,請使用 request() 方法的 serverParameters 引數來設定 Cookie 標頭值。

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 標頭都必須以 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() 方法刪除用戶端的歷史記錄。這也會刪除所有 Cookie。

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 用戶端選項,例如 ciphersauth_basicquery。它們必須作為預設 options 引數傳遞給 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
本作品,包括程式碼範例,以 Creative Commons BY-SA 3.0 授權條款授權。
目錄
    版本