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

Include Interceptor Laravel Package

infection/include-interceptor

PHP stream wrapper that intercepts the file:// protocol to override the content of any included or autoloaded file at runtime. Register a mapping from original file to replacement, enable the interceptor, and includes/file_get_contents load the replacement instead.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Installation: Add the package to your Laravel project’s dev dependencies:

    composer require --dev infection/include-interceptor
    
  2. First Use Case: Enable the interceptor in your test bootstrap file (e.g., tests/bootstrap.php) before any include/require calls or autoloading occurs:

    use Infection\IncludeInterceptor\Interceptor;
    
    $interceptor = new Interceptor();
    $interceptor->enable();
    
  3. Basic Interception: Register a file replacement before enabling:

    $interceptor->intercept(
        '/path/to/original_file.php',  // Original file path
        '/path/to/replacement_file.php' // Replacement file path
    );
    

    Now, any include/require of original_file.php will load replacement_file.php instead.

  4. Verify Setup: Test with a simple include in a test:

    public function test_interception()
    {
        $this->expectFileToBeIncluded('replacement_file.php');
        include 'original_file.php';
    }
    

Implementation Patterns

1. Mutation Testing with Infection

Leverage the interceptor to power Infection’s mutation testing by replacing original files with mutated versions:

// In your Infection configuration or test setup
$interceptor = new Interceptor();
$interceptor->intercept(
    $originalFilePath,
    $mutatedFilePath  // Generated by Infection
);
$interceptor->enable();

// Run tests to verify mutations

2. Test Coverage Analysis

Use the interceptor to log included files during tests and identify missing coverage:

$interceptor->enable();
$this->runTests(); // Your test suite
$includedFiles = $interceptor->getInterceptions();

// Analyze gaps
$uncoveredFiles = array_diff(
    $this->getAllProjectFiles(),
    array_keys($includedFiles)
);

3. Dynamic Path Rewriting for Legacy Code

Rewrite legacy include paths to modern autoloader-based paths during tests:

$interceptor->intercept(
    'legacy/config.php',
    __DIR__ . '/../config/test.php' // Modern path
);

4. Conditional File Loading

Block or redirect includes based on test environment:

if (app()->environment('testing')) {
    $interceptor->intercept(
        'production/config.php',
        'tests/config/mock.php'
    );
}
$interceptor->enable();

5. Integration with Laravel’s Test Helpers

Combine with Laravel’s RefreshDatabase or MigrateFresh to ensure consistent file loading:

public function test_with_fresh_db()
{
    $this->artisan('migrate:fresh');
    $interceptor->intercept(
        'database/migrations/2023_*.php',
        'tests/migrations/mock.php'
    );
    $interceptor->enable();
    // Run tests...
}

Gotchas and Tips

Pitfalls

  1. Autoloader Conflicts:

    • Enable the interceptor before any spl_autoload_register() calls (e.g., Composer autoloader). Late registration may miss some includes.
    • Fix: Place the enable() call in tests/bootstrap.php or a phpunit.xml bootstrap file.
  2. Stream Wrapper Registration Order:

    • If other packages (e.g., Xdebug, custom wrappers) register stream handlers, the interceptor may not work as expected.
    • Fix: Unregister conflicting wrappers first:
      stream_wrapper_unregister('file');
      $interceptor->enable();
      
  3. include_once/require_once Caching:

    • The interceptor respects PHP’s internal “already included” cache. If you intercept a file twice, only the first include will trigger the replacement.
    • Workaround: Reset PHP’s internal state (not recommended) or design replacements to be idempotent.
  4. Performance Overhead:

    • Each include/require adds ~1–2ms overhead. Disable outside CLI/test contexts:
      if (PHP_SAPI === 'cli') {
          $interceptor->enable();
      }
      
  5. Path Resolution Quirks:

    • The interceptor uses realpath for comparisons. Relative paths (e.g., include 'config.php') may not match unless resolved to absolute paths.
    • Tip: Use realpath() when registering interceptions:
      $interceptor->intercept(
          realpath('original.php'),
          realpath('replacement.php')
      );
      
  6. No Runtime Handler Modification:

    • Handlers are set once during enable(). For dynamic behavior (e.g., test-group-specific replacements), subclass Interceptor and override stream_open() logic.

Debugging Tips

  • Log Interceptions: Dump captured events to debug:
    $interceptions = $interceptor->getInterceptions();
    file_put_contents(
        'interceptions.log',
        print_r($interceptions, true)
    );
    
  • Verify Registration: Check if the wrapper is active:
    var_dump(stream_wrapper_restore('file')); // Should return 'interceptor'
    
  • Test Isolation: Run tests in isolation to avoid conflicts with other stream wrappers.

Extension Points

  1. Custom Handlers: Override the interceptor’s behavior by extending the class:

    class CustomInterceptor extends Interceptor {
        protected function handleInclude($path) {
            if (str_contains($path, 'vendor/')) {
                return false; // Skip vendor files
            }
            return parent::handleInclude($path);
        }
    }
    
  2. Event-Based Replacements: Use the interceptor to trigger events when files are included:

    $interceptor->onInclude(function ($path) {
        event(new FileIncluded($path));
    });
    
  3. Laravel Service Provider: Register the interceptor in a test service provider:

    public function boot()
    {
        if ($this->app->environment('testing')) {
            $interceptor = new Interceptor();
            $interceptor->enable();
            $this->app->singleton('interceptor', fn() => $interceptor);
        }
    }
    

Laravel-Specific Quirks

  • Artisan Commands: Avoid enabling the interceptor in commands that load production files (e.g., php artisan migrate). Restrict to test contexts:
    if (app()->runningUnitTests()) {
        $interceptor->enable();
    }
    
  • Cached Configs: The interceptor won’t affect Laravel’s cached configs (e.g., bootstrap/cache/config.php). Target source files instead:
    $interceptor->intercept(
        config_path('app.php'),
        'tests/config/app_test.php'
    );
    
  • Composer Autoloader: The interceptor works alongside Composer’s autoloader but won’t intercept PSR-4 autoloaded classes. Use it for include/require only.
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.
symfony/ai-symfony-mate-extension
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata