rindow/rindow-math-buffer-ffi
A universal one-dimensional buffer for exchanging numeric data between PHP and C/C++ through FFI, designed for math libraries. Provides fast, shared memory access for vectors and matrices across Linux, Windows, and macOS on PHP 8.1 through 8.4.
Installation
composer require rindow/rindow-math-buffer-ffi
Ensure your project uses PHP 8.1+ and runs on Linux/macOS/Windows. As of 1.0.7 the package is tested against PHP 8.5, so it is safe to include in CI matrices targeting the newest runtime.
First Use Case: Buffer Creation
use Rindow\Math\Buffer\FFI\Buffer;
// Initialize FFI buffer (adjust path to your math library)
$ffi = FFI::cdef("
typedef double double_t;
double_t *malloc(size_t size);
void free(double_t *ptr);
", 'path/to/your/math_library.so');
$buffer = new Buffer($ffi, 10); // Buffer of 10 doubles
$buffer->fill(0.0); // Fill with zeros
Key Entry Points
Buffer::create(): Initialize a new buffer.Buffer::fill(): Set all values to a scalar.Buffer::get()/Buffer::set(): Access individual elements.Buffer::toArray()/Buffer::fromArray(): Convert between PHP arrays and FFI buffers.offsetSet() ($buffer[$i] = $value) now enforces strict typing when the buffer dtype is not complex — assigning an object to a real/int buffer is rejected instead of being silently coerced.Data Exchange with C Libraries
// Pass buffer to C function (e.g., BLAS/LAPACK)
$ffi->my_c_function($buffer->getPointer(), $buffer->getSize());
Batch Operations
// Fill with sequential values
for ($i = 0; $i < $buffer->getSize(); $i++) {
$buffer->set($i, $i * 1.5);
}
ArrayAccess-Style Writes (now type-checked)
$buffer[0] = 1.5; // scalar — fine for a real buffer
$buffer[1] = $someObject; // throws on non-complex dtypes as of 1.0.7
Use offsetSet for ergonomic indexing, but only push objects into buffers whose dtype is a complex type.
Memory Management
// Explicit cleanup (if not using RAII)
$buffer->free();
Leverage with Rindow Math Libraries
Combine with rindow/math for matrix operations:
use Rindow\Math\Matrix\Matrix;
$matrix = Matrix::create(3, 3);
$buffer = $matrix->toBuffer(); // Convert to FFI buffer
Performance Optimization
Buffer::copy() to avoid reallocations.Type Safety
Ensure FFI cdef matches the target library’s signatures (e.g., double_t for BLAS). The 1.0.7 offsetSet strictness aligns buffer writes with that same intent — keep your dtype declarations honest so the new checks accept your values.
CI Matrix Add PHP 8.5 to your test matrix; it is now an officially exercised runtime. Keep 8.1+ in the matrix for backward compatibility of consumers.
offsetSet Strict Object Checks (Behavioral change in 1.0.7)
$buffer[$i] = $object throws on a buffer whose dtype is not complex; previously it may have been silently accepted or cast.$buffer[$i] = $complexValue->getReal(); // real buffer
// or use a complex-dtype buffer for complex objects
$buffer[...] = assignments where the right-hand side is an object, and audit the buffer dtype at each site.FFI Initialization Errors
FFI::cdef() fails silently or throws cryptic errors.// Debug: Check if the library loads
$ffi = FFI::load('math_library.so');
Memory Leaks
$buffer->free() or use a finally block:
try {
$buffer = new Buffer($ffi, 100);
// ... operations ...
} finally {
$buffer->free();
}
Cross-Platform Quirks
.dll paths (e.g., 'C:\path\to\lib.dll').chmod +r library.so).PHPUnit Hang (macOS)
print_r($buffer->toArray());
php.ini:
ffi.debug = 1
Custom Buffer Types
Extend Buffer to support complex numbers or mixed types:
class ComplexBuffer extends Buffer {
public function setComplex(int $index, float $real, float $imag): void {
$this->set($index * 2, $real);
$this->set($index * 2 + 1, $imag);
}
}
Note that custom subclasses overriding offsetSet should preserve the 1.0.7 strict-check semantics for non-complex dtypes, or document the deviation explicitly.
Integration with Laravel
$this->app->singleton('math.buffer', function ($app) {
$ffi = FFI::load('vendor/math_library.so');
return new Buffer($ffi, 1024);
});
MathBuffer facade for cleaner syntax:
use Illuminate\Support\Facades\Facade;
class MathBuffer extends Facade {
protected static function getFacadeAccessor() { return 'math.buffer'; }
}
offsetSet check applies to every write through the container-resolved instance.Async Operations
Use Swoole or ReactPHP to offload heavy computations to worker processes:
$buffer = new Buffer($ffi, 1000);
$worker = new \Swoole\Process(function () use ($buffer) {
// Heavy math ops here
});
How can I help you explore Laravel packages today?