*/ private array $rules; /** * Constructor. * * @param list $rules Rules to run on each scan. */ public function __construct( array $rules ) { $this->rules = $rules; } /** * Parses the given HTML and runs all registered rules against it. * * @param string $html HTML to scan. * @param array $context Optional context passed to each rule. * @return list */ public function run( string $html, array $context = array() ): array { if ( '' === trim( $html ) ) { return array(); } $dom = $this->loadDom( $html ); $xpath = new DOMXPath( $dom ); $issues = array(); foreach ( $this->rules as $rule ) { foreach ( $rule->evaluate( $dom, $xpath, $context ) as $issue ) { $issues[] = $issue; } } return $issues; } /** * Parses an HTML string into a DOMDocument, suppressing libxml warnings. * * @param string $html Raw HTML fragment. * @return DOMDocument */ private function loadDom( string $html ): DOMDocument { $dom = new DOMDocument(); // Force UTF-8 interpretation via an XML prolog. For a full document // (contains ) we load it as-is so is preserved; a // fragment is wrapped in a known root. $is_document = 1 === preg_match( '/]/i', $html ); $markup = $is_document ? '' . $html : '
' . $html . '
'; // Suppress libxml's noisy warnings about HTML5 tags it doesn't recognise. $previous = libxml_use_internal_errors( true ); $dom->loadHTML( $markup, LIBXML_NOERROR | LIBXML_NOWARNING ); libxml_clear_errors(); libxml_use_internal_errors( $previous ); return $dom; } }