Slug Collision Resolver
Adds numeric suffixes or ID prefixes when a generated slug already exists.
Function signature
ogResolveSlugCollision(base_slug, existing_slugs = array(), max_attempts = 100, options = array())
Categories
- SEO and Routing
Parameters
base_slugProposed slug.existing_slugsExisting slug values to avoid.max_attemptsMaximum suffix attempts.optionsOptional prefix and suffix_start values. Recognized keys: `prefix`, `suffix_start`.Return value
Short public-safe status message.
- slug
- attempts
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.
*/
/**
* Adds numeric suffixes or ID prefixes when a generated slug already exists.
*
* Primary use case: Database-backed SEO routing.
* Typical inputs: base slug, lookup callback, max attempts.
* Typical output: unique slug.
*
* Implementation note: Use indexed lookup fields for performance.
*
* @param string $base_slug Proposed slug.
* @param array $existing_slugs Existing slug values to avoid.
* @param int $max_attempts Maximum suffix attempts.
* @param array $options Optional prefix and suffix_start values.
* @return array Structured slug collision result.
*/
function ogResolveSlugCollision($base_slug, $existing_slugs = array(), $max_attempts = 100, $options = array()) {
$result = array(
'success' => false,
'message' => '',
'data' => array('slug' => '')
);
$base_slug = trim((string)$base_slug);
if (!is_array($existing_slugs)) {
$existing_slugs = array();
}
$max_attempts = (int)$max_attempts;
if (!is_array($options)) {
$options = array();
}
if (empty($base_slug)) {
$result['message'] = 'Missing base slug.';
return $result;
}
if ($max_attempts < 1) {
$max_attempts = 1;
}
$normalized_existing = array();
foreach ($existing_slugs as $existing_slug) {
$existing_slug = strtolower(trim((string)$existing_slug));
if (!empty($existing_slug)) {
$normalized_existing[$existing_slug] = true;
}
}
$prefix = '';
if (!empty($options['prefix'])) {
$prefix = trim((string)$options['prefix'], '-') . '-';
}
$candidate = $prefix . $base_slug;
if (empty($normalized_existing[strtolower($candidate)])) {
$result['success'] = true;
$result['message'] = 'Slug did not collide.';
$result['data'] = array('slug' => $candidate, 'attempts' => 0);
return $result;
}
$suffix_start = 2;
if (!empty($options['suffix_start'])) {
$suffix_start = (int)$options['suffix_start'];
}
if ($suffix_start < 2) {
$suffix_start = 2;
}
for ($attempt = 0; $attempt < $max_attempts; $attempt++) {
$suffix = $suffix_start + $attempt;
$candidate = $prefix . $base_slug . '-' . $suffix;
if (empty($normalized_existing[strtolower($candidate)])) {
$result['success'] = true;
$result['message'] = 'Unique slug resolved.';
$result['data'] = array('slug' => $candidate, 'attempts' => $attempt + 1);
return $result;
}
}
$result['message'] = 'Unable to resolve a unique slug within the attempt limit.';
return $result;
}