$objectId, 'type' => 'post', 'subtype' => get_post_type( $objectId ), ], $parser); } /** * Analyze the page for Advanced SEO issues. * * @since 4.8.6 * * @return array List of checkers/issues. */ public function get() { $result = [ 'noindex' => $this->analyzeNoIndex(), 'openGraph' => $this->analyzeOpenGraph(), 'schema' => $this->analyzeSchema(), 'canonicalTag' => $this->analyzeCanonicalTag(), 'authorBio' => $this->analyzeAuthorHasBio(), // only if EEAT is active and page supports author 'mainKeyword' => $this->analyzeHasMainKeyword(), // only if is not a woocommerce page, not the posts page and is TruSEO eligible 'staleContent' => $this->analyzeStaleContent(), ]; return array_filter( $result, function( $check ) { return ! is_null( $check ); } ); } /** * Get the robots rules from the page. * * @since 4.8.6 * * @return array The robots rules eg('noindex', 'nofollow') */ private function getRobotsRules() { $rules = $this->pageParser->getMetaTag( 'robots' ); if ( empty( $rules ) ) { return []; } $result = explode( ',', $rules[0] ); $result = array_map( function( $item ) { return trim( $item ); }, $result ); return $result; } /** * Analyze the page for noindex meta tag. * * @since 4.8.6 * * @return CheckerResult */ private function analyzeNoIndex() { if ( in_array( 'noindex', $this->getRobotsRules(), true ) ) { return new CheckerResult( 'noindex' ); } return new CheckerResult( 'noindex-ok' ); } /** * Analyze the page for Open Graph issues. * * @since 4.8.6 * * @return CheckerResult */ private function analyzeOpenGraph() { $openGraphTags = $this->pageParser->getOpenGraphTags(); $required = [ 'og:title' => 0, 'og:type' => 0, 'og:image' => 0, 'og:url' => 0, 'og:description' => 0 ]; if ( ! $this->pageParser->isScraping ) { $post = Post::getPost( $this->object['id'] ); $facebook = [ 'title' => aioseo()->tags->replaceTags( aioseo()->helpers->encodeOutputHtml( aioseo()->social->facebook->getTitle( $this->object['id'] ) ), $this->object['id'] ), 'description' => aioseo()->tags->replaceTags( aioseo()->helpers->encodeOutputHtml( aioseo()->social->facebook->getDescription( $this->object['id'] ) ), $this->object['id'] ), 'image' => aioseo()->social->facebook->getImage( $this->object['id'] ), ]; $twitter = [ 'title' => aioseo()->tags->replaceTags( aioseo()->helpers->encodeOutputHtml( aioseo()->social->twitter->getTitle( $this->object['id'] ) ), $this->object['id'] ), 'description' => aioseo()->tags->replaceTags( aioseo()->helpers->encodeOutputHtml( aioseo()->social->twitter->getDescription( $this->object['id'] ) ), $this->object['id'] ), 'image' => aioseo()->social->twitter->getImage( $this->object['id'] ), ]; $required = [ 'og:title' => 0, 'og:description' => 0, 'og:image' => 0, ]; $openGraphTags = [ [ 'key' => 'og:title', 'value' => $facebook['title'] ?? $twitter['title'] ?? $post->og_title ], [ 'key' => 'og:description', 'value' => $facebook['description'] ?? $twitter['description'] ?? $post->og_description ], [ 'key' => 'og:image', 'value' => $facebook['image'] ?? $twitter['image'] ?? $post->og_image_url ] ]; } foreach ( $openGraphTags as $tag ) { if ( isset( $required[ $tag['key'] ] ) && ! empty( $tag['value'] ) ) { $required[ $tag['key'] ] += 1; } } $missingTags = array_filter( $required, function( $item ) { return 0 === $item; } ); if ( ! empty( $missingTags ) ) { return new CheckerResult( 'ogp-missing', [ 'items' => array_keys( $missingTags ) ] ); } $duplicatedTags = array_filter( $required, function( $item ) { return 1 < $item; } ); if ( ! empty( $duplicatedTags ) ) { return new CheckerResult( 'ogp-duplicates', [ 'items' => array_keys( $duplicatedTags ) ] ); } return new CheckerResult( 'ogp-ok' ); } /** * Analyze the page for Schema issues. * * @since 4.8.6 * * @return CheckerResult */ private function analyzeSchema() { if ( $this->pageParser->isScraping && empty( $this->pageParser->getSchemas() ) ) { return new CheckerResult( 'schema-missing' ); } return new CheckerResult( 'schema-ok' ); } /** * Analyze the page for Canonical Tag issues. * * @since 4.8.6 * * @return CheckerResult */ private function analyzeCanonicalTag() { $canonicalUrl = $this->pageParser->getCanonicalUrl(); if ( ! $this->pageParser->isScraping ) { $post = Post::getPost( $this->object['id'] ); $canonicalUrl = $post->canonical_url; } if ( empty( $canonicalUrl ) ) { return new CheckerResult( 'canonical-missing' ); } return new CheckerResult( 'canonical-ok' ); } /** * Analyze the page for Author Bio. * * @since 4.8.6 * * @return CheckerResult|null */ private function analyzeAuthorHasBio() { $object = $this->object; $authorId = get_post_field( 'post_author', $object['id'] ); $authorMetaData = function_exists( 'aioseoEeat' ) ? aioseoEeat()->helpers->getAuthorMetaData( $authorId ) : []; $isSingular = 'page' !== $object['subtype']; $supportsAuthor = post_type_supports( $object['subtype'], 'author' ); if ( ! $isSingular || ! $supportsAuthor || empty( $authorMetaData ) || ( isset( $authorMetaData['enabled'] ) && ! $authorMetaData['enabled'] ) ) { return null; } if ( empty( $authorMetaData['authorBio'] ) ) { return new CheckerResult( 'author-bio-missing' ); } return new CheckerResult( 'author-bio-ok' ); } /** * Analyze the page for Main Keyword. * * @since 4.8.6 * * @return CheckerResult|null */ private function analyzeHasMainKeyword() { if ( aioseo()->helpers->isWooCommercePage( $this->object['id'] ) || aioseo()->helpers->isStaticPostsPage( $this->object['id'] ) || ! aioseo()->helpers->isTruSeoEligible( $this->object['id'] ) ) { return null; } $object = $this->object; $post = Post::getPost( $object['id'] ); $keyphrases = Post::getKeyphrasesDefaults( $post->keyphrases ); if ( empty( $keyphrases->focus->keyphrase ) ) { return new CheckerResult( 'main-keyword-missing' ); } return new CheckerResult( 'main-keyword-ok' ); } /** * Analyze the page for Stale Content. * * @since 4.8.6 * * @return CheckerResult */ private function analyzeStaleContent() { $post = get_post( $this->object['id'] ); $currentTime = current_time( 'mysql' ); $modifiedDate = new \DateTime( $post->post_modified ); $currentDate = new \DateTime( $currentTime ); // Calculate the difference $interval = $currentDate->diff( $modifiedDate ); if ( 2 <= $interval->y ) { return new CheckerResult( 'stale-content-too-old' ); } return new CheckerResult( 'stale-content-ok' ); } }