126 lines
3.5 KiB
PHP
126 lines
3.5 KiB
PHP
<?php
|
|
|
|
function visitor_ideas_dir(): string {
|
|
return getenv('WEBSITE_IDEAS_WRITE_DIR') ?: sys_get_temp_dir() . '/website-ideas';
|
|
}
|
|
|
|
function visitor_ideas_file(): string {
|
|
return visitor_ideas_dir() . '/ideas.jsonl';
|
|
}
|
|
|
|
function visitor_ideas_rate_file(): string {
|
|
return visitor_ideas_dir() . '/rate.json';
|
|
}
|
|
|
|
function visitor_ideas_ensure_dir(): bool {
|
|
$dir = visitor_ideas_dir();
|
|
return is_dir($dir) || mkdir($dir, 0750, true);
|
|
}
|
|
|
|
function visitor_idea_clean(string $value, int $maxLength): string {
|
|
$value = preg_replace('/<\s*(script|style)\b[^>]*>.*?<\s*\/\s*\1\s*>/is', '', $value) ?? $value;
|
|
$value = strip_tags($value);
|
|
$value = str_replace(["\r\n", "\r"], "\n", $value);
|
|
$value = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $value) ?? '';
|
|
$value = preg_replace('/[ \t]+/u', ' ', $value) ?? $value;
|
|
$value = preg_replace("/\n{3,}/", "\n\n", $value) ?? $value;
|
|
$value = trim($value);
|
|
if (strlen($value) > $maxLength) {
|
|
$value = substr($value, 0, $maxLength);
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
function visitor_ideas_recently_submitted(int $seconds = 45): bool {
|
|
if (!visitor_ideas_ensure_dir()) {
|
|
return true;
|
|
}
|
|
|
|
$file = visitor_ideas_rate_file();
|
|
$key = hash('sha256', $_SERVER['REMOTE_ADDR'] ?? 'unknown');
|
|
$now = time();
|
|
$handle = fopen($file, 'c+');
|
|
if (!$handle) {
|
|
return true;
|
|
}
|
|
|
|
flock($handle, LOCK_EX);
|
|
$raw = stream_get_contents($handle);
|
|
$rates = json_decode($raw ?: '{}', true);
|
|
if (!is_array($rates)) {
|
|
$rates = [];
|
|
}
|
|
|
|
foreach ($rates as $rateKey => $timestamp) {
|
|
if (!is_int($timestamp) && !ctype_digit((string) $timestamp)) {
|
|
unset($rates[$rateKey]);
|
|
continue;
|
|
}
|
|
if ($now - (int) $timestamp > 86400) {
|
|
unset($rates[$rateKey]);
|
|
}
|
|
}
|
|
|
|
$last = (int) ($rates[$key] ?? 0);
|
|
$blocked = $now - $last < $seconds;
|
|
if (!$blocked) {
|
|
$rates[$key] = $now;
|
|
rewind($handle);
|
|
ftruncate($handle, 0);
|
|
fwrite($handle, json_encode($rates, JSON_UNESCAPED_SLASHES));
|
|
}
|
|
fflush($handle);
|
|
flock($handle, LOCK_UN);
|
|
fclose($handle);
|
|
|
|
return $blocked;
|
|
}
|
|
|
|
function visitor_ideas_append(string $name, string $idea): bool {
|
|
if (!visitor_ideas_ensure_dir()) {
|
|
return false;
|
|
}
|
|
|
|
$entry = [
|
|
'name' => $name !== '' ? $name : 'Anonymous visitor',
|
|
'idea' => $idea,
|
|
'created_at' => gmdate('c'),
|
|
];
|
|
|
|
return file_put_contents(
|
|
visitor_ideas_file(),
|
|
json_encode($entry, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n",
|
|
FILE_APPEND | LOCK_EX
|
|
) !== false;
|
|
}
|
|
|
|
function visitor_ideas_read(int $limit = 6): array {
|
|
$file = visitor_ideas_file();
|
|
if (!is_readable($file)) {
|
|
return [];
|
|
}
|
|
|
|
$lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
if (!$lines) {
|
|
return [];
|
|
}
|
|
|
|
$ideas = [];
|
|
foreach (array_reverse($lines) as $line) {
|
|
$entry = json_decode($line, true);
|
|
if (!is_array($entry) || !isset($entry['idea'], $entry['name'], $entry['created_at'])) {
|
|
continue;
|
|
}
|
|
$ideas[] = [
|
|
'name' => visitor_idea_clean((string) $entry['name'], 80),
|
|
'idea' => visitor_idea_clean((string) $entry['idea'], 600),
|
|
'created_at' => visitor_idea_clean((string) $entry['created_at'], 40),
|
|
];
|
|
if (count($ideas) >= $limit) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $ideas;
|
|
}
|