Skip to content
← Back to Functions
Code

Slug Creation Pipeline

Creates a clean SEF slug from a title while preserving readability and uniqueness support.

Function signature

ogCreateSeoSlug(title, max_length = 90, options = array())

Categories

  • SEO and Routing

Parameters

titleSource title.max_lengthMaximum slug length.optionsOptional stop_words and fallback_slug values. Recognized keys: `fallback_slug`, `stop_words`.

Return value

Short public-safe status message.

  • slug

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

/**
 * Creates a clean SEF slug from a title while preserving readability and uniqueness support.
 *
 * Primary use case: SEO URLs for snippets, functions, scripts, articles.
 * Typical inputs: title string, max length, stop word options.
 * Typical output: slug string.
 *
 * Implementation note: Normalize Unicode carefully and avoid duplicate slugs.
 *
 * @param string $title Source title.
 * @param int $max_length Maximum slug length.
 * @param array $options Optional stop_words and fallback_slug values.
 * @return array Structured slug creation result.
 */
function ogCreateSeoSlug($title, $max_length = 90, $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array('slug' => '')
	);

	$title = trim((string)$title);
	$max_length = (int)$max_length;
	if (!is_array($options)) {
		$options = array();
	}

	if ($max_length < 20) {
		$max_length = 20;
	}
	if ($max_length > 180) {
		$max_length = 180;
	}
	if (empty($title)) {
		$result['message'] = 'Missing title.';
		return $result;
	}

	$source = $title;
	if (function_exists('iconv')) {
		$converted = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $source);
		if ($converted !== false) {
			$source = $converted;
		}
	}

	$slug = strtolower($source);
	$slug = preg_replace('/[^a-z0-9]+/i', '-', $slug);
	$slug = trim($slug, '-');
	$slug = preg_replace('/-+/', '-', $slug);

	if (!empty($options['stop_words']) && is_array($options['stop_words'])) {
		$parts = explode('-', $slug);
		$filtered = array();
		foreach ($parts as $part) {
			if (!in_array($part, $options['stop_words'], true)) {
				$filtered[] = $part;
			}
		}
		if (!empty($filtered)) {
			$slug = implode('-', $filtered);
		}
	}

	if (strlen($slug) > $max_length) {
		$slug = substr($slug, 0, $max_length);
		$slug = trim($slug, '-');
		$last_dash = strrpos($slug, '-');
		if ($last_dash !== false && $last_dash > 20) {
			$slug = substr($slug, 0, $last_dash);
		}
	}

	if (empty($slug)) {
		$slug = 'item';
		if (!empty($options['fallback_slug'])) {
			$slug = ogCreateSeoSlug($options['fallback_slug'], $max_length);
			$slug = $slug['data']['slug'];
		}
	}

	$result['success'] = true;
	$result['message'] = 'SEO slug created.';
	$result['data'] = array('slug' => $slug);

	return $result;
}