Extracted data array with 'title', 'content', 'author', 'journal'. */ public static function extract( string $html = '', string $url = '' ): array { if ( empty( $html ) && ! empty( $url ) ) { $response = wp_remote_get( $url, array( 'timeout' => 15, 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', ) ); if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { return array( 'title' => '', 'content' => '', 'author' => '', 'journal' => '', ); } $html = (string) wp_remote_retrieve_body( $response ); } if ( empty( $html ) ) { return array( 'title' => '', 'content' => '', 'author' => '', 'journal' => '', ); } // Enforce 500 KB size cap safely for multi-byte UTF-8 characters. if ( strlen( $html ) > self::MAX_HTML_SIZE ) { $html = function_exists( 'mb_strcut' ) ? mb_strcut( $html, 0, self::MAX_HTML_SIZE, 'UTF-8' ) : substr( $html, 0, self::MAX_HTML_SIZE ); } // Strip scripts, styles, svgs, and base64 data URIs before DOM parsing. $html = preg_replace( '/<(script|style|svg|noscript|iframe)[^>]*>.*?<\/\1>/is', '', $html ); $html = preg_replace( '/data:image\/[a-z0-9+-]+;base64,[a-z0-9+\/=_]+/i', '', (string) $html ); // Enforce UTF-8 pre-encoding normalization without deprecated mb_convert_encoding. $html_encoded = '' . (string) $html; $libxml_prev = libxml_use_internal_errors( true ); $dom = new DOMDocument(); $dom->loadHTML( $html_encoded, LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NONET ); libxml_clear_errors(); libxml_use_internal_errors( $libxml_prev ); $xpath = new DOMXPath( $dom ); // 1. Try JSON-LD Schema.org Article / ScholarlyArticle $jsonld_data = self::extract_from_jsonld( $xpath ); if ( ! empty( $jsonld_data['content'] ) && strlen( $jsonld_data['content'] ) > 100 ) { return $jsonld_data; } // 2. Try Highwire Press / Citation Meta Tags $highwire_data = self::extract_from_highwire( $xpath ); if ( ! empty( $highwire_data['content'] ) && strlen( $highwire_data['content'] ) > 100 ) { return $highwire_data; } // 3. Fallback: DOMXPath Paragraph Density Scoring return self::extract_from_paragraphs( $xpath, $jsonld_data, $highwire_data ); } /** * Extract data from JSON-LD `