zeriyoshi/pskel を読んで動かしてみる
昨日、 PHP Extension の記事を公開したら、「 PHP Extension の雛形作っているよ(&公開しているよ)」という話を聞いたので、早速触っていこうと思います。
読むパート
動いている GitHub Actions を見てみる
まず、 GitHub Actions が動いているので確認します。
arch × version × type × distro の数のマトリックスで実行されています(圧巻ですね。)
やっていることは Docker のビルドと、docker run
なので、次は Dockerfile を確認します。
Dockerfile
FROM ${IMAGE}:${TAG}
で PHP のイメージを持ってきて、 RUN ~~
のところで、ディストーションやコンパイラ( GCC or Clang )によって場合分けした PHP のビルドを行っているみたいです。
続いて ci.sh を Docker 内に持ってきて、 ENTRYPOINT で docker run
の実行コマンドとして設定されています。
最後に ./ext
ディレクトリがコピーされます。
ci.sh
ci.sh は、先程 Dockerfile でコピーした /ext
をビルドするスクリプトになっています。
与えられた環境変数によって実行するコマンドが少し変わっていますが、基本的には phpize
./configure
を実行して、 make -j$(nproc)
でビルド、 make test
でテストをしているみたいです。
逆に、環境変数を与えないと何も実行されないようになっています。
この環境変数は GitHub Actions の実行コマンドを例に見ると、どう使えばいいかわかります。
動かすパート
テンプレートから作ってみる
Pskel は Template として公開されているので、 早速使ってみることができます。
Create a new repository を押して、このテンプレートを使った repository を作成します。
今回も例のごとく HashTable を表示する PHP Extension の my_print_ht というリポジトリを作ってみました。
とりあえず動かす
git clone して Dockerfile を開いてみると、「俺を押せ」ボタンがあったので実行してみます。
(これは docker build && docker run を実行しています。[1])
何も環境変数を設定していないので、 ci.sh は何も実行せず、正常にコンテナが削除されて終了します。
Successfully built 376f38060922
Creating container…
Container Id: f25730ac363d56348b51382964a4b51b1acfc0a1ff2cbb9056e8e6d340ec9cd6
Container name: '/affectionate_jang'
Starting container '/affectionate_jang'
'<unknown> Dockerfile: Dockerfile' has been deployed successfully.
TEST_EXTENSION=1
で環境変数を設定し、 Docker 環境内で PHP を動かして見ます。
ENTRYPOINT で PHP Extension のビルドが行われるので、ビルド後に/bin/bash
を実行します。
+ exec /bin/bash
起動した Docker 環境のターミナルで-d extension=/ext/modules/skeleton.so
をつけて PHP を実行。動きました。
名前を変える
今は雛形のままであり、 skeleton という名前のままなので、まずは名前を変えてみます。
名前の変更は全て機械的に行いました。
先程同様動かしてみます。
$ php -d extension=/ext/modules/my_print_ht.so -a
動きました🙌
Extension を書いてみる
HashTable のプロパティの情報を print する Extension を作ってみます。
/* {{{ void array_info( [ array $a ] ) */
PHP_FUNCTION(array_info)
{
zval *arr;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY(arr)
ZEND_PARSE_PARAMETERS_END();
php_printf("ht->nTableMask: %#x\r\n", Z_ARRVAL_P(arr)->nTableMask);
php_printf("ht->nNumUsed: %d\r\n", Z_ARRVAL_P(arr)->nNumUsed);
php_printf("ht->nTableSize: %d\r\n", Z_ARRVAL_P(arr)->nTableSize);
php_printf("ht->nNumOfElements: %d\r\n", Z_ARRVAL_P(arr)->nNumOfElements);
php_printf("ht->nNextFreeElement: %d\r\n", Z_ARRVAL_P(arr)->nNextFreeElement);
php_printf("ht->nInternalPointer: %d\r\n", Z_ARRVAL_P(arr)->nInternalPointer);
}
/* }}}*/
function array_info(array $a): void {}
ちゃんとテストも書きます。
--TEST--
array_info() Basic test
--EXTENSIONS--
my_print_ht
--FILE--
<?php
array_info([]);
array_info([0, 1, 2, 3]);
?>
--EXPECT--
ht->nTableMask: 0xfffffffe
ht->nNumUsed: 0
ht->nTableSize: 8
ht->nNumOfElements: 0
ht->nNextFreeElement: 0
ht->nInternalPointer: 0
ht->nTableMask: 0xfffffffe
ht->nNumUsed: 4
ht->nTableSize: 8
ht->nNumOfElements: 4
ht->nNextFreeElement: 4
ht->nInternalPointer: 0
これで再び docker build && docker run をして PHP を起動します。
PHP Extension が作れました!
CI を通す
作成出来た PHP Extension を GitHub に push すると、 GitHub Actions が実行されます。しかし、コケます。
+ exec /bin/bash
/usr/bin/ci: exec: line 40: /bin/bash: not found
Error: Process completed with exit code 127.
/bin/bash
が無いと言われていますが、これはローカル環境検証用なので、 push されたリポジトリ( CI で動作を保証する部分)には不要です。
今回は、TEST_EXTENSION=1
の場合と全く同じ構造で、RUN_LOCAL=1
の場合のみ実行可能なブロックを作り、その中にexec /bin/bash
を入れました。
+if test "${RUN_LOCAL}" != ""; then
+ cd "/ext"
+ phpize
+ ./configure --with-php-config=$(which php-config)
+ make clean
+ make -j$(nproc)
+ TEST_PHP_ARGS="--show-diff -q" make test
+ exec /bin/bash
+fi
...
-exec /bin/bash
こうして、docker run
時に引き渡す環境変数を書き換えることで、これまで通りローカル環境で実行が出来ます。
Push して数分待つと、 All Passed となりました!
終わりに
実際に触ってみた所感、雛形として整っているし、 Docker さえ動けば開発出来るし、 CI で動作保証出来るし、新規で PHP Extension を作るには便利だなと思いました。
↓ぜひスターを↓
〜雑談〜
「情報は発信するところに集まる」はマジだなぁと思いつつ、「先ず試してみる」ということ[2]をやってみました。
Extension 開発で培われる知見は php-src にも活かせると思うので、気軽に PHP Extension 開発する人が増えると良いですね。
Discussion