WebOrbiton
v2.0.0.0

Publisium

175 lines · 5.1 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. final class Config
  6. {
  7. private static ?array $values = null;
  8. private static ?string $envPath = null;
  9. ​
  10. public static function envFilePath(): string
  11. {
  12. if (self::$envPath !== null) {
  13. return self::$envPath;
  14. }
  15. ​
  16. $envRoot = dirname(__DIR__, 2) . '/publisium-env';
  17. $pointerFile = $envRoot . '/active-env.txt';
  18. ​
  19. if (is_file($pointerFile)) {
  20. $projectName = trim((string) file_get_contents($pointerFile));
  21. $candidate = $envRoot . '/.env.project.' . $projectName;
  22. if ($projectName !== '' && is_file($candidate)) {
  23. self::$envPath = $candidate;
  24. return self::$envPath;
  25. }
  26. }
  27. ​
  28. self::$envPath = $envRoot . '/.env';
  29. return self::$envPath;
  30. }
  31. ​
  32. public static function startSession(?string $name = null): void
  33. {
  34. if (session_status() === PHP_SESSION_ACTIVE) {
  35. return;
  36. }
  37. ​
  38. if ($name !== null) {
  39. session_name($name);
  40. }
  41. ​
  42. ini_set('session.use_strict_mode', '1');
  43. ini_set('session.use_only_cookies', '1');
  44. session_set_cookie_params([
  45. 'lifetime' => 0,
  46. 'path' => '/',
  47. 'secure' => self::cookieSecure(),
  48. 'httponly' => true,
  49. 'samesite' => 'Lax',
  50. ]);
  51. session_start();
  52. }
  53. ​
  54. public static function cookieSecure(): bool
  55. {
  56. $https = (!empty($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) !== 'off')
  57. || (int) ($_SERVER['SERVER_PORT'] ?? 0) === 443
  58. || strtolower((string) ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '')) === 'https';
  59. ​
  60. return $https && self::get('SESSION_SECURE', '1') !== '0';
  61. }
  62. ​
  63. public static function isInstalled(): bool
  64. {
  65. return is_file(self::envFilePath());
  66. }
  67. ​
  68. public static function load(): array
  69. {
  70. if (self::$values !== null) {
  71. return self::$values;
  72. }
  73. ​
  74. $path = self::envFilePath();
  75. $values = [];
  76. ​
  77. if (is_file($path)) {
  78. $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
  79. foreach ($lines as $line) {
  80. $line = trim($line);
  81. if ($line === '' || str_starts_with($line, '#')) {
  82. continue;
  83. }
  84. if (!str_contains($line, '=')) {
  85. continue;
  86. }
  87. [$key, $value] = explode('=', $line, 2);
  88. $key = trim($key);
  89. $value = trim($value);
  90. if (strlen($value) >= 2 && $value[0] === '"' && $value[-1] === '"') {
  91. $value = substr($value, 1, -1);
  92. }
  93. $values[$key] = $value;
  94. }
  95. }
  96. ​
  97. self::$values = $values;
  98. return self::$values;
  99. }
  100. ​
  101. public static function get(string $key, ?string $default = null): ?string
  102. {
  103. $values = self::load();
  104. return $values[$key] ?? $default;
  105. }
  106. ​
  107. public static function write(string $projectName, array $values): bool
  108. {
  109. $envRoot = dirname(__DIR__, 2) . '/publisium-env';
  110. ​
  111. if (!is_dir($envRoot)) {
  112. if (!mkdir($envRoot, 0750, true) && !is_dir($envRoot)) {
  113. return false;
  114. }
  115. }
  116. ​
  117. $safeName = preg_replace('/[^a-zA-Z0-9_-]/', '', $projectName);
  118. if ($safeName === '') {
  119. $safeName = 'default';
  120. }
  121. ​
  122. $targetFile = $envRoot . '/.env.project.' . $safeName;
  123. $lines = [];
  124. foreach ($values as $key => $value) {
  125. $needsQuotes = str_contains((string) $value, ' ') || $value === '';
  126. $lines[] = $needsQuotes ? sprintf('%s="%s"', $key, $value) : sprintf('%s=%s', $key, $value);
  127. }
  128. ​
  129. $written = file_put_contents($targetFile, implode(PHP_EOL, $lines) . PHP_EOL);
  130. if ($written === false) {
  131. return false;
  132. }
  133. ​
  134. file_put_contents($envRoot . '/active-env.txt', $safeName);
  135. chmod($targetFile, 0640);
  136. ​
  137. self::$values = null;
  138. self::$envPath = null;
  139. ​
  140. return true;
  141. }
  142. ​
  143. // Dashboard forms send fields that may contain HTML or JavaScript as "<name>__b64", so hosting
  144. // firewalls (ModSecurity and similar) don't block the request. Decode them back into $_POST.
  145. public static function decodeEncodedPost(): void
  146. {
  147. if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST' || empty($_POST)) {
  148. return;
  149. }
  150. ​
  151. foreach (array_keys($_POST) as $postKey) {
  152. if (!is_string($postKey) || !str_ends_with($postKey, '__b64')) {
  153. continue;
  154. }
  155. ​
  156. $encoded = $_POST[$postKey];
  157. unset($_POST[$postKey]);
  158. $field = substr($postKey, 0, -5);
  159. ​
  160. if ($field === '' || !is_string($encoded)) {
  161. continue;
  162. }
  163. ​
  164. $decoded = base64_decode($encoded, true);
  165. if ($decoded === false || !mb_check_encoding($decoded, 'UTF-8')) {
  166. continue;
  167. }
  168. ​
  169. $_POST[$field] = $decoded;
  170. }
  171. }
  172. }
  173. ​
  174. Config::decodeEncodedPost();
  175. ​