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(mixed $type [, bool $own = true [, bool $persistent = false]]): FFI\CData
Creates native data structure of given C type. $type may be any valid C string declaration or an instance of FFI\CType created before. Using the second argument, it's possible to create owned data (default), or unmanaged. In first case, data structure is going to leave together with returned FFI\CData object, and die when last reference is released by regular PHP reference counting or GC. However, in some cases, programmer may decide to keep C data even after, releasing of FFI\CData object and manually free it through FFI::free() similar to regular C. By default, the memory for the data is allocated on PHP request heap (using emalloc()), but it's also possible to use system heap, specifying true in the third argument.
This function may be called statically and use only predefined C type names (e.g int, char, etc), or as a method or previusly created FFI object. In last case, it may reuse any types declared in constructor.
...
FFI::free(FFI\CData $cdata): void
Manually release previously created “not-owned” data structure.
FFI::cast(mixed $type, FFI\CData $cdata): FFI\CData
Performs C type cast. It creates a new FFI\CData object, that references the same C data structure but using different type. The resulting object doesn't own the C data, and the source $cdata must relive the result. C type mat be specified as any valid C type declaration string or FFI\CType object created before.
This function may be called statically and use only predefined C type names (e.g int, char, etc), or as a method or previusly created FFI object. In last case, it may reuse any types declared in constructor.
FFI::type(string $type): FFI\CType
Creates FF\CType object
FFI::array
FFI::addr
FFI::type(FFI\CData $data): FFI\CType
Returns type of the given FFI::CData
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
- the version(s) it was merged into
- a link to the git commit(s)
- a link to the PHP manual entry for the feature
- a link to the language specification section (if any)
References
Rejected Features
Keep this updated with features that were discussed on the mail lists.