共计 4756 个字符,预计需要花费 12 分钟才能阅读完成。
最近感觉 http_build_query 函数的性能蛮神奇的。能够将任意一个简单数组转换成一个简单的 URL 查问字符串。于是本人尝试手动实现了一下 (user_http_build_query)。
版本一、主函数递归额定辅助函数实现
/**
* http_build_query 的实现。*
* @param array $queryPayload
* @param string $numericPrefix
* @param string $argSeparator
* @param int $encType
*
* @return string
*/
function user_http_build_query(array $queryPayload, string $numericPrefix = '', string $argSeparator ='&', int $encType = PHP_QUERY_RFC1738): string
{reset($queryPayload);
$queryStr = '';
foreach ($queryPayload as $k => $v) {
// 非凡值解决
if ($v === null) {continue;}
if ($v === 0 || $v === false) {$v = '0';}
// 为了对数据进行解码时获取非法的变量名
if (is_numeric($k) && ! is_string($k)) {$k = $numericPrefix . $k;}
$queryStr .= is_scalar($v)
? sprintf("%s=%s$argSeparator", $encType === PHP_QUERY_RFC3986 ? rawurlencode($k) : urlencode($k), $encType === PHP_QUERY_RFC3986 ? rawurlencode($v) : urlencode($v))
: to_query_str($k, $v, $argSeparator, $encType);
}
return substr($queryStr, 0, -strlen($argSeparator));
}
/**
* 转换值是非标量的状况
*
* @param string $key
* @param array|object $value
* @param string $argSeparator
* @param int $encType
*
* @return string
*/
function to_query_str(string $key, $value, string $argSeparator, int $encType): string
{
$queryStr = '';
foreach ($value as $k => $v) {
// 非凡值解决
if ($v === null) {continue;}
if ($v === 0 || $v === false) {$v = '0';}
$fullKey = "{$key}[{$k}]";
$queryStr .= is_scalar($v)
? sprintf("%s=%s$argSeparator", $encType === PHP_QUERY_RFC3986 ? rawurlencode($fullKey) : urlencode($fullKey), $encType === PHP_QUERY_RFC3986 ? rawurlencode($v) : urlencode($v))
: to_query_str($fullKey, $v, $argSeparator, $encType); // 递归调用
}
return $queryStr;
}
版本二、单函数递归匿名函数实现
用两个函数去实现一个函数的性能,显然是不太称心的。略微改变一下,提取辅助函数为匿名函数到主函数外部,递归匿名函数就能够了。
/**
* http_build_query 的实现。*
* @param array $queryPayload
* @param string $numericPrefix
* @param string $argSeparator
* @param int $encType
*
* @return string
*/
function user_http_build_query(array $queryPayload, string $numericPrefix = '', string $argSeparator ='&', int $encType = PHP_QUERY_RFC1738): string
{
/**
* 转换值是非标量的状况
*
* @param string $key
* @param array|object $value
* @param string $argSeparator
* @param int $encType
*
* @return string
*/
$toQueryStr = static function (string $key, $value, string $argSeparator, int $encType) use (&$toQueryStr): string{
$queryStr = '';
foreach ($value as $k => $v) {
// 非凡值解决
if ($v === null) {continue;}
if ($v === 0 || $v === false) {$v = '0';}
$fullKey = "{$key}[{$k}]";
$queryStr .= is_scalar($v)
? sprintf("%s=%s$argSeparator", $encType === PHP_QUERY_RFC3986 ? rawurlencode($fullKey) : urlencode($fullKey), $encType === PHP_QUERY_RFC3986 ? rawurlencode($v) : urlencode($v))
: $toQueryStr($fullKey, $v, $argSeparator, $encType); // 递归调用
}
return $queryStr;
};
reset($queryPayload);
$queryStr = '';
foreach ($queryPayload as $k => $v) {
// 非凡值解决
if ($v === null) {continue;}
if ($v === 0 || $v === false) {$v = '0';}
// 为了对数据进行解码时获取非法的变量名
if (is_numeric($k) && ! is_string($k)) {$k = $numericPrefix . $k;}
$queryStr .= is_scalar($v)
? sprintf("%s=%s$argSeparator", $encType === PHP_QUERY_RFC3986 ? rawurlencode($k) : urlencode($k), $encType === PHP_QUERY_RFC3986 ? rawurlencode($v) : urlencode($v))
: $toQueryStr($k, $v, $argSeparator, $encType);
}
return substr($queryStr, 0, -strlen($argSeparator));
}
测试
$queryPayload = [
1 => 'a',
'10' => 'b',
'01' => 'c',
'keyO1' => null,
'keyO2' => false,
'keyO3' => true,
'keyO4' => 0,
'keyO5' => 1,
'keyO6' => 0.0,
'keyO7' => 0.1,
'keyO8' => [],
'keyO9' => '','key10'=> new \stdClass(),'pastimes'=> ['golf','opera','poker','rap'],'user'=> ['name'=>'Bob Smith','age'=> 47,'sex'=>'M','dob'=>'5/12/1956'],'children'=> ['sally'=> ['age'=> 8,'sex'=> null],'bobby'=> ['sex'=>'M','age' => 12],
],
];
dd($queryStr1 = http_build_query($queryPayload),
$queryStr2 = user_http_build_query($queryPayload),
$queryStr3 = urldecode($queryStr1),
$queryStr4 = urldecode($queryStr2),
$queryStr1 === $queryStr2,
$queryStr3 === $queryStr4,
);
// "1=a&10=b&01=c&keyO2=0&keyO3=1&keyO4=0&keyO5=1&keyO6=0&keyO7=0.1&keyO9=&pastimes%5B0%5D=golf&pastimes%5B1%5D=opera&pastimes%5B2%5D=poker&pastimes%5B3%5D=rap&user%5Bname%5D=Bob+Smith&user%5Bage%5D=47&user%5Bsex%5D=M&user%5Bdob%5D=5%2F12%2F1956&children%5Bsally%5D%5Bage%5D=8&children%5Bbobby%5D%5Bsex%5D=M&children%5Bbobby%5D%5Bage%5D=12"
// "1=a&10=b&01=c&keyO2=0&keyO3=1&keyO4=0&keyO5=1&keyO6=0&keyO7=0.1&keyO9=&pastimes%5B0%5D=golf&pastimes%5B1%5D=opera&pastimes%5B2%5D=poker&pastimes%5B3%5D=rap&user%5Bname%5D=Bob+Smith&user%5Bage%5D=47&user%5Bsex%5D=M&user%5Bdob%5D=5%2F12%2F1956&children%5Bsally%5D%5Bage%5D=8&children%5Bbobby%5D%5Bsex%5D=M&children%5Bbobby%5D%5Bage%5D=12"
// "1=a&10=b&01=c&keyO2=0&keyO3=1&keyO4=0&keyO5=1&keyO6=0&keyO7=0.1&keyO9=&pastimes[0]=golf&pastimes[1]=opera&pastimes[2]=poker&pastimes[3]=rap&user[name]=Bob Smith&user[age]=47&user[sex]=M&user[dob]=5/12/1956&children[sally][age]=8&children[bobby][sex]=M&children[bobby][age]=12"
// "1=a&10=b&01=c&keyO2=0&keyO3=1&keyO4=0&keyO5=1&keyO6=0&keyO7=0.1&keyO9=&pastimes[0]=golf&pastimes[1]=opera&pastimes[2]=poker&pastimes[3]=rap&user[name]=Bob Smith&user[age]=47&user[sex]=M&user[dob]=5/12/1956&children[sally][age]=8&children[bobby][sex]=M&children[bobby][age]=12"
// true
// true
原文链接
- https://github.com/guanguans/guanguans.github.io/issues/47
正文完