319 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			319 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * The frontend code of the Schema module.
 | 
						|
 *
 | 
						|
 * @since      1.4.3
 | 
						|
 * @package    RankMath
 | 
						|
 * @subpackage RankMath\Schema
 | 
						|
 * @author     Rank Math <support@rankmath.com>
 | 
						|
 */
 | 
						|
 | 
						|
namespace RankMath\Schema;
 | 
						|
 | 
						|
use RankMath\Helper;
 | 
						|
use RankMath\Traits\Hooker;
 | 
						|
use RankMath\Helpers\Str;
 | 
						|
 | 
						|
defined( 'ABSPATH' ) || exit;
 | 
						|
 | 
						|
/**
 | 
						|
 * Frontend class.
 | 
						|
 */
 | 
						|
class Frontend {
 | 
						|
 | 
						|
	use Hooker;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Hold post object.
 | 
						|
	 *
 | 
						|
	 * @var WP_Post
 | 
						|
	 */
 | 
						|
	public $post = null;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Hold post ID.
 | 
						|
	 *
 | 
						|
	 * @var ID
 | 
						|
	 */
 | 
						|
	public $post_id = 0;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * The Constructor.
 | 
						|
	 */
 | 
						|
	public function __construct() {
 | 
						|
		if ( Helper::is_divi_frontend_editor() ) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		$this->action( 'rank_math/json_ld', 'add_schema', 10, 2 );
 | 
						|
		$this->action( 'rank_math/json_ld', 'connect_schema_entities', 99, 2 );
 | 
						|
		$this->filter( 'rank_math/snippet/rich_snippet_event_entity', 'validate_event_schema', 11, 2 );
 | 
						|
		$this->filter( 'rank_math/snippet/rich_snippet_article_entity', 'add_name_property', 11, 2 );
 | 
						|
		$this->action( 'rank_math/schema/validated_data', 'remove_person_entity', 999 );
 | 
						|
 | 
						|
		new Opengraph();
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Output schema data for a post.
 | 
						|
	 *
 | 
						|
	 * @param array  $data   Array of json-ld data.
 | 
						|
	 * @param JsonLD $jsonld Instance of jsonld.
 | 
						|
	 *
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	public function add_schema( $data, $jsonld ) {
 | 
						|
		if ( ! is_singular() || post_password_required() ) {
 | 
						|
			return $data;
 | 
						|
		}
 | 
						|
 | 
						|
		global $post;
 | 
						|
		$schemas = array_filter(
 | 
						|
			DB::get_schemas( $post->ID ),
 | 
						|
			function( $schema ) {
 | 
						|
				return ! in_array( $schema['@type'], [ 'WooCommerceProduct', 'EDDProduct' ], true );
 | 
						|
			}
 | 
						|
		);
 | 
						|
 | 
						|
		// Check & Unpublish the JobPosting post.
 | 
						|
		DB::unpublish_jobposting_post( $jsonld, $schemas );
 | 
						|
 | 
						|
		$schemas = $jsonld->replace_variables( $schemas, [], $data );
 | 
						|
		$schemas = $jsonld->filter( $schemas, $jsonld, $data );
 | 
						|
 | 
						|
		return array_merge( $data, $schemas );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Connect different schema entities using isPartOf & publisher properties.
 | 
						|
	 *
 | 
						|
	 * @param array  $schemas Array of json-ld data.
 | 
						|
	 * @param JsonLD $jsonld  Instance of jsonld.
 | 
						|
	 *
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	public function connect_schema_entities( $schemas, $jsonld ) {
 | 
						|
		if ( empty( $schemas ) ) {
 | 
						|
			return $schemas;
 | 
						|
		}
 | 
						|
 | 
						|
		$jsonld->parts['canonical'] = ! empty( $jsonld->parts['canonical'] ) ? $jsonld->parts['canonical'] : \RankMath\Paper\Paper::get()->get_canonical();
 | 
						|
 | 
						|
		$schema_types = [];
 | 
						|
		foreach ( $schemas as $id => $schema ) {
 | 
						|
			if ( ! Str::starts_with( 'schema-', $id ) && 'richSnippet' !== $id ) {
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			$schema_types[] = $schema['@type'];
 | 
						|
			$this->connect_properties( $schema, $id, $jsonld, $schemas );
 | 
						|
			$this->add_main_entity_of_page( $schema, $jsonld );
 | 
						|
			$schemas[ $id ] = $schema;
 | 
						|
		}
 | 
						|
 | 
						|
		return $this->change_webpage_entity( $schemas, $schema_types );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Add name property to the Article schema.
 | 
						|
	 *
 | 
						|
	 * @since 1.0.61
 | 
						|
	 *
 | 
						|
	 * @param  array $schema Snippet Data.
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	public function add_name_property( $schema ) {
 | 
						|
		if ( empty( $schema['headline'] ) ) {
 | 
						|
			return $schema;
 | 
						|
		}
 | 
						|
 | 
						|
		$schema['name'] = $schema['headline'];
 | 
						|
		if ( ! isset( $schema['articleSection'] ) ) {
 | 
						|
			global $post;
 | 
						|
			$schema['articleSection'] = Helper::replace_vars( '%primary_taxonomy_terms%', $post );
 | 
						|
		}
 | 
						|
 | 
						|
		return $schema;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Add timezone to startDate field.
 | 
						|
	 *
 | 
						|
	 * @param array $schema Event schema Data.
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	public function validate_event_schema( $schema ) {
 | 
						|
		if ( ! empty( $schema['startDate'] ) ) {
 | 
						|
			$start_date          = date_i18n( 'Y-m-d H:i:sP', strtotime( $schema['startDate'] ) );
 | 
						|
			$schema['startDate'] = str_replace( ' ', 'T', $start_date );
 | 
						|
		}
 | 
						|
 | 
						|
		if ( ! empty( $schema['endDate'] ) ) {
 | 
						|
			$end_date          = date_i18n( 'Y-m-d H:i:sP', strtotime( $schema['endDate'] ) );
 | 
						|
			$schema['endDate'] = str_replace( ' ', 'T', $end_date );
 | 
						|
		}
 | 
						|
 | 
						|
		return $schema;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Connect schema properties.
 | 
						|
	 *
 | 
						|
	 * @param array  $schema  Schema Entity.
 | 
						|
	 * @param string $id      Schema Entity ID.
 | 
						|
	 * @param JsonLD $jsonld  JsonLD Instance.
 | 
						|
	 * @param array  $schemas Array of json-ld data.
 | 
						|
	 */
 | 
						|
	private function connect_properties( &$schema, $id, $jsonld, $schemas ) {
 | 
						|
		if ( isset( $schema['isCustom'] ) ) {
 | 
						|
			unset( $schema['isCustom'] );
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		// Remove empty ImageObject.
 | 
						|
		if ( isset( $schema['image'] ) && empty( $schema['image']['url'] ) && ! is_array( $schema['image'] ) ) {
 | 
						|
			unset( $schema['image'] );
 | 
						|
		}
 | 
						|
 | 
						|
		$jsonld->parts['canonical'] = ! empty( $jsonld->parts['canonical'] ) ? $jsonld->parts['canonical'] : \RankMath\Paper\Paper::get()->get_canonical();
 | 
						|
		$schema['@id']              = $jsonld->parts['canonical'] . '#' . $id;
 | 
						|
 | 
						|
		$types = array_map( 'strtolower', (array) $schema['@type'] );
 | 
						|
		foreach ( $types as $type ) {
 | 
						|
			$is_event = Str::contains( 'event', $type );
 | 
						|
			// Add publisher entity @id in the organizer property of Event schema.
 | 
						|
			if ( $is_event ) {
 | 
						|
				$jsonld->add_prop( 'publisher', $schema, 'organizer', $schemas );
 | 
						|
			}
 | 
						|
 | 
						|
			$props = [
 | 
						|
				'is_part_of' => [
 | 
						|
					'key'   => 'webpage',
 | 
						|
					'value' => ! in_array( $type, [ 'jobposting', 'musicgroup', 'person', 'product', 'productgroup', 'restaurant', 'service' ], true ) && ! $is_event,
 | 
						|
				],
 | 
						|
				'publisher'  => [
 | 
						|
					'key'   => 'publisher',
 | 
						|
					'value' => ! in_array( $type, [ 'jobposting', 'musicgroup', 'person', 'product', 'productgroup', 'restaurant', 'service' ], true ) && ! $is_event,
 | 
						|
				],
 | 
						|
				'thumbnail'  => [
 | 
						|
					'key'   => 'image',
 | 
						|
					'value' => ! in_array( $type, [ 'videoobject' ], true ) || isset( $schema['image'] ),
 | 
						|
				],
 | 
						|
				'language'   => [
 | 
						|
					'key'   => 'inLanguage',
 | 
						|
					'value' => ! in_array( $type, [ 'person', 'service', 'restaurant', 'product', 'productgroup', 'musicgroup', 'musicalbum', 'jobposting' ], true ),
 | 
						|
				],
 | 
						|
			];
 | 
						|
 | 
						|
			if ( isset( $schema['image'] ) && 'product' === $type && is_array( $schema['image'] ) ) {
 | 
						|
				$props['thumbnail']['value'] = false;
 | 
						|
			}
 | 
						|
 | 
						|
			foreach ( $props as $prop => $data ) {
 | 
						|
				if ( ! $data['value'] ) {
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
 | 
						|
				$jsonld->add_prop( $prop, $schema, $data['key'], $schemas );
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Add mainEntityOfPage property to Primary schema entity.
 | 
						|
	 *
 | 
						|
	 * @param array  $schema Schema Entity.
 | 
						|
	 * @param JsonLD $jsonld JsonLD Instance.
 | 
						|
	 */
 | 
						|
	private function add_main_entity_of_page( &$schema, $jsonld ) {
 | 
						|
		if ( ! isset( $schema['isPrimary'] ) ) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( ! empty( $schema['isPrimary'] ) ) {
 | 
						|
			$schema['mainEntityOfPage'] = [ '@id' => $jsonld->parts['canonical'] . '#webpage' ];
 | 
						|
		}
 | 
						|
 | 
						|
		unset( $schema['isPrimary'] );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Remove Person entity if it is not referenced in any other entities.
 | 
						|
	 *
 | 
						|
	 * @param array $data Array of json-ld data.
 | 
						|
	 *
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	public function remove_person_entity( $data ) {
 | 
						|
		if ( empty( $data['ProfilePage'] ) || empty( $data['ProfilePage']['@id'] ) || ! is_singular() ) {
 | 
						|
			return $data;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( function_exists( 'bp_is_user' ) && bp_is_user() ) {
 | 
						|
			return $data;
 | 
						|
		}
 | 
						|
 | 
						|
		$temp_data = $data;
 | 
						|
		$id        = $temp_data['ProfilePage']['@id'];
 | 
						|
		$ids       = [];
 | 
						|
 | 
						|
		unset( $temp_data['ProfilePage'] );
 | 
						|
		array_walk_recursive(
 | 
						|
			$temp_data,
 | 
						|
			function( $value, $key ) use ( &$ids, $id ) {
 | 
						|
				if ( '@id' === $key && $value === $id ) {
 | 
						|
					$ids[] = $value;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		);
 | 
						|
 | 
						|
		if ( empty( $ids ) ) {
 | 
						|
			unset( $data['ProfilePage'] );
 | 
						|
		}
 | 
						|
 | 
						|
		return $data;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Change WebPage entity type depending on the schemas on the page.
 | 
						|
	 *
 | 
						|
	 * @param array $schemas Schema data.
 | 
						|
	 * @param array $types   Schema types.
 | 
						|
	 *
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	private function change_webpage_entity( $schemas, $types ) {
 | 
						|
		if ( in_array( 'Product', $types, true ) ) {
 | 
						|
			$schemas['WebPage']['@type'] = 'ItemPage';
 | 
						|
		}
 | 
						|
 | 
						|
		if ( isset( $schemas['howto'] ) && ! empty( $schemas['WebPage'] ) ) {
 | 
						|
			$schemas['howto']['mainEntityOfPage'] = [ '@id' => $schemas['WebPage']['@id'] ];
 | 
						|
		}
 | 
						|
 | 
						|
		$faq_data = array_map(
 | 
						|
			function( $schema ) {
 | 
						|
				return isset( $schema['@type'] ) && 'FAQPage' === $schema['@type'];
 | 
						|
			},
 | 
						|
			$schemas
 | 
						|
		);
 | 
						|
 | 
						|
		$faq_key = is_array( $faq_data ) && ! empty( $faq_data ) ? key( array_filter( $faq_data ) ) : '';
 | 
						|
		if ( ! $faq_key ) {
 | 
						|
			return $schemas;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( in_array( $faq_key, array_keys( $schemas ), true ) ) {
 | 
						|
			$schemas['WebPage']['@type'] =
 | 
						|
				! empty( $types )
 | 
						|
				? array_merge( (array) $schemas['WebPage']['@type'], [ 'FAQPage' ] )
 | 
						|
				: 'FAQPage';
 | 
						|
 | 
						|
			$schemas['WebPage']['mainEntity'] = $schemas[ $faq_key ]['mainEntity'];
 | 
						|
 | 
						|
			unset( $schemas[ $faq_key ] );
 | 
						|
		}
 | 
						|
 | 
						|
		return $schemas;
 | 
						|
	}
 | 
						|
}
 |