249 lines
6.1 KiB
PHP
249 lines
6.1 KiB
PHP
<?php
|
|
|
|
use App\Core\Csrf;
|
|
use App\Core\Session;
|
|
|
|
function base_path(): string
|
|
{
|
|
foreach ([
|
|
$_SERVER['KAFFEEKASSE_PROXY_PREFIX'] ?? null,
|
|
$_SERVER['HTTP_X_FORWARDED_PREFIX'] ?? null,
|
|
$_ENV['APP_BASE_PATH'] ?? null,
|
|
] as $prefix) {
|
|
$prefix = trim((string) $prefix);
|
|
|
|
if ($prefix !== '') {
|
|
return normalize_base_path($prefix);
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function normalize_base_path(string $prefix): string
|
|
{
|
|
$path = parse_url($prefix, PHP_URL_PATH);
|
|
$path = is_string($path) ? $path : $prefix;
|
|
$path = '/' . trim($path, '/');
|
|
|
|
return $path === '/' ? '' : $path;
|
|
}
|
|
|
|
function e(?string $value): string
|
|
{
|
|
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
function current_path(): string
|
|
{
|
|
$uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
|
|
|
|
$path = is_string($uri) && $uri !== '' ? $uri : '/';
|
|
$basePath = base_path();
|
|
|
|
if ($basePath !== '' && ($path === $basePath || str_starts_with($path, $basePath . '/'))) {
|
|
$path = substr($path, strlen($basePath)) ?: '/';
|
|
}
|
|
|
|
$path = '/' . ltrim($path, '/');
|
|
|
|
return $path === '/' ? '/' : rtrim($path, '/');
|
|
}
|
|
|
|
function url(string $path = '/', array $query = []): string
|
|
{
|
|
$path = trim($path);
|
|
|
|
if ($path === '') {
|
|
$path = '/';
|
|
}
|
|
|
|
if (preg_match('#^(?:[a-z][a-z0-9+.-]*:)?//#i', $path) || str_starts_with($path, '#')) {
|
|
return append_query($path, $query);
|
|
}
|
|
|
|
$fragment = '';
|
|
$hashPosition = strpos($path, '#');
|
|
if ($hashPosition !== false) {
|
|
$fragment = substr($path, $hashPosition);
|
|
$path = substr($path, 0, $hashPosition);
|
|
}
|
|
|
|
$existingQuery = '';
|
|
$queryPosition = strpos($path, '?');
|
|
if ($queryPosition !== false) {
|
|
$existingQuery = substr($path, $queryPosition + 1);
|
|
$path = substr($path, 0, $queryPosition);
|
|
}
|
|
|
|
$targetPath = '/' . ltrim($path, '/');
|
|
$targetPath = $targetPath === '//' ? '/' : $targetPath;
|
|
$targetPath = $targetPath === '/' ? '/' : rtrim($targetPath, '/');
|
|
$queryString = build_query_string($existingQuery, $query);
|
|
$basePath = base_path();
|
|
|
|
if ($basePath !== '') {
|
|
return $basePath . ($targetPath === '/' ? '/' : $targetPath) . $queryString . $fragment;
|
|
}
|
|
|
|
return relative_url($targetPath) . $queryString . $fragment;
|
|
}
|
|
|
|
function append_query(string $url, array $query): string
|
|
{
|
|
if ($query === []) {
|
|
return $url;
|
|
}
|
|
|
|
return $url . (str_contains($url, '?') ? '&' : '?') . http_build_query($query, '', '&', PHP_QUERY_RFC3986);
|
|
}
|
|
|
|
function build_query_string(string $existingQuery, array $query): string
|
|
{
|
|
$parts = [];
|
|
|
|
if ($existingQuery !== '') {
|
|
$parts[] = $existingQuery;
|
|
}
|
|
|
|
if ($query !== []) {
|
|
$parts[] = http_build_query($query, '', '&', PHP_QUERY_RFC3986);
|
|
}
|
|
|
|
return $parts === [] ? '' : '?' . implode('&', $parts);
|
|
}
|
|
|
|
function relative_url(string $targetPath): string
|
|
{
|
|
$currentPath = current_path();
|
|
$fromSegments = path_segments($currentPath);
|
|
|
|
if (!str_ends_with($currentPath, '/')) {
|
|
array_pop($fromSegments);
|
|
}
|
|
|
|
$toSegments = path_segments($targetPath);
|
|
$common = 0;
|
|
$maxCommon = min(count($fromSegments), count($toSegments));
|
|
|
|
while ($common < $maxCommon && $fromSegments[$common] === $toSegments[$common]) {
|
|
$common++;
|
|
}
|
|
|
|
if ($toSegments !== [] && $common === count($toSegments) && $common === count($fromSegments)) {
|
|
return '../' . end($toSegments);
|
|
}
|
|
|
|
$relativeSegments = array_merge(
|
|
array_fill(0, count($fromSegments) - $common, '..'),
|
|
array_slice($toSegments, $common)
|
|
);
|
|
|
|
return $relativeSegments === [] ? '.' : implode('/', $relativeSegments);
|
|
}
|
|
|
|
function path_segments(string $path): array
|
|
{
|
|
$trimmed = trim($path, '/');
|
|
|
|
return $trimmed === '' ? [] : explode('/', $trimmed);
|
|
}
|
|
|
|
function asset_url(string $path): string
|
|
{
|
|
return url('/assets/' . ltrim($path, '/'));
|
|
}
|
|
|
|
function old(string $key, mixed $default = ''): mixed
|
|
{
|
|
return $_SESSION['_old'][$key] ?? $default;
|
|
}
|
|
|
|
function remember_old_input(array $input): void
|
|
{
|
|
foreach (['password', 'admin_password', 'login_password', 'owner_password', 'db_pass'] as $sensitiveKey) {
|
|
if (array_key_exists($sensitiveKey, $input)) {
|
|
unset($input[$sensitiveKey]);
|
|
}
|
|
}
|
|
|
|
$_SESSION['_old'] = $input;
|
|
}
|
|
|
|
function clear_old_input(): void
|
|
{
|
|
unset($_SESSION['_old']);
|
|
}
|
|
|
|
function csrf_field(Csrf $csrf): string
|
|
{
|
|
return '<input type="hidden" name="_token" value="' . e($csrf->token()) . '">';
|
|
}
|
|
|
|
function flash(Session $session, string $key, mixed $default = null): mixed
|
|
{
|
|
return $session->pullFlash($key, $default);
|
|
}
|
|
|
|
function tenant_url(string $tenantSlug, string $path = ''): string
|
|
{
|
|
return url(tenant_path($tenantSlug, $path));
|
|
}
|
|
|
|
function tenant_path(string $tenantSlug, string $path = ''): string
|
|
{
|
|
$suffix = $path === '' ? '' : '/' . ltrim($path, '/');
|
|
|
|
return '/t/' . rawurlencode($tenantSlug) . $suffix;
|
|
}
|
|
|
|
function money_from_cents(int $amountCents): string
|
|
{
|
|
return number_format($amountCents / 100, 2, ',', '.') . ' EUR';
|
|
}
|
|
|
|
function secure_password_hash(string $password): string
|
|
{
|
|
$algo = defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_BCRYPT;
|
|
|
|
return password_hash($password, $algo);
|
|
}
|
|
|
|
function normalize_datetime_input(?string $value): string
|
|
{
|
|
$value = trim((string) $value);
|
|
|
|
if ($value === '') {
|
|
return gmdate('Y-m-d H:i:s');
|
|
}
|
|
|
|
$normalized = str_replace('T', ' ', $value);
|
|
|
|
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $normalized)) {
|
|
return $normalized . ':00';
|
|
}
|
|
|
|
return $normalized;
|
|
}
|
|
|
|
function request_origin_matches_host(): bool
|
|
{
|
|
$host = $_SERVER['HTTP_HOST'] ?? '';
|
|
|
|
if ($host === '') {
|
|
return true;
|
|
}
|
|
|
|
foreach (['HTTP_ORIGIN', 'HTTP_REFERER'] as $header) {
|
|
if (!empty($_SERVER[$header])) {
|
|
$originHost = parse_url((string) $_SERVER[$header], PHP_URL_HOST);
|
|
|
|
if (is_string($originHost) && $originHost !== '' && !hash_equals($host, $originHost)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|