post_type, $allowed_types, true ) ) {
return;
}
$content = $post->post_content;
if ( empty( $content ) ) {
delete_post_meta( $post_id, '_wpaw_faq_schema' );
return;
}
// Strip Gutenberg block HTML comments to make regex matching easier.
$clean_content = preg_replace( '//s', '', $content );
$clean_content = preg_replace( '//s', '', $clean_content );
// Regex to find H2, H3, or H4 that contain a question mark, immediately followed by a paragraph.
// Matches:
Answer
$pattern = '/]*>(.*?)<\/p>/is'; $faqs = array(); if ( preg_match_all( $pattern, $clean_content, $matches, PREG_SET_ORDER ) ) { foreach ( $matches as $match ) { $question = wp_strip_all_tags( $match[2] ); $answer = wp_strip_all_tags( $match[3] ); // Basic validation: question must not be too short, answer must have some length. if ( strlen( $question ) > 10 && strlen( $answer ) > 15 ) { $faqs[] = array( '@type' => 'Question', 'name' => $question, 'acceptedAnswer' => array( '@type' => 'Answer', 'text' => $answer, ), ); } } } // Only save schema if we actually detected 1 or more valid FAQ pairs. if ( ! empty( $faqs ) ) { $schema = array( '@context' => 'https://schema.org', '@type' => 'FAQPage', 'mainEntity' => $faqs, ); update_post_meta( $post_id, '_wpaw_faq_schema', wp_json_encode( $schema, JSON_UNESCAPED_UNICODE ) ); } else { delete_post_meta( $post_id, '_wpaw_faq_schema' ); } } /** * Output the JSON-LD schema script in the frontend
. * This serves as a fallback for sites without a primary SEO plugin. */ public function output_faq_schema_in_head() { if ( ! is_singular() ) { return; } // Abort if feature is manually disabled in Settings $settings = get_option( 'wp_agentic_writer_settings', array() ); if ( isset( $settings['enable_faq_schema'] ) && ! $settings['enable_faq_schema'] ) { return; } // Prevent duplicate output if a major SEO plugin is active and handling our schema if ( defined( 'WPSEO_VERSION' ) || class_exists( 'RankMath' ) ) { return; } $post_id = get_the_ID(); // Attempt to fetch schema generated by WP Agentic Writer. $schema_json = get_post_meta( $post_id, '_wpaw_faq_schema', true ); if ( ! empty( $schema_json ) ) { echo "\n\n"; echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo "\n\n"; } } /** * Inject the FAQ schema directly into Yoast SEO's JSON-LD graph. * This prevents disjointed schema and consolidates everything into Yoast's payload. * * @param array $graph The Yoast schema graph array. * @return array */ public function inject_into_yoast_schema( $graph ) { if ( ! is_singular() ) { return $graph; } // Abort if feature is manually disabled in Settings $settings = get_option( 'wp_agentic_writer_settings', array() ); if ( isset( $settings['enable_faq_schema'] ) && ! $settings['enable_faq_schema'] ) { return $graph; } $post_id = get_the_ID(); // Fetch our pre-computed schema $schema_json = get_post_meta( $post_id, '_wpaw_faq_schema', true ); if ( ! empty( $schema_json ) ) { $schema_array = json_decode( $schema_json, true ); if ( is_array( $schema_array ) ) { // Yoast specifically expects nodes with an @id property $schema_array['@id'] = get_permalink( $post_id ) . '#wpaw-faq'; // Yoast wraps all schemas in a global @context, so we can unset our local one to remain clean if ( isset( $schema_array['@context'] ) ) { unset( $schema_array['@context'] ); } // Append our FAQ schema snippet to Yoast's massive graph $graph[] = $schema_array; } } return $graph; } /** * Inject the FAQ schema directly into RankMath's JSON-LD output. * This prevents disjointed schema and consolidates everything into RankMath's payload. * * @param array $data The RankMath JSON-LD data array. * @param object $jsonld The RankMath JsonLd object. * @return array */ public function inject_into_rankmath_schema( $data, $jsonld ) { if ( ! is_singular() ) { return $data; } // Abort if feature is manually disabled in Settings $settings = get_option( 'wp_agentic_writer_settings', array() ); if ( isset( $settings['enable_faq_schema'] ) && ! $settings['enable_faq_schema'] ) { return $data; } $post_id = get_the_ID(); // Fetch our pre-computed schema $schema_json = get_post_meta( $post_id, '_wpaw_faq_schema', true ); if ( ! empty( $schema_json ) ) { $schema_array = json_decode( $schema_json, true ); if ( is_array( $schema_array ) && ! empty( $schema_array['mainEntity'] ) ) { // RankMath expects a keyed array for each schema type $data['WPAWFaqPage'] = array( '@type' => 'FAQPage', 'mainEntity' => $schema_array['mainEntity'], ); } } return $data; } }