1. Installing PHPUnit

Requirements

PHPUnit 9.6 requires PHP 7.3; using the latest version of PHP is highly recommended.

PHPUnit requires the dom and json extensions, which are normally enabled by default.

PHPUnit also requires the pcre, reflection, and spl extensions. These standard extensions are enabled by default and cannot be disabled without patching PHP’s build system and/or C sources.

The code coverage report feature requires the Xdebug (2.7.0 or later) and tokenizer extensions. Generating XML reports requires the xmlwriter extension.

Prophecy

PHPUnit has out-of-the-box support for using Prophecy to create test doubles. However, as of PHPUnit 9.5.23 you have to add a dependency on phpspec/prophecy to your project’s composer.json file if you install PHPUnit using Composer and want to use Prophecy through PHPUnit’s TestCase::prophesize() method.

Please note that PHPUnit’s out-of-the-box support for Prophecy is deprecated as of PHPUnit 9.1.0 and will be removed in PHPUnit 10. Also note that Prophecy does not support PHP 8.2 as of August 2022.

More details on why you have to add a dependency on phpspec/prophecy to your project’s composer.json as well as PHP 8.2 support is available here.

For the output of PHPUnit to be most informative, it is recommended to have the following configuration set in the php.ini file used for test runs:

  1. memory_limit=-1
  2. error_reporting=-1
  3. log_errors_max_len=0
  4. zend.assertions=1
  5. assert.exception=1
  6. xdebug.show_exception_trace=0

PHP Archive (PHAR)

The easiest way to obtain PHPUnit is to download a PHP Archive (PHAR) that has all required (as well as some optional) dependencies of PHPUnit bundled in a single file.

The phar extension is required for using PHP Archives (PHAR).

If the Suhosin extension is enabled, you need to allow execution of PHARs in your php.ini:

  1. suhosin.executor.include.whitelist = phar

The PHPUnit PHAR can be used immediately after download:

  1. $ curl -LO https://phar.phpunit.de/phpunit-9.6.phar
  2. $ php phpunit-9.6.phar --version
  3. PHPUnit x.y.z by Sebastian Bergmann and contributors.

It is a common practice to make the PHAR executable:

  1. $ curl -LO https://phar.phpunit.de/phpunit-9.6.phar
  2. $ chmod +x phpunit-9.6.phar
  3. $ ./phpunit-9.6.phar --version
  4. PHPUnit x.y.z by Sebastian Bergmann and contributors.

PHAR Implementation Details

To avoid problems that occur when the code under test shares dependencies with PHPUnit but requires different versions than the ones bundled in the PHAR, the following measures have been implemented.

With the exception of classes such as PHPUnit\Framework\TestCase that are part of PHPUnit’s public API, all units of code bundled in PHPUnit’s PHAR distribution, including all dependencies such as vendor directories, are moved to a new and distinct namespace.

All units of code bundled in the PHAR are loaded on startup using a combination of static preloading and dynamic autoloading.

Verifying PHPUnit PHAR Releases

All official releases of code distributed by the PHPUnit Project are signed by the release manager for the release. PGP signatures and SHA256 hashes are available for verification on phar.phpunit.de.

The following example details how release verification works. We start by downloading phpunit.phar as well as its detached PGP signature phpunit.phar.asc:

  1. $ curl -LO https://phar.phpunit.de/phpunit-9.6.phar
  2. $ curl -LO https://phar.phpunit.de/phpunit-9.6.phar.asc

We want to verify PHPUnit’s PHP Archive (phpunit-x.y.phar) against its detached signature (phpunit-x.y.phar.asc):

  1. $ gpg --verify phpunit-9.6.phar.asc
  2. gpg: assuming signed data in 'phpunit-9.6.phar'
  3. gpg: Signature made Mon Jul 19 06:13:42 2021 UTC
  4. gpg: using RSA key D8406D0D82947747293778314AA394086372C20A
  5. gpg: issuer "sb@sebastian-bergmann.de"
  6. gpg: Can't check signature: No public key

We do not have the release manager’s public key in our local system. In order to proceed with the verification we need to import this key:

  1. $ curl --silent https://sebastian-bergmann.de/gpg.asc | gpg --import
  2. gpg: key 4AA394086372C20A: 452 signatures not checked due to missing keys
  3. gpg: key 4AA394086372C20A: public key "Sebastian Bergmann <sb@sebastian-bergmann.de>" imported
  4. gpg: Total number processed: 1
  5. gpg: imported: 1
  6. gpg: no ultimately trusted keys found

Now we have imported a public key for an entity known as “Sebastian Bergmann <sb@sebastian-bergmann.de>”. However, we have no way of verifying this key was created by the person known as Sebastian Bergmann. But, let’s try to verify the release signature again.

  1. $ gpg --verify phpunit-9.6.phar.asc
  2. gpg: assuming signed data in 'phpunit-9.6.phar'
  3. gpg: Signature made Mon Jul 19 06:13:42 2021 UTC
  4. gpg: using RSA key D8406D0D82947747293778314AA394086372C20A
  5. gpg: issuer "sb@sebastian-bergmann.de"
  6. gpg: Good signature from "Sebastian Bergmann <sb@sebastian-bergmann.de>" [unknown]
  7. gpg: aka "Sebastian Bergmann <sebastian@thephp.cc>" [unknown]
  8. gpg: aka "Sebastian Bergmann <sebastian@phpunit.de>" [unknown]
  9. gpg: aka "Sebastian Bergmann <sebastian@php.net>" [unknown]
  10. gpg: aka "Sebastian Bergmann <sebastian.bergmann@thephp.cc>" [unknown]
  11. gpg: aka "[jpeg image of size 40635]" [unknown]
  12. gpg: WARNING: This key is not certified with a trusted signature!
  13. gpg: There is no indication that the signature belongs to the owner.
  14. Primary key fingerprint: D840 6D0D 8294 7747 2937 7831 4AA3 9408 6372 C20A

At this point, the signature is good, but we do not trust this key. A good signature means that the file has not been tampered. However, due to the nature of public key cryptography, you need to additionally verify that the key you just imported was created by the real Sebastian Bergmann.

Any attacker can create a public key and upload it to the public key servers. They can then create a malicious release signed by this fake key. Then, if you tried to verify the signature of this corrupt release, it would succeed because the key was not the “real” key. Therefore, you need to validate the authenticity of this key. Validating the authenticity of a public key, however, is outside the scope of this documentation.

Manually verifying the authenticity and integrity of a PHPUnit PHAR using GPG is tedious. This is why PHIVE, the PHAR Installation and Verification Environment, was created. You can learn about PHIVE on its website

Composer

Add a (development-time) dependency on phpunit/phpunit to your project’s composer.json file if you use Composer to manage the dependencies of your project:

  1. composer require --dev phpunit/phpunit ^9.6

Global Installation

Please note that it is not recommended to install PHPUnit globally, as /usr/bin/phpunit or /usr/local/bin/phpunit, for instance.

Instead, PHPUnit should be managed as a project-local dependency.

Either put the PHAR of the specific PHPUnit version you need in your project’s tools directory (which should be managed by PHIVE) or depend on the specific PHPUnit version you need in your project’s composer.json if you use Composer.

Webserver

PHPUnit is a framework for writing as well as a commandline tool for running tests. Writing and running tests is a development-time activity. There is no reason why PHPUnit should be installed on a webserver.

If you upload PHPUnit to a webserver then your deployment process is broken. On a more general note, if your vendor directory is publicly accessible on your webserver then your deployment process is also broken.

Please note that if you upload PHPUnit to a webserver “bad things” may happen. You have been warned.