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

Php Autoload Override Laravel Package

adriansuter/php-autoload-override

Override fully qualified global function calls inside class methods so you can mock them in tests. Works with PHP 8.2+ and Composer PSR-4 autoloading; integrates via a PHPUnit bootstrap using OverrideFactory to map functions (e.g., rand) to real implementations.

View on GitHub
Deep Wiki
Context7

PHP-Autoload-Override

Build Status Coverage Status Total Downloads License

This library allows overriding fully qualified function calls inside your class methods in order to be able to mock them during testing.

NOTE: The library can be used for other scenarios as well. But we recommend using it for testing purposes only.

PHP-Autoload-Override Website

Requirements

  • PHP 8.2 or later
  • Composer with PSR-4 (PSR-0 is not supported)

Installation

composer require --dev adriansuter/php-autoload-override ^2.0

Usage with PHPUnit

Say we want to unit test the following class Probability.

namespace My\App;

class Probability
{
    public function pick(int $probability, string $color1, string $color2): string
    {
        if (\rand(1, 100) <= $probability) {
            return $color1;
        } else {
            return $color2;
        }
    }
}

The class uses \rand() from the global scope. Because we cannot control its output, we cannot test pick() deterministically — until we override it.

Setting up the bootstrap

Open the bootstrap script of your test suite and register the override. The recommended approach uses OverrideFactory:

// tests/bootstrap.php

use AdrianSuter\Autoload\Override\OverrideFactory;
use My\App\Probability;

/** @var \Composer\Autoload\ClassLoader $classLoader */
$classLoader = require_once __DIR__ . '/../vendor/autoload.php';

OverrideFactory::create()
    ->forClass(Probability::class, ['rand' => \rand(...)])
    ->apply($classLoader);

Each entry in forClass() maps a function name to its real implementation, written as a first-class callable. OverrideFactory generates the override closure automatically: when a test sets a mock value via MockRegistry::set(), that value is returned; otherwise the real \rand() is called. No mock value is registered initially, so non-test code is unaffected.

For multiple classes, chain forClass() calls:

OverrideFactory::create()
    ->forClass(Clock::class,       ['time' => \time(...)])
    ->forClass(Probability::class, ['rand' => \rand(...)])
    ->apply($classLoader);

If you need the raw declarations array instead (e.g. for an AbstractIntegrationTestCase), use build() instead of apply():

protected function getOverrideDeclarations(): array
{
    return OverrideFactory::create()
        ->forClass(Probability::class, ['rand' => \rand(...)])
        ->build();
}

Writing the test

Set a mock value with MockRegistry::set() before calling the code under test, and reset it in tearDown() so it does not affect other tests:

namespace My\App\Tests;

use AdrianSuter\Autoload\Override\MockRegistry;
use My\App\Probability;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class ProbabilityTest extends TestCase
{
    protected function tearDown(): void
    {
        MockRegistry::reset(Probability::class);
    }

    #[Test]
    public function pickReturnsSecondColorWhenRandExceedsProbability(): void
    {
        MockRegistry::set(Probability::class, 'rand', 35);

        $p = new Probability();
        $this->assertSame('blue', $p->pick(34, 'red', 'blue'));
    }

    #[Test]
    public function pickReturnsFirstColorWhenRandMeetsProbability(): void
    {
        MockRegistry::set(Probability::class, 'rand', 35);

        $p = new Probability();
        $this->assertSame('red', $p->pick(35, 'red', 'blue'));
    }
}

MockRegistry::reset(Probability::class) clears only the overrides for that class. Call MockRegistry::reset() without arguments to clear all registered overrides at once.

Note that these overrides are only applied during the unit tests.

Sharing an override across multiple classes

MockRegistry::set() registers an override for one specific class. To register a fallback that applies to every class, use setGlobal():

MockRegistry::setGlobal('time', 1574333284);

If a class also has a per-class override for the same function, the per-class value takes priority. Reset only the global overrides with MockRegistry::resetGlobal().

Using Override::apply() directly

If you register overrides via Override::apply() directly rather than using OverrideFactory, you write the closure yourself. MockRegistry::get() takes three arguments: the class name, the function name, and a default that is returned when no mock is registered:

Override::apply($classLoader, [
    Probability::class => [
        'rand' => function (int $min, int $max): int {
            return MockRegistry::get(Probability::class, 'rand', \rand($min, $max));
        }
    ]
]);

Be aware that the third argument — \rand($min, $max) — is evaluated on every call, even when a mock value is set. This is harmless for \rand(), but if the real function is expensive or has side effects that must be avoided when a mock is active, guard the call with MockRegistry::has():

'rand' => function (int $min, int $max): int {
    if (MockRegistry::has(Probability::class, 'rand')) {
        return MockRegistry::get(Probability::class, 'rand');
    }
    return \rand($min, $max);
}

Note: Using $GLOBALS inside override closures still works and remains fully supported. MockRegistry is a cleaner alternative, not a replacement — existing code does not need to be migrated.

Learn More

License

The PHP-Autoload-Override library is licensed under the MIT license. See License File for more information.

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.
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
anil/file-picker
broqit/fields-ai