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.
Installation: Add the package to your Laravel project’s dev dependencies:
composer require --dev infection/include-interceptor
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();
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.
Verify Setup: Test with a simple include in a test:
public function test_interception()
{
$this->expectFileToBeIncluded('replacement_file.php');
include 'original_file.php';
}
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
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)
);
Rewrite legacy include paths to modern autoloader-based paths during tests:
$interceptor->intercept(
'legacy/config.php',
__DIR__ . '/../config/test.php' // Modern path
);
Block or redirect includes based on test environment:
if (app()->environment('testing')) {
$interceptor->intercept(
'production/config.php',
'tests/config/mock.php'
);
}
$interceptor->enable();
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...
}
Autoloader Conflicts:
spl_autoload_register() calls (e.g., Composer autoloader). Late registration may miss some includes.enable() call in tests/bootstrap.php or a phpunit.xml bootstrap file.Stream Wrapper Registration Order:
stream_wrapper_unregister('file');
$interceptor->enable();
include_once/require_once Caching:
include will trigger the replacement.Performance Overhead:
include/require adds ~1–2ms overhead. Disable outside CLI/test contexts:
if (PHP_SAPI === 'cli') {
$interceptor->enable();
}
Path Resolution Quirks:
include 'config.php') may not match unless resolved to absolute paths.realpath() when registering interceptions:
$interceptor->intercept(
realpath('original.php'),
realpath('replacement.php')
);
No Runtime Handler Modification:
enable(). For dynamic behavior (e.g., test-group-specific replacements), subclass Interceptor and override stream_open() logic.$interceptions = $interceptor->getInterceptions();
file_put_contents(
'interceptions.log',
print_r($interceptions, true)
);
var_dump(stream_wrapper_restore('file')); // Should return 'interceptor'
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);
}
}
Event-Based Replacements: Use the interceptor to trigger events when files are included:
$interceptor->onInclude(function ($path) {
event(new FileIncluded($path));
});
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);
}
}
php artisan migrate). Restrict to test contexts:
if (app()->runningUnitTests()) {
$interceptor->enable();
}
bootstrap/cache/config.php). Target source files instead:
$interceptor->intercept(
config_path('app.php'),
'tests/config/app_test.php'
);
include/require only.How can I help you explore Laravel packages today?