Weave 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 Mock Prophecy Laravel Package

php-mock/php-mock-prophecy

Prophecy integration for php-mock lets you mock PHP built-in functions like time, rand, and sleep inside Prophecy test doubles. Define expected behavior for global functions alongside class mocks, making time-dependent code testable without hard dependencies.

Deep Wiki
Context7

Getting Started

Install (dev-only; it hooks into the test suite, never runtime):

composer require --dev php-mock/php-mock-prophecy

It pulls in php-mock/php-mock (the engine that actually defines the function stubs) and Prophecy (already a transitive dependency of phpspec, so this is a natural fit there). PHPUnit also works if you're already comfortable with Prophecy.

Where to look first: the README example — it's essentially the whole package. Then read phpmock\prophecy\Prophet and phpmock\prophecy\FunctionMock, which are thin wrappers around php-mock's FunctionMock. You should understand php-mock's core trick before anything else: it cannot replace a built-in function. Instead it defines a same-named function in a chosen namespace, so PHP's unqualified-call fallback resolves your SUT's time() to the generated stub.

First use case — a deterministic clock:

namespace App\Service;

class OrderNumberGenerator
{
    public function generate(): string
    {
        return 'ORD-' . time();   // unqualified: resolves to App\Service\time() if defined
    }
}
namespace App\Service;

class OrderNumberGeneratorTest extends \PHPUnit\Framework\TestCase
{
    public function testGenerateUsesCurrentTimestamp(): void
    {
        $prophet  = new \phpmock\prophecy\Prophet(__NAMESPACE__);
        $prophecy = $prophet->prophesize('time');
        $prophecy->willReturn(1234567890);
        $prophecy->reveal();               // defines App\Service\time()

        $this->assertSame('ORD-1234567890', (new OrderNumberGenerator())->generate());
    }
}

The namespace argument is the crux. It must match the namespace your code-under-test calls the function from. If the constructor signature differs in your installed version, check the README — but the concept is always the same: you are mocking the function in a namespace, not the global one. Once that clicks, the package is trivial to use. Until it clicks, every test "fails silently" by calling the real time().

Implementation Patterns

  • Deterministic time. Mock time(), microtime(), and date() (or, more reliably, inject a clock and only mock time() at the edge). Use with() to answer differently per argument:

    $prophecy = $prophet->prophesize('date');
    $prophecy->with('Y-m-d', 1234567890)->willReturn('2009-02-13');
    
  • Seeded randomness. rand(), mt_rand(), random_int(), uniqid() — return a fixed value or a sequence:

    $prophecy->rand()->willReturn(42);
    // or a queue
    $prophecy->rand()->willReturn(1, 2, 3);
    
  • Filesystem and network boundaries. file_get_contents(), fopen(), file_exists(), curl_exec(), fsockopen(), gethostbyname(). This is where the package earns its keep for legacy code you can't refactor:

    $prophecy = $prophet->prophesize('file_exists');
    $prophecy->with('/etc/config.yml')->willReturn(false);
    
  • Failure paths without touching the real world. willThrow() beats @group integration for exercising error branches:

    $prophecy->file_get_contents()->willReturn(false);
    $prophecy->curl_exec()->willThrow(new \RuntimeException('timeout'));
    
  • Call verification. Treat it like any Prophecy double — shouldBeCalled(), shouldBeCalledTimes(), shouldNotBeCalled() work, but remember the Prophecy predictions are only checked when something runs checkPredictions(). Under phpspec that's automatic; under PHPUnit call it in tearDown() or use ->shouldBeCalled() alongside a $prophet->checkPredictions().

  • phpspec integration. In let()/it_*() you already have a Prophet, so keep the namespace argument tied to your spec's subject and you get one consistent mocking style across the whole spec. This is the package's real audience.

  • Process-global mocks. Because the stub is a real function, one reveal() covers every call site in that namespace for the rest of the process. Prefer a fresh Prophet per test, and avoid static caches in the SUT (a cached time() will never be re-mocked).

  • Keep it at the edges. Wrap built-ins (Clock, Filesystem, Random) and mock those collaborators directly; reserve php-mock-prophecy for code you can't restructure.

Gotchas and Tips

  • Namespacing is everything. Fully-qualified calls (\time()), use function time; imports, and calls from a different namespace all bypass the mock. The most common "it doesn't work" bug is a mismatch between the mocked namespace and the SUT's actual namespace. Verify with function_exists('App\Service\time') after reveal().
  • You cannot un-define a function. Once a namespace has a generated stub, it stays defined for the process. php-mock exposes disable()/enable() on the underlying FunctionMock for that reason; repeated reveal() calls in the same process can conflict. If tests bleed into each other, consider @runInSeparateProcess or a dedicated test process.
  • Only functions. Language constructs (isset, echo, new, instanceof) and constants are out of scope. For date_default_timezone_get()-style configuration, prefer putenv()/date_default_timezone_set() in setUp()/tearDown().
  • Mock before the SUT runs. In PHPUnit, put reveal() at the top of the test (or in setUpAfterClass for shared setups). A reveal() that runs after the assertion reads as a no-op.
  • No effect on compiled/static caches. OPcache is not the issue; application-level memoization is. Any private static ?int $cachedNow in the SUT defeats the mock.
  • Case sensitivity of the target name. PHP function names are case-insensitive but php-mock keys on the string you pass. Pass the function name in lowercase exactly as it appears in source, and be consistent across tests.
  • Debugging aids. ReflectionFunction('App\Service\time')->getFileName() tells you whether the stub or the real builtin was resolved (real builtins report as internal). error_log inside the stub is not possible here, but willReturn of a sentinel value makes a hit obvious.
  • Ecosystem risk. 16 stars, WTFPL, last release November 2025 — maintained, but a narrow bridge. Compatibility is constrained by three moving parts (PHP version, php-mock core, Prophecy/PHPUnit). Pin versions in composer.json and add a smoke test that asserts the mock actually takes effect, so a dependency bump can't silently turn your deterministic tests back into flaky ones.
  • Consider php-mock/php-mock-phpunit if you use PHPUnit more than phpspec — its PHPMock trait is the more actively exercised code path in the same family. And for new code, an injected clock beats mocking built-ins every time; this package is best reserved for untangling legacy code.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin