Windowsユーザーの補助になればとの思いで綴りました。
環境
- Windows 11
- PHP 8.1.6
- Apache 2.4
きっかけ
Slack APIのchat.postMessage
を使ってlocalhostのApacheから送信しようとした。
function send_slack(string $text): void
{
// SlackbotのtokenとchannelIDを取得
$token = 'xoxb-XXXXXXXXXXXXXXXXXXXXX';
$channel_id = 'XXXXXXXXXXX';
// クエリ
$query = 'channel='.$channel_id.'&text='.$text;
// APIリクエストのヘッダーを作成
$headers = [
'Authorization: Bearer ' . $token
];
// cURLを使用してAPIリクエストを送信
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://slack.com/api/chat.postMessage');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
// curl_setopt($ch, CURLOPT_POSTFIELDS, "channel=$channel_id&text=$text");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
curl_close($ch);
// APIレスポンスを返す
// echo $response;
return;
}
実行すると、以下のエラーが発生した。
Fatal error: Call to undefined function curl_init() in ...
確かにcurlの設定してなかった。やったろやないか、ということで取り掛かったのだが、ヒットした記事にWindowsのものがあまりなく、Linuxが大多数だったので小一時間悩んだ。
試したこと
コマンドプロンプトからモジュール一覧を表示
php -m
をコマンドプロンプトで実行する。
C:\Users\username>php -m
[PHP Modules]
bcmath
calendar
Core
ctype
curl
date
dom
filter
hash
iconv
json
libxml
mbstring
mysqlnd
openssl
...
[Zend Modules]
curlが項目にあるが、機能していないということはモジュールの一部が正しく読み込めていないのだろう。
ということで色々試す。
phpの設定を確認する
phpinfo()でPHPの設定を出力したとき、有効化されていれば下図のようなcurlの項目が出る。下図は有効化した現在だから表示されているが、エラー発生時はこの項目が無かった。
ということは、読み込みに何らかの支障が出ているのだろう。
php.iniの確認
確認項目は次の通りである。
-
extension_dir
がPHPのext
ディレクトリを指定している。 -
extension=curl
のコメントアウトを解除している。openSSLのモジュールもついでに確認。
curl、openSSLをインストール
curlはダウンロード済み。openSSLは64bit版(軽量)をダウンロードしたのでC:\Program Files
以下にインストール。詳細は省略します。
curlモジュールの依存関係を調べる
PHPに同梱されているdeplister.exe
を使う。コマンドプロンプトでPHPを入れているディレクトリに飛んでから、以下のコマンドを実行する。
C:\PHP>deplister.exe ext\php_curl.dll
結果がこちら。
C:\PHP>deplister.exe ext\php_curl.dll
php8ts.dll,OK
libcrypto-1_1-x64.dll,OK
libssl-1_1-x64.dll,OK
CRYPT32.dll,OK
WLDAP32.dll,OK
Normaliz.dll,OK
libssh2.dll,OK
nghttp2.dll,OK
KERNEL32.dll,OK
WS2_32.dll,OK
VCRUNTIME140.dll,OK
api-ms-win-crt-heap-l1-1-0.dll,OK
api-ms-win-crt-stdio-l1-1-0.dll,OK
api-ms-win-crt-string-l1-1-0.dll,OK
api-ms-win-crt-runtime-l1-1-0.dll,OK
api-ms-win-crt-convert-l1-1-0.dll,OK
api-ms-win-crt-filesystem-l1-1-0.dll,OK
api-ms-win-crt-time-l1-1-0.dll,OK
api-ms-win-crt-utility-l1-1-0.dll,OK
OK
でなくNOT_FOUND
などになっていたらモジュールが無かったり、破損していたりすることがある。openSSLやVisual C++ 再頒布可能パッケージなどを整えるとこれらのファイルは揃うはず。
大体の記事がここまでで解決している(?)ようだったが、私は解決しなかった。ここから色々調べた結果、次の方法で動作するようになった。
【結論】httpd.confに追記する
ファイル末尾に以下の記述を追加した。
# cURL
LoadFile "C:\PHP\libssh2.dll"
LoadFile "C:\PHP\libcrypto-1_1-x64.dll"
LoadFile "C:\PHP\libssl-1_1-x64.dll"
これは正攻法なのだろうか…と思いつつ、追記後Apacheを再起動すると、phpinfo()にもcurlの項目が追加され、Slackにも送信ができるようになった。これにて解決!
と思いきや、もう一波あった
Fatal error: Uncaught GuzzleHttp\Exception\RequestException: cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://oauth2.googleapis.com/token in ...
これはSlack APIではなく、Google Sheet APIを叩いたときに起こっているようだ。対処法は次の2つがあるらしい。
- SSL証明書の検証を無効化する
- 正しいルート証明書を設定する
1はセキュリティ的によろしくないだったので、2を試した。
ルート証明書を設定する
今回は提供されている証明書を利用した。自己産の証明書を生成する方法は他の方の記事を参照。以下からcacert.pem
をダウンロードする。
cacert.pemを適当な場所に設置し、php.ini
に証明書のパスを追記する。私はopenSSLのフォルダ内に設置したため、以下のようになる。
curl.cainfo = "C:\Program Files\OpenSSL-Win64\cacert.pem"