我正在寻找最简洁的URL而不是最短的PHP代码。我不希望我的用户被编码数组时PHP创建的可怕URL吓到。
如果你只是通过http_build_query
填充一个数组($fn
),那么PHP会在查询字符串中做很多重复:
$fs = array(5, 12, 99);
$url = "http://$_SERVER[HTTP_HOST]/?" .
http_build_query(array('c' => 'asdf', 'fs' => $fs));
结果$url
是
http://example.com/?c=asdf&fs[0]=5&fs[1]=12&fs[3]=99
如何将其降至最低(使用PHP或在PHP中轻松实现的方法)?
答案 0 :(得分:5)
http_build_query
的作用是将数组序列化为URL的常用方法。 PHP会自动在$_GET
中反序列化它。
当想要仅序列化(非关联)整数数组时,您还有其他选项。
对于小型数组,转换为下划线列表非常方便有效。它由$fs = implode('_', $fs)
完成。那么您的网址将如下所示:
http://example.com/?c=asdf&fs=5_12_99
缺点是您必须明确explode('_', $_GET['fs'])
才能将值作为数组返回。
也可以使用其他分隔符。下划线被认为是字母数字,因此很少有特殊含义。在URL中,它通常用作空间替换(例如,通过MediaWiki)。在下划线文本中使用时很难区分。连字符是另一种常见的空间替代品。它也经常用作减号。逗号是典型的列表分隔符,但与下划线和连字符不同,由http_build_query
进行百分比编码,并且几乎在任何地方都具有特殊含义。类似的情况是垂直条(“管道”)。
在URL中使用大型数组时,您应该首先停止编写开始思考。这几乎总是表明糟糕的设计。 POST HTTP方法不会更合适吗?您是否有更多可读和节省空间的方法来识别所寻址的资源?
理想情况下,URL应该易于理解并且(至少部分地)记住。在里面放置一个大blob真是个坏主意。
现在我警告过你。如果您仍需要在URL中嵌入大型数组,请继续。尽可能地压缩数据base64 - 对它们进行编码以将二进制blob转换为文本,并对文本进行url-encode以对其进行清理以嵌入URL中。
嗯。或者更好地使用modified version of base64。我选择的是
-
代替+
,_
代替/
和=
。define('URL_BASE64_FROM', '+/');
define('URL_BASE64_TO', '-_');
function url_base64_encode($data) {
$encoded = base64_encode($data);
if ($encoded === false) {
return false;
}
return str_replace('=', '', strtr($encoded, URL_BASE64_FROM, URL_BASE64_TO));
}
function url_base64_decode($data) {
$len = strlen($data);
if (is_null($len)) {
return false;
}
$padded = str_pad($data, 4 - $len % 4, '=', STR_PAD_RIGHT);
return base64_decode(strtr($padded, URL_BASE64_TO, URL_BASE64_FROM));
}
这会在每个字符上保存两个字节,否则将进行百分比编码。也无需调用urlencode
函数。
应该在gzip(gzcompress
)和bzip2(bzcompress
)之间做出选择。不想在比较中投入时间,对于任何块大小的设置,gzip在几个相对较小的输入(大约100个字符)上看起来更好。
但是应该将哪些数据输入压缩算法?
在C中,可以将整数数组转换为字符数组(字节)并将其交给压缩函数。这是最明显的做事方式。在PHP中,最明显的做法是将所有整数转换为十进制表示为字符串,然后使用分隔符进行连接,并且仅在压缩之后进行连接。多么浪费空间!
所以,让我们使用C方法!我们将摆脱分隔符和其他方式浪费空间,并使用pack
编码2个字节的每个整数:
define('PACK_NUMS_FORMAT', 'n*');
function pack_nums($num_arr) {
array_unshift($num_arr, PACK_NUMS_FORMAT);
return call_user_func_array('pack', $num_arr);
}
function unpack_nums($packed_arr) {
return unpack(PACK_NUMS_FORMAT, $packed_arr);
}
警告:在这种情况下,pack
和unpack
行为与机器有关。字节顺序可以在机器之间改变。但我认为这在实践中不会成为问题,因为应用程序不会同时在具有不同字节序的两个系统上运行。但是,在集成多个系统时,可能会出现问题。此外,如果您切换到具有不同字节顺序的系统,使用原始链接的链接将会中断。
现在打包,压缩和修改base64,一体化:
function url_embed_array($arr) {
return url_base64_encode(gzcompress(pack_nums($arr)));
}
function url_parse_array($data) {
return unpack_nums(gzuncompress(url_base64_decode($data)));
}
请参阅result on IdeOne。它优于OP的答案,在他的40元素数组中,我的解决方案产生了91个字符,而他的一个字符为98.使用range(1, 1000)
(生成array(1, 2, 3, …, 1000)
)作为基准时,OP’s solution produces 2712 characters while mine just 2032 characters。这大约好了25%。
为了完整起见,OP的解决方案是
function url_embed_array($arr) {
return urlencode(base64_encode(gzcompress(implode(',', $arr))));
}
答案 1 :(得分:1)
可能有多种方法:
$foo = array('some unsafe data', '&&&==http://', '65535');
$ser = base64_encode(serialize($foo));
$imp = implode($foo, '|');
$imp2 = base64_encode($imp);
echo "$ser\n$imp\n$imp2";
结果如下:
YTozOntpOjA7czoxNjoic29tZSB1bnNhZmUgZGF0YSI7aToxO3M6MTI6IiYmJj09aHR0cDovLyI7aToyO3M6NToiNjU1MzUiO30=
some unsafe data|&&&==http://|65535
c29tZSB1bnNhZmUgZGF0YXwmJiY9PWh0dHA6Ly98NjU1MzU=
虽然序列化+ base64结果非常长,但是implode + serialize为GET提供了可管理长度的输出......除了那个=
之外。
答案 2 :(得分:0)
我认为答案取决于查询字符串的大小。
对于较短的查询字符串,这可能是最好的方法:
$fs = array(5, 12, 99);
$fs_no_array = implode(',', $fs);
$url = "http://$_SERVER[HTTP_HOST]/?" .
http_build_query(array('c' => 'asdf', 's' => 'jkl')) . '&fs=' . $fs_no_array;
导致
http://example.com/?c=asdf&s=jkl&fs=5,12,99
另一方面,你这样做是为了让你的阵列回来:
$fs = array_map('intval', explode(',', $_GET['fs']));
关于分隔符的快速说明:避免使用逗号的正当理由是它们在许多其他应用程序中用作分隔符。例如,在可能的情况下,您可能希望在Excel中解析URL,逗号可能会使其稍微困难一些。下划线也可以工作,但可以与链接的Web格式标准下划线混合使用。因此破折号实际上可能是比逗号或下划线更好的选择。
我遇到了another possible solution:
$fs_compressed = urlencode(base64_encode(gzcompress($fs_no_array)));
另一方面,它可以通过
解压缩$fs_decompressed = gzuncompress(base64_decode($_GET['fs']));
$fs = array_map('intval', explode(',', $fs_decompressed));
假设它是通过GET变量传递的。
$fs = array(7,2,3,4,5,6,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,52,53,54,61);
结果:
eJwFwckBwCAQxLCG%2FMh4D6D%2FxiIdpGiG5fLIR0IkRZoMWXLIJQ8%2FDIqFjYOLBy8jU0yz%2BQGlbxAB
$fs_no_array
长度为84个字符,$fs_compressed
长度为84个字符。同样的!
$fs = array(7,2,3,4,5,6,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,52,53,54,61);
结果:
eJwNzEkBwDAQAzFC84jtPRL%2BxFoB0GJC0QyXhw4SMgoq1GjQoosePljYOLhw48GLL37kEJE%2FDCnSZMjSpkMXow%2BdIBUs
$fs_no_array
长度为111个字符,$fs_compressed
长度为98个字符。
节省的费用仅为10%左右。但是,在更大程度上,节省的费用将增加到50%以上。
如果你使用雅虎网站,你会注意到逗号分隔列表以及有时候一系列随机字符。他们可能已经在野外使用这些解决方案。
另请查看this stack question,其中详细介绍了URI中允许的内容。