2018年12月19日

Linuxで動作する既存のCプログラムソースをPHPのエクステンション化する

Linux(CentOS)上で動作するC言語で書かれた既存のプログラムソースがあるのですが、これをPHP7のエクステンションとして読み込めるようにしました。プログラムはコマンドラインで動作しテキスト処理を行うだけの単純なものです。

参考資料
PHP7 でのエクステンションの書き方を調べた - Qiita
PHP Extensionの開発@ - Qiita

1.PHP7ソースを入手
$ sudo yum install git
$ git clone git@github.com:php/php-src.git
$ cd php-src
$ git tag --list
:
php-7.1.24
:
$ php -v
PHP 7.1.24 (cli) (built: Nov 7 2018 18:45:17) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies
$ git checkout php-7.1.24

自分の環境には git が入っていなかったので git のインストールから必要でした。
また、Permission Denied のエラーが表示されてしまったので、SSH Keyの登録を行いました。
自分の環境にインストールされているPHPのバージョンを確認したところ 7.1.24 だったので、git checkout でこのバージョンをダウンロードします。

2.エクステンションの雛形ファイルを作る
$ cd ext
$ ./ext_skel --extname=sampleext

sampleext というディレクトリが作成され、その中に必要なファイルが作られます。
まずは下記のコマンドを入力してエクステンションが作られるか確認します。
$ cd sampleext
$ vi config.m4
$ pipize
$ ./configure
$ make
$ php -d extension=./modules/sampleext.so sampleext.php
Congratulations! You have successfully modified ext/sampleext/config.m4. Module myext is now compiled into PHP.

config.m4 ファイルの下記の部分をアンコメントします。(dnl という文字を削除する)
PHP_ARG_WITH(myext, for myext support,
Make sure that the comment is aligned:
[ --with-myext Include myext support])

Conguratulations! 〜〜という表示が出れば成功です。

3.自分のプログラムのソースを準備する
既存プログラムはコマンドライン上で起動するものでしたが、シェアードライブラリとしてコンパイルすることでエクステンションから呼び出せるようだったので、コンパイルし直すことにしました。
$ mkdir myprg
$ cd myprg
※このフォルダ内にソースを準備する
$ vi Makefile
$ make

ソースファイルは php-src/ext/sanpleext/myprg/ の中に入れます。
シェアードライブラリとしてコンパイルするために Makefile の CFLAGS と CPPFLAGS に -shared -fPIC を追加します。
CFLAGS = -shared -fPIC -pipe -Wall -W -O2 -w
CXXFLAGS= -shared -fPIC -pipe -Wall -W -O2 -w

最終的にコンパイルされたライブラリは php-src/ext/sampleext/myprg/lib ディレクトリの中に libmyprg.so という名前で保存されるようにします。

4.PHPエクステンションにリンクする
既存プログラムのリンクは config.m4 を修正することで設定します。
$ cd ..
$ vi config.m4

config.m4 の修正点は以下のとおりです。
・if test "$PHP_SAMPLEEXT" != "no"; then の行から下の行のコメント(dnl)を解除
・SEARCH_PATH に ./myprg を追加
・PHP_ADD_INCLUDE(./myprg)
・LIBNAME=myprg
・LIBSYMBOL=myfunc

LIBNAME はリンクするシェアードライブラリの名前を指定します。上記のように書くと ./myprg/lib/libmyprg.so がリンクされます。
LIBSYMBOL はリンクするシェアードライブラリを取り違えていないか確認するためのもので、中に入っている関数名(シンボル)を指定することで、正しいライブラリであることが確認されます。

5.既存プログラムを呼び出す部分を修正する
PHP側とC言語側の間を取り持つのは sampleext.c になりますので、この中に作成していきます。
5-1.関数の宣言
下記の部分にPHPから呼び出すときの関数名を登録します。
const zend_function_entry sampleext_functions[] = {
PHP_FE(confirm_sampleext_compiled, NULL) /* For testing, remove later. */
PHP_FE(call_myfunc, NULL) /* added */
PHP_FE_END /* Must be the last line in sampleext_functions[] */
};

PHP_FE に指定する関数名は PHP 側で見えるものなので、C言語のプログラムソース内の関数と同じ名前でも大丈夫ですが、混乱を避けるために call_myfunc のように変えておくのが良いと思います。第2引数はとりえあずNULLでも動きますが、できれば指定したほうが良いようです(詳細は省略)。
5-2.関数本体の定義
PHP_FUNCTION(confirm_sampleext_compiled) が定義されていると思いますので、その下あたりに定義を追加します。
PHP_FUNCTION(call_myfunc)
{
zend_long a;
char *str;
size_t len;
int status;

ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_LONG(a)
Z_PARAM_STRING(str, len)
ZEND_PARSE_PARAMETERS_END

/* 何らかの処理 */
status = myfunc(a, str);

RETURN_LONG(ret);
}

引数の受け取りについては他に詳しい記事がありますので参照していただくとして、上記の例では、引数2個、1個めは数値、2個めは文字列を受け取ります。
戻り値を返すには、RETURN_LONG を使うと数値、RETURN_STRING を使うと文字列を返せます。

6.動作確認
テスト用のPHPスクリプト(test_sampleext.php)を作成して走らせることで動作確認しました。
<?php
$r = call_myfunc(1, "abc");
echo "$r\n";

下記のようにして実行します。
$ php -d extension=./modules/sampleext.so test_sampleext.php


7.トライアンドエラー
2つの異なる世界のプログラムを連携させているので、一筋縄ではいかないと思います。実際、エラーメッセージとにらめっこしながら何度もコンパイルをやり直しました。
$ vi config.m4
$ phpize
$ ./configure
$ vi sampleext.c
$ make
$ php -d extension=./modules/sampleext.so test_sampleext.php


8.インストール
稼働中のPHPにインストールするには make install を使用します。
インストールされた sampleext.so を読み込むには /etc/php.ini を修正します。
sampleext.so が変更されたら apache を再起動する必要があります。
$ sudo make install
$ vi /etc/php.ini
extension=./modules/sampleext.so
$ sudo apachectl restart



posted by はるこち at 19:00| Comment(0) | サーバ関連 | このブログの読者になる | 更新情報をチェックする

2018年12月18日

git clone しようとしたら Permission denied (publickey) エラーが表示された

PHP7 用のエクステンションを作ってみたいと思い、いろいろな記事を参考にしたところ PHP7 のソースコードが必要ということがわかり、Github から clone しようとしたら、エラーが表示されてしまいました。
$ git clone git@github.com:php/php-src.git
Cloning into 'php-src'...
Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.


パーミッションがないというエラーですが、ファイルシステムのパーミッションではなく Github 側でパーミッションがないようです。
今回のケースでは、Github に登録してある自分のアカウントに SSH Key を登録します。
※Github にアカウントがあることを前提にした説明ですので、アカウントがない場合は登録からお願いします。

$ ssh-keygen -t rsa -C "your_mail_address@domain"


~/.ssh/id_rsa.pub の内容を Github のアカウント→[Settings]→[SSH and GPG Keys]→[New SSH Key]をクリックして登録します。
title 部分には自分でわかりやすい名前(サーバ名など)を入力します。
key のところに貼り付けます。

これで、git clone できるようになりました。

参考にさせていただいた記事
git cloneしようとした時でたエラーと戦った話 - Qiita
posted by はるこち at 19:00| Comment(0) | 開発関係 | このブログの読者になる | 更新情報をチェックする

×

この広告は180日以上新しい記事の投稿がないブログに表示されております。