🏠 Root
/
home
/
artorgp
/
www
/
wp-content
/
plugins
/
wordpress-seo
/
src
/
schema-aggregator
/
application
/
enhancement
/
Editing: article-schema-enhancer.php
<?php // phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure. namespace Yoast\WP\SEO\Schema_Aggregator\Application\Enhancement; use Exception; use Yoast\WP\SEO\Models\Indexable; use Yoast\WP\SEO\Schema_Aggregator\Domain\Enhancement\Schema_Enhancement_Interface; use Yoast\WP\SEO\Schema_Aggregator\Domain\Schema_Piece; use Yoast\WP\SEO\Schema_Aggregator\Infrastructure\Enhancement\Article_Config; /** * The Article schema pieces enhancer. */ class Article_Schema_Enhancer extends Abstract_Schema_Enhancer implements Schema_Enhancement_Interface { /** * The config. * * @var Article_Config */ private $config; /** * Sets the Article_Config instance. * * @required * * @param Article_Config $config The Article_Config instance. * * @return void */ public function set_article_config( Article_Config $config ) { $this->config = $config; } /** * Enhances specific Article schema pieces. * * @param Schema_Piece $schema_piece The schema piece to enhance. * @param Indexable $indexable The indexable object that is the source of the schema piece. * * @return Schema_Piece The enhanced schema piece. */ public function enhance( Schema_Piece $schema_piece, Indexable $indexable ): Schema_Piece { $schema_data = $schema_piece->get_data(); if ( ! isset( $schema_data['@type'] ) ) { return $schema_piece; } if ( \in_array( $schema_data['@type'], [ 'Article', 'NewsArticle', 'BlogPosting', ], true, ) ) { $schema_data = $this->enhance_schema_piece( $schema_data, $indexable ); } if ( \is_array( $schema_data['@type'] ) && \in_array( 'Article', $schema_data['@type'], true ) ) { $schema_data = $this->enhance_schema_piece( $schema_data, $indexable ); } return new Schema_Piece( $schema_data, $schema_piece->get_type() ); } /** * Enhance a single schema piece * * @param array<string> $schema_data The schema data to enhance. * @param Indexable $indexable The indexable object that is the source of the schema piece. * * @return array<string> The enhanced schema data. */ protected function enhance_schema_piece( array $schema_data, Indexable $indexable ): array { try { $has_excerpt = false; if ( $this->config->is_enhancement_enabled( 'use_excerpt' ) ) { $excerpt = $this->get_excerpt( $indexable->object_id ); $has_excerpt = ! empty( $excerpt ); if ( $has_excerpt && ! isset( $schema_data['description'] ) ) { $schema_data['description'] = $excerpt; } } if ( $this->config->is_enhancement_enabled( 'article_body' ) && ! isset( $schema_data['articleBody'] ) ) { if ( $this->config->should_include_article_body( $has_excerpt ) ) { $article_body = $this->get_article_body( $indexable->object_id ); if ( ! empty( $article_body ) ) { $schema_data['articleBody'] = $article_body; } } } if ( $this->config->is_enhancement_enabled( 'keywords' ) ) { $keywords = $this->get_article_keywords( $indexable->object_id ); if ( ! empty( $keywords ) ) { $existing = (array) ( $schema_data['keywords'] ?? [] ); $schema_data['keywords'] = \implode( ', ', \array_unique( \array_merge( $existing, $keywords ) ) ); } } return $schema_data; } catch ( Exception $e ) { return $schema_data; } } /** * Get article keywords * * Extracts post tags and optionally categories as keywords. * * @param int $post_id Post ID. * * @return array<string> Array of keyword strings. */ private function get_article_keywords( int $post_id ): array { try { $keywords = []; if ( $this->config->get_config_value( 'categories_as_keywords', false ) ) { $categories = \get_the_category( $post_id ); if ( \is_array( $categories ) && ! empty( $categories ) ) { foreach ( $categories as $category ) { if ( isset( $category->name ) && $category->name !== 'Uncategorized' ) { $keywords[] = $category->name; } } } } return \array_unique( $keywords ); } catch ( Exception $e ) { return []; } } /** * Get article excerpt for description field * * Retrieves post excerpt with robust validation (no empty/whitespace-only). * Falls back to auto-generated excerpt from content unless prefer_manual is enabled. * * @param int $post_id Post ID. * * @return string|null Excerpt or null if unavailable/invalid. */ private function get_excerpt( int $post_id ): ?string { try { $excerpt = \get_post_field( 'post_excerpt', $post_id ); if ( \is_wp_error( $excerpt ) ) { $excerpt = ''; } if ( empty( $excerpt ) || \trim( $excerpt ) === '' ) { if ( $this->config->get_config_value( 'excerpt_prefer_manual', false ) ) { return null; } $content = \get_post_field( 'post_content', $post_id ); if ( \is_wp_error( $content ) || empty( $content ) ) { return null; } $excerpt = \wp_trim_excerpt( $content, $post_id ); if ( empty( $excerpt ) || \trim( $excerpt ) === '' ) { return null; } } $excerpt = \wp_strip_all_tags( $excerpt ); // Apply max length if configured. $max_length = $this->config->get_config_value( 'excerpt_max_length', 0 ); $excerpt = $this->trim_content_to_max_length( $max_length, $excerpt ); $excerpt = \trim( $excerpt ); return ( $excerpt !== '' ) ? $excerpt : null; } catch ( Exception $e ) { return null; } } /** * Get article body (full post content) * * Extracts full post content with optional HTML and shortcode stripping. * Respects max_length configuration if set. * * @param int $post_id Post ID. * * @return string|null Article body or null if unavailable. */ private function get_article_body( int $post_id ): ?string { try { $content = \get_post_field( 'post_content', $post_id ); if ( \is_wp_error( $content ) || empty( $content ) ) { return null; } if ( $this->config->get_config_value( 'strip_shortcodes_from_body', true ) ) { $content = \strip_shortcodes( $content ); } if ( $this->config->get_config_value( 'strip_html_from_body', true ) ) { $content = \wp_strip_all_tags( $content ); } $max_length = $this->config->get_config_value( 'article_body_max_length', Article_Config::DEFAULT_MAX_ARTICLE_BODY_LENGTH ); return $this->trim_content_to_max_length( $max_length, $content ); } catch ( Exception $e ) { return null; } } }
Save
Cancel