## FFI の基本的な使い方

FFI API の詳細に深く立ち入る前に、よくあるタスクに対する FFI API の使い方が どれほど簡単かを示す例をいくつか見てみましょう。

> 注意: これらの例の中には、libc.so.6 を必要とするものがあります。 それらは、このライブラリが利用できないシステムでは動きません。

例1 共有ライブラリの関数を呼ぶ

    <?php
    // FFI オブジェクトを作成し、libc を読み込んで printf() 関数をエクスポートする
    $ffi = FFI::cdef(
        "int printf(const char *format, ...);", // ここは通常の C の宣言
        "libc.so.6");
    // C の printf() を呼ぶ
    $ffi->printf("Hello %s!\n", "world");
    ?>

上の例の出力は以下となります。

    Hello world!

> 注意: C の関数のうちのいくつかは、特定の呼び出し規約 (例: `__fastcall`、 `__stdcall`、`,__vectorcall` など) を必要とすることに注意してください。

例2 関数を呼び出し、構造体を引数経由で返す

    <?php
    // gettimeofday() のバインディングを作成する
    $ffi = FFI::cdef("
        typedef unsigned int time_t;
        typedef unsigned int suseconds_t;

        struct timeval {
            time_t      tv_sec;
            suseconds_t tv_usec;
        };

        struct timezone {
            int tz_minuteswest;
            int tz_dsttime;
        };

        int gettimeofday(struct timeval *tv, struct timezone *tz);
    ", "libc.so.6");
    // C のデータ構造を作成する
    $tv = $ffi->new("struct timeval");
    $tz = $ffi->new("struct timezone");
    // C の gettimeofday() を呼ぶ
    var_dump($ffi->gettimeofday(FFI::addr($tv), FFI::addr($tz)));
    // C のデータ構造のフィールドにアクセスする
    var_dump($tv->tv_sec);
    // C のデータ構造全体を出力する
    var_dump($tz);
    ?>

上の例の出力は、 たとえば以下のようになります。

    int(0)
    int(1555946835)
    object(FFI\CData:struct timezone)#3 (2) {
      ["tz_minuteswest"]=>
      int(0)
      ["tz_dsttime"]=>
      int(0)
    }

例3 既存の C の変数にアクセスする

    <?php
    // FFI オブジェクトを作成し、libc を読み込んで errno 変数をエクスポートする
    $ffi = FFI::cdef(
        "int errno;", // ここは通常の C の宣言
        "libc.so.6");
    // C の errno を出力する
    var_dump($ffi->errno);
    ?>

上の例の出力は以下となります。

    int(0)

例4 C の変数を作成して書き換える

    <?php
    // 新しい C の int 変数を作成する
    $x = FFI::new("int");
    var_dump($x->cdata);

    // 単純な代入
    $x->cdata = 5;
    var_dump($x->cdata);

    // 複合代入
    $x->cdata += 2;
    var_dump($x->cdata);
    ?>

上の例の出力は以下となります。

    int(0)
    int(5)
    int(7)

例5 C の配列を扱う

    <?php
    // C のデータ構造を作成する
    $a = FFI::new("long[1024]");
    // 通常の PHP の配列を扱うのと同じように扱う
    for ($i = 0; $i < count($a); $i++) {
        $a[$i] = $i;
    }
    var_dump($a[25]);
    $sum = 0;
    foreach ($a as $n) {
        $sum += $n;
    }
    var_dump($sum);
    var_dump(count($a));
    var_dump(FFI::sizeof($a));
    ?>

上の例の出力は以下となります。

    int(25)
    int(523776)
    int(1024)
    int(8192)

例6 C の enum を扱う

    <?php
    $a = FFI::cdef('typedef enum _zend_ffi_symbol_kind {
        ZEND_FFI_SYM_TYPE,
        ZEND_FFI_SYM_CONST = 2,
        ZEND_FFI_SYM_VAR,
        ZEND_FFI_SYM_FUNC
    } zend_ffi_symbol_kind;
    ');
    var_dump($a->ZEND_FFI_SYM_TYPE);
    var_dump($a->ZEND_FFI_SYM_CONST);
    var_dump($a->ZEND_FFI_SYM_VAR);
    ?>

上の例の出力は以下となります。

    int(0)
    int(2)
    int(3)

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open