Speeding PHP with FFI

Jorge Castro
Cook php
Published in
3 min readAug 23, 2020

FFI is an experimental extension that allows us to use an external library (.so or .dll), without creating an extension. This library could be preloaded or loaded per each request. It is recommended to runs it preloaded but it’s easy to load per request.

What we could do with FFI?

For example, we could use C, C++, Rust, or practically any language that allows us to create a library (C style). It could increase the performance tenfold times.

1) Creating the library

For this example, I will use CLion (ide),mingw64 as the toolchain but we could use any ide or toolchain. I also use Windows.

We will create a shared library for C (shared means a .so or .dll file)

Don’t forget to set the toolchain.

Code

library.c

#include "library.h"const char* ping(const char *pong) {
return pong;
}

This code is simple, it has a simple function with a string argument and it returns the same string, i.e. it is a pong-pong function.

library.h

#define FFI_SCOPE "MYLIB"
#define FFI_LIB "C:\CLionProjects\untitled\cmake-build-debug\libuntitled.dll"
const char* ping(const char *pong);

What is the value of FFI_LIB? Is the path where the library (DLL or so) is compiled. We are not compiled but we need to edit it. So, right now you can write any value.

FFI_SCOPE is also the “scope” (i.e. some sort of namespace) of our library.

Compilation

It is our compilation

====================[ Build | all | Debug ]=====================================
"D:\Program Files\JetBrains\CLion 2020.2\bin\cmake\win\bin\cmake.exe" --build C:\CLionProjects\untitled\cmake-build-debug --target all -- -j 9
Scanning dependencies of target untitled
[ 50%] Building C object CMakeFiles/untitled.dir/library.c.obj
[100%] Linking C shared library libuntitled.dll
[100%] Built target untitled
Build finished

And we found where our library is compiled. So now, we could edit our .h file.

MinGW64 (Optional)

I’m using the MINGW toolchain. It includes GCC libraries and other stuff. If we want to use their libraries, then we could add to the path or we could include the libraries inside ours.

CMakeLists.txt

cmake_minimum_required(VERSION 3.17)
project(untitled7)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_EXE_LINKER_FLAGS "-static")
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static")
set(CMAKE_SHARED_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static")
add_library(untitled7 SHARED library.cpp library.h)

We need to add the lines -static-libgcc for C or -static-libstdc++ for C++ (or both).

2) Configuring PHP

First, we need to install and enable the FFI extension. it requires PHP 7.4 or higher.

If you are using Linux, then you will need to recompile with the FFI extension.

php.ini

extension=ffi
[ffi]
ffi.enable=true

Note: We shouldn’t forget to restart our web server.

We could preload FFI but for our example, we will load it per request.

PHPInfo should show the next information

3) Our PHP code (First alternative)

There are two ways to call our code, with FFI::cdef and FFI:load. FFI::cdef requires the library and the definition of it.

<?php$file='C:\CLionProjects\untitled\cmake-build-debug\libuntitled.dll'; // the path of our dll
$ffi=FFI::cdef('const char* ping(const char *pong);',$file);
var_dump($ffi->ping("hello world")); // string(11) "hello world"

4) Our PHP Code (Second alternative)

The second alternative is to use the .h file defined in our code. This code requires that our .h file has the next lines

#define FFI_SCOPE "MYLIB"
#define FFI_LIB "C:\CLionProjects\untitled\cmake-build-debug\libuntitled.dll"

And we could call our code as follow

<?php$hfile='C:\CLionProjects\untitled\library.h'; // the path of our .h file.
$r2=FFI::load($hfile);
var_dump($r2->ping("hello world")); // string(11) "hello world"

--

--