{"id":13700106,"url":"https://github.com/dstogov/php-ffi","last_synced_at":"2025-04-05T21:11:43.884Z","repository":{"id":149146402,"uuid":"129404630","full_name":"dstogov/php-ffi","owner":"dstogov","description":"PHP Foreign Function Interface","archived":false,"fork":false,"pushed_at":"2020-10-29T07:16:19.000Z","size":364,"stargazers_count":571,"open_issues_count":20,"forks_count":32,"subscribers_count":36,"default_branch":"master","last_synced_at":"2025-03-29T20:09:49.464Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dstogov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-04-13T13:15:46.000Z","updated_at":"2025-02-07T08:10:25.000Z","dependencies_parsed_at":null,"dependency_job_id":"81efe27d-6b74-49ab-9db5-ed162e1d4926","html_url":"https://github.com/dstogov/php-ffi","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dstogov%2Fphp-ffi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dstogov%2Fphp-ffi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dstogov%2Fphp-ffi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dstogov%2Fphp-ffi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dstogov","download_url":"https://codeload.github.com/dstogov/php-ffi/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247399886,"owners_count":20932880,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T20:00:48.655Z","updated_at":"2025-04-05T21:11:43.864Z","avatar_url":"https://github.com/dstogov.png","language":"C","funding_links":[],"categories":["C","跨语言"],"sub_categories":[],"readme":"# FFI PHP extension (Foreign Function Interface)\n\n\u003e This extension has been merged into php main source tree. The latest version is available as part of PHP sourcses at [php.net](https://www.php.net/downloads.php) or at [github mirror](https://github.com/php/php-src/tree/master/ext/ffi) !\n\nFFI PHP extension provides a simple way to call native functions, access native variables and create/access data structures defined in C language. The API of the extension is very simple and demonstrated by the following example and its output.\n\n```php\n\u003c?php\n$libc = FFI::cdef(\"\n    int printf(const char *format, ...);\n    const char * getenv(const char *);\n    unsigned int time(unsigned int *);\n\n    typedef unsigned int time_t;\n    typedef unsigned int suseconds_t;\n\n    struct timeval {\n        time_t      tv_sec;\n        suseconds_t tv_usec;\n    };\n\n    struct timezone {\n        int tz_minuteswest;\n        int tz_dsttime;\n    };\n\n\tint gettimeofday(struct timeval *tv, struct timezone *tz);\n\", \"libc.so.6\");\n\n$libc-\u003eprintf(\"Hello World from %s!\\n\", \"PHP\");\nvar_dump($libc-\u003egetenv(\"PATH\"));\nvar_dump($libc-\u003etime(null));\n\n$tv = $libc-\u003enew(\"struct timeval\");\n$tz = $libc-\u003enew(\"struct timezone\");\n$libc-\u003egettimeofday(FFI::addr($tv), FFI::addr($tz));\nvar_dump($tv-\u003etv_sec, $tv-\u003etv_usec, $tz);\n?\u003e\n```\n\n```\nHello World from PHP!\nstring(135) \"/usr/lib64/qt-3.3/bin:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/home/dmitry/.local/bin:/home/dmitry/bin\"\nint(1523617815)\nint(1523617815)\nint(977765)\nobject(FFI\\CData:\u003cstruct\u003e)#3 (2) {\n  [\"tz_minuteswest\"]=\u003e\n  int(-180)\n  [\"tz_dsttime\"]=\u003e\n  int(0)\n}\n```\n\nFFI::cdef() takes two arguments (both are optional). The first one is a collection of C declarations and the second is DSO library. All variables and functions defined by first arguments are bound to corresponding native symbols in DSO library and then may be accessed as FFI object methods and properties. C types of argument, return value and variables are automatically converted to/from PHP types (if possible). Otherwise, they are wrapped in a special CData proxy object and may be accessed by elements.\n\nIn some cases (e.g. passing C structure by pointer) we may need to create a real C data structures. This is possible using FFF::new() method. It takes a C type definition and may reuse C types and tags defined by FFI::cdef().\n\nIt's also possible to use FFI::new() as a static method to create arbitrary C data structures.\n\n``` php\n\u003c?php\n$p = FFI::new(\"struct {int x,y;} [2]\");\n$p[0]-\u003ex = 5;\n$p[1]-\u003ey = 10;\nvar_dump($p);\n```\n\n```\nobject(FFI\\CData:\u003cstruct\u003e[2])#1 (2) {\n  [0]=\u003e\n  object(FFI\\CData:\u003cstruct\u003e)#2 (2) {\n    [\"x\"]=\u003e\n    int(5)\n    [\"y\"]=\u003e\n    int(0)\n  }\n  [1]=\u003e\n  object(FFI\\CData:\u003cstruct\u003e)#3 (2) {\n    [\"x\"]=\u003e\n    int(0)\n    [\"y\"]=\u003e\n    int(10)\n  }\n}\n```\n\n### API Reference\n\n##### function FFI::cdef([string $cdef = \"\" [, string $lib = null]]): FFI\n\n##### Call Native Functions\n\nAll functions defined in FFI::cdef() may be called as methods of the created FFI object.\n\n```php\n$libc = FFI::cdef(\"const char * getenv(const char *);\", \"libc.so.6\");\nvar_dump($libc-\u003egetenv(\"PATH\"));\n```\n\n##### Read/Write Values of Native Variables\n\nAll functions defined in FFI::cdef() may be accessed as properties of the created FFI object.\n\n```php\n$libc = FFI::cdef(\"extern int errno;\", \"libc.so.6\");\nvar_dump($libc-\u003eerrno);\n```\n\n##### function FFI::type(string $type): FFI\\CType\n\nThis function creates and returns a **FFI\\CType** object, representng type of the given C type declaration string.\n\nFFI::type() may be called statically and use only predefined types, or as a method of previously created FFI object. In last case the first argument may reuse all type and tag names defined in FFI::cdef().\n\n\n##### function FFI::typeof(FFI\\CData $type): FFI\\CType\n\nThis function returns a **FFI\\CType** object, representing the type of the given **FFI\\CData** object.\n\n##### static function FFI::arrayType(FFI\\CType $type, array $dims): FFI\\CType\n\nConstructs a new C array type with elements of $type and dimensions specified by $dims.\n\n##### function FFI::new(mixed $type [, bool $own = true [, bool $persistent = false]]): FFI\\CData\n\nThis function may be used to create a native data structure. The first argument is a C type definition. It may be a **string** or **FFI\\CType** object. The following example creates two dimensional array of integers.\n\n```php\n$p = FFI::new(\"int[2][2]\");\nvar_dump($p, FFI::sizeof($p));\n```\n\nFFI::new() may be called statically and use only predefined types, or as a method of previously created FFI object. In last case the first argument may reuse all type and tag names defined in FFI::cdef().\n\nBy default **FFI::new()** creates \"owned\" native data structures, that live together with corresponding PHP object, reusing PHP reference-counting and GC. However, in some cases it may be necessary to manually control the life time of the data structure. In this case, the PHP ownership on the corresponding data, may be manually changed, using **false** as the second optianal argument. Later, not-owned CData should be manually deallocated using **FFI::free()**.\n\nUsing the optional $persistent argument it's possible to allocate C objects in persistent memory, through malloc(), otherwise memory is allocated in PHP request heap, through emalloc().\n\n##### static function FFI::free(FFI\\CData $cdata): void\n\nmanually removes previously created \"not-owned\" data structure.\n\n##### Read/Write Elements of Native Arrays\n\nElements of native array may be accessed in the same way as elements of PHP arrays. Of course, native arrays support only integer indexes. It's not possible to check element existence using isset() or empty() and remove element using unset(). Native arrays work fine with \"foreach\" statement.\n\n```php\n$p = FFI::new(\"int[2]\");\n$p[0] = 1;\n$p[1] = 2;\nforeach ($p as $key =\u003e $val) {\n\techo \"$key =\u003e $val\\n\";\n}\n```\n\n##### Read/Write Fields of Native \"struct\" or \"union\"\n\nFields of native struct/union may be accessed in the same way as properties of PHP objects. It's not possible to check filed existence using isset() or empty(), remove them using unset(), and iterate using \"foreach\" statement.\n\n```php\n$pp = FFI::new(\"struct {int x,y;}[2]\");\nforeach($pp as $n =\u003e \u0026$p) {\n\t$p-\u003ex = $p-\u003ey = $n;\n}\nvar_dump($pp);\n```\n\n##### Pointer arithmetic\n\nCData pointer values may be incremented/decremented by a number. The result is a pointer of the same type moved on given offset.\n\nTwo pointers to the same type may be subtracted and return difference (similar to C).\n\n##### static function FFI::sizeof(mixed $cdata_or_ctype): int\n\nreturns size of C data type of the given **FFI\\CData** or **FFI\\CType**.\n\n##### static function FFI::alignof(mixed $cdata_or_ctype): int\n\nreturns size of C data type of the given **FFI\\CData** or **FFI\\CType**.\n\n##### static function FFI::memcpy(FFI\\CData $dst, mixed $src, int $size): void\n\ncopies $size bytes from memory area $src to memory area $dst. $src may be any native data structure (**FFI\\CData**) or PHP **string**.\n\n##### static function FFI::memcmp(mixed $src1, mixed $src2, int $size): int\n\ncompares $size bytes from memory area $src1 and $dst2. $src1 and $src2 may be any native data structures (**FFI\\CData**) or PHP **string**s.\n\n##### static function FFI::memset(FFI\\CData $dst, int $c, int $size): void\n\nfills the $size bytes of the memory area pointed to by $dst with the constant byte $c\n\n##### static function FFI::string(FFI\\CData $src [, int $size]): string\n\ncreates a PHP string from $size bytes of memory area pointed by $src. If size is omitted, $src must be zero terminated array of C chars.\n\n##### function FFI::cast(mixed $type, FFI\\CData $cdata): FFI\\CData\n\nCasts given $cdata to another C type, specified by C declaration **string** or **FFI\\CType** object.\n\nThis function may be called statically and use only predefined types, or as a method of previously created FFI object. In last case the first argument may reuse all type and tag names defined in FFI::cdef().\n\n##### static function addr(FFI\\CData $cdata): FFI\\CData;\n\nReturns C pointer to the given C data structure. The pointer is not \"owned\" and won't be free. Anyway, this is a potentially unsafe operation, because the life-time of the returned pointer may be longer than life-time of the source object, and this may cause dangling pointer dereference (like in regular C).\n\n##### static function load(string $filename): FFI;\n\nInstead of embedding of a long C definition into PHP string, and creating FFI through FFI::cdef(), it's possible to separate it into a C header file. Note, that C preprocessor directives (e.g. #define or #ifdef) are not supported. And only a couple of special macros may be used especially for FFI.\n\n``` C\n#define FFI_LIB \"libc.so.6\"\n\nint printf(const char *format, ...);\n```\n\nHere, FFI_LIB specifies, that the given library should be loaded.\n\n``` php\n$ffi = FFI::load(__DIR__ . \"/printf.h\");\n$ffi-\u003eprintf(\"Hello world!\\n\");\n\n```\n\n##### static function scope(string $name): FFI;\n\nFFI definition parsing and shared library loading may take significant time. It's not useful to do it on each HTTP request in WEB environment. However, it's possible to pre-load FFI definitions and libraries at php startup, and instantiate FFI objects when necessary. Header files may be extended with **FFI_SCOPE** define (default pre-loading scope is \"C\"). This name is going to be used as **FFI::scope()** argument. It's possible to pre-load few files into a single scope.\n\n``` C\n#define FFI_LIB \"libc.so.6\"\n#define FFI_SCOPE \"libc\"\n\nint printf(const char *format, ...);\n```\n\nThese files are loaded through the same **FFI::load()** load function, executed from file loaded by **opcache.preload** php.ini directive.\n\n``` ini\nffi.preload=/etc/php/ffi/printf.h\n```\n\nFinally, **FFI::scope()** instantiate an **FFI** object, that implements all C definition from the given scope.\n\n``` php\n$ffi = FFI::scope(\"libc\");\n$ffi-\u003eprintf(\"Hello world!\\n\");\n```\n\n##### Owned and Not-Owned CData\n\nFFI extension uses two kind of native C data structures. \"Owned\" pointers are created using **FFI::new([, true])**, **clone**ed. Owned data is deallocated together with last PHP variable, that reference it. This mechanism reuses PHP reference-counting and garbage-collector.\n\nElements of C arrays and structures, as well as most data structures returned by C functions are \"not-owned\". They work just as regular C pointers. They may leak memory, if not freed manually using **FFI::free()**, or may become dangling pointers and lead to PHP crashes.\n\nThe following example demonstrates the problem.\n\n```php\n$p1 = FFI::new(\"int[2][2]\"); // $p1 is owned pointer\n$p2 = $p1[0];                // $p2 is not-owned part of $p1\nunset($p1);                  // $p1 is deallocated ($p2 became dangling pointer)\nvar_dump($p2);               // crash because dereferencing of dangling pointer\n```\n\nIt's possible to change ownership, to avoid this crash, but this would require manual memory management and may lead to memory leaks\n\n```php\n$p1 = FFI::new(\"int[2][2]\", false); // $p1 is not-owned pointer\n$p2 = $p1[0];\nunset($p1);                         // $p1 CData is keep alive (memory leak)\nvar_dump($p2);                      // works fine, except of memory leak\n```\n\n##### PHP Callbacks\n\nIt's possible to assign PHP function to native function variable (or pass it as a function argument). This seems to work, but this functionality is not supported on all libffi platforms, it is not efficient and leaks resources by the end of request.\n\n##### FFI API restriction\n\nWith FFI users may do almost anything, like in C, and therefor may crash PHP in thousand ways.  It's possible to completely disable or enable all FFI functions using ffi.enable=0/1 configuration directives, or limit FFI usage to preloaded scripts using ffi.enable=preload (this is the default setting). In case FFI is not completely disabled, it's also enabled for CLI scripts. Finally, the restriction affects only FFI functions their selves, but not the overloaded method of created FFI or CData objects.\n\n### Status\n\nIn current state, access to FFI data structures is significantly (about 2 times) slower, than access to PHP arrays and objects. It make no sense to use them for speed, but may make sense to reduce memory consumption.\n\nFFI functionality may be included into PHP-8 core, to provide better interpretation performance and integrate with JIT, providing almost C performance (similar to LuaJIT)\n\n### Requirement\n\n- php-master (7.4)\n- [libffi-3.*](http://sourceware.org/libffi/)\n\n### Install\n\n``` bash\nphpize\n./configure --with-ffi\nmake\nsudo make install\n```\n\n### Real Usage\n\nFFI extension was used to implement [PHP TensorFlow binding](https://github.com/dstogov/php-tensorflow)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdstogov%2Fphp-ffi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdstogov%2Fphp-ffi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdstogov%2Fphp-ffi/lists"}