美文网首页
递归匿名函数手动实现 http_build_query 系统函数

递归匿名函数手动实现 http_build_query 系统函数

作者: guanguans | 来源:发表于2022-07-11 14:02 被阅读0次

    最近觉得 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
    

    原文链接

    相关文章

      网友评论

          本文标题:递归匿名函数手动实现 http_build_query 系统函数

          本文链接:https://www.haomeiwen.com/subject/luhwjrtx.html