Help us understand the problem. What is going on with this article?

今時のPHP HTTPクライアントのGuzzleを使ってみた

はじめに

LaravelでHTTPリクエストを投げたかったのでGuzzleを使ってみました。
以前ですとPEARのHTTP_Request2を使っていましたが、あまり最近使われていないようなので新しいパッケージを使ってみました。
HTTP_Request2は長年使ってまして、使いやすくて好きです。cURLも悪くはないのですが…。
Laravelで使いましたが、もちろん生のPHPでも使えます。

インストール

今回はLaravelで使ってみました。生のPHPでもOKです。

$ laravel new hogeproject
$ cd hogeproject
$ composer require guzzlehttp/guzzle
Using version ^6.2 for guzzlehttp/guzzle
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)

コントローラの作成

$ php artisan make:controller HogeController

リクエストの作成

$ php artisan make:controller HogeController
routes/web.php
Route::get('/basic_request', 'HogeController@basic_request');
Route::get('/with_headers', 'HogeController@with_headers');

GET

GETリクエスト

app/Http/Controllers/HogeController.php
public function basic_request() {
    $base_url = 'http://example.com';
    $client = new \GuzzleHttp\Client( [
      'base_uri' => $base_url,
    ] );
    $path = '/index.html';
    $response = $client->request( 'GET', $path,
     [
       'allow_redirects' => true,
     ] );
    $response_body = (string) $response->getBody();
    echo $response_body;
}

ステータスコード取得

public function get_http_status_code() {
    $base_url = 'http://example.com';
    $client = new \GuzzleHttp\Client( [
        'base_uri' => $base_url,
    ] );

    $path = '/index.html';
    $response = $client->request( 'GET', $path,
        [
            'allow_redirects' => true,
        ] );
    $response_body = (string) $response->getStatusCode();
    echo $response_body;
}

JSONで送る

$response = $client->request('PUT', '/put', ['json' => ['foo' => 'bar']]);

requestのオプション引数にjsonで配列を渡すと良い感じにしてくれます。
(おそらく)json_encode(), Conent-Type: application/jsonの指定を行ってくれます。

http://docs.guzzlephp.org/en/latest/request-options.html#json

リクエストヘッダー指定

リファラーなどのリクエストヘッダーを指定する場合には配列を渡します。

app/Http/Controllers/HogeController.php
public function with_headers() {
    $base_url = 'http://example.com';
    $client = new \GuzzleHttp\Client( [
        'base_uri' => $base_url,
    ] );

    $path = '/index.html';
    $headers = [
        'Origin'                    => 'http://www.example.com',
        'Accept-Encoding'           => 'gzip, deflate, br',
        'Accept-Language'           => 'ja,en-US;q=0.8,en;q=0.6',
        'Upgrade-Insecure-Requests' => '1',
        'User-Agent'                => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36',
        'Content-Type'              => 'application/x-www-form-urlencoded',
        'Accept'                    => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Cache-Control'             => 'max-age=0',
        'Referer'                   => 'http://www.example.com',
        'Connection'                => 'keep-alive'
    ];
    $response = $client->request( 'GET', $path,
        [
            'allow_redirects' => true,
            'headers'         => $headers,
            //'form_params'     => $form_params,
        ] );
    $response_body = (string) $response->getBody();
    echo $response_body;
}

GETでクエリストリングを指定

http://docs.guzzlephp.org/en/latest/quickstart.html#query-string-parameters

queryに配列を指定するとクエリストリングを指定できるようです。

public function get_with_params() {
    $base_url = 'http://example.com';
    $client = new \GuzzleHttp\Client( [
        'base_uri' => $base_url,
    ] );

    $path = '/index.html';
    $dev_null = $client->get($path,
        [
            'query' => ['foo' => 'bar'],
            'on_stats' => function (\GuzzleHttp\TransferStats $stats) use (&$url) {
                $url = $stats->getEffectiveUri();
            }
        ] )->getBody()->getContents();
    echo $url;
}

404を許容する書き方

404の場合には例外を投げられてしまいます。
この例外を回避する方法です。

