141 lines
4.1 KiB
PHP
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;
|
|
}
|
|
}
|