This page is inspired by the curl project's Verify page.
Do not trust, verify!
Every PHPUnit release is shipped as a PHP Archive (PHAR). PHAR releases are available on https://phar.phpunit.de/.
Each PHAR is signed by Sebastian Bergmann. PGP signatures and SHA256 hashes are provided alongside every release for verification. The digital signatures can be used to verify that the PHARs were produced by Sebastian Bergmann and have not been tampered with.
If the PHPUnit website or PHAR distribution server were breached and fake PHPUnit releases were provided, they could be detected using these signatures.
Sebastian Bergmann's public GPG key: D840 6D0D 8294 7747 2937 7831 4AA3 9408 6372 C20A
For information on all keys Sebastian Bergmann uses to sign code, see his code signing policy.
Here is how you can manually verify a PHP Archive of a PHPUnit release using its detached PGP signature. We use PHPUnit 13 as an example throughout, but the process is the same for any version.
wget -O phpunit.phar https://phar.phpunit.de/phpunit-13.phar
wget -O phpunit.phar.asc https://phar.phpunit.de/phpunit-13.phar.asc
You need Sebastian Bergmann's public key in your local GPG keyring. You can import it directly from his website:
curl --silent https://sebastian-bergmann.de/gpg.asc | gpg --import
Alternatively, you can fetch it from a key server:
gpg --keyserver pgp.uni-mainz.de --recv-keys 0x4AA394086372C20A
gpg --verify phpunit.phar.asc phpunit.phar
If the PHAR has not been tampered with, GPG will report a good signature:
gpg: Signature made Mon Jul 19 06:13:42 2021 UTC
gpg: using RSA key D8406D0D82947747293778314AA394086372C20A
gpg: issuer "[email protected]"
gpg: Good signature from "Sebastian Bergmann <[email protected]>" [unknown]
gpg: aka "Sebastian Bergmann <[email protected]>" [unknown]
gpg: aka "Sebastian Bergmann <[email protected]>" [unknown]
gpg: aka "Sebastian Bergmann <[email protected]>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: D840 6D0D 8294 7747 2937 7831 4AA3 9408 6372 C20A
At this point, the signature is good, which means the file has not been tampered with. However, you may see a warning that the key is not certified with a trusted signature. This is expected if you have not established a chain of trust to Sebastian Bergmann's key through the GPG web of trust.
A good signature confirms the file was signed by whoever controls the private key matching the fingerprint D840 6D0D 8294 7747 2937 7831 4AA3 9408 6372 C20A.
To be certain that this key actually belongs to Sebastian Bergmann, you need to validate the authenticity of the key itself.
For example, by verifying the fingerprint through an independent channel or through the PGP web of trust.
If you see BAD signature, the file has been modified after it was signed.
Do not use it.
Download the PHAR again from a trusted source and repeat the verification.
In addition to PGP signatures, SHA256 hashes are published on https://phar.phpunit.de/. You can verify the integrity of a downloaded PHAR by comparing its hash:
sha256sum phpunit.phar
Compare the output against the hash published on the distribution page.
PHPUnit's PHAR bundles all of its dependencies in a single file. The PHAR itself provides three CLI options that let you inspect exactly what is inside it, without having to extract or reverse-engineer anything.
--manifestPrints a list of every package bundled in the PHAR together with its exact version number:
php phpunit.phar --manifest
phpunit/phpunit: 13.0.5
myclabs/deep-copy: 1.13.4
nikic/php-parser: v5.7.0
phar-io/manifest: 2.0.4
phar-io/version: 3.2.1
phpunit/php-code-coverage: 13.0.1
...
This is the quickest way to check which version of a particular dependency is bundled.
--sbomPrints a full Software Bill of Materials (SBOM) in CycloneDX XML format. For every bundled component, it includes the group, name, version, license, and a Package URL (purl):
php phpunit.phar --sbom
<?xml version="1.0"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.4">
<components>
<component type="library">
<group>phpunit</group>
<name>phpunit</name>
<version>13.0.5</version>
<description>The PHP Unit Testing framework.</description>
<licenses>
<license>
<id>BSD-3-Clause</id>
</license>
</licenses>
<purl>pkg:composer/phpunit/[email protected]</purl>
</component>
...
</components>
</bom>
The SBOM can be fed into supply-chain security tools that track known vulnerabilities across your dependency tree.
--composer-lock
Prints the composer.lock file that was used to install PHPUnit's dependencies when the PHAR was built:
php phpunit.phar --composer-lock
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "efcffb01d63d6a8a77320b50ca07079d",
...
}
This is useful for reproducing the exact build or for source-based distributions (such as Nix) that compile everything from source and need a lock file to guarantee reproducibility.
Together, these three options give you full transparency into what the PHAR contains and how it was built, complementing the signature-based verification described above.
Manually verifying PGP signatures every time you download or update PHPUnit is tedious. PHIVE (the PHAR Installation and Verification Environment) was created to automate this process. PHIVE handles downloading, signature verification, and management of PHAR-based tools for your PHP projects.
Install PHIVE:
wget -O phive.phar https://phar.io/releases/phive.phar
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x6AF725270AB81E04D79442549D8A98B29B2D5D79
gpg --verify phive.phar.asc phive.phar
chmod +x phive.phar
sudo mv phive.phar /usr/local/bin/phive
Then install PHPUnit with automatic signature verification:
phive install phpunit
PHIVE downloads the PHAR, fetches the PGP signature, verifies it, and only installs the tool if the signature is valid.
Sebastian Bergmann signs Git commits and tags for the PHPUnit source code repository.
Since February 18, 2026, Git commits and tags are signed with an SSH key.
From August 18, 2020 until February 18, 2025, Git commits and tags were signed with a GPG key.
Prior to August 18, 2020, Git commits and tags were not signed.
You can verify signed commits and tags in the PHPUnit source repository on GitHub using the corresponding public key.
How do you then verify that what is in the PHPUnit source repository is fine to build a product from?
The PHPUnit project maintains quality and integrity of its source code through several measures:
When you install PHPUnit via Composer (as a development dependency in your project), the verification story is different from the PHAR approach. Composer does not natively verify signatures on individual packages. Instead, integrity relies on a combination of Composer's built-in mechanisms and additional tooling.
Composer downloads packages over HTTPS from Packagist and checks that the downloaded archive matches the commit hash or tag recorded in composer.lock.
When you commit composer.lock to version control, subsequent runs of composer install will fetch the exact same versions and verify that the downloaded contents match the recorded hashes.
This protects against silent changes to a package after it was locked, but it does not verify that the package was authored by the person you expect.
Composer verifies its own integrity during self-updates. Composer treats repositories as canonical by default. This prevents an attacker from injecting a higher version from a repository they control.
Composer does not verify GPG or SSH signatures on the Git tags it downloads. There is currently no maintained Composer plugin that fills this gap.
If you need to verify tag signatures, you would have to do so manually against the PHPUnit source repository using the appropriate public key (see the Git commits and tags section above).
The PHAR distribution provides a stronger verification story out of the box: every release has a detached GPG signature that can be verified before execution. The PHAR also avoids dependency conflicts with your project's own dependencies. If cryptographic verification of PHPUnit itself is important to you, the PHAR (verified manually or via PHIVE) is the better choice.
You should not blindly trust this page or the verification tools. The GPG commands shown here are standard and can be independently confirmed from the GPG documentation. The key fingerprint published here can be cross-referenced against Sebastian Bergmann's code signing page, his profiles on GitHub, and other independent sources.
If in doubt, verify through multiple channels.