PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.10.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.10.0
2.10.0 2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 All 51 releases
← All changes | includes/seo/class-content-optimization-manager.php +36 -19 2.3.0 → 2.10.0 View file →
@@ -33,19 +33,28 @@
33 33
34 34 /**
35 35 * Content optimization scoring weights (2025 SEO standards)
36 36 *
37 + * Semantic relevance used to sit here at 5%, fed by a block that returned
38 + * the same numbers for every input (#538). Removing a factor would have
39 + * capped the score at 95, so the remaining six were rescaled by 100/95 and
40 + * rounded to whole numbers, which preserves their former ratio
41 + * (25:20:15:15:10:10) and keeps the array at 100.
42 + *
43 + * Rounding left technical_seo and user_experience, which were equal at 10,
44 + * one point apart at 11 and 10. That is an artefact of landing the spare
45 + * point somewhere, not a judgement that technical SEO matters more.
46 + *
37 47 * @since 1.0.0
38 48 * @var array
39 49 */
40 50 private array $optimization_weights = [
41 - 'content_quality' => 25, // Content depth, uniqueness, value
42 - 'keyword_optimization' => 20, // Keyword usage and distribution
43 - 'readability' => 15, // Reading ease and comprehension
44 - 'structure' => 15, // Headings, paragraphs, formatting
45 - 'technical_seo' => 10, // Meta tags, URLs, schema
46 - 'user_experience' => 10, // Engagement signals, CTR optimization
47 - 'semantic_relevance' => 5 // Topic relevance and entity coverage
51 + 'content_quality' => 26, // Content depth, uniqueness, value
52 + 'keyword_optimization' => 21, // Keyword usage and distribution
53 + 'readability' => 16, // Reading ease and comprehension
54 + 'structure' => 16, // Headings, paragraphs, formatting
55 + 'technical_seo' => 11, // Meta tags, URLs, schema
56 + 'user_experience' => 10 // Engagement signals, CTR optimization
48 57 ];
49 58
50 59 /**
51 60 * SEO content templates for different content types
@@ -712,13 +721,8 @@
712 721 // User experience score (10% weight)
713 722 $ux_score = $this->calculate_user_experience_score($content_analysis, $template_compliance);
714 723 $scores['user_experience'] = $ux_score * ($this->optimization_weights['user_experience'] / 100);
715 724
716 - // Semantic relevance score (5% weight)
717 - if (!empty($content_analysis['semantic_analysis']['relevance_score'])) {
718 - $scores['semantic_relevance'] = $content_analysis['semantic_analysis']['relevance_score'] * ($this->optimization_weights['semantic_relevance'] / 100);
719 - }
720 -
721 725 return (int) round(array_sum($scores));
722 726 }
723 727
724 728 /**
@@ -1049,9 +1053,9 @@
1049 1053 // Get recent posts for blog homepage
1050 1054 $recent_posts = get_posts(['numberposts' => 3]);
1051 1055 $content_parts = [];
1052 1056 foreach ($recent_posts as $post) {
1053 - $content_parts[] = $post->post_title . ' ' . wp_trim_words($post->post_content, 100);
1057 + $content_parts[] = $post->post_title . ' ' . \ThinkRank\Core\Seo_Text::trim_words($post->post_content, 100, '...', 1000);
1054 1058 }
1055 1059 $content = implode(' ', $content_parts);
1056 1060 }
1057 1061 break;
@@ -1446,11 +1450,14 @@
1446 1450
1447 1451 return max(0, $score);
1448 1452 }
1449 1453
1450 - // Placeholder implementations for methods referenced but not yet implemented
1454 + // Placeholder implementations for methods referenced but not yet implemented.
1455 + // Anything here that cannot measure its subject returns null or an empty
1456 + // set, never a plausible-looking number: a consumer can branch on "not
1457 + // measured", but not on an 80 that was typed rather than computed (#538).
1451 1458 private function calculate_performance_metrics(array $content_analysis, array $template_compliance): array {
1452 - return ['performance_score' => 80, 'metrics' => []];
1459 + return ['performance_score' => null, 'metrics' => []];
1453 1460 }
1454 1461
1455 1462 private function generate_optimization_recommendations(array $content_analysis, array $template_compliance, string $content_type): array {
1456 1463 $recommendations = [];
@@ -1467,9 +1474,12 @@
1467 1474 return $recommendations;
1468 1475 }
1469 1476
1470 1477 private function identify_optimization_opportunities(array $content_analysis, array $template_compliance, string $content_type): array {
1471 - return ['opportunities' => [], 'potential_impact' => 'medium'];
1478 + // No potential_impact: with no opportunities found there is nothing to
1479 + // rate, and 'medium' was a verdict on an empty list. The sibling in
1480 + // Performance_Monitoring_Manager already returns just this shape.
1481 + return ['opportunities' => []];
1472 1482 }
1473 1483
1474 1484 private function generate_content_suggestions(array $content_analysis, string $content_type, array $keywords): array {
1475 1485 return ['suggestions' => [], 'content_ideas' => []];
@@ -1498,10 +1508,13 @@
1498 1508 private function calculate_improvement_rate(array $performance_trends): float {
1499 1509 return 0.0;
1500 1510 }
1501 1511
1502 - private function calculate_performance_score(array $metrics): int {
1503 - return 80;
1512 + private function calculate_performance_score(array $metrics): ?int {
1513 + // Nothing here scores $metrics, so there is no score to report. Null
1514 + // travels into track_performance()'s 'performance_score' and reads as
1515 + // "not measured"; an 80 read as a healthy page (#538).
1516 + return null;
1504 1517 }
1505 1518
1506 1519 private function compare_with_benchmarks(array $metrics, string $context_type): array {
1507 1520 return ['benchmark_comparison' => []];
@@ -1507,7 +1520,11 @@
1507 1520 return ['benchmark_comparison' => []];
1508 1521 }
1509 1522
1510 1523 private function store_performance_data(string $context_type, ?int $context_id, array $tracking): bool {
1511 - return true;
1524 + // Reports failure because it stores nothing. The single caller discards
1525 + // the return, so this changes no behaviour today, but a caller added
1526 + // later must not read "stored successfully" from a method with no
1527 + // storage in it (#538).
1528 + return false;
1512 1529 }
1513 1530 }