Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
229
Help us understand the problem. What is going on with this article?
@yousan

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

はじめに

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 を叩く

229
Help us understand the problem. What is going on with this article?
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
yousan
自動化が好きです。CI/CDのCDが好きです。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
229
Help us understand the problem. What is going on with this article?