file_get_contents() で外部のURLを指定する

ちょっと急ぎ目で要点だけ残します。

http:// なページを見に行ってたときはそんなに問題になることはなかったと思います。

https:// なページを指定し始めると色々とやることが増えてきます。

経緯

ちなみに私の持ってるサイトでは、参考リンクとして第三者のwebサイトにリンクを貼ることが多々あるのですが、気が付けばページが無くなっていたり、サイトが消滅していたりすることがあります。
いわゆる「リンク切れ」ですね。

過去記事とはいえリンク切れにしておくのも申し訳ないので、自サイト発のリンクは一括で管理していて、定期的に死活監視を行っています。

そんなときに必要になってくるのが本記事の内容です。

PHPから外部ファイルを取得する

CURLを使う方法もあるのですが、気に入ってよく使っているのは次のコードです。

$url = 'https://example.com/';

// コンテキストオプション
$option = [];
$option['http'] = [];
$option['http']['ignore_errors'] = true;
$option['http']['method'] = 'GET';
$option['http']['header'] = 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36';
$option['ssl'] = [];
$option['ssl']['verify_peer'] = false;
$option['ssl']['verify_peer_name'] = false;

// レスポンスヘッダーがここに入ってくる
$http_response_header = null;

// 取得
$html = file_get_contents($url, false, stream_context_create($option));

// ステータスコードをチェック
$matches = null;
$status_code = 0;
if (preg_match('/HTTP\/1\.[0|1|x] ([0-9]{3})/', $http_response_header[0], $matches)) {
	$status_code = $matches[1];
}

// 状態により対処する
if ($status_code === '200') {
    echo 'OK';
    // 問題ないので何もしない
    
} else {
    // 閲覧できなくなってる
    // なんらかの対応が必要
    // 一時的なものか恒久的なものかの判断
}

SSLのエラーが出る

本記事の本題でもあるんですが、SSL関連のエラー対応について始まります。

エラーメッセージ

エラー1

file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed

エラー2

file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages:
error:0A000126:SSL routines::unexpected eof while reading

エラー3

file_get_contents(): php_network_getaddresses: getaddrinfo failed:

1つ目の解決策

おそらく本ページにたどり着くまでに見かけた方法で既に知っていると思いますが、答えは既に上に登場しています。

下記、要点だけ持ってきましたが、コンテキストオプションで、verify_peer と verify_peer_name を false ・・・ つまり「エラーが出ても無視してね」って指定しましょうってことです。

// コンテキストオプション
$option = [];
$option['ssl'] = [];
$option['ssl']['verify_peer'] = false;
$option['ssl']['verify_peer_name'] = false;

// 取得
$html = file_get_contents($url, false, stream_context_create($option));

ここで解決しているならOKです。

2つ目の解決策

そもそもPHPとOpenSSLの設定はきちんと出来てる?
ってのが2つ目です。

確認していきましょう。

phpinfo() を使います。

<?php
phpinfo();
[/SC_CODE]



<p>よく見かけるいつものやつです。<br>上記2行だけを書いた php ファイルを用意して、サーバに設置します。</p>



<p>CTRL + F でブラウザの検索機能から「openssl」で検索して移動します。</p>



<figure class="wp-block-image size-full"><img src="https://aulta.co.jp/wp-content/uploads/2022/04/2022-04-14_16h29_14.png" alt="" class="wp-image-9606"/></figure>



<p>「openssl.cafile」を確認します。<br>上図のように「no value」になっていれば対応が必要です。</p>



<p>ここでは、CentOS Stream 9 でのお話しになるのでOSによっては異なるかもですが、基本的な考え方は同じです。</p>



<p>画面の上のほうに戻って、php.ini のファイルがどこにあるか確認しておきます。</p>



<figure class="wp-block-image size-full"><img src="https://aulta.co.jp/wp-content/uploads/2022/04/2022-04-14_16h50_49.png" alt="" class="wp-image-9610"/></figure>



<p>赤枠上段「Loaded Configuration File」が読み込まれている ini ファイルです。</p>



<p>今回は、グローバルに反映させたいので赤枠下段「/etc/php.d」の下にファイルをつくることにします。<br>このあたりは状況によりどの ini ファイルを編集するか調整してください。</p>



<p>で、php.iniの編集の前に下記のファイルが存在していることを確認しておきます。</p>



ls -la /etc/ssl/certs/ca-bundle.crt
普通に php.ini を編集する場合
今回、私が行う方法とは異なりますが、一般的な方法です。
php.ini を次のように編集します。
※ php.ini の最終行まで移動してちょっと戻ったあたりにあります。
[openssl]
; The location of a Certificate Authority (CA) file on the local filesystem
; to use when verifying the identity of SSL/TLS peers. Most users should
; not specify a value for this directive as PHP will attempt to use the
; OS-managed cert stores in its absence. If specified, this value may still
; be overridden on a per-stream basis via the "cafile" SSL stream context
; option.
openssl.cafile=/etc/ssl/certs/ca-bundle.crt

openssl.cafile の先頭の ; を外して、「/etc/ssl/certs/ca-bundle.crt」を加えます。

グローバルに反映させる場合

vi /etc/php.d/90-openssl.ini

ファイル名の「90-openssl.ini」は、他に openssl と名の付いたファイルがあればそちらでも良いです。

openssl.cafile=/etc/ssl/certs/ca-bundle.crt

上記1行を加えます。

httpd を再起動します。

systemctl restart httpd.service

phpinfo のページに戻って再読込みすると、「openssl.cafile」に反映されていることが確認できます。

Windows PHPの場合

Windows の場合は下記ページをご覧ください。
https://aulta.co.jp/technical/server-build/windows10/php/php8-1-5#phpini-2

初期設定 > php.ini の設定変更 > opensslの証明書

ちなみに CURL

ちなみに curl で同じようなエラーが出てる場合も同じです。

phpinfo() からは「curl.cainfo」をご確認ください。

openssl と同じように php.ini から▼のようになります。

curl.cainfo=/etc/ssl/certs/ca-bundle.crt

file_get_contents(): php_network_getaddresses: getaddrinfo failed:

PHP 8.1 の頃から(もしかすると PHP 8.0 から)見かけるようになったエラーです。

こちらについては、curl を使うような仕様変更も重なったことで深入りしないままになってます。

また機会があれば調査したいと思います。

感覚的にですが、PHPのバージョンっていうより、opensslが怪しそうな匂いがします。