Upgrade to Pro — share decks privately, control downloads, hide ads and more …

strict_typesを雰囲気で終わらせない:PHPの型変換とTypeErrorの境界

Avatar for ずみ ずみ
April 12, 2026
55

 strict_typesを雰囲気で終わらせない:PHPの型変換とTypeErrorの境界

Avatar for ずみ

ずみ

April 12, 2026

Transcript

  1. ©Fusic Co., Ltd. 2 善住 直紘 C H I H

    I R O Y O S H I Z U M I 株式会社Fusic エンジニア 生まれも育ちも大阪府。中学時代に通ったプログラミングスクー ルがきっかけで情報分野に興味を持ち、情報系の大学に進学。 「面白そうな人たちが面白そうなことしてる」とFusicの個性に惹 かれ2025年4月に新卒入社。 自己紹介 はじめに @zumi_engineer ←福岡でPHPの勉強会を運営しています
  2. ©Fusic Co., Ltd. 5 PHP RFC: Scalar Type Declarations declare(strict_types=1);

    ↑RFCのページ↑ • スカラー型宣言(int、float、string、bool)を追加 • declare(strict_types=1);ディレクティブを追加 ↓ざっくり説明↓
  3. ©Fusic Co., Ltd. 8 declare(strict_types=1); declare(strict_types=1);とは... Fatal error: Uncaught TypeError:

    increment(): Argument #1 ($a) must be of type int, string given declare(strict_types=1);がある時 increment(“1”)
  4. ©Fusic Co., Ltd. 9 おさらい ちなみに弱い型の変換はこんな感じ * floatをintに変換するときは基本的に切り捨てる † 数値以外の文字列は

    NG。末尾に文字が付いた数値文字列は通るけど、通知が表示される。 ‡ オブジェクトが__toStringメソッドを持っている時だけOK declare(strict_types=1);がない時
  5. ©Fusic Co., Ltd. 11 declare(strict_types=1); ファイル単位 increment.php main.php Fatal error:

    Uncaught TypeError: increment(): Argument #1 ($a) must be of type int, string given, called in main.php on line 7 and defined in increment.php:3
  6. ©Fusic Co., Ltd. 13 おさらい • declare(strict_types=1); でファイル単位の型チェックが厳密になる • 型の厳密性の責任は呼び出し側

    が持つ ここから • これをPHPはどうやって実現しているのか • php-srcを追って、その仕組みを見てみよう strict_typesを設定する おさらい → 実装へ
  7. ©Fusic Co., Ltd. 15 strict_typesを設定する 用語解説 AST(抽象構文木) ソースコードを木構造で表現したもの。 コンパイラはこの木を辿りながら命令列に変換する。 opcode

    PHPコードを Zend VMが実行できる命令列に変換したもの。 関数ポインタ  関数のアドレス(その関数がどこのメモリにいるのかの情報)を格納した変  数。実行時に別の関数に差し替えられる。 SAPI  PHPを様々な環境から呼び出すための入り口。 参考:PHP Conference Japan 2022: php-srcを読んでみよう / 田実 誠 (https://youtu.be/9k9-DM-q27c?si=9I2WHRhDRU8daP3R )
  8. ©Fusic Co., Ltd. 16 strict_typesを設定する フラグ設定までの全体の流れ zend_execute_script zend_compile_file (compile_file) zend_compile

    zend_compile _top_stmt zend_compile _stmt zend_compile _declare PHPの呼び出しからstrict_typesが設定されるまで SAPI
  9. ©Fusic Co., Ltd. 17 どの入口(CLI・PHP-FPM・mod_phpなど)から実 行しても最終的にzend_execute_script()が呼ばれ る strict_typesを設定する ざっくりphpが実行されてからstrict_typesが格納されるまで zend_compile_fileは関数ポインタなので、動作環境によっ

    て実際の動作関数は異なる。 (zend_startup()にてzend_compile_file = compile_file;と 初期化されている) zend_execute_script zend_compile_file (compile_file) zend_compile zend_compile _top_stmt zend_compile _stmt zend_compile _declare SAPI
  10. ©Fusic Co., Ltd. 18 - ユーザー定義関数・メソッド・ファイルトップレベルを 表す構造体 - ファイル単位、関数単位 strict_typesを設定する

    コンパイル結果:zend_op_array strict_typesが入る場所 fn_flags 厳 密な型フラグ もここに入る zend_execute_script zend_compile_file (compile_file) zend_compile zend_compile _top_stmt zend_compile _stmt zend_compile _declare SAPI
  11. ©Fusic Co., Ltd. 19 strict_typesを設定する compile_fileからzend_compileへ op_array = zend_compile(ZEND_USER_ FUNCTION)

    ZEND_USER_FUNCTIONは(ユーザーが 定義した関数であるという情報) zend_execute_script zend_compile_file (compile_file) zend_compile zend_compile _top_stmt zend_compile _stmt zend_compile _declare SAPI
  12. ©Fusic Co., Ltd. 20 strict_typesを設定する zemd_compileからzend_compile_top_stmtへ zend_compile_top_stmt(CG(ast)) - CGは Compiler

    Globalsの略(コンパイ ル中のグローバル変数) - astは構文木 - この構文木の根ノードを渡している zend_execute_script zend_compile_file (compile_file) zend_compile zend_compile _top_stmt zend_compile _stmt zend_compile _declare SAPI
  13. ©Fusic Co., Ltd. 24 - ファイルのop_array (fn_flags に ZEND_ACC_STRICT_TYPES) └──

    内部で定義された関数のop_array (別のop_array) - 厳密なファイルを実行→そこで呼ばれた関数はファイルの厳密さを受け継ぐ - 呼び出し側のファイルの op_array が strict かどうか → ファイル単位で厳密性を管理する 根拠 strict_typesを設定する ファイルと関数は別々の op_array
  14. ©Fusic Co., Ltd. 26 strict_typesを設定する フラグ使用までの全体の流れ zend_execute_script zend_execute zend_execute_ex (execute_ex)

    ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint 設定されたstrict_typesが使われるまで zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  15. ©Fusic Co., Ltd. 27 strict_typesを設定する フラグ使用までの全体の流れ zend_execute_script zend_execute zend_execute_ex (execute_ex)

    ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint 設定されたstrict_typesが使われるまで zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  16. ©Fusic Co., Ltd. 28 設定したstrict_typesを使う zend_execute_script()に立ち戻る zend_execute_script zend_execute zend_execute_ex (execute_ex)

    ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  17. ©Fusic Co., Ltd. 29 設定したstrict_typesを使う zend_execute zend_execute_ex(execute_data) zend_execute_exは関数ポインタなので、動作環境 によって実際の動作関数は異なる。 (zend_startup()にてzend_execute_ex

    = execute_ex;と初期化されている) zend_execute_script zend_execute zend_execute_ex (execute_ex) ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  18. ©Fusic Co., Ltd. 30 func->op_array.fn_flags を 辿ることで strict_types を参照 する

    設定したstrict_typesを使う zend_execute_data zend_execute_script zend_execute zend_execute_ex (execute_ex) ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  19. ©Fusic Co., Ltd. 31 設定したstrict_typesを使う execute_ex execute_ex ZEND_DO_FCALL_SPEC_ RETVAL_USED_HANDLER ZEND_RECV_SPEC_UNUSED

    _HANDLER 数多のディスパッチマクロ達 while(1)ループ 「呼び出し元関数」の設定 実際の型チェック zend_execute_script zend_execute zend_execute_ex (execute_ex) ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  20. ©Fusic Co., Ltd. 32 設定したstrict_typesを使う ZEND_DO_FCALL_SPEC_RETVAL _USED_HANDLER EX(call)には呼び出す関数の zend_execute_dataが格納されている call->prev_execute_data

    = execute_dataで呼 び出し元と呼び出し先をつないでいる zend_execute_script zend_execute zend_execute_ex (execute_ex) ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  21. ©Fusic Co., Ltd. 33 設定したstrict_typesを使う ZEND_RECV_SPEC_UNUSED_HANDLER 型が完全一致していれば そのまま終了 。 合わない場合だけ

    zend_verify_recv_arg_type_helper_SPEC →zend_verify_recv_arg_type →zend_check_type →zend_check_type_slow と呼び出されていく。 zend_execute_script zend_execute zend_execute_ex (execute_ex) ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  22. ©Fusic Co., Ltd. 34 zend_verify_scalar_type_hint を 呼び出す時に呼ばれる ZEND_ARG_USES_STRICT_TYPESというマ クロの中でstrict_typesが参照される 設定したstrict_typesを使う

    zend_check_type_slow zend_execute_script zend_execute zend_execute_ex (execute_ex) ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  23. ©Fusic Co., Ltd. 35 • EG(current_execute_data)= 実行中のグローバル引数で設定されている 現状実行している関数のzend_execute/data • EG(current_execute_data)->prev_execute_data

    = 呼び出し元の zend_execute_data • caller の fn_flags を見ることで、呼び出し元ファイルの strict_types を判定 設定したstrict_typesを使う fn_flagsへのアクセス:ZEND_ARG_USES_STRICT_TYPES
  24. ©Fusic Co., Ltd. 36 設定したstrict_typesを使う zend_verify_scalar_type_hint zend_execute_script zend_execute zend_execute_ex (execute_ex)

    ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES strict_types=1だった時に 実行されるチェック処理
  25. ©Fusic Co., Ltd. 37 strict モードでも int を float 引数に渡すのは許容される

    設定したstrict_typesを使う strict でも int → float は例外 The one exception is that widening primitive conversion is allowed for int to float. This means that parameters that declare float can also accept int. (RFCより抜粋) 3が3.0になっても数値に関する情報が失われないの で例外的にOK ≒float ≒int
  26. ©Fusic Co., Ltd. 38 設定したstrict_typesを使う 厳密な型の変換 Type declaretion int float

    string bool object int yes no no no no float yes yes no no no string no no yes no no bool no no no yes no declare(strict_types=1);がある時 declare(strict_types=1);がない時
  27. ©Fusic Co., Ltd. 40 まとめ Point.01 Point.02 Point.03 厳密な型宣言は基本的に仮引数と実引数が等しくなければエラーを返すが、例外 的にint

    -> floatの変換はしてくれる(プリミティブ変換の拡張) 型があっていればそもそも strict_typesは参照されない コンパイル時にzend_compile_declare()にてstrict_typesをファイル単位で設定
  28. ©Fusic Co., Ltd. 43 設定したstrict_typesを使う zend_check_type → zend_check_type_slow ZEND_TYPE_CONTAINS_CODE で型コードが一致す

    れば即 終了。 不一致なら zend_check_type_slow へ進む。 zend_execute_script zend_execute zend_execute_ex (execute_ex) ZEND_RECV_SPEC_UNUSED _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  29. ©Fusic Co., Ltd. 44 設定したstrict_typesを使う zend_execute_script zend_execute zend_execute_ex (execute_ex) ZEND_RECV_SPEC_UNUSED

    _HANDLER zend_verify_recv_arg_type_helper_SPEC zend_verify_recv_arg_type zend_verify_scalar_type_hint zend_check_type zend_check_type_slow ZEND_ARG_USES_STRICT_TYPES
  30. ©Fusic Co., Ltd. 45 strict_types スイッチは古いコードの移行メカニズムとして設計されたのではな く、どちらも同じように新しい 2 つの異なるオプション間の真の選択として設計さ れたのです。

    strict_types ディレクティブの背後にある考え方は、 2つのバージョンの機能を 1 つのシステムに統合し、ユーザーが自由に切り替えられるようにすることでし た。提案では、両方のモードにそれぞれ長所と短所があることが明確に示され ていました。 Re: strict_types will be default at some moment? 我々はどう使い分けるか https://news-web.php.net/php.internals/112232