loadXML( $xml, LIBXML_NONET | LIBXML_NOWARNING ); $problems = libxml_get_errors(); libxml_clear_errors(); if ( isset( $loader ) ) { libxml_disable_entity_loader( $loader ); } libxml_use_internal_errors( $previous ); foreach ( $problems as $problem ) { if ( $problem->level >= LIBXML_ERR_ERROR ) { return array( null, array( __( 'The XML is not well-formed. Check element and attribute names, namespaces and attributes.', 'feedweaver' ) ) ); } } return $loaded ? array( $dom, array() ) : array( null, array( __( 'The XML is not well-formed.', 'feedweaver' ) ) ); } /** * Checks that a document is well-formed XML. * * @param string $xml XML. * @return string[] Errors; empty when valid. */ public static function validate_xml( string $xml ): array { return self::parse( $xml )[1]; } /** * Checks that a document is a valid RSS 2.0 feed: structure, required channel and item * elements, dates, links, guids and enclosures. * * @param string $xml XML. * @return string[] Errors; empty when valid. */ public static function validate_rss( string $xml ): array { list( $dom, $errors ) = self::parse( $xml ); if ( ! $dom ) { return $errors; } $xpath = new \DOMXPath( $dom ); $root = $dom->documentElement; if ( ! $root || 'rss' !== $root->localName || '2.0' !== $root->getAttribute( 'version' ) ) { return array( __( 'The document root must be .', 'feedweaver' ) ); } $channels = $xpath->query( '/rss/channel' ); if ( 1 !== $channels->length ) { return array( __( 'An RSS feed needs exactly one channel.', 'feedweaver' ) ); } foreach ( array( 'title', 'link', 'description' ) as $required ) { if ( '' === trim( (string) $xpath->evaluate( 'string(/rss/channel/' . $required . ')' ) ) ) { /* translators: %s: element name. */ $errors[] = sprintf( __( 'The RSS channel needs a non-empty <%s>.', 'feedweaver' ), $required ); } } if ( ! Rss_Profile::is_http_url( (string) $xpath->evaluate( 'string(/rss/channel/link)' ) ) ) { $errors[] = __( 'The RSS channel link must be an http(s) address.', 'feedweaver' ); } foreach ( $xpath->query( '/rss/channel/item' ) as $i => $item ) { $n = $i + 1; if ( '' === trim( (string) $xpath->evaluate( 'string(title)', $item ) ) && '' === trim( (string) $xpath->evaluate( 'string(description)', $item ) ) ) { /* translators: %d: item number. */ $errors[] = sprintf( __( 'RSS item %d has neither a title nor a description.', 'feedweaver' ), $n ); } foreach ( $xpath->query( 'link', $item ) as $link ) { if ( ! Rss_Profile::is_http_url( trim( $link->textContent ) ) ) { /* translators: %d: item number. */ $errors[] = sprintf( __( 'RSS item %d has a link that is not an http(s) address.', 'feedweaver' ), $n ); } } foreach ( $xpath->query( 'pubDate', $item ) as $date ) { if ( ! Rss_Profile::is_rss_date( trim( $date->textContent ) ) ) { /* translators: %d: item number. */ $errors[] = sprintf( __( 'RSS item %d has a pubDate that is not an RFC 822 date.', 'feedweaver' ), $n ); } } foreach ( $xpath->query( 'guid', $item ) as $guid ) { $permalink = $guid->getAttribute( 'isPermaLink' ); if ( '' === trim( $guid->textContent ) || ( '' !== $permalink && ! in_array( $permalink, array( 'true', 'false' ), true ) ) ) { /* translators: %d: item number. */ $errors[] = sprintf( __( 'RSS item %d has an empty guid or an isPermaLink that is not true or false.', 'feedweaver' ), $n ); } } foreach ( $xpath->query( 'enclosure', $item ) as $enclosure ) { if ( ! Rss_Profile::is_http_url( $enclosure->getAttribute( 'url' ) ) || 1 !== preg_match( '/^[1-9][0-9]{0,15}$/D', $enclosure->getAttribute( 'length' ) ) || ! Rss_Profile::is_mime_type( $enclosure->getAttribute( 'type' ) ) ) { /* translators: %d: item number. */ $errors[] = sprintf( __( 'RSS item %d has an enclosure without a valid url, length and type.', 'feedweaver' ), $n ); } } }//end foreach return $errors; } }