{"record":{"id":"c105e467d97c1ecf","repo":"sebastianbergmann/phpunit","slug":"phpt-file-must-contain-one-of-the-sections-s","errorCode":null,"errorMessage":"PHPT file must contain one of the sections --%s--","messagePattern":"PHPT file must contain one of the sections --(.+?)--","errorType":"exception","errorClass":"InvalidPhptFileException","httpStatus":null,"severity":"error","filePath":"src/Runner/Phpt/Parser.php","lineNumber":307,"sourceCode":"     * @param non-empty-list<non-empty-string> $sectionFamily\n     *\n     * @throws ConflictingPhptSectionsException\n     * @throws InvalidPhptFileException\n     *\n     * @return non-empty-string\n     */\n    private function ensureExactlyOneSectionOf(array $sections, array $sectionFamily): string\n    {\n        $found = [];\n\n        foreach ($sectionFamily as $name) {\n            if (isset($sections[$name])) {\n                $found[] = $name;\n            }\n        }\n\n        if ($found === []) {\n            throw new InvalidPhptFileException(\n                sprintf(\n                    'PHPT file must contain one of the sections --%s--',\n                    implode('--, --', $sectionFamily),\n                ),\n            );\n        }\n\n        if (count($found) > 1) {\n            throw new ConflictingPhptSectionsException($found);\n        }\n\n        return $found[0];\n    }\n\n    /**\n     * @param array<non-empty-string, string> $sections\n     * @param non-empty-string                $codeSection\n     *","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Runner/Phpt/Parser.php#L289-L325","documentation":"parse() calls ensureExactlyOneSectionOf() twice: once for the code family (FILE, FILEEOF, FILE_EXTERNAL) and once for the expectation family (EXPECT, EXPECT_EXTERNAL, EXPECTF, EXPECTF_EXTERNAL, EXPECTREGEX, EXPECTREGEX_EXTERNAL). When no member of a family is present, InvalidPhptFileException('PHPT file must contain one of the sections --%s--') is thrown with the family list joined as --FILE--, --FILEEOF--, --FILE_EXTERNAL--. Every .phpt needs exactly one code section and exactly one expectation section.","triggerScenarios":"A .phpt file with an expectation section but no --FILE--/--FILEEOF--/--FILE_EXTERNAL--, or with a code section but no --EXPECT*-- section (for example only --TEST-- and --FILE--).","commonSituations":"Truncating a template when creating a new test; deleting a section while editing; assuming --TEST-- alone is enough because some other PHPT runner tolerates it.","solutions":["Add the missing code section (--FILE-- is the usual choice) when the complaint lists the FILE family.","Add the missing expectation section (--EXPECT--, or --EXPECTF--/--EXPECTREGEX-- for fuzzy matching) when the complaint lists the EXPECT family.","Even when using --SKIPIF-- to skip the test, keep one code and one expectation section in the file."],"exampleFix":"-- before\n--TEST--\nDemo\n--FILE--\necho 'Hello';\n\n-- after\n--TEST--\nDemo\n--FILE--\necho 'Hello';\n--EXPECT--\nHello","handlingStrategy":"validation","validationCode":"function assertPhptHasRequiredFamilies(string $phptFile): void\n{\n    $body = (string) file_get_contents($phptFile);\n    $code   = ['FILE--', 'FILEEOF--', 'FILE_EXTERNAL--'];\n    $expect = ['EXPECT--', 'EXPECT_EXTERNAL--', 'EXPECTF--', 'EXPECTF_EXTERNAL--', 'EXPECTREGEX--', 'EXPECTREGEX_EXTERNAL--'];\n\n    foreach (array_merge($code, $expect) as $needle) {\n        if (str_contains($body, '--' . $needle)) {\n            return; // at least one section header found\n        }\n    }\n\n    throw new RuntimeException($phptFile . ' lacks a code and/or expectation section');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Scaffold new .phpt files from a template that already contains --TEST--, --FILE-- and --EXPECT--.","Treat deletion of any of those three headers as a review-blocking change."],"tags":["phpunit","phpt","required-section","parser"],"backgroundTag":"missing-required-section","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}