1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PHP 8.4のcurl_version()['feature_list']は何を解決したのかーー標準関数としてのcurl_has_feature()の検討

1
Last updated at Posted at 2026-07-21

想定読者

この記事は、PHPのcurl拡張でHTTP/2やHTTP/3の対応状況を確認したことがあり、PHP 8.4のfeature_listが何を改善したのか知りたい人を想定しています。

あわせて、ユーザーランド関数とPHP標準APIの責任境界や、curl_has_feature()を標準化する価値に関心がある人も対象です。

コマンドラインでcurl --versionを実行すると、そのcurl環境で利用できるプロトコルや機能を確認できます。

$ curl --version

Protocols: dict file ftp ftps http https ...
Features: alt-svc AsynchDNS brotli HTTP2 HTTP3 IPv6 SSL ...

PHPからcurlを使う場合も、知りたいことは基本的に同じです。

このPHP環境で使われているlibcurlは、HTTP/2やHTTP/3に対応しているのか。

ところがPHP 8.3以前では、この問いに答えるためにビットマスクを読む必要がありました。

PHP 8.3以前はビットマスクを読んでいた

従来のHTTP/3判定は、次のように書けます。

$version = curl_version();

$supportsHttp3 =
    defined('CURL_VERSION_HTTP3')
    && (($version['features'] & CURL_VERSION_HTTP3) !== 0);

curl_version()が返すfeaturesは、CURL_VERSION_*定数を組み合わせた整数です。

このコードを書くには、次の前提を理解する必要があります。

  • featuresはビットマスクである
  • HTTP/3の対応状況はCURL_VERSION_HTTP3で調べる
  • 環境によっては定数が存在しない
  • ビット演算で結果を判定する

さらに、curl拡張には似た名前の定数が並びます。

CURL_VERSION_HTTP3
CURL_HTTP_VERSION_3
CURL_HTTP_VERSION_3ONLY
CURLOPT_HTTP_VERSION
CURLINFO_HTTP_VERSION

役割はそれぞれ異なります。

  • CURL_VERSION_HTTP3は、libcurlがHTTP/3に対応しているかを調べる
  • CURLOPT_HTTP_VERSIONは、通信時に使うHTTPバージョンを設定する
  • CURL_HTTP_VERSION_3は、その設定へ渡す値である
  • CURLINFO_HTTP_VERSIONは、実際に使われたHTTPバージョンを調べる

難しいのはビット演算そのものではありません。

多数の定数から、自分の問いに答えるものを見つけることです。

PHP 8.4でfeature_listが追加された

PHP 8.4では、curl_version()の戻り値にfeature_listが追加されました。

$features = curl_version()['feature_list'];

$supportsHttp2 = $features['HTTP2'] ?? false;
$supportsHttp3 = $features['HTTP3'] ?? false;

結果は次のような連想配列です。

[
    'SSL' => true,
    'HTTP2' => true,
    'HTTP3' => false,
    'BROTLI' => true,
]

PHP 8.3以前の判定と比べると、違いは明確です。

// PHP 8.3以前
($version['features'] & CURL_VERSION_HTTP3) !== 0;

// PHP 8.4以降
$version['feature_list']['HTTP3'] ?? false;

feature_listによって、PHP利用者は次の作業から解放されました。

  • ビット演算を書く
  • CURL_VERSION_*定数を探す
  • libcurlの内部表現をPHP側で解釈する

環境診断やテストも書きやすくなります。

feature_listは新しい通信機能を追加したものではありません。既存の機能情報を、PHPコードから扱いやすい形へ変換した機能です。

知りたいのはバージョンではなく対応機能

ここで一つ違和感が残ります。

curl_version()['feature_list']['HTTP3'] ?? false;

知りたいのはHTTP/3への対応状況ですが、呼び出している関数はcurl_version()です。

現在のcurl_version()は、単純なバージョン取得関数ではありません。

  • libcurlのバージョン
  • TLSライブラリの情報
  • zlibの情報
  • ビルド対象ホスト
  • 対応プロトコル
  • 機能ビットマスク
  • 機能ごとの真偽値

実態は、libcurl環境全体の診断APIに近くなっています。

既存の戻り値へfeature_listを追加した判断は、変更規模を小さくする方法として合理的です。一方で、利用者の問いに対して最も自然な入口とは限りません。

既存APIへ追加しやすい場所と、利用者が探しやすい場所は同じとは限りません。

教育用ショートカットとしてcurl_features()を作る

責務の違いを見えやすくするため、記事内でcurl_features()を定義してみます。これはPHP標準関数ではありません。

function curl_features(): array
{
    static $features = null;

    return $features ??=
        curl_version()['feature_list'] ?? [];
}

利用側は次のようになります。

$features = curl_features();

$supportsHttp2 = $features['HTTP2'] ?? false;
$supportsHttp3 = $features['HTTP3'] ?? false;

複数の機能をまとめて確認する場合にも使えます。

$features = curl_features();

if (
    ($features['HTTP3'] ?? false)
    && ($features['BROTLI'] ?? false)
) {
    // 必要な機能がそろっている
}

