Share
Embed
PHPのバージョンアップ時にASTが変わらない変更の見抜き方
2026-11-14 PHPカンファレンス新潟2026 レギュラートーク資料
https://fortee.jp/phpconniigata-2026
Atsushi Matsuo
November 14, 2026
Transcript
40 slides
-
はじめに 自己紹介 • 松尾篤 • PHP使用歴:約21年 • PHPを使った開発歴は18年 • 2023年4月にGaroon開発チームに加入
• これまでの登壇実績(AST関連) • PHPカンファレンス名古屋2025「PHPのバージョンアップ時にも役立ったAST」 • PHPerKaigi 2026「PHPのバージョンアップ時にも役立ったAST(2026年版)」 • PHPerKaigi mini #3「PHP 8.5へのバージョンアップ時にも役立つAST(抽象構文木)」 3 -
PHPのリリースサイクルとバージョンアップの必要性 もしもバージョンアップを行わなかったら • PHPに起因するセキュリティ脆弱性への対応が困難になる • 例:2026年10月のPHP2Shellでは、PHP 8.2以降向けには修正バージョンが公開されたが、サポート終了済みの PHP 7.4、PHP 8.0、PHP
8.1は例外的にパッチが提供されただけで修正版のリリースはなかった • 深刻なセキュリティ脆弱性がPHPに見つかって製品に重大な影響を与えてしまうような問題が出てきた場 合にユーザーが危険な状況に陥ってしまう • サービスやビジネスの継続に影響を与えてしまう恐れも 6 -
PHPのバージョンアップとAST AST • 抽象構文木、Abstract Syntax Treeの略 • プログラムの本質的な構造を木構造で表したもので、ソースコードの解析や変換に活用できる • 括弧、空白、コメントなど表記上の情報は含まれず、論理的な構造の比較に適している
• ただし、/** */のPHPDocコメントはASTに含まれる ソースコード <?php $var = "Hello World!"; echo "${var}"; AST_STMT_LIST 0: AST_ASSIGN (PHP 8.1+php-astでの出力例) var: AST_VAR name: "var" expr: "Hello World!" 1: AST_ECHO expr: AST_ENCAPS_LIST 0: AST_VAR name: "var" AST 10 -
PHPのバージョンアップとAST 同じソースコードであればASTも同じになる 1回目 ソースコード AST <?php $var = "Hello World!";
echo "${var}"; AST_STMT_LIST 0: AST_ASSIGN var: AST_VAR name: "var" expr: "Hello World!" 1: AST_ECHO expr: AST_ENCAPS_LIST 0: AST_VAR name: "var" ハッシュ値(SHA-256) 410dfe506da6502f9f81b89710ad379159b644d63520de6eca79f8ed86b6ffc4 11 -
PHPのバージョンアップとAST 同じソースコードであればASTも同じになる 1回目 ソースコード AST 2回目 <?php $var = "Hello
World!"; echo "${var}"; AST_STMT_LIST 0: AST_ASSIGN var: AST_VAR name: "var" expr: "Hello World!" 1: AST_ECHO expr: AST_ENCAPS_LIST 0: AST_VAR name: "var" ハッシュ値(SHA-256) ソースコード AST <?php $var = "Hello World!"; echo "${var}"; AST_STMT_LIST 0: AST_ASSIGN var: AST_VAR name: "var" expr: "Hello World!" 1: AST_ECHO expr: AST_ENCAPS_LIST 0: AST_VAR name: "var" 410dfe506da6502f9f81b89710ad379159b644d63520de6eca79f8ed86b6ffc4 12 -
PHPのバージョンアップとAST PHPをバージョンアップする前に • 新バージョンへの移行ガイドを必ず確認 • 移行ガイドに記載されている内容に基づいて製品への影響がないか調査する • 移行ガイドには新機能だけでなく「下位互換性のない変更点」や「推奨されなくなる機能」も記載されている • PHP
8.5の移行ガイド : https://www.php.net/manual/ja/migration85.php 13 -
PHPのバージョンアップ時にASTが変わらない変更の見抜き方 PHPのバージョンアップ時にASTが変わらない変更の見抜き方 • 移行ガイドにある「推奨されなくなる機能」をまず見る • 書き換えてもASTが変わらない項目が含まれることがある • 「推奨されなくなりました」および(推奨されなくなったものの代わりに)「使ってください」という記 述のある置き換え案内において表記や区切り記号が変わる場合にはASTが変わらない変更の候補 •
関数名・引数・制御フロー・構文形式が変わる場合にはASTは変わる • 最終的にはバージョンアップ前のPHPでASTを出力して確認 • 「Aの代わりにB」なら何でも対象ではない 15 -
PHPのバージョンアップ時にASTが変わらない変更の見抜き方 「何が変わるか」で仕分ける 変わる要素 例 ASTで変わるもの 表記・区切り記号 ${var} → {$var} (integer)
→ (int) case 1; → case 1: 何も変わらない(候補) 関数名・API名・識別子名 is_integer() → is_int() 名前ノードが持つ値 引数・入れ子構造 オブジェクト → get_object_vars() ?array → array ノードの数や入れ子 制御フロー finallyからのreturnをやめる ノードの位置 16 -
PHPのバージョンアップ時にASTが変わらない変更の見抜き方 なぜ表記や区切り記号ならASTが変わらないのか • ソース → 字句解析 → 構文解析(AST) → コンパイル(opcode)
→ 実行 • 表記や区切り記号は、ASTになる前に消える • キャストの別名は字句解析で正規形のトークンに畳まれ、caseの;は構文解析時に消える • 関数名・引数・制御フローは、ASTのノードとして残る • ただし、新バージョンは、非推奨を検知するためにASTにフラグ(属性)を付けることがある(8.2の"${var}"、 8.5のcase 1;) • そのため、バージョンアップ前のPHPを使うとASTが同じであることを確認しやすい • 書き換えていなければ、PHPをバージョンアップした時点でフラグが付いてASTが変わる • 書き換えてあれば、PHPをバージョンアップしてもASTは変わらない • ASTが同じでも、新バージョンで警告が消えたことは別途確認する必要あり 17 -
PHPのバージョンアップ時にASTが役立った事例 PHP 8.1からPHP 8.2にバージョンアップした際にASTが役立った事例 • PHP 8.2にバージョンアップする作業の一部でASTの差分がないことを確認することで大幅にテスト工数 を削減できたケースがあったことをPHPカンファレンス名古屋2025とPHPerKaigi 2026で紹介 •
https://fortee.jp/phpcon-nagoya-2025/proposal/143e3ef0-b235-423e-b3e8-8f6338aab8a8 • https://fortee.jp/phperkaigi-2026/proposal/6c422c5c-8789-4464-9ccd-5e351a0f3bbc 19 -
PHPのバージョンアップ時にASTが役立った事例 きっかけはPHP勉強会@東京 • PHP 8.2にバージョンアップする際の「"${var}" / "${expr}" 形式の、文字列への値の埋め込み」につ いてはASTを活用できることに気づく •
2023年11月に開催された「第158回 PHP勉強会@東京」で金城さんのASTに関する発表を聞いたことが きっかけ 金城さんのASTに関する発表スライドより 20 -
PHPのバージョンアップ時にASTが役立った事例 PHP 8.1からPHP 8.2にバージョンアップした際にASTが役立った事例 • 当該事例では、PHP 8.2で非推奨になった「“${var}” / “${expr}” 形式の、文字列への値の埋め込み」
について434箇所変更すべき箇所が見つかった • 実装としてはエディターで${を{$に一括置換するだけだったが、品質保証やテスト戦略をどうするべき かという課題に直面 • 変更前の例:$message = "${var}"; • 変更後の例:$message = "{$var}"; • バージョンアップ前のPHP 8.1+php-astで、ast_dump()の出力から求めたSHA-256ハッシュ値が変更 前後で変わらないことを確認することで434箇所個別に手動テストを行う必要はないと最終的に判断 21 -
PHPのバージョンアップ時にASTが役立った事例 "${var}"から"{$var}"に置き換えてもASTが変わらない(PHP 8.1の場合) "${var}" ソースコード AST "{$var}" <?php $var =
"Hello World!"; echo "${var}"; AST_STMT_LIST 0: AST_ASSIGN var: AST_VAR name: "var" expr: "Hello World!" 1: AST_ECHO expr: AST_ENCAPS_LIST 0: AST_VAR name: "var" ハッシュ値(SHA-256) ソースコード AST <?php $var = "Hello World!"; echo "{$var}"; AST_STMT_LIST 0: AST_ASSIGN var: AST_VAR name: "var" expr: "Hello World!" 1: AST_ECHO expr: AST_ENCAPS_LIST 0: AST_VAR name: "var" 410dfe506da6502f9f81b89710ad379159b644d63520de6eca79f8ed86b6ffc4 22 -
PHPのバージョンアップ時にASTが役立った事例 "${var}"のままだとASTが変わる(PHP 8.2の場合) "${var}" ソースコード AST <?php $var = "Hello
World!"; echo "${var}"; AST_STMT_LIST 0: AST_ASSIGN var: AST_VAR name: "var" expr: "Hello World!" 1: AST_ECHO expr: AST_ENCAPS_LIST 0: AST_VAR flags: ENCAPS_VAR_DOLLAR_CURLY (1) name: "var" ハッシュ値(SHA-256) 2c12d382a9c54af519fbb7391c85f0652236bd896e464a2a9ff055ce9fd26068 23 -
PHPのバージョンアップ時にASTが役立った事例 "${var}"から"{$var}"に置き換えるとASTが元に戻る(PHP 8.2の場合) "{$var}" ソースコード AST ハッシュ値(SHA-256) <?php $var =
"Hello World!"; echo "{$var}"; AST_STMT_LIST 0: AST_ASSIGN var: AST_VAR name: "var" expr: "Hello World!" 1: AST_ECHO expr: AST_ENCAPS_LIST 0: AST_VAR name: "var" 410dfe506da6502f9f81b89710ad379159b644d63520de6eca79f8ed86b6ffc4 24 -
PHPのバージョンアップ時にASTが役立った事例 ASTが同じならなぜテスト工数を削減できるのか • ASTが同じ=プログラムの構造が同じ(違うのは表記だけ) • PHP 7以降、コンパイラはASTを入力としてopcodeを生成する • ASTに関するPHP RFC
: https://wiki.php.net/rfc /abstract_syntax_tree • 同じバージョンのPHPおよび同じコンパイル時設定でASTが同じなら、生成されるopcodeも同じ • opcodeが同じなら実行時の挙動も変わらないと判断できる 25 -
PHPでASTを取得およびそのハッシュ値を比較する方法 php-astのインストール • https://github.com/nikic/php-ast?tab=readme-ov-file#installation を参照 • macOSやLinuxの場合 • 「pecl install
ast」を実行してphp.iniに「extension=ast.so」を追加 • 今ならPIE(PHP Installer for Extensions)を使ってインストールする方法も • もしくはコンパイルしてからインストールする方法もある(phpize → ./configure → make → sudo make install) • Windowsの場合 • php-astのDLLファイルをダウンロードしてextディレクトリに配置してからphp.iniに「extension=php_ast.dll」を追加 28 -
PHPでASTを取得およびそのハッシュ値を比較する方法 php-astでASTのハッシュ値を取得するサンプル • hash()を使って取得したASTのハッシュ値(SHA-256)を生成 • 引数にファイルパスを指定して実行 • 実行例 : php
dump-ast-hash-by-php-ast.php /path/to/file.php 31 -
PHPでASTを取得およびそのハッシュ値を比較する方法 ASTを比べるときの注意 • バージョンアップ前のPHPで実行する • PHP 8.1からPHP 8.2にバージョンアップする場合ならPHP 8.1で実行する •
比較に使うPHPやphp-astのバージョンを揃える • PHPDocコメントを無視する場合にはast_dump()の第2引数に AST_DUMP_EXCLUDE_DOC_COMMENTを指定する • バージョンアップ後、書き換えたコードをもう一度ダンプし、バージョンアップ前の結果と一致すること を確認する(PHPやphp-astだけでなくAST versionも揃える) 32 -
PHPでASTを取得およびそのハッシュ値を比較する方法 PHP 8.5での実例1:正規化されていない型名でのキャスト • (integer)は(int)に、(boolean)は(bool)に、(double)は(float)に、(binary)は(string)に変える • ソースコードの表記は変わっているが、ASTとしては同じ構造になることが期待できそうに見える • 実際にphp-astを使って確認してみたところ、対象となるキャスト表記を正規化したうえで変更前後の ASTを比較し、ASTに差分が出ないことを確認できた
PHP 8.4でphp-astを使用 PHP 8.5でphp-astを使用 ソースコード変更前のハッシュ値 (例) 4693d072bbfa4fb1832507cd6bea6 183e79a5f1bf42bddd1f47df8b7e5b 454b6 4693d072bbfa4fb1832507cd6bea6 183e79a5f1bf42bddd1f47df8b7e5b 454b6 ソースコード変更後のハッシュ値 (例) 4693d072bbfa4fb1832507cd6bea6 183e79a5f1bf42bddd1f47df8b7e5b 454b6 4693d072bbfa4fb1832507cd6bea6 183e79a5f1bf42bddd1f47df8b7e5b 454b6 34 -
PHPでASTを取得およびそのハッシュ値を比較する方法 PHP 8.5での実例2: switch文のcaseをセミコロンで終了させる書き方 • case文の末尾にある ;(セミコロン)を :(コロン)に変更した場合 • テキストとしては;が:に変わるため差分があるが、構文上はどちらもswitch文のcaseラベルを表す
• バージョンアップ前のPHP 8.4環境でphp-astを使って確認してみたところ、セミコロンをコロンに置き 換えてもASTは同じであり、switch文の分岐構造そのものは変わっていないことを確認できた PHP 8.4でphp-astを使用 PHP 8.5でphp-astを使用 ソースコード変更前のハッシュ値 (例) 617f67046f0e38cb19be702c4946a a21dbf9fc5817f2450305d12a59c15 b243b4ae61bdd445c1d8a51afe8cfe 0861fe3d161db5c4ae9e0363560cc 9a0ae a5ee4 ソースコード変更後のハッシュ値 (例) 617f67046f0e38cb19be702c4946a 617f67046f0e38cb19be702c4946a b243b4ae61bdd445c1d8a51afe8cfe b243b4ae61bdd445c1d8a51afe8cfe 9a0ae 9a0ae 35 -
PHPでASTを取得およびそのハッシュ値を比較する方法 余談:ASTが違ってもZend Engineが最後に呼ぶ処理は同じことがある • 同じ「別名の正規化」でも結果が違う • キャストの別名は字句解析で解決され、関数の別名は実行時に解決される • is_integer()とis_int()はASTでは別の名前だが、Zend Engineの関数テーブルでは同一のCハンドラーを
指す • PHPをバージョンアップする際にASTが違っていてもテスト工数を削減できるケースがあるかもしれない 36 -
まとめ PHPのバージョンアップ時にASTが変わらない変更の見抜き方 • 移行ガイドにある「推奨されなくなる機能」をまず見る • 書き換えてもASTが変わらない項目が含まれることがある • 「推奨されなくなりました」および(推奨されなくなったものの代わりに)「使ってください」という記 述のある置き換え案内において表記や区切り記号が変わる場合にはASTが変わらない変更の候補 •
関数名・引数・制御フロー・構文形式が変わる場合にはASTは変わる • 候補はバージョンアップ前のPHPでASTを出力して確かめる • 「Aの代わりにB」なら何でも対象ではない • 最終的にはopcodeを比較するのが確実 38 -
まとめ まとめ • ASTが変わらない変更を見分ければ、PHPの移行ガイドはテスト工数削減のヒントに変わる • 移行ガイドの「推奨されなくなる機能」には、ASTが変わらない変更が含まれることがある • ASTが変わらないのは表記や区切り記号を変える変更 • 関数名が異なるとASTは変わる
• ASTはバージョンアップ前のPHPで確認すると比較しやすい • 警告が消えたことは別途確認する必要あり • ASTが違ってもZend Engineが最後に呼ぶ処理は同じことがある話は来年どこかのカンファレンスで 39