PHPソースコードを読むうえで欠かせない「zval」とは?
ここでPHPのソースコードを読むうえで、重要な概念である「zval」について触れておきましょう。PHP言語の変数はC言語の変数とは全く違った考え方の上に作られていて、型情報を内部的には持っているものの、必要に応じてどんどん変換していくという特徴を持っています。
その実装を実現しているのがzvalという仕組みです。
zvalの定義はZend/zend_types.hにあります。zvalの本質的な構造はzend_valueという共用体になっていて、このzend_valueにさまざまな管理情報を付加したものがzvalの実態となります。
typedef union _zend_value { zend_long lval; /* long value */ double dval; /* double value */ zend_refcounted *counted; zend_string *str; zend_array *arr; zend_object *obj; zend_resource *res; zend_reference *ref; zend_ast_ref *ast; zval *zv; void *ptr; zend_class_entry *ce; zend_function *func; struct { uint32_t w1; uint32_t w2; } ww; } zend_value;
zend_valueの実装を見ると、PHPで扱えるさまざまな型を1つの概念として統合して扱えるようになっています。zend_valueが実際にどの型の値を持っているかは、そのzend_valueを保持しているzvalにフラグ情報を持つ仕組みによって決まります。
explode()のコードに出てきたZSTR_LEN()などのマクロは、渡されたzvalの内部情報をチェックし、その中に格納されている文字列の長さを返すというコードに展開されるマクロだということになります。
zvalを使用するもう1つの特徴として、メモリの自動開放があります。C言語はメモリの確保と開放がプログラム記述者に任されており、そのため使用後のメモリ解放忘れなどメモリリークを避けることが難しくなっています。PHPランタイムはPHP環境内の変数をすべてzvalで管理することにより、変数の使用状況をチェックすることができ、不必要になったメモリを自動的に開放する仕組みを持っています。
引数の型チェックはどのように実現されている?
PHPはバージョン8から、関数やメソッドの、引数と返り値の型チェックが可能になりました。これはどのように実現されているのでしょうか。またexplode()を例に探ってみましょう。
調べるとext/standard/basic_functions_arginfo.hに記述されています。
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_explode, 0, 2, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, separator, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "PHP_INT_MAX") ZEND_END_ARG_INFO()
このようにexplode()の引数と返り値の型が記述されています。上記で引用したstring.cの実装部分と記述がかぶっているようにも感じられますが、PHPの型チェックが新しく入った後付けの機能なので致し方ないことなのでしょう。
さいごに
PHPのコードの実行方法にはあまり触れずに、PHPで呼び出した関数の実装がどうなっているのかを探るという観点で解説をしてみました。いかがだったでしょうか。
PHPのソースコードはその役割ごとにきれいに整理されているので、目的の場所を探しやすい構成になっていると思います。興味を持った方は是非ソースコード探索を楽しんでください。
PHPカンファレンス2024 実行委員より
約5カ月先の開催となるカンファレンスの準備は、実行委員の一般募集が開始され、いよいよ本格始動したと言った段階です。またスポンサー募集も開始していますので、PHPカンファレンス2024の企業協賛に興味のある方は公式Xのポストをご覧いただき、応募いただければと思います。