Disable "square bracket to array" parsing of form value names - php

As you know, if you have a form containing
<select name="user">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="text" name="user[name]" />
<input type="password" name="user[password]" />
And you submit that form, in PHP, $_REQUEST['user'] will automatically become an array containing the keys name and password, and the value of the select will be gone. The opposite occurs if the <select> is moved after the other two fields. $_REQUEST['user'] will contain the value of the select, and the values in the inputs will be gone, with nothing being set to either $_REQUEST['user[name]'] or $_REQUEST['user[password]'].
To my knowledge, the same applies to $_POST, $_GET and $_FILES.
Also, input streams are unavailable when the form's MIME is multipart/form-data.
So, is there any way to disable this automatic parsing?

A. change
<select name="user">
to
<select name="user[user]">
That way you have a key set for the user array. If you are going to use post arrays you have to make sure every single one has a key.
B. (the way i'd do it)
Forget about the arrays. Use a unique name for each one. Using the arrays give you no benefit. Call the first one user, the second one, user_name, and the 3rd "password"

I wish there was a way to disable PHP's arrayification of query parameter names, but I don't think there is. What you can do (except with enctype="multipart/form-data") is parse the query yourself using something like:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$qs = file_get_contents ('php://input');
} else {
$qs = $_SERVER['QUERY_STRING'];
}
if ($qs !== '') {
$params = explode ('&', $qs);
foreach ($params as $p) {
$nv = explode ('=', $p, 2);
$name = urldecode ($nv[0]);
$value = urldecode ($nv[1]);
echo htmlspecialchars ($name), ': "', htmlspecialchars ($value), "\"\n";
}
}
This also gets around PHP's annoying habit of replacing "illegal" characters in query parameter names, such as dots with underscores.

There's no way to do it as of version 5.6 main development branch at 20 June 2014

No, it is not possible.
If you don't want form arrays, don't use form arrays. Call your inputs something else. It's quite simple.
And apparently you are not interested in workable, conventional and meaningful solutions to your problem.

Related

php warning: strtolower() expects parameter 1 to be string, array given

I have a querystring variable whitelist checker on my website. It checks the given url against permissible/allowed querystring variables (key and value). The first part checks the querystring key against allowed keys - if the key is not allowed (not in the whitelist) it will reject the querystring. the second part checks the "value" part of the querystring key/value to see if the value includes a bad word from a blacklist - if the value is in the blacklist it will reject the querystring.
This seems to work well but I notice from my server logs that the line that converts the querystring value to lowercase causes a PHP warning:
PHP Warning: strtolower() expects parameter 1 to be string, array
given
This is the code:
$rejectqstr = "N";
//loop through the querystring, check each querystring KEY against the three whitelist arrays. Any key found which is not in the list will set the reject variable to Y
foreach ($_GET as $key => $value){
if(in_array($key, $cmsnumparams) || in_array($key, $cmsstrparams) || in_array($key, $cmsboolparams)){
//do nothing if it found
} else {
$rejectqstr = "Y";
}
//loop through the blacklist values and check each querystring value against the list
$value = strtolower($value);
foreach($cmsblklstparams as $blklist){
if(strpos($value, $blklist) !== false){
$rejectqstr = "Y";
}
}
}
its the line $value = strtolower($value); that is logged as a warning in the server logs.
I can't see what is wrong with this. My blacklist array (cmsblklstparams) is all lowercase so I convert the querystring value to lowercase before seeing if it is in the blacklist array.
This warning is not listed all the time in the server log so I guess it could be caused if a user tried to "inject" something in the querystring (changing it from a string to an array)?
Is there a better way of doing this or should I check if $value is an array (if so, reject the querystring) before converting it to lowercase?
I've tried testing the code with normal querystrings and it seems to work ok so I'm not sure what it being added to the querystring to cause this warning in the server logs.
Many thanks.
I guess it could be caused if a user tried to "inject" something in the querystring (changing it from a string to an array)?
I would agree, either intentionally or unintentionally. Take for example the following HTML form:
<form action="my-request.php" method="GET">
<label for="checkbox-setting1">
Setting 1
<input type="checkbox" id="checkbox-setting1" name="setting[]" value="setting1" />
</label>
<label for="checkbox-setting2">
Setting 2
<input type="checkbox" id="checkbox-setting2" name="setting[]" value="setting2" />
</label>
<label for="checkbox-setting3">
Setting 3
<input type="checkbox" id="checkbox-setting3" name="setting[]" value="setting3" />
</label>
<button type="submit">Search</button>
</form>
Assume this form is submitted with every checkbox checked, it will make a request using the URL .../my-request.php?setting[]=setting1&setting[]=setting2&setting[]=setting3
This is a valid GET request and PHP will interpret the setting query string parameter as an array. The way your current code is setup is that it always assumes that the incoming query string parameters will be a String.
It sounds like you want to do a type check to check if the currently iterated query string parameter is an array. If it is and you do not allow for arrays, then do your rejection. If it is and you do allow for arrays, then setup an inner loop:
// do not allow query string parameter values
if (is_array($value))
{
$rejectqstr = "Y";
}
// or allow them
foreach($cmsblklstparams as $blklist)
{
if (is_array($value))
{
foreach($value as $innerValue)
{
if(strpos($innerValue, $blklist) !== false)
{
$rejectqstr = "Y";
}
}
}
else if(strpos($value, $blklist) !== false)
{
$rejectqstr = "Y";
}
}

