将所有相对 URL 替换为绝对 URL

Posted

技术标签:

【中文标题】将所有相对 URL 替换为绝对 URL【英文标题】:Replace all relative URLs with absolute URLS 【发布时间】:2018-07-27 22:10:41 【问题描述】:

我已经看到了一些答案(例如this one),但我有一些更复杂的情况我不知道如何解释。

我基本上拥有完整的 html 文档。我需要用绝对 URL 替换每个相对 URL。

潜在 HTML 中的元素如下所示,也可能是其他情况:

<img src="/relative/url/img.jpg" />
<form action="/">
<form action="/contact-us/">
<a href='/relative/url/'>Note the Single Quote</a>
<img src="//example.com/protocol-relative-img.jpg" />

期望的输出是:

// "//example.com/" is ideal, but "http(s)://example.com/" are acceptable

<img src="//example.com/relative/url/img.jpg" />
<form action="//example.com/">
<form action="//example.com/contact-us/">
<a href='//example.com/relative/url/'>Note the Single Quote</a>
<img src="//example.com/protocol-relative-img.jpg" /> <!-- Unmodified -->

我不想替换协议相对 URL,因为它们已经充当绝对 URL。我想出了一些有效的代码,但我想知道是否可以稍微清理一下,因为它非常重复。

但我必须考虑 srchrefaction 的单引号和双引号属性值(我是否遗漏了任何可以具有相对 URL 的属性?)同时避免协议相对 URL。

这是我目前所拥有的:

// Make URL replacement protocol relative to not break insecure/secure links
$url = str_replace( array( 'http://', 'https://' ), '//', $url );

// Temporarily Modify Protocol-Relative URLS
$str = str_replace( 'src="//', 'src="::TEMP_REPLACE::', $str );
$str = str_replace( "src='//", "src='::TEMP_REPLACE::", $str );
$str = str_replace( 'href="//', 'href="::TEMP_REPLACE::', $str );
$str = str_replace( "href='//", "href='::TEMP_REPLACE::", $str );
$str = str_replace( 'action="//', 'action="::TEMP_REPLACE::', $str );
$str = str_replace( "action='//", "action='::TEMP_REPLACE::", $str );

// Replace all other Relative URLS
$str = str_replace( 'src="/', 'src="'. $url .'/', $str );
$str = str_replace( "src='/", "src='". $url ."/", $str );
$str = str_replace( 'href="/', 'href="'. $url .'/', $str );
$str = str_replace( "href='/", "href='". $url ."/", $str );
$str = str_replace( 'action="/', 'action="'. $url .'/', $str );
$str = str_replace( "action='/", "action='". $url ."/", $str );

// Change Protocol Relative URLs back
$str = str_replace( 'src="::TEMP_REPLACE::', 'src="//', $str );
$str = str_replace( "src='::TEMP_REPLACE::", "src='//", $str );
$str = str_replace( 'href="::TEMP_REPLACE::', 'href="//', $str );
$str = str_replace( "href='::TEMP_REPLACE::", "href='//", $str );
$str = str_replace( 'action="::TEMP_REPLACE::', 'action="//', $str );
$str = str_replace( "action='::TEMP_REPLACE::", "action='//", $str );

我的意思是,它有效,但它uuugly,我在想可能有更好的方法来做到这一点。

【问题讨论】:

为什么,绝对网址不能很好地传播 我知道这很不寻常,不幸的是在这个 one 特殊情况下,它们比相对 URL 传播得更好。为了争论,我有一个存在并在https://example.com/contact 进行自我验证的表单,默认标签是&lt;form action="/contact"&gt; 可以正常工作,但是我们的客户需要将此内容1:1 克隆到另一个URL,其中/contact 没有'不存在或没有验证,因此表单将在提交时出现 404 或不验证。与链接相同。我知道这是不典型的,但对于这个项目,绝对 URL 更适合它。 您是否要更改一堆 php 文件中的所有引用?如果是这样,基于 Perl 命令行的正则表达式可能会很好地工作: perl -p -i.bak -e 's/search/replace/g' *.php 不,不幸的是,这是一个客户端应用程序。目前最常见的用例是我们的网站客户端使用 WordPress 网站。我们还有一个专有的登陆页面生成软件。一些客户希望他们的目标网页与他们的网站共享相同的 URL(巨大的争用点)。如果不设置域映射(我们不控制所有托管),我们需要将它们的内容带过来。所以我写了一个插件来终止原始请求,并将其替换为新 URL 中的内容。它真的很好用,除了...... ...当有相对 URL 时,尤其是表单和图像。我有另一种方法可以在内容中使用 iframe,但它可以工作,但挑剔的客户不希望它 iframe(哈哈?),以及 301 重定向的第三个选项,但它否定了整个“相同的 URL”部分。下载效果最好,因为我可以缓存响应,并且上面的代码 works 可以一路替换相对 URL,只是想知道是否有更好的方法,而不是 18 个后续的 str_replace() 函数。跨度> 【参考方案1】:

新答案

如果你真正的 html 文档是有效的(并且有一个父/包含标签),那么最合适和最可靠的技术将是使用适当的 DOM 解析器。

以下是如何使用 DOMDocument 和 Xpath 优雅地定位和替换您指定的标签属性:

