Get PHP to stop replacing '.' characters in $_GET or $_POST arrays?

Viewed 46706

If I pass PHP variables with . in their names via $_GET PHP auto-replaces them with _ characters. For example:

<?php
echo "url is ".$_SERVER['REQUEST_URI']."<p>";
echo "x.y is ".$_GET['x.y'].".<p>";
echo "x_y is ".$_GET['x_y'].".<p>";

... outputs the following:

url is /SpShipTool/php/testGetUrl.php?x.y=a.b
x.y is .
x_y is a.b.

... my question is this: is there any way I can get this to stop? Cannot for the life of me figure out what I've done to deserve this

PHP version I'm running with is 5.2.4-2ubuntu5.3.

13 Answers

Here's PHP.net's explanation of why it does it:

Dots in incoming variable names

Typically, PHP does not alter the names of variables when they are passed into a script. However, it should be noted that the dot (period, full stop) is not a valid character in a PHP variable name. For the reason, look at it:

<?php
$varname.ext;  /* invalid variable name */
?>

Now, what the parser sees is a variable named $varname, followed by the string concatenation operator, followed by the barestring (i.e. unquoted string which doesn't match any known key or reserved words) 'ext'. Obviously, this doesn't have the intended result.

For this reason, it is important to note that PHP will automatically replace any dots in incoming variable names with underscores.

That's from http://ca.php.net/variables.external.

Also, according to this comment these other characters are converted to underscores:

The full list of field-name characters that PHP converts to _ (underscore) is the following (not just dot):

  • chr(32) ( ) (space)
  • chr(46) (.) (dot)
  • chr(91) ([) (open square bracket)
  • chr(128) - chr(159) (various)

So it looks like you're stuck with it, so you'll have to convert the underscores back to dots in your script using dawnerd's suggestion (I'd just use str_replace though.)

Long-since answered question, but there is actually a better answer (or work-around). PHP lets you at the raw input stream, so you can do something like this:

$query_string = file_get_contents('php://input');

which will give you the $_POST array in query string format, periods as they should be.

You can then parse it if you need (as per POSTer's comment)

<?php
// Function to fix up PHP's messing up input containing dots, etc.
// `$source` can be either 'POST' or 'GET'
function getRealInput($source) {
    $pairs = explode("&", $source == 'POST' ? file_get_contents("php://input") : $_SERVER['QUERY_STRING']);
    $vars = array();
    foreach ($pairs as $pair) {
        $nv = explode("=", $pair);
        $name = urldecode($nv[0]);
        $value = urldecode($nv[1]);
        $vars[$name] = $value;
    }
    return $vars;
}

// Wrapper functions specifically for GET and POST:
function getRealGET() { return getRealInput('GET'); }
function getRealPOST() { return getRealInput('POST'); }
?>

Hugely useful for OpenID parameters, which contain both '.' and '_', each with a certain meaning!

Highlighting an actual answer by Johan in a comment above - I just wrapped my entire post in a top-level array which completely bypasses the problem with no heavy processing required.

In the form you do

<input name="data[database.username]">  
<input name="data[database.password]">  
<input name="data[something.else.really.deep]">  

instead of

<input name="database.username"> 
<input name="database.password"> 
<input name="something.else.really.deep">  

and in the post handler, just unwrap it:

$posdata = $_POST['data'];

For me this was a two-line change, as my views were entirely templated.

FYI. I am using dots in my field names to edit trees of grouped data.

The reason this happens is because of PHP's old register_globals functionality. The . character is not a valid character in a variable name, so PHP coverts it to an underscore in order to make sure there's compatibility.

In short, it's not a good practice to do periods in URL variables.

My solution to this problem was quick and dirty, but I still like it. I simply wanted to post a list of filenames that were checked on the form. I used base64_encode to encode the filenames within the markup and then just decoded it with base64_decode prior to using them.

Related