Introduzione
PHP FFI (Foreign Function Interface) è un’estensione di PHP che permette di caricare librerie condivise, chiamare funzioni C e accedere a strutture dati C. Senza scrivere estensioni PHP native puoi usare direttamente da PHP API C già esistenti. La documentazione ufficiale di PHP descrive FFI come una funzionalità di basso livello e pericolosa. Va usata solo da chi conosce a fondo C e l’API della libreria di destinazione. FFI non è nemmeno “una funzione per velocizzare”. Anche la documentazione ufficiale spiega che l’accesso alle strutture dati via FFI è più lento rispetto ad array e oggetti PHP nativi. Il motivo per scegliere FFI non è la velocità: è poter usare da PHP librerie C già esistenti.Ambienti in cui FFI è utilizzabile e limitazioni
La documentazione ufficiale di PHP indica i seguenti modi per abilitare l’estensione FFI.- Compilare PHP con
--with-ffi - Su Windows abilitare
php_ffi.dllinphp.ini - Controllare l’abilitazione con
ffi.enableinphp.ini
ffi.enable accetta i seguenti tre valori.
| Valore | Significato |
|---|---|
true | Abilita l’API FFI |
false | Disabilita l’API FFI |
preload | Consente l’API FFI solo per la SAPI CLI e per i file preloaded |
Sui web server è comune trovare
ffi.enable=false o ffi.enable=preload. Anche sugli hosting (incluso Laravel Cloud) non è detto che tu possa usare FFI in una normale web app.Uso di base
Le basi di PHP FFI sono quattro:FFI::cdef(), FFI::new(), FFI::addr() e FFI::string().
FFI::cdef()crea un oggetto FFI da una stringa di dichiarazioni C e dal nome di una libreria condivisa$ffi->new('struct timeval')alloca una struttura dati CFFI::addr($tv)produce un argomento puntatore comestruct timeval *- Puoi accedere ai campi della struttura con
$tv->tv_sec
FFI::string().
Caricare file header
ConFFI::load() puoi caricare le dichiarazioni da un file header C. Nell’header puoi scrivere FFI_SCOPE e FFI_LIB.
FFI::cdef() né FFI::load() supportano le normali direttive del preprocessore C. #include, #define, la compilazione condizionale non possono essere passate così come sono.
Per questo, nella pratica, si sceglie una delle due strade seguenti.
Per API semplici scrivi direttamente in `FFI::cdef()`
Se i tipi e le funzioni da cui dipendi sono pochi, incorpori le sole dichiarazioni necessarie lato PHP.
Caso d’uso reale: VOICEVOX Core for PHP
VOICEVOX Core for PHP è un esempio concreto che usa la libreria dinamica C di VOICEVOX CORE da PHP puro. Raccoglie pattern pratici di FFI, quindi è un caso di studio più concreto delle spiegazioni astratte.1. Un header preparato per FFI
L’header originale di VOICEVOX Core non viene usato così com’è: si estraggono le dichiarazioni utili inheaders/voicevox_core_ffi.h, che espone puntatori opachi, struct e funzioni di free.
2. Concentrare FFI::cdef() in un unico punto
src/VoicevoxFFI.php concentra in un solo punto il caricamento dell’header e la risoluzione del path della libreria.
FFI::cdef() dall’applicazione. Le differenze di path della libreria vengono assorbite da variabile d’ambiente e detection dell’OS.
3. Wrappare gli out parameter in oggetti
Il costruttore insrc/Synthesizer.php alloca struct VoicevoxSynthesizer* e passa l’indirizzo a voicevox_synthesizer_new().
Type **out_value delle API C.
- Crea una variabile puntatore con
new('struct VoicevoxSynthesizer*') - Passa
Type **conFFI::addr($ptr) - Conserva l’handle ottenuto come proprietà dell’oggetto PHP
4. Copia la memoria restituita dal C in una stringa PHP e libera subito
VOICEVOX Core for PHP, quando riceve stringhe JSON o binari WAV, li copia prima in una stringa PHP conFFI::string() e poi chiama subito la funzione di free lato C.
Note e best practice
| Aspetto | Punto pratico |
|---|---|
| Ambiente d’esecuzione | Progetta prima di tutto per la CLI |
| Gestione degli header | Non passare l’header originale; gestisci separatamente dichiarazioni preparate per FFI |
| Path della libreria | Rendilo configurabile via path assoluto o variabile d’ambiente |
| Gestione della memoria | Copia in PHP con FFI::string() e chiama sempre la funzione di free specifica della libreria |
| Design del wrapper | Non diffondere il CData grezzo nell’app: chiudilo in una classe dedicata |
| Compatibilità | Verifica insieme la versione di PHP e le eventuali variazioni di ABI della libreria |
Riepilogo
Con PHP FFI puoi chiamare da PHP puro librerie C già esistenti. Ma FFI è un meccanismo di basso livello e pericoloso, e non è detto che sia utilizzabile su un web server. Guardando l’implementazione di VOICEVOX Core for PHP si nota che nella pratica sono importanti quattro punti.- Preparare un header dedicato all’FFI
- Concentrare in un solo punto
FFI::cdef()e la risoluzione del path della libreria - Incapsulare i puntatori opachi in classi dedicate
- Copiare in PHP la memoria restituita dal C e liberarla con la funzione specifica