sebastianbergmann/phpunit · error · PhptExternalFileCannotBeLoadedException

Could not load --%s-- %s for PHPT file

Error message

Could not load --%s-- %s for PHPT file

What it means

parseExternal() resolves the --FILE_EXTERNAL--, --EXPECT_EXTERNAL--, --EXPECTF_EXTERNAL-- and --EXPECTREGEX_EXTERNAL-- values: the referenced file must exist in the same directory as the .phpt file (path is testDirectory . filename, so references are relative to the test, never to the CWD) and be readable; otherwise PhptExternalFileCannotBeLoadedException('Could not load --%s-- %s for PHPT file') is thrown. Both is_file() and is_readable() must pass after trim()ing the value.

Source

Thrown at src/Runner/Phpt/Parser.php:262

     */
    private function parseExternal(string $phptFile, array &$sections): void
    {
        $allowSections = [
            'FILE',
            'EXPECT',
            'EXPECTF',
            'EXPECTREGEX',
        ];

        $testDirectory = dirname($phptFile) . DIRECTORY_SEPARATOR;

        foreach ($allowSections as $section) {
            if (isset($sections[$section . '_EXTERNAL'])) {
                $externalFilename = trim($sections[$section . '_EXTERNAL']);

                if (!is_file($testDirectory . $externalFilename) ||
                    !is_readable($testDirectory . $externalFilename)) {
                    throw new PhptExternalFileCannotBeLoadedException(
                        $section,
                        $testDirectory . $externalFilename,
                    );
                }

                $externalPath = $testDirectory . $externalFilename;
                $contents     = file_get_contents($externalPath);

                assert($contents !== false);

                $sections[$section] = $contents;

                if ($section === 'FILE') {
                    $resolvedPath = realpath($externalPath);

                    assert(is_string($resolvedPath));
                    assert($resolvedPath !== '');

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Check that the referenced file exists in the same directory as the .phpt file and that its name (including case) matches exactly.
  2. Ship or restore the missing external fixture in your test distribution.
  3. Fix permissions so the PHP process can read the file (is_readable must pass, not just file_exists).

Example fix

-- before
--FILE_EXTERNAL--
example_code.php.txt   % actual file is example_code.php

-- after
--FILE_EXTERNAL--
example_code.php
Defensive patterns

Strategy: validation

Validate before calling

function assertPhptExternalFilesExist(string $phptFile): void
{
    $dir = dirname($phptFile) . DIRECTORY_SEPARATOR;

    foreach (file($phptFile) as $line) {
        if (preg_match('/^--(FILE|EXPECT|EXPECTF|EXPECTREGEX)_EXTERNAL--/', $line, $m) === 0) {
            continue;
        }
        // value = next line(s); simplified: assume single-line value follows
    }

    $sections = (new \PHPUnit\Runner\Phpt\Parser())->parse($phptFile); // not reached if invalid
}

// simpler targeted check before running a suite:
foreach (glob('tests/*.phpt') as $phpt) {
    $dir = dirname($phpt) . DIRECTORY_SEPARATOR;
    $body = file_get_contents($phpt);
    foreach (['FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX'] as $s) {
        if (preg_match('/--' . $s . '_EXTERNAL--\R\s*(\S+)/', $body, $m) === 1
            && (!is_file($dir . $m[1]) || !is_readable($dir . $m[1]))) {
            throw new RuntimeException("Missing external fixture: {$dir}{$m[1]}");
        }
    }
}

Try / catch

try {
    (new \PHPUnit\Runner\Phpt\Parser())->parse($phptFile);
} catch (\PHPUnit\Runner\Phpt\PhptExternalFileCannotBeLoadedException $e) {
    // message names the section and expected path; restore the fixture and retry
}

Prevention

When it happens

Trigger: An external section references a file that does not exist next to the .phpt file (typo, wrong case, missing from the distribution) or exists but is not readable by the PHP process; a stray trailing character in the section value makes the trimmed filename wrong.

Common situations: Packaging PHPT suites where external fixture files were excluded from the dist/zipball; case-sensitivity differences between macOS and Linux filesystems; checkouts or containers where file permissions deny read access.

Related errors


AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23). Data as JSON: /api/errors/14d75e09e86aaaa2. Report an issue: GitHub.