cURL

導入

Windowsでは、

  • libeay32.dll
  • ssleay32.dll

の2つのファイルにPATHが通っていなければなりません。これらのファイルは既定ではphp.exeと同一のディレクトリにあります。もしcURLの関数の呼び出し時に「PHP Fatal error: Call to undefined function curl_init()」のようにエラーとなるならば、このPATHの設定が原因です。PHP: インストール手順 - Manual

基本的な使用方法

cURLは、基本的に次の手順で使用します。

  1. セッションの初期化 … curl_init()
  2. オプションの設定 … curl_setopt()
  3. 転送の実行 … curl_exec()
  4. セッションの終了 … curl_close()
PHP: 基本的な curl の使用法 - Manual

同一のクエリでも異なる結果を返す場合、キャッシュの有効期限を指示するようにします。さもなくばブラウザなどのキャッシュが参照されてしまい、サーバにリクエストされないことがあります。

file_get_contents()との比較

file_get_contents()ならば、

$response = file_get_contents( $url );

$urlの内容を取得できます。curlでこれと同じ結果を得るには、

$ch = curl_init( $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, TRUE );

$response = curl_exec( $ch );
curl_close( $ch );

のようにします。

file_get_contents()のリクエスト先にはファイル名を使用できますが、curl_init()のそれはつねにURLです。よってホスト名から指定する必要があり、さもなくばcurl_error()では「Could not resolve host: xx; Host not found」というエラーとなります。

転送オプション (curl_setopt)

curl_setopt()で、転送時のオプションを設定できます。

bool curl_setopt ( resource $ch , int $option , mixed $value )
PHP: curl_setopt - Manual

$optionには下表の値を、$valueにはそのオプションの値を指定します。

オプションの値 (一部)
オプション 説明
CURLOPT_RETURNTRANSFER TRUEならば、curl_exec()の戻り値が文字列となる。さもなくばデータをそのまま出力する。
CURLOPT_FAILONERROR TRUEならば、HTTPステータス コードが400以上のとき処理失敗と判断する。

既定では、ステータス コードの値を無視してページの内容を取得する。

CURLOPT_FOLLOWLOCATION TRUEならば、"Location: "ヘッダの内容へリダイレクトする。

※CURLOPT_MAXREDIRSで、リダイレクトする回数の上限を指定する。さもなくば無限にリダイレクトする。

CURLOPT_MAXREDIRS リダイレクトする回数の上限 (CURLOPT_FOLLOWLOCATIONとあわせて使用)
CURLOPT_TIMEOUT cURL関数がタイムアウトするまでの秒数
CURLOPT_USERAGENT "User-Agent: "ヘッダの内容
CURLOPT_COOKIE "Cookie: " ヘッダの内容
CURLOPT_REFERER "Referer: "ヘッダの内容
CURLOPT_HTTPHEADER 任意のHTTPヘッダ。ヘッダの情報はarray( 'Content-Type: text/plain', 'Content-Length: 100' )のように、ヘッダをそれぞれ文字列で指定する。
PHP: curl_setopt - Manual
設定例
目的 $optionの値 $valueの値
gzip CURLOPT_ENCODING 'gzip, deflate'
ETag CURLOPT_HTTPHEADER array( 'If-None-Match: 123' )

設定するオプションが大量にある場合には、それらを配列で一括して設定できるcurl_setopt_array()があります。

bool curl_setopt_array ( resource $ch , array $options )
PHP: curl_setopt_array - Manual

エラーの確認 (curl_error)

直近に発生したエラーについて、curl_error()によって文字列でその情報を取得できます。

string curl_error ( resource $ch )
PHP: curl_error - Manual
$response = curl_exec( $ch );
if( $response === FALSE )
{
    // エラー文字列を出力する
    echo curl_error( $ch );
}

エラー発生の判定

エラー発生の判定は、curl_exec()の戻り値で行えます。しかしオプションでCURLOPT_RETURNTRANSFERを指定していると、成功時には文字列が返されるため

$response = curl_exec( $ch );
if( ! $response )

のようにエラーを判定しようとすると、文字列の'0'や空の文字列が返されたときに誤判定してしまいます。よって前述のように比較演算子 (===) を用いて、FALSEと厳密に判定するようにします。

メソッド

GETメソッド

既定でGETのため、これを使用するのに特別な指定は必要ありません。クエリもURLに含めるだけです。

$params = array( 'data1'=>123, 'data2'=>'foo' );

$ch = curl_init( $url.'?'.http_build_query( $params ) );

curl_exec( $ch );
curl_close( $ch );

