prepare("SELECT activity_ids FROM selection_hashes WHERE hash = ?"); $lookup->execute([$candidate]); $owner = $lookup->fetchColumn(); if ($owner === false) { try { $pdo->prepare("INSERT INTO selection_hashes (hash, activity_ids) VALUES (?, ?)") ->execute([$candidate, $idsStr]); return $candidate; } catch (PDOException $e) { // 23000 = integrity constraint: a concurrent request claimed // this candidate between the SELECT and the INSERT. Re-read // and fall through to the ownership check below. if ($e->getCode() !== '23000') { throw $e; } $lookup->execute([$candidate]); $owner = $lookup->fetchColumn(); } } if ($owner === $idsStr) { return $candidate; // this selection already lives here } // Taken by a different selection – try one character longer. } throw new RuntimeException("Could not allocate a hash for selection [$idsStr]"); } // Sanitize input $raw = isset($_GET['aID']) && is_array($_GET['aID']) ? $_GET['aID'] : []; $ids = []; foreach ($raw as $v) { $v = (int)$v; if ($v > 0) $ids[] = $v; } $ids = array_unique($ids); sort($ids); if (empty($ids)) { header('Location: index.php?error=no_selection'); exit; } // Validate IDs exist in DB $placeholders = implode(',', array_fill(0, count($ids), '?')); $stmt = $pdo->prepare("SELECT activityID FROM activities WHERE activityID IN ($placeholders)"); $stmt->execute($ids); $validIds = $stmt->fetchAll(PDO::FETCH_COLUMN); if (empty($validIds)) { header('Location: index.php?error=invalid_selection'); exit; } sort($validIds); $idsStr = implode(',', $validIds); // Reuse the stored hash when this selection already has a usable one. $stmt = $pdo->prepare("SELECT hash FROM selection_hashes WHERE activity_ids = ?"); $stmt->execute([$idsStr]); $existing = $stmt->fetchColumn(); if ($existing !== false && preg_match('/^[a-f0-9]{' . HASH_MIN_LEN . ',' . HASH_MAX_LEN . '}$/', $existing)) { $hash = $existing; } else { if ($existing !== false) { // A leftover 64-character hash from before short links; packing.php no // longer accepts it, so drop the row and allocate a fresh short one. $pdo->prepare("DELETE FROM selection_hashes WHERE activity_ids = ?")->execute([$idsStr]); } $hash = allocate_selection_hash($pdo, $idsStr); } // Redirect to the shareable packing list URL header("Location: packing.php?h=" . urlencode($hash)); exit;