plain text: no shortcodes, scripts, styles or tags; entities decoded; whitespace collapsed. */ function sxseo_plain( $html ) { $html = (string) $html; $html = strip_shortcodes( $html ); $html = preg_replace( '#<(script|style|noscript|svg)[^>]*>.*?#is', ' ', $html ); $html = preg_replace( '##s', ' ', $html ); $html = preg_replace( '##i', ' $0', $html ); $text = wp_strip_all_tags( $html ); $text = html_entity_decode( $text, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); return trim( preg_replace( '/\s+/u', ' ', $text ) ); } function sxseo_len( $s ) { return function_exists( 'mb_strlen' ) ? mb_strlen( (string) $s, 'UTF-8' ) : strlen( (string) $s ); } function sxseo_sub( $s, $start, $len = null ) { if ( function_exists( 'mb_substr' ) ) { return mb_substr( (string) $s, $start, $len, 'UTF-8' ); } return null === $len ? substr( (string) $s, $start ) : substr( (string) $s, $start, $len ); } function sxseo_lower( $s ) { return function_exists( 'mb_strtolower' ) ? mb_strtolower( (string) $s, 'UTF-8' ) : strtolower( (string) $s ); } /** Trim to at most $max characters, preferring a sentence end, then a word boundary. */ function sxseo_trim( $text, $max = 155 ) { $text = trim( (string) $text ); if ( sxseo_len( $text ) <= $max ) { return $text; } $cut = sxseo_sub( $text, 0, $max ); if ( preg_match( '/^(.{' . (int) floor( $max * 0.72 ) . ',})[.!?](\s|$)/us', $cut, $m ) ) { return trim( $m[1] ) . sxseo_sub( $cut, sxseo_len( $m[1] ), 1 ); } $space = strrpos( $cut, ' ' ); if ( false !== $space && $space > $max * 0.5 ) { $cut = substr( $cut, 0, $space ); } return rtrim( $cut, " ,;:-–—(" ) . '…'; } function sxseo_words( $text ) { $parts = preg_split( '/[^\p{L}\p{N}\']+/u', sxseo_lower( $text ), -1, PREG_SPLIT_NO_EMPTY ); return $parts ? $parts : array(); } function sxseo_stopwords() { static $s = null; if ( null === $s ) { $s = array_flip( explode( ',', 'a,about,above,after,again,all,also,am,an,and,any,are,as,at,be,because,been,before,being,below,between,both,but,by,can,could,did,do,does,doing,down,during,each,few,for,from,further,had,has,have,having,he,her,here,hers,him,his,how,i,if,in,into,is,it,its,just,me,more,most,my,no,nor,not,now,of,off,on,once,only,or,other,our,ours,out,over,own,same,she,should,so,some,such,than,that,the,their,theirs,them,then,there,these,they,this,those,through,to,too,under,until,up,very,was,we,were,what,when,where,which,while,who,whom,why,will,with,would,you,your,yours,s,t,ll,re,ve,d,m,get,got,one,two,new,use,using,used,make,may,many,much,like,even,well,way,us' ) ); } return $s; } function sxseo_home_host() { return wp_parse_url( home_url(), PHP_URL_HOST ); } /** Site-relative request path, lowercased, no trailing slash (home = ''). */ function sxseo_request_path() { $uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; $path = (string) wp_parse_url( $uri, PHP_URL_PATH ); $base = (string) wp_parse_url( home_url(), PHP_URL_PATH ); if ( '' !== $base && 0 === strpos( $path, $base ) ) { $path = substr( $path, strlen( $base ) ); } return sxseo_norm_path( $path ); } function sxseo_norm_path( $path ) { $path = rawurldecode( (string) $path ); $path = '/' . trim( sxseo_lower( $path ), '/' ); return '/' === $path ? '' : $path; } function sxseo_x( $s ) { return htmlspecialchars( (string) $s, ENT_XML1 | ENT_QUOTES, 'UTF-8' ); } function sxseo_w3c( $mysql_gmt ) { return $mysql_gmt ? gmdate( 'c', strtotime( $mysql_gmt . ' UTC' ) ) : ''; } /** Public post types the plugin optimises (everything public except attachments). */ function sxseo_post_types() { $types = get_post_types( array( 'public' => true ), 'names' ); unset( $types['attachment'] ); $off = (array) sxseo_opt( 'sitemap_exclude_types', array() ); return array_values( array_diff( array_values( $types ), $off ) ); } function sxseo_taxonomies() { $tax = get_taxonomies( array( 'public' => true ), 'names' ); unset( $tax['post_format'] ); return array_values( $tax ); } function sxseo_json( $data ) { return wp_json_encode( $data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP ); } /** Post title as plain text: no tags, entities decoded (get_the_title returns "&"). */ function sxseo_title_text( $post ) { return wp_specialchars_decode( wp_strip_all_tags( get_the_title( $post ) ), ENT_QUOTES ); } /** * Whether a taxonomy archive is worth indexing. An explicit per-term noindex always wins; otherwise empty archives * and tag archives with fewer than 3 posts are thin duplicate content and stay out of search results and the sitemap. */ function sxseo_term_indexable( $term ) { if ( '1' === (string) get_term_meta( $term->term_id, 'sxseo_noindex', true ) ) { return false; } $min = 'post_tag' === $term->taxonomy ? 3 : 1; return (int) $term->count >= (int) apply_filters( 'sxseo_min_term_posts', $min, $term ); }