代码 1 - 嵌套 Xpath 查询:(Demo)

$domain = '//example.com';
$tagsAndAttributes = [
    'img' => 'src',
    'form' => 'action',
    'a' => 'href'
];

$dom = new DOMDocument; 
$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($dom);
foreach ($tagsAndAttributes as $tag => $attr) 
    foreach ($xpath->query("//$tag[not(starts-with(@$attr, '//'))]") as $node) 
        $node->setAttribute($attr, $domain . $node->getAttribute($attr));
    

echo $dom->saveHTML();

Code2 - 带条件块的单个 Xpath 查询:(Demo)

$domain = '//example.com';
$targets = [
    "//img[not(starts-with(@src, '//'))]",
    "//form[not(starts-with(@action, '//'))]",
    "//a[not(starts-with(@href, '//'))]"
];

$dom = new DOMDocument; 
$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($dom);
foreach ($xpath->query(implode('|', $targets)) as $node) 
    if ($src = $node->getAttribute('src')) 
        $node->setAttribute('src', $domain . $src);
     elseif ($action = $node->getAttribute('action')) 
        $node->setAttribute('action', $domain . $action);
     else 
        $node->setAttribute('href', $domain . $node->getAttribute('href'));
    

echo $dom->saveHTML();

旧答案:(...regex 不是“DOM 感知”并且容易受到意外破坏)

如果我正确理解你,你心中有一个基本价值,你只想将它应用到相对路径。

Pattern Demo

代码:(Demo)

$html=<<<HTML
<img src="/relative/url/img.jpg" />
<form action="/">
<a href='/relative/url/'>Note the Single Quote</a>
<img src="//site.com/protocol-relative-img.jpg" />
HTML;

$base='https://example.com';

echo preg_replace('~(?:src|action|href)=[\'"]\K/(?!/)[^\'"]*~',"$base$0",$html);

输出:

<img src="https://example.com/relative/url/img.jpg" />
<form action="https://example.com/">
<a href='https://example.com/relative/url/'>Note the Single Quote</a>
<img src="//site.com/protocol-relative-img.jpg" />

模式细分:

~                      #Pattern delimiter
(?:src|action|href)    #Match: src or action or href
=                      #Match equal sign
[\'"]                  #Match single or double quote
\K                     #Restart fullstring match (discard previously matched characters
/                      #Match slash
(?!/)                  #Negative lookahead (zero-length assertion): must not be a slash immediately after first matched slash
[^\'"]*                #Match zero or more non-single/double quote characters
~                      #Pattern delimiter

【讨论】:

这是您的预期操作吗?正则表达式对于进行变量/动态替换相当方便。如果这按需要工作,我会花时间写一个模式解释。 ...当然,我需要提供标准免责声明,即使用正则表达式解析 html 可能不稳定(但与 str_replace() 相比,这种情况的风险并不小。 哇,非常感谢!这实际上非常接近!我对正则表达式仍然很糟糕。我试图修改代码,但我无法让它正常工作。我不需要替换基础本身,但我需要添加绝对 url。期望的输出是:&lt;img src="https://example.com/relative/url/img.jpg" /&gt;&lt;form action="https://example.com/"&gt;&lt;a href='https://example.com/relative/url'&gt;Link&lt;/a&gt;&lt;img src="//example.com/protocol-relative-img.jpg" /&gt; 请在我编辑我的答案之前确认此更新:3v4l.org/W24bp 抱歉延迟响应,但实际上更接近了!然而,它似乎在 URL 前面加上了一个正斜杠,所以我的相对图像是 /http://example.com/images/img.jpg,而表单操作只是 //http://example.com/ 我也真诚地感谢故障!【参考方案2】:

我认为&lt;base&gt; 元素就是您要寻找的...

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

&lt;base&gt; 是一个空元素,位于&lt;head&gt; 中。使用 &lt;base href="https://example.com/path/" /&gt; 将告诉文档中的所有相对 URL 引用 https://example.com/path/ 而不是父 URL

【讨论】:

哇,多么棒的元素!多年来,这将派上用场很多次,哈哈。 不幸的是,虽然它确实适用于图像和其他内容,但它并不能取代表单操作,这可以说是我需要的最重要的部分。我想知道我是否可以(错误应该?)将str_replace() 函数用于该操作并删除它们以代替使用&lt;base&gt; 元素,或者我是否应该保留所有这些函数以便完成所有代码操作同样的方式在同一个地方 @Xhynk 您是否已经尝试使用 Grunt 或其他任务运行程序自动替换路径? @Rolland 不幸的是,这是不可能的,我需要一种运行时方法。 +mickmackusa 让我非常接近他的正则表达式方法,我只需要他的帮助进行进一步的调整

以上是关于将所有相对 URL 替换为绝对 URL的主要内容,如果未能解决你的问题,请参考以下文章

将相对 URL 转换为绝对 URL

将相对 url 路径解析为其绝对路径

如何从 django 视图中的绝对 url 获取相对 url?

从相对 URL 获取绝对 URL。 (IE6问题)

替换 Facebook Feed 中的相对 URL

Java - 如果我知道域,如何将相对 URL 字符串更改为绝对 URL?