A build stage that exists only to fail the build

A stage that runs the analyser and the tests, produces no artefact, and is depended on by the final stage so it cannot be skipped.

FROM vendor AS check
RUN vendor/bin/phpstan analyse --no-progress
RUN vendor/bin/phpunit --no-coverage

FROM base AS final
COPY --from=check /app/composer.lock /tmp/.check
COPY --from=vendor /app/vendor /app/vendor
COPY . /app

# the COPY from `check` is doing nothing except forcing
# the stage to run. it is a dependency edge, written as a copy.

BuildKit skips any stage nothing depends on, which is a good default and means a check stage is silently unreachable unless something pulls from it. The copy of a file nobody uses is the idiom for creating that edge and it reads as a mistake, so it needs the comment. Whether tests belong in a build at all is a fair question — here it stopped an image being pushed from a branch that had not run CI.