🗂
【PHP FFI】C 言語の変数や配列、構造体にアクセスする
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::type
や FFI::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