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

2018年5月28日月曜日

PHP SAPI

[Java,JNI]JNIでクラス検索するときのパッケージ名 では、CからJVMを起動してみたけど、今度はCからPHPを呼び出してみよう。

Linux上で、PHP 7.1.1を使った例。

Embed SAPI

Cから直接PHPを呼び出すためにはEmbed SAPIを使う。
SAPIというのはServer APIと呼ばれるもので、実際にPHPのスクリプトを解釈する部分と実行環境の橋渡しをする。
PHPスクリプトがWebサーバから呼び出されたり、コマンドラインから呼び出されたりすると、SAPIが異なる状態で、呼び出されることになる。

http://php.net/manual/ja/function.php-sapi-name.php

PHPの実行用ファイルだけではなく、以下のファイルが必要になるので、PHP7をソースコードからビルドするか、LinuxディストリビューションのパッケージでPHPのdevelopがあれば、それを使ってもいい。
  • PHPの各種ヘッダーファイル
  • libphp7.so

呼び出し側

早速、呼び出し側のCのコード。
sapi/embed/php_embed.hをインクルードして、php_execute_scriptを呼び出す。
zend_file_handleにはPHPスクリプトのファイルパスを設定する。

注意点はPHP_EMBED_START_BLOCK,PHP_EMBED_END_BLOCKで処理を囲む必要があるところ。

exec_php.c
#include <sapi/embed/php_embed.h>

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

    zend_file_handle file_handle;
    file_handle.type = ZEND_HANDLE_FILENAME;
    file_handle.filename = "/path/to/main.php";
    file_handle.handle.fp = NULL;
    file_handle.opened_path = NULL;
    file_handle.free_filename = 0;

    php_execute_script(&file_handle);

    PHP_EMBED_END_BLOCK()

    return 0;
}

PHPスクリプト

呼び出される側のPHPスクリプトも用意しておこう。

main.php
<?php
$sapi = php_sapi_name();
echo "$sapi\n";

これを普通にコマンドラインPHPから呼び出すと、SAPIの名前として、cliが出力される。
$ php main.php
cli

ビルド

ビルド時の注意点は各種ヘッダーファイルをインクルードパスに含めることと、libphp7.soをリンクすることぐらい。
$ 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 exec_php exec_php.c

実行

実行時にlibphp7.soへの参照ができていない場合は、参照をつけて実行する。

$ LD_LIBRARY_PATH=/path/to/lib ./exec_php
embed

SAPIの名前として、embedが出力されることが確認できた。