rfc:ffi

This is an old revision of the document!


PHP RFC: FFI - Foreign Function Interface

  • Version: 0.9
  • Date: 2013-12-04
  • Author: Dmitry Stogov, dmitry@zend.com
  • Status: Under Discussion
  • First Published at: https://wiki.php.net/rfc/ffi

Introduction

FFI is one of the features that made Python and LuaJIT very useful for fast prototyping. It allows calling C functions and use C data types from pure scripting language and therefore develop “system code” more productively. For PHP, FFI opens a way to write PHP extensions and bindings to C libraries in pure PHP.

Proposal

It is proposed to extend PHP with a simple FFI API designed after LuaJTI/FFI and Python/CFFI (actually, the second was based on the first). This API allows loading shared libraries (.DLL or .so), calling C functions and accessing C data structures, in pure PHP, without learning 3rd “intermediate” language.

The public API is implemented as a single class FFI with few static methods (some of them may be called non-statically), and overloaded object methods, that perform actual interaction with C data. Before diving into the details of FFI API, take a look into few examples, to see, how it's simple to use this API for regular tasks.

Calling a function from shared library

<?php
// create FFI object, loading libc and exporting function printf()
$ffi = new FFI(
    "int printf(const char *format, ...);", // this is regular C declaration
    "libc.so.6");
// call C printf()
$ffi->printf("Hello %s!\n", "world");

Calling a function, returning structure through argument

<?php
// create gettimeofday() binding
$ffi = new FFI("
    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");
// calls C 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);

Accessing C variables

<?php
// create FFI object, loading libc and exporting errno variable
$ffi = new FFI("int errno;", // this is regular C declaration
    "libc.so.6");
// print C errno
var_dump($ffi->errno);

Working with C arrays

<?php
// create C data structure
$a = FFI::new("unsigned char[1024*1024]"); // "FFI::new" is different from "new FFI"
// work with it like with regular PHP array
for ($i = 0; $i < 1024 * 1024; $i++) {
  $a[$i] = $i;
}
var_dump($a[25]);
var_dump(count($a));
$sum = 0;
foreach ($a as $n) {
  $sum += $n;
}
var_dump($sum);
var_dump(FFI::sizeof($a));

PHP FFI API

FFI::__construct([string $cdef = "" [, string $lib = null]])

Creates a new FFI object. The first optional argument is a string, containing a sequence of declarations in regular C languages (types, structures, functions, variables, etc). Actually, this string may be copy-pasted from C header files. All the declared entities are going to be available to PHP through overloaded functions or other FFI API functions. Declared C variables may be accessed as FFI object properties, C functions - called as FFI object methods. C type names may be used to create new C data structures... The second optional argument is a shared library file name, to be loaded and linked with definitions.

We don't support C preprocessor, yet. #include, #define and CPP macros don't work.

FFI::new

FFI::free

FFI::cast

FFI::type

FFI::array

FFI::addr

FFI::sizeof

FFI::alignof

FFI::memcpy

FFI::memcmp

FFI::memset

FFI::string

FFI::load

FFI::scope

Backward Incompatible Changes

None, except of introduced FFI class and namespace.

Proposed PHP Version(s)

PHP 7.4

RFC Impact

To Opcache

FFI is designed in conjunction with preloading (curently implemented as part of opcache). FFI C headers may be loaded during preloading by FFI::load() and become available to all the following HTTP requests without reloading overhead.

php.ini Defaults

ffi.enable=false|preload|true 

allows enabling or disabling FFI API usage, or restricting it only to preloaded files. The default value is preload

Open Issues

Make sure there are no open issues when the vote starts!

Future Scope

Currently, the performance of C data structures access is worst, then access of native PHP data structures (arrays and objects). This is a common problem of LuaJIT (in interpretator mode) and Python as well. However, LuaJIT may also compile these access code in very efficient way (almost as C compiler), and produce highly efficient machine code. It's planned to try similar things, implementing JIT for PHP.

Proposed Voting Choices

Include FFI extension into PHP-7.4 This project requires 50%+1 majority

Patches and Tests

Implementation

After the project is implemented, this section should contain

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature
  4. a link to the language specification section (if any)

References

Rejected Features

Keep this updated with features that were discussed on the mail lists.

rfc/ffi.1543922975.txt.gz · Last modified: 2018/12/04 11:29 by dmitry