Ryanhub - file viewer
filename: config.php
branch: main
back to repo
<?php

// Basic configuration and bootstrap

declare(strict_types=1);

session_start();

/**
 * Very small .env loader.
 *
 * Loads key=value pairs from a .env file in the project root
 * into getenv()/$_ENV (lines starting with # are comments).
 */
function load_env(string $path): void
{
    if (!is_readable($path)) {
        return;
    }

    $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    if ($lines === false) {
        return;
    }

    foreach ($lines as $line) {
        $line = trim($line);
        if ($line === '' || str_starts_with($line, '#')) {
            continue;
        }
        $pos = strpos($line, '=');
        if ($pos === false) {
            continue;
        }
        $name = trim(substr($line, 0, $pos));
        $value = trim(substr($line, $pos + 1));

        // Strip optional surrounding quotes
        if ((str_starts_with($value, '"') && str_ends_with($value, '"')) ||
            (str_starts_with($value, "'") && str_ends_with($value, "'"))) {
            $value = substr($value, 1, -1);
        }

        if ($name !== '') {
            putenv($name . '=' . $value);
            $_ENV[$name] = $value;
        }
    }
}

// Load .env from the project root (same dir as this file)
load_env(__DIR__ . '/.env');

// Application timezone: default to US Eastern if not explicitly set.
// This ensures all "today"/"now" calculations and formatted dates
// are anchored to East Coast time across the entire site.
$appTimezone = getenv('APP_TIMEZONE') ?: 'America/New_York';
date_default_timezone_set($appTimezone);

// Database configuration from environment, with sensible defaults.
$dbHost = getenv('DB_HOST');
$dbName = getenv('DB_NAME');
$dbUser = getenv('DB_USER');
$dbPass = getenv('DB_PASS');

// Optional app-level config from env
$appEnv = getenv('APP_ENV') ?: 'production';
$debug  = filter_var(getenv('APP_DEBUG') ?: '0', FILTER_VALIDATE_BOOL);

// In debug mode, show mysqli warnings as exceptions
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

try {
    $db = new mysqli($dbHost, $dbUser, $dbPass, $dbName);
    $db->set_charset('utf8mb4');
} catch (mysqli_sql_exception $e) {
    http_response_code(500);
    if ($debug) {
        echo 'Database connection error: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
    } else {
        echo 'Database connection error.';
    }
    exit;
}

// Base path helper (for running under a subdirectory such as /7oclockdinner)
// APP_BASE_PATH can be set in .env, otherwise it falls back to the directory name.
$envBase = getenv('APP_BASE_PATH') ?: '/7oclockdinner';
define('APP_BASE_PATH', rtrim($envBase, '/'));

function url(string $path = ''): string
{
    $base = APP_BASE_PATH === '' ? '' : APP_BASE_PATH;
    $path = '/' . ltrim($path, '/');
    return $base . $path;
}

// Simple helper to get the current path (without query string),
// normalised and with the APP_BASE_PATH prefix stripped.
function current_path(): string
{
    $uri = $_SERVER['REQUEST_URI'] ?? '/';
    $uri = strtok($uri, '?') ?: '/';

    // Normalise multiple slashes
    $uri = '/' . trim(preg_replace('#/+#', '/', $uri), '/');
    if ($uri === '//') {
        $uri = '/';
    }

    // Strip base path prefix if present (e.g. /7oclockdinner)
    if (APP_BASE_PATH !== '' && APP_BASE_PATH !== '/') {
        $base = APP_BASE_PATH;
        // Ensure base starts with a single leading slash
        if ($base[0] !== '/') {
            $base = '/' . $base;
        }
        $base = rtrim($base, '/');

        if ($uri === $base || str_starts_with($uri, $base . '/')) {
            $uri = substr($uri, strlen($base));
            if ($uri === '' || $uri === false) {
                $uri = '/';
            }
        }
    }

    return $uri === '' ? '/' : $uri;
}

// Simple view renderer
function render_view(string $view, array $data = []): void
{
    // Expose the database connection to views
    global $db;

    extract($data, EXTR_SKIP);
    $viewFile = __DIR__ . '/views/' . $view . '.php';
    if (!is_file($viewFile)) {
        http_response_code(500);
        echo 'View not found.';
        return;
    }
    include __DIR__ . '/views/layout/header.php';
    include $viewFile;
    include __DIR__ . '/views/layout/footer.php';
}

// Simple visit logger
function log_visit(mysqli $db, string $path): void
{
    $ip = $_SERVER['REMOTE_ADDR'] ?? '';
    $ua = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 255);
    $userId = $_SESSION['user_id'] ?? null;
    $now = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');

    $stmt = $db->prepare('INSERT INTO visits (path, user_id, ip_address, user_agent, created_at) VALUES (?, ?, ?, ?, ?)');
    $stmt->bind_param('sisss', $path, $userId, $ip, $ua, $now);
    $stmt->execute();
}