/* ---------- Tags and trending (shared by the site and the admin) ---------- */ if (!defined('CONTENT_STOPWORDS')) { define('CONTENT_STOPWORDS', [ 'about','above','after','again','against','also','another','around','because','been','before','being', 'below','between','both','could','does','doing','down','during','each','even','ever','every','from', 'have','here','hers','herself','into','itself','just','like','made','make','many','more','most','much', 'must','never','only','other','ours','over','same','should','since','some','such','take','than','that', 'their','theirs','them','then','there','these','they','this','those','through','under','until','upon', 'very','were','what','when','where','which','while','will','with','within','would','your','yours','work', 'works','year','years','still','about','page','click','read','more','view','show','shows','first','last', ]); } /** Split a comma list of tags: trimmed, no '#', no empties, no case-insensitive duplicates. */ function content_tag_list(string $raw): array { $out = []; $seen = []; foreach (explode(',', $raw) as $t) { $t = trim(ltrim(trim($t), '#')); if ($t === '') { continue; } $k = mb_strtolower($t); if (isset($seen[$k])) { continue; } $seen[$k] = true; $out[] = $t; } return $out; } /** Words that best describe a post: title counts 3x, subtitle 2x, body 1x. */ function content_detect_tags(string $title, string $subtitle, string $html, int $want): array { $counts = []; $add = function (string $text, int $weight) use (&$counts): void { preg_match_all('/\p{L}{4,}/u', $text, $m); foreach ($m[0] as $word) { if (in_array($word, CONTENT_STOPWORDS, true)) { continue; } $counts[$word] = ($counts[$word] ?? 0) + $weight; } }; $add(mb_strtolower($title), 3); $add(mb_strtolower($subtitle), 2); $add(mb_strtolower(strip_tags($html)), 1); arsort($counts); $out = []; foreach (array_keys($counts) as $word) { $out[] = mb_convert_case($word, MB_CASE_TITLE, 'UTF-8'); if (count($out) >= $want) { break; } } return $out; } /** * Final tag string for a post: the tags the editor typed come first (max 3). * If fewer than 2 were typed, detected words fill up to 3 total. Returns "a, b, c" or ''. */ function content_tags_build(array $manual, string $title, string $subtitle, string $html): string { $tags = array_slice(content_tag_list(implode(',', $manual)), 0, 3); if (count($tags) < 2) { $have = array_map('mb_strtolower', $tags); foreach (content_detect_tags($title, $subtitle, $html, 6) as $cand) { if (count($tags) >= 3) { break; } if (!in_array(mb_strtolower($cand), $have, true)) { $tags[] = $cand; $have[] = mb_strtolower($cand); } } } return implode(', ', $tags); } /** Trending: views and likes, with a time decay so new posts can rise. Works on a content row. */ function content_trend_score(array $r): float { $views = (float)($r['views'] ?? 0); $likes = (float)($r['like'] ?? 0); $ts = strtotime((string)($r['date'] ?? '')) ?: time(); $days = max(0.0, (time() - $ts) / 86400); return ($views + 3 * $likes + 1) / pow($days + 2, 1.5); }
Sign in to continue
Continue with Google Continue with Discord Continue with FacebookDon't have an account? Sign up
Loading…