PHP 7.4 Direct “C” Calls

Do You “C”?

C language was developed at Bell Labs by Dennis Ritchie in late 1972. Since that time, despite the introduction of its object-oriented cousin C++, this language continues to dominate the programming language landscape. As PHP itself is written in C, the ability to directly load C shared libraries, and to gain direct access to C functions and data structures is an incredibly important addition to the PHP language.  This goal has been accomplished in PHP 7.4 with the introduction of FFI (Foreign Function interface).

 

What the “FFI”?

The idea behind FFI is neither new, nor surprising (given that PHP is written in “C”). FFI was actually first introduced as a PECL extension in January 2004 by Wez Furlong and Ilia Alshanetsky.  Wez and Ilia are both core PHP developers who have worked on dozens of extensions, most notably the PDO (PHP Data Objects) extension.  Given the success of LuaJTI/FFI and Python/CFFI in fast prototyping, however, a newly re-architected version of FFI was proposed in December of 2018 by Dmitry Stogov, and accepted into PHP 7.4.  The proof of concept was an experimental binding of PHP to the TensorFlow library (a machine learning platform), using the new version of FFI.

 

Great Expectations

Before getting into details, it’s extremely important to set expectations.  Do not use FFI if you have a need for speed!  This might seem an odd thing to say given that FFI gives us direct access to “C” language libraries and functions, however the reality is that native PHP functions, and PHP extensions are tightly integrated.  In contrast, making calls to “foreign” functions involves additional translation and overhead.  If you are still not convinced, have a look at this benchmark, taken from the RFC re-introducing FFI to PHP.  The table shows time of execution of an ary3 benchmark from bench.php. Times are stated in seconds; lower is better.  As you can see the problem is not confined to PHP: other languages with FFI implementations see a similar performance drop.

ary3 benchmark

So … Why Bother?

The next logical question is: why bother?  Why use FFI at all?  The answer is threefold.  First and foremost, if you are in the process of evaluating a custom PHP extension, using FFI allows you to do fast prototyping.  You can craft “C” language functions and libraries piece by piece, and test the functionality immediately using FFI.  Another reason is that FFI gives the PHP developer direct access to foreign C libraries and functions, opening a whole new world to the language.  Imagine, for example, that you develop for a customer who owns a custom machine shop.  If each machine is programmable (as most are these days), you can use FFI to tap into an C libraries used.  Finally, FFI includes the ability to preload C libraries in advance, somewhat alleviating the performance drop.

Preparing the C Code

Now that your expectations are set properly, let’s have a look at how this whole thing works.  As a practical example, have a look at this C function, which implements a bubble sort.  For those fortunate developers who were never assigned this as a university C language course assignment, a bubble sort iterates through an array at most “N” times, where “N” represents the number of elements in the array.  On each pass, an element index X is compared with an element index X+1.  If element[X] > element[X+1], the two elements are swapped.  Thus lower values tend to “bubble” up, and higher values bubble down.  When no swaps have been made, the iteration stops.

Here is an example of a bubble sort written in C:

#include <stdio.h>
void bubble_sort(long [], long);
void bubble_sort(long list[], long n) {
  long c, d, t;
  int p;
  for (c = 0 ; c < n - 1; c++) {
    p = 0;
    for (d = 0 ; d < n - c - 1; d++) {
      if (list[d] > list[d+1]) {
        /* Swapping */
        t         = list[d];
        list[d]   = list[d+1];
        list[d+1] = t;
        p++;
      }
    }
    if (p == 0) break;
}}

We then compile this simple function into an object file, and then produce a shared library (*.so) file:

gcc -c -Wall -Werror -fpic bubble.c
gcc -shared -o libbubble.so bubble.o

Creating the FFI Instance in PHP

We are now in a position, in our PHP 7.4 demo app, to define an FFI instance.  As you can see from the code block shown below, we use the FFI::cdef() method to identify the C function signature as the first argument, and the shared library as the second:

$ffi = FFI::cdef(
    "void bubble_sort(long [], long);",
    "./libbubble.so");

We then create simple wrapper code which populates an FFI C array with random values:

$max   = 16;
$array = FFI::new('long[' . $max . ']');
for ($i = 0; $i < $max; $i++) $array[$i] = rand(0,9999);

The code making the actual call to the C function is next:

$ffi->bubble_sort($array, $max);

And finally, assuming we add code to produce properly formatted output, here are the before and after results:

Final Words

In closing, you now have an idea where and when to use the new PHP 7.4 feature, the FFI extension.  You also have an idea how to incorporate a “native” C language function directly into your PHP code.  Happy coding1