Skip to content
← Back to Functions
Code

Keyword Extractor

Extracts likely keywords from title, description, and body using stop-word filtering.

Function signature

ogExtractContentKeywords(content_fields, options = array())

Categories

  • Search and Discovery

Parameters

content_fieldsAssociative content fields such as title, description, and body.optionsOptional stop_words, max_keywords, and min_length settings. Recognized keys: `max_keywords`, `min_length`, `stop_words`.

Return value

Short public-safe status message.

  • keywords
  • counts

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.
 */

/**
 * Extracts likely content keywords using simple token frequency scoring.
 *
 * This is an editorial helper, not a machine-learning classifier. It strips
 * tags, removes common stop words, and returns the strongest remaining terms.
 *
 * @param array $content_fields Associative content fields such as title, description, and body.
 * @param array $options Optional stop_words, max_keywords, and min_length settings.
 * @return array Structured result with keyword list and token counts.
 */
function ogExtractContentKeywords($content_fields, $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($content_fields)) {
		$result['message'] = 'Content fields must be an array.';
		return $result;
	}

	if (!is_array($options)) {
		$options = array();
	}

	$max_keywords = 12;
	if (!empty($options['max_keywords'])) {
		$max_keywords = (int)$options['max_keywords'];
	}
	if ($max_keywords < 1) {
		$max_keywords = 12;
	}

	$min_length = 3;
	if (!empty($options['min_length'])) {
		$min_length = (int)$options['min_length'];
	}

	$stop_words = array('the', 'and', 'for', 'with', 'that', 'this', 'from', 'into', 'your', 'you', 'are', 'was', 'were', 'have', 'has', 'php');
	if (!empty($options['stop_words']) && is_array($options['stop_words'])) {
		$stop_words = array_merge($stop_words, $options['stop_words']);
	}

	$text = '';
	foreach ($content_fields as $field_value) {
		if (is_scalar($field_value)) {
			$text .= ' ' . strip_tags((string)$field_value);
		}
	}

	$text = strtolower(html_entity_decode($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'));
	$tokens = preg_split('/[^a-z0-9]+/i', $text);
	$counts = array();

	foreach ($tokens as $token) {
		$token = trim($token);
		if (strlen($token) < $min_length) {
			continue;
		}
		if (in_array($token, $stop_words, true)) {
			continue;
		}
		if (!array_key_exists($token, $counts)) {
			$counts[$token] = 0;
		}
		$counts[$token]++;
	}

	arsort($counts);
	$keywords = array_slice(array_keys($counts), 0, $max_keywords);

	$result['success'] = true;
	$result['message'] = 'Content keywords extracted.';
	$result['data'] = array(
		'keywords' => $keywords,
		'counts' => array_slice($counts, 0, $max_keywords, true)
	);

	return $result;
}