Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ffigen Laravel Package

klitsche/ffigen

CLI tool to generate and update low-level PHP FFI bindings from C headers. Produces constants.php and Methods.php (static method wrappers with phpdoc). Configurable via .ffigen.yml and optional custom parser hooks for preprocessing.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Install the Package Add to composer.json under require-dev:

    composer require --dev klitsche/ffigen
    
  2. Create a Configuration File Generate a .ffigen.yml in your project root with basic settings:

    headerFiles:
      - /path/to/library.h
    libraryFile: /path/to/liblibrary.so
    outputPath: ./generated/
    namespace: App\FFI\Bindings
    
  3. Run the Generator Execute the CLI command:

    vendor/bin/ffigen
    

    This generates:

    • constants.php (autoloadable constants)
    • Methods.php (static method bindings with PHPDoc)
  4. Autoload Generated Files Add constants.php to composer.json under "autoload":

    "autoload": {
        "files": ["generated/constants.php"]
    }
    

    Run:

    composer dump-autoload
    
  5. Use in Laravel Load the bindings in a Service Provider:

    use App\FFI\Bindings\Methods;
    
    class FFIServiceProvider extends ServiceProvider
    {
        public function register()
        {
            $this->app->singleton('ffi.bindings', function () {
                return new class {
                    public function callNativeMethod()
                    {
                        return Methods::nativeFunction(...);
                    }
                });
            });
        }
    }
    

First Use Case: Wrapping a C Library

Example: Integrate libuuid for generating UUIDs.

  1. Install libuuid-dev on your system.
  2. Configure .ffigen.yml:
    headerFiles:
      - /usr/include/uuid/uuid.h
    libraryFile: libuuid.so
    outputPath: ./app/FFI/UUID/
    namespace: App\FFI\UUID
    excludeMethods:
      - /^uuid_.*_free$/
    
  3. Generate bindings:
    vendor/bin/ffigen
    
  4. Use in Laravel:
    use App\FFI\UUID\Methods;
    
    $uuid = Methods::uuid_generate();
    

Implementation Patterns

Workflow: CI/CD Integration

  1. Generate Bindings on Build Add a script to composer.json:

    "scripts": {
        "post-install-cmd": [
            "@ffigen"
        ],
        "ffigen": "vendor/bin/ffigen"
    }
    

    Bindings are auto-generated on composer install.

  2. Version-Lock Bindings Commit constants.php and Methods.php to Git (or use a vendor/ subdirectory). Regenerate only when C headers change.

  3. Dynamic Updates (Advanced) Use a custom command to regenerate bindings on demand:

    // app/Console/Commands/RegenerateFFIBindings.php
    namespace App\Console\Commands;
    
    use Illuminate\Console\Command;
    use Symfony\Component\Process\Process;
    
    class RegenerateFFIBindings extends Command
    {
        protected $signature = 'ffi:regenerate';
        protected $description = 'Regenerate FFI bindings';
    
        public function handle()
        {
            $process = new Process(['vendor/bin/ffigen']);
            $process->run();
            $this->info($process->getOutput());
        }
    }
    

    Run with:

    php artisan ffi:regenerate
    

Laravel-Specific Patterns

  1. Facade for Clean API Create a facade to abstract FFI calls:

    // app/Facades/FFI.php
    namespace App\Facades;
    
    use Illuminate\Support\Facades\Facade;
    
    class FFI extends Facade
    {
        protected static function getFacadeAccessor()
        {
            return 'ffi.bindings';
        }
    }
    

    Usage:

    use App\Facades\FFI;
    
    $result = FFI::uuidGenerate();
    
  2. Error Handling Wrapper Wrap FFI calls in a service class to handle exceptions:

    // app/Services/FFIService.php
    namespace App\Services;
    
    use App\FFI\UUID\Methods;
    use FFI\Exception;
    
    class FFIService
    {
        public function generateUUID()
        {
            try {
                return Methods::uuid_generate();
            } catch (Exception $e) {
                \Log::error("FFI Error: " . $e->getMessage());
                throw new \RuntimeException("Failed to generate UUID", 0, $e);
            }
        }
    }
    
  3. Type Mapping for Laravel Eloquent Convert C structs to Laravel collections/models:

    use App\FFI\SomeLibrary\Methods;
    
    $struct = Methods::someFunction();
    $data = [
        'field1' => $struct->field1,
        'field2' => $struct->field2,
    ];
    return new SomeModel($data);
    

