plugin = ai_seo_article_generator(); $this->init_hooks(); } private function init_hooks() { add_action('wp_head', array($this, 'inject_aeo_schema'), 5); add_filter('ai_seo_article_generator_schema_types', array($this, 'get_available_schema_types')); } /** * Main method to inject AEO-optimized schema markup */ public function inject_aeo_schema() { global $post; if (!is_single() || !$post || !get_post_meta($post->ID, 'ai_seo_article_generator_generated', true)) { return; } $content = get_post_field('post_content', $post->ID); $content_type = $this->detect_content_type($content); $schemas = array(); switch ($content_type) { case 'howto': $schemas[] = $this->generate_howto_schema($post->ID, $content); break; case 'qa': $schemas[] = $this->generate_qapage_schema($post->ID, $content); break; case 'review': $schemas[] = $this->generate_review_schema($post->ID, $content); break; case 'video': $schemas[] = $this->generate_video_schema($post->ID, $content); break; default: $schemas[] = $this->generate_enhanced_article_schema($post->ID, $content); break; } // Always add FAQ schema if Q&A patterns detected $faq_schema = $this->generate_enhanced_faq_schema($post->ID, $content); if ($faq_schema) { $schemas[] = $faq_schema; } // Output all schemas foreach ($schemas as $schema) { if ($schema) { echo '' . "\n"; } } } /** * Auto-detect content type based on content analysis */ public function detect_content_type($content) { $text = wp_strip_all_tags($content); // Hebrew and English patterns for different content types $patterns = array( 'howto' => array( 'hebrew' => '/(?:שלבים?|צעדים?|אופן|איך|כיצד|מדריך|הוראות|ביצוע)/ui', 'english' => '/(?:steps?|how\s+to|guide|instructions|tutorial|process)/ui' ), 'qa' => array( 'hebrew' => '/(?:שאלות?\s+ותשובות?|ש[:\-]|ת[:\-]|שאלה|תשובה)/ui', 'english' => '/(?:q[\s\:&]*a|question|answer|faq)/ui' ), 'review' => array( 'hebrew' => '/(?:ביקורת|סקירה|דירוג|הערכה|המלצה|כוכבים)/ui', 'english' => '/(?:review|rating|stars?|recommendation|evaluation)/ui' ), 'video' => array( 'hebrew' => '/(?:וידאו|סרטון|צפייה|צפו|רצח)/ui', 'english' => '/(?:video|watch|play|youtube|vimeo)/ui' ) ); $scores = array(); foreach ($patterns as $type => $pattern_set) { $scores[$type] = 0; foreach ($pattern_set as $lang => $pattern) { $matches = preg_match_all($pattern, $text); if ($matches) { $scores[$type] += $matches; } } } // Return the type with highest score, or 'article' as default $max_score = max($scores); if ($max_score >= 2) { return array_search($max_score, $scores); } return 'article'; } /** * Generate HowTo schema for step-by-step content */ public function generate_howto_schema($post_id, $content) { $post = get_post($post_id); // Extract steps from content $steps = $this->extract_steps($content); if (empty($steps)) { return false; } $schema = array( '@context' => 'https://schema.org', '@type' => 'HowTo', 'name' => get_the_title($post_id), 'description' => $this->get_post_excerpt($post_id), 'image' => $this->get_post_image($post_id), 'step' => array() ); foreach ($steps as $index => $step) { $schema['step'][] = array( '@type' => 'HowToStep', 'position' => $index + 1, 'name' => $step['title'], 'text' => $step['description'] ); } // Add estimated time if detectable $estimated_time = $this->extract_estimated_time($content); if ($estimated_time) { $schema['totalTime'] = $estimated_time; } // Add tools/supplies if mentioned $tools = $this->extract_tools_supplies($content); if (!empty($tools)) { $schema['tool'] = array_map(function($tool) { return array( '@type' => 'HowToTool', 'name' => $tool ); }, $tools); } return $schema; } /** * Generate QAPage schema for Q&A content */ public function generate_qapage_schema($post_id, $content) { $qa_pairs = $this->extract_qa_pairs($content); if (empty($qa_pairs)) { return false; } $schema = array( '@context' => 'https://schema.org', '@type' => 'QAPage', 'mainEntity' => array() ); foreach ($qa_pairs as $qa) { $schema['mainEntity'][] = array( '@type' => 'Question', 'name' => $qa['question'], 'acceptedAnswer' => array( '@type' => 'Answer', 'text' => $qa['answer'], 'author' => array( '@type' => 'Person', 'name' => get_the_author_meta('display_name', get_post($post_id)->post_author) ) ) ); } return $schema; } /** * Generate Review schema for review content */ public function generate_review_schema($post_id, $content) { $review_data = $this->extract_review_data($content); if (!$review_data) { return false; } $schema = array( '@context' => 'https://schema.org', '@type' => 'Review', 'itemReviewed' => array( '@type' => 'Thing', 'name' => $review_data['item_name'] ), 'author' => array( '@type' => 'Person', 'name' => get_the_author_meta('display_name', get_post($post_id)->post_author) ), 'reviewBody' => $review_data['review_text'], 'datePublished' => get_the_date('c', $post_id) ); if (isset($review_data['rating'])) { $schema['reviewRating'] = array( '@type' => 'Rating', 'ratingValue' => $review_data['rating'], 'bestRating' => $review_data['max_rating'] ?? 5 ); } return $schema; } /** * Generate VideoObject schema for video content */ public function generate_video_schema($post_id, $content) { $video_data = $this->extract_video_data($content); if (!$video_data) { return false; } $schema = array( '@context' => 'https://schema.org', '@type' => 'VideoObject', 'name' => get_the_title($post_id), 'description' => $this->get_post_excerpt($post_id), 'thumbnailUrl' => $this->get_post_image($post_id), 'uploadDate' => get_the_date('c', $post_id) ); if (isset($video_data['url'])) { $schema['contentUrl'] = $video_data['url']; } if (isset($video_data['duration'])) { $schema['duration'] = $video_data['duration']; } return $schema; } /** * Generate enhanced article schema with AEO optimizations */ public function generate_enhanced_article_schema($post_id, $content) { $post = get_post($post_id); $main_keyword = get_post_meta($post_id, 'ai_seo_article_generator_main_keyword', true); $sub_keywords = get_post_meta($post_id, 'ai_seo_article_generator_sub_keywords', true); $is_hebrew = get_post_meta($post_id, 'ai_seo_article_generator_is_hebrew', true); $schema = array( '@context' => 'https://schema.org', '@type' => 'Article', 'headline' => get_the_title($post_id), 'description' => $this->get_post_excerpt($post_id), 'author' => array( '@type' => 'Person', 'name' => get_the_author_meta('display_name', $post->post_author), 'url' => get_author_posts_url($post->post_author) ), 'publisher' => array( '@type' => 'Organization', 'name' => get_bloginfo('name'), 'url' => home_url(), 'logo' => array( '@type' => 'ImageObject', 'url' => get_site_icon_url(60) ) ), 'datePublished' => get_the_date('c', $post_id), 'dateModified' => get_the_modified_date('c', $post_id), 'mainEntityOfPage' => array( '@type' => 'WebPage', '@id' => get_permalink($post_id) ), 'image' => $this->get_post_image($post_id) ); // Add keywords if ($main_keyword && $sub_keywords) { $schema['keywords'] = $main_keyword . ', ' . $sub_keywords; } // Add language if ($is_hebrew) { $schema['inLanguage'] = 'he-IL'; } // Add article sections for better structure $headings = $this->extract_headings($content); if (!empty($headings)) { $schema['articleSection'] = array_map(function($h) { return $h['text']; }, $headings); } // Add word count for AEO $word_count = str_word_count(wp_strip_all_tags($content)); $schema['wordCount'] = $word_count; // Add citation-ready abstract $abstract = $this->generate_citation_abstract($content, $main_keyword); if ($abstract) { $schema['abstract'] = $abstract; } return $schema; } /** * Enhanced FAQ schema with better Hebrew pattern recognition */ public function generate_enhanced_faq_schema($post_id, $content) { $text = wp_strip_all_tags($content); // Enhanced Hebrew Q&A patterns $hebrew_patterns = array( '/(?:^|\n)(?:שאלה\s*[\d\)\.]*\s*[\:\-]?\s*)(.+?)\n(?:תשובה\s*[\d\)\.]*\s*[\:\-]?\s*)(.+?)(?=\n(?:שאלה|$))/uis', '/(?:^|\n)(?:ש\s*[\d\)\.]*\s*[\:\-]\s*)(.+?)\n(?:ת\s*[\d\)\.]*\s*[\:\-]\s*)(.+?)(?=\n(?:ש\s*[\d\)\.]*\s*[\:\-]|$))/uis', '/(?:מהו|מה זה|איך|כיצד|למה|מתי|איפה|מדוע)\s+(.+?)\?\s*\n(.+?)(?=\n(?:מהו|מה זה|איך|כיצד|למה|מתי|איפה|מדוע)|$)/uis' ); // English Q&A patterns $english_patterns = array( '/(?:^|\n)(?:Q\s*[\d\)\.]*\s*[\:\-]?\s*)(.+?)\n(?:A\s*[\d\)\.]*\s*[\:\-]?\s*)(.+?)(?=\n(?:Q\s*[\d\)\.]*\s*[\:\-]|$))/uis', '/(?:What|How|Why|When|Where|Which)\s+(.+?)\?\s*\n(.+?)(?=\n(?:What|How|Why|When|Where|Which)|$)/uis' ); $faqs = array(); // Process Hebrew patterns foreach ($hebrew_patterns as $pattern) { if (preg_match_all($pattern, $text, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $question = trim($match[1]); $answer = trim($match[2]); if (strlen($question) > 10 && strlen($answer) > 20) { $faqs[] = array( '@type' => 'Question', 'name' => $this->clean_hebrew_text($question), 'acceptedAnswer' => array( '@type' => 'Answer', 'text' => $this->clean_hebrew_text($answer), 'author' => array( '@type' => 'Person', 'name' => get_the_author_meta('display_name', get_post($post_id)->post_author) ) ) ); } } } } // Process English patterns foreach ($english_patterns as $pattern) { if (preg_match_all($pattern, $text, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $question = trim($match[1]); $answer = trim($match[2]); if (strlen($question) > 10 && strlen($answer) > 20) { $faqs[] = array( '@type' => 'Question', 'name' => $question, 'acceptedAnswer' => array( '@type' => 'Answer', 'text' => $answer, 'author' => array( '@type' => 'Person', 'name' => get_the_author_meta('display_name', get_post($post_id)->post_author) ) ) ); } } } } if (count($faqs) >= 2) { return array( '@context' => 'https://schema.org', '@type' => 'FAQPage', 'mainEntity' => $faqs ); } return false; } /** * Get available schema types for admin interface */ public function get_available_schema_types() { return array( 'article' => __('Article', 'ai-seo-article-generator'), 'howto' => __('How-To Guide', 'ai-seo-article-generator'), 'qa' => __('Q&A Page', 'ai-seo-article-generator'), 'review' => __('Review', 'ai-seo-article-generator'), 'video' => __('Video Content', 'ai-seo-article-generator'), 'faq' => __('FAQ Page', 'ai-seo-article-generator') ); } // Helper methods private function extract_steps($content) { $text = wp_strip_all_tags($content); $steps = array(); // Hebrew step patterns $hebrew_patterns = array( '/(?:שלב\s*[\d\)\.]+\s*[\:\-]?\s*)(.+?)(?:\n(.+?))?(?=\n(?:שלב\s*[\d\)\.]+|$))/uis', '/(?:צעד\s*[\d\)\.]+\s*[\:\-]?\s*)(.+?)(?:\n(.+?))?(?=\n(?:צעד\s*[\d\)\.]+|$))/uis' ); // English step patterns $english_patterns = array( '/(?:step\s*[\d\)\.]+\s*[\:\-]?\s*)(.+?)(?:\n(.+?))?(?=\n(?:step\s*[\d\)\.]+|$))/uis', '/(?:^\d+\.\s*)(.+?)(?:\n(.+?))?(?=\n(?:\d+\.|$))/uis' ); $all_patterns = array_merge($hebrew_patterns, $english_patterns); foreach ($all_patterns as $pattern) { if (preg_match_all($pattern, $text, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $title = trim($match[1]); $description = isset($match[2]) ? trim($match[2]) : ''; if (strlen($title) > 5) { $steps[] = array( 'title' => $title, 'description' => $description ?: $title ); } } break; // Use first matching pattern } } return $steps; } private function extract_qa_pairs($content) { $text = wp_strip_all_tags($content); $qa_pairs = array(); // Use enhanced FAQ extraction logic $temp_schema = $this->generate_enhanced_faq_schema(0, $content); if ($temp_schema && isset($temp_schema['mainEntity'])) { foreach ($temp_schema['mainEntity'] as $item) { $qa_pairs[] = array( 'question' => $item['name'], 'answer' => $item['acceptedAnswer']['text'] ); } } return $qa_pairs; } private function extract_review_data($content) { $text = wp_strip_all_tags($content); // Look for rating patterns $rating_patterns = array( '/(?:דירוג|ציון|כוכבים?)\s*[\:\-]?\s*(\d+(?:\.\d+)?)\s*(?:מתוך|\/)\s*(\d+)/ui', '/(?:rating|score|stars?)\s*[\:\-]?\s*(\d+(?:\.\d+)?)\s*(?:out\s+of|\/)\s*(\d+)/ui', '/(\d+(?:\.\d+)?)\s*(?:כוכבים?|stars?)/ui' ); $rating = null; $max_rating = 5; foreach ($rating_patterns as $pattern) { if (preg_match($pattern, $text, $matches)) { $rating = floatval($matches[1]); if (isset($matches[2])) { $max_rating = intval($matches[2]); } break; } } if ($rating === null) { return false; } // Extract item name (usually from title or first heading) $item_name = get_the_title(); return array( 'rating' => $rating, 'max_rating' => $max_rating, 'item_name' => $item_name, 'review_text' => substr($text, 0, 500) // First 500 chars as review excerpt ); } private function extract_video_data($content) { // Look for video URLs or embeds if (preg_match('/(?:youtube\.com\/watch\?v=|youtu\.be\/|vimeo\.com\/)([^\s&]+)/i', $content, $matches)) { return array( 'url' => $matches[0], 'duration' => null // Could be enhanced to extract duration ); } return false; } private function extract_estimated_time($content) { $text = wp_strip_all_tags($content); // Time patterns in Hebrew and English $time_patterns = array( '/(?:זמן\s+ביצוע|משך\s+זמן|לוקח)\s*[\:\-]?\s*(\d+)\s*(?:דקות?|שעות?)/ui', '/(?:takes?|duration|time)\s*[\:\-]?\s*(\d+)\s*(?:minutes?|hours?|mins?)/ui' ); foreach ($time_patterns as $pattern) { if (preg_match($pattern, $text, $matches)) { $time = intval($matches[1]); $unit = 'M'; // Default to minutes if (strpos(strtolower($matches[0]), 'hour') !== false || strpos($matches[0], 'שעה') !== false) { $unit = 'H'; } return 'PT' . $time . $unit; } } return null; } private function extract_tools_supplies($content) { $text = wp_strip_all_tags($content); $tools = array(); // Look for tool/supply lists $tool_patterns = array( '/(?:כלים|ציוד|חומרים)\s*[\:\-]?\s*(.+?)(?=\n\n|\n[א-ת]|\n[A-Z]|$)/uis', '/(?:tools?|supplies|materials?|equipment)\s*[\:\-]?\s*(.+?)(?=\n\n|\n[A-Z]|$)/uis' ); foreach ($tool_patterns as $pattern) { if (preg_match($pattern, $text, $matches)) { $tool_text = $matches[1]; // Split by common separators $items = preg_split('/[\n\-\*\d\.\)]+/', $tool_text); foreach ($items as $item) { $item = trim($item); if (strlen($item) > 3 && strlen($item) < 100) { $tools[] = $item; } } break; } } return array_slice($tools, 0, 10); // Limit to 10 tools } private function extract_headings($content) { $headings = array(); if (preg_match_all('/]*>(.+?)<\/h[1-6]>/i', $content, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $headings[] = array( 'level' => intval($match[1]), 'text' => wp_strip_all_tags($match[2]) ); } } return $headings; } private function generate_citation_abstract($content, $main_keyword) { $text = wp_strip_all_tags($content); $sentences = preg_split('/[.!?]+/', $text); // Find sentences containing the main keyword $relevant_sentences = array(); foreach ($sentences as $sentence) { $sentence = trim($sentence); if (strlen($sentence) > 20 && strlen($sentence) < 200) { if (stripos($sentence, $main_keyword) !== false) { $relevant_sentences[] = $sentence; } } } if (!empty($relevant_sentences)) { // Return the first relevant sentence as abstract (40-60 words ideal for AI citation) $abstract = $relevant_sentences[0]; $word_count = str_word_count($abstract); if ($word_count > 60) { $words = explode(' ', $abstract); $abstract = implode(' ', array_slice($words, 0, 55)) . '...'; } return $abstract; } return null; } private function get_post_excerpt($post_id) { $excerpt = get_the_excerpt($post_id); if (!$excerpt) { $content = get_post_field('post_content', $post_id); $excerpt = wp_trim_words(wp_strip_all_tags($content), 30); } return $excerpt; } private function get_post_image($post_id) { $image_url = get_the_post_thumbnail_url($post_id, 'large'); if ($image_url) { return array( '@type' => 'ImageObject', 'url' => $image_url, 'width' => 1200, 'height' => 630 ); } // Fallback to site icon $site_icon = get_site_icon_url(512); if ($site_icon) { return array( '@type' => 'ImageObject', 'url' => $site_icon, 'width' => 512, 'height' => 512 ); } return null; } private function clean_hebrew_text($text) { if (!$text) return ''; // Remove multiple backslashes and fix encoding issues $cleaned = $text; $cleaned = preg_replace('/```html\s*/', '', $cleaned); $cleaned = preg_replace('/```\s*/', '', $cleaned); $cleaned = preg_replace('/\\\\+\'/', "'", $cleaned); $cleaned = preg_replace('/\\\\+"/', '"', $cleaned); $cleaned = html_entity_decode($cleaned, ENT_QUOTES | ENT_HTML5, 'UTF-8'); $cleaned = stripslashes($cleaned); $cleaned = preg_replace('/\s+/', ' ', $cleaned); return trim($cleaned); } }