POSTメソッド

POSTメソッドを使用するには、CURLOPT_POSTFIELDで送信するデータを指定します。

$params = array( 'data1'=>123, 'data2'=>'foo' );

$ch = curl_init( $url );
curl_setopt( $ch, CURLOPT_POSTFIELDS, $params );

curl_exec( $ch );
curl_close( $ch );

CURLOPT_POSTFIELDを指定するとCURLOPT_POSTも暗黙的に指定されるため、これを別に指定する必要はありません。逆にこれをFALSEとしてもその指定は無視されるため、POSTを用いないならばCURLOPT_POSTFIELDを削除します。CURLOPT_POSTFIELDS

Content-Typeの指定

curl_setopt( $ch, CURLOPT_POSTFIELDS, $params );

$paramsの型によって、リクエストされるときのContent-Typeが変化します。

引数の型 Content-Type
配列 multipart/form-data
文字列 application/x-www-form-urlencoded
multipart/form-data

パラメータの名前と値を「'name'=>value」とした連想配列とします。このとき値をURLエンコードする必要はありません。

$params = array( 'a'=>'abc', 'b'=>100, 'c'=>'&' );
POST /test.php HTTP/1.1
Host: localhost
Accept: */*
Content-Length: 256
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------aaaaaaaaaaaa

配列でデータを渡すとき、その要素の文字列は「@」で始めてはなりません。それはファイルを指定するときの書式であり、そのファイルの指定が正しくなければ通信に失敗します。そしてcurl_error()では「failed creating formpost data」または「couldn't open file "xx"」が返されます。

フィールドを配列で渡すときは、curl_setopt()で

curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/x-www-form-urlencoded' ) );

のようにヘッダを明示的に指定しても、リクエストは

Content-Type: application/x-www-form-urlencoded; boundary=----------------------------xxxxxxxxxxxx

のようにmultipart/form-dataの形式になります。

application/x-www-form-urlencoded

パラメータの名前と値を「name=value」とした文字列とします。値はURLエンコードし、複数のパラメータは&で連結します。

$params = 'a=abc&b=100&c='.rawurlencode( '&' );
POST /test.php HTTP/1.1
Host: localhost
Accept: */*
Content-Length: 256
Content-Type: application/x-www-form-urlencoded

ファイルの送信

ファイルを送信するには、ファイル名の先頭を@としてフルパスで指定します。そのときファイル タイプを明示するには、ファイル名の後に「;type=MIME Type」の形式で記述します。PHP: curl_setopt - Manual

データは必ず配列でなければなりません。

ファイルの指定に誤りがあるときは、curl_error()で「failed creating formpost data」というエラーとなります。

$params = array( 'foo'=>'@/home/user/test.png' );
$params = array( 'foo'=>'@C:/test.png' ); // Windows環境でのローカル

ファイルを受信する側は、$_FILESでデータにアクセスできます。

PHP: ファイルアップロードの処理 - Manual

HEADメソッド

HEADメソッドを使用するには、curl_setopt()でCURLOPT_NOBODYをTRUEと指定します。こうすることで出力から本文が削除され、メソッドはHEADとなります。

curl_setopt( $ch, CURLOPT_NOBODY, TRUE );

DELETEメソッド

DELETEメソッドを使用するには、curl_setopt()でCURLOPT_CUSTOMREQUESTを'DELETE'と指定します。

$ch = curl_init( $url );

curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'DELETE' );

curl_exec( $ch );
curl_close( $ch );

通信に関する情報の取得 (curl_getinfo)

curl_getinfo()で、直近の通信に関する情報を取得できます。

mixed curl_getinfo ( resource $ch [, int $opt = 0 ] )
PHP: curl_getinfo - Manual

$optで取得する情報を指定できます。それを省略すると、すべての値が配列で返されます。

なお取得対象のcURLハンドルがすでに閉じられていると、Warning: curl_getinfo(): 2 is not a valid cURL handle resourceのように警告され処理に失敗します。

