Files
anxinyan/server-api/app/support/PublicAssetUrlService.php
wushumin edd1a02157 first
2026-05-11 15:28:27 +08:00

141 lines
4.1 KiB
PHP

<?php
namespace app\support;
use support\Request;
class PublicAssetUrlService
{
public function buildUrl(Request $request, string $relativePath): string
{
$relativePath = ltrim($relativePath, '/');
$publicPath = $this->configService()->isOss()
? $this->configService()->objectKey($relativePath)
: $relativePath;
return $this->resolveBaseUrl($request) . '/' . ltrim($publicPath, '/');
}
public function normalizeUrl(string $value, Request $request): string
{
$value = trim($value);
if ($value === '') {
return '';
}
$parts = parse_url($value);
$path = (string)($parts['path'] ?? '');
$query = (string)($parts['query'] ?? '');
$fragment = (string)($parts['fragment'] ?? '');
$host = strtolower((string)($parts['host'] ?? ''));
$scheme = (string)($parts['scheme'] ?? '');
if ($scheme === '' && $host === '') {
return $this->appendQueryAndFragment(
$this->buildUrl($request, $path !== '' ? $path : $value),
$query,
$fragment
);
}
if ($path !== '' && $this->shouldRewriteHost($host)) {
return $this->appendQueryAndFragment(
$this->buildUrl($request, $path),
$query,
$fragment
);
}
return $value;
}
public function storagePath(string $value): string
{
$value = trim($value);
if ($value === '') {
return '';
}
$path = parse_url($value, PHP_URL_PATH);
if (!is_string($path) || $path === '') {
$path = ltrim($value, '/');
return $this->configService()->isOss() ? $this->configService()->removePathPrefix($path) : $path;
}
$path = ltrim($path, '/');
return $this->configService()->isOss() ? $this->configService()->removePathPrefix($path) : $path;
}
private function resolveBaseUrl(Request $request): string
{
$configured = $this->configService()->publicBaseUrl();
if ($configured !== '') {
return $configured;
}
$configured = trim((string)($_ENV['PUBLIC_FILE_BASE_URL'] ?? ($_ENV['APP_PUBLIC_BASE_URL'] ?? '')));
if ($configured !== '') {
return $this->normalizeBaseUrl($configured);
}
$scheme = trim((string)($request->header('x-forwarded-proto') ?: 'http'));
$host = trim((string)($request->header('x-forwarded-host') ?: $request->header('host') ?: $request->host()));
if (($commaPos = strpos($host, ',')) !== false) {
$host = trim(substr($host, 0, $commaPos));
}
$port = trim((string)($request->header('x-forwarded-port') ?: ''));
if ($port !== '' && strpos($host, ':') === false) {
if (!(($scheme === 'http' && $port === '80') || ($scheme === 'https' && $port === '443'))) {
$host .= ':' . $port;
}
}
return $this->normalizeBaseUrl($scheme . '://' . $host);
}
private function normalizeBaseUrl(string $baseUrl): string
{
$baseUrl = trim($baseUrl);
if ($baseUrl === '') {
return '';
}
if (!preg_match('/^https?:\/\//i', $baseUrl)) {
$baseUrl = 'https://' . ltrim($baseUrl, '/');
}
return rtrim($baseUrl, '/');
}
private function configService(): FileStorageConfigService
{
return new FileStorageConfigService();
}
private function shouldRewriteHost(string $host): bool
{
if ($host === '') {
return false;
}
if (in_array($host, ['localhost', '127.0.0.1', '0.0.0.0', 'host.docker.internal'], true)) {
return true;
}
return preg_match('/^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[0-1])\.)/', $host) === 1;
}
private function appendQueryAndFragment(string $url, string $query, string $fragment): string
{
if ($query !== '') {
$url .= '?' . $query;
}
if ($fragment !== '') {
$url .= '#' . $fragment;
}
return $url;
}
}