# BypassFinals BypassFinals is a PHP library that removes `final` and `readonly` keywords from PHP source code on-the-fly during runtime. This enables developers to mock final classes and methods in their unit tests, which is particularly useful when working with third-party libraries or legacy code that uses final declarations extensively. The library works by registering a custom PHP stream wrapper that intercepts file reads and modifies the source code before it's parsed by PHP. The library seamlessly integrates with popular PHP testing frameworks including PHPUnit, Mockery, and Nette Tester. It supports PHP versions 7.1 through 8.4 and can be easily installed via Composer. BypassFinals uses pattern-based path filtering to control which files are modified, and includes optional caching to improve performance in large codebases. ## Installation Install BypassFinals via Composer as a development dependency. ```bash composer require dg/bypass-finals --dev ``` ## DG\BypassFinals::enable() Enables the BypassFinals stream wrapper to intercept PHP file loading and remove `final` and `readonly` keywords. This method should be called as early as possible in your test bootstrap, ideally right after loading the Composer autoloader, to ensure all classes are processed before they are used. ```php createMock(FinalService::class); $mock->method('process')->willReturn('mocked result'); $this->assertEquals('mocked result', $mock->process()); } } ``` ## DG\BypassFinals::allowPaths() Restricts BypassFinals to only modify files matching the specified path patterns. This whitelist approach is useful when you only need to mock classes from specific directories or vendor packages, improving performance and avoiding potential conflicts with other libraries. ```php // BypassFinals Debug Information // ------------------------------ // // Configuration: // Bypass 'final': enabled // Bypass 'readonly': enabled // // From where BypassFinals::enable() was started: // #0 in /var/www/tests/bootstrap.php:5 // // Classes already loaded before BypassFinals was started: // no classes // // Files where BypassFinals removed final/readonly: // - /var/www/src/FinalClass.php // ``` ## PHPUnit Extension Integration For PHPUnit 10 and newer, BypassFinals provides a dedicated extension that can be configured directly in your phpunit.xml file. This approach automatically enables BypassFinals before any tests run and excludes PHPUnit's own files from modification to prevent conflicts. ```xml tests ``` ## Complete Test Bootstrap Example A comprehensive example showing how to configure BypassFinals for a typical PHPUnit test suite with path filtering and caching enabled. ```php createMock(FinalPaymentProcessor::class); $processor->expects($this->once()) ->method('charge') ->with(100.00, 'USD') ->willReturn(['status' => 'success', 'transaction_id' => 'txn_123']); $service = new PaymentService($processor); $result = $service->processPayment(100.00, 'USD'); $this->assertEquals('success', $result['status']); } } ``` ## Summary BypassFinals is an essential tool for PHP developers who need to write comprehensive unit tests for codebases that use final classes and readonly properties. The primary use cases include: mocking third-party library classes that are declared final, testing legacy code without modifying original source files, and enabling dependency injection testing patterns in projects with strict immutability requirements. The library's path filtering system allows precise control over which files are modified, making it safe to use alongside frameworks that depend on their final declarations. Integration with existing projects is straightforward - simply require the package as a dev dependency, call `DG\BypassFinals::enable()` in your test bootstrap before any classes are loaded, and optionally configure path filters and caching for optimal performance. For PHPUnit users, the built-in extension provides zero-configuration setup through phpunit.xml. The library operates transparently at the stream wrapper level, meaning no changes to your actual source code are needed, and the modifications only affect the runtime representation of classes during test execution.