戻り値の名前 引数$optの定数 説明
url CURLINFO_EFFECTIVE_URL 直近の有効なURL。通常はリクエストしたURLが返されるが、リダイレクトされた場合にはその転送先のURL
content_type CURLINFO_CONTENT_TYPE 要求されたドキュメントのContent-Type。NULLはサーバが適切なContent-Typeヘッダを返さなかったことを示す
http_code CURLINFO_HTTP_CODE 最後に受け取ったHTTPステータスコード (HTTP status code)
header_size CURLINFO_HEADER_SIZE 受信したヘッダのサイズ
request_size CURLINFO_REQUEST_SIZE 発行されたリクエストのサイズ (現在はHTTPリクエストの場合のみ)
filetime CURLINFO_FILETIME ドキュメントを取得するのにかかった時間。取得できなかった場合は-1
ssl_verify_result CURLINFO_SSL_VERIFYRESULT CURLOPT_SSL_VERIFYPEERを設定したときに要求される、SSL証明書の認証結果
redirect_count    
total_time CURLINFO_TOTAL_TIME 直近の伝送にかかった、秒数
namelookup_time CURLINFO_NAMELOOKUP_TIME 名前解決が完了するまでにかかった、秒数
connect_time CURLINFO_CONNECT_TIME 接続を確立するまでにかかった、秒数
pretransfer_time CURLINFO_PRETRANSFER_TIME 開始からファイル伝送がはじまるまでにかかった、秒数
size_upload CURLINFO_SIZE_UPLOAD アップロードされた、バイト数
size_download CURLINFO_SIZE_DOWNLOAD ダウンロードされた、バイト数
speed_download CURLINFO_SPEED_DOWNLOAD 平均のダウンロード速度
speed_upload CURLINFO_SPEED_UPLOAD 平均のアップロード速度
download_content_length CURLINFO_CONTENT_LENGTH_DOWNLOAD ダウンロードされるサイズ。Content-Lengthフィールドの内容を取得する
upload_content_length CURLINFO_CONTENT_LENGTH_UPLOAD アップロードされるサイズ
starttransfer_time CURLINFO_STARTTRANSFER_TIME 最初のバイトの伝送がはじまるまでの、秒数
redirect_time CURLINFO_REDIRECT_TIME 伝送が始まるまでのリダイレクト処理の、秒数
certinfo    
request_header CURLINFO_HEADER_OUT 送信したリクエスト文字列

※この値を取得するには、事前に

curl_setopt($ch, CURLINFO_HEADER_OUT, TRUE);

としておく必要がある。

パラメータ - PHP: curl_getinfo - Manual

第2引数で取得する情報を指定すると、

$info = curl_getinfo( $ch, CURLINFO_EFFECTIVE_URL );

echo $info;
// http://www.yahoo.com/

のように結果を得られ、指定しないと次のようになります。

$info = curl_getinfo( $ch );

print_r( $info );
// Array
// (
//     [url] => http://www.yahoo.com/
//     [content_type] => text/html;charset=utf-8
//     [http_code] => 200
//     [header_size] => 2402
//     [request_size] => 52
//     [filetime] => -1
//     [ssl_verify_result] => 0
//     [redirect_count] => 0
//     [total_time] => 2.262
//     [namelookup_time] => 0.031
//     [connect_time] => 0.171
//     [pretransfer_time] => 0.171
//     [size_upload] => 0
//     [size_download] => 226941
//     [speed_download] => 100327
//     [speed_upload] => 0
//     [download_content_length] => -1
//     [upload_content_length] => 0
//     [starttransfer_time] => 0.593
//     [redirect_time] => 0
//     [request_header] => GET / HTTP/1.1\r\nHost: www.yahoo.com\r\nAccept: */*\r\n\r\n
// )

ヘッダの取得

期待する結果を得られない場合には、ヘッダを確認するのが有効です。

$ch = curl_init( 'http://example.com' );
curl_setopt( $ch, CURLOPT_HEADER, TRUE );
curl_setopt( $ch, CURLINFO_HEADER_OUT, TRUE );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, TRUE );

$response = curl_exec( $ch );

header( 'Content-Type: text/plain; charset=utf-8' );
echo curl_getinfo( $ch , CURLINFO_HEADER_OUT ); // リクエスト ヘッダ
echo $response; // レスポンス ヘッダを最初に含む、すべての結果
curl_close( $ch );

並列通信 (curl_multi)

前処理

resource curl_multi_init ( void )
PHP: curl_multi_init - Manual
int curl_multi_add_handle ( resource $mh , resource $ch )
PHP: curl_multi_add_handle - Manual

実行

curl_multi_exec()で処理を実行します。

int curl_multi_exec ( resource $mh , int &$still_running )
PHP: curl_multi_exec - Manual

$still_runningで、処理中のハンドルの数を確認できます。よってすべての処理の完了を待つには、これが0となるまでループさせます。

データの取得

cURLハンドルからデータを文字列で取得するには、curl_setopt()でCURLOPT_RETURNTRANSFERを指定し、curl_multi_getcontent()で結果を取得します。

string curl_multi_getcontent ( resource $ch )
PHP: curl_multi_getcontent - Manual