Integration Tips

  1. Header File Paths

    • Use absolute paths in .ffigen.yml to avoid issues across environments.
    • For system libraries, include paths like /usr/include/ or /usr/local/include/.
  2. Namespace Strategy

    • Prefix generated namespaces with your Laravel app namespace (e.g., App\FFI\Library).
    • Avoid collisions with existing classes by using unique prefixes.
  3. Exclusion Patterns

    • Use regex in excludeMethods/excludeConstants to filter unwanted bindings:
      excludeMethods:
        - /^internal_/
        - /_test$/
      
  4. Custom Parser for Complex Types Extend the default parser to handle edge cases (e.g., void* pointers, custom structs):

    // app/FFI/CustomParser.php
    namespace App\FFI;
    
    use Klitsche\FFIGen\Config;
    use Klitsche\FFIGen\Adapter\PHPCParser\Parser;
    
    class CustomParser extends Parser
    {
        public function __construct(Config $config)
        {
            parent::__construct($config);
            // Add custom type mappings
            $this->context->define('MY_CUSTOM_TYPE', 'int');
        }
    
        protected function parseHeaderFile(string $file): array
        {
            // Preprocess headers (e.g., add includes)
            $preprocessed = $this->preprocessHeaders($file);
            return parent::parseHeaderFile($preprocessed);
        }
    }
    

    Update .ffigen.yml:

    parserClass: App\FFI\CustomParser
    
  5. Testing Bindings Write PHPUnit tests to verify generated bindings:

    // tests/FFI/UUIDTest.php
    namespace Tests\FFI;
    
    use App\FFI\UUID\Methods;
    use PHPUnit\Framework\TestCase;
    
    class UUIDTest extends TestCase
    {
        public function testGenerateUUID()
        {
            $uuid = Methods::uuid_generate();
            $this->assertRegExp('/^[0-9a-f]{32}$/', $uuid);
        }
    }
    

Gotchas and Tips

Pitfalls

  1. FFI Extension Missing

    • Error: Class 'FFI' not found.
    • Fix: Enable the FFI extension in php.ini or use a Docker image with FFI preinstalled (e.g., php:8.2-fpm with docker-php-ext-install ffi).
  2. Library Not Found

    • Error: Failed to load library: liblibrary.so.
    • Fix:
      • Ensure the library path is correct (use ldconfig -p to list available libraries on Linux).
      • Set LD_LIBRARY_PATH before running PHP:
        export LD_LIBRARY_PATH=/path/to/library:$LD_LIBRARY_PATH
        vendor/bin/ffigen
        
  3. Header File Not Found

    • Error: File not found: library.h.
    • Fix:
      • Use absolute paths in .ffigen.yml.
      • Add include paths to the parser:
        $this->context->headerSearchPaths = ['/usr/include', '/opt/local/include'];
        
  4. Type Mismatches

    • Error: Type error: Expected int, got string.
    • Fix:
      • Customize type mappings in the parser (e.g., map void* to resource).
      • Use FFI::type() to define custom types:
        $ffi = FFI::cdef("typedef struct { int id; } MyStruct;");
        
  5. Memory Leaks

    • Risk: Forgetting to free dynamically allocated memory (e.g., malloc in C).
    • Fix:
      • Always call _free for allocations (e.g., uuid_unparse returns a buffer that must be freed).
      • Wrap FFI calls in a resource manager:
        $buffer = Methods::uuid_unparse($uuid);
        $result = Methods
        
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
besmartand-pro/php-quality-config
sentix/ai-chatbot
codifyo/ts-generator-bundle
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor