DEV Community

loading...
Cover image for Speeding PHP with FFI

Speeding PHP with FFI

Jorge Castro
You are free to believe in whatever you want to, me too. So, stop preaching your religion, politics, or belief. Do you have facts? Then I will listen. Do you have a personal belief? Sorry but no.
Originally published at southprojects.com ・3 min read

Speeding PHP with FFI

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 of our tenfold times. In my initial test, a simple loop with doubles executes 800% fastest than in native PHP.

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)

Alt Text

Don't forget to set the toolchain.

Alt Text

Code

library.c

#include "library.h"

const char* ping(const char *pong) {
    return pong;
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

And we found where our library is compiled correctly in some path. So now, we could edit our .h file and put the correct path. We need to do it if we want to use the .h directly in PHP.

2) 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)
Enter fullscreen mode Exit fullscreen mode

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

3) 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
Enter fullscreen mode Exit fullscreen mode

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

Alt Text

4) 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"
Enter fullscreen mode Exit fullscreen mode

5) 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"
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

Discussion (1)

Collapse
_garybell profile image
Gary Bell

If I'm understanding this correctly, this essentially allows PHP to call C (or similar) libraries, and then let the developer put their other logic around it. Essentially, do the heavy lifting in C, and wrap the interactions in PHP and your preferred framework?

Forem Open with the Forem app