{"record":{"id":"ba3289cbc287f5e1","repo":"hamcrest/hamcrest-php","slug":"must-pass-a-valid-html-or-xhtml-document","errorCode":null,"errorMessage":"Must pass a valid HTML or XHTML document","messagePattern":"Must pass a valid HTML or XHTML document","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"hamcrest/Hamcrest/Xml/HasXPath.php","lineNumber":85,"sourceCode":"\n    /**\n     * Creates and returns a <code>DOMDocument</code> from the given\n     * XML or HTML string.\n     *\n     * @param string $text\n     * @return \\DOMDocument built from <code>$text</code>\n     * @throws \\InvalidArgumentException if the document is not valid\n     */\n    protected function createDocument($text)\n    {\n        $document = new \\DOMDocument();\n        if (preg_match('/^\\s*<\\?xml/', $text)) {\n            if (!@$document->loadXML($text)) {\n                throw new \\InvalidArgumentException('Must pass a valid XML document');\n            }\n        } else {\n            if (!@$document->loadHTML($text)) {\n                throw new \\InvalidArgumentException('Must pass a valid HTML or XHTML document');\n            }\n        }\n\n        return $document;\n    }\n\n    /**\n     * Applies the configured XPath to the DOM node and returns either\n     * the result if it's an expression or the node list if it's a query.\n     *\n     * @param \\DOMNode $node context from which to issue query\n     * @return mixed result of expression or DOMNodeList from query\n     */\n    protected function evaluate(\\DOMNode $node)\n    {\n        if ($node instanceof \\DOMDocument) {\n            $xpathDocument = new \\DOMXPath($node);\n","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/hamcrest/hamcrest-php/blob/aa726aeff92ee86f82a89ca30dc0af9e01fb3539/hamcrest/Hamcrest/Xml/HasXPath.php#L67-L103","documentation":"The HTML branch of HasXPath::createDocument(): when the input does not start with an XML declaration, it is loaded as HTML via loadHTML(); failure throws 'Must pass a valid HTML or XHTML document'. loadHTML errors are suppressed, so any parse failure surfaces as this InvalidArgumentException.","triggerScenarios":"Passing empty strings, plain text, or badly broken markup (that libxml refuses) to hasXPath() without an XML declaration; passing XML that lacks the <?xml header but is not valid HTML either.","commonSituations":"Asserting against empty or whitespace-only response bodies; fragments with unclosed critical tags; passing JSON or plain text mistakenly expecting HTML.","solutions":["Check the string is non-empty and contains markup before calling hasXPath","Validate with a standalone loadHTML call in a test precondition","If the content is XML, add the <?xml declaration so the loadXML branch runs","Fix the upstream producer emitting empty/invalid HTML"],"exampleFix":"// before\nassertThat($emptyBody, hasXPath('//p'));\n// after\nif (trim($emptyBody) === '') { throw new RuntimeException('Empty HTML body'); }\nassertThat($emptyBody, hasXPath('//p'));","handlingStrategy":"validation","validationCode":"function isNonEmptyHtml(string $text): bool {\n    $t = trim($text);\n    if ($t === '') return false;\n    $d = new DOMDocument();\n    libxml_use_internal_errors(true);\n    $ok = @$d->loadHTML($t);\n    libxml_clear_errors();\n    return $ok;\n}","typeGuard":null,"tryCatchPattern":"try {\n    assertThat($html, hasXPath('//p'));\n} catch (\\InvalidArgumentException $e) {\n    if (strpos($e->getMessage(), 'HTML or XHTML') === false) throw $e;\n    fail('Invalid/empty HTML body: '.var_export(substr($html, 0, 200), true));\n}","preventionTips":["Assert response bodies are non-empty before XPath assertions","Preflight loadHTML validation on dynamic content","Add the <?xml declaration when content is XML/XHTML to route to loadXML","Check upstream content-type headers match the expected markup"],"tags":["php","html","hamcrest","parse-error"],"backgroundTag":"xml-parse-error","analyzedSha":"aa726aeff92ee86f82a89ca30dc0af9e01fb3539","analyzedAt":"2026-09-15T04:07:57.177Z","contentChangedAt":"2026-09-15T04:07:57.177Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}