views:

66

answers:

3

I've been working a lot with forms lately and decided to make a php script to simplify some aspects that I see myself repeating, I won't post the full monstrosity that I have created, but instead I will ask you to help me simplify the following code if possible:

function add($name,$input,&$array)
{
 $p = explode('[',$name);
 if(isset($p[1]))
 {
  list($key) = explode(']',$p[1]);
  if(ctype_digit($key))
  {
    $array['forms'][$p[0]][$key] = $input;
  }else{
    $array['forms'][$p[0]][] = $input;
  } 
 }else{
    $array['forms'][$name] = $input;
 }
}


$array = array();
add('image[]','',$array);
add('image[8]','',$array);
add('image[1]','',$array);
add('image[]','',$array);

echo '<PLAINTEXT>';
print_r($array);

it makes $array into:

Array
(
    [forms] => Array
        (
            [image] => Array
                (
                    [0] => 
                    [8] => 
                    [1] => 
                    [9] => 
                )

        )

)

the problem here is that if you add a "image" as the $name, then it must be added to the array as if it was Posted, so it will be array(image=>data), if you enter image[], then it will be array(image=>array(0=>data)).

I find my code to be way too bulky, I found parse_str, which parses the "image[]", but it did not serve me as I need the names to be added separately...

Can this function be made more elegant?

clarification:

Is there a better way to add "name[]" into a array as if it was part of a list of names to be added to the array.

so I need a parse_str replacement that does not overwrite the $array. Example:

$array = array();
parse_str('image[]=test',$array);
parse_str('image[]=test1',$array);
parse_str('image[]=test2',$array);

but the result looks like:

Array
(
    [image] => Array
        (
            [0] => test2
        )

)

but needs to look like:

Array
(
    [image] => Array
        (
            [0] => test
            [1] => test1
            [2] => test2
        )

)

This would really simplify the above function!

+1  A: 

Right, another attempt with your clarification in mind:

function add($name,&$array)
{
    $type = explode('[',$name);
    $key = str_replace(array($type['0'], ']=', '[', ']'), '', $name);
    $array[$type['0']][] = $key;
}

$array = array();
add('image[]=test',$array);
add('image[test1]',$array);
add('image[]=test2',$array);
add('video[test5]',$array);

echo '<PLAINTEXT>';
print_r($array);

Will return:

Array
(
  [image] => Array
      (
          [0] => test
          [1] => test1
          [2] => test2
      )

  [video] => Array
      (
          [0] => test5
      )

)

OK, one last go! There isn't a suitable function to my knowledge, and its not terribly easy to tidy your existing code more than it is, but I did my best!

function add($name,&$array)
{
    $type = explode('[',$name);
    $key = (!empty($type[1])) ? explode(']', $type[1]) : false;
    $value = str_replace(array($key[0], $type[0], ']=', '[', ']'), '', $name);
    if ($key[0]) $array[$type['0']][$key[0]] = $value;
    else $array[$type['0']][] = $value;
}

$array = array();
add('image[]=test',$array);
add('image[text8]=test4',$array);
add('image[]=test2',$array);
add('video[test5]',$array);

echo '<PLAINTEXT>';
print_r($array);

Will return:

Array
(
  [image] => Array
      (
          [0] => test
          [text8] => test4
          [1] => test2
      )

  [video] => Array
      (
          [test5] => 
      )

)
John
thanks for the second attempt :) but your example should return array('image'=>array(0=>'test','test1'=>'',1=>'test2'),'video'=>array('test5'=>''))
YuriKolovsky
Isn't there some built in function that does this?
YuriKolovsky
@John haha nice last attempt, I'm surprised at your patience. But! I have found a error in your code, if I set image[0] the specific 0 will be ignored and will auto-increment it to a higher value! But It is "shorter" than mine ;)
YuriKolovsky
also it does not seem to work with 'file=name', which is a valid key value pair.
YuriKolovsky
+1  A: 

Perhaps throwing in an array_merge_recursive can help you simplify your code a bit. Working from John's method signature it might look something like this :

function add($value, &$target) {
    $temp = array();
    parse_str($value, $temp);
    $target = array_merge_recursive($target, $temp);
}

$array = array();
add('image[]=test',$array);
add('image[text8]=test4',$array);
add('image[]=test2',$array);
add('video[test5]',$array);

Which (as expected, I believe) also yields

Array
(
    [image] => Array
        (
            [0] => test
            [text8] => test4
            [1] => test2
        )

    [video] => Array
        (
            [test5] => 
        )

)

Hopefully this helps :)

edit :

If you'd like to have exactly the same behavior (in a minimal amount of lines), you could always rebuild the query string, append your value and parse it again. The resulting code isn't optimal or pretty, but it does the job ;). Illustrated :

function add($value, &$target) {
    $query = urldecode(http_build_query($target, '', '&'));
    $query = "{$query}&{$value}";
    parse_str($query, $target);
}

$array = array();

add($array, 'image[]');
add($array, 'image[8]=test4');
add($array, 'image[1]=test2');
add($array, 'image[]');

print_r($array);

Would result in

Array
(
    [image] => Array
        (
            [0] => 
            [8] => test4
            [1] => test2
            [9] => 
        )

)
taitale
It does not behave exactly like post, in that it forgets the keys if they are numbers, but I can find use in this ;)
YuriKolovsky
Glad you like it :), I also added an alternative based on http_build_query which should result in the behavior as described in your post.
taitale
YuriKolovsky
the performance hit is negligible really, it might not be optimal, but if multi-level-inputs in post/get ever get implemented your function will keep on working :)
YuriKolovsky
taitale
+1  A: 

I'm not terribly sure why preg hasn't been mentioned yet, but that seems like that would be what you really want

function add( $input, &$target )
{
    // sanity check.  If the brackets are missing, add them.
    if( strpos( $input, '[' ) === FALSE )
    {
        $input = str_replace( '=', '[]=',  $input );
    }
    // The regex means, "Get the starting variable name segment" 
    // (begins with a letter here, you could just make it \w+)
    // followed by the portion between the left and right brackets
    // and finally by the value after the period until the end of the input
    preg_match( '/^([a-zA-Z]\w*)\[(\w*)\]=?(.*)$/', $input, $matches );

    // the first value in the matches array is the original string.
    array_shift( $matches );

    // sanity check -- if the string doesn't match the above regex, ignore it.
    if( !isset( $matches[ 1 ] ) ) return;
    $m1 = $matches[ 1 ];
    $m2 = ( isset( $matches[ 2 ] ) )? $matches[ 2 ]: NULL;

    // numeric keys are translated to the equivalent of array_push.
    if( is_numeric( $m1 ) || $m1 == "" )
    {
        $target[ $matches[ 0 ] ][] = $m2;
    }
    // non-numerics are kept as is.
    else
    {
        $target[ $matches[ 0 ] ][ $m1 ] = $m2;
    }
}
Christopher W. Allen-Poole
does not work with "file=test"
YuriKolovsky
No, it looked like name[] or name[ val ] were givens. Give me a sec and I'll see what I can do
Christopher W. Allen-Poole
Well, not the ideal. I simply added a sanity check.
Christopher W. Allen-Poole
+1 for the useful regex, although I still consider taitale's answer to be the best so far.
YuriKolovsky