404だったときのエラーメッセージ
 [GuzzleHttp\Exception\ClientException]
  Client error: `GET http://www.l2tp.org/index.html` resulted in a `404 Not Foun
  d` response:
  <!DOCTYPE html>
  <html lang="ja" class="no-js no-svg">
  <head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width (truncated...)

http_errorsをfalseにする

@bootjp さんに教えてもらいました。ありがとうございます!
リクエストを投げる際にhttp_errorsというオプションをfalseにすると例外を投げられないようです。

$client->request('GET', '/status/500');
// Throws a GuzzleHttp\Exception\ServerException

$res = $client->request('GET', '/status/500', ['http_errors' => false]);
echo $res->getStatusCode();
// 500

falseに設定すると400番台、500番台などのHTTPエラーを無効化します。デフォルトでは例外が飛ばされます。

Set to false to disable throwing exceptions on an HTTP protocol errors (i.e., 4xx and 5xx responses). Exceptions are thrown by default when HTTP protocol errors are encountered.

http://docs.guzzlephp.org/en/stable/request-options.html#http-errors

例外を拾う

もう一つの方法はClientExceptionで例外を拾います。
投げられる例外のClientExceptionはResponseと同じような使い方ができます。

try {
    $client->request('GET', 'https://github.com/_abc_123_404');
} catch (ClientException $e) {
    echo Psr7\str($e->getRequest());
    echo Psr7\str($e->getResponse());
}

http://docs.guzzlephp.org/en/latest/quickstart.html

もしくはレスポンスを使う場合、getResponseを受けて渡すと良いようです。

try {
    $client->request('GET', 'https://github.com/_abc_123_404');
} catch (ClientException $e) {
    $response = $e->getResponse();
}
  echo $response->getStatusCode();

リクエストを確認する

デバッグを行うときにそもそも思った通りのリクエストが投げれているか、ということを確認したい場合があります。
そういう場合の確認方法です。

リクエストしたbodyの確認

上記とは変わってbodyの確認を行います。
どういったリクエストが送られているか確認するときに使います。

public function send_request_with_dump() {
    $base_url = 'http://example.com';
    $client = new \GuzzleHttp\Client( [
        'base_uri' => $base_url,
    ] );

    $path = '/index.html';

    $stack = HandlerStack::create();
    // my middleware
    $stack->push(Middleware::mapRequest(function (RequestInterface $request) use (&$contentsRequest) {
    $contentsRequest = (string) $request->getBody();
      var_dump($contentsRequest);
      return $request;
    }));

    $dev_null = $client->request( 'GET', $path,
        [
              'handler' => $stack,
        ] )->getBody()->getContents();
    echo $url;
}

How do I get the body of SENT data with Guzzle PHP?
http://stackoverflow.com/a/34911017/2405335

ミドルウェアの機構を使ってリクエスト時に関数を実行します。戻り値でのとり方はわからなかったので参考先通りにvar_dump()しています。

リクエストヘッダの確認

リクエストしたヘッダの確認です。レスポンスヘッダじゃなくてリクエストしたヘッダです。

$YourGuzzleclient=new Client();
$YourGuzzleclient->request('POST', '{Your url}',
  ['debug'=>true,'otheroptions'=>array()]
);

http://stackoverflow.com/a/39239978/2405335

リクエストしたURLの確認

ちょっと書き方が新しいですね。on_statsTransferStatusを受けるクロージャを渡すと、その中でurlを取得できるようです。
useと参照渡しで外側でも参照できます。JavaScriptっぽいですね。
PHPの内部で変数などを実行した際にちゃんと送れているか、というデバッグに使いました。

public function get_requested_url() {
    $base_url = 'http://example.com';
    $client = new \GuzzleHttp\Client( [
        'base_uri' => $base_url,
    ] );

    $path = '/index.html';
    $dev_null = $client->request( 'GET', $path,
        [
            'on_stats' => function (\GuzzleHttp\TransferStats $stats) use (&$url) {
                $url = $stats->getEffectiveUri();
            }
        ] )->getBody()->getContents();
    echo $url;  // http://example.com/index.html
}

参考: How to read the response effective URL in Guzzle ~6.0

参考

Using GuzzleHttp with Laravel
Guzzle で Twitter REST API を叩く

yousan
自動化が好きです。CI/CDのCDが好きです。
https://floatingweed.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした