Scripts in composer.json run with vendor/bin prepended to PATH, so the tools do not need a path prefix — which is why "test": "phpunit" works and the same command in a Makefile does not.
{
"scripts": {
"test": "phpunit --colors=always",
"cs": "phpcs --standard=phpcs.xml src",
"ci": ["@cs", "@test"]
}
}
The knock-on effect is that anything spawned by those scripts inherits the path too, so a test that shells out to a project tool finds it. It only applies for the duration of the script; a developer typing phpunit in a terminal still gets whatever is installed globally, which is a common source of “it passes for me” when the global version differs from the locked one.