PHP独自の機能は? 「ext/standard」のソースコードを見てみよう
ext ディレクトリの下には、PHP本体にバンドルされている拡張モジュールのソースがありますが、この中で特に面白いのはext/standardです。この中にはかなり多くのファイルが含まれていて、いわゆるPHP標準の組み込み関数の多くもここにあります。標準の組み込み関数も「standard」という拡張モジュールの形式で実装されているのが面白いですね。
それでは実際のPHP標準関数として explode() を例にソースコードを見ていきましょう。
explode() のソースを探る
ext/standardで"explode"をファイル検索すると、簡単に該当部分が見つかります。割と短いのですべて引用しましょう。
PHP_FUNCTION(explode) { zend_string *str, *delim; zend_long limit = ZEND_LONG_MAX; /* No limit */ zval tmp; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(delim) Z_PARAM_STR(str) Z_PARAM_OPTIONAL Z_PARAM_LONG(limit) ZEND_PARSE_PARAMETERS_END(); if (ZSTR_LEN(delim) == 0) { zend_argument_value_error(1, "cannot be empty"); RETURN_THROWS(); } array_init(return_value); if (ZSTR_LEN(str) == 0) { if (limit >= 0) { ZVAL_EMPTY_STRING(&tmp); zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp); } return; } if (limit > 1) { php_explode(delim, str, return_value, limit); } else if (limit < 0) { php_explode_negative_limit(delim, str, return_value, limit); } else { ZVAL_STR_COPY(&tmp, str); zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp); } }
普通のC言語のソースコードとは結構違いますね。まず関数宣言からしてPHP_FUNCTION()
という見知らぬ記述で書かれています。ある程度大規模なプロジェクトではよくありますが、これはC言語のマクロを利用して実際の複雑なコードを隠蔽している記述方法です。
ここで使われているZEND_
やZSTR_
で始まっている関数形式のものはすべてマクロで、Zend EngineやPHP独自の内部実装を使いやすく記述するためのものです。
そもそもなぜこのようなマクロが必要なのでしょうか?
PHPは言語機能としてC言語にはないさまざまな特徴を提供しており、そのために値や関数、クラスの持ち方もPHP独自の実現方法を持っています。その仕組みを毎回考慮してソースコードを書かなくても済むように、これらのマクロを利用してコードをわかりやすくしているわけです。
explode() の引数
上記のexplode()の実装部分を見ればわかりますが、PHPのexplode()の引数はC言語の関数の引数として渡ってきてはいません。引数の受取り部分は以下となります。
ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(delim) Z_PARAM_STR(str) Z_PARAM_OPTIONAL Z_PARAM_LONG(limit) ZEND_PARSE_PARAMETERS_END();
ZEND_PARSE_PARAMETERS_START
、ZEND_PARSE_PARAMETERS_END
というマクロで挟んだ部分で各引数を受け取っています。
ZEND_PARSE_PARAMETERS_STARTマクロはZend/Zend_API.hで定義されていて
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args) \ ZEND_PARSE_PARAMETERS_START_EX(0, min_num_args, max_num_args)
必須の引数の数が2で、最大の数が3であるという意味ですね。PHP関数のexplode()の定義は
explode(string $separator, string $string, int $limit = PHP_INT_MAX): array
となっているので、$separatorと$stringという2つの引数が必須で、オプションとして$limitを渡すことができると、両者の意味がマッチしているのがわかります。
C言語ソースコード側のexplode関数は、引数を受け取ったあとはその引数のチェックを行い、limitの値によって
- php_explode()
- php_explode_negative_limit()
の2つの内部関数を呼び分けていることがわかります。実際のソースコードの引用は行いませんが、これらの2つは通常のCの関数なので、PHP独自の値の扱いを除けば普通にC言語のプログラムとして読むことができます。