PHP object class not getting the submit field value

When I print the $letter variable, I am not getting the correct value. It always coming 0.
index.php
<form method="get" class="txtweb-form" action="index.php">
Guess a letter: <input type="text" name="txtweb-message" />
<input type="submit" value="submit" name="guess" />
</form>
<?php
$params = (object) $_REQUEST;
//print_r($params);
if (isset($params->guess)) {
$letter = $params->txtweb-message;
echo $letter;exit;
}
?>
You need to probably use _ instead of - in input name. - is not a valid character in PHP variable of property names.
What is actually happening is this:
$letter = $params->txtweb - message; // a subtraction operation
You end up subtracting an unset constant message from an unset object property $params->txtweb. Thus you get 0.
You can keep - in input name but you should use $_REQUEST['txtweb-message'] or $_GET['txtweb-message'] (without casting to object) to retrieve the value.
There really is no reason whatsoever to cast the superglobal array to an object, and this is what introduced your issue.
An additional note here. You really should be developing with error reporting turned on. That operation shown above would have resulted in two warnings showing up, which could have helped you understand what is happening.
why are you casting into an object?
wouldn't treating it as an array be easier like
<?php
$params = $_REQUEST;
if (isset($params["guess"])) {
$letter = $params["txtweb-message"];
echo $letter;exit;
}
?>

Is it possible to get empty array value via $_GET?

