From 5d539c130f6b32c7611d3f8bee6a9d3743865b1f Mon Sep 17 00:00:00 2001 From: Tom Hempel Date: Wed, 27 May 2026 20:13:43 +0200 Subject: [PATCH] added qdb_normalize_stable_key function to create stable identifiers from arbitrary key strings --- common.php | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/common.php b/common.php index 2650bdf..9b489ee 100644 --- a/common.php +++ b/common.php @@ -441,12 +441,41 @@ function qdb_is_stable_key(string $s): bool { return $s !== '' && (bool)preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $s); } +/** + * Coerce an arbitrary key string into a stable identifier (letters, digits, underscores; + * must start with a letter). Used on import and API writes so bundles are not limited to + * pre-validated keys (e.g. numeric option labels "1", "42"). + */ +function qdb_normalize_stable_key(string $s): string { + $s = trim($s); + if ($s === '') { + return ''; + } + if (qdb_is_stable_key($s)) { + return $s; + } + $out = preg_replace('/[^a-zA-Z0-9_]+/', '_', $s); + $out = preg_replace('/_+/', '_', $out ?? ''); + $out = trim($out, '_'); + if ($out === '') { + return 'k_' . substr(hash('crc32b', $s), 0, 8); + } + if (!preg_match('/^[a-zA-Z]/', $out)) { + $out = 'k_' . $out; + } + return qdb_is_stable_key($out) ? $out : 'k_' . substr(hash('crc32b', $s), 0, 8); +} + function qdb_validate_stable_key(string $s, string $label = 'Key'): string { $s = trim($s); - if (!qdb_is_stable_key($s)) { - json_error('INVALID_FIELD', "$label must match [a-zA-Z][a-zA-Z0-9_]*", 400); + if ($s === '') { + json_error('INVALID_FIELD', "$label is required", 400); } - return $s; + $normalized = qdb_normalize_stable_key($s); + if (!qdb_is_stable_key($normalized)) { + json_error('INVALID_FIELD', "$label could not be normalized to a valid key", 400); + } + return $normalized; } /** Resolve app lookup key for a question row. */