これはCURLOPT_RETURNTRANSFERが指定されているときの、curl_exec()の戻り値と同じです。

情報の取得

array curl_multi_info_read (
    resource $mh
    [, int &$msgs_in_queue = NULL ]
    )
PHP: curl_multi_info_read - Manual

取得する結果が存在しないときは、FALSEが返されます。

print_r( curl_multi_info_read( $mh ) );
// Array
// (
//     [msg] => 1
//     [result] => 0
//     [handle] => Resource id #2
// )

['result']は下表の値のいずれかです。その値は定数「CURLE_*」で定義され、その意味はlibcurl - Error Codesにあります。

定数
0 CURLE_OK
1 CURLE_UNSUPPORTED_PROTOCOL
2 CURLE_FAILED_INIT
3 CURLE_URL_MALFORMAT
4 CURLE_URL_MALFORMAT_USER
5 CURLE_COULDNT_RESOLVE_PROXY
6 CURLE_COULDNT_RESOLVE_HOST
7 CURLE_COULDNT_CONNECT
8 CURLE_FTP_WEIRD_SERVER_REPLY
9 CURLE_FTP_ACCESS_DENIED
10 CURLE_FTP_USER_PASSWORD_INCORRECT
11 CURLE_FTP_WEIRD_PASS_REPLY
12 CURLE_FTP_WEIRD_USER_REPLY
13 CURLE_FTP_WEIRD_PASV_REPLY
14 CURLE_FTP_WEIRD_227_FORMAT
15 CURLE_FTP_CANT_GET_HOST
16 CURLE_FTP_CANT_RECONNECT
17 CURLE_FTP_COULDNT_SET_BINARY
18 CURLE_PARTIAL_FILE
19 CURLE_FTP_COULDNT_RETR_FILE
20 CURLE_FTP_WRITE_ERROR
21 CURLE_FTP_QUOTE_ERROR
22 CURLE_HTTP_NOT_FOUND
23 CURLE_WRITE_ERROR
24 CURLE_MALFORMAT_USER
25 CURLE_FTP_COULDNT_STOR_FILE
26 CURLE_READ_ERROR
27 CURLE_OUT_OF_MEMORY
28 CURLE_OPERATION_TIMEOUTED
29 CURLE_FTP_COULDNT_SET_ASCII
30 CURLE_FTP_PORT_FAILED
31 CURLE_FTP_COULDNT_USE_REST
32 CURLE_FTP_COULDNT_GET_SIZE
33 CURLE_HTTP_RANGE_ERROR
34 CURLE_HTTP_POST_ERROR
35 CURLE_SSL_CONNECT_ERROR
36 CURLE_FTP_BAD_DOWNLOAD_RESUME
37 CURLE_FILE_COULDNT_READ_FILE
38 CURLE_LDAP_CANNOT_BIND
39 CURLE_LDAP_SEARCH_FAILED
40 CURLE_LIBRARY_NOT_FOUND
41 CURLE_FUNCTION_NOT_FOUND
42 CURLE_ABORTED_BY_CALLBACK
43 CURLE_BAD_FUNCTION_ARGUMENT
44 CURLE_BAD_CALLING_ORDER
45 CURLE_HTTP_PORT_FAILED
46 CURLE_BAD_PASSWORD_ENTERED
47 CURLE_TOO_MANY_REDIRECTS
48 CURLE_UNKNOWN_TELNET_OPTION
49 CURLE_TELNET_OPTION_SYNTAX
50 CURLE_OBSOLETE
51 CURLE_SSL_PEER_CERTIFICATE
52 CURLE_GOT_NOTHING
53 CURLE_SSL_ENGINE_NOTFOUND
54 CURLE_SSL_ENGINE_SETFAILED
55 CURLE_SEND_ERROR
56 CURLE_RECV_ERROR
57 CURLE_SHARE_IN_USE
58 CURLE_SSL_CERTPROBLEM
59 CURLE_SSL_CIPHER
60 CURLE_SSL_CACERT
61 CURLE_BAD_CONTENT_ENCODING
62 CURLE_LDAP_INVALID_URL
63 CURLE_FILESIZE_EXCEEDED
64 CURLE_FTP_SSL_FAILED
PHP: 定義済み定数 - Manual ※値は連続しています。

後処理

次の3つの処理が必要です。

  1. マルチハンドルからのcURLハンドルの削除
  2. マルチハンドルのクローズ
  3. cURLハンドルのクローズ

マルチハンドルからcURLハンドルを削除するには、curl_multi_remove_handle()を呼び出します。

