如何在 PHP 中正确创建 HTML 链接?

2024-04-15

这个问题是关于正确使用原始网址编码 https://www.php.net/manual/en/function.rawurlencode.php, http_build_query https://www.php.net/manual/en/function.http-build-query.php & html特殊字符 https://www.php.net/manual/en/function.htmlspecialchars.php.

到目前为止,我在 vanilla PHP 中创建 HTML 链接的标准方法是:

$qs = [
    'foo' => 'foo~bar',
    'bar' => 'bar foo',
];
echo '<a href="?' . http_build_query($qs) . '">Link</a>';

最近我了解到这并不是100%正确。这里有几个问题:

  • http_build_query默认使用PHP_QUERY_RFC1738代替PHP_QUERY_RFC3986。 RFC3986 是标准并取代了 RFC1738,在 PHP 中仅保留用于遗留用途。
  • 虽然键和值部分中的“特殊”HTML 字符将被编码为百分比编码表示形式,但参数分隔符将是一个 & 符号。在大多数理智的情况下,这不会是一个问题,但有时你的键名可能是quot;然后你的链接将失效:

    $qs = [
        'a' => 'a',
        'quot;' => 'bar',
    ];
    echo '<a href="?' . http_build_query($qs) . '">Link</a>';
    

    上面的代码将生成此链接:?a=a"%3B=bar!
    IMO 这意味着该功能http_build_query需要通过第三个参数调用上下文感知&amp;当在 HTML 中时,并且只需&当在header('Location: ...');。另一种选择是通过它htmlspecialchars在以 HTML 形式显示之前。

  • PHP 手册urlencode https://www.php.net/manual/en/function.urlencode.php (IMO 很久以前就应该弃用)建议仅对查询字符串的值部分进行编码,然后将整个查询字符串传递给htmlentities在以 HTML 形式显示之前。这对我来说看起来非常不正确;关键部分仍然可能包含禁止的 URL 字符。

    $query_string = 'foo=' . urlencode($foo) . '&bar=' . urlencode($bar);
    echo '<a href="mycgi?' . htmlentities($query_string) . '">';
    

我的结论是按照以下思路做一些事情:

$qs = [
    'a' => 'a',
    'quot;' => 'bar foo',
];
echo '<a href="?' . http_build_query($qs, null, '&amp;', PHP_QUERY_RFC3986) . '">Link</a>';

在 PHP 中创建 HTML 链接的推荐方法是什么?有比我想出的更简单的方法吗?我是否遗漏了任何关键点?


如何使用查询字符串动态构建 HTML 链接?

如果您需要创建要在 HTML 链接中使用的查询字符串(例如<a href="index.php?param1='.$value.'">Link</a>)那么你应该使用http_build_query https://www.php.net/manual/en/function.http-build-query.php。 该函数接受 4 个参数,第一个参数是查询数据的数组/对象。在大多数情况下,其他 3 个参数是无关紧要的。

$qs = [
    'a' => 'a',
    'quot;' => 'bar foo',
];
echo '<a href="?' . http_build_query($qs) . '">Link</a>';

然而,你仍然应该通过函数的输出htmlspecialchars https://www.php.net/manual/en/function.htmlspecialchars.php来编码&正确。“一个好的框架会自动执行此操作,例如 Laravel 的 {{ }}”

echo '<a href="?' . htmlspecialchars(http_build_query($qs)) . '">Link</a>';

或者,您可以将第三个参数传递给http_build_query as '&amp;',留下第二个null。这将使用&amp;代替&这是什么htmlspecialchars会做。

关于空间.
用于表单数据(即查询字符串)时,空格应编码为+在任何其他地方,它应该编码为%20 https://stackoverflow.com/a/29948396/1839439 e.g. new%20page.php?my+field=my+val。这是为了确保与所有浏览器的向后可比性。您可以使用较新的 RFC3986 它将空格编码为%20它可以在所有常见的浏览器中运行,并与现代标准保持同步。

echo '<a href="?' . http_build_query($qs, null, '&amp;', PHP_QUERY_RFC3986) . '">Link</a>';

rawurlencode vs urlencode

对于 URL 之前的任何部分?你应该使用rawurlencode https://www.php.net/manual/en/function.rawurlencode.php。例如:

$subdir = rawurlencode('blue+light blue');
echo '<a href="'.$subdir.'/index.php">rawurlencode</a>';

如果在上面的例子中你使用了urlencode该链接将被破坏。urlencode用途非常有限,应避免使用。

不要传递整个 URLrawurlencode。分离器/URL 中的其他特殊字符如果要实现其功能,则不应进行编码。


Footnote

对于使用的最佳实践尚未达成普遍共识http_build_query,除了它应该通过的事实之外htmlspecialchars就像 HTML 上下文中的任何其他输出一样。

Laravel https://github.com/laravel/framework/blob/024783a3328c1e666390307e6704e64f256cb327/src/Illuminate/Support/Arr.php#L592 uses http_build_query($array, null, '&', PHP_QUERY_RFC3986)

代码点火器 https://github.com/codeigniter4/framework/blob/4fc8c8549ea009c7489183e951d7da7f8a7dcd39/system/HTTP/URI.php#L826 uses http_build_query($query)

Symfony https://github.com/symfony/symfony/blob/2c9a1960a164a857cd551881b580266bc77bdafa/src/Symfony/Component/Routing/Generator/UrlGenerator.php#L291 uses http_build_query($extra, '', '&', PHP_QUERY_RFC3986)

Slim https://github.com/slimphp/Slim/blob/fe39e6ef0e669a0003123247e9a9da73f10fef92/Slim/Router.php#L389 uses http_build_query($queryParams)

CakePHP https://github.com/cakephp/cakephp/blob/5abb52489c3c98631c21a740ca9024c579c77b1e/src/Http/Client.php#L470 uses http_build_query($query)

Twig https://github.com/twigphp/Twig/blob/1190d3d42031d0484f421bb5dc2e8759e786caa4/src/Extension/CoreExtension.php#L608 uses http_build_query($url, '', '&', PHP_QUERY_RFC3986)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 PHP 中正确创建 HTML 链接? 的相关文章

随机推荐