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

Bypass Finals Laravel Package

dg/bypass-finals

DG\BypassFinals lets you bypass PHP’s final classes and methods at runtime so you can mock, extend, or patch code that’s otherwise locked down—useful for testing legacy dependencies. Lightweight, Composer-ready, and works with popular test frameworks.

View on GitHub
Deep Wiki
Context7
## Getting Started

1. **Installation**:
   ```bash
   composer require --dev dg/bypass-finals

Add the package as a dev dependency to ensure it’s excluded from production.

  1. Bootstrap Integration: Register the loader in your test bootstrap file (e.g., tests/bootstrap.php or phpunit.xml):

    <!-- phpunit.xml -->
    <php>
        <autoload>
            <classmap prefix="BypassFinals"/>
        </autoload>
    </php>
    <bootstrap>vendor/autoload.php</bootstrap>
    

    Or in tests/bootstrap.php:

    require __DIR__ . '/../vendor/autoload.php';
    (new \BypassFinals\Loader())->register();
    
  2. First Use Case: Mock a final class in PHPUnit:

    use PHPUnit\Framework\TestCase;
    
    class FinalClassTest extends TestCase
    {
        public function testMockFinalClass()
        {
            $mock = $this->createMock(FinalClass::class);
            $mock->method('finalMethod')->willReturn('mocked');
            $this->assertEquals('mocked', $mock->finalMethod());
        }
    }
    

    Now test methods/properties in final classes as if they weren’t final.


Implementation Patterns

1. Environment Isolation

  • Restrict to Tests/CI Only: Wrap registration in an environment check (e.g., tests/bootstrap.php):

    if (app()->environment('testing') || PHP_SAPI === 'cli') {
        (new \BypassFinals\Loader())->register();
    }
    

    Or use a CI-specific flag (e.g., env('RUNNING_TESTS')).

  • PHPUnit Configuration: Add to phpunit.xml to auto-register:

    <php>
        <ini name="auto_prepend_file" value="vendor/dg/bypass-finals/bootstrap.php"/>
    </php>
    

2. Selective Path Bypassing

  • Whitelist/Deny Paths: Limit bypass to specific files/directories (e.g., vendor packages):
    $loader = new \BypassFinals\Loader();
    $loader->allowPaths([
        __DIR__ . '/../vendor/symfony/http-foundation',
        __DIR__ . '/../vendor/laravel/framework'
    ]);
    $loader->denyPath(__DIR__ . '/../vendor/phpunit');
    $loader->register();
    

3. Integration with Mocking Tools

  • PHPUnit: Use createMock() or getMockBuilder() as usual for final classes.
  • Mockery:
    $mock = Mockery::mock(FinalClass::class)->makePartial();
    $mock->shouldReceive('finalMethod')->andReturn('mocked');
    
  • Pest:
    use function Pest\Laravel\actingAs;
    
    test('mock final class', function () {
        $mock = Mockery::mock(FinalClass::class)->makePartial();
        $mock->shouldReceive('finalMethod')->andReturn('mocked');
    });
    

4. Debugging Bypass Behavior

  • Verify Bypass: Use BypassFinals::debugInfo() to check if a class was bypassed:
    if (BypassFinals::debugInfo(FinalClass::class)['bypassed']) {
        echo "FinalClass was bypassed!";
    }
    
  • Log Load Order: Debug class loading sequence with:
    spl_autoload_register(function ($class) {
        if (str_contains($class, 'FinalClass')) {
            error_log("Loading: $class");
        }
    });
    

5. Caching for Performance

  • Enable Caching: Reduce overhead in CI by caching rewritten bytecode:
    $loader = new \BypassFinals\Loader();
    $loader->setCacheDir(__DIR__ . '/../storage/bypass-finals-cache');
    $loader->register();
    
    Clear cache between major PHP version updates.

Gotchas and Tips

1. Common Pitfalls

  • Accidental Production Use:

    • Risk: Forgetting to restrict bypass to tests can break production if final classes rely on their immutability.
    • Fix: Add a CI check (e.g., fail build if bypass-finals is loaded outside testing):
      if (!app()->environment('testing') && PHP_SAPI !== 'cli') {
          throw new \RuntimeException('BypassFinals loaded in non-test environment!');
      }
      
  • Opcache Conflicts:

    • Issue: Opcache may cache bypassed bytecode, causing stale or incorrect behavior.
    • Fix: Disable opcache in test environments:
      # php.ini (test environment)
      opcache.enable=0
      
  • PHP 8.2+ readonly Quirks:

    • Behavior: readonly properties are bypassed only in PHP 8.1+. In PHP 8.2+, the package may not handle them correctly.
    • Workaround: Explicitly disable readonly bypass if needed:
      BypassFinals::enable(bypassReadOnly: false);
      

2. Debugging Techniques

  • Check Bypass Status: Use BypassFinals::debugInfo() to verify if a class/method was bypassed:

    var_dump(BypassFinals::debugInfo(FinalClass::class));
    

    Output:

    array(
        'bypassed' => true,
        'original' => 'final class FinalClass { ... }',
        'rewritten' => 'class FinalClass { ... }',
    )
    
  • Trace Autoloading: If mocks fail, trace which loader is active:

    spl_autoload_register(function ($class) {
        if (str_contains($class, 'Final')) {
            error_log("Autoloading: $class via " . get_class(Loader::getInstance()));
        }
    });
    

3. Performance Considerations

  • Memory Usage:
    • Large codebases may hit memory limits during bytecode rewriting.
    • Fix: Increase memory limit in CI:
      php -d memory_limit=-1 vendor/bin/phpunit
      
  • Test Speed:
    • Bypass adds ~5–10% overhead to test execution.
    • Mitigation: Cache rewritten bytecode (see Implementation Patterns).

4. Laravel-Specific Tips

  • Service Container Conflicts:

    • If final classes are Laravel services (e.g., Illuminate\Foundation\Application), bypass may interfere with container resolution.
    • Workaround: Mock at the boundary layer (e.g., HTTP layer) instead of the service container.
  • Pest Integration:

    • Register bypass in pest.php:
      uses(Tests\TestCase::class)->in('Feature');
      
      beforeAll(function () {
          if (app()->environment('testing')) {
              (new \BypassFinals\Loader())->register();
          }
      });
      

5. Advanced Customization

  • Extend Rewriter: Override default behavior by extending BypassFinals\Rewriter:
    class CustomRewriter extends \BypassFinals\Rewriter
    {
        protected function shouldBypassMethod($methodName)
        {
            return parent::shouldBypassMethod($methodName) && $methodName !== 'protectedMethod';
        }
    }
    
    Register it:
    $loader = new \BypassFinals\Loader();
    $loader->setRewriter(new CustomRewriter());
    $loader->register();
    
    Warning: Internal API is unstable; prefer configuration over extension.

6. CI/CD Safeguards

  • Prevent Accidental Deployment: Add a composer.json script to block production installs:
    "scripts": {
        "post-autoload-dump": "if [ \"$(php -r 'echo php_sapi_name();')\" != \"cli\" ]; then echo \"ERROR: bypass-finals loaded in non-CLI environment!\" >&2; exit 1; fi"
    }
    
  • Test Environment Validation: Fail builds if bypass is active outside tests:
    if (!app()->runningUnitTests() && !app()->runningInConsole()) {
        throw new \RuntimeException('BypassFinals must only run in tests!');
    }
    

7. License and Maintenance Risks

  • NOASSERTION License:
    • Risk: Legal ambiguity may cause issues in enterprise environments.
    • Mitigation: Review license terms or use a fork with a permissive license (e.g., MIT).
  • Repository Status:
    • Check: Monitor for updates (last release: 2026-06-01). Consider forking if maintenance stalls.
    • Fallback: If the package breaks, implement a custom final bypass using eval() or opcache_compile_file().
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope