Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
B

bypass-finals

> 测试质量
Open source

Removes `final` and `readonly` keywords from source code on-the-fly and allows mocking of final methods and classes. It can be used together with any test tool

574 stars0 likes0 views
WebsiteGitHub

About

Removes `final` and `readonly` keywords from source code on-the-fly and allows mocking of final methods and classes. It can be used together with any test tool

Introduction

BypassFinals effortlessly strips away final and readonly keywords from your PHP code on-the-fly. This handy tool makes it possible to mock final methods and classes, seamlessly integrating with popular testing frameworks like PHPUnit, Mockery, or Nette Tester.

 

Installation

The easiest way to install BypassFinals is via Composer. Just run the following command in your project directory:

composer require dg/bypass-finals --dev

 

Usage

To get BypassFinals up and running, just invoke:

DG\BypassFinals::enable();

Make sure to call this method early, preferably immediately after your vendor/autoload.php is loaded, to ensure all classes are processed before they are used.

The final keyword is removed from classes and methods as well as from properties and property hooks introduced in PHP 8.4. Note that final internal PHP classes like Closure are not mockable.

To avoid removing readonly keywords, you can disable this feature by passing a parameter:

DG\BypassFinals::enable(bypassReadOnly: false);

To narrow down the application scope of BypassFinals, use a whitelist to specify directories or files:

DG\BypassFinals::allowPaths([
	'*/Nette/*',
]);

Or, conversely, you can specify which paths not to search using DG\BypassFinals::denyPaths(). This gives you finer control and can solve issues with certain frameworks and libraries.

Enhance performance by caching transformed files (the directory is created automatically):

DG\BypassFinals::setCacheDirectory(__DIR__ . '/cache');

Since the cached files are executed as PHP code, always use a private, trusted directory and never a shared world-writable location such as /tmp on multi-user machines, where another local user could inject code into the cache.

For integration with PHPUnit 10 or newer, simply add BypassFinals as an extension in your PHPUnit XML configuration file:


	

Optionally you can configure BypassFinals in your phpunit.xml file:


	
		
		
		
		
		
	

The allowPaths and denyPaths parameters accept multiple masks separated by semicolons.

 

Troubleshooting

Class is still final after enabling BypassFinals

BypassFinals can only strip the final keyword from files loaded after enable() is called. If a class is loaded before that — for example via a files entry in a package's autoload configuration, which Composer loads during require 'vendor/autoload.php' — the class will remain final.

To handle this, include src/bootstrap.php before vendor/autoload.php in your test bootstrap file:

// tests/bootstrap.php
require __DIR__ . '/../vendor/dg/bypass-finals/src/bootstrap.php';
require __DIR__ . '/../vendor/autoload.php';

This activates the stream wrapper early enough to intercept every file loaded during autoloading.

If you encounter other issues with BypassFinals not working as expected, you can use the debugInfo() method to gain insights into its internal state. Calling this method will output valuable information to help diagnose the problem:

DG\BypassFinals::debugInfo();

This will display:

  • Configuration: Whether BypassFinals is enabled for removing final and/or readonly keywords.
  • BypassFinals startup call stack: The sequence of function calls that led to DG\BypassFinals::enable(), helping you verify when and where BypassFinals was started.
  • Classes loaded before BypassFinals startup: A list of classes that were already loaded in PHP before BypassFinals was started. Keywords in these classes cannot be removed, as the classes are already defined. This can help identify potential conflicts or reasons why certain classes aren't being modified.
  • Modified files: A list of the files that BypassFinals has successfully modified. If the file containing a class you expect to be modified isn't in this list, it suggests a problem with path matching or the timing of the BypassFinals startup.

By examining this output, you can better understand how BypassFinals is configured and whether it's operating on the intended files and classes. This can significantly speed up the process of identifying and resolving issues.

 

Do you like this project?

Check out my other innovative open-source projects that might catch your interest:

✅ Latte: The only safe and intuitive templating system for PHP

✅ Tracy: An addictive debugging tool to enhance your development workflow

✅ PhpGenerator: A robust library for generating PHP code with modern features

✅ Nette Framework: A thoughtfully engineered and popular web framework.

 

Support Project

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

PHPfile-mutatormockingphpunittesting

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category测试质量
PricingOpen source

> Related tools

J
Jest
JavaScript 测试框架
P
Playwright
现代端到端测试框架
E
eslint-plugin-test-selectors
Enforces that data-test-id attributes are added to interactive DOM elements (JSX) to help with UI testing. JSX only.