🗂

【PHP FFI】C 言語の変数や配列、構造体にアクセスする

2024/05/03に公開

C 言語の変数や配列、構造体を生成して扱ってみた。Gist でも同じコードを公開している。

まず整数型の変数を扱ってみる

<?php

$x = FFI::new('int');
$x->cdata = 5;

var_dump(
    5 === $x->cdata
);

次は配列

<?php

$a = FFI::new('long[10]');
// $a = FFI::new(FFI::type('long[10]'));
// $a = FFI::new(FFI::arrayType(FFI::type('long'), [10]));

$size = count($a);

for ($i = 0; $i < $size; ++$i) {
    $a[$i] = $i + 1;
}

$sum = 0;

foreach ($a as $v) {
    $sum += $v;
}

var_dump(55 === $sum);

多次元配列も利用できる。型の宣言には FFI::typeFFI::arrayType が使える

$a = FFI::new('int[2][2]');
// $a = FFI::new(FFI::type('int[2][2]'));
// $a = FFI::new(FFI::arrayType(FFI::type('int'), [2, 2]));

$a[0][0] = 1;
$a[0][1] = 2;
$a[1][0] = 3;
$a[1][1] = 4;

var_dump($a);

FFI::addr を使って C 言語の文字列を扱うためのポインターをつくることもできる

<?php

$str = '🐶🐱🐘';
$size = strlen($str);

$p = FFI::addr(FFI::new('uint8_t['.$size.']'));

// $p = FFI::addr(
//   FFI::new(FFI::arrayType(FFI::type('uint8_t'), [$size]))
// );

FFI::memcpy($p, $str, $size);

var_dump(
    'F09F90B6' === strtoupper(bin2hex('🐶')),
    0xF0 === ord($str[0]),
    0xF0 === $p[0][0],
    $str === FFI::string($p[0], $size)
);

FFI::free($p);

今度は構造体

<?php

$p = FFI::new('struct {int x,y;}');
$p->x = 1;
$p->y = 2;

var_dump(
    1 === $p->x
);

構造体の要素に文字列を割り当ててみよう

<?php
// https://stackoverflow.com/a/62693231/531320

$name = 'John';
$size = strlen($name);

$struct = FFI::new('struct { uint8_t *name }');
$struct->name = FFI::new('uint8_t['.$size.']', 0);
FFI::memcpy($struct->name, $name, $size);
$ret =  FFI::string($struct->name, $size);

FFI::free($struct->name);

var_dump(
    NULL === $struct->name,
    $name === $ret
);

最後に構造体の配列に取り組んでみよう

<?php

$ffi = FFI::cdef('
struct header {
    uint8_t *name;
    uint8_t * value;
};
');

// https://nghttp2.org/documentation/tutorial-hpack.html#deflate-c
$headers = [
    [':scheme', 'https'],
    [':authority', 'example.org'],
    [':path', '/'],
    ['user-agent', 'libnghttp2'],
    ['accept-encoding', 'gzip, deflate']
];

$size = count($headers);
$nva = $ffi->new('struct header['.$size.']');

$name = $headers[0][0];
$namelen = strlen($name);
$value = $headers[0][1];
$valuelen = strlen($value);

$nva[0]->name = FFI::new('uint8_t['.$namelen.']', 0);
FFI::memcpy($nva[0]->name, $name, $namelen);

$nva[0]->value = FFI::new('uint8_t['.$valuelen.']', 0);
FFI::memcpy($nva[0]->value, $value, $valuelen);

var_dump(
    $name === FFI::string($nva[0]->name, $namelen),
    $value === FFI::string($nva[0]->value, $valuelen),
);

Discussion