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

Binary Driver Laravel Package

alchemy/binary-driver

PHP toolkit for building reusable, testable binary drivers on top of symfony/process. Provides AbstractBinary, binary detection, command generation, logging via PSR-3, and process listeners for debugging and customization across CLI tools.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require alchemy/binary-driver
    

    Ensure symfony/process and psr/log are also installed (or add them via Composer).

  2. First Use Case: Create a simple binary driver for a command like ls:

    use Alchemy\BinaryDriver\AbstractBinary;
    
    class LsDriver extends AbstractBinary {
        public function getName() {
            return 'ls';
        }
    }
    
  3. Load and Execute:

    $driver = Driver::load('ls');
    $output = $driver->command(['-a', '-l']);
    echo $output;
    
  4. Key Configuration: Set timeouts or environment variables via the Configuration class:

    $config = new \Alchemy\BinaryDriver\Configuration(['timeout' => 30]);
    $driver->setConfiguration($config);
    

Where to Look First

  • AbstractBinary: Core class for extending binary drivers.
  • Driver::load(): Factory method to instantiate drivers.
  • ProcessBuilderFactory: For creating Symfony Process objects.
  • Listeners: Debugging and event handling (e.g., DebugListener).

Implementation Patterns

Usage Patterns

  1. Driver Creation: Extend AbstractBinary for custom commands:

    class GitDriver extends AbstractBinary {
        public function getName() {
            return 'git';
        }
    
        public function pull() {
            return $this->command(['pull']);
        }
    }
    
  2. Command Execution: Use the command() method to run binaries:

    $gitDriver = Driver::load('git');
    $result = $gitDriver->command(['status']);
    
  3. Configuration Management: Centralize settings (e.g., timeouts, environment variables):

    $config = new \Alchemy\BinaryDriver\Configuration([
        'timeout' => 60,
        'env' => ['GIT_TERMINAL_PROMPT' => '0']
    ]);
    $driver->setConfiguration($config);
    
  4. Logging: Inject a PSR-3 logger for observability:

    $logger = new \Monolog\Logger('binary_driver');
    $driver = Driver::load('git', $logger);
    
  5. Event Listeners: Attach listeners for real-time output or error handling:

    $driver->listen(new \Alchemy\BinaryDriver\Listeners\DebugListener());
    $driver->on('error', function ($line) {
        error_log('[ERROR] ' . $line);
    });
    

Workflows

  1. Wrapper for CLI Tools: Encapsulate complex commands (e.g., Docker, FFmpeg) into reusable drivers:

    class DockerDriver extends AbstractBinary {
        public function build($image) {
            return $this->command(['build', '-t', $image, '.']);
        }
    }
    
  2. Debugging Workflow: Use listeners to log process output during development:

    $driver->listen(new DebugListener());
    $driver->on('debug', function ($line) {
        echo "[DEBUG] $line\n";
    });
    
  3. Error Handling: Catch execution failures with ExecutionFailureException:

    try {
        $driver->command(['invalid-command']);
    } catch (\Alchemy\BinaryDriver\Exception\ExecutionFailureException $e) {
        echo "Command failed: " . $e->getCommand() . "\n";
        echo "Error: " . $e->getErrorOutput() . "\n";
    }
    

Integration Tips

  1. Laravel Integration: Bind drivers to the container for dependency injection:

    $this->app->bind('git.driver', function () {
        return Driver::load('git');
    });
    
  2. Testing: Mock drivers in unit tests:

    $mockDriver = Mockery::mock('overload:Alchemy\BinaryDriver\Driver');
    $mockDriver->shouldReceive('command')->andReturn('mocked output');
    
  3. Environment-Specific Config: Load configurations dynamically (e.g., from .env):

    $config = new Configuration([
        'timeout' => env('BINARY_DRIVER_TIMEOUT', 30)
    ]);
    
  4. ProcessBuilderFactory: Reuse process creation logic:

    $factory = new \Alchemy\BinaryDriver\ProcessBuilderFactory('/usr/bin/php');
    $process = $factory->create(['-v']);
    

