I’ll build this as a drop-in inc/helpers/schema-article.php style file for your RX WordPress theme, with safe fallbacks, SEO-friendly JSON-LD, Article/MedicalWebPage support, breadcrumbs, author, publisher, image, FAQ, speakable, and medical metadata where available.
Below is an advanced schema-article.php file for your RX theme.
Place it here:
rx-theme/inc/helpers/schema-article.php
Then load it from functions.php:
require_once get_template_directory() . '/inc/helpers/schema-article.php';
<?php
/**
* RX Theme - Advanced Article Schema JSON-LD
*
* File: inc/helpers/schema-article.php
*
* Features:
* - Article / BlogPosting / MedicalWebPage / MedicalScholarlyArticle support
* - Author schema
* - Publisher schema
* - Organization schema
* - WebSite schema
* - WebPage schema
* - BreadcrumbList schema
* - ImageObject schema
* - Person schema
* - Speakable schema
* - FAQPage schema from post meta
* - HowTo schema from post meta
* - MedicalEntity-ready schema fields
* - Last modified date support
* - Reading time support
* - Word count support
* - Keywords support
* - SameAs social links support
* - Safe sanitization and escaping
*
* @package RX_Theme
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! function_exists( 'rx_schema_clean_text' ) ) {
/**
* Clean text for schema output.
*
* @param string $text Raw text.
* @return string
*/
function rx_schema_clean_text( $text ) {
$text = wp_strip_all_tags( $text );
$text = html_entity_decode( $text, ENT_QUOTES, get_bloginfo( 'charset' ) );
$text = preg_replace( '/\s+/', ' ', $text );
return trim( $text );
}
}
if ( ! function_exists( 'rx_schema_get_excerpt' ) ) {
/**
* Get SEO-safe article excerpt.
*
* @param int $post_id Post ID.
* @return string
*/
function rx_schema_get_excerpt( $post_id ) {
$excerpt = get_the_excerpt( $post_id );
if ( empty( $excerpt ) ) {
$post = get_post( $post_id );
$content = isset( $post->post_content ) ? $post->post_content : '';
$excerpt = wp_trim_words( wp_strip_all_tags( strip_shortcodes( $content ) ), 35, '' );
}
return rx_schema_clean_text( $excerpt );
}
}
if ( ! function_exists( 'rx_schema_get_word_count' ) ) {
/**
* Get article word count.
*
* @param int $post_id Post ID.
* @return int
*/
function rx_schema_get_word_count( $post_id ) {
$post = get_post( $post_id );
if ( ! $post || empty( $post->post_content ) ) {
return 0;
}
$content = wp_strip_all_tags( strip_shortcodes( $post->post_content ) );
$words = preg_split( '/\s+/', trim( $content ) );
return is_array( $words ) ? count( array_filter( $words ) ) : 0;
}
}
if ( ! function_exists( 'rx_schema_get_reading_time' ) ) {
/**
* Estimate reading time.
*
* @param int $post_id Post ID.
* @return string
*/
function rx_schema_get_reading_time( $post_id ) {
$word_count = rx_schema_get_word_count( $post_id );
$minutes = max( 1, ceil( $word_count / 200 ) );
return 'PT' . absint( $minutes ) . 'M';
}
}
if ( ! function_exists( 'rx_schema_get_logo_url' ) ) {
/**
* Get website logo URL.
*
* @return string
*/
function rx_schema_get_logo_url() {
$custom_logo_id = get_theme_mod( 'custom_logo' );
if ( $custom_logo_id ) {
$logo = wp_get_attachment_image_url( $custom_logo_id, 'full' );
if ( $logo ) {
return esc_url_raw( $logo );
}
}
$site_icon = get_site_icon_url( 512 );
if ( $site_icon ) {
return esc_url_raw( $site_icon );
}
return esc_url_raw( get_template_directory_uri() . '/assets/images/logo.png' );
}
}
if ( ! function_exists( 'rx_schema_get_post_image' ) ) {
/**
* Get post image schema.
*
* @param int $post_id Post ID.
* @return array|null
*/
function rx_schema_get_post_image( $post_id ) {
$image_id = get_post_thumbnail_id( $post_id );
if ( ! $image_id ) {
return null;
}
$image_url = wp_get_attachment_image_url( $image_id, 'full' );
if ( ! $image_url ) {
return null;
}
$metadata = wp_get_attachment_metadata( $image_id );
$image_schema = array(
'@type' => 'ImageObject',
'@id' => esc_url_raw( $image_url ) . '#primaryimage',
'url' => esc_url_raw( $image_url ),
);
if ( ! empty( $metadata['width'] ) ) {
$image_schema['width'] = absint( $metadata['width'] );
}
if ( ! empty( $metadata['height'] ) ) {
$image_schema['height'] = absint( $metadata['height'] );
}
$alt = get_post_meta( $image_id, '_wp_attachment_image_alt', true );
if ( ! empty( $alt ) ) {
$image_schema['caption'] = rx_schema_clean_text( $alt );
}
return $image_schema;
}
}
if ( ! function_exists( 'rx_schema_get_author_schema' ) ) {
/**
* Get author schema.
*
* @param int $author_id Author ID.
* @return array
*/
function rx_schema_get_author_schema( $author_id ) {
$author_url = get_author_posts_url( $author_id );
$author_name = get_the_author_meta( 'display_name', $author_id );
$author_description = get_the_author_meta( 'description', $author_id );
$same_as = array();
$social_fields = array(
'facebook',
'twitter',
'x',
'linkedin',
'youtube',
'instagram',
'pinterest',
'github',
'website',
);
foreach ( $social_fields as $field ) {
$value = get_the_author_meta( $field, $author_id );
if ( ! empty( $value ) && filter_var( $value, FILTER_VALIDATE_URL ) ) {
$same_as[] = esc_url_raw( $value );
}
}
$schema = array(
'@type' => 'Person',
'@id' => esc_url_raw( $author_url ) . '#author',
'name' => rx_schema_clean_text( $author_name ),
'url' => esc_url_raw( $author_url ),
);
if ( ! empty( $author_description ) ) {
$schema['description'] = rx_schema_clean_text( $author_description );
}
if ( ! empty( $same_as ) ) {
$schema['sameAs'] = array_values( array_unique( $same_as ) );
}
return $schema;
}
}
if ( ! function_exists( 'rx_schema_get_organization_schema' ) ) {
/**
* Get organization schema.
*
* @return array
*/
function rx_schema_get_organization_schema() {
$home_url = home_url( '/' );
$logo_url = rx_schema_get_logo_url();
$site_name = get_bloginfo( 'name' );
$same_as = array();
/**
* Add your official social links here.
*/
$theme_social_links = array(
get_theme_mod( 'rx_facebook_url' ),
get_theme_mod( 'rx_twitter_url' ),
get_theme_mod( 'rx_linkedin_url' ),
get_theme_mod( 'rx_youtube_url' ),
get_theme_mod( 'rx_instagram_url' ),
get_theme_mod( 'rx_pinterest_url' ),
);
foreach ( $theme_social_links as $link ) {
if ( ! empty( $link ) && filter_var( $link, FILTER_VALIDATE_URL ) ) {
$same_as[] = esc_url_raw( $link );
}
}
$schema = array(
'@type' => 'Organization',
'@id' => esc_url_raw( $home_url ) . '#organization',
'name' => rx_schema_clean_text( $site_name ),
'url' => esc_url_raw( $home_url ),
'logo' => array(
'@type' => 'ImageObject',
'@id' => esc_url_raw( $home_url ) . '#logo',
'url' => esc_url_raw( $logo_url ),
),
);
if ( ! empty( $same_as ) ) {
$schema['sameAs'] = array_values( array_unique( $same_as ) );
}
return $schema;
}
}
if ( ! function_exists( 'rx_schema_get_website_schema' ) ) {
/**
* Get WebSite schema.
*
* @return array
*/
function rx_schema_get_website_schema() {
$home_url = home_url( '/' );
return array(
'@type' => 'WebSite',
'@id' => esc_url_raw( $home_url ) . '#website',
'url' => esc_url_raw( $home_url ),
'name' => rx_schema_clean_text( get_bloginfo( 'name' ) ),
'description' => rx_schema_clean_text( get_bloginfo( 'description' ) ),
'publisher' => array(
'@id' => esc_url_raw( $home_url ) . '#organization',
),
'potentialAction' => array(
'@type' => 'SearchAction',
'target' => array(
'@type' => 'EntryPoint',
'urlTemplate' => esc_url_raw( home_url( '/?s={search_term_string}' ) ),
),
'query-input' => 'required name=search_term_string',
),
);
}
}
if ( ! function_exists( 'rx_schema_get_breadcrumb_schema' ) ) {
/**
* Get breadcrumb schema.
*
* @param int $post_id Post ID.
* @return array
*/
function rx_schema_get_breadcrumb_schema( $post_id ) {
$items = array();
$items[] = array(
'@type' => 'ListItem',
'position' => 1,
'name' => 'Home',
'item' => esc_url_raw( home_url( '/' ) ),
);
$position = 2;
$categories = get_the_category( $post_id );
if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) {
$primary_category = $categories[0];
$items[] = array(
'@type' => 'ListItem',
'position' => $position,
'name' => rx_schema_clean_text( $primary_category->name ),
'item' => esc_url_raw( get_category_link( $primary_category->term_id ) ),
);
$position++;
}
$items[] = array(
'@type' => 'ListItem',
'position' => $position,
'name' => rx_schema_clean_text( get_the_title( $post_id ) ),
'item' => esc_url_raw( get_permalink( $post_id ) ),
);
return array(
'@type' => 'BreadcrumbList',
'@id' => esc_url_raw( get_permalink( $post_id ) ) . '#breadcrumb',
'itemListElement' => $items,
);
}
}
if ( ! function_exists( 'rx_schema_get_keywords' ) ) {
/**
* Get post keywords from tags and categories.
*
* @param int $post_id Post ID.
* @return array
*/
function rx_schema_get_keywords( $post_id ) {
$keywords = array();
$tags = get_the_tags( $post_id );
if ( ! empty( $tags ) && ! is_wp_error( $tags ) ) {
foreach ( $tags as $tag ) {
$keywords[] = rx_schema_clean_text( $tag->name );
}
}
$categories = get_the_category( $post_id );
if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) {
foreach ( $categories as $category ) {
$keywords[] = rx_schema_clean_text( $category->name );
}
}
return array_values( array_unique( array_filter( $keywords ) ) );
}
}
if ( ! function_exists( 'rx_schema_get_article_section' ) ) {
/**
* Get article section.
*
* @param int $post_id Post ID.
* @return string
*/
function rx_schema_get_article_section( $post_id ) {
$categories = get_the_category( $post_id );
if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) {
return rx_schema_clean_text( $categories[0]->name );
}
return 'Article';
}
}
if ( ! function_exists( 'rx_schema_get_faq_schema' ) ) {
/**
* Build FAQPage schema from post meta.
*
* Expected post meta format:
* rx_faq_schema = array(
* array(
* 'question' => 'Question text',
* 'answer' => 'Answer text',
* ),
* );
*
* @param int $post_id Post ID.
* @return array|null
*/
function rx_schema_get_faq_schema( $post_id ) {
$faqs = get_post_meta( $post_id, 'rx_faq_schema', true );
if ( empty( $faqs ) || ! is_array( $faqs ) ) {
return null;
}
$main_entity = array();
foreach ( $faqs as $faq ) {
if ( empty( $faq['question'] ) || empty( $faq['answer'] ) ) {
continue;
}
$main_entity[] = array(
'@type' => 'Question',
'name' => rx_schema_clean_text( $faq['question'] ),
'acceptedAnswer' => array(
'@type' => 'Answer',
'text' => rx_schema_clean_text( $faq['answer'] ),
),
);
}
if ( empty( $main_entity ) ) {
return null;
}
return array(
'@type' => 'FAQPage',
'@id' => esc_url_raw( get_permalink( $post_id ) ) . '#faq',
'mainEntity' => $main_entity,
);
}
}
if ( ! function_exists( 'rx_schema_get_howto_schema' ) ) {
/**
* Build HowTo schema from post meta.
*
* Expected post meta format:
* rx_howto_schema = array(
* 'name' => 'How to title',
* 'description' => 'Short description',
* 'steps' => array(
* array(
* 'name' => 'Step title',
* 'text' => 'Step text',
* ),
* ),
* );
*
* @param int $post_id Post ID.
* @return array|null
*/
function rx_schema_get_howto_schema( $post_id ) {
$howto = get_post_meta( $post_id, 'rx_howto_schema', true );
if ( empty( $howto ) || ! is_array( $howto ) || empty( $howto['steps'] ) ) {
return null;
}
$steps = array();
$count = 1;
foreach ( $howto['steps'] as $step ) {
if ( empty( $step['text'] ) ) {
continue;
}
$steps[] = array(
'@type' => 'HowToStep',
'position' => $count,
'name' => ! empty( $step['name'] ) ? rx_schema_clean_text( $step['name'] ) : 'Step ' . $count,
'text' => rx_schema_clean_text( $step['text'] ),
);
$count++;
}
if ( empty( $steps ) ) {
return null;
}
$name = ! empty( $howto['name'] ) ? $howto['name'] : get_the_title( $post_id );
$description = ! empty( $howto['description'] ) ? $howto['description'] : rx_schema_get_excerpt( $post_id );
return array(
'@type' => 'HowTo',
'@id' => esc_url_raw( get_permalink( $post_id ) ) . '#howto',
'name' => rx_schema_clean_text( $name ),
'description' => rx_schema_clean_text( $description ),
'step' => $steps,
);
}
}
if ( ! function_exists( 'rx_schema_is_medical_article' ) ) {
/**
* Detect whether current article is medical content.
*
* @param int $post_id Post ID.
* @return bool
*/
function rx_schema_is_medical_article( $post_id ) {
$medical_meta = get_post_meta( $post_id, 'rx_is_medical_article', true );
if ( '1' === (string) $medical_meta || 'yes' === strtolower( (string) $medical_meta ) ) {
return true;
}
$categories = get_the_category( $post_id );
if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) {
foreach ( $categories as $category ) {
$slug = strtolower( $category->slug );
$name = strtolower( $category->name );
if (
str_contains( $slug, 'medical' ) ||
str_contains( $slug, 'health' ) ||
str_contains( $slug, 'disease' ) ||
str_contains( $slug, 'medicine' ) ||
str_contains( $name, 'medical' ) ||
str_contains( $name, 'health' ) ||
str_contains( $name, 'disease' ) ||
str_contains( $name, 'medicine' )
) {
return true;
}
}
}
return false;
}
}
if ( ! function_exists( 'rx_schema_get_medical_extra' ) ) {
/**
* Get optional medical schema fields from post meta.
*
* Useful meta keys:
* - rx_medical_condition
* - rx_medical_specialty
* - rx_medical_audience
* - rx_reviewed_by
* - rx_reviewed_by_url
* - rx_last_medical_review_date
*
* @param int $post_id Post ID.
* @return array
*/
function rx_schema_get_medical_extra( $post_id ) {
$extra = array();
$condition = get_post_meta( $post_id, 'rx_medical_condition', true );
$specialty = get_post_meta( $post_id, 'rx_medical_specialty', true );
$audience = get_post_meta( $post_id, 'rx_medical_audience', true );
$reviewer = get_post_meta( $post_id, 'rx_reviewed_by', true );
$reviewerurl = get_post_meta( $post_id, 'rx_reviewed_by_url', true );
$review_date = get_post_meta( $post_id, 'rx_last_medical_review_date', true );
if ( ! empty( $condition ) ) {
$extra['about'] = array(
'@type' => 'MedicalCondition',
'name' => rx_schema_clean_text( $condition ),
);
}
if ( ! empty( $specialty ) ) {
$extra['medicalSpecialty'] = rx_schema_clean_text( $specialty );
}
if ( ! empty( $audience ) ) {
$extra['audience'] = array(
'@type' => 'MedicalAudience',
'name' => rx_schema_clean_text( $audience ),
);
}
if ( ! empty( $reviewer ) ) {
$reviewed_by = array(
'@type' => 'Person',
'name' => rx_schema_clean_text( $reviewer ),
);
if ( ! empty( $reviewerurl ) && filter_var( $reviewerurl, FILTER_VALIDATE_URL ) ) {
$reviewed_by['url'] = esc_url_raw( $reviewerurl );
}
$extra['reviewedBy'] = $reviewed_by;
}
if ( ! empty( $review_date ) ) {
$timestamp = strtotime( $review_date );
if ( $timestamp ) {
$extra['lastReviewed'] = gmdate( 'c', $timestamp );
}
}
return $extra;
}
}
if ( ! function_exists( 'rx_schema_get_article_schema' ) ) {
/**
* Build main article schema.
*
* @param int $post_id Post ID.
* @return array
*/
function rx_schema_get_article_schema( $post_id ) {
$post = get_post( $post_id );
$permalink = get_permalink( $post_id );
$home_url = home_url( '/' );
$author_id = (int) $post->post_author;
$image = rx_schema_get_post_image( $post_id );
$is_medical = rx_schema_is_medical_article( $post_id );
$schema_type = $is_medical ? 'MedicalWebPage' : 'Article';
/**
* Allow developer override:
* add_filter( 'rx_schema_article_type', function() { return 'BlogPosting'; } );
*/
$schema_type = apply_filters( 'rx_schema_article_type', $schema_type, $post_id );
$schema = array(
'@type' => $schema_type,
'@id' => esc_url_raw( $permalink ) . '#article',
'isPartOf' => array(
'@id' => esc_url_raw( $permalink ) . '#webpage',
),
'mainEntityOfPage' => array(
'@id' => esc_url_raw( $permalink ) . '#webpage',
),
'headline' => rx_schema_clean_text( get_the_title( $post_id ) ),
'description' => rx_schema_get_excerpt( $post_id ),
'datePublished' => get_the_date( DATE_W3C, $post_id ),
'dateModified' => get_the_modified_date( DATE_W3C, $post_id ),
'author' => array(
'@id' => esc_url_raw( get_author_posts_url( $author_id ) ) . '#author',
),
'publisher' => array(
'@id' => esc_url_raw( $home_url ) . '#organization',
),
'inLanguage' => get_bloginfo( 'language' ),
'url' => esc_url_raw( $permalink ),
'articleSection' => rx_schema_get_article_section( $post_id ),
'wordCount' => rx_schema_get_word_count( $post_id ),
'timeRequired' => rx_schema_get_reading_time( $post_id ),
'copyrightYear' => get_the_date( 'Y', $post_id ),
'copyrightHolder' => array(
'@id' => esc_url_raw( $home_url ) . '#organization',
),
'speakable' => array(
'@type' => 'SpeakableSpecification',
'cssSelector' => array(
'h1',
'.entry-title',
'.post-title',
'.entry-content p:first-of-type',
),
),
);
if ( ! empty( $image ) ) {
$schema['image'] = $image;
$schema['thumbnailUrl'] = $image['url'];
}
$keywords = rx_schema_get_keywords( $post_id );
if ( ! empty( $keywords ) ) {
$schema['keywords'] = implode( ', ', $keywords );
}
$medical_extra = rx_schema_get_medical_extra( $post_id );
if ( ! empty( $medical_extra ) ) {
$schema = array_merge( $schema, $medical_extra );
}
return apply_filters( 'rx_schema_article_schema', $schema, $post_id );
}
}
if ( ! function_exists( 'rx_schema_get_webpage_schema' ) ) {
/**
* Get WebPage schema.
*
* @param int $post_id Post ID.
* @return array
*/
function rx_schema_get_webpage_schema( $post_id ) {
$permalink = get_permalink( $post_id );
$image = rx_schema_get_post_image( $post_id );
$schema = array(
'@type' => rx_schema_is_medical_article( $post_id ) ? 'MedicalWebPage' : 'WebPage',
'@id' => esc_url_raw( $permalink ) . '#webpage',
'url' => esc_url_raw( $permalink ),
'name' => rx_schema_clean_text( get_the_title( $post_id ) ),
'description' => rx_schema_get_excerpt( $post_id ),
'isPartOf' => array(
'@id' => esc_url_raw( home_url( '/' ) ) . '#website',
),
'primaryImageOfPage' => ! empty( $image ) ? array(
'@id' => esc_url_raw( $image['url'] ) . '#primaryimage',
) : null,
'breadcrumb' => array(
'@id' => esc_url_raw( $permalink ) . '#breadcrumb',
),
'inLanguage' => get_bloginfo( 'language' ),
'datePublished' => get_the_date( DATE_W3C, $post_id ),
'dateModified' => get_the_modified_date( DATE_W3C, $post_id ),
);
return array_filter( $schema );
}
}
if ( ! function_exists( 'rx_schema_output_article_jsonld' ) ) {
/**
* Output complete JSON-LD graph for single posts.
*
* @return void
*/
function rx_schema_output_article_jsonld() {
if ( ! is_singular( 'post' ) ) {
return;
}
if ( is_admin() || is_feed() || wp_doing_ajax() ) {
return;
}
global $post;
if ( ! $post instanceof WP_Post ) {
return;
}
$post_id = get_the_ID();
$author_id = (int) get_post_field( 'post_author', $post_id );
$graph = array();
$graph[] = rx_schema_get_organization_schema();
$graph[] = rx_schema_get_website_schema();
$graph[] = rx_schema_get_author_schema( $author_id );
$graph[] = rx_schema_get_webpage_schema( $post_id );
$graph[] = rx_schema_get_breadcrumb_schema( $post_id );
$graph[] = rx_schema_get_article_schema( $post_id );
$faq_schema = rx_schema_get_faq_schema( $post_id );
if ( ! empty( $faq_schema ) ) {
$graph[] = $faq_schema;
}
$howto_schema = rx_schema_get_howto_schema( $post_id );
if ( ! empty( $howto_schema ) ) {
$graph[] = $howto_schema;
}
/**
* Final graph filter.
*/
$graph = apply_filters( 'rx_schema_article_graph', $graph, $post_id );
$json_ld = array(
'@context' => 'https://schema.org',
'@graph' => array_values( array_filter( $graph ) ),
);
echo "\n<!-- RX Theme Advanced Article Schema -->\n";
echo '<script type="application/ld+json">' . wp_json_encode(
$json_ld,
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
) . '</script>' . "\n";
}
}
add_action( 'wp_head', 'rx_schema_output_article_jsonld', 30 );
Optional post meta you can use later
This file supports extra medical and FAQ fields. You can add these custom fields in WordPress posts:
rx_is_medical_article
rx_medical_condition
rx_medical_specialty
rx_medical_audience
rx_reviewed_by
rx_reviewed_by_url
rx_last_medical_review_date
rx_faq_schema
rx_howto_schema
Example for a medical article:
rx_is_medical_article = yes
rx_medical_condition = Staphyloma
rx_medical_specialty = Ophthalmology
rx_medical_audience = Patient
rx_reviewed_by = Dr. Rx Harun
rx_last_medical_review_date = 2026-05-12
Important note
This code uses str_contains(), which requires PHP 8+. Since most modern hosting supports PHP 8.1 or 8.2, it should be okay.
If your Hostinger PHP version is lower than 8, replace this part:
str_contains( $slug, 'medical' )
with:
false !== strpos( $slug, 'medical' )
This file is safe to use as a strong advanced base for your RX theme article schema.