array( 'what is', 'what are', 'definition', 'meaning', 'overview' ), 'process' => array( 'how to', 'how does', 'steps', 'process', 'guide', 'tutorial' ), 'benefits' => array( 'benefits', 'advantages', 'pros', 'why use', 'reasons' ), 'conclusion' => array( 'conclusion', 'summary', 'final thoughts', 'takeaway', 'in summary' ), ); /** * Analyze content structure and return guidance. * * @param int $post_id Post ID. * @return array Analysis results. */ public static function analyze_content( $post_id ) { $post = get_post( $post_id ); if ( ! $post ) { return array(); } // Parse Gutenberg blocks $blocks = parse_blocks( $post->post_content ); $flat_blocks = self::flatten_blocks( $blocks ); // Run all analyses $results = array( 'heading_structure' => self::analyze_heading_structure( $flat_blocks ), 'ai_questions' => self::analyze_ai_questions( $flat_blocks ), 'answer_placement' => self::analyze_answer_placement( $flat_blocks ), 'first_paragraph' => self::analyze_first_paragraph( $flat_blocks ), 'section_coverage' => self::analyze_section_coverage( $flat_blocks ), ); // Allow filtering return apply_filters( 'ai_answer_toolkit_content_guidance', $results, $post_id ); } /** * Flatten nested blocks (columns, groups, etc.). * * @param array $blocks Blocks array. * @return array Flattened blocks. */ private static function flatten_blocks( $blocks ) { $flat = array(); foreach ( $blocks as $block ) { if ( empty( $block['blockName'] ) ) { continue; } $flat[] = $block; // Recursively flatten inner blocks if ( ! empty( $block['innerBlocks'] ) ) { $flat = array_merge( $flat, self::flatten_blocks( $block['innerBlocks'] ) ); } } return $flat; } /** * Analyze heading structure. * * @param array $blocks Flattened blocks. * @return array Analysis results. */ private static function analyze_heading_structure( $blocks ) { $headings = array(); $issues = array(); // Extract all headings foreach ( $blocks as $block ) { if ( $block['blockName'] === 'core/heading' ) { $level = isset( $block['attrs']['level'] ) ? $block['attrs']['level'] : 2; $content = wp_strip_all_tags( $block['innerHTML'] ); $headings[] = array( 'level' => $level, 'content' => $content, ); } } // Check for exactly one H1 $h1_count = count( array_filter( $headings, function( $h ) { return $h['level'] === 1; } ) ); $h2_count = count( array_filter( $headings, function( $h ) { return $h['level'] === 2; } ) ); $has_h1_issue = false; $has_h2_issue = false; $has_hierarchy_issue = false; // Check H1 requirements if ( $h1_count === 0 ) { $has_h1_issue = true; } elseif ( $h1_count > 1 ) { $has_h1_issue = true; } // Check H2 usage if ( $h2_count === 0 && count( $headings ) > 1 ) { $has_h2_issue = true; } // Check heading hierarchy (no skipped levels) $prev_level = 0; foreach ( $headings as $heading ) { if ( $prev_level > 0 && $heading['level'] > $prev_level + 1 ) { $has_hierarchy_issue = true; break; } $prev_level = $heading['level']; } // Generate single message based on all checks if ( ! $has_h1_issue && ! $has_h2_issue && ! $has_hierarchy_issue && count( $headings ) > 0 ) { $issues[] = array( 'type' => 'success', 'message' => 'H1 and H2 headings are correctly structured.', 'help' => 'Your heading hierarchy is well-structured for AI engines.', 'tooltip' => 'AI engines use H1 to identify the main topic and H2 headings to understand key sections. Proper heading structure helps AI extract and organize information accurately. Keep one H1 at the top and use H2 for major sections.', ); } else { $error_parts = array(); if ( $h1_count === 0 ) { $error_parts[] = 'Missing H1 heading'; } elseif ( $h1_count > 1 ) { $error_parts[] = 'Multiple H1 headings detected'; } if ( $has_h2_issue ) { $error_parts[] = 'Missing H2 headings'; } if ( $has_hierarchy_issue ) { $error_parts[] = 'Heading order not correct'; } $message = ! empty( $error_parts ) ? implode( ' / ', $error_parts ) : 'Heading structure needs improvement'; $issues[] = array( 'type' => 'warning', 'message' => $message . '.', 'help' => 'Add one H1 and ensure logical H2 hierarchy.', 'tooltip' => 'Fix: Add exactly one H1 heading at the top of your content to define the main topic. Use H2 headings for major sections. Ensure heading levels are sequential (don\'t skip from H1 to H3). This structure helps AI engines understand your content hierarchy and extract information accurately.', ); } return array( 'headings' => $headings, 'h1_count' => $h1_count, 'h2_count' => $h2_count, 'issues' => $issues, ); } /** * Analyze AI-friendly questions in headings. * * @param array $blocks Flattened blocks. * @return array Analysis results. */ private static function analyze_ai_questions( $blocks ) { $questions = array(); $questions_with_answers = 0; $questions_without_answers = 0; $issues = array(); for ( $i = 0; $i < count( $blocks ); $i++ ) { $block = $blocks[ $i ]; if ( $block['blockName'] === 'core/heading' ) { $content = wp_strip_all_tags( $block['innerHTML'] ); $content_lower = strtolower( $content ); // Check if ends with question mark $is_question = substr( trim( $content ), -1 ) === '?'; // Check for AI triggers $has_trigger = false; foreach ( self::$question_triggers as $trigger ) { if ( strpos( $content_lower, $trigger ) !== false ) { $has_trigger = true; break; } } if ( $is_question || $has_trigger ) { // Check if there's a direct answer after this question $has_answer = false; if ( isset( $blocks[ $i + 1 ] ) ) { $next_block = $blocks[ $i + 1 ]; if ( $next_block['blockName'] === 'core/paragraph' ) { $answer = wp_strip_all_tags( $next_block['innerHTML'] ); if ( strlen( trim( $answer ) ) >= 30 ) { $has_answer = true; $questions_with_answers++; } else { $questions_without_answers++; } } else { $questions_without_answers++; } } else { $questions_without_answers++; } $questions[] = array( 'content' => $content, 'is_question' => $is_question, 'has_trigger' => $has_trigger, 'has_answer' => $has_answer, ); } } } $question_count = count( $questions ); if ( $question_count === 0 ) { $issues[] = array( 'type' => 'warning', 'message' => 'No AI-friendly questions found.', 'help' => 'Use question-style headings with direct answers.', 'tooltip' => 'AI engines prioritize content with question-style headings because they can extract direct answers. Use patterns like "What is...", "How to...", "Why should...", "When is...", "Where can...", "Which are...". Place a clear answer paragraph (30+ characters) immediately after each question heading.', ); } elseif ( $questions_without_answers > 0 ) { $issues[] = array( 'type' => 'warning', 'message' => sprintf( 'AI-friendly questions found but %d missing direct answers.', $questions_without_answers ), 'help' => 'Add answer paragraphs immediately after each question heading.', 'tooltip' => 'You have question-style headings, but some lack immediate answers. Place a paragraph (30+ characters) directly after each question heading. This helps AI engines extract and display your answers in search results and AI responses.', ); } else { $issues[] = array( 'type' => 'success', 'message' => 'AI-friendly questions detected and answered.', 'help' => sprintf( '%d question(s) with direct answers found.', $question_count ), 'tooltip' => 'Your content uses question-style headings with direct answers. This structure is ideal for AI engines to extract and display your content in answer boxes and AI-generated responses.', ); } return array( 'questions' => $questions, 'count' => $question_count, 'with_answers' => $questions_with_answers, 'without_answers' => $questions_without_answers, 'issues' => $issues, ); } /** * Analyze answer placement after questions. * * @param array $blocks Flattened blocks. * @return array Analysis results. */ private static function analyze_answer_placement( $blocks ) { $issues = array(); $valid_answers = 0; $missing_answers = 0; for ( $i = 0; $i < count( $blocks ); $i++ ) { $block = $blocks[ $i ]; // Check if this is a heading with a question if ( $block['blockName'] === 'core/heading' ) { $content = wp_strip_all_tags( $block['innerHTML'] ); $content_lower = strtolower( $content ); $is_question = substr( trim( $content ), -1 ) === '?'; $has_trigger = false; foreach ( self::$question_triggers as $trigger ) { if ( strpos( $content_lower, $trigger ) !== false ) { $has_trigger = true; break; } } if ( $is_question || $has_trigger ) { // Check next block if ( isset( $blocks[ $i + 1 ] ) ) { $next_block = $blocks[ $i + 1 ]; if ( $next_block['blockName'] === 'core/paragraph' ) { $answer = wp_strip_all_tags( $next_block['innerHTML'] ); if ( strlen( $answer ) >= 30 ) { $valid_answers++; } else { $missing_answers++; } } else { $missing_answers++; } } else { $missing_answers++; } } } } if ( $valid_answers > 0 && $missing_answers === 0 ) { $issues[] = array( 'type' => 'success', 'message' => 'Answer directly follows question heading', 'help' => 'All questions have immediate, substantial answers.', ); } elseif ( $missing_answers > 0 ) { $issues[] = array( 'type' => 'warning', 'message' => sprintf( 'No direct answer found after %d question(s)', $missing_answers ), 'help' => 'Place a paragraph (30+ characters) immediately after each question heading for AI extraction.', ); } return array( 'valid_answers' => $valid_answers, 'missing_answers' => $missing_answers, 'issues' => $issues, ); } /** * Analyze first paragraph optimization. * * @param array $blocks Flattened blocks. * @return array Analysis results. */ private static function analyze_first_paragraph( $blocks ) { $issues = array(); $first_paragraph = null; // Find first meaningful paragraph foreach ( $blocks as $block ) { if ( $block['blockName'] === 'core/paragraph' ) { $content = wp_strip_all_tags( $block['innerHTML'] ); $content = trim( $content ); if ( ! empty( $content ) ) { $first_paragraph = $content; break; } } } if ( ! $first_paragraph ) { $issues[] = array( 'type' => 'warning', 'message' => 'No introductory paragraph found or too short.', 'help' => 'Add a strong opening paragraph to introduce your topic.', 'tooltip' => 'AI engines use the first paragraph to understand your content\'s main topic and context. Write a clear, informative opening (40+ characters) that directly introduces your subject. Avoid filler phrases like "Welcome to" or "In this post". Start with valuable information that answers what your content is about.', ); } else { $length = strlen( $first_paragraph ); $content_lower = strtolower( $first_paragraph ); // Check for fluff words $fluff_words = array( 'welcome to', 'in this post', 'in this article', 'today we will', 'hello', 'hi there', 'greetings' ); $has_fluff = false; foreach ( $fluff_words as $fluff ) { if ( strpos( $content_lower, $fluff ) !== false ) { $has_fluff = true; break; } } if ( $length < 40 ) { $issues[] = array( 'type' => 'warning', 'message' => 'No introductory paragraph found or too short.', 'help' => 'Add a strong opening paragraph to introduce your topic.', 'tooltip' => 'Your first paragraph is too short (less than 40 characters). Expand it to provide clear context about your topic. AI engines rely on the opening paragraph to understand what your content covers. Make it informative and direct.', ); } elseif ( $has_fluff ) { $issues[] = array( 'type' => 'warning', 'message' => 'No introductory paragraph found or too short.', 'help' => 'Add a strong opening paragraph to introduce your topic.', 'tooltip' => 'Your first paragraph contains filler phrases like "Welcome to" or "In this post". AI engines prefer direct, valuable information. Rewrite your opening to immediately introduce your topic with substantive content. Example: Instead of "Welcome to our guide", start with "SEO optimization improves website visibility by..."', ); } else { $issues[] = array( 'type' => 'success', 'message' => 'First paragraph is strong and informative.', 'help' => 'Your opening paragraph provides clear context.', 'tooltip' => 'Your first paragraph is well-written and informative. It provides clear context without filler phrases, helping AI engines understand your content\'s main topic immediately. This improves how AI systems extract and present your content.', ); } } return array( 'first_paragraph' => $first_paragraph, 'issues' => $issues, ); } /** * Analyze content section coverage. * * @param array $blocks Flattened blocks. * @return array Analysis results. */ private static function analyze_section_coverage( $blocks ) { $found_sections = array( 'definition' => false, 'process' => false, 'benefits' => false, 'conclusion' => false, ); // Extract all text content $all_text = ''; foreach ( $blocks as $block ) { if ( in_array( $block['blockName'], array( 'core/heading', 'core/paragraph' ), true ) ) { $all_text .= ' ' . wp_strip_all_tags( $block['innerHTML'] ); } } $all_text_lower = strtolower( $all_text ); // Check for each section type foreach ( self::$section_keywords as $section => $keywords ) { foreach ( $keywords as $keyword ) { if ( strpos( $all_text_lower, $keyword ) !== false ) { $found_sections[ $section ] = true; break; } } } $section_tooltips = array( 'definition' => 'Definition section explains what your topic is. Include a clear explanation using headings like "What is [topic]" or "Definition". This helps AI engines provide accurate answers when users ask basic questions about your topic.', 'process' => 'Process section explains how something works or how to do it. Use headings like "How to [action]", "Steps", or "Process". This helps AI engines extract step-by-step instructions and procedural information.', 'benefits' => 'Benefits section explains why your topic matters. Include headings like "Benefits", "Advantages", or "Why use [topic]". This helps AI engines answer questions about value and importance.', 'conclusion' => 'Conclusion section summarizes key points. Use headings like "Conclusion", "Summary", or "Key Takeaways". This helps AI engines extract main points and provide comprehensive answers.', ); $issues = array(); $all_present = true; $missing_sections = array(); foreach ( $found_sections as $section => $found ) { if ( ! $found ) { $all_present = false; $missing_sections[] = ucfirst( $section ); } $section_name = ucfirst( $section ); $tooltip = isset( $section_tooltips[ $section ] ) ? $section_tooltips[ $section ] : ''; if ( $found ) { $issues[] = array( 'type' => 'success', 'section' => $section_name, 'found' => true, 'message' => $section_name . ' section present.', 'tooltip' => $tooltip, ); } else { $issues[] = array( 'type' => 'warning', 'section' => $section_name, 'found' => false, 'message' => $section_name . ' missing.', 'help' => 'Add ' . $section_name . ' to improve AI answer extraction.', 'tooltip' => $tooltip, ); } } return array( 'sections' => $found_sections, 'all_present' => $all_present, 'missing_sections' => $missing_sections, 'issues' => $issues, ); } }