schema_factory = new Schema_Factory(); $this->schema_validator = new Schema_Validator(); } /** * Generate optimization recommendations for schema markup * PRESERVED: Maintains exact same optimization logic from original Schema_Generator * * @since 1.0.0 * * @param array $schema_data Schema markup to optimize * @param string $schema_type Schema type * @param array $content_data Original content data * @return array Optimization recommendations */ public function generate_optimization_recommendations(array $schema_data, string $schema_type, array $content_data = []): array { $recommendations = [ 'seo_score' => 0, 'completeness_score' => 0, 'rich_snippets_potential' => 0, 'recommendations' => [], 'warnings' => [], 'improvements' => [], 'missing_properties' => [], 'optimization_opportunities' => [] ]; // Get validation results first $validation = $this->schema_validator->validate_schema($schema_data); $recommendations['seo_score'] = $validation['score']; $recommendations['warnings'] = $validation['warnings']; // Calculate completeness score $recommendations['completeness_score'] = $this->calculate_completeness_score($schema_data, $schema_type); // Calculate rich snippets potential $recommendations['rich_snippets_potential'] = $this->calculate_rich_snippets_potential($schema_data, $schema_type); // Generate type-specific recommendations $recommendations = $this->add_type_specific_recommendations($schema_data, $schema_type, $recommendations); // Add general SEO recommendations $recommendations = $this->add_general_seo_recommendations($schema_data, $recommendations); // Identify missing properties $recommendations['missing_properties'] = $this->identify_missing_properties($schema_data, $schema_type); // Generate optimization opportunities $recommendations['optimization_opportunities'] = $this->generate_optimization_opportunities($schema_data, $schema_type, $content_data); return $recommendations; } /** * Calculate completeness score based on required and recommended properties * PRESERVED: Exact same calculation logic from original Schema_Generator * * @since 1.0.0 * * @param array $schema_data Schema markup * @param string $schema_type Schema type * @return int Completeness score (0-100) */ private function calculate_completeness_score(array $schema_data, string $schema_type): int { $spec = $this->schema_factory->get_schema_specification($schema_type); if (empty($spec)) { return 0; } $total_properties = count($spec['required']) + count($spec['recommended']); if ($total_properties === 0) { return 100; } $present_properties = 0; // Check required properties foreach ($spec['required'] as $required_prop) { if (isset($schema_data[$required_prop]) && !empty($schema_data[$required_prop])) { $present_properties++; } } // Check recommended properties foreach ($spec['recommended'] as $recommended_prop) { if (isset($schema_data[$recommended_prop]) && !empty($schema_data[$recommended_prop])) { $present_properties++; } } return (int) round(($present_properties / $total_properties) * 100); } /** * Calculate rich snippets potential score * PRESERVED: Exact same calculation logic from original Schema_Generator * * @since 1.0.0 * * @param array $schema_data Schema markup * @param string $schema_type Schema type * @return int Rich snippets potential score (0-100) */ private function calculate_rich_snippets_potential(array $schema_data, string $schema_type): int { $score = 0; // Base score for having structured data $score += 20; // Type-specific scoring switch ($schema_type) { case 'Article': case 'BlogPosting': if (isset($schema_data['headline'])) $score += 15; if (isset($schema_data['image'])) $score += 15; if (isset($schema_data['author'])) $score += 15; if (isset($schema_data['datePublished'])) $score += 15; if (isset($schema_data['publisher'])) $score += 20; break; case 'Product': if (isset($schema_data['name'])) $score += 15; if (isset($schema_data['image'])) $score += 15; if (isset($schema_data['offers'])) $score += 25; if (isset($schema_data['review']) || isset($schema_data['aggregateRating'])) $score += 25; if (isset($schema_data['brand'])) $score += 20; break; case 'Organization': case 'LocalBusiness': if (isset($schema_data['name'])) $score += 20; if (isset($schema_data['logo'])) $score += 20; if (isset($schema_data['address'])) $score += 20; if (isset($schema_data['telephone'])) $score += 20; if (isset($schema_data['openingHours'])) $score += 20; break; case 'FAQPage': if (isset($schema_data['mainEntity']) && is_array($schema_data['mainEntity'])) { $question_count = count($schema_data['mainEntity']); $score += min(60, $question_count * 15); // Max 60 points for questions } break; default: $score += 40; // Default scoring for other types break; } return min(100, $score); } /** * Add type-specific optimization recommendations * PRESERVED: Exact same recommendation logic from original Schema_Generator * * @since 1.0.0 * * @param array $schema_data Schema markup * @param string $schema_type Schema type * @param array $recommendations Current recommendations * @return array Updated recommendations */ private function add_type_specific_recommendations(array $schema_data, string $schema_type, array $recommendations): array { switch ($schema_type) { case 'Article': case 'BlogPosting': if (!isset($schema_data['image'])) { $recommendations['recommendations'][] = 'Add a featured image to improve article rich snippets appearance'; } if (!isset($schema_data['dateModified'])) { $recommendations['recommendations'][] = 'Add dateModified to show content freshness'; } if (!isset($schema_data['wordCount'])) { $recommendations['recommendations'][] = 'Add word count for better content analysis'; } break; case 'Product': if (!isset($schema_data['offers'])) { $recommendations['recommendations'][] = 'Add pricing information to enable product rich snippets'; } if (!isset($schema_data['review']) && !isset($schema_data['aggregateRating'])) { $recommendations['recommendations'][] = 'Add customer reviews or ratings to improve product credibility'; } if (!isset($schema_data['brand'])) { $recommendations['recommendations'][] = 'Add brand information to improve product visibility'; } if (!isset($schema_data['sku'])) { $recommendations['recommendations'][] = 'Add SKU for better product identification'; } break; case 'Organization': case 'LocalBusiness': if (!isset($schema_data['logo'])) { $recommendations['recommendations'][] = 'Add a logo to improve brand recognition in search results'; } if (!isset($schema_data['address'])) { $recommendations['recommendations'][] = 'Add business address for better local SEO'; } if (!isset($schema_data['telephone'])) { $recommendations['recommendations'][] = 'Add phone number for better local search visibility'; } if (!isset($schema_data['openingHours'])) { $recommendations['recommendations'][] = 'Add business hours for better local search results'; } break; case 'FAQPage': if (isset($schema_data['mainEntity']) && count($schema_data['mainEntity']) < 3) { $recommendations['recommendations'][] = 'Add more FAQ questions (minimum 3) for better rich snippets'; } break; case 'Event': if (!isset($schema_data['location'])) { $recommendations['recommendations'][] = 'Add event location for better event rich snippets'; } if (!isset($schema_data['startDate'])) { $recommendations['recommendations'][] = 'Add event start date for proper event markup'; } break; case 'HowTo': if (!isset($schema_data['step']) || count($schema_data['step']) < 3) { $recommendations['recommendations'][] = 'Add detailed steps (minimum 3) for better how-to rich snippets'; } if (!isset($schema_data['totalTime'])) { $recommendations['recommendations'][] = 'Add total time estimate for better user experience'; } break; } return $recommendations; } /** * Add general SEO recommendations * PRESERVED: Exact same recommendation logic from original Schema_Generator * * @since 1.0.0 * * @param array $schema_data Schema markup * @param array $recommendations Current recommendations * @return array Updated recommendations */ private function add_general_seo_recommendations(array $schema_data, array $recommendations): array { // Check for description if (!isset($schema_data['description'])) { $recommendations['recommendations'][] = 'Add a description to improve search result snippets'; } elseif (strlen($schema_data['description']) < 120) { $recommendations['improvements'][] = 'Consider expanding the description (current: ' . strlen($schema_data['description']) . ' characters, recommended: 120-160)'; } // Check for URL if (!isset($schema_data['url'])) { $recommendations['recommendations'][] = 'Add a URL to help search engines understand the content location'; } // Check for image if (!isset($schema_data['image'])) { $recommendations['recommendations'][] = 'Add an image to improve visual appeal in search results'; } // Check for name/title length if (isset($schema_data['name']) && strlen($schema_data['name']) > 60) { $recommendations['warnings'][] = 'Title/name is longer than 60 characters, may be truncated in search results'; } if (isset($schema_data['headline']) && strlen($schema_data['headline']) > 110) { $recommendations['warnings'][] = 'Headline is longer than 110 characters, may be truncated in search results'; } return $recommendations; } /** * Identify missing properties for schema type * PRESERVED: Exact same identification logic from original Schema_Generator * * @since 1.0.0 * * @param array $schema_data Schema markup * @param string $schema_type Schema type * @return array Missing properties categorized by importance */ private function identify_missing_properties(array $schema_data, string $schema_type): array { $spec = $this->schema_factory->get_schema_specification($schema_type); $missing = [ 'required' => [], 'recommended' => [], 'optional' => [] ]; if (empty($spec)) { return $missing; } // Check required properties foreach ($spec['required'] as $required_prop) { if (!isset($schema_data[$required_prop]) || empty($schema_data[$required_prop])) { $missing['required'][] = $required_prop; } } // Check recommended properties foreach ($spec['recommended'] as $recommended_prop) { if (!isset($schema_data[$recommended_prop]) || empty($schema_data[$recommended_prop])) { $missing['recommended'][] = $recommended_prop; } } // Check optional properties (sample of most important ones) $important_optional = array_slice($spec['optional'], 0, 5); foreach ($important_optional as $optional_prop) { if (!isset($schema_data[$optional_prop]) || empty($schema_data[$optional_prop])) { $missing['optional'][] = $optional_prop; } } return $missing; } /** * Generate optimization opportunities based on content analysis * PRESERVED: Exact same opportunity generation logic from original Schema_Generator * * @since 1.0.0 * * @param array $schema_data Schema markup * @param string $schema_type Schema type * @param array $content_data Original content data * @return array Optimization opportunities */ private function generate_optimization_opportunities(array $schema_data, string $schema_type, array $content_data): array { $opportunities = []; // Content-based opportunities if (!empty($content_data['content'])) { $content = $content_data['content']; // Check for FAQ content that could be structured if ($schema_type !== 'FAQPage' && preg_match('/\b(faq|frequently asked|question)\b/i', $content)) { $opportunities[] = [ 'type' => 'schema_addition', 'title' => 'FAQ Schema Opportunity', 'description' => 'Content contains FAQ-style information that could benefit from FAQ schema markup', 'impact' => 'high' ]; } // Check for how-to content if ($schema_type !== 'HowTo' && preg_match('/\b(how to|step|instruction|tutorial)\b/i', $content)) { $opportunities[] = [ 'type' => 'schema_addition', 'title' => 'How-To Schema Opportunity', 'description' => 'Content contains instructional information that could benefit from HowTo schema markup', 'impact' => 'medium' ]; } // Check for product information if ($schema_type !== 'Product' && preg_match('/\b(price|buy|purchase|\$[0-9])/i', $content)) { $opportunities[] = [ 'type' => 'schema_addition', 'title' => 'Product Schema Opportunity', 'description' => 'Content contains product information that could benefit from Product schema markup', 'impact' => 'high' ]; } } // Schema enhancement opportunities if ($schema_type === 'Article' && !isset($schema_data['keywords'])) { $opportunities[] = [ 'type' => 'property_enhancement', 'title' => 'Add Keywords', 'description' => 'Adding relevant keywords can improve content categorization', 'impact' => 'low' ]; } if (in_array($schema_type, ['Product', 'LocalBusiness']) && !isset($schema_data['review'])) { $opportunities[] = [ 'type' => 'property_enhancement', 'title' => 'Add Customer Reviews', 'description' => 'Customer reviews can significantly improve click-through rates', 'impact' => 'high' ]; } return $opportunities; } }