Setting Up a PHP 7.4 / PHPUnit Toolchain for Moodle Development
How to configure a PHP 7.4 environment alongside a newer system PHP so that PHPUnit runs under the correct interpreter for a specific Moodle or Totara version.
Legacy Totara and Moodle checkouts requiring PHP 7.4 run into problems when the host's default PHP is newer. This guide shows how to create a dedicated PHP launcher directory and bypass Totara's PATH reset behavior so PHPUnit uses the correct interpreter.
Running the correct version of PHP and PHPUnit for a specific Moodle release is a prerequisite for reliable plugin tests. This guide covers setting up a parallel PHP 7.4 environment alongside a newer system PHP, and configuring PHPUnit to use it — working around the PATH reset issue in Totara's PHPUnit bootstrap.
Use this when a legacy Totara/Moodle checkout needs PHPUnit under PHP 7.4, but the host default php binary is a newer version and Totara's PHPUnit bootstrap keeps resolving nested php calls to the wrong interpreter.
Scope
This applies when all of the following are true:
- the application requires PHP 7.4 for test tooling
- `/usr/bin/php` points to a newer version
- Totara's `test/phpunit/phpunit.php` bootstrap is used
- Composer dependency install is blocked by a newer Composer or audit defaults
Why the normal PATH trick fails
Totara's PHPUnit wrapper resets PATH using dirname(PHP_BINARY). If you start it with /usr/bin/php7.4, it prepends /usr/bin again, and nested php calls still resolve to /usr/bin/php instead of PHP 7.4.
Recommended approach
- Create a dedicated launcher directory with a copied `php` binary for PHP 7.4.
- Skip Totara's Composer bootstrap if necessary and install PHPUnit dependencies manually with a Composer version that still supports the locked package set.
- Use Totara's lower-level `server/admin/tool/phpunit/cli/util.php` entrypoint directly, not `test/phpunit/phpunit.php init`, if the wrapper still leaks to the system PHP.
Example
Assumptions:
- PHP 7.4 binary: `/usr/bin/php7.4`
- project root: `/path/to/project/totara`
Create the launcher:
mkdir -p /tmp/php74bin
cp /usr/bin/php7.4 /tmp/php74bin/php
/tmp/php74bin/php -r 'echo PHP_BINARY, PHP_EOL;'
Expected output:
/tmp/php74bin/php
If the Totara checkout expects totara/config.php, provide the instance bridge temporarily:
ln -sf /path/to/instance/config.php /path/to/project/totara/config.php
Prepare Composer manually when Totara's init step cannot:
cd /path/to/project/totara
curl -L https://getcomposer.org/download/2.6.6/composer.phar -o composer.phar
/tmp/php74bin/php composer.phar --version
/tmp/php74bin/php composer.phar -d test/phpunit update phpunit/phpunit brianium/paratest -W
Provide a PHPUnit config if the instance config does not define phpunit_* settings:
cat > /tmp/project-phpunit-config.php <<'PHP'
<?php
$CFG = new stdClass();
$CFG->dataroot = '/tmp/project_phpunit_data';
$CFG->dbtype = 'pgsql';
$CFG->dblibrary = 'native';
$CFG->dbhost = 'localhost';
$CFG->dbname = 'project_db';
$CFG->dbuser = 'dbuser';
$CFG->dbpass = 'dbpass';
$CFG->prefix = 'phpu_';
$CFG->dboptions = [
'dbpersist' => false,
'dbsocket' => false,
'dbport' => '',
];
PHP
ln -sf /tmp/project-phpunit-config.php /path/to/project/totara/test/phpunit/config.php
Initialize the isolated PHPUnit site directly:
cd /path/to/project/totara
/tmp/php74bin/php -d max_input_vars=5000 server/admin/tool/phpunit/cli/util.php --install
/tmp/php74bin/php -d max_input_vars=5000 server/admin/tool/phpunit/cli/util.php --buildconfig
Run a targeted test:
cd /path/to/project/totara
/tmp/php74bin/php -d max_input_vars=5000 server/admin/tool/phpunit/cli/util.php --run --filter local_multitenant_delete_capability_testcase server/local/multitenant/tests/delete_capability_test.php
Cleanup:
rm -f /path/to/project/totara/config.php
rm -f /path/to/project/totara/test/phpunit/config.php
Notes
- Prefer a project-local `composer.phar` instead of changing the system Composer.
- If Composer still blocks old locked packages, either use an older Composer release or repair the PHPUnit lockfile under PHP 7.4.
- Do not use a shell wrapper script for `php`; in this environment `PHP_BINARY` still resolved to the original binary path. A copied PHP 7.4 binary worked.
- The `test/phpunit/phpunit.php init` wrapper may still leak to the host default `php`. If that happens, use `server/admin/tool/phpunit/cli/util.php` directly.
Solin specializes in Moodle development tooling and test infrastructure.
Contact us