php in ruby
TRANSCRIPT
PHP in Ruby
2012/09/01 闇 PHP 勉強会
do_aki
@do_aki
http://do-aki.net/
AGENDA
1. 概要 2. 導入
3. 深部
概要
Ruby on PHP
Ruby on PHP sample (PHP Script)
<?php
ruby_eval(<<<'EOC' def hello_ruby_on_php "HELLO Ruby on PHP!!!" endEOC);
echo ruby_eval('hello_ruby_on_php()');
Ruby on PHP の 構造
php-extension
libruby
php script
反転させてみた
Ruby on PHP の 構造
ruby-extension
libphp5
ruby script
embed
SAPI
php_embed sample (Ruby Script)
# encoding: UTF-8require 'php_embed'
p PhpEmbed.eval('1')p PhpEmbed.eval('true')p PhpEmbed.eval('null')p PhpEmbed.eval('array(1,2,3)')
# 1# true# nil# [1, 2, 3]
導入
Install
1. php-embed SAPI を用意– コンパイル– OS Package (php-embedded / php-
devel)
2. ruby 1.9 環境– 1.8 では動きません– rbenv/ruby-builid が便利
3. gem install php-embed
CentOS6
php-embed SAPI
• ./configure --enable-embed=shared (or static)
• Windows 版もある
• 開発止まってる……?
EXPERIMENTAL
php-build 使ってる場合はPHP_BUILD_CONFIGURE_OPTS=‘--enable-embed’で指定しましょー
ruby 1.9
• OS 標準はだいたい 1.8
• rbenv / ruby-build 使えば導入は簡単– https://github.com/sstephenson/rbenv– https://
github.com/sstephenson/ruby-build
ruby 1.9 構築手順$ cd $ git clone git://github.com/sstephenson/rbenv.git .rbenv$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
# ここで再ログイン
$ mkdir -p ~/.rbenv/plugins$ cd ~/.rbenv/plugins$ git clone git://github.com/sstephenson/ruby-build.git
$ rbenv install 1.9.3-p194
readme に書いてあるけどね
gem install php-embed
• gem : Ruby のパッケージ管理ツール– 1.9 からは標準– rubygems.org– Pure Ruby or C 拡張 関係ないみたい
• bundler で超簡単にライブラリ公開
PHP Programming (in Ruby)
# encoding: UTF-8require 'php_embed'
p PhpEmbed::VERSIONp PhpEmbed.call('phpversion')
p PhpEmbed.call(‘array_intersect’, [1,2,3], [2,3,4]) # [2,3]
深部
php-embed の簡単な使い方#include <sapi/embed/php_embed.h>
int main(int argc, char* argv[]) { PHP_EMBED_START_BLOCK(argc, argv); zend_eval_string( "echo 'Hello Embeded PHP Workd';" , NULL, (char*)"Embeded PHP" TSRMLS_CC );
PHP_EMBED_END_BLOCK();return 0;
}// gcc sample.c –lphp5
zend_eval_stringl
• php code を実行– create_function / preg / ereg で使われてる
• 第3引数 retval_ptr– NULL -> そのまま実行– 非 NULL -> “return” と “ ;” で囲んで実行
• zend_compile_string (= compile_string) – Zend/zend_language_scanner.c
• zend_execute (=dtrace_execute or execute)– Zend/zend_dtrace.c– Zend/zend_vm_execute.h
PHP_EMBED_START(END)_BLOCK
• マクロ• ZTS (Zend Thread Safety) の有無で分
岐
• php_embed_init• try – catch • php_embed_shutdown
PHP_EMBED_*_BLOCK を展開(非ZTS)
int main(int argc, char* argv[]){
php_embed_init(argc, argv);zend_first_try {
zend_eval_string(...)} zend_catch {
} zend_end_try();php_embed_shutdown(TSRMLS_C);
return 0;}
さらに展開
php_embed_init(argc, argv);EG(bailout)=NULL; {
JMP_BUF *__orig_bailout = EG(bailout);JMP_BUF __bailout;
EG(bailout) = &__bailout;if (SETJMP(__bailout)==0) {
zend_eval_string(...)} else {
EG(bailout) = __orig_bailout; }
EG(bailout) = __orig_bailout;}php_embed_shutdown(TSRMLS_C);
出力のハンドリングstatic int php_ub_write(const char *str, unsigned int str_length TSRMLS_DC) { return str_length }
static void php_log_message(char *message) {}
static void php_sapi_error(int type, const char *fmt, ...) {}
php_embed_module.ub_write = php_ub_write;php_embed_module.log_message = php_log_message;php_embed_module.sapi_error = php_sapi_error;
zval と VALUE• zval– php の内部表現– _zval_struct 構造体– = zvalue_value 構造体 + リファレンスカウン
タ
• VALUE– ruby の内部表現– ポインタと同じサイズを持つ unsigned な整数– RVALUE 構造体が実体 (FIXNUM を除いて )
構造体
ポインタ
TYPE(VALUE)
• T_NONE• T_NIL• T_OBJECT• T_CLASS• T_ICLASS• T_MODULE• T_FLOAT• T_STRING• T_REGEXP• T_ARRAY• T_HASH• T_STRUCT• T_BIGNUM
• T_FILE• T_FIXNUM• T_TRUE• T_FALSE• T_DATA• T_MATCH• T_SYMBOL• T_RATIONAL• T_COMPLEX• T_UNDEF• T_NODE• T_ZOMBIE
Z_TYPE (zval)
• IS_NULL• IS_LONG• IS_DOUBLE• IS_BOOL• IS_ARRAY• IS_OBJECT• IS_STRING• IS_RESOURCE• IS_CONSTANT• IS_CONSTANT_ARRAY• IS_CALLABLE
相互変換• それぞれの最終的な内部表現に応じて変
換• いずれも 言語が用意しているマクロや関
数を介して操作
• まだ対応してない型も多い• RESOURCE 型は難しいかもね
嵌ったこと• Ruby 側も、 setjmp / longjmp を使っ
ているらしい• 入れ子状態によってはコアダンプ
最後に• ruby から php のコードを実行できる
ライブラリを作りました
• もうちょいきちんと作り込んで PHP Conference 2012 で発表するつもり
1
Questions?