Files
tcms/app/Config.php
T
2026-07-05 16:23:17 -04:00

172 lines
4.8 KiB
PHP

<?php
declare(strict_types=1);
namespace NeonBlog;
final class Config
{
/** @var array<string, mixed> */
private array $data;
private string $path;
/** @param array<string, mixed> $data */
private function __construct(string $path, array $data)
{
$this->path = $path;
$this->data = array_replace_recursive(self::defaults(), $data);
}
public static function load(string $path): self
{
$data = [];
if (is_file($path)) {
$decoded = json_decode((string) file_get_contents($path), true);
if (is_array($decoded)) {
$data = $decoded;
}
}
return new self($path, $data);
}
/** @return mixed */
public function get(string $key, mixed $default = null): mixed
{
$segments = explode('.', $key);
$value = $this->data;
foreach ($segments as $segment) {
if (!is_array($value) || !array_key_exists($segment, $value)) {
return $default;
}
$value = $value[$segment];
}
return $value;
}
public function set(string $key, mixed $value): void
{
$segments = explode('.', $key);
$cursor = &$this->data;
foreach ($segments as $segment) {
if (!isset($cursor[$segment]) || !is_array($cursor[$segment])) {
$cursor[$segment] = [];
}
$cursor = &$cursor[$segment];
}
$cursor = $value;
}
/** @return array<string, mixed> */
public function all(): array
{
return $this->data;
}
public function save(): void
{
$dir = dirname($this->path);
if (!is_dir($dir)) {
mkdir($dir, 0775, true);
}
file_put_contents(
$this->path,
json_encode($this->data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL
);
}
public function baseUrl(): string
{
$configured = trim((string) $this->get('base_url', ''));
if ($configured !== '') {
$parts = parse_url($configured);
if (is_array($parts) && isset($parts['scheme'], $parts['host'])) {
$port = isset($parts['port']) ? ':' . $parts['port'] : '';
return $parts['scheme'] . '://' . $parts['host'] . $port;
}
return rtrim($configured, '/');
}
if (PHP_SAPI === 'cli') {
return '';
}
$https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
$scheme = $https ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
return $scheme . '://' . $host;
}
public function basePath(): string
{
$configured = trim((string) $this->get('base_path', ''));
if ($configured !== '') {
return self::normalizeBasePath($configured);
}
$baseUrl = trim((string) $this->get('base_url', ''));
if ($baseUrl !== '') {
$path = parse_url($baseUrl, PHP_URL_PATH);
if (is_string($path) && $path !== '') {
return self::normalizeBasePath($path);
}
}
if (PHP_SAPI === 'cli') {
return '';
}
$scriptName = str_replace('\\', '/', (string) ($_SERVER['SCRIPT_NAME'] ?? ''));
$scriptDir = dirname($scriptName);
if ($scriptDir === '.' || $scriptDir === '/') {
return '';
}
return self::normalizeBasePath($scriptDir);
}
/** @return array<string, mixed> */
private static function defaults(): array
{
return [
'site' => [
'title' => "Ty Clifford's Content Management System",
'tagline' => 'A fast file-first publishing system',
'description' => "Ty Clifford's Content Management System is powered by PHP, SQLite, JSON, and plain Markdown files.",
'author' => 'Ty Clifford',
'language' => 'en',
],
'base_url' => '',
'base_path' => '',
'timezone' => 'UTC',
'theme' => 'neon',
'posts_per_page' => 6,
'comments' => [
'enabled' => true,
'moderate' => true,
'captcha' => true,
'honeypot_field' => 'website_url',
],
'stats' => [
'enabled' => true,
'hash_salt' => 'change-me',
],
'api' => [
'enabled' => true,
],
'social' => [],
];
}
private static function normalizeBasePath(string $path): string
{
$path = '/' . trim($path, '/');
return $path === '/' ? '' : $path;
}
}