自前で半端な実装をするより、優れたライブラリを使いましょう。
例えば、Composer でインストールできる guzzlehttp/psr7 があります。
composer require guzzlehttp/psr7
インストールしたら、GuzzleHttp\Psr7\UriResolver::resolve で変換できます。
直接文字列を渡すことはできないので、GuzzleHttp\Psr7\Uri の形で渡します。
require_once 'vendor/autoload.php';
$base_uri = new GuzzleHttp\Psr7\Uri('http://example.com/');
$relative_uri = new GuzzleHttp\Psr7\Uri('sample.php');
$absolute_uri = GuzzleHttp\Psr7\UriResolver::resolve($base_uri, $relative_uri);
echo $absolute_uri; // http://example.com/sample.php
結構奇っ怪なURLでもなんとかしてくれます。
$base_uri = new GuzzleHttp\Psr7\Uri('http://user:pass@example.com/base/path/here/index.php?p=q');
$relative_uri = new GuzzleHttp\Psr7\Uri('.././/new/../././rel5.html');
$absolute_uri = GuzzleHttp\Psr7\UriResolver::resolve($base_uri, $relative_uri);
echo $absolute_uri; // http://user:pass@example.com/base/path//rel5.html
GuzzleHttp\Psr7\UriResolver::relativize が逆関数(絶対URL→相対パス)となります。
$base_uri = new GuzzleHttp\Psr7\Uri('http://example.com/');
$absolute_uri = new GuzzleHttp\Psr7\Uri('http://example.com/sample.php');
$relative_uri = GuzzleHttp\Psr7\UriResolver::relativize($base_uri, $absolute_uri);
echo $relative_uri; // sample.php
relativize はシンプルな相対パスを導出するので、元の相対URLと一言一句一致するわけではありません。
$base_uri = new GuzzleHttp\Psr7\Uri('http://user:pass@example.com/base/path/here/index.php?p=q');
$relative_uri = new GuzzleHttp\Psr7\Uri('.././/new/../././rel5.html');
$absolute_uri = GuzzleHttp\Psr7\UriResolver::resolve($base_uri, $relative_uri);
$new_relative_uri = GuzzleHttp\Psr7\UriResolver::relativize($base_uri, $absolute_uri);
echo $new_relative_uri; // ..//rel5.html
今回のサンプルコード:https://github.com/mifumi323/php-relative-url-sample
動作確認環境:PHP 8.2.4、guzzlehttp/psr7 2.4.4