LoginSignup
251
254

More than 3 years have passed since last update.

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

Last updated at Posted at 2017-02-01

はじめに

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

Laravelのラッパー

Laravel 7系ではGuzzleラッパーが提供されているようです。

$response = Http::post('http://test.com/users', [
    'name' => 'Steve',
    'role' => 'Network Administrator',
]);

@macky4 さんありがとうございます。

Laravelだとラッパーのおかけでもっと楽に書けますよ!
https://readouble.com/laravel/7.x/ja/http-client.html

インストール

今回は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でクエリストリングを指定

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の場合には例外を投げられてしまいます。
この例外を回避する2つの方法です。

まずはエラーメッセージについてです。

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.

例外を拾う

もう一つの方法は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());
}

もしくはレスポンスを使う場合、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

並列アクセス

Promiseを使った並列アクセスが可能です。
クロージャを用意し、new Pool()$clientとRequestを行う関数を渡します。

<?php
require_once 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;

$client = new Client();

$requests = function ($urls) {
    for ($i = 0; $i < count($urls); $i++) {
        yield new Request('GET', $urls[$i]);
    }
};

$urls = [
    'http://example.com',
    'http://example.com',
    'http://example.com',
    'http://example.com',
];

$pool = new Pool($client, $requests($urls), [
    'concurrency' => 5,
    'fulfilled' => function (Response $response, $index) {
        // var_dump($response->getHeader('Server'));
        // this is delivered each successful response
    },
    'rejected' => function (RequestException $reason, $index) {
        // this is delivered each failed request
    },
]);

// Initiate the transfers and create a promise
$promise = $pool->promise();

// Force the pool of requests to complete.
$promise->wait();

参考

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

251
254
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
251
254