First, I'm still fairly new to PHP, so please forgive my lack of understanding. I'm working on a form that takes in options from multiple selects, and provides the user results based on these options. This form also stores these search queries so that they can be exported later on.
Initially, the form only had four parameters needing to be stored, but now I've added a fifth which is making things not so cooperative. I've searched, and tried multiple different things but I've hit a wall and am seeking assistance.
What is intended to happen is the user selects one, or all, of the available options. The first four selects are simple Yes/No questions with values of 1 for Yes, 0 for No. The fifth is a series of county names with the values being set to their id in the database. The county select options are populated dynamically via Smarty.
Here's the PHP used to store these values.
public function recordFilter($args)
{
$db = DBManager::getConnection();
$_POST['type'] = 'f'; // type of search, find providers
foreach($args as $key => $a)
{
if(isset($_GET['county']) && is_numeric($_GET['county'])
{
$_POST[$key] = $a ? (int)$_GET['county'] : 0; // store value of last parameter, or 0 if not set
}
$_POST[$key] = $a ? "y" : "n"; // store 'y' if $a is set, 'n' if not
var_dump($_POST[$key]);
}
parent::save();
}
Currently what is happening is I'm able to get all the values into this function, and iterate through them. But since I've introduced this fifth field (and through the different approaches I've tried to piece this together) is either my fifth parameter gets set to 'y', which won't store in the database as it's field is an int(2), or the set values of the first four parameters take on the value of the fifth parameter, and wind up having the id associated with the county in their fields.
What I'm looking to learn is what better approach is there to handle this type of problem? I thought perhaps a while loop would be appropriate to iterate through the first four parameters and handle the fifth after those are complete, but figuring out the syntax for that is a bit beyond me. I also tried a switch statement but that simply didn't work. Having the if statement seems to be the big wrench in the situation, as it throws the whole loop off if 'county' is set. Any assistance will be greatly appreciated.
Since writing code doesn't look properly on comment, I post my guess here. If you describe more of your code, such as what $args is, or how you handle the request, it'll help people to understand your problem.
Treat the last request separately
Since it's a $_GET request, you can't iterate it with $_POST
foreach ($args as $key => $a) {
$_POST[$key] = $a ? "y" : "n"; // store 'y' if $a is set, 'n' if not
}
if (isset($_GET['county']) && is_numeric($_GET['county']) {
$_POST['county'] = $a ? (int)$_GET['county'] : 0; // store value of last parameter, or 0 if not set
}
And the second, I think this is better approach
Don't rely on super global variable
Assign them to another variable and don't forget to refactor the code on parent::save() method
public function recordFilter($args)
{
$db = DBManager::getConnection();
$request = [];
foreach ($args as $key => $a) {
//Sounds like you forgot to check if the $_POST[$key] is set
$request[$key] = isset($_POST[$key], $a) ? "y" : "n";
}
//You don't have to assign a value to super global $_POST with key of type
$request['type'] = 'f';
//Or you may need if statement if this field doesn't a required field
$request['county'] = (int) $_GET['county'];
parent::save($request);
}
And somewhere in your parent class
protected function save(array $request)
{
//this is your bussiness
}
Since I'm just guessing on what $args is, this is better than the better one
Assign it to local variable with filter_input() and filter_input_array()
public function recordFilter($args)
{
$db = DBManager::getConnection();
//This array will be used for validating the input $_POST.
//First, I grab the key of $args then assign the value
//to $validate with the same key to return it as bool
$validate = [];
foreach(array_keys($args) as $key) {
$validate[$key] = FILTER_VALIDATE_BOOLEAN;
}
//Get the post request value
$request = filter_input_array(INPUT_POST, $validate);
//You don't have to assign a value to super global $_POST with key of type
$request['type'] = 'f';
//Receive 'county' only if it's int and greater than 1 and lower than 99
$options = ['options' => ['min_range' => 1, 'max_range' => 99]];
$request['county'] = filter_input(INPUT_GET, 'county', FILTER_VALIDATE_INT, $options);
parent::save($request);
}
Again, the parent::save() method needs to be refactored.
Related
Is there a safe way to auto assign the keys in a posted array? Below are two examples of wrong ways...
foreach( $_POST as $key => $value ) {
$$key = $value;
}
or
extract($_POST)
Is there a better way, or is it best to code:
$foo = $_POST('foo');
$bar = $_POST('bar');
....
for all 50 inputs on my form?
(the posted info will be inserted into a database).
One more cautious way of extracting all input fields at once is:
extract( $_POST, EXTR_OVERWRITE, "form_" );
This way all your input variables will be called $form_foo and $form_bar at least. Avoid doing that in the global scope - not because global is evil, but because nobody ever cleans up there.
However, since mostly you do that in a localized scope, you can as well apply htmlentities if for example you need all fields just for output:
extract(array_map("htmlspecialchars", $_POST), EXTR_OVERWRITE, "form_");
There is not a single reason to do it.
To handle user inputs an array is 100 times better than separate variables
I like an approach where you let dynamic getters and setters in a class do all the work for you. Here's how I would code it.
First, create a bass class to hold data:
class FormParameterHandler {
protected $parameters;
public function __construct($associative_array) {
$this->parameters = array();
foreach($associative_array as $key => $value) {
$this->{$key} = $value;
}
}
public function __get($key) {
$value = null;
if(method_exists($this, "get_$key")) {
$value = $this->{"get_$key"}();
} else {
$value = $this->parameters[$key];
}
return $value;
}
public function __set($key, $value) {
if(method_exists($this, "set_$key")) {
$this->{"set_$key"}($value);
} else {
$this->parameters[$key] = $value;
}
}
}
Next, create a specific class to use for some specific form where there is something special to validate. Use your freedom as a programmer here to implement it any way you want to. And remember, since we're using reflection to look for setter methods, we can write specific setter methods for known problem areas, like e.g. to check for equal passwords in a "register user" form:
class RegisterFormParameterHandler extends FormParameterHandler {
private $passwords_are_equal = null;
public function __construct($register_form_parameters) {
parent::__construct($register_form_parameters);
}
public function has_equal_passwords() {
return $this->passwords_are_equal;
}
public function set_password($password) {
$this->parameters['password'] = $password;
$this->compare_passwords();
}
public function set_password_repeat($password_repeat) {
$this->parameters['password_repeat'] = $password_repeat;
$this->compare_passwords();
}
private function compare_passwords() {
if(isset($this->parameters['password']) && isset($this->parameters['password_repeat'])) {
$this->passwords_are_equal = ($this->parameters['password'] === $this->parameters['password_repeat']);
}
}
}
Finally, use the derived class in a "register user" flow, to easily find out if the two entered passwords match:
$registerFormParameterHandler = new RegisterFormParameterHandler($_POST);
if($registerFormParameterHandler->has_equal_passwords()) {
print "are equal";
//register user
} else {
print "are not equal";
}
You can test this by creating an HTML form that has one input field with the name "password", and another input field with the name "password_repeat".
To access any of the form data, use your form data object variable name, followed by the access operator "dash larger than" -> , followed by the name of the parameter. In the example above, if there was an input field named "user_name", it would be accessed through a call to
$registerFormParameterHandler->user_name
Rr, if you have defined the name of the field you want to get in some other variable, use reflection:
$registerFormParameterHandler->{$settings['form_data_user_name']}
Have fun! :)
A safe way to extract variables into the local scope is not to. You're injecting variables into your local scope, which is a problem however you do it. Even if you limit the variables to only a few select ones that won't clash with other variable names in the scope now, if you start adding elements to your form you may be in trouble later.
Arrays are specifically for holding an unlimited amount of named values without crowding the variable namespace. Use them! You may have to type a little more, but that's par for the course.
While it is best to refer to them with $_POST['variablename'], it is possible to expand only the variables you are expecting.
$expected = array('name', 'telephone', /* etc */);
foreach ($_POST as $key => $value) {
if (!in_array($key, $expected)) {
continue;
}
${$key} = $value;
}
Or, I prefer this:
foreach ($_POST as $key => $value) {
switch ($key) {
case 'name':
case 'telephone':
/* etc. */
${$key} = $value;
break;
default:
break;
}
}
The answer to your question depends on the computer, language, and security knowledge of the programmer. The opening sequence of processing $_POST is kind of like the opening move in a game of chess. Many use foreach loops without realizing that foreach will make a copy of the contents of $_POST the way you have it used (Programming PHP: Chapter 5, p.128-129). Wouldn't it be funny if you caused a buffer overflow simply by using foreach!
One commenter implied that everything should just be worked with inside of the $_POST superglobal. There are some merits to this... However, forgetting cache memory for a moment, access to array values is slower than direct access to a variable.
Since you have fifty (50) controls to validate (with potentially large contents), I might not want to take that array access performance hit more than 50 times (the original access hits). Moreover, if you are concerned about writing secure input validation routines, keeping your dirty laundry (non-validated input) separate from your clean (validated input) laundry is a good idea. That said, you may need a clean array anyway (hence the $_POST advocate's response), but at least you are reducing risk in the process by keeping the hopefully good separate from the potentially bad.
Is there a safe way to auto assign the keys in a posted array?
I might start like this:
Function library for this example.
function all_controls_submitted($controls) {
$postKeys = array_keys($_POST);
foreach($controls as $key) {
if(! array_key_exists($key, $postKeys)) {
return false;
}
}
return true;
}
function all_controls_set($controls) {
foreach($controls as $key) {
if(! isset($_POST[$key])) {
return false;
}
}
return true;
}
if(is_array($_SERVER) && isset($_SERVER['REQUEST_METHOD'], $_SERVER[REQUEST_URI]) && $_SERVER['REQUEST_METHOD'] === 'GET' && $_SERVER['REQUEST_URI'] === '/contact.php') {
$newForm = true;
} elseif (is_array($_SERVER) && isset($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['REQUEST_URI'] === '/contact.php') {
$newForm = false;
$controlNames = array('firstName', 'lastName', 'e-mail', 'company', 'subject', 'message', 'captcha');
define('NUM_CONTROLS_EXPECTED', count($controlNames)); //Seven (7)
if(is_array($_POST) && count($_POST) === NUM_CONTROLS_EXPECTED && all_controls_submitted($controlNames) && all_controls_set($controlNames)) {
//Begin input validation
}
} else {
header('location: http://www.nsa.gov');
}
Notice that I prep things with the $controlNames array, therefore I do not have to ask $_POST for the keys. After all, I should know them! :-) The two user-defined functions, all_controls_submitted() and all_controls_set() are two fundamental questions that should be asked before trying to use any values in $_POST (I say, anyway). Admittedly, I do use $_POST in all_controls_submitted(), but only to obtain the names of the controls submitted, not the values.
$postKeys = array_keys($_POST);
However, who is to say that the control names themselves could not be poisonous and in need of input validation?!! Same goes for values in $_SERVER. See, a chess game.
Is there a safe way to auto assign the keys in a posted array? I cannot tell you for certain, but perhaps something like the code above could help? You'd have the keys, at least.
Programming PHP: Chapter 5, p.125
Try this inbuilt method/function filter_input
Reference URL: php.net function filter input
I've search different forms but didn't understand how to solve the problem first found here (first link, second link) but its giving me the error in eval. I can't figure it out how to solve in that code in foreach loop.
foreach($_POST as $key => $value) {
if(!strstr($key, 'removeFile')){
//initialize variables using eval
eval("$" . $key . " = '" . sanitize($value) . "';");
}
}
First, the issues I have with your code:
eval is very, very rarely needed, and extremely dangerous, use with caution. I've been developing in PHP for over 10 years, and never really encountered a situation that needed eval. This is no exception. Eval is not required
You're sanitizing the entire $_POST array. That's great, but there are special functions for doing that, like: filter_input_array, array_filter and many, many more... not to mention ready-made, open source projects and frameworks that already contain a solid request validation component.
Always check the return values of functions, which you seem to be doing with strstr, but be weary of functions that return different types (like strstr: it returns false if the needle isn't found, but returns 0 if the needle is found at the start of the haystack string). Your if statement might not work as intended.
You're assuming sanitize($value) values will not contain any single quotes. Why? Because if they do, you'll end up with a syntax error in your evaled string
Be that as it may, you could easily write your code using variable variables and add a simple check not to step on existing variables in scope:
$sanitized = array_filter($_POST, 'sanitize');//call sanitize on all values
foreach ($sanitized as $key => $value)
{
if (!isset($$key) && strstr($key, 'removeFile') === false)
$$key = $value;
}
But really, $_POST values belong together, they are part of the request, and should remain grouped... either in an array, or in an object of some sort. Don't assign each value to its own variable, because pretty soon you'll loose track of what variables are set and which are not. Using an unset variable creates that variable, assigning value null, so what you have now makes for very error-prone code:
//request 1: POST => id=123&foo=bar
foreach ($sanitized as $k => $v)
$$k = $v;
$query = 'SELECT x, y, z FROM tbl WHERE id = ?';//using posted ID as value
$stmt = $db->prepare($query);
$stmt->execute(array($id));
All is well, because $id was set, but never trust the network, don't assume that, just because $_POST is set, all the keys will be set, and their values will be correct:
//request 2: POST => foo=bar&page=2
foreach ($sanitized as $k => $v)
$$k = $v;
$query = 'SELECT x, y, z FROM tbl WHERE id = ?';//using posted ID as value
$stmt = $db->prepare($query);
$stmt->execute(array($id));//id is null
Now we have a problem. This is just one example of how your code might cause issues. Imagine the script grows a bit, and look at this:
//request 3: POST => id=123&foo=bar&page=2
foreach ($sanitized as $k => $v)
$$k = $v;
//$id is 123, $foo is bar and $page = 2
$query = 'SELECT x, y, z FROM tbl WHERE id = ? LIMIT 10';//using posted ID as value
//a lot more code containing this statement:
$page = someFunc();
$log->write('someFunc returned log: '.$page);
//more code
$offset = 10*($page-1);//<-- page is not what we expected it to be
$query .= sprintf(' OFFSET %d', $offset);
$stmt = $db->prepare($query);
$stmt->execute(array($id));
Now this may seem far-fetched, and idiotic, but believe me: all of these things happen, more than I care to know. Adding some code that accidentally overwrites an existing variable that is used further down happens all the time. Especially in procedural code. Don't just blindly unpack an array. Keep that single variable, and use the keys to avoid:
grey hair
sudden, dramatic baldness
loss of sanity
bleeding ulcers
In a work environment: catastrophic loss of data
Sudden loss of job
... because code like this makes unicorns cry, and bronies will hunt you down
As the first answer to the post you linked to, the problem is that when using double quotes PHP thinks your eval() code starts with a variable. As that is not the case you have two options. Use single quotes and remember to escape the single quotes declaring a string in the code or escape the dollar sign.
Bonus note
There exist more elegant solutions to the problem you are trying to solve. The best solution I can think of is using the extract function. This gives to two main benefits.
It works with all associative arrays
You can specify different flags that can help you distinguish the extracted variables apart and avoid variable injection.
One flag you can use is EXTR_PREFIX_ALL. This will prefix all the extracted variables with your own prefix. You would then access the variables with a prefix of 'PREFIX_' like the following:
$array = [
'variable1' => 'foo',
'variable2' => 'bar'
];
extract($array, EXTR_PREFIX_ALL, 'PREFIX_');
$value1 = $PREFIX_variable1; // Equals: foo
$value2 = $PREFIX_variable2; // Equals: bar
A little on code injection
Suppose you have some code:
$login = false;
The $login variable determines if a user is logged in or not.
Then somewhere you use the ´extract´ function with an array of the following without using any flags.
$array = [
'login' => true,
'foo' => 'bar'
];
extract($array);
Now your $login variable would be set to true and the user posting the data would have overwritten the initial setting and gained access to your website without a valid login. Bear in mind this is a over simplified example, but nonetheless valid.
To overcome this you can use the flag EXTR_SKIP or prefix them like I previously showed. The EXTR_SKIP flag will skip the array element if a variable with the same name already is defined. So now your code would not overwrite your $login variable.
extract($array, EXTR_SKIP); // Skip existing variables
// Or
extract($array, EXTR_PREFIX_ALL, 'prefix'); // Prefix them all.
Hope this can guide to the right choice for your needs.
Regards.
I am trying to get multiple value from user input from text field and want to explode or keep adding into if condition statement
Here is my code
foreach ($list['post'] as $item) {
if( ($item['single']['catid'] != 8) AND ($item['single']['catid'] != 4) ){
$this->list_post($item);
}
}
Now what exactly I am looking for is in if( ($item['single']['catid'] != 8) AND ($item['single']['catid'] != 4) ) I want allow user to add multiple category ID and each ID will add AND and further id code AND ($item['single']['catid'] != 4)
I never done this before and don't know either this is proper way to do or any other possible better way.
Thanks a lot
You should have some kind of an array of the category IDs you want to check for, for example:
$categories = array(8, 4);
Then you could use something like the in_array(needle, haystack) built-in function of PHP.
Your if condition would become like that one: if (!in_array($item['single']['catid'], $categories)) {.
You should be using the above, but I am going to give you an idea of how it works, so you can understand the principle for more complex issues:
function exists($target, $array) {
foreach ($array as $element) { // Go through each element in the array
if ($element == $target) { // Check to see if any element there is what you are searching for
return true; // Return true, that it does exist, and stop there.
} else {
// Just ignore it...
}
}
return false; // If you get here, it means nothing returned true, so it does not exist...
}
To be used as if (exists($item['single']['catid'], $categories)) {.
It wouldn't work if it was "inside" the if statement because you have to do some processing before evaluating if it exists or not. So you either could have done that before the if statement, and store the result in a variable, or use a function (which PHP provides).
Hopefully the concept will help you fir more complex problems...
Note: this assumes your input is in the form of an array, which you can build via various ways, if not provided as is directly.
Update:
The input you get via the input field is sent through form, to which you specify either POST or GET as a method. Assuming it is POST that you are using, this is how you'd get the input as a string as it was entered in the text field:
$categories_string = $_POST['the_name_field_in_the_input_tag'];
After that you have to understand it as a list of items, let's say separated by commas: 1,3,5,8. Then this is simply separating by commas. You can use explode($delimiter, $string). Like that:
$categories_array = explode(',', $_POST['categories']);
But you cannot trust the input, so you could get something like 1, 2, 3,5,6. The spaces will mess it up, because you will have spaces all around. To remove them you can use trim for example.
$categories = array(); // Create the array in which the processed input will go
foreach ($categories_array as $c) { // Go through the unprocessed one
$categories[] = trim($c) * 1; // Process it, and fill the result. The times one is just so that you get numbers in the end and not strings...
}
Then you can use it as shown earlier, but keep in mind that this is just an example, and you might not even need all these steps, and there are much more efficient ways to process this input (regular expressions for example). But the concern here is not sanitizing input, but keep in mind you will need to do that eventually.
Hope it's clear enough :)
You might be better off with in_array() for checking a value against a variable number of possibilities.
I'm not sure I understand your problem. You want user to be able to input different values, e.g.:
$string = "5, 6, 7, 8, 10";
Afterwards, you want to check if 'catid' is not in that array and if it isn't you want to run $this->list_post($item);
If so, then you should use something like this:
$values = explode(", ", $string); //make array from values
foreach ($list['post'] as $item) {
if (!in_array($item['single']['catid'], $values)) { //check whether catid is in array
$this->list_post($item); // execute whatever you want
}
}
Hope it helps.
I was checking my script for vulnerabilities and was shocked the way i used to do in the past which is extremely insecure:
foreach ($_GET as $key => $value){
$$key = $value;
}
or shorter
extract( $_GET );
I altered with firebug some POST/GET variables to match a name i used in my script. they can be overwritten if the name would be guessed correctly.
So i thought i had to do it individually naming like this:
$allowed_vars =
$allowed_vars = array("time","hotfile","netload","megaupload","user","pfda","xyz","sara","amount_needed");
foreach ($_GET as $key => $value)
{
if (in_array($key,$allowed_vars))
{
$$key = $value;
}
}
This way saves some time than naming them individually.
What kind of automation have to be used for this?
I don't use any automatism of the kind.
I see no point in assigning request variables to global variables automatically.
If it's one or two variables, I could deal with them manually.
If there are more, I'd rather keep them as array members for the convenient handling.
Yet I am using some sort of whitelisting approach similar to yours.
but not to create global variables out of POST data but to add that data into SQL query.
Like in this simple helper function to produce SET statement:
function dbSet($fields) {
$set='';
foreach ($fields as $field) {
if (isset($_POST[$field])) {
$set.="`$field`='".mysql_real_escape_string($_POST[$field])."', ";
}
}
return substr($set, 0, -2);
}
$id = intval($_POST['id']);
$fields = explode(" ","name surname lastname address zip fax phone");
$query = "UPDATE $table SET ".dbSet($fields)." stamp=NOW() WHERE id=$id";
You can save even more time by not extrating them at all. Just use them from the $_GET array. The advantages of this are not just avoiding collision with script variables (or worse) but also that you don't have to update that "automatism" when you add request parameters.
When I am working with POST data, as from a form, I often process each explicitly:
$data = array();
$data['field1'] = someSaniFunction($_POST['field1']);
$data['field2'] = someOtherFunction($_POST['field2']);
...
In this way I ensure that each field is properly handled, and only the fields I expect are touched.
In my experience, you shouldn't transform data in the $_REQUEST array into variables using $$ as it gives the possibility of overwriting variables held within the current scope.
Instead, you should consider having a request object or array in which you filter the data and only access named variables that you require. This way, you do not have to keep extending your allowed variable names and still maintain security.
The ZF for example has a request object, and they recommend using a input filter when working on this data:
http://framework.zend.com/manual/en/zend.filter.input.html
You can use the extract function in a more secure way:
extract($_REQUEST, EXTR_SKIP);
This will not overwrite variables that already exist in your code. See here for other parameters you can use
Could anyone tell me why I am not retrieving info from a form I have submited within a wordpress template? The variables are being passed but they have no values?!?
New answer to an age-old question!
I came across this post, which didn't help, and wrote my own utility (happily shared and feel free to improve)
/* Get Parameters from $_POST and $_GET (WordPress)
$param = string name of specific parameter requested (default to null, get all parameters
$null_return = what you want returned if the parameter is not set (null, false, array() etc
returns $params (string or array depending upon $param) of either parameter value or all parameters by key and value
Note: POST overrules GET (if both are set with a value and GET overrules POST if POST is not set or has a non-truthful value
All parameters are trimmed and sql escaped
*/
function wordpress_get_params($param = null,$null_return = null){
if ($param){
$value = (!empty($_POST[$param]) ? trim(esc_sql($_POST[$param])) : (!empty($_GET[$param]) ? trim(esc_sql($_GET[$param])) : $null_return ));
return $value;
} else {
$params = array();
foreach ($_POST as $key => $param) {
$params[trim(esc_sql($key))] = (!empty($_POST[$key]) ? trim(esc_sql($_POST[$key])) : $null_return );
}
foreach ($_GET as $key => $param) {
$key = trim(esc_sql($key));
if (!isset($params[$key])) { // if there is no key or it's a null value
$params[trim(esc_sql($key))] = (!empty($_GET[$key]) ? trim(esc_sql($_GET[$key])) : $null_return );
}
}
return $params;
}
}
Just came up against the same/similar issue; it is not ideal to use get variables on Wordpress as the URL is structured using mod_rewrite and has some reserved query parameters. The Wordpress Docs on query vars gives you a bit of a list, but it is not comprehensive.
In summary, the variables you were using may have been one of those reserved or modified or handled by Wordpress?
(I know this is an old question but it needs an answer or clarification.)
Please check form method
<form name="frmlist" method="post">
Try with this
print var_dump($_GET);
print var_dump($_POST);