- new blog cms

This commit is contained in:
Ty Clifford
2026-07-04 19:41:44 -04:00
parent 64cb76a846
commit 782b983bf4
819 changed files with 3103 additions and 88735 deletions
+125
View File
@@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace NeonBlog;
final class Stats
{
private string $path;
private Config $config;
public function __construct(string $path, Config $config)
{
$this->path = $path;
$this->config = $config;
}
public function record(string $routeType, string $slug, int $statusCode): void
{
if (!$this->config->get('stats.enabled', true) || PHP_SAPI === 'cli') {
return;
}
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'GET') {
return;
}
$dir = dirname($this->path);
if (!is_dir($dir)) {
mkdir($dir, 0775, true);
}
$handle = fopen($this->path, 'ab');
if (!$handle) {
return;
}
flock($handle, LOCK_EX);
clearstatcache(true, $this->path);
if ((filesize($this->path) ?: 0) === 0) {
fputcsv($handle, [
'date',
'time',
'request_id',
'ip_hash',
'method',
'path',
'route_type',
'slug',
'query',
'referrer',
'user_agent',
'status_code',
], ',', '"', '', "\n");
}
$query = parse_url((string) ($_SERVER['REQUEST_URI'] ?? ''), PHP_URL_QUERY) ?: '';
fputcsv($handle, [
date('Y-m-d'),
date('H:i:s'),
bin2hex(random_bytes(6)),
$this->ipHash(),
$_SERVER['REQUEST_METHOD'] ?? 'GET',
parse_url((string) ($_SERVER['REQUEST_URI'] ?? '/'), PHP_URL_PATH) ?: '/',
$routeType,
$slug,
$query,
$_SERVER['HTTP_REFERER'] ?? '',
substr((string) ($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 300),
$statusCode,
], ',', '"', '', "\n");
flock($handle, LOCK_UN);
fclose($handle);
}
/** @return array<string, mixed> */
public function summary(): array
{
if (!is_file($this->path)) {
return [
'visits' => 0,
'routes' => [],
'top_paths' => [],
];
}
$handle = fopen($this->path, 'rb');
if (!$handle) {
return ['visits' => 0, 'routes' => [], 'top_paths' => []];
}
$header = fgetcsv($handle, null, ',', '"', '') ?: [];
$visits = 0;
$routes = [];
$paths = [];
while (($row = fgetcsv($handle, null, ',', '"', '')) !== false) {
if ($row === $header) {
continue;
}
$record = array_combine($header, $row);
if (!is_array($record)) {
continue;
}
$visits++;
$routes[$record['route_type']] = ($routes[$record['route_type']] ?? 0) + 1;
$paths[$record['path']] = ($paths[$record['path']] ?? 0) + 1;
}
fclose($handle);
arsort($routes);
arsort($paths);
return [
'visits' => $visits,
'routes' => $routes,
'top_paths' => array_slice($paths, 0, 10, true),
];
}
private function ipHash(): string
{
$ip = (string) ($_SERVER['REMOTE_ADDR'] ?? 'cli');
$salt = (string) $this->config->get('stats.hash_salt', 'change-me');
return hash_hmac('sha256', $ip, $salt);
}
}