Unpacking an array_value()'d $_POST array - php

I'm having some issues that I suspect are from misunderstanding how to handle $_POST data from a web form. It's racking my brains, because my method works in one scenario, but breaks in its successor.
In a nutshell: I can call key=>value pairs of $_POST in a method, but I can't manipulate the $_POST array itself, which always returns as 1.
Initial Scenario. Refer to comments for context & gripes. This method works:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$post = $_POST; // I'm aware $_POST is a superglobal and can be called within the method
$cms->addPage($table, $post);
}
addPage() Method from CMS class:
public function addPage($table, $post) {
global $db; // SQL object declared in config
$url = preg_replace('/\s+/', '-', $post['pageTitle']);
$vals = array($post['pageTitle'], $post['isRoot'], $post['metaTitle'], $post['metaDesc'], $post['pageCont'], $url, $post['dateCreated'], $post['pageBanner']);
$cols = array("pageTitle", "isRoot", "metaTitle", "metaDesc", "pageCont", "pageURL", "dateCreated", "pageBanner");
$types = "sissssss";
$db->prep_Insert($table, $cols);
$db->bind_Prep($types, $vals); // This method unpacks the $vals array with ...
$db->execute_Prep();
$db->close_Prep();
$_SESSION['success'] = 'Page Updated';
header('Location: ' . DIRADMIN . 'manage/pages');
exit();
}
This works fine. Pops right up in the database, and bob's your uncle. But, this CMS should be able to handle multiple form objects, and I don't want to write an add / update method for every custom feature a client might want. So I came up with this:
public function addContent($table, $post, $types) {
global $db;
$cols = self::getColumns($table); // Helper method I wrote to get column names from the table being added to
array_shift($cols); // AUTO_INCREMENT is set in database, so knock off the ID row
/* This is where it falls apart. All I want is to take the $_POST array
I got from the form and reduce it from associative to indexed.
The array_values() should do this, right? */
$post = array_values($post);
// $post = array_values($_POST);
/* Also doesn't work. It doesn't matter if I pass forward the
$_POST array as a parameter, or if I call it directly here. */
/* Second attempt at getting $_POST into an indexed array. I don't need the keys. */
/*$i = 0;
$vals = array();
foreach ($_POST as $key => $value) {
$vals[$i] = $value;
$i++;
}*/
$db->prep_Insert($table, $cols);
$db->bind_Prep($types, $post); // This is where I got the initial unpacking error; can't unpack associative arrays. Okay, I'll use array_value($_POST). Except, that always just outputs 1, and breaks the prep statement anyway.
$db->execute_Prep();
$db->close_Prep();
$_SESSION['success'] = 'Page Updated';
header('Location: ' . DIRADMIN . 'manage/' . $table);
exit();
}
So to reiterate, for some reason I can get $_POST data by its individual key=>value pairs in the initial method, but if I try to manipulate the $_POST array in any way (array_keys(), reset(), foreach loop, etc), it will output as 1. print_r() outputs 1, var_dump() outputs blank, and in the exception message it returns as 1.
Is there some limitation or rule to handling $_POST data that I'm not getting? I can't figure out why I can get key value pairs, but otherwise can't manipulate it at all.

Related

Updating a PHP array of POST datas, before processing them

I need to apply some functions to a key items inside an array before moving on with the whole array, but I probably miss something.
Here is my code:
// Get generated datas
$data_post = $this->input->post('form_data'); // Need to update this array
foreach( $data_post as $data ){
$data['password'] = password_encrypt($data['password']);
var_dump($data); // Password encryption succeed
}
var_dump($data_post); // But here, the password is still the same, no encryption applied
So as commented in the CODE section, how should I update the main $data_post array with the modifications made in foreach() ?
Inside a foreach, data isn't passed by reference. This means that modifying the variable $data doesn't modify $data_post. You can modify the original array in more than one way, but here is how I would do it:
foreach( $data_post as $key => $data ){
$data_post[$key]['password'] = password_encrypt($data['password']);
}
Note that this presumes that $data_post contains multiple sub arrays, each with the password key (or else notices will be thrown).
Try this:
$data_post = $this->input->post('form_data'); // Need to update this array
foreach( $data_post as $k=>$v ){
if($k == "password"){
$data_post[$k] = password_encrypt($v);
}
}
var_dump($data_post);

