meta-tags.php

I’ll build this as a production-style inc/seo/meta-tags.php file for your RX theme, with SEO meta, Open Graph, Twitter cards, canonical, robots, schema hooks, author/date/article data, thumbnails, pagination, and safe fallbacks.

Yes friend — create this file:

rx-theme/inc/seo/meta-tags.php

Then paste this full advanced code inside it.

WordPress prints head data through the wp_head hook, so this file attaches your RX SEO meta output there. It uses escaping functions like esc_attr() and esc_url() for safer HTML output, and wp_json_encode() for JSON-LD schema output.

<?php
/**
 * RX Theme Advanced SEO Meta Tags
 *
 * File: inc/seo/meta-tags.php
 *
 * Features:
 * - Basic SEO meta title/description/keywords
 * - Open Graph tags
 * - Twitter Card tags
 * - Canonical URL support
 * - Robots meta control
 * - Article published/modified time
 * - Author meta
 * - Image meta from featured image/site logo/fallback
 * - Pagination meta
 * - JSON-LD schema for WebSite, Organization, Article, Breadcrumb
 * - WooCommerce support if active
 * - Search, archive, taxonomy, author, 404 support
 * - Safe escaping and filter-ready architecture
 *
 * Important:
 * In header.php, make sure you have:
 * <?php wp_head(); ?>
 *
 * In functions.php, require this file:
 * require_once get_template_directory() . '/inc/seo/meta-tags.php';
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( 'RX_Advanced_Meta_Tags' ) ) :

final class RX_Advanced_Meta_Tags {

	/**
	 * Theme prefix.
	 */
	const PREFIX = 'rx';

	/**
	 * Default image fallback.
	 * You can create: /assets/images/rx-default-og.jpg
	 */
	const DEFAULT_OG_IMAGE = '/assets/images/rx-default-og.jpg';

	/**
	 * Meta description length.
	 */
	const DESCRIPTION_LENGTH = 160;

	/**
	 * Constructor.
	 */
	public function __construct() {
		add_action( 'wp_head', array( $this, 'output_meta_tags' ), 1 );
		add_action( 'wp_head', array( $this, 'output_json_ld_schema' ), 30 );

		add_filter( 'document_title_parts', array( $this, 'filter_document_title_parts' ), 20 );
		add_filter( 'wp_robots', array( $this, 'filter_wp_robots' ), 20 );
	}

	/**
	 * Main meta output.
	 */
	public function output_meta_tags() {
		if ( is_admin() || is_feed() || wp_doing_ajax() ) {
			return;
		}

		$context     = $this->get_context();
		$title       = $this->get_meta_title();
		$description = $this->get_meta_description();
		$keywords    = $this->get_meta_keywords();
		$url         = $this->get_current_url();
		$canonical   = $this->get_canonical_url();
		$image       = $this->get_meta_image();
		$image_data  = $this->get_meta_image_data();
		$type        = $this->get_og_type();
		$site_name   = get_bloginfo( 'name' );
		$locale      = $this->get_locale();
		$author      = $this->get_author_name();

		echo "\n<!-- RX Advanced SEO Meta Tags -->\n";

		$this->print_meta_name( 'description', $description );

		if ( ! empty( $keywords ) ) {
			$this->print_meta_name( 'keywords', $keywords );
		}

		$this->print_meta_name( 'author', $author );
		$this->print_meta_name( 'publisher', $site_name );
		$this->print_meta_name( 'application-name', $site_name );
		$this->print_meta_name( 'generator', 'RX Theme SEO System' );
		$this->print_meta_name( 'referrer', 'strict-origin-when-cross-origin' );
		$this->print_meta_name( 'format-detection', 'telephone=no' );
		$this->print_meta_name( 'theme-color', $this->get_theme_color() );

		if ( $canonical ) {
			printf(
				'<link rel="canonical" href="%s" />' . "\n",
				esc_url( $canonical )
			);
		}

		$this->output_prev_next_links();

		/**
		 * Open Graph.
		 */
		$this->print_meta_property( 'og:locale', $locale );
		$this->print_meta_property( 'og:type', $type );
		$this->print_meta_property( 'og:title', $title );
		$this->print_meta_property( 'og:description', $description );
		$this->print_meta_property( 'og:url', $url );
		$this->print_meta_property( 'og:site_name', $site_name );

		if ( $image ) {
			$this->print_meta_property( 'og:image', $image );
			$this->print_meta_property( 'og:image:secure_url', $image );
			$this->print_meta_property( 'og:image:alt', $this->get_image_alt_text() );

			if ( ! empty( $image_data['width'] ) ) {
				$this->print_meta_property( 'og:image:width', (string) absint( $image_data['width'] ) );
			}

			if ( ! empty( $image_data['height'] ) ) {
				$this->print_meta_property( 'og:image:height', (string) absint( $image_data['height'] ) );
			}

			if ( ! empty( $image_data['mime'] ) ) {
				$this->print_meta_property( 'og:image:type', $image_data['mime'] );
			}
		}

		if ( is_singular() ) {
			$post_id = get_queried_object_id();

			$this->print_meta_property( 'article:published_time', get_the_date( DATE_W3C, $post_id ) );
			$this->print_meta_property( 'article:modified_time', get_the_modified_date( DATE_W3C, $post_id ) );
			$this->print_meta_property( 'og:updated_time', get_the_modified_date( DATE_W3C, $post_id ) );
			$this->print_meta_property( 'article:author', $author );

			$categories = get_the_category( $post_id );

			if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) {
				foreach ( $categories as $category ) {
					$this->print_meta_property( 'article:section', $category->name );
				}
			}

			$tags = get_the_tags( $post_id );

			if ( ! empty( $tags ) && ! is_wp_error( $tags ) ) {
				foreach ( $tags as $tag ) {
					$this->print_meta_property( 'article:tag', $tag->name );
				}
			}
		}

		/**
		 * Twitter Card.
		 */
		$this->print_meta_name( 'twitter:card', $image ? 'summary_large_image' : 'summary' );
		$this->print_meta_name( 'twitter:title', $title );
		$this->print_meta_name( 'twitter:description', $description );

		if ( $image ) {
			$this->print_meta_name( 'twitter:image', $image );
			$this->print_meta_name( 'twitter:image:alt', $this->get_image_alt_text() );
		}

		$twitter_site = $this->get_twitter_site();

		if ( $twitter_site ) {
			$this->print_meta_name( 'twitter:site', $twitter_site );
			$this->print_meta_name( 'twitter:creator', $twitter_site );
		}

		/**
		 * Extra mobile/social meta.
		 */
		$this->print_meta_name( 'apple-mobile-web-app-title', $site_name );
		$this->print_meta_name( 'apple-mobile-web-app-capable', 'yes' );
		$this->print_meta_name( 'apple-mobile-web-app-status-bar-style', 'default' );
		$this->print_meta_name( 'mobile-web-app-capable', 'yes' );

		/**
		 * Optional RSS discovery.
		 */
		printf(
			'<link rel="alternate" type="application/rss+xml" title="%s Feed" href="%s" />' . "\n",
			esc_attr( $site_name ),
			esc_url( get_bloginfo( 'rss2_url' ) )
		);

		/**
		 * Optional REST API discovery.
		 */
		printf(
			'<link rel="https://api.w.org/" href="%s" />' . "\n",
			esc_url( rest_url() )
		);

		/**
		 * Custom hook for future RX theme extension.
		 */
		do_action(
			'rx_after_advanced_meta_tags',
			array(
				'context'     => $context,
				'title'       => $title,
				'description' => $description,
				'keywords'    => $keywords,
				'url'         => $url,
				'canonical'   => $canonical,
				'image'       => $image,
				'type'        => $type,
			)
		);

		echo "<!-- /RX Advanced SEO Meta Tags -->\n\n";
	}

	/**
	 * Filter WordPress document title parts.
	 *
	 * WordPress exposes document title parts through document_title_parts.
	 */
	public function filter_document_title_parts( $parts ) {
		if ( is_admin() ) {
			return $parts;
		}

		if ( is_front_page() || is_home() ) {
			$parts['title'] = get_bloginfo( 'name' );

			if ( get_bloginfo( 'description' ) ) {
				$parts['tagline'] = get_bloginfo( 'description' );
			}
		} elseif ( is_singular() ) {
			$parts['title'] = single_post_title( '', false );
		} elseif ( is_category() || is_tag() || is_tax() ) {
			$parts['title'] = single_term_title( '', false );
		} elseif ( is_author() ) {
			$parts['title'] = sprintf(
				/* translators: %s author name */
				__( 'Author: %s', 'rx' ),
				get_the_author_meta( 'display_name', get_queried_object_id() )
			);
		} elseif ( is_search() ) {
			$parts['title'] = sprintf(
				/* translators: %s search term */
				__( 'Search results for: %s', 'rx' ),
				get_search_query()
			);
		} elseif ( is_404() ) {
			$parts['title'] = __( 'Page Not Found', 'rx' );
		}

		return $parts;
	}

	/**
	 * Robots meta filter.
	 */
	public function filter_wp_robots( $robots ) {
		if ( is_search() || is_404() ) {
			$robots['noindex']  = true;
			$robots['nofollow'] = false;
		}

		if ( is_paged() ) {
			$robots['noindex'] = false;
			$robots['follow']  = true;
		}

		if ( is_singular() ) {
			$post_id = get_queried_object_id();

			if ( $post_id && '0' === get_post_meta( $post_id, '_rx_seo_index', true ) ) {
				$robots['noindex'] = true;
			}
		}

		$robots['max-image-preview'] = 'large';
		$robots['max-snippet']       = -1;
		$robots['max-video-preview'] = -1;

		return $robots;
	}

	/**
	 * Get current context.
	 */
	private function get_context() {
		if ( is_front_page() ) {
			return 'front_page';
		}

		if ( is_home() ) {
			return 'blog_home';
		}

		if ( is_singular( 'post' ) ) {
			return 'single_post';
		}

		if ( is_singular( 'page' ) ) {
			return 'single_page';
		}

		if ( is_singular() ) {
			return 'single';
		}

		if ( is_category() ) {
			return 'category';
		}

		if ( is_tag() ) {
			return 'tag';
		}

		if ( is_tax() ) {
			return 'taxonomy';
		}

		if ( is_author() ) {
			return 'author';
		}

		if ( is_date() ) {
			return 'date_archive';
		}

		if ( is_search() ) {
			return 'search';
		}

		if ( is_404() ) {
			return '404';
		}

		if ( function_exists( 'is_shop' ) && is_shop() ) {
			return 'shop';
		}

		if ( function_exists( 'is_product' ) && is_product() ) {
			return 'product';
		}

		return 'general';
	}

	/**
	 * Get SEO title.
	 */
	private function get_meta_title() {
		$title = '';

		if ( is_front_page() || is_home() ) {
			$title = get_bloginfo( 'name' );

			if ( get_bloginfo( 'description' ) ) {
				$title .= ' - ' . get_bloginfo( 'description' );
			}
		} elseif ( is_singular() ) {
			$post_id      = get_queried_object_id();
			$custom_title = get_post_meta( $post_id, '_rx_seo_title', true );

			$title = $custom_title ? $custom_title : get_the_title( $post_id );
			$title .= ' - ' . get_bloginfo( 'name' );
		} elseif ( is_category() || is_tag() || is_tax() ) {
			$title = single_term_title( '', false ) . ' - ' . get_bloginfo( 'name' );
		} elseif ( is_author() ) {
			$title = sprintf(
				__( 'Author: %s', 'rx' ),
				get_the_author_meta( 'display_name', get_queried_object_id() )
			) . ' - ' . get_bloginfo( 'name' );
		} elseif ( is_search() ) {
			$title = sprintf(
				__( 'Search results for: %s', 'rx' ),
				get_search_query()
			) . ' - ' . get_bloginfo( 'name' );
		} elseif ( is_404() ) {
			$title = __( 'Page Not Found', 'rx' ) . ' - ' . get_bloginfo( 'name' );
		} else {
			$title = wp_get_document_title();
		}

		return apply_filters( 'rx_meta_title', wp_strip_all_tags( $title ) );
	}

	/**
	 * Get SEO description.
	 */
	private function get_meta_description() {
		$description = '';

		if ( is_front_page() || is_home() ) {
			$description = get_bloginfo( 'description' );
		} elseif ( is_singular() ) {
			$post_id            = get_queried_object_id();
			$custom_description = get_post_meta( $post_id, '_rx_seo_description', true );

			if ( $custom_description ) {
				$description = $custom_description;
			} elseif ( has_excerpt( $post_id ) ) {
				$description = get_the_excerpt( $post_id );
			} else {
				$post = get_post( $post_id );

				if ( $post ) {
					$description = $this->trim_text( wp_strip_all_tags( strip_shortcodes( $post->post_content ) ), self::DESCRIPTION_LENGTH );
				}
			}
		} elseif ( is_category() || is_tag() || is_tax() ) {
			$description = term_description();

			if ( ! $description ) {
				$description = sprintf(
					__( 'Read the latest articles about %s on %s.', 'rx' ),
					single_term_title( '', false ),
					get_bloginfo( 'name' )
				);
			}
		} elseif ( is_author() ) {
			$description = get_the_author_meta( 'description', get_queried_object_id() );

			if ( ! $description ) {
				$description = sprintf(
					__( 'Articles written by %s on %s.', 'rx' ),
					get_the_author_meta( 'display_name', get_queried_object_id() ),
					get_bloginfo( 'name' )
				);
			}
		} elseif ( is_search() ) {
			$description = sprintf(
				__( 'Search results for %s on %s.', 'rx' ),
				get_search_query(),
				get_bloginfo( 'name' )
			);
		} elseif ( is_404() ) {
			$description = sprintf(
				__( 'The page you are looking for could not be found on %s.', 'rx' ),
				get_bloginfo( 'name' )
			);
		}

		if ( empty( $description ) ) {
			$description = get_bloginfo( 'description' );
		}

		$description = wp_strip_all_tags( strip_shortcodes( $description ) );
		$description = preg_replace( '/\s+/', ' ', $description );
		$description = trim( $description );
		$description = $this->trim_text( $description, self::DESCRIPTION_LENGTH );

		return apply_filters( 'rx_meta_description', $description );
	}

	/**
	 * Get meta keywords.
	 *
	 * Meta keywords are not a major modern SEO ranking factor,
	 * but this is included because you requested as much functionality as possible.
	 */
	private function get_meta_keywords() {
		$keywords = array();

		if ( is_singular() ) {
			$post_id = get_queried_object_id();

			$custom_keywords = get_post_meta( $post_id, '_rx_seo_keywords', true );

			if ( $custom_keywords ) {
				return apply_filters( 'rx_meta_keywords', sanitize_text_field( $custom_keywords ) );
			}

			$tags = get_the_tags( $post_id );

			if ( $tags && ! is_wp_error( $tags ) ) {
				foreach ( $tags as $tag ) {
					$keywords[] = $tag->name;
				}
			}

			$categories = get_the_category( $post_id );

			if ( $categories && ! is_wp_error( $categories ) ) {
				foreach ( $categories as $category ) {
					$keywords[] = $category->name;
				}
			}
		} elseif ( is_category() || is_tag() || is_tax() ) {
			$keywords[] = single_term_title( '', false );
		}

		$keywords[] = get_bloginfo( 'name' );

		$keywords = array_filter( array_unique( array_map( 'sanitize_text_field', $keywords ) ) );

		return apply_filters( 'rx_meta_keywords', implode( ', ', $keywords ) );
	}

	/**
	 * Get current URL.
	 */
	private function get_current_url() {
		if ( is_singular() ) {
			return get_permalink();
		}

		if ( is_category() || is_tag() || is_tax() ) {
			$term = get_queried_object();

			if ( $term && ! is_wp_error( $term ) ) {
				$link = get_term_link( $term );

				if ( ! is_wp_error( $link ) ) {
					return $link;
				}
			}
		}

		if ( is_author() ) {
			return get_author_posts_url( get_queried_object_id() );
		}

		if ( is_search() ) {
			return get_search_link();
		}

		$scheme = is_ssl() ? 'https://' : 'http://';
		$host   = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : wp_parse_url( home_url(), PHP_URL_HOST );
		$uri    = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '/';

		return esc_url_raw( $scheme . $host . $uri );
	}

	/**
	 * Get canonical URL.
	 */
	private function get_canonical_url() {
		if ( is_singular() ) {
			return get_permalink();
		}

		if ( is_front_page() || is_home() ) {
			return home_url( '/' );
		}

		if ( is_category() || is_tag() || is_tax() ) {
			$term = get_queried_object();

			if ( $term && ! is_wp_error( $term ) ) {
				$link = get_term_link( $term );

				if ( ! is_wp_error( $link ) ) {
					return $link;
				}
			}
		}

		if ( is_author() ) {
			return get_author_posts_url( get_queried_object_id() );
		}

		if ( is_search() || is_404() ) {
			return '';
		}

		return $this->get_current_url();
	}

	/**
	 * Output prev/next pagination links.
	 */
	private function output_prev_next_links() {
		if ( is_singular() ) {
			$prev = get_adjacent_post( false, '', true );
			$next = get_adjacent_post( false, '', false );

			if ( $prev ) {
				printf(
					'<link rel="prev" href="%s" />' . "\n",
					esc_url( get_permalink( $prev ) )
				);
			}

			if ( $next ) {
				printf(
					'<link rel="next" href="%s" />' . "\n",
					esc_url( get_permalink( $next ) )
				);
			}

			return;
		}

		global $paged, $wp_query;

		$current_page = max( 1, absint( $paged ) );
		$total_pages  = isset( $wp_query->max_num_pages ) ? absint( $wp_query->max_num_pages ) : 1;

		if ( $total_pages <= 1 ) {
			return;
		}

		if ( $current_page > 1 ) {
			printf(
				'<link rel="prev" href="%s" />' . "\n",
				esc_url( get_pagenum_link( $current_page - 1 ) )
			);
		}

		if ( $current_page < $total_pages ) {
			printf(
				'<link rel="next" href="%s" />' . "\n",
				esc_url( get_pagenum_link( $current_page + 1 ) )
			);
		}
	}

	/**
	 * Get Open Graph type.
	 */
	private function get_og_type() {
		if ( is_singular( 'post' ) ) {
			return 'article';
		}

		if ( function_exists( 'is_product' ) && is_product() ) {
			return 'product';
		}

		if ( is_front_page() || is_home() ) {
			return 'website';
		}

		return 'object';
	}

	/**
	 * Get meta image.
	 *
	 * WordPress featured image helpers like has_post_thumbnail()
	 * and get_the_post_thumbnail_url() are used here.
	 */
	private function get_meta_image() {
		$image = '';

		if ( is_singular() ) {
			$post_id = get_queried_object_id();

			if ( has_post_thumbnail( $post_id ) ) {
				$image = get_the_post_thumbnail_url( $post_id, 'full' );
			}
		}

		if ( ! $image ) {
			$custom_logo_id = get_theme_mod( 'custom_logo' );

			if ( $custom_logo_id ) {
				$image = wp_get_attachment_image_url( $custom_logo_id, 'full' );
			}
		}

		if ( ! $image ) {
			$fallback = get_template_directory_uri() . self::DEFAULT_OG_IMAGE;
			$image    = $fallback;
		}

		return apply_filters( 'rx_meta_image', esc_url_raw( $image ) );
	}

	/**
	 * Get image data.
	 */
	private function get_meta_image_data() {
		$data = array(
			'width'  => '',
			'height' => '',
			'mime'   => '',
		);

		if ( is_singular() ) {
			$post_id = get_queried_object_id();

			if ( has_post_thumbnail( $post_id ) ) {
				$thumb_id = get_post_thumbnail_id( $post_id );
				$meta     = wp_get_attachment_metadata( $thumb_id );
				$mime     = get_post_mime_type( $thumb_id );

				if ( ! empty( $meta['width'] ) ) {
					$data['width'] = absint( $meta['width'] );
				}

				if ( ! empty( $meta['height'] ) ) {
					$data['height'] = absint( $meta['height'] );
				}

				if ( $mime ) {
					$data['mime'] = sanitize_text_field( $mime );
				}
			}
		}

		return apply_filters( 'rx_meta_image_data', $data );
	}

	/**
	 * Get image alt.
	 */
	private function get_image_alt_text() {
		$alt = get_bloginfo( 'name' );

		if ( is_singular() ) {
			$post_id = get_queried_object_id();

			if ( has_post_thumbnail( $post_id ) ) {
				$thumb_id = get_post_thumbnail_id( $post_id );
				$image_alt = get_post_meta( $thumb_id, '_wp_attachment_image_alt', true );

				if ( $image_alt ) {
					$alt = $image_alt;
				} else {
					$alt = get_the_title( $post_id );
				}
			}
		}

		return apply_filters( 'rx_meta_image_alt', wp_strip_all_tags( $alt ) );
	}

	/**
	 * Get locale.
	 */
	private function get_locale() {
		$locale = get_locale();
		$locale = str_replace( '-', '_', $locale );

		return apply_filters( 'rx_meta_locale', $locale );
	}

	/**
	 * Get theme color.
	 */
	private function get_theme_color() {
		$color = get_theme_mod( 'rx_theme_color', '#0f766e' );

		if ( ! preg_match( '/^#[a-fA-F0-9]{6}$/', $color ) ) {
			$color = '#0f766e';
		}

		return apply_filters( 'rx_theme_color_meta', $color );
	}

	/**
	 * Twitter handle.
	 *
	 * Change this to your real Twitter/X handle if needed.
	 */
	private function get_twitter_site() {
		$handle = get_theme_mod( 'rx_twitter_handle', '' );

		if ( empty( $handle ) ) {
			return '';
		}

		$handle = sanitize_text_field( $handle );

		if ( '@' !== substr( $handle, 0, 1 ) ) {
			$handle = '@' . $handle;
		}

		return apply_filters( 'rx_twitter_site', $handle );
	}

	/**
	 * Get author name.
	 */
	private function get_author_name() {
		if ( is_singular() ) {
			$post = get_post();

			if ( $post ) {
				return get_the_author_meta( 'display_name', $post->post_author );
			}
		}

		if ( is_author() ) {
			return get_the_author_meta( 'display_name', get_queried_object_id() );
		}

		return get_bloginfo( 'name' );
	}

	/**
	 * Output JSON-LD schema.
	 */
	public function output_json_ld_schema() {
		if ( is_admin() || is_feed() || wp_doing_ajax() ) {
			return;
		}

		$schemas = array();

		$schemas[] = $this->get_website_schema();
		$schemas[] = $this->get_organization_schema();

		if ( is_singular() ) {
			$schemas[] = $this->get_article_schema();
		}

		if ( function_exists( 'is_product' ) && is_product() ) {
			$product_schema = $this->get_product_schema();

			if ( $product_schema ) {
				$schemas[] = $product_schema;
			}
		}

		$breadcrumb_schema = $this->get_breadcrumb_schema();

		if ( $breadcrumb_schema ) {
			$schemas[] = $breadcrumb_schema;
		}

		$schemas = array_filter( $schemas );

		if ( empty( $schemas ) ) {
			return;
		}

		foreach ( $schemas as $schema ) {
			$this->print_json_ld( $schema );
		}
	}

	/**
	 * WebSite schema.
	 */
	private function get_website_schema() {
		$schema = array(
			'@context'        => 'https://schema.org',
			'@type'           => 'WebSite',
			'@id'             => home_url( '/#website' ),
			'url'             => home_url( '/' ),
			'name'            => get_bloginfo( 'name' ),
			'description'     => get_bloginfo( 'description' ),
			'inLanguage'      => get_bloginfo( 'language' ),
			'potentialAction' => array(
				'@type'       => 'SearchAction',
				'target'      => home_url( '/?s={search_term_string}' ),
				'query-input' => 'required name=search_term_string',
			),
		);

		return apply_filters( 'rx_website_schema', $schema );
	}

	/**
	 * Organization schema.
	 */
	private function get_organization_schema() {
		$logo = '';
		$custom_logo_id = get_theme_mod( 'custom_logo' );

		if ( $custom_logo_id ) {
			$logo = wp_get_attachment_image_url( $custom_logo_id, 'full' );
		}

		$schema = array(
			'@context' => 'https://schema.org',
			'@type'    => 'Organization',
			'@id'      => home_url( '/#organization' ),
			'name'     => get_bloginfo( 'name' ),
			'url'      => home_url( '/' ),
		);

		if ( $logo ) {
			$schema['logo'] = array(
				'@type' => 'ImageObject',
				'url'   => esc_url_raw( $logo ),
			);
		}

		$social_links = $this->get_social_links();

		if ( ! empty( $social_links ) ) {
			$schema['sameAs'] = $social_links;
		}

		return apply_filters( 'rx_organization_schema', $schema );
	}

	/**
	 * Article schema.
	 */
	private function get_article_schema() {
		$post_id = get_queried_object_id();

		if ( ! $post_id ) {
			return array();
		}

		$image = $this->get_meta_image();

		$schema = array(
			'@context'         => 'https://schema.org',
			'@type'            => is_singular( 'post' ) ? 'Article' : 'WebPage',
			'@id'              => get_permalink( $post_id ) . '#article',
			'mainEntityOfPage' => array(
				'@type' => 'WebPage',
				'@id'   => get_permalink( $post_id ),
			),
			'headline'         => $this->get_meta_title(),
			'description'      => $this->get_meta_description(),
			'url'              => get_permalink( $post_id ),
			'datePublished'    => get_the_date( DATE_W3C, $post_id ),
			'dateModified'     => get_the_modified_date( DATE_W3C, $post_id ),
			'inLanguage'       => get_bloginfo( 'language' ),
			'author'           => array(
				'@type' => 'Person',
				'name'  => get_the_author_meta( 'display_name', get_post_field( 'post_author', $post_id ) ),
				'url'   => get_author_posts_url( get_post_field( 'post_author', $post_id ) ),
			),
			'publisher'        => array(
				'@id' => home_url( '/#organization' ),
			),
		);

		if ( $image ) {
			$schema['image'] = array(
				'@type' => 'ImageObject',
				'url'   => esc_url_raw( $image ),
			);
		}

		$word_count = str_word_count( wp_strip_all_tags( get_post_field( 'post_content', $post_id ) ) );

		if ( $word_count ) {
			$schema['wordCount'] = absint( $word_count );
		}

		$categories = get_the_category( $post_id );

		if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) {
			$schema['articleSection'] = wp_list_pluck( $categories, 'name' );
		}

		$tags = get_the_tags( $post_id );

		if ( ! empty( $tags ) && ! is_wp_error( $tags ) ) {
			$schema['keywords'] = implode( ', ', wp_list_pluck( $tags, 'name' ) );
		}

		return apply_filters( 'rx_article_schema', $schema, $post_id );
	}

	/**
	 * Product schema for WooCommerce.
	 */
	private function get_product_schema() {
		if ( ! function_exists( 'wc_get_product' ) ) {
			return array();
		}

		$product_id = get_queried_object_id();
		$product    = wc_get_product( $product_id );

		if ( ! $product ) {
			return array();
		}

		$schema = array(
			'@context'    => 'https://schema.org',
			'@type'       => 'Product',
			'@id'         => get_permalink( $product_id ) . '#product',
			'name'        => get_the_title( $product_id ),
			'description' => $this->get_meta_description(),
			'url'         => get_permalink( $product_id ),
			'image'       => $this->get_meta_image(),
			'sku'         => $product->get_sku(),
		);

		$price = $product->get_price();

		if ( '' !== $price ) {
			$schema['offers'] = array(
				'@type'         => 'Offer',
				'price'         => $price,
				'priceCurrency' => get_woocommerce_currency(),
				'availability'  => $product->is_in_stock() ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',
				'url'           => get_permalink( $product_id ),
			);
		}

		return apply_filters( 'rx_product_schema', $schema, $product_id, $product );
	}

	/**
	 * Breadcrumb schema.
	 */
	private function get_breadcrumb_schema() {
		$items = array();

		$items[] = array(
			'@type'    => 'ListItem',
			'position' => 1,
			'name'     => __( 'Home', 'rx' ),
			'item'     => home_url( '/' ),
		);

		if ( is_singular() ) {
			$post_id = get_queried_object_id();

			if ( is_singular( 'post' ) ) {
				$categories = get_the_category( $post_id );

				if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) {
					$category = $categories[0];

					$items[] = array(
						'@type'    => 'ListItem',
						'position' => count( $items ) + 1,
						'name'     => $category->name,
						'item'     => get_category_link( $category->term_id ),
					);
				}
			}

			$items[] = array(
				'@type'    => 'ListItem',
				'position' => count( $items ) + 1,
				'name'     => get_the_title( $post_id ),
				'item'     => get_permalink( $post_id ),
			);
		} elseif ( is_category() || is_tag() || is_tax() ) {
			$items[] = array(
				'@type'    => 'ListItem',
				'position' => count( $items ) + 1,
				'name'     => single_term_title( '', false ),
				'item'     => $this->get_current_url(),
			);
		} elseif ( is_search() ) {
			$items[] = array(
				'@type'    => 'ListItem',
				'position' => count( $items ) + 1,
				'name'     => sprintf( __( 'Search: %s', 'rx' ), get_search_query() ),
				'item'     => get_search_link(),
			);
		} elseif ( is_author() ) {
			$items[] = array(
				'@type'    => 'ListItem',
				'position' => count( $items ) + 1,
				'name'     => get_the_author_meta( 'display_name', get_queried_object_id() ),
				'item'     => get_author_posts_url( get_queried_object_id() ),
			);
		} else {
			return array();
		}

		$schema = array(
			'@context'        => 'https://schema.org',
			'@type'           => 'BreadcrumbList',
			'itemListElement' => $items,
		);

		return apply_filters( 'rx_breadcrumb_schema', $schema );
	}

	/**
	 * Social links for Organization schema.
	 *
	 * Add your links using this filter from functions.php:
	 *
	 * add_filter( 'rx_social_links', function( $links ) {
	 *     $links[] = 'https://www.facebook.com/yourpage';
	 *     $links[] = 'https://x.com/yourhandle';
	 *     return $links;
	 * });
	 */
	private function get_social_links() {
		$links = array();

		$links = apply_filters( 'rx_social_links', $links );

		$links = array_filter(
			array_map(
				static function( $url ) {
					return esc_url_raw( $url );
				},
				(array) $links
			)
		);

		return array_values( array_unique( $links ) );
	}

	/**
	 * Print meta name.
	 */
	private function print_meta_name( $name, $content ) {
		$content = trim( (string) $content );

		if ( '' === $content ) {
			return;
		}

		printf(
			'<meta name="%s" content="%s" />' . "\n",
			esc_attr( $name ),
			esc_attr( $content )
		);
	}

	/**
	 * Print meta property.
	 */
	private function print_meta_property( $property, $content ) {
		$content = trim( (string) $content );

		if ( '' === $content ) {
			return;
		}

		printf(
			'<meta property="%s" content="%s" />' . "\n",
			esc_attr( $property ),
			esc_attr( $content )
		);
	}

	/**
	 * Print JSON-LD.
	 */
	private function print_json_ld( $data ) {
		if ( empty( $data ) || ! is_array( $data ) ) {
			return;
		}

		$json = wp_json_encode(
			$data,
			JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
		);

		if ( ! $json ) {
			return;
		}

		echo '<script type="application/ld+json">' . "\n";
		echo $json . "\n";
		echo '</script>' . "\n";
	}

	/**
	 * Trim text by character length.
	 */
	private function trim_text( $text, $length = 160 ) {
		$text = trim( (string) $text );

		if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_substr' ) ) {
			if ( mb_strlen( $text ) <= $length ) {
				return $text;
			}

			return rtrim( mb_substr( $text, 0, $length - 3 ) ) . '...';
		}

		if ( strlen( $text ) <= $length ) {
			return $text;
		}

		return rtrim( substr( $text, 0, $length - 3 ) ) . '...';
	}
}

endif;

/**
 * Boot RX advanced meta tags.
 */
function rx_boot_advanced_meta_tags() {
	new RX_Advanced_Meta_Tags();
}
add_action( 'after_setup_theme', 'rx_boot_advanced_meta_tags' );

Now add this line in your main functions.php:

require_once get_template_directory() . '/inc/seo/meta-tags.php';

Also make sure your header.php has this before </head>:

<?php wp_head(); ?>

Important: featured images are used for og:image and Twitter image when available. WordPress supports checking featured images with has_post_thumbnail() and retrieving image URLs with get_the_post_thumbnail_url() / wp_get_attachment_image_url().

Leave a Reply

Your email address will not be published. Required fields are marked *