PHP's FFI (Foreign Function Interface) extension allows us to call C language functions and data structures directly in PHP, which is very useful in handling performance-sensitive or underlying interaction scenarios. But once you use it in depth, you will find some hidden "mines", such as - what happens if you forget to write struct when declaring a struct in FFI::cdef() ?
Suppose we have a simple C structure:
typedef struct {
int x;
int y;
} Point;
We use PHP's FFI::cdef() declaration:
$ffi = FFI::cdef("
typedef struct {
int x;
int y;
} Point;
");
Then you can use it like this:
$point = $ffi->new("Point");
$point->x = 10;
$point->y = 20;
echo "Point: ({$point->x}, {$point->y})\n";
Everything is running normally. But now the question is: What happens if you forget to write struct ?
In C language, struct Point and Point are two different namespaces. Unless you use typedef , Point is not a valid type name. In PHP's FFI::cdef() , the situation is slightly different - its parsing rules are slightly different from those of the C compiler, and the fault tolerance is low.
For example, you wrote a statement like this:
$ffi = FFI::cdef("
typedef struct Point {
int x;
int y;
} Point;
");
$pt = $ffi->new("Point");
This code is OK. But if you carelessly miss the typedef part, for example:
$ffi = FFI::cdef("
struct Point {
int x;
int y;
};
");
$pt = $ffi->new("Point"); // An error occurred
Here $ffi->new("Point") will throw an exception, prompting that the type "Point" cannot be found. Because at this time "Point" is a struct Point , not a separate type alias.
The correct call should be:
$pt = $ffi->new("struct Point");
This is the pitfall that many people fall into: they think that they have defined the structure name "Point", but in fact, you only define "struct Point". If you don't do typedef, you cannot use Point directly.
If you see an error like the following:
FFI\Exception: undefined C type 'Point'
Then you first need to check how you declare it in cdef() . Did you just write struct Point without typedef? If so, you can only use struct Point to reference.
A debugging technique is to print out your cdef() declaration to check whether the structure has typedef, for example:
echo $ffi->cdef;
Although this method does not always work (especially in some versions of PHP), it can help you confirm that the type is registered correctly.
To avoid confusion, it is recommended that you bring typedef in all structural declarations. For example:
typedef struct {
int x;
int y;
} Point;
In this way, you can happily write $ffi->new("Point") in PHP, and no longer have to worry about whether to add struct prefix.
Suppose you define a structure that nests another structure:
typedef struct {
int x;
int y;
} Point;
typedef struct {
Point start;
Point end;
} Line;
FFI::cdef() in PHP should be written like this:
$ffi = FFI::cdef("
typedef struct {
int x;
int y;
} Point;
typedef struct {
Point start;
Point end;
} Line;
");
Then you can use it like this:
$line = $ffi->new("Line");
$line->start->x = 1;
$line->start->y = 2;
$line->end->x = 10;
$line->end->y = 20;
echo "Start: ({$line->start->x}, {$line->start->y})\n";
echo "End: ({$line->end->x}, {$line->end->y})\n";
If you miss typedef in the declaration, such as Point does not have typedef, then the definition of Line will report an error because it cannot find the Point type.
When calling C's structure using PHP FFI, be sure to pay attention to the following points:
If the structure is not typedef, the type name must be prefixed with struct
It is recommended that all structures use typedef declarations to avoid naming confusion
When debugging, I encountered an undefined type error. First check whether the declarations in cdef are consistent.
Through these specifications, you can reduce a lot of inexplicable type errors, allowing you to reduce pitfalls and lose hair when playing C interface with PHP.
For more examples about PHP FFI, please refer to https://gitbox.net/docs/php-ffi-examples .