all variables from url

I have a code like that:
session_start();
$user = $_GET['user'];
$mode = $_GET['mode'];
$id1 = $_GET['id1'];
$id2 = $_GET['id2'];
$id3 = $_GET['id3'];
$id4 = $_GET['id4'];
$id5 = $_GET['id5'];
$id6 = $_GET['id6'];
$id7 = $_GET['id7'];
$id8 = $_GET['id8'];
$dep= $mode;
switch ($dep)
{
case "3m":
$dep = "Text 1";
break;
case "all":
$dep = "More text";
break;
default:
$dep = "Text1";
}
There are more other cases. I think I will have more id's and cases soon. Is there a simpler way to get all id's from URL push them into PHP code for evaluating?
I have found a code foreach:
foreach($_GET as $key => $value) {
echo 'key is: <b>' . $key . '</b>, value is: <b>' .$value. '</b><br />';
}
And it gets all variables from URL, but how to change the code in order to have it like this:
$variable = $_GET['variable'];
Any help appreciated :)
Use arrays, that's exactly what they're for. Name the parameters in the URL like:
id[]=1&id[]=2&...
Then $_GET['id'] is an array which you can easily loop through.
Many ids means you're looking for an array of ids, not id1, id2, id3 etc. An array allows you to access them virtually the same way as $id[1], $id[2] etc, but without the headache of needing to herd hundreds of independent variables.
There's also no need to assign each value from $_GET into its own variable, you can use those values directly from $_GET. They're not getting any better by assigning them into individual variables.
Assuming I understood correctly and you want all GET variables to be actual variables you can use, there's a function extract to do that.
However, as noted in the documentation:
Do not use extract() on untrusted data, like user input (i.e. $_GET,
$_FILES, etc.). If you do, for example if you want to run old code
that relies on register_globals temporarily, make sure you use one of
the non-overwriting flags values such as EXTR_SKIP and be aware that
you should extract in the same order that's defined in variables_order
within the php.ini.
So basically you should not do this unless you have good reasons, and be careful if you do as to not overwrite any existing values.
However, if your alternative is to do this:
foreach($_GET as $key => $value) {
$$key=$value;
}
then extract is certainly better, as a) you can set it to not overwrite any existing variables, and b) you can have a prefix, so those imported variables can be distinguished. Like this:
extract ( $_GET, EXTR_SKIP);
For $user, $mode, $id which don't overwrite anything, or
extract ( $_GET, EXTR_PREFIX_ALL, 'user_input' );
for $user_input_mode, $user_input_user, $user_input_id etc.
function getIDs()
{
$ids = array();
if(isset($_GET['id']))
{
foreach($_GET['id'] as $key => $value)
{
$ids[$key] = $value;
}
}
return $ids;
}
If you can get your URL to send an array of id GET parameters to a PHP script you can just parse the $_GET['id'] array in the above function, assuming the URI looks like ?id[]=1&id[]=2&id[]=3
$ids = getIDs();
You can write
foreach($_GET as $key => $value) {
$$key=$value;
}
this will assign every key name as a varibale.

PHP split and mongodb

ok, this might sound strange, but i have a form and our business wants to track what is getting changed, when a user adds a new lead etc.
So i set up a function that does the following
function savedata($data){
$collection = $this->db->retail_logs;
$this->data = explode('|', $data['data']);
print_r($this->data);
try {
$collection->update(
array($this->data['0']=>$this->data['1'],$this->data[2]=>$this->data[3]),
array("date"=> date("d.m.Y"), "time"=>date("H:i:s"),"whochanged"=>$_COOKIE['CRMUIDkey']), // new lead document to insert
array("upsert" => true, "safe" => true)
);
} catch (Exception $e) {
// Something went wrong ..
}
}
it is basiclly a log file.
but as you may be able to see the $data sends data though like tradingname|ABC|owner|ownerID
But if I want to add to that i would need to run a loop or a foreach I am wondering what is the best way to make sure all teh data gets saved and not just data[0] to 3 so say they send 16 fields and values in it I need a foreach or something to split it.
It appears that you want to map the $data['data'] into key=>value pairs (an associative array). You want to be very careful about what fields you allow in this format especially since it looks like user-provided data (or data they can modify in a post request?). For example, a malicious user could update or add to another user's account if you aren't checking the allowed keys before doing the upsert.
To convert the $data string you want to do something like:
<?php
// Keys that can be updated
$allowed = array('tradingname','owner');
// Sample data
$data = 'tradingname|ABC|owner|ownerID|badkey|foo';
// Split into arrays based on '|' delimiter
preg_match_all("/([^\|]+)\|([^\|]+)/", $data, $keypairs);
// Combine matches into key => value array
$keypairs = array_combine($keypairs[1], $keypairs[2]);
// Sanity check to create $criteria
foreach ($keypairs as $key => $value) {
if (in_array($key, $allowed)) {
// Perhaps do some extra sanity checking that $value is expected format
$criteria[$key] = $value;
} else {
// Log and/or bailout as appropriate
echo "Ignoring: [$key] => [$value]\n";
}
}
// Criteria should now be reasonable to use in $collection->update(..) upsert
print_r($criteria);
?>
Send your data as json. And then use json_decode to convert it to the array you want.

