Базовая работа с модулем FFI

Перед погружением в детали API-интерфейса модуля FFI, давайте рассмотрим несколько примеров упрощённой работы API модуля FFI в стандартных задачах.

Замечание: Для ряда этих примеров потребуется библиотека libc.so.6. Они не будут работать в системах, где библиотека недоступна.

Пример #1 Вызов функции из общей библиотеки

<?php

// Создаём объект FFI, загружаем библиотеку libc и экспортируем функцию printf()
$ffi = FFI::cdef(
    "int printf(const char *format, ...);", // Это стандартная декларация в языке C
    "libc.so.6"
);

// Вызываем функцию printf()
$ffi->printf("Привет, %s!\n", "мир");

?>

Результат выполнения приведённого примера:

Привет, мир!

Замечание: Обратите внимание, что для отдельных функций языка 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");

// Вызываем функцию gettimeofday()
var_dump($ffi->gettimeofday(FFI::addr($tv), FFI::addr($tz)));

// Получаем доступ к полю структуры данных языка C
var_dump($tv->tv_sec);

// Выводим структуру данных
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"
);

// Выводим значение переменной 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

// Создаём структуру данных
$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

<?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)