Content Gap Finder
Identifies topics users search for that lack matching content or have weak results.
Function signature
ogFindContentGaps(search_terms = array(), options = array())
Categories
- Search and Discovery
Parameters
search_termsSearch term rows or strings.optionsOptional keys: content_index, minimum_searches. Recognized keys: `content_index`, `minimum_searches`.Return value
Short public-safe status message.
- gaps
- gap_count
Compatibility
Existing function name and call order preserved; metadata signature corrected to source.
Minimum PHP version: 7.4
Security notes
Validate request method, identity, permissions, and caller-owned allowlists before use; keep secrets and internal paths out of public output.
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Identifies topics users search for that lack matching content or have weak results.
*
* @param array $search_terms Search term rows or strings.
* @param array $options Optional keys: content_index, minimum_searches.
* @return array Gap report for editorial review.
*/
function ogFindContentGaps($search_terms = array(), $options = array()) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
if (!is_array($search_terms)) {
$result['message'] = 'Search terms must be an array.';
return $result;
}
if (!is_array($options)) {
$options = array();
}
$content_index = array();
if (!empty($options['content_index']) && is_array($options['content_index'])) {
$content_index = $options['content_index'];
}
$minimum_searches = 1;
if (!empty($options['minimum_searches'])) {
$minimum_searches = (int)$options['minimum_searches'];
}
$normalized_content = array();
foreach ($content_index as $content) {
$normalized_content[] = strtolower(strip_tags((string)$content));
}
$gaps = array();
foreach ($search_terms as $row) {
$term = '';
$searches = 1;
if (is_array($row)) {
if (!empty($row['term'])) {
$term = trim((string)$row['term']);
}
if (!empty($row['searches']) && is_numeric($row['searches'])) {
$searches = (int)$row['searches'];
}
} else {
$term = trim((string)$row);
}
if (strlen($term) < 2 || $searches < $minimum_searches) {
continue;
}
$needle = strtolower($term);
$matches = 0;
foreach ($normalized_content as $content) {
if (strpos($content, $needle) !== false) {
$matches++;
}
}
if ($matches == 0) {
$gaps[] = array(
'term' => $term,
'searches' => $searches,
'matches' => 0,
'recommendation' => 'Review for new content or better internal search coverage.'
);
}
}
$result['success'] = true;
$result['message'] = 'Content gaps identified.';
$result['data'] = array('gaps' => $gaps, 'gap_count' => count($gaps));
return $result;
}