ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
**Example #1 从共享库调用函数** ``` // create FFI object, loading libc and exporting function printf() $ffi = FFI::cdef(     "int printf(const char *format, ...);", // this is a regular C declaration     "libc.so.6"); // call C's printf() $ffi->printf("Hello %s!\n", "world"); ``` 以上例程会输出: ~~~ Hello world! ~~~ **Example #2 调用函数,通过参数返回结构** ``` // create gettimeofday() binding $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"); // create C data structures $tv = $ffi->new("struct timeval"); $tz = $ffi->new("struct timezone"); // call C's gettimeofday() var_dump($ffi->gettimeofday(FFI::addr($tv), FFI::addr($tz))); // access field of C data structure var_dump($tv->tv_sec); // print the whole C data structure var_dump($tz); ``` 以上例程的输出类似于: ~~~ int(0) int(1555946835) object(FFI\CData:struct timezone)#3 (2) { ["tz_minuteswest"]=> int(0) ["tz_dsttime"]=> int(0) } ~~~ **Example #3 访问现有的C变量** ``` // create FFI object, loading libc and exporting errno variable $ffi = FFI::cdef( "int errno;", // this is a regular C declaration "libc.so.6"); // print C's errno var_dump($ffi->errno); ``` 以上例程会输出: ~~~ int(0) ~~~ **Example #4 创建和修改C变量** ``` // create a new C int variable $x = FFI::new("int"); var_dump($x->cdata); // simple assignment $x->cdata = 5; var_dump($x->cdata); // compound assignment $x->cdata += 2; var_dump($x->cdata); ``` 以上例程会输出: ~~~ int(0) int(5) int(7) ~~~ **Example #5 使用C数组** ``` // create C data structure $a = FFI::new("long[1024]"); // work with it like with a regular PHP array 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) ~~~ 可以将PHP闭包分配给函数指针类型的本机变量,也可以将其作为函数参数传递 ``` $zend = FFI::cdef("     typedef int (*zend_write_func_t)(const char *str, size_t str_length);     extern zend_write_func_t zend_write; ");   echo "Hello World 1!\n";   $orig_zend_write = clone $zend->zend_write; $zend->zend_write = function($str, $len) {     global $orig_zend_write;     $orig_zend_write("{\n\t", 3);     $ret = $orig_zend_write($str, $len);     $orig_zend_write("}\n", 2);     return $ret; }; echo "Hello World 2!\n"; $zend->zend_write = $orig_zend_write; echo "Hello World 3!\n"; ``` 以上例程会输出: ~~~ Hello World 1! { Hello World 2! } Hello World 3! ~~~ >[warning]尽管这样做是有效的,但是并不是所有libffi平台都支持这种功能,这种功能效率不高,并且在请求结束时泄漏资源 因此,建议尽量减少使用PHP回调 ## **一个完整的PHP/FFI/预加载示例** php.ini ~~~ ffi.enable=preload opcache.preload=preload.php ~~~ preload.php ``` FFI::load(__DIR__ . "/dummy.h"); opcache_compile_file(__DIR__ . "/dummy.php"); ``` dummy.h ~~~ #define FFI_SCOPE "DUMMY" #define FFI_LIB "libc.so.6" int printf(const char *format, ...); ~~~ dummy.php ``` final class Dummy {     private static $ffi = null;     function __construct() {         if (is_null(self::$ffi)) {             self::$ffi = FFI::scope("DUMMY");         }     }     function printf($format, ...$args) {        return (int)self::$ffi->printf($format, ...$args);     } } ``` test.php ``` $d = new Dummy(); $d->printf("Hello %s!\n", "world"); ```