file_get_contents( - 修复相对 URL
file_get_contents( - Fix relative urls
我正在尝试向用户显示一个网站,该网站已使用 php 下载。
这是我正在使用的脚本:
<?php
$url = 'http://whosebug.com/pagecalledjohn.php';
//Download page
$site = file_get_contents($url);
//Fix relative URLs
$site = str_replace('src="','src="' . $url,$site);
$site = str_replace('url(','url(' . $url,$site);
//Display to user
echo $site;
?>
到目前为止,除了 str_replace 函数的一些主要问题外,该脚本还算不错。问题来自相对 urls。如果我们在合成的 pagecalledjohn.php 猫上使用图像(类似这样:)。它是一个 png,正如我所见,它可以使用 6 个不同的 urls:
放置在页面上
1. src="//www.whosebug.com/cat.png"
2. src="http://www.whosebug.com/cat.png"
3. src="https://www.whosebug.com/cat.png"
4. src="somedirectory/cat.png"
4 在这种情况下不适用,但还是添加了!
5. src="/cat.png"
6. src="cat.png"
有没有办法,使用 php,我可以搜索 src=" 并将其替换为正在下载页面的 url(文件名已删除),但不会粘贴 url 如果是选项 1,2 或 3 并稍微更改 4,5 和 6 的程序?
我建议多做几步。
为了不使解决方案复杂化,我们假设任何 src 值始终是图像(它也可以是其他东西,例如脚本)。
另外,我们假设等号和引号之间没有空格(如果有的话可以很容易地修复)。最后,我们假设文件名不包含任何转义引号(如果包含,正则表达式会更复杂)。
因此,您将使用以下正则表达式查找所有图像引用:
src="([^"]*)"
。 (此外,这不包括 src 包含在单引号中的情况。但是很容易为此创建类似的正则表达式。)
然而,处理逻辑可以用 preg_replace_callback 函数完成,而不是 str_replace
。您可以为此函数提供回调,其中每个 url 都可以根据其内容进行处理。
所以你可以这样做(未测试!):
$site = preg_replace_callback(
'src="([^"]*)"',
function ($src) {
$url = $src[1];
$ret = "";
if (preg_match("^//", $url)) {
// case 1.
$ret = "src='" . $url . '"';
}
else if (preg_match("^https?://", $url)) {
// case 2. and 3.
$ret = "src='" . $url . '"';
}
else {
// case 4., 5., 6.
$ret = "src='http://your.site.com.com/" . $url . '"';
}
return $ret;
},
$site
);
我不知道我是否完全正确地回答了你的问题,如果你想处理包含在 src="
和 "
中的所有文本序列,以下模式可以做到:
~(\ssrc=")([^"]+)(")~
它有三个捕获组,其中第二个包含您感兴趣的数据。第一个和最后一个用于更改整个匹配。
现在您可以使用更改位置的回调函数替换所有实例。我已经创建了一个包含所有 6 个案例的简单字符串:
$site = <<<BUFFER
1. src="//www.whosebug.com/cat.png"
2. src="http://www.whosebug.com/cat.png"
3. src="https://www.whosebug.com/cat.png"
4. src="somedirectory/cat.png"
5. src="/cat.png"
6. src="cat.png"
BUFFER;
让我们暂时忽略没有周围的 HTML 标签,无论如何你都没有解析 HTML 我敢肯定,因为你没有要求 HTML解析器,但用于正则表达式。在下面的示例中,中间的匹配项 (URL) 将被括起来,以明确匹配:
现在要替换每个链接,让我们从简单地开始,只需在字符串中突出显示它们。
$pattern = '~(\ssrc=")([^"]+)(")~';
echo preg_replace_callback($pattern, function ($matches) {
return $matches[1] . ">>>" . $matches[2] . "<<<" . $matches[3];
}, $site);
给出的示例的输出是:
1. src=">>>//www.whosebug.com/cat.png<<<"
2. src=">>>http://www.whosebug.com/cat.png<<<"
3. src=">>>https://www.whosebug.com/cat.png<<<"
4. src=">>>somedirectory/cat.png<<<"
5. src=">>>/cat.png<<<"
6. src=">>>cat.png<<<"
由于替换字符串的方式要改,所以可以提取出来,所以更容易改:
$callback = function($method) {
return function ($matches) use ($method) {
return $matches[1] . $method($matches[2]) . $matches[3];
};
};
此函数根据您作为参数传递的替换方法创建替换回调。
这样的替换函数可以是:
$highlight = function($string) {
return ">>>$string<<<";
};
它的调用方式如下:
$pattern = '~(\ssrc=")([^"]+)(")~';
echo preg_replace_callback($pattern, $callback($highlight), $site);
输出保持不变,这只是为了说明提取的工作原理:
1. src=">>>//www.whosebug.com/cat.png<<<"
2. src=">>>http://www.whosebug.com/cat.png<<<"
3. src=">>>https://www.whosebug.com/cat.png<<<"
4. src=">>>somedirectory/cat.png<<<"
5. src=">>>/cat.png<<<"
6. src=">>>cat.png<<<"
这样做的好处是,对于替换函数,您只需要将 URL 匹配作为单个字符串处理,而不需要使用正则表达式匹配数组来处理不同的组。
现在回答问题的后半部分:如何将其替换为特定的 URL 处理方式,例如删除文件名。这可以通过解析 URL 本身并从路径组件中删除文件名(基本名称)来完成。由于提取,您可以将其放入一个简单的函数中:
$removeFilename = function ($url) {
$url = new Net_URL2($url);
$base = basename($path = $url->getPath());
$url->setPath(substr($path, 0, -strlen($base)));
return $url;
};
此代码使用了 Pear's Net_URL2 URL component(也可通过 Packagist 和 Github 获得,您的 OS 软件包可能也有)。它可以轻松地解析和修改 URLs,因此很适合这份工作。
现在使用新的 URL 文件名替换功能完成替换:
$pattern = '~(\ssrc=")([^"]+)(")~';
echo preg_replace_callback($pattern, $callback($removeFilename), $site);
然后结果是:
1. src="//www.whosebug.com/"
2. src="http://www.whosebug.com/"
3. src="https://www.whosebug.com/"
4. src="somedirectory/"
5. src="/"
6. src=""
请注意,这是示范性的。它展示了如何使用正则表达式来做到这一点。但是,您也可以使用 HTML 解析器。让我们把它变成一个真正的 HTML 片段:
1. <img src="//www.whosebug.com/cat.png"/>
2. <img src="http://www.whosebug.com/cat.png"/>
3. <img src="https://www.whosebug.com/cat.png"/>
4. <img src="somedirectory/cat.png"/>
5. <img src="/cat.png"/>
6. <img src="cat.png"/>
然后使用创建的替换过滤器函数处理所有 <img>
“src
”属性:
$doc = new DOMDocument();
$saved = libxml_use_internal_errors(true);
$doc->loadHTML($site, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_use_internal_errors($saved);
$srcs = (new DOMXPath($doc))->query('//img/@hsrc') ?: [];
foreach ($srcs as $src) {
$src->nodeValue = $removeFilename($src->nodeValue);
}
echo $doc->saveHTML();
结果又是:
1. <img src="//www.whosebug.com/cat.png">
2. <img src="http://www.whosebug.com/cat.png">
3. <img src="https://www.whosebug.com/cat.png">
4. <img src="somedirectory/cat.png">
5. <img src="/cat.png">
6. <img src="cat.png">
只是使用了不同的解析方式 - 替换仍然是相同的。只是提供两种不同的方式,部分相同。
与其尝试更改源代码中的每个路径引用,不如简单地在 header 中插入一个 <base>
标记来专门指示基 URL应该计算哪些所有相对 URL?
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
这可以使用您选择的 DOM 操作工具来实现。下面的示例将显示如何使用 DOMDocument 和相关 类.
执行此操作
$target_domain = 'http://whosebug.com/';
$url = $target_domain . 'pagecalledjohn.php';
//Download page
$site = file_get_contents($url);
$dom = DOMDocument::loadHTML($site);
if($dom instanceof DOMDocument === false) {
// something went wrong in loading HTML to DOM Document
// provide error messaging and exit
}
// find <head> tag
$head_tag_list = $dom->getElementsByTagName('head');
// there should only be one <head> tag
if($head_tag_list->length !== 1) {
throw new Exception('Wow! The HTML is malformed without single head tag.');
}
$head_tag = $head_tag_list->item(0);
// find first child of head tag to later use in insertion
$head_has_children = $head_tag->hasChildNodes();
if($head_has_children) {
$head_tag_first_child = $head_tag->firstChild;
}
// create new <base> tag
$base_element = $dom->createElement('base');
$base_element->setAttribute('href', $target_domain);
// insert new base tag as first child to head tag
if($head_has_children) {
$base_node = $head_tag->insertBefore($base_element, $head_tag_first_child);
} else {
$base_node = $head_tag->appendChild($base_element);
}
echo $dom->saveHTML();
至少,如果您真的想修改源代码中的所有路径引用,我强烈建议您使用 DOM 操作工具(DOMDOcument,DOMXPath 等)而不是正则表达式。我想你会发现它是一个更稳定的解决方案。
我正在尝试向用户显示一个网站,该网站已使用 php 下载。 这是我正在使用的脚本:
<?php
$url = 'http://whosebug.com/pagecalledjohn.php';
//Download page
$site = file_get_contents($url);
//Fix relative URLs
$site = str_replace('src="','src="' . $url,$site);
$site = str_replace('url(','url(' . $url,$site);
//Display to user
echo $site;
?>
到目前为止,除了 str_replace 函数的一些主要问题外,该脚本还算不错。问题来自相对 urls。如果我们在合成的 pagecalledjohn.php 猫上使用图像(类似这样:
1. src="//www.whosebug.com/cat.png"
2. src="http://www.whosebug.com/cat.png"
3. src="https://www.whosebug.com/cat.png"
4. src="somedirectory/cat.png"
4 在这种情况下不适用,但还是添加了!
5. src="/cat.png"
6. src="cat.png"
有没有办法,使用 php,我可以搜索 src=" 并将其替换为正在下载页面的 url(文件名已删除),但不会粘贴 url 如果是选项 1,2 或 3 并稍微更改 4,5 和 6 的程序?
我建议多做几步。
为了不使解决方案复杂化,我们假设任何 src 值始终是图像(它也可以是其他东西,例如脚本)。
另外,我们假设等号和引号之间没有空格(如果有的话可以很容易地修复)。最后,我们假设文件名不包含任何转义引号(如果包含,正则表达式会更复杂)。
因此,您将使用以下正则表达式查找所有图像引用:
src="([^"]*)"
。 (此外,这不包括 src 包含在单引号中的情况。但是很容易为此创建类似的正则表达式。)
然而,处理逻辑可以用 preg_replace_callback 函数完成,而不是 str_replace
。您可以为此函数提供回调,其中每个 url 都可以根据其内容进行处理。
所以你可以这样做(未测试!):
$site = preg_replace_callback(
'src="([^"]*)"',
function ($src) {
$url = $src[1];
$ret = "";
if (preg_match("^//", $url)) {
// case 1.
$ret = "src='" . $url . '"';
}
else if (preg_match("^https?://", $url)) {
// case 2. and 3.
$ret = "src='" . $url . '"';
}
else {
// case 4., 5., 6.
$ret = "src='http://your.site.com.com/" . $url . '"';
}
return $ret;
},
$site
);
我不知道我是否完全正确地回答了你的问题,如果你想处理包含在 src="
和 "
中的所有文本序列,以下模式可以做到:
~(\ssrc=")([^"]+)(")~
它有三个捕获组,其中第二个包含您感兴趣的数据。第一个和最后一个用于更改整个匹配。
现在您可以使用更改位置的回调函数替换所有实例。我已经创建了一个包含所有 6 个案例的简单字符串:
$site = <<<BUFFER
1. src="//www.whosebug.com/cat.png"
2. src="http://www.whosebug.com/cat.png"
3. src="https://www.whosebug.com/cat.png"
4. src="somedirectory/cat.png"
5. src="/cat.png"
6. src="cat.png"
BUFFER;
让我们暂时忽略没有周围的 HTML 标签,无论如何你都没有解析 HTML 我敢肯定,因为你没有要求 HTML解析器,但用于正则表达式。在下面的示例中,中间的匹配项 (URL) 将被括起来,以明确匹配:
现在要替换每个链接,让我们从简单地开始,只需在字符串中突出显示它们。
$pattern = '~(\ssrc=")([^"]+)(")~';
echo preg_replace_callback($pattern, function ($matches) {
return $matches[1] . ">>>" . $matches[2] . "<<<" . $matches[3];
}, $site);
给出的示例的输出是:
1. src=">>>//www.whosebug.com/cat.png<<<"
2. src=">>>http://www.whosebug.com/cat.png<<<"
3. src=">>>https://www.whosebug.com/cat.png<<<"
4. src=">>>somedirectory/cat.png<<<"
5. src=">>>/cat.png<<<"
6. src=">>>cat.png<<<"
由于替换字符串的方式要改,所以可以提取出来,所以更容易改:
$callback = function($method) {
return function ($matches) use ($method) {
return $matches[1] . $method($matches[2]) . $matches[3];
};
};
此函数根据您作为参数传递的替换方法创建替换回调。
这样的替换函数可以是:
$highlight = function($string) {
return ">>>$string<<<";
};
它的调用方式如下:
$pattern = '~(\ssrc=")([^"]+)(")~';
echo preg_replace_callback($pattern, $callback($highlight), $site);
输出保持不变,这只是为了说明提取的工作原理:
1. src=">>>//www.whosebug.com/cat.png<<<"
2. src=">>>http://www.whosebug.com/cat.png<<<"
3. src=">>>https://www.whosebug.com/cat.png<<<"
4. src=">>>somedirectory/cat.png<<<"
5. src=">>>/cat.png<<<"
6. src=">>>cat.png<<<"
这样做的好处是,对于替换函数,您只需要将 URL 匹配作为单个字符串处理,而不需要使用正则表达式匹配数组来处理不同的组。
现在回答问题的后半部分:如何将其替换为特定的 URL 处理方式,例如删除文件名。这可以通过解析 URL 本身并从路径组件中删除文件名(基本名称)来完成。由于提取,您可以将其放入一个简单的函数中:
$removeFilename = function ($url) {
$url = new Net_URL2($url);
$base = basename($path = $url->getPath());
$url->setPath(substr($path, 0, -strlen($base)));
return $url;
};
此代码使用了 Pear's Net_URL2 URL component(也可通过 Packagist 和 Github 获得,您的 OS 软件包可能也有)。它可以轻松地解析和修改 URLs,因此很适合这份工作。
现在使用新的 URL 文件名替换功能完成替换:
$pattern = '~(\ssrc=")([^"]+)(")~';
echo preg_replace_callback($pattern, $callback($removeFilename), $site);
然后结果是:
1. src="//www.whosebug.com/"
2. src="http://www.whosebug.com/"
3. src="https://www.whosebug.com/"
4. src="somedirectory/"
5. src="/"
6. src=""
请注意,这是示范性的。它展示了如何使用正则表达式来做到这一点。但是,您也可以使用 HTML 解析器。让我们把它变成一个真正的 HTML 片段:
1. <img src="//www.whosebug.com/cat.png"/>
2. <img src="http://www.whosebug.com/cat.png"/>
3. <img src="https://www.whosebug.com/cat.png"/>
4. <img src="somedirectory/cat.png"/>
5. <img src="/cat.png"/>
6. <img src="cat.png"/>
然后使用创建的替换过滤器函数处理所有 <img>
“src
”属性:
$doc = new DOMDocument();
$saved = libxml_use_internal_errors(true);
$doc->loadHTML($site, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_use_internal_errors($saved);
$srcs = (new DOMXPath($doc))->query('//img/@hsrc') ?: [];
foreach ($srcs as $src) {
$src->nodeValue = $removeFilename($src->nodeValue);
}
echo $doc->saveHTML();
结果又是:
1. <img src="//www.whosebug.com/cat.png">
2. <img src="http://www.whosebug.com/cat.png">
3. <img src="https://www.whosebug.com/cat.png">
4. <img src="somedirectory/cat.png">
5. <img src="/cat.png">
6. <img src="cat.png">
只是使用了不同的解析方式 - 替换仍然是相同的。只是提供两种不同的方式,部分相同。
与其尝试更改源代码中的每个路径引用,不如简单地在 header 中插入一个 <base>
标记来专门指示基 URL应该计算哪些所有相对 URL?
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
这可以使用您选择的 DOM 操作工具来实现。下面的示例将显示如何使用 DOMDocument 和相关 类.
执行此操作$target_domain = 'http://whosebug.com/';
$url = $target_domain . 'pagecalledjohn.php';
//Download page
$site = file_get_contents($url);
$dom = DOMDocument::loadHTML($site);
if($dom instanceof DOMDocument === false) {
// something went wrong in loading HTML to DOM Document
// provide error messaging and exit
}
// find <head> tag
$head_tag_list = $dom->getElementsByTagName('head');
// there should only be one <head> tag
if($head_tag_list->length !== 1) {
throw new Exception('Wow! The HTML is malformed without single head tag.');
}
$head_tag = $head_tag_list->item(0);
// find first child of head tag to later use in insertion
$head_has_children = $head_tag->hasChildNodes();
if($head_has_children) {
$head_tag_first_child = $head_tag->firstChild;
}
// create new <base> tag
$base_element = $dom->createElement('base');
$base_element->setAttribute('href', $target_domain);
// insert new base tag as first child to head tag
if($head_has_children) {
$base_node = $head_tag->insertBefore($base_element, $head_tag_first_child);
} else {
$base_node = $head_tag->appendChild($base_element);
}
echo $dom->saveHTML();
至少,如果您真的想修改源代码中的所有路径引用,我强烈建议您使用 DOM 操作工具(DOMDOcument,DOMXPath 等)而不是正则表达式。我想你会发现它是一个更稳定的解决方案。