$headOnly, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => false, CURLOPT_TIMEOUT => $timeoutSec, CURLOPT_HEADER => true, CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; SplitPageUrlResolver/1.0)', ]); $body = curl_exec($ch); if ($body === false) { curl_close($ch); return null; } $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); $redirectUrl = (string) curl_getinfo($ch, CURLINFO_REDIRECT_URL); curl_close($ch); $location = $redirectUrl; if ($location === '' && is_string($body)) { $location = self::parseLocationHeader($body); } return ['code' => $code, 'location' => trim($location)]; } private static function parseLocationHeader(string $rawHeaders): string { foreach (preg_split('/\r\n|\n|\r/', $rawHeaders) as $line) { if (stripos($line, 'Location:') === 0) { return trim(substr($line, 9)); } } return ''; } private static function resolveRelativeUrl(string $baseUrl, string $location): string { if (preg_match('#^https?://#i', $location)) { return $location; } $base = parse_url($baseUrl); if (!is_array($base)) { return $location; } $scheme = (string) ($base['scheme'] ?? 'https'); $host = (string) ($base['host'] ?? ''); if ($host === '') { return $location; } if (strpos($location, '/') === 0) { return $scheme . '://' . $host . $location; } $path = (string) ($base['path'] ?? '/'); $dir = rtrim(str_replace(basename($path), '', $path), '/'); return $scheme . '://' . $host . ($dir !== '' ? $dir . '/' : '/') . $location; } }