ログ日記

作業ログと日記とメモ

CodeGen_PECLとHaskellを使ってクラスを操作するExtensionを作る

http://d.hatena.ne.jp/n314/20090918/1253256703 の続き。


CodeGen_PECLをインストール。

pear install -a CodeGen_PECL

作業ディレクトリ作成。

mkdir point
cd point
mkdir hs


Haskellのコードを書いてコンパイル

cd hs
emacs point.hs
module PhpPoint where

import Foreign.C.Types
foreign export ccall hsDistance :: CInt -> CInt -> IO CInt

hsDistance :: CInt -> CInt -> IO CInt
hsDistance x y = do
  return (x * y)
ghc -ffi -c point.hs
cd ..


specファイルを書く。

emacs point.xml
<?xml version="1.0" ?>
<extension name="point" version="1.1.2">
  <function role="internal" name="MINIT">
    <code>
      <?data
hs_init(0, 0);
      ?>
    </code>
  </function>
  <function role="internal" name="MSHUTDOWN">
    <code>
      <?data
hs_exit();
      ?>
    </code>
  </function>

  <class name="Point">
    <summary>Point</summary>
    <description>Point class</description>
    <property name="x" access="public" type="int" value="0" />
    <property name="y" access="public" type="int" value="0" />
    <function name="__construct">
      <proto>void __construct([int _x[, int _y]])</proto>
      <code>
        <?data
PROP_SET_LONG(x, _x);
PROP_SET_LONG(y, _y);
        ?>
      </code>
    </function>
  </class>

  <class name="Point_Result">
    <summary>Point_Result</summary>
    <description>Point Result class</description>
    <property name="a" access="public" type="int" value="0" />
    <function name="__construct">
      <proto>void __construct([int _a])</proto>
      <code>
        <?data
PROP_SET_LONG(a, _a);
        ?>
      </code>
    </function>
  </class>

  <class name="Point_Distance">
    <summary>Point_Distance</summary>
    <description>Point Distance class</description>
    <function name="calc">
      <proto>int calc(object Point p)</proto>
      <code>
        <?data
zend_class_entry *ret_ce, *point_ce;
long x, y, dist;

Z_TYPE_P(return_value) = IS_OBJECT;
object_init_ex(return_value, Point_Result_ce_ptr);
ret_ce = Z_OBJCE_P(return_value);

point_ce = Z_OBJCE_P(p);
x = Z_LVAL_P(zend_read_property(point_ce, p, "x", strlen("x"), 1 TSRMLS_CC));
y = Z_LVAL_P(zend_read_property(point_ce, p, "y", strlen("y"), 1 TSRMLS_CC));

dist = hsDistance(x, y);

zend_update_property_long(ret_ce, return_value, "a", strlen("a"), dist TSRMLS_CC
);
        ?>
      </code>
    </function>
  </class>
</extension>

ビルド。

pecl-gen -f point.xml
cd point
./configure --enable-point
make
cd ..
ghc -optl-shared point/.lib/point.o hs/point.o hs/point_stub.o -optl-Wl,-soname \
  -optl-Wl,point.so -o point.so
cp point.so /usr/lib/php5/20060613+lfs/

テスト用のPHPファイルを書いて実行。

<?php
dl('point.so');
$point = new Point(3, 4);
var_dump($point);

$distance = new Point_Distance();
var_dump($distance);

$ret = $distance->calc($point);
var_dump($ret);
% php test.php
object(Point)#1 (2) {
  ["x"]=>
  int(3)
  ["y"]=>
  int(4)
}
object(Point_Distance)#2 (0) {
}
object(Point_Result)#3 (1) {
  ["a"]=>
  int(12)
}


Extensionの中でのクラス操作の仕方が全然分からない。
zend_*_property* 系の関数って、もっとシンプルなのがありそうな気がするけど検索しても全然ヒットしなくて困った。
説明が空のマニュアルが大量に引っかかるし…。


本当はHaskell側にクラスを渡したかったんだけど、zval を渡すわけにもいかないので取り敢えずここまで。
独自クラスを作ろうと思ったらzend_object_store_set_object、zend_object_store_get_object が関係する気がするが分からない。