PHP FFI: Introduction – Part 1


Introduction

Since version 7.4 of PHP, we finally have access to interoperability functions between different languages and PHP: the FFI or Foreign Function Interface.

What is FFI?
It is simply the ability to use an external library (.dll or .so) directly in PHP, without having to create a PHP extension.
Yes, with just a PHP script!
It is, among other things, what has made Python so famous and allowed it to have so many features.

The great thing about FFI is that a PHP developer who is not an expert in C/Rust/Go/Kotlin (non-exhaustive list) can finally bind an external library by themselves. So, if you have a project with a very specific need, a proprietary library, or whatever, and you do not have too much load, you can directly tackle the development of its use.
It rarely happens, but here at partITech, we have had to develop PHP modules. As a result, developing the connection with the library in PHP should make the procedure much simpler and quicker to deploy.

Of course, this functionality comes at a cost since, even though the procedure allows for faster development, it is less quick than a true PHP module written in C/C++.
Indeed, accessing structures with FFI is currently twice as slow as with a native module. Therefore, it is not recommended to use FFI to improve the performance of your application. However, it can greatly help you reduce memory usage on very demanding processes. (source https://www.php.net/manual/en/intro.ffi.php).

Well, enough with the intros and chit-chat. What we want to see is code.
So, I propose that we look at several problems encountered during our tests. Of course, every project is different, but if this article can help you get your hands dirty with the "guts," we’ll be happy.

The Hello World

The basic benchmark test remains the famous "Hello World", so let's begin our journey into the wonderful world of FFI with a C function that will return "Hello World" to us.

hello.c

#include <stdio.h>

 const char * hello() {
   return "Hello, World!";
}

hello.h

export const char * hello();

We can now compile.

gcc -c hello.c

We ask gcc to create our shared library, the famous .so file.

gcc -shared -o hello.so hello.o

There you have it! We now have our material to play with directly in PHP-ffi.

Here is the PHP code that allows us to call our hello() function.

hello.php

#!/usr/bin/php8.1
<?php
$ffi = FFI::cdef(
    "const char *hello();",
    __DIR__ ."/hello.so"
);

echo $ffi->hello();
chmod +x hello.php
./hello.php
Hello, World!

Explanations:

The cdef method allows us to create a new FFI object. The first parameter is a string containing the definition of our library. We are not required to put the entirety of the library's information, only what interests us.

Note that if you use somewhat complex header files, there is a good chance that it will crash directly. From what I've been able to use, I've systematically recreated and simplified the definition files.
We will see in another example how to create and use directly a header file too large to be placed directly in our script. It can quickly become complicated 😊

The second parameter is our shared library. No real need to explain.

Finally, we will directly call the function and echo the result.

Simple... Basic.

Shall we move on to a slightly more advanced example? See you in the second part of our dossier on the wonderful world of Foreign Function Interfaces.

PHP FFI: Passing parameters and using the zend engine - part 2

Thanks to Thomas Bourdin, Cédric Le Jallé, and Stéphane Pechard for their help, advice, and proofreading.