PHPackages                             rasuvaeff/understudy-psalm - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. rasuvaeff/understudy-psalm

ActivePsalm-plugin[Testing &amp; Quality](/categories/testing)

rasuvaeff/understudy-psalm
==========================

Psalm plugin for the understudy test double library: matcher-aware setup closures and misuse diagnostics

v0.1.0(today)02↑2900%BSD-3-ClausePHPPHP 8.3 - 8.5CI passing

Since Aug 25Pushed todayCompare

[ Source](https://github.com/rasuvaeff/understudy-psalm)[ Packagist](https://packagist.org/packages/rasuvaeff/understudy-psalm)[ Docs](https://github.com/rasuvaeff/understudy-psalm)[ RSS](/packages/rasuvaeff-understudy-psalm/feed)WikiDiscussions master Synced today

READMEChangelog (2)Dependencies (12)Versions (2)Used By (0)

rasuvaeff/understudy-psalm
==========================

[](#rasuvaeffunderstudy-psalm)

[![Latest Stable Version](https://camo.githubusercontent.com/b8d389a21f42bdd5968d948acc23f3e11b2dfea5b4910f95c5b7f93c3bd6ee71/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f756e64657273747564792d7073616c6d2f76)](https://packagist.org/packages/rasuvaeff/understudy-psalm)[![Total Downloads](https://camo.githubusercontent.com/32f9f6cced74127114bafd467ca6d6fe9ca6128e933e80987f2d74ff2fe5d7dd/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f756e64657273747564792d7073616c6d2f646f776e6c6f616473)](https://packagist.org/packages/rasuvaeff/understudy-psalm)[![Build](https://github.com/rasuvaeff/understudy-psalm/actions/workflows/build.yml/badge.svg)](https://github.com/rasuvaeff/understudy-psalm/actions/workflows/build.yml)[![Static analysis](https://github.com/rasuvaeff/understudy-psalm/actions/workflows/static-analysis.yml/badge.svg)](https://github.com/rasuvaeff/understudy-psalm/actions/workflows/static-analysis.yml)[![Psalm level](https://camo.githubusercontent.com/68f7f31799f2b93c710b14ba3877072e7fe07ec9d7cee3fdf67e14beab3e1b6f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7073616c6d2d6c6576656c5f312d626c75652e737667)](https://github.com/rasuvaeff/understudy-psalm/actions/workflows/static-analysis.yml)[![PHP](https://camo.githubusercontent.com/65df352af1dcf1f26b7bf3d213e7282f6d4db6198788d8e012ffd157b92cc5a4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f7261737576616566662f756e64657273747564792d7073616c6d2f706870)](https://packagist.org/packages/rasuvaeff/understudy-psalm)[![License](https://camo.githubusercontent.com/6cb285b57819f8de0acfb34923298f4f569f962544e8fe35331da2d163f4e485/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4253442d2d332d2d436c617573652d626c75652e737667)](LICENSE.md)[Русская версия](README.ru.md)

Psalm plugin for [understudy](https://github.com/rasuvaeff/understudy).

> Using an AI coding assistant? Point it at [llms.txt](llms.txt).

Requirements
------------

[](#requirements)

- PHP 8.3 - 8.5
- `vimeo/psalm` ^6.16
- `rasuvaeff/understudy` ^0.1

Installation
------------

[](#installation)

```
composer require --dev rasuvaeff/understudy-psalm
vendor/bin/psalm-plugin enable rasuvaeff/understudy-psalm
```

What it does
------------

[](#what-it-does)

understudy specifies a call by making it inside a closure:

```
when(fn () => $repo->find(Arg::int(min: 1)))->returns($book);
```

`Arg::int()` is declared `mixed`, because a matcher has to be passable wherever the contract declares anything at all. Psalm reports that as an argument-type error — correctly, in general, and wrongly here.

The plugin drops that report inside a specification closure, and only there:

WherePsalm`when(fn () => $repo->find(Arg::int()))`silent`Understudy::expect(fn () => $repo->find(Arg::any()))`silent`Understudy::calls(fn () => $repo->find(Arg::any()))`silent`Understudy::verifySequence(fn () => ..., fn () => ...)`silent`$repo->find(Arg::int())` — a real callstill an errorThe last row is the point. A matcher reaching a real call raises `MatcherLeaked` at runtime, and a plugin that hid it would be worse than no plugin at all.

What else it reports
--------------------

[](#what-else-it-reports)

Reported asFor`UnderstudyMisuse`A matcher whose kind the parameter can never accept. A closure that specifies nothing, or two calls, or calls a static method. Cardinality no run can satisfy. `verify()` arguments that contradict each other.Psalm's own `InvalidArgument``returns()` and `answers()` against the method being specified. The plugin does not check these — it fills in the builder's template parameter, and `WhenBuilder` already declares `returns(TReturn ...)`. Psalm does the rest.Psalm's own array and method diagnostics`wire()`, whose shape is read from the named class's constructor: an unknown key is an error, and each double is typed as its contract. A dynamic class-string is left alone.Everything the plugin is not sure about stays silent. A false accusation costs more than a missed one here, because the engine still catches what static analysis misses.

API
---

[](#api)

TypePurpose`UnderstudyMisuse`The issue every diagnostic of this plugin is reported as. One type rather than one per rule: you either have understudy analysing your specifications or you do not, and needing to silence each rule separately would be a worse contract than needing to silence none.`Plugin`The entry point Psalm loads, registered through `extra.psalm.pluginClass`. Nothing else in this package is public — the handlers it registers are `@internal`, and what they decide is the plugin's behaviour, not its API.The understudy family
---------------------

[](#the-understudy-family)

PackageWhat it is[rasuvaeff/understudy](https://github.com/rasuvaeff/understudy)The engine: doubles, matchers, expectations, verification.[rasuvaeff/understudy-testo](https://github.com/rasuvaeff/understudy-testo)Testo adapter — verification and reset around every test.[rasuvaeff/understudy-phpunit](https://github.com/rasuvaeff/understudy-phpunit)PHPUnit and Pest adapter — the same, through a trait.**rasuvaeff/understudy-psalm** *(this package)*Psalm plugin — matcher-aware specifications and misuse diagnostics.[rasuvaeff/understudy-phpstan](https://github.com/rasuvaeff/understudy-phpstan)PHPStan extension — the same for PHPStan, plus its own rules.Development
-----------

[](#development)

```
make build          # validate + cs + psalm + unit + integration
make test-integration
```

The integration suite runs real Psalm processes over a fixture project, twice: once with the plugin and once without. The run without it is what proves the run with it means anything — a plugin that loads and does nothing passes a positive fixture exactly as well as one that works.

License
-------

[](#license)

BSD-3-Clause.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance100

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b0812d5572a7041dfe36e222d295b2e6dc55833a605350fcde58a51a5965ed30?d=identicon)[rasuvaeff](/maintainers/rasuvaeff)

---

Top Contributors

[![rasuvaeff](https://avatars.githubusercontent.com/u/1352718?v=4)](https://github.com/rasuvaeff "rasuvaeff (10 commits)")

---

Tags

mockmockingphpphp8psalmpsalm-pluginstatic-analysistest-doubletestingtestingstatic analysismockstubtest doublepsalm

###  Code Quality

Static AnalysisRector

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/rasuvaeff-understudy-psalm/health.svg)

```
[![Health](https://phpackages.com/badges/rasuvaeff-understudy-psalm/health.svg)](https://phpackages.com/packages/rasuvaeff-understudy-psalm)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[mockery/mockery

Mockery is a simple yet flexible PHP mock object framework

10.7k536.9M28.7k](/packages/mockery-mockery)[phpunit/php-code-coverage

Library that provides collection, processing, and rendering functionality for PHP code coverage information.

8.9k951.4M1.8k](/packages/phpunit-php-code-coverage)[behat/behat

Scenario-oriented BDD framework for PHP

4.0k103.4M2.3k](/packages/behat-behat)[infection/infection

Infection is a Mutation Testing framework for PHP. The mutation adequacy score can be used to measure the effectiveness of a test set in terms of its ability to detect faults.

2.2k30.0M3.0k](/packages/infection-infection)[php-mock/php-mock

PHP-Mock can mock built-in PHP functions (e.g. time()). PHP-Mock relies on PHP's namespace fallback policy. No further extension is needed.

37020.4M140](/packages/php-mock-php-mock)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