Is it possible to get empty array(array with 0 items) value via $_GET?
Is it possible with direct value setting?
count($_GET['param'])==0
You just need an empty value url = myform.php?param=&param2=
In form just let the value blank:
<input type='text' name='param' value ='' />
For an empty array:
url: myform.php?param[]=&param2[some_key]=
in form: <input type='text' name='param[]' value ='' />
From Ajax: (I remember this was so anti-intuitive and hard to search for):
ajax{
...
data: {'params[]':'','params2[some_key]':''}
}
Workaround:
Just edit the back-end and if there is no data for params or it is not an array (null, empty whatever ..) just assign an empty string to it:
$param = (isset($_GET['param']) && is_array($_GET['param']))? $_GET['param'] : array();
Update:
I did few tests and it seems there is no way to put "nothing" in the request ussing a form or ajax.
0, '', Null are valid values for the $_GET but empty array is not even created.
So to answer your question, it is NOT possible to get empty array value from the front-end.
There are few options to edit $_GET manually in the back-end:
<?php
if(!isset($_GET['param']) || !$_GET['param']){ //not set or (null,0,"")
$_GET['param'] = array();
}
if(count($_GET['param'])==0){...}; // 0 if no 'param' was provided.
If array is empty or containing values that doesn't matters. Just declare a variable and pass the $_GET[] to the variable.
for example,
$para=$_GET['param'];
and now
if(is_array($para))
{
//
}
else{
$para=new array();
}
passing empty array via GET is not possible under normal situation. That said, I can think of a really rare way that the checking you used will return true.
http://domain.com/receiver?param=a%3A0%3A%7B%7D
The above is basically a serialized and urlencoded empty array with the key 'param'
if the target server have some sort of filter that auto unserialize all incoming data, then it might happen.
(or something like the below)
foreach($_GET as $key => $value){
$_GET[$key] = unserialize($value);
}
count($_GET['param'])==0
I know this is a far fetch but it is possible, maybe some private test server that only handles serialized data but accidentally open to public e.t.c.
That said, it is still only passing a serialized empty array instead of a empty array itself. But let's be honest, this answer is more like a joke/fun answer that tries to point out under some very rare case
count($_GET['param'])==0
will return true (Without actively assigning values # server side)

Validating bunch of form data in php - Is it necessary?

I am making a survey website. I just want to know if it's really necessary to validate each and every POST data into the database by using isset and !empty. I have around 30 columns and I find it redundant.
Is there any cleaner way than this?
EDIT:
And how about if I have optional fields? How do I come about that?
You can do this such way:
PHP 5.5:
if(!empty(array_filter($_POST)))
{
//there are non-empty fields in $_POST
}
PHP <5.5:
$filtered = array_filter($_POST);
if(!empty($filtered))
{
//there are non-empty fields in $_POST
}
this difference is because before PHP 5.5 empty() was not able to accept non-variables (i.e. expressions, for example)
Edit: if you have some optional fields, then you can deal with this, for example, with having list of mandatory fields:
$array = ['one'=>36, 'two'=>null, 'three'=>'data', 'four'=>0];
$mandatory = ['one', 'three'];
$data = array_intersect_key($array, array_flip($mandatory));
if(!empty($data))
{
//all mandatory fields are not empty
}

Dynamically create variables in PHP

I want to create 1 variable name, but part of the name is the value stored in $i. Same for the GET result:
$Site.$i = $_GET['site'.$i]; // Should look something like $Site1 = $GET['site1'];
Please help me understand how to do this.
If you want a set of related variables, use an array:
$site[ $i ] = $_GET['site'.$i];
Even better, your GET parameters can also be an array
HTML
<input name="site[foo]" value="bar" />
PHP
$site = $_GET[ "site" ];
print_r( $site );
output
$site = array(
"foo" => "bar"
)
If you want the indexes for the array to decided automatically then you can do
<input name="site[]" value="foo" />
<input name="site[]" value="bar" />
<input name="site[]" value="baz" />
and get $_GET[ "site" ] out as
$site = array(
0 => "foo",
1 => "bar",
2 => "baz"
);
Direct Answer to Question
This is how you can do it. Not the best idea however.
$var = "$Site$i";
$$var = $_GET['site'.$i];
This makes use of variable variables.
Alternative Maintaining Current URL Structure
Alternatively perhaps something like this might work for you:
$vars = array();
foreach($_GET as $key => $value) {
if(0 === strpos($key, 'site')) { // Only grab value if the key is prefaced by the string 'site'
// You must sanitise the value some way here eg:
// $value = filter_var($value, FILTER_SANITIZE_STRING);
$vars[] = $value;
}
}
See filter_var() man page for more information on PHP filters and sanitisation/validation.
Revised URL Structure
I think this probably best solved however by making use of HTML arrays at the point your URL is generated. For more information on HTML arrays please see the PHP man page.
This allows you to access your information like the following:
$site1 = $_GET['site'][0];
$site2 = $_GET['site'][4];
This is the most logical method of dealing with this situation.
Update also see #Mat's answer for more information on this.
This is a bad idea for several reasons:
You have to loop through $_GET to find all variables (there's no language construct to pattern-match them)
Dynamic variables names are confusing, and may open security holes.
You will find that using an array will solve the second point, and also make it a lot easier to work with the code.
The first point can be solved by only using variable names you know. Send a variable containing a count how how many "sites" there are, for example:
site1=example&site2=example2&sitecount=2
This way you know that you only need to read site1 and site2, and you donät need to examine any other GET variables.
you van use $ as $_GLOBAL like this.
${'Site' . $i} = $_GET['site' . $i];
or you can use extract
please read the warnings about exract.
You can use variable variables like this:
$varname = $Site.$i;
$$varname = $_GET['site'.$i];
Doing this is discouraged however, because this is a huge security risk. You may write classes with fields representing your values from $_GET and validating them within the class.

Categories