[PHP] Embed SAPIでCからPHPを呼び出してみる その2

2018年6月4日月曜日

PHP SAPI

[PHP] Embed SAPIでCからPHPを呼び出してみる その1では、PHPのファイルパスをSAPIに渡すことによって、PHPスクリプトをCから実行してみた。
今度はPHPファイルを介さずに直接、文字列をPHPスクリプトとして実行してみよう。

※今回も同様に、Linux上で、PHP 7.1.1を使った例。

eval

PHPの関数に文字列をそのままPHPスクリプトとして解釈するevalという関数があるけど、それと同じようにCから文字列をevalするにはzend_eval_stringlを使う。

例えば、"2 + 3;"をPHPスクリプトとして解釈、実行させるには以下のようにする。

eval_php.c
#include <string.h>
#include <sapi/embed/php_embed.h>

int main(int argc, char *argv[]) {
    PHP_EMBED_START_BLOCK(argc, argv)

    zval ret = {{ 0 }};
    char php_src[] = "2 + 3;";
    int stat = zend_eval_stringl(php_src, strlen(php_src), &ret, "phpsrc");
    printf("%d\n", stat);
    printf("%d\n", ret.u1.type_info);
    printf("%ld\n", ret.value.lval);

    printf("%d\n", Z_TYPE(ret));
    printf("%d\n", Z_TYPE(ret) == IS_LONG);
    printf("%ld\n", Z_LVAL(ret));

    PHP_EMBED_END_BLOCK()

    return 0;
}

ビルド & 実行


$ export PHP_INCLUDE=/path/to/include/php
$ gcc -Wall -I$PHP_INCLUDE -I$PHP_INCLUDE/main -I$PHP_INCLUDE/Zend -I$PHP_INCLUDE/TSRM -I$PHP_INCLUDE/sapi -L/path/to/lib -lphp7 -o eval_php eval_php.c
$ LD_LIBRARY_PATH=/path/to/lib ./eval_php
0
4
5
4
1
5

出力値について

実際にPHPスクリプトを解釈、実行しているのは9行目のzend_eval_stringlだけど、その後のprintfでステータスや戻り値を出力している。
    printf("%d\n", stat);
    printf("%d\n", ret.u1.type_info);
    printf("%ld\n", ret.value.lval);

statはzend_eval_stringlの戻り値で、実行が成功したかどうかを示している。
今回の値は0なので、実行は成功している。

次にzend_eval_stringlの第3引数として渡したretの値を見ている。これは第1引数で渡した文字列を解釈した結果の最後のしきの値を示している。
今回の場合は、"2 + 3"の結果なので、型が4(LONG)で、値が5となっている。

type_infoの値は"Zend/zend_types.h"で、以下のように定義されている。
#define IS_UNDEF                                        0
#define IS_NULL                                         1
#define IS_FALSE                                        2
#define IS_TRUE                                         3
#define IS_LONG                                         4
#define IS_DOUBLE                                       5
#define IS_STRING                                       6
#define IS_ARRAY                                        7
#define IS_OBJECT                                       8
#define IS_RESOURCE                                     9
#define IS_REFERENCE                            10

zval用マクロ

PHPの変数の値は、おおよそzvalという構造体になるけど、実際の情報にアクセスするには構造体の内部構造に依存しないようにした方がいい。
このためのマクロが定義されているので、先ほど構造体のメンバーに直接アクセスしていたのをマクロにすると、以下のようになる。
    printf("%d\n", Z_TYPE(ret));
    printf("%d\n", Z_TYPE(ret) == IS_LONG);
    printf("%ld\n", Z_LVAL(ret));

Z_TYPEはzvalの型を返してくれるので、"Z_TYPE(ret) == IS_LONG"のようにして、型を調査できる。
また、zvalの型が事前に分かっているときに値を取り出すマクロもある。
今回の場合はLONGなので、Z_LVALを使って、long型の値を取り出している。