> Per-request cache. */ private static $cache = array(); /** Forget cached values (used after saves and in tests). */ public static function flush( $post_id = 0 ) { if ( $post_id ) { unset( self::$cache[ (int) $post_id ] ); } else { self::$cache = array(); } DescriptionResolver::flush( $post_id ); } /** * Resolved SEO data for a post, with the source of each value. * * @param int|\WP_Post $post Post. * @return array Empty array when the post does not exist. */ public static function for_post( $post ) { $post = get_post( $post ); if ( ! $post instanceof \WP_Post ) { return array(); } if ( isset( self::$cache[ $post->ID ] ) ) { return self::$cache[ $post->ID ]; } $id = (int) $post->ID; $meta = new MetaGenerator(); $title_override = (string) PostMeta::get( $id, PostMeta::TITLE, '' ); $canon_override = (string) PostMeta::get( $id, PostMeta::CANONICAL, '' ); $ogt_override = (string) PostMeta::get( $id, PostMeta::OG_TITLE, '' ); $ogd_override = (string) PostMeta::get( $id, PostMeta::OG_DESCRIPTION, '' ); $robots_set = (string) PostMeta::get( $id, PostMeta::ROBOTS, '' ); $title = $meta->title_for_post( $id ); // One shared resolver decides the description and reports its source (override, excerpt, content, builder, // page_type or global), so this panel always matches what the prints. $resolved = DescriptionResolver::for_post( $post ); $description = $resolved['text']; // Social overrides are templates too: resolve %%variables%% so raw tokens never reach og:/twitter: tags. $og_title = '' !== $ogt_override ? self::clean( $meta->resolve_template( $ogt_override, $id, 'title' ), 255 ) : ''; $og_title = '' !== $og_title ? $og_title : $title; $og_description = '' !== $ogd_override ? self::clean( $meta->resolve_template( $ogd_override, $id, 'description' ), 320 ) : ''; $og_description = '' !== $og_description ? $og_description : $description; $canonical = $meta->canonical_for_post( $id ); $image = self::image_for_post( $id ); $data = array( 'id' => $id, 'title' => $title, 'description' => $description, 'canonical' => $canonical, 'robots' => '' !== $robots_set ? $robots_set : (string) get_option( 'publishrank_seo_default_robots', 'index,follow' ), 'og_title' => $og_title, 'og_description' => $og_description, 'image' => $image, 'sources' => array( 'title' => '' !== $title_override ? 'override' : 'auto', 'description' => $resolved['source'], 'canonical' => '' !== $canon_override && self::same_host( $canon_override ) ? 'override' : 'auto', 'robots' => '' !== $robots_set ? 'override' : 'auto', 'image' => isset( $image['source'] ) ? $image['source'] : 'none', 'image_alt' => isset( $image['alt_source'] ) ? $image['alt_source'] : 'none', ), ); /** * Filter the resolved SEO data for a post. * * @param array $data Resolved data. * @param \WP_Post $post Post. */ $data = (array) apply_filters( 'publishrank_seo_post_data', $data, $post ); self::$cache[ $id ] = $data; return $data; } /** * Automatic meta description: excerpt, else text extracted from the content, else page-builder text. * Returns '' when the page has no usable text of its own (callers decide on a fallback). Never leaks protected content. */ public static function auto_description( $post, $length = self::DESCRIPTION_LENGTH ) { $result = DescriptionResolver::automatic( $post, $length ); return $result['text']; } /** * Automatic social image: featured image, then the attachment itself, then the first * image in the content. Works for every public post type, not only articles. * * @return array|null url,id,width,height,type,alt,alt_source,source. */ public static function image_for_post( $post ) { $post = get_post( $post ); if ( ! $post instanceof \WP_Post ) { return null; } $override = (string) PostMeta::get( $post->ID, PostMeta::OG_IMAGE, '' ); if ( '' !== $override ) { $id = function_exists( 'attachment_url_to_postid' ) ? absint( attachment_url_to_postid( $override ) ) : 0; $info = $id ? self::attachment_info( $id, $post ) : array( 'url' => $override, 'id' => 0, 'width' => 0, 'height' => 0, 'type' => '', 'alt' => '', 'alt_source' => 'none', ); $info['url'] = $override; $info['source'] = 'override'; return self::apply_alt_override( $info, $post ); } $thumb_id = (int) get_post_thumbnail_id( $post ); if ( $thumb_id && wp_attachment_is_image( $thumb_id ) ) { $info = self::attachment_info( $thumb_id, $post ); if ( $info ) { $info['source'] = 'featured'; return self::apply_alt_override( $info, $post ); } } if ( 'attachment' === $post->post_type && wp_attachment_is_image( $post ) ) { $info = self::attachment_info( (int) $post->ID, $post ); if ( $info ) { $info['source'] = 'attachment'; return self::apply_alt_override( $info, $post ); } } if ( ! post_password_required( $post ) && '' !== (string) $post->post_content ) { $content = (string) $post->post_content; if ( preg_match( '/