PHPでHTTPS接続をする
PHPから何かしらのAPIをたたいたりするときに必要じゃないか、ということで、環境作成と接続テストをしてみる。
環境構築
環境は以前に作ったものをベースにする。(Apache httpd 2.2/PHP 5.3)
(実際は、Ubuntuの仮想マシン上でも構築、と思ったのだが、特に設定が必要ない状態だったので割愛)
設定自体は楽なもので、今回使用する環境ではphp.ini内の以下のコメントを外すだけ。
;extension=php_openssl.dll
あと、後述のサンプルを実行するには、URLをファイルとして開くことが許可されていることが必要らしい。
; Whether to allow the treatment of URLs (like http:// or ftp://) as files. ; http://php.net/allow-url-fopen allow_url_fopen = On
自分の環境ではもともとOnになっていたが、そうでない場合は適当に設定変更。
Apache経由で利用する場合は、Apacheを再起動。これで設定は完了。
phpinfo()を見てみると、以下のような記述が見つかるはず。
Registered PHP Streams => php, file, glob, data, http, ftp, zip, compress.zlib, https, ftps, phar, sqlsrv Registered Stream Socket Transports => tcp, udp, ssl, sslv3, sslv2, tls
ちなみに、今回使用するApacheの環境では、以下のようなレスポンスを返す。
- http://localhost/samples2/protocol.txt
- "HTTP"
- https://localhost/samples2/protocol.txt
- "HTTP over SSL"
("samples2"というのに特に意味はない。)
サンプル
今回、以下の4種類の関数を使ったサンプルプログラムを作成。
(注:エラーチェックとか、HTTPへのまじめな対応とかは一切していないので(以下略))
- file_get_contents (PHP 4.3.0以降, 5)
- fopen (PHP 4, 5)
- fsockopen (PHP 4, 5)
- stream_socket_client (PHP 5)
上2つ(file_get_contents、fopen)は、プロトコルを判断して適切に処理してくれるタイプの関数なので、HTTPならばレスポンスボディのみが返される。
下2つ(fsockopen、stream_socket_client)は、トランスポート層レベルを扱うタイプの関数なので、生のHTTPリクエスト/レスポンスをプログラム側で扱う必要がある。
他にpfsockopenというのがあるらしいが、基本はfsockopenと同じなので省略。
<?php function test_file_get_contents($url) { printf("=== %s ===\n", __FUNCTION__); var_dump(file_get_contents($url)); } function test_fopen($url) { printf("=== %s ===\n", __FUNCTION__); $sock = fopen($url, 'rb'); if ($sock) { while (($s = fgets($sock))) { var_dump($s); } fclose($sock); } } function test_fsockopen($host, $port, $path) { printf("=== %s ===\n", __FUNCTION__); $sock = fsockopen($host, $port); if ($sock) { fputs($sock, "GET {$path} HTTP/1.1\r\n"); fputs($sock, "Host: localhost\r\n"); fputs($sock, "\r\n"); $len = 0; while (strlen(trim($s = fgets($sock)))) { if (preg_match("/^Content-Length: /", $s)) { $len = (int)preg_replace("/^Content-Length: /", '', $s); } echo '[H]' . $s; } $s = fread($sock, $len); var_dump($s); fclose($sock); } } function test_stream_socket_client($remote_socket, $path) { printf("=== %s ===\n", __FUNCTION__); $sock = stream_socket_client($remote_socket); if ($sock) { fputs($sock, "GET {$path} HTTP/1.1\r\n"); fputs($sock, "Host: localhost\r\n"); fputs($sock, "\r\n"); $len = 0; while (strlen(trim($s = fgets($sock)))) { if (preg_match("/^Content-Length: /", $s)) { $len = (int)preg_replace("/^Content-Length: /", '', $s); } echo '[H]' . $s; } $s = fread($sock, $len); var_dump($s); fclose($sock); } } test_file_get_contents('http://localhost/samples2/protocol.txt'); test_file_get_contents('https://localhost/samples2/protocol.txt'); echo "\n"; test_fopen('http://localhost/samples2/protocol.txt'); test_fopen('https://localhost/samples2/protocol.txt'); echo "\n"; test_fsockopen('localhost', 80, '/samples2/protocol.txt'); test_fsockopen('ssl://localhost', 443, '/samples2/protocol.txt'); echo "\n"; test_stream_socket_client('tcp://localhost:80', '/samples2/protocol.txt'); test_stream_socket_client('ssl://localhost:443', '/samples2/protocol.txt'); ?>
比較のために、HTTPで通信する場合の処理も一緒に入れてみた。
これを実行すると、以下のような出力が得られる。
=== test_file_get_contents === string(4) "HTTP" === test_file_get_contents === string(13) "HTTP over SSL" === test_fopen === string(4) "HTTP" === test_fopen === string(13) "HTTP over SSL" === test_fsockopen === [H]HTTP/1.1 200 OK [H]Date: Sat, 24 Dec 2011 19:19:14 GMT [H]Server: Apache/2.2.16 (Win32) mod_ssl/2.2.16 OpenSSL/0.9.8o PHP/5.3.3 DAV/2 [H]Last-Modified: Sat, 24 Dec 2011 18:46:38 GMT [H]ETag: "400000009a523-4-4b4daf41d0f50" [H]Accept-Ranges: bytes [H]Content-Length: 4 [H]Content-Type: text/plain string(4) "HTTP" === test_fsockopen === [H]HTTP/1.1 200 OK [H]Date: Sat, 24 Dec 2011 19:19:14 GMT [H]Server: Apache/2.2.16 (Win32) mod_ssl/2.2.16 OpenSSL/0.9.8o PHP/5.3.3 DAV/2 [H]Last-Modified: Sat, 24 Dec 2011 18:46:58 GMT [H]ETag: "500000009a522-d-4b4daf54f0770" [H]Accept-Ranges: bytes [H]Content-Length: 13 [H]Content-Type: text/plain string(13) "HTTP over SSL" === test_stream_socket_client === [H]HTTP/1.1 200 OK [H]Date: Sat, 24 Dec 2011 19:19:14 GMT [H]Server: Apache/2.2.16 (Win32) mod_ssl/2.2.16 OpenSSL/0.9.8o PHP/5.3.3 DAV/2 [H]Last-Modified: Sat, 24 Dec 2011 18:46:38 GMT [H]ETag: "400000009a523-4-4b4daf41d0f50" [H]Accept-Ranges: bytes [H]Content-Length: 4 [H]Content-Type: text/plain string(4) "HTTP" === test_stream_socket_client === [H]HTTP/1.1 200 OK [H]Date: Sat, 24 Dec 2011 19:19:15 GMT [H]Server: Apache/2.2.16 (Win32) mod_ssl/2.2.16 OpenSSL/0.9.8o PHP/5.3.3 DAV/2 [H]Last-Modified: Sat, 24 Dec 2011 18:46:58 GMT [H]ETag: "500000009a522-d-4b4daf54f0770" [H]Accept-Ranges: bytes [H]Content-Length: 13 [H]Content-Type: text/plain string(13) "HTTP over SSL"
単純なGETだけなら上記のどれでもよいが、POSTなどが絡んでくると下2つを使わざるを得ない。(2021/05/30 ウソウソ。上2つでもPOSTはできる。hhelibex.hatenablog.jp)が、SSLの話とは関係なくなるので、ここでは触れない。
参考
http://labs.unoh.net/2007/09/phpssl.htmlSSL通信関係の設定ではまったときに。- (2017/12/06)リンク切れになっていたので・・・