int curl_multi_remove_handle ( resource $mh , resource $ch )
PHP: curl_multi_remove_handle - Manual
void curl_multi_close ( resource $mh )
PHP: curl_multi_close - Manual

cURLハンドルのクローズは、curl_close()で行います。

サンプルコード

$ch1 = curl_init( $url1 );
$ch2 = curl_init( $url2 );

curl_setopt( $ch1, CURLOPT_RETURNTRANSFER, TRUE );
curl_setopt( $ch2, CURLOPT_RETURNTRANSFER, TRUE );

$mh = curl_multi_init();

curl_multi_add_handle( $mh, $ch1 );
curl_multi_add_handle( $mh, $ch2 );

$running = null;
do
{
    curl_multi_exec( $mh, $running );
} while( $running );

$response1 = curl_multi_getcontent( $ch1 );
$response2 = curl_multi_getcontent( $ch1 );

curl_multi_remove_handle( $mh, $ch1 );
curl_multi_remove_handle( $mh, $ch2 );

curl_multi_close( $mh );

curl_close( $ch1 );
curl_close( $ch2 );

より汎用的には、次のようにします。

$urls = array( $url1, $url2 );

$chs = array();
foreach( $urls as $url ) $chs[] = curl_init( $url );

$mh = curl_multi_init();
foreach( $chs as $ch )
{
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, TRUE );
    curl_setopt( $ch, CURLOPT_TIMEOUT, 1 );

    curl_multi_add_handle( $mh, $ch );
}

$running = null;
do
{
    curl_multi_exec( $mh, $running );
} while( $running );

$responses = array();
foreach( $chs as $ch )
{
    $responses[] = curl_multi_getcontent( $ch );

    curl_multi_remove_handle( $mh, $ch );
    curl_close( $ch );
}
curl_multi_close( $mh );

連続したリクエスト

前述の例ではすべての接続を同時に行おうとしますが、次の例では最大接続数を維持しながら、接続の完了を待って次の接続を行います。

次の関数は指定URLへ順次リクエストを送り、そのレスポンスのHTTPコードを出力します。

function CheckResponse( $urls, $maxConnection )
{
    $mh = curl_multi_init();
    $chs = array();

    $running = 0;
    do
    {
        if( $running < maxConnection && 0 < count( $urls ) )
        {
            $ch = curl_init( array_shift( $urls ) );
            curl_setopt( $ch, CURLOPT_NOBODY, TRUE );
            curl_setopt( $ch, CURLOPT_TIMEOUT, 30 );

            curl_multi_add_handle( $mh, $ch );
            $chs[] = $ch;
        }

        usleep( 1000 );
        curl_multi_exec( $mh, $running );

        foreach( $chs as $key=>$ch )
        {
            $info = curl_getinfo( $ch );
            if( $info[ 'http_code' ] != 0 )
            {
                echo "{$info[ 'http_code' ]} : {$info[ 'url' ]}\n";
                @ob_flush();
                @flush();

                curl_multi_remove_handle( $mh, $ch );
                curl_close( $ch );

                unset( $chs[ $key ] );
            }
        }

    } while( 0 < $running );

    print_r( curl_multi_info_read( $mh ) );
    curl_multi_close( $mh );
}

次のように呼び出します。

$urls = array(
    'http://example.com/1/',
    'http://example.com/2/',
    'http://example.com/3/',
    'http://example.com/4/',
    'http://example.com/5/',
    );
CheckResponse( $urls, 3 );

HTTPS接続

cURLは既定ではすべての認証局 (Certificate Authority : CA) を信頼しないため、HTTPS接続できません。接続を試みても失敗し、curl_error()では

SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

のように返されます。これを解決するには証明書を入手し、それを利用するように転送オプションで指示します。

curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, TRUE );
curl_setopt( $ch, CURLOPT_CAINFO, 'ca.crt' );

もしくは証明書の検証が不要ならば、

curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, FALSE );

として、検証自体を無効にします。

ファイルのダウンロードと保存

curl_setopt()で、CURLOPT_FILEに保存先のファイルのポインタを指定します。

$downloadPath = 'http://example.com/'; // ダウンロードするファイルのURL
$savePath = 'test.txt';                // 保存先のファイルのパス

$ch = curl_init( $downloadPath );
$fp = fopen( $savePath, 'w' );

curl_setopt( $ch, CURLOPT_FILE, $fp );
curl_setopt( $ch, CURLOPT_HEADER, FALSE );

curl_close( $ch );
fclose( $fp );
PHP: 基本的な curl の使用法 - Manual