Skip to content

Instantly share code, notes, and snippets.

@rubo77
Forked from Shagshag/gist:5849065
Last active May 7, 2021 11:14
Show Gist options
  • Save rubo77/6821632 to your computer and use it in GitHub Desktop.
Save rubo77/6821632 to your computer and use it in GitHub Desktop.
This is a replacement for the original idea how to cirumvent the max_input_vars limitation, that uses the original parse_str() on each element of the query to parse
<?php
/**
* do the same than parse_str without max_input_vars limitation:
* Parses $string as if it were the query string passed via a URL and sets variables in the current scope.
* @param $string array string to parse (not altered like in the original parse_str(), use the second parameter!)
* @param $result array If the second parameter is present, variables are stored in this variable as array elements
* @return bool true or false if $string is an empty string
*
* @author rubo77 at https://gist.github.com/rubo77/6821632
**/
function my_parse_str($string, &$result) {
if($string==='') return false;
$result = array();
// find the pairs "name=value"
$pairs = explode('&', $string);
foreach ($pairs as $pair) {
// use the original parse_str() on each element
parse_str($pair, $params);
$k=key($params);
if(!isset($result[$k])) $result+=$params;
else $result[$k] = array_merge_recursive_distinct($result[$k], $params[$k]);
}
return true;
}
// better recursive array merge function listed on the array_merge_recursive PHP page in the comments
function array_merge_recursive_distinct ( array &$array1, array &$array2 ){
$merged = $array1;
foreach ( $array2 as $key => &$value ) {
if ( is_array ( $value ) && isset ( $merged [$key] ) && is_array ( $merged [$key] ) ){
$merged [$key] = array_merge_recursive_distinct ( $merged [$key], $value );
} else {
$merged [$key] = $value;
}
}
return $merged;
}
my_parse_str($query, $array);
?>
@rubo77
Copy link
Author

rubo77 commented Oct 4, 2013

used for my script to circumvent the max_input_vars limitation in PHP https://gist.github.com/rubo77/6815945

@marek-saji
Copy link

This does not work, when elements with empty keys appear in query string (like a[]=1&a[]=2). Using array_merge in line 23 fixes that.

@sebastianwebb
Copy link

Unfortunately this function isn't fully working for my multidimensional array of 6/7 levels deep. I feel there is probably a need for a recursive function somewhere. Or the array_merge technique marek-saji flagged. I'm just having some issues getting my head around it. I'll post an answer here if I manage to crack it.

@sebastianwebb
Copy link

sebastianwebb commented Nov 3, 2014

OK so I managed to sort this in the end. I adjusted the function by removing the urldecode() function (which was causing probs if any values had a "&" - and it's redundant because pasre_str calls urldecode).

Critically though, I replaced line 23 with a recursive array merge function, which is a slight adaptation of the native array_merge_recursive function. The native one was creating unwanted additional keys.

~ added in Revision 7 ~

@rubo77
Copy link
Author

rubo77 commented Apr 21, 2016

@sebastianwebb: Thanks, I added your version

@infrat
Copy link

infrat commented May 25, 2016

And again, as @marek-saji noticed, there's still some problems when dynamic-keys notation such as:
"key[]=1&key[]=2&key[]=3"

I'd resolved it with passing $dynamicKey flag to merging function:

/**
 * @param $string
 * @return array|bool
 */
private function parseUrl($string)
{
    if($string==='') {
        return false;
    }

    $result = array();
    // find the pairs "name=value"
    $pairs = explode('&', $string);
    foreach ($pairs as $pair) {
        $dynamicKey = false !== strpos($pair,'[]=');
        // use the original parse_str() on each element
        parse_str($pair, $params);
        $k = key($params);
        if (!isset($result[$k])) {
            $result += $params;
        } else {
            $result[$k] = $this->arrayMergeRecursiveDistinct($result[$k], $params[$k], $dynamicKey);
        }
    }
    return $result;
}

/**
 * @param array $array1
 * @param array $array2
 * @param $dynamicKey
 * @return array
 */
private function arrayMergeRecursiveDistinct(array &$array1, array &$array2, $dynamicKey)
{
    $merged = $array1;
    foreach ($array2 as $key => &$value) {
        if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
            $merged[$key] = self::arrayMergeRecursiveDistinct($merged[$key], $value, $dynamicKey);
        } else {
            if ($dynamicKey) {
                $merged[] = $value;
            }
            $merged[$key] = $value;
        }
    }
    return $merged;
}

