Search code examples
phparrayspostarray-key

Why cant I access $_POST variable with a hyphen/dash in its key if passing key as variable?


I've written a small static method for my class which returns either $_POST variable if it is set or NULL otherwise. Input elements in HTML form have names with hyphens for example 'customer-name'.

So I think I could access them like this $var = $_POST['customer-name']. But with my method:

public static function getPost($param) {
    echo $param." = ".$_POST[$param]."<br/>";
    return isset($_POST[$param]) ? $_POST[$param] : NULL;
}

I can not. And I notice some strange behavior when added some echo statements to my method. It cuts off everything after a hyphen, so I got error:

Notice: Undefined index: customer- in .. on line ..

This is how I test it:

$arr = (array)$object;
$newArr = array();
foreach($arr as $key => $val) {
    $newKey = str_replace(get_class($object), "", $key);
    $newArr[$newKey] = MyObject::getPost(strtolower(get_class($object))."-".$newKey);
}

And this is the output from my test:

...
Notice: Undefined index: customer- in .. on line 116
customer-id =

Notice: Undefined index: customer- in .. on line 116
customer-name =

Notice: Undefined index: customer- in .. on line 116
customer-phonecode =
...

EDIT 1 - I was asked for HTML form:

<form action="" method="post" class="form-horizontal" role="form">
   <input type="text" name="customer-name" id="customer-name" class="form-control" placeholder="Name" required="required" autocomplete="off" />
   <select id="customer-phonecode" name="customer-phonecode" class="form-control">
      <option value="+123"></option>
   </select>
</form>

EDIT 2 - Tested on phptester.net on 5.2, 5.3, 5.4, 5.5 php versions. Getting same error.

EDIT 3 - Tested following script. If passing string as key I get element in super global $_POST/an array. But if passing a variable which points to a string, an element cannot be accessed

<?php
$test = array('customer-test1' => 1, 'customer-test2' => 2);
function getPost($param) {
    global $test;
    $newParam = (string)$param;
    echo $param." = ".$test[$newParam]."<br/>";
    return isset($test[$newParam]) ? $test[$newParam] : NULL;
}
class Customer {
    private $test1;
    private $test2;     
    function __construct() { }
}
$object = new Customer();
$arr = (array)$object;
$newArr = array();
foreach($arr as $key => $val) {
    $newKey = str_replace(get_class($object), "", $key);
    $newArr[$newKey] = getPost(strtolower(get_class($object))."-".$newKey);
}

Might this be a PHP bug?


Solution

  • So the problem was, that casting object to an array adds null characters to array keys. They are not just class name+property name. It's how PHP manages private class properties when casting.

    $object = new Customer();
    $arr = (array)$object;
    print_r(array_map("addslashes", array_keys($arr)));
    

    Outputs:

    Array ( 
            [0] => \0Customer\0test1 
            [1] => \0Customer\0test2
          )
    

    Im not sure why var_dump() doesnt show those null bytes. Might be my next question I guess. So those nulls were still there in my static method argument. But why PHP stops right after dash/hyphen?

    In PHP we can simply write:

    $Tmp= 'hehe';
    

    But for the same in C, we would use the following code:

    Char Tmp [4];
    Tmp [0] = 'h';
    Tmp [1] = 'e';
    Tmp [2] = 'h';
    Tmp [3] = 'e';
    Tmp [4] = '\0';
    

    C handles strings as a character array, it needs a way to define the last character of the string. This is done using a null byte. A null byte is donated by \0 in C. So when the program runs, it starts reading the string from the first character until the null byte is reached. This creates a problem. As we know, PHP is also implemented in C. This can become an issue, because some functions in PHP might handle an input string as they are handled by C.

    Sources: #71673, null-byte-injection-php

    EDIT 1: Solution added

    Solution is to replace '\0' characters as well as class name with "" in my foreach loop:

    foreach($arr as $key => $val) {
        $newKey = str_replace(array(get_class($object), "\0"), "", $key);
        $newArr[$newKey] = getPost(strtolower(get_class($object))."-".$newKey);
    }