Einleitung
PHP FFI (Foreign Function Interface) ist eine PHP-Erweiterung, mit der Sie Shared Libraries laden, C-Funktionen aufrufen und auf C-Datenstrukturen zugreifen. Sie können vorhandene C-APIs direkt aus PHP nutzen, ohne selbst eine PHP-Extension zu schreiben. Die offizielle PHP-Dokumentation beschreibt FFI als eine Low-Level- und potenziell gefährliche Funktion. Sie sollte nur von Entwicklern eingesetzt werden, die C und die API der Zielbibliothek verstehen. FFI ist auch keine „Performance-Funktion”. Selbst die offizielle Doku weist darauf hin, dass der Datenstruktur-Zugriff via FFI langsamer ist als native PHP-Arrays oder -Objekte. Der Grund für FFI ist nicht Geschwindigkeit, sondern die Nutzung bestehender C-Bibliotheken aus PHP heraus.Verfügbare Umgebungen und Einschränkungen
Die offizielle PHP-Dokumentation nennt folgende Wege, um die FFI-Erweiterung zu aktivieren:- PHP mit
--with-ffibauen - Unter Windows in
php.iniphp_ffi.dllaktivieren - Über
ffi.enableinphp.inidie Verfügbarkeit steuern
ffi.enable kennt drei Werte:
| Wert | Bedeutung |
|---|---|
true | Die FFI-API ist aktiviert |
false | Die FFI-API ist deaktiviert |
preload | FFI ist nur in der CLI-SAPI und für preloadete Dateien erlaubt |
In Webserver-Umgebungen wird häufig
ffi.enable=false oder ffi.enable=preload gesetzt. Auch bei Hosting-Plattformen einschließlich Laravel Cloud lässt sich FFI in einer klassischen Webanwendung nicht garantiert nutzen.Grundlagen der Nutzung
Die vier zentralen Bausteine von PHP FFI sindFFI::cdef(), FFI::new(), FFI::addr() und FFI::string().
FFI::cdef()erzeugt aus einer C-Deklaration und einem Bibliotheksnamen ein FFI-Objekt.$ffi->new('struct timeval')allokiert die C-Datenstruktur.FFI::addr($tv)erzeugt ein Pointer-Argument wiestruct timeval *.- Auf Struktur-Felder greifen Sie über
$tv->tv_seczu.
FFI::string().
Header-Dateien einlesen
FFI::load() liest Deklarationen aus einer C-Header-Datei. Diese kann FFI_SCOPE und FFI_LIB enthalten.
FFI::cdef() noch FFI::load() gewöhnliche C-Präprozessor-Direktiven verarbeiten. #include, #define und Bedingungskompilate lassen sich nicht direkt übergeben.
In der Praxis wählt man daher meist einen dieser zwei Wege:
Bei einfachen APIs direkt in `FFI::cdef()` schreiben
Sind wenige Typen und Funktionen betroffen, betten Sie die benötigten Deklarationen direkt im PHP-Code ein.
Praxisbeispiel: VOICEVOX Core for PHP
VOICEVOX Core for PHP ist ein reales Beispiel, das die dynamische C-Bibliothek von VOICEVOX CORE aus reinem PHP ansteuert. Die dortigen FFI-Muster sind kompakt zusammengefasst und daher leichter nachzuvollziehen als abstrakte Erklärungen.1. Ein FFI-eigenes Header-File pflegen
Der Original-Header von VOICEVOX Core wird nicht direkt verwendet. Stattdessen werden inheaders/voicevox_core_ffi.h FFI-spezifische Deklarationen ausgelagert, unter anderem für opake Pointer, Structs und free-Funktionen.
2. FFI::cdef() an einer Stelle konzentrieren
src/VoicevoxFFI.php bündelt das Header-Laden und die Bibliothekspfad-Auflösung an einem Ort.
FFI::cdef() nicht direkt aufrufen. Auch Unterschiede beim Bibliothekspfad lassen sich über eine Umgebungsvariable und Betriebssystem-Erkennung abfangen.
3. Out-Parameter kapseln und in Objekte überführen
Der Konstruktor insrc/Synthesizer.php allokiert einen struct VoicevoxSynthesizer* und übergibt dessen Adresse an voicevox_synthesizer_new().
Type **out_value in PHP annehmen:
new('struct VoicevoxSynthesizer*')legt die Pointer-Variable an.FFI::addr($ptr)liefert dasType **-Argument.- Das erhaltene Handle halten Sie als Property des PHP-Objekts.
4. Von C zurückgegebenen Speicher in einen PHP-String kopieren und sofort freigeben
VOICEVOX Core for PHP kopiert JSON-Strings und WAV-Binärdaten zunächst mitFFI::string() in einen PHP-String und ruft anschließend die C-seitige Free-Funktion auf.
Hinweise und Best Practices
| Aspekt | Praxis-Tipp |
|---|---|
| Laufzeitumgebung | Entwerfen Sie zunächst für die CLI |
| Header-Verwaltung | Reichen Sie den Original-Header nicht durch — pflegen Sie FFI-Deklarationen separat |
| Bibliothekspfad | Machen Sie ihn per absolutem Pfad oder Umgebungsvariable umschaltbar |
| Speicherverwaltung | Kopieren Sie über FFI::string() und rufen Sie zwingend die zugehörige Free-Funktion auf |
| Wrapper-Design | Halten Sie CData nicht app-weit sichtbar, kapseln Sie es in eigene Klassen |
| Kompatibilität | Prüfen Sie PHP-Version und ABI-Änderungen der Zielbibliothek gemeinsam |
Fazit
Mit PHP FFI rufen Sie vorhandene C-Bibliotheken aus reinem PHP heraus auf. FFI ist jedoch ein Low-Level- und riskanter Mechanismus, der sich nicht immer in Webservern nutzen lässt. Die Implementierung von VOICEVOX Core for PHP zeigt, was in der Praxis zählt:- Einen eigenen FFI-Header pflegen.
FFI::cdef()und die Bibliothekspfad-Auflösung an einer Stelle bündeln.- Opake Pointer in eigenen Klassen kapseln.
- Von C zurückgegebenen Speicher in PHP kopieren und mit der dedizierten Free-Funktion freigeben.