@kgilden
Copy link

kgilden commented Oct 21, 2016

@infrat I think there might be 2 slight problems.

  • dynamic keys in encoded query strings are not properly detected. In parseUrl the dynamic key check should be changed to:

    $dynamicKey = (false !== strpos($pair, '[]=')) || (false !== strpos($pair, '%5B%5D='));
  • in arrayMergeRecursiveDistinct and else clause is missing, causing values to be mistakenly overwritten. Should be changed to:

    if ($dynamicKey) {
        $merged[] = $value;
    } else {
        $merged[$key] = $value;
    }

@OriginalEXE
Copy link

Thanks a lot guys for sharing this.

I had some issues with the functions when using dynamic keys, and after a few hours of head banging, here is the final version that seems to be working for me:

// https://gist.github.com/rubo77/6821632
/**
* @param $string
* @return array|bool
*/
function parseUrl($string) {
  if($string==='') {
    return false;
  }
  
  $result = array();
  // find the pairs "name=value"
  $pairs = explode('&', $string);
  foreach ($pairs as $pair) {
    $dynamicKey = (false !== strpos($pair, '[]=')) || (false !== strpos($pair, '%5B%5D='));
    // use the original parse_str() on each element
    parse_str($pair, $params);
    $k = key($params);
    if (!isset($result[$k])) {
      $result += $params;
    } else {
      $result[$k] = arrayMergeRecursiveDistinct($result[$k], $params[$k], $dynamicKey);
    }
  }
  return $result;
}

/**
* @param array $array1
* @param array $array2
* @param $dynamicKey
* @return array
*/
function arrayMergeRecursiveDistinct(array &$array1, array &$array2, $dynamicKey) {
  $merged = $array1;
  foreach ($array2 as $key => &$value) {
    if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
      $merged[$key] = arrayMergeRecursiveDistinct($merged[$key], $value, $dynamicKey);
    } else {
      if ($dynamicKey) {
        
        if ( ! isset( $merged[$key] ) ) {
          
          $merged[$key] = $value;
          
        } else {
          
          if ( is_array( $merged[$key] ) ) {
            
            $merged[$key] = array_merge_recursive( $merged[$key], $value );
            
          } else {
            
            $merged[] = $value;
            
          }
          
        }
        
      } else {
        
        $merged[$key] = $value;
        
      }
    }
  }
  return $merged;
}

@phinor
Copy link

phinor commented Sep 19, 2019

This last function still has inconsistent behaviour with parse_str:

$string = "key[][index]=1&key[][index]=2&key[][index]=3&var2[][]=1&var2[][]=2";
$parsed = my_parse_str($string);
print_r($parsed);
parse_str($string, $parsedOrig);
print_r($parsedOrig);

The goal, as I understood it, was to mirror the functionality of parse_str without the limitation of the max_input_vars limit.

@phinor
Copy link

phinor commented Sep 19, 2019

OK, the following seems to mirror parse_str perfectly, including dynamic keys which are not last. Specifically, the my_parse_str function becomes:

function my_parse_str($string)
{
    if ($string === '') {
        return [];
    }

    $vars = explode ('&', $string);
    $result = [];
    $count = [];
    foreach ($vars as $var) {
        $dkIndex = strpos($var, '[]');
        if (false === $dkIndex) {
             $dkIndex = strpos ($var, '%5B%5D');
        }
        if (false !== $dkIndex) {
            $varPrefix = substr($var, 0, $dkIndex);
            if (!isset ($count [$varPrefix])) {
                $count [$varPrefix] = 0;
            }
            $var = str_replace(['[]', '%5B%5D'], "[" . $count [$varPrefix]++ . "]", $var);
        }

        // use the original parse_str() on each element
        $params = null;
        parse_str ($var, $params);
        $k = key ($params);
        if (!isset($result[$k])) {
            $result += $params;
        }
        else {
            $result[$k] = array_merge_recursive_distinct ($result[$k], $params[$k]);
        }
    }

    return $result;
}

This works with the following test case:

$string = "key[][index]=1&key[][index]=2&key[][index]=3&var2[][]=1&var2[][]=2&key[1][index]=hi";
$parsed = my_parse_str($string);
print_r ($parsed);
parse_str ($string, $results);
print_r ($results);

Note carefully the overwritten second key[][index]=2 which is consistent with parse_str's behaviour.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment