Skip to content
← Back to Functions
Code

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;
}