Gotchas and Tips

Pitfalls

  1. Binary Detection in PHP-FPM/Nginx:

    • Issue: $_ENV['PATH'] may be empty, causing binary detection to fail.
    • Fix: Set fastcgi_param PATH /your/current/path in Nginx’s fastcgi_params.
  2. PHP Version Compatibility:

    • Issue: Last release (2020) targets PHP 7.1–7.4. PHP 8.x may break untested code (e.g., Throwable changes).
    • Fix: Fork and update dependencies (e.g., symfony/process to v6.x).
  3. Listener Event Forwarding:

    • Issue: Custom listeners must implement forwardedEvents() to emit events to the driver.
    • Fix: Always define forwardedEvents() in custom listeners:
      public function forwardedEvents() {
          return ['error', 'out'];
      }
      
  4. Configuration Overrides:

    • Issue: Configuration changes may not persist across driver instances.
    • Fix: Set configuration early and avoid late modifications:
      $driver = Driver::load('git');
      $driver->setConfiguration($config); // Do this first!
      
  5. Error Output Handling:

    • Issue: ExecutionFailureException may not capture full error output in all cases.
    • Fix: Use listeners to log stderr separately:
      $driver->listen(new DebugListener());
      $driver->on('error', function ($line) {
          error_log($line);
      });
      

Debugging Tips

  1. Enable Verbose Logging:

    $logger = new \Monolog\Logger('driver', [
        new \Monolog\Handler\StreamHandler('php://stderr', \Monolog\Logger::DEBUG)
    ]);
    $driver = Driver::load('git', $logger);
    
  2. Inspect Process Objects: Use ProcessBuilderFactory to debug command construction:

    $factory = new \Alchemy\BinaryDriver\ProcessBuilderFactory('/usr/bin/git');
    $process = $factory->create(['status']);
    echo $process->getCommandLine(); // Debug the exact command
    
  3. Check Environment Variables: Ensure $_ENV['PATH'] is set correctly:

    echo "PATH: " . printenv('PATH') . "\n";
    

Extension Points

  1. Custom Listeners: Extend EventEmitter and implement ListenerInterface:

    class CustomListener extends \Evenement\EventEmitter implements \Alchemy\BinaryDriver\ListenerInterface {
        public function handle($type, $data) {
            // Custom logic
        }
        public function forwardedEvents() {
            return ['custom_event'];
        }
    }
    
  2. Driver Decorators: Wrap existing drivers to add behavior:

    class LoggingDriverDecorator extends AbstractBinary {
        protected $driver;
        public function __construct(AbstractBinary $driver) {
            $this->driver = $driver;
        }
        public function command($args) {
            $this->logger->info("Executing: " . $this->driver->getName() . " " . implode(' ', $args));
            return $this->driver->command($args);
        }
    }
    
  3. Configuration Providers: Dynamically load configurations from external sources (e.g., database):

    $config = new Configuration([]);
    $config->set('timeout', $this->getTimeoutFromDatabase());
    

Configuration Quirks

  1. Default Timeout:

    • No default timeout is set; always configure it explicitly:
      $config = new Configuration(['timeout' => 30]);
      
  2. Environment Variables:

    • Use the env key in Configuration:
      $config = new Configuration([
          'env' => ['GIT_AUTHOR_NAME' => 'CI']
      ]);
      
  3. ArrayAccess vs. Methods:

    • Both ArrayAccess and method calls work, but prefer methods for clarity:
      // Preferred:
      $config->set('timeout', 30);
      // Over:
      $config['timeout'] = 30;
      

Performance Considerations

  1. Avoid Frequent Driver Instantiation: Reuse driver instances where possible:
    $gitDriver = Driver::load('git'); // Create once
    $gitDriver->command(['status']); //
    
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.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
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