Build a json in php from POST data

I need to update a json list of object via url post data. For example, with url:
http://myweb.com/index.php?name=Peter&surname=Brown
in php, using get method:
$name = $_GET["name"];
$surname = $_GET["surname"];
$json = array();
$json["nombre"] = $name;
$json["lat"] = $lat;
$data[] = $json;
$json_end= json_encode($data);
and json_end efectively is done like I want:
[{"name":"Peter","surname":"Brown"}]
My question is about how I can do it incrementing the json, in order to build an array like:
[{"name":"Peter","surname":"Brown"}]
[{"name":"newname","surname":"newsurname"}]
// and so on
each time user use the url with new parameters.
Do I need to write to a file, or to database? Any tips will be apreciated.
The idea is to be able that any user can add some dat using url. I tried to store the json to a fiel but the storing is durable only along the current session.
<?
/* This needs to be at the top of your file, without ANYTHING above it */
session_start();
/* ... */
if(!array_key_exists('entries', $_SESSION))
{
$_SESSION['entries'] = array();
}
$_SESSION['entries'][] = array("name" => $_GET["name"], "surname" => $_GET["surname"]);
$json_string = json_encode($_SESSION['entries']);
This would produce a single JSON. However I don't know whether you meant to or not, but your output is a series of separate JSONs. You could do this by replacing the last line above with:
$json_string = '';
foreach($_SESSION['entries'] as $entry){
$json_string.= json_encode($entry) . "\n";
}
You would also probably want a way to reset/empty the array. In which case I'd change the if test from:
if(!array_key_exists('entries', $_SESSION))
to:
if(!array_key_exists('entries', $_SESSION) || array_key_exists('reset', $_GET))
Which you could use by visiting
http://myweb.com/index.php?reset
Edit: If somewhere you add the following code:
foreach($_SESSION['entries'] as $id=>$entry){
printf("%2d: %s\n", $id, json_encode($entry));
}
You'll get a list of the json elements enumerated by their respective keys. For example, it might look like:
0: "[{\"name\":\"Peter\",\"surname\":\"Brown\"}]"
1: "[{\"name\":\"newname\",\"surname\":\"newsurname\"}]"
2: "[{\"name\":\"George\",\"surname\":\"Washington\"}]"
3: "[{\"name\":\"John\",\"surname\":\"Adams\"}]"
If you then add the following code:
if(array_key_exists('del', $_GET) && is_numeric($_GET['del']))
{
$key = (int)$_GET['del'];
if(array_key_exists($key, $_SESSION['entries']))
{
unset($_SESSION['entries'][$key]);
}
else
{
printf('<strong>ERROR: $_GET['del'] = %d but $_SESSION['entries'][%d] doesn't exist.</strong>', $key, $key);
}
}
you'll be able to delete individual json entries by specifying id as the del GET parameter.
For example,
http://myweb.com/index.php?del=2
would delete the entry corresponding to '[{"name":"George","surname":"Washington"}]';
And the remaining entries would be:
0: "[{\"name\":\"Peter\",\"surname\":\"Brown\"}]"
1: "[{\"name\":\"newname\",\"surname\":\"newsurname\"}]"
3: "[{\"name\":\"John\",\"surname\":\"Adams\"}]"
4: "[{\"name\":\"Thomas\",\"surname\":\"Jefferson\"}]"
Just make a nested array of users:
$data = array (
0 => array("name"=>"Peter","surname"=>"Brown"),
1 => array("name"=>"newname","surname"=>"newsurname")
);
echo json_encode($data);
// [{"name":"Peter","surname":"Brown"},{"name":"newname","surname":"newsurname"}]
I think it would be easiest to store the data in a session, something like this:
<?php
session_start();
if (isset($_GET['name'])) {
$_SESSION['json'][] = $_GET;
}
echo json_encode($_SESSION['json']);
?>
Edit: You may want to filter the $_GET array before storing it in the session so that you don't store values that aren't meant to be stored.
Edit: Of course, if you want to save this data for more than one session you would need to use files or a database (or perhaps a cookie). It all depends on what you want to do with the information.

Calling PHP function in itself

In the following script function clean($data) calls it within it, that I understand but how it is cleaning data in the statement $data[clean($key)] = clean($value);??? Any help is appreciated.. I am trying to figure it out as I am new to PHP. Regards.
if (ini_get('magic_quotes_gpc')) {
function clean($data) {
if (is_array($data)) {
foreach ($data as $key => $value) {
$data[clean($key)] = clean($value);
}
} else {
$data = stripslashes($data);
}
return $data;
}
$_GET = clean($_GET);
$_POST = clean($_POST);
$_REQUEST = clean($_REQUEST);
$_COOKIE = clean($_COOKIE);
}
Your Question:
So if I undertsand correctly you want to know what is the function doing in the line
$data[clean($key)] = clean($value);
The Answer:
See the prime purpose of the function is to remove slashes from string with php's stripslashes method.
If the input item is an array then it tries to clean the keys of the array as well as the values of the array by calling itself on the key and value.
In php arrays are like hashmaps and you can iterate over the key and value both with foreach loop like following
foreach ($data as $key => $value) {....}
So if you want to summarize the algorithm in your code snippet it would be as under
Check if the input is array. If it is not then go to step 4
For each item of array clean the key and value by calling clean method on it (Recursively)
Return the array
clean the input string using stripslashes method
5 return the cleaned input
From my understanding it's not cleaning the key but creates a new element with a clean key while the uncleaned key remains.
$a['foo\bar'] : val\ue
becomes
$a['foo\bar'] : val\ue
$a['foobar'] : value
Someone correct me if im wrong.
Maybe you'll understand the code better if it's put this way:
foreach ($data as $key => $value) {
$key = clean($key); // Clean the key, the
$value = clean($value); // Clean the value
$data[$key] = $value; // Put it in the array that will be returned
}
Assuming you have an array like this:
$_POST = array(0 => 'foo', 1 => array('bar' => 'baz'));
the following will happen:
Call clean($_POST);
call clean 0
call clean 'foo'
$return[0] = 'foo'
call clean 1
call clean 'bar'
call clean 'baz'
$return[1] = array('bar' => 'baz');
You should probably read this: http://www.codewalkers.com/c/a/Miscellaneous/Recursion-in-PHP/
The main purpose of the function is to clean an associative array or a single variable. An associative array is an array where you define keys and values for that keys; so are special arrays used in PHP like $_GET $_POST and so on.
The meaning of "cleaning" is to check whether magic quotes are active - this causes some characters in these arrays to be escaped with backslashes when you post dynamic data to a PHP page.
$_GET["Scarlett"] = "O' Hara" becomes with magic quotes $_GET["Scarlett"] = "O\' Hara"
So if magic quotes are active, the function takes care of this, and slashes are stripped so that the strings retain their correct, not escaped value.
The algorithm checks if the data passed to the function is an array, if not it cleans directly the value.
$string = "Escapes\'in\'a string";
clean($string);
is it an array? No. Then return stripslashes(my data)
$array = array("key\'with\'escapes"=>"value\'with\'escapes", "another\'key"=>"another value");
clean($array)
is it an array? Yes. So cycle through each key/value pair with foreach, take the key and clean it like the first example; then take the value and do the same and put the cleaned versions in the array.
As you see the function has two different behaviours differentiated by that "if" statement.
If you pass an array, you activate the second behaviour that in turns passes couples of strings, not arrays, triggering the first behaviour.
My thought is that this function doesn't work properly, though. Anyone got the same sensation? I have it not tested yet but it seems it's not "cleaning" the key/values in the sense of replacing them, but adds the cleaned versions along the uncleaned ones.

Categories