Comparing Data Using FFI

Learn how to compare data using FFI.

It’s important to keep in mind that when we create a C-language data structure using the FFI extension, it exists outside of our PHP application. PHP can interact with the C data to a certain extent. However, for comparison purposes, it’s best to use FFI::memcmp(), as native PHP functions might return inconsistent results.

The two comparison functions available in the FFI extension are summarized here in the following table.

Summary of FFI Class Comparison Methods

FFI Method

Returns

Notes

isNull()

bool

Returns TRUE if the FFI\CData instances is a null pointer.

memcmp()

int

Operates in much the same manner as the PHP strcmp() function or the <=> operator. Returns a value less than 0 if the first FFI\CData instance is less than the second. Returns 0 if both are equal and returns a value greater than 0 if the second argument’s value is greater than the first.

FFI::isNull() can be used to determine whether or not the FFI\CData instance is NULL. What is more interesting is FFI::memcmp(). Although this function operates in the same manner as the spaceship operator (<=>), it accepts a third argument that represents how many bytes we wish to include in the comparison. The following example illustrates this usage:

Press + to interact
<?php
// Create native C data types using FFI
$a = FFI::new("char[6]"); // Array of 6 characters
$b = FFI::new("char[6]");
$c = FFI::new("char[6]");
$d = FFI::new("char[6]");
// Function to populate the given C data array with values
$populate = function ($cdata, $start, $offset, $num) {
// Populate with alpha chars
for ($x = 0; $x < $num; $x++) {
$cdata[$x + $offset] = chr($x + $offset + $start); // Assign character values to the array
}
return $cdata;
};
// Populate the arrays with values
$a = $populate($a, 65, 0, 6); // 'A' to 'F'
$b = $populate($b, 65, 0, 3); // 'A' to 'C'
$b = $populate($b, 85, 3, 3); // 'U' to 'W'
$c = $populate($c, 71, 0, 6); // 'G' to 'L'
$d = $populate($d, 71, 0, 6); // 'G' to 'L'
// Display the contents of the arrays
$patt = "%2s : %6s\n";
printf($patt, '$a', FFI::string($a, 6));
printf($patt, '$b', FFI::string($b, 6));
printf($patt, '$c', FFI::string($c, 6));
printf($patt, '$d', FFI::string($d, 6));
/*
$a : ABCDEF
$b : ABCUVW
$c : GHIJKL
$d : GHIJKL
*/
// Note: The following code snippets are commented out because they will throw fatal errors or exceptions due to incompatible types
// Attempt to compare arrays using PHP <=>
// var_dump($a <=> $b);
// PHP Fatal error: Uncaught FFI\Exception: Comparison of incompatible C types
// Attempt to compare arrays using PHP strcmp()
// var_dump(strcmp($a,$b));
// PHP Fatal error: Uncaught TypeError: strcmp(): Argument #1 ($string1) must be of type string
// Compare arrays using FFI::memcmp()
echo "\nUsing FFI::memcmp()\n";
$p = "%20s : %2d\n";
printf($p, 'memcmp($a, $b, 6)', FFI::memcmp($a, $b, 6));
printf($p, 'memcmp($c, $a, 6)', FFI::memcmp($c, $a, 6));
printf($p, 'memcmp($c, $d, 6)', FFI::memcmp($c, $d, 6));
// Compare arrays using FFI::memcmp(), but not the full length of the arrays
echo "\nUsing FFI::memcmp() but not full length\n";
printf($p, 'memcmp($a, $b, 3)', FFI::memcmp($a, $b, 3));
?>

Let’s get into the code.

  • Lines 3–6: We first define a set of four variables representing FFI\CData instances that can contain up to six characters and populate the instances with sample data.

  • Lines 9–15: Recall that the C language treats character data as an array, so we can’t just directly assign a string, even if using the cdata property. Accordingly, we need to define an anonymous function that populates the instances with ...