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

Phpspec Typehintedmethods Laravel Package

ciaranmcnulty/phpspec-typehintedmethods

PhpSpec extension that generates typehinted methods for your collaborators, so spec-generated stubs and mocks include proper parameter and return types. Keeps specs clean while producing type-safe, IDE-friendly code.

View on GitHub
Deep Wiki
Context7

Getting Started

phpspec-typehintedmethods is a phpspec extension that changes how phpspec generates doubles. By default, the methods phpspec writes onto a generated double can lose the parameter and return type declarations of the collaborator they mirror. That is usually harmless on loose code, but the moment a collaborator looks like this:

interface UserRepository
{
    public function find(int $id): User;
}

phpspec can generate a find() on the double whose signature no longer matches, and PHP 7+ fails with a Declaration of PhpSpec\...\Double\...::find() must be compatible with ... fatal before a single example executes. This extension preserves the type hints on the generated methods so the double is a faithful stand-in.

Install it as a dev dependency (phpspec itself is already a dev dependency):

composer require --dev ciaranmcnulty/phpspec-typehintedmethods

Register the extension in your phpspec config. This is the step people forget — installing alone does nothing:

# phpspec.yml (or phpspec.yml.dist for the whole team)
extensions:
    - CiaranMcNulty\PhpSpecTypehintedMethods\Extension   # confirm the FQCN in the package README

Where to look first: open src/Extension.php in the package. It is tiny and shows which phpspec service it decorates/replaces for double generation, plus which phpspec version it targets. Then run vendor/bin/phpspec run against a suite that was failing on signature compatibility and confirm the generated double now carries the hints.

First use case — a service that depends on a typed collaborator:

class UserServiceSpec extends ObjectBehavior
{
    function let(UserRepository $users)
    {
        $this->beConstructedWith($users);
    }

    function it_returns_a_user(UserRepository $users, User $user)
    {
        $users->find(1)->willReturn($user);

        $this->get(1)->shouldBe($user);
    }
}

Without the extension, the double for UserRepository may not declare : User; with it, the generated find(int $id): User matches the interface and the spec runs.

Implementation Patterns

Turn it on project-wide, not per spec. The extension is global config; there is no per-example flag. Put it in phpspec.yml.dist, commit that file, and let phpspec.yml override locally if needed.

Type-hinted collaborators in let() / letGo(). Once installed, you can spec against interfaces and classes that use scalar, class, nullable, and return type declarations. phpspec will resolve the type-hinted constructor parameters into doubles, and the extension ensures those doubles satisfy the declared contract.

Stub values must match the declared return type. The extension is not a coercion layer — it copies the signature. If the collaborator declares : User, then willReturn($user) is correct and willReturn(null) is a runtime TypeError. For nullable methods (?User), willReturn(null) is valid.

Void methods are a separate pattern. For collaborators like:

interface Mailer
{
    public function send(Message $message): void;
}

do not write willReturn(...); a void method cannot return a value. Use shouldBeCalled() / shouldBeCalledTimes() instead:

$mailer->send($message)->shouldBeCalled();

Typed arguments still work with argument matching. Scalar type hints on parameters are enforced by the generated double, so withArguments([1]) is fine while withArguments(['1']) may be coerced or rejected depending on the PHP version. Keep matchers typed.

Use it as a guardrail in CI. Run the same phpspec suite on the minimum and maximum PHP versions you support. Signature problems from type hints usually appear only under the PHP version that actually enforces them, and the extension makes that failure explicit and early.

When not to reach for it: codebases with no type hints get no benefit (the extension is effectively a no-op), and very recent phpspec releases may already handle return types natively. Check composer show phpspec/phpspec and try a failing case without the extension before adding another dependency.

Gotchas and Tips

Maintenance and version compatibility. The last release was in 2018 and it has effectively no dependents. It targets the phpspec/PHP 7 era. Verify it against your installed phpspec major version before wiring it into CI; newer phpspec versions may have fixed the underlying generation or changed the service it decorates.

Failures are fatal errors, not spec failures. Signature mismatches, ParseErrors from generated code, and TypeErrors from the doubles surface as PHP errors before the pretty-phpspec output. Read the full stack trace: "Declaration must be compatible" points at the double generation, not your spec logic.

Missing stubs become TypeErrors. Because the double now declares return types, an unstubbed method returning null will throw Return value must be of type User, none returned. That is arguably better feedback, but it changes the failure mode from "unexpected null" to a type error — update specs to stub all typed methods the subject exercises.

self vs static return types. A method returning self on a generated double is declared in the double class, so self resolves to the double rather than your original class. Stubbing it with a concrete instance can fail. Prefer static (which resolves to the double and remains assignable), or avoid stubbing these methods with real objects.

Union, intersection, and never types are outside the 2018 code's model. PHP 8 union types (User|Guest), intersection types, and never are unlikely to be handled correctly by a package last touched in 2018. Test them explicitly, or treat them as unsupported.

Watch extension ordering. Any other extension that also decorates phpspec's double generation can conflict. If adding this one causes a different failure, remove it, confirm the baseline, then re-add it last.

Autoloading after install. If phpspec reports "class not found" for the extension, run composer dump-autoload and re-check the FQCN in phpspec.yml against the package's README.

No cache to clear. Doubles are generated in memory at runtime; there is no generated-file cache to wipe after changing signatures or upgrading the extension. Just re-run phpspec.

Extension point for unsupported cases. Because the extension is small and decorates a single phpspec service, the practical escape hatch for a type it cannot handle is to copy/decorate the same service yourself, or fork the extension. Read its load() method to find the service ID to decorate.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console