この関数は新しい判定方法を導入していません。curl_version()から機能一覧だけを取り出しているだけです。

curl_version()
    libcurl環境全体の情報を取得する

curl_features()
    その中から機能一覧だけを取り出す

curl_features()はユーザーランドで簡単に書けるため、標準関数として追加する必要性は高くありません。ただし、curl_version()が複数の責務を持っていることを理解する教材にはなります。

単一判定にはcurl_has_feature()

一つの機能だけを確認するなら、述語関数のほうが利用者の問いに合います。

function curl_has_feature(string $feature): bool
{
    return curl_features()[$feature] ?? false;
}
if (curl_has_feature('HTTP3')) {
    // HTTP/3を利用する構成を選ぶ
}

feature_listcurl_has_feature()の違いは、判定内容ではなくAPIの入口です。

feature_list
    利用できる機能を一覧する

curl_has_feature()
    一つの機能が利用できるか問い合わせる

複数の機能をまとめて扱うなら一覧取得が向いています。一つだけ確認するなら、curl_has_feature()のほうが意図を直接表現できます。

curl 7.87.0は機能を名前で報告し始めた

libcurl側でも、機能情報の表現が変化しています。

curl 7.87.0では、curl_version_info_datafeature_namesが追加されました。利用できる機能を、NULL終端の文字列配列として取得できます。

const char *const *feature_names;

従来のfeaturesは、機能ごとにビットを割り当てる方式でした。

bit 16 = HTTP2
bit 25 = HTTP3

feature_namesでは、利用できる機能を名前で列挙します。

HTTP2
HTTP3
brotli
ECH
HTTPSRR
NativeCA

これは可読性だけの改善ではありません。新しい機能を公開するたびに、固定されたビット空間へ割り当て続ける必要がなくなります。

libcurlの現在のドキュメントでも、従来のfeaturesビットマスクよりfeature_namesの利用が推奨されています。

一覧と単一判定は同じ情報を使う

curl_has_feature()を追加するなら、feature_listとは別の判定ロジックを持たせるべきではありません。ext/curl内部でlibcurlの機能情報を読み取り、両方へ同じ結果を返します。

古いlibcurlではfeaturesビットマスク、curl 7.87.0以降ではfeature_namesも利用できます。この違いはext/curl内部で吸収します。

curl_has_feature('HTTP3')
    === (curl_version()['feature_list']['HTTP3'] ?? false);

利用者にとって重要なのは、内部表現ではなく、一覧取得と単一判定の結果が一致することです。

標準 curl_has_feature()を検討する論点

ユーザーランド版は簡単に実装できます。

function curl_has_feature(string $feature): bool
{
    return curl_version()['feature_list'][$feature] ?? false;
}

しかし、この実装では機能が未対応なのか、名前を間違えたのかを区別できません。

curl_version()['feature_list']['HTTP3'] ?? false;
curl_version()['feature_list']['HTP3'] ?? false;

どちらもfalseになりますが、前者はHTTP/3未対応、後者は入力ミスです。

標準のcurl_has_feature()を設けるなら、一般的な表記揺れはext/curl側で吸収できます。

curl_has_feature('HTTP3');
curl_has_feature('http3');
curl_has_feature('HTTP/3');

一方、正規化しても認識できない名前は、falseではなくValueErrorにする設計が考えられます。

curl_has_feature('HTP3');
// ValueError

結果の意味は次のように分離できます。

true
    機能を認識しており、現在の環境で利用できる

false
    機能を認識しているが、現在の環境では利用できない

ValueError
    機能名を認識できない

未知の名前をfalseにすれば将来追加される機能を問い合わせやすくなりますが、タイプミスも見逃します。feature_listが既知の機能名を一覧として提供していることを考えると、未知の名前をValueErrorにする方が判定の意味は明確です。

標準関数として追加する意義は、コードを短くすることだけではありません。

表記揺れはext/curlが吸収し、入力ミスはエラーにする。falseは、正しい機能名を指定したうえで未対応だった場合だけに使う。

これにより、うろ覚えの機能名を未対応と誤判定する事故を防げます。

まとめ

PHP 8.4のfeature_listは、libcurlの機能ビットマスクを、機能名と真偽値からなる連想配列へ変換しました。

これにより、PHP利用者はCURL_VERSION_*定数やビット演算を直接扱わずに、HTTP/2やHTTP/3への対応状況を確認できます。

$supportsHttp3 =
    curl_version()['feature_list']['HTTP3'] ?? false;

一方、一つの機能を確認するだけでも、curl_version()の戻り値とfeature_listの構造を知る必要があります。

curl_has_feature('HTTP3');

標準のcurl_has_feature()があれば、libcurlのバージョン差、内部表現、機能名の対応関係をext/curlへ集約し、「この機能を使えるか」という問いを直接表現できます。

feature_listは機能情報を一覧として読みやすくし、curl_has_feature()は同じ情報へ単一判定の入口を加えます。両者が同じ結果を返すことで、ext/curlはlibcurl固有の事情をPHP利用者から隠す役割を果たせます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?