- What does dg/bypass-finals do?
- dg/bypass-finals is a PHP library that registers a stream wrapper to rewrite source files as they are included, stripping final and readonly keywords before PHP compiles them. That lets mocking tools like PHPUnit and Mockery subclass or override classes and methods that would otherwise be unmockable. It sits below Laravel, so it does not depend on the container, service providers, or facades.
- How do I install and enable dg/bypass-finals in a Laravel project?
- Install it as a dev dependency with composer require --dev dg/bypass-finals. Enable it once in your test bootstrap, or preferably register the bundled PHPUnit extension in phpunit.xml. Do not enable it in production configuration; keep it scoped to the test process. Verify the exact API against the installed version because older snippets reference BypassFinals\Loader::register() while current releases use DG\BypassFinals::enable().
- Does dg/bypass-finals work with PHPUnit, Pest, and Mockery?
- Yes. It works with any tool that resolves classes through standard autoloading, including PHPUnit, Pest, and Mockery. The PHPUnit extension is the maintained integration path and is configurable; for other runners you enable the wrapper in the bootstrap. No changes to application code or test structure are required.
- Can I mock final classes and final methods without changing source code?
- Yes. The stream wrapper tokenizes source on include and removes final and readonly from class, method, and promoted property declarations before the engine sees them. That allows mocking final classes and final methods without editing vendor code or forking packages. The tradeoff is that the class under test no longer has exactly the same semantics as in production.
- How do I limit dg/bypass-finals to specific directories with allowPaths or denyPath?
- Use allowPaths() to opt in specific directories and denyPath() to exclude paths; earlier versions exposed setWhitelist(). The PHPUnit extension also pre-denies vendor/phpunit by default so PHPUnit internals are not rewritten. Prefer per-directory opt-in over global rewriting, because the wrapper otherwise intercepts every include in the process. Check your installed version for the exact method names.
- Is dg/bypass-finals safe to use in production?
- It is not intended for production. Enabling it outside tests changes runtime semantics for every included file, can hide design constraints, and adds process-wide blast radius. Keep it in require-dev, enable it only in the test bootstrap or the PHPUnit extension, and add a CI guardrail that the wrapper is never registered in production configuration.
- Which Laravel and PHP versions are supported?
- The package targets modern PHP versions and is used alongside PHPUnit-based Laravel test suites. Laravel itself rarely marks first-party classes final, so version support is less about Laravel and more about your PHP and PHPUnit majors. Pin a compatible pair and verify by inspection of the tag history rather than release metadata, which the evaluation flagged as inconsistent.
- How do I register the PHPUnit extension for dg/bypass-finals?
- Register the extension in phpunit.xml under the extensions section using the class name shipped by the installed version. Availability starts at 1.6 and configurability arrived in 1.9; the extension pre-denies vendor/phpunit by default. If you cannot use the extension, call DG\BypassFinals::enable() from the test bootstrap instead. Confirm the exact configuration keys from the package source for your pinned version.
- What are the license and maintenance risks of dg/bypass-finals?
- The license metadata is reported as NOASSERTION, which is a hard gate for many engineering organizations and must be cleared by legal before adoption. The project is effectively single-maintainer, internal classes such as Rewriter and NativeWrapper carry no backward-compatibility guarantee, and popularity metrics show stars without dependents. Treat it as a targeted tool for vendor-owned unmockable classes, not a general testing habit, and be prepared to fork a few hundred lines if upstream stalls.
- What alternatives exist to dg/bypass-finals for testing final classes?
- The cleanest alternative is removing final from code you own or introducing an adapter at your boundary so you never mock the vendor class directly. For vendor-owned final classes, wrapper or facade patterns and integration tests against a real dependency are more permanent than runtime rewriting. dg/bypass-finals is pragmatic when the list of unmockable vendor classes is long and cannot be changed. If the list is short, an adapter is cheaper and avoids silent behavior divergence.