symplely / zend-ffi
Provides the base API for creating extensions, or modifying Zend/PHP internal core with FFI.
Installs: 2 341
Dependents: 1
Suggesters: 0
Security: 0
Stars: 11
Watchers: 2
Forks: 3
Open Issues: 0
Language:C
Type:project
Requires
- php: >7.4
- ext-ffi: *
This package is auto-updated.
Last update: 2024-10-23 04:36:54 UTC
README
Provides the base API for creating extensions, or modifying Zend/PHP
internal core with FFI.
- For PHP 7.4, 8.0, 8.1, 8.2, Windows, Mac, Linux
It allows loading of shared libraries (.dll
or .so
), calling of C functions and accessing of C type data structures in pure PHP
.
This package breaks down the Zend extension API
into PHP classes getting direct access to all PHP internal actions. You can actually change PHP default behaviors. You will be able to get the behavior of componere extension without needing it.
Many routines here is a rewrite of Z-Engine library package that use different terminology and setup structure. This package follow/keep PHP C
source code style with a skeleton FFi installation process.
There are many extensions on Pecl that hasn't been updated.
Installation
For normal standalone usage.
composer require symplely/zend-ffi
To setup a skeleton for FFI integration.
composer create-project symplely/zend-ffi .
Minimum php.ini
setting:
extension=ffi extension=openssl extension=sockets zend_extension=opcache [opcache] ; Determines if Zend OPCache is enabled opcache.enable=1 ; Determines if Zend OPCache is enabled for the CLI version of PHP opcache.enable_cli=1 [ffi] ; FFI API restriction. Possible values: ; "preload" - enabled in CLI scripts and preloaded files (default) ; "false" - always disabled ; "true" - always enabled ffi.enable="true" ; List of headers files to preload, wildcard patterns allowed. `ffi.preload` has no effect on Windows. ; See headers directory for `your-php-version`, this feature is untested, since not enabled for Windows. ;ffi.preload=path/to/vendor/symplely/zend-ffi/headers/ze(your-php-version).h ;This feature is untested. ;opcache.preload==path/to/vendor/symplely/zend-ffi/preload.php
For a simple FFI integration process create/edit:
ffi_extension.json
each package/library should list the files to preload, will be process byffi_preloader.php
script..ignore_autoload.php
will be called/executed bycomposer create-project your_package .cdef/foldername
event.- This event is only called when your package is installed by
composer create-project
command.
- This event is only called when your package is installed by
.preload.php
for general common FFI functions to be used, change thetag_changeMe
skeleton name..github\workflows\*.yml
these GitHub Actions is designed for cross-compiling and committing thebinary
back to your repo, changesome_lib
andsome_repo
skeleton names.- The idea of this is to make installation totally self-contained, the necessary third party library binary is bundled in.
- The CI build Actions is setup for manually runs only.
// Skeleton for `ffi_extension.json` file { // The same name to be used in `composer create-project package .cdef/foldername` "name": "foldername", // Either "preload": { "files": [ "path/to/file.php", "...", "..." ], // Or "directory": [ "path/to/directory" ] } }
Documentation
The functions in preload.php and Functions.php should be used or expanded upon.
See tests folder for examples. Copy/paste the code between --FILE--
and --EXPECT--
blocks in the .phpt files.
For general FFI C data
handling see CStruct class.
Functions c_int_type()
, c_struct_type()
, c_array_type()
and c_typedef()
are wrappers for any C data typedef turning it into PHP CStruct class instance, with all FFI functions as methods with additional features.
For AST handling:
zend_parse_string()
will convert PHP source code into native C datazend_ast
node held in ZendAst class, useprint_ast()
to display results.zend_ast_process(function(\ZE\AstProcess $hook){})
to intercept and modify AST after compilation process.
Get the behavior of PHP extensions like nikic/php-ast and sgolemon/astkit that provide low-level bindings to the underlying AST structures, without any addition library.
The whole PHP lifecycle process can be achieved by just extending StandardModule abstract class.
declare(strict_types=1); require 'vendor/autoload.php'; final class SimpleCountersModule extends \StandardModule { protected string $module_version = '0.4'; //Represents ZEND_DECLARE_MODULE_GLOBALS macro. protected string $global_type = 'unsigned int[10]'; // Do module startup? protected bool $m_startup = true; protected bool $r_startup = true; // Represents PHP_MINIT_FUNCTION() macro. public function module_startup(int $type, int $module_number): int { echo 'module_startup' . \PHP_EOL; return \ZE::SUCCESS; } // Represents PHP_RINIT_FUNCTION() macro. public function request_startup(...$args): int { echo 'request_startup' . \PHP_EOL; $data = $this->get_globals(); $data[5] = 25; return \ZE::SUCCESS; } // Represents PHP_GINIT_FUNCTION() macro. public function global_startup(\FFI\CData $memory): void { if (\PHP_ZTS) { \tsrmls_activate(); } echo 'global_startup' . \PHP_EOL; \FFI::memset($this->get_globals(), 0, $this->globals_size()); } } $module = new SimpleCountersModule(); if (!$module->is_registered()) { $module->register(); $module->startup(); } // Represents ZEND_MODULE_GLOBALS_ACCESSOR() macro. $data = $module->get_globals(); $module->get_globals('4', 20); $data[0] = 5; $data[9] = 15; var_dump($data); ob_start(); phpinfo(8); $value = ob_get_clean(); preg_match('/simple_counters support => enabled/', $value, $matches); var_dump($matches[0]);
A hack for headers_sent()
like errors:
PHP Warning: Cannot modify header information - headers already sent by (output started at xxxxxxxx
require 'vendor/autoload.php'; function headers_sent_reset() { zend_sg('headers_sent', 0); } echo 'any non-buffered output'; var_dump(headers_sent()); // true headers_sent_reset(); var_dump(headers_sent()); // false // This would have otherwise produced warning/errors! header('Location: http://www.example.com/');
To create proper FFI C library headers from any C
ABI library *.h
file
Linux:
cpp -P -D"__attribute__(ARGS)=" path/to/original/header.h -o ffi_header.h
Windows:
First download mcpp
mcpp -P -D"__attribute__(ARGS)=" path/to/original/header.h -o ffi_header.h
The option -I <directory>
might be needed to search/find additional include sources, and the output file will still need editing, mostly 96% proper headers, FFI
will complain, just edit/check the 2 lines after the indicated line.
Reference/Credits
-
Getting Started with PHP-FFI Youtube
Possible Security Risks
The Beginning
Contributing
Contributions are encouraged and welcome; I am always happy to get feedback or pull requests on Github :) Create Github Issues for bugs and new features and comment on the ones you are interested in.
License
The MIT License (MIT). Please see License File for more information.