$_GET variables being passed with empty string values - php

I have my local test server and my running server. On the test server, I use this url:
http://localhost/?&start[]=0&duration_[]=2&name[]=David&setting[]=26&start[]=6&duration[]=18&name[]=john&setting[]=26
From this URL, I use a for loop to run through the array:
$num = count($_GET['start']);
if (isset($_GET['start']) && count($_GET['start']))
{
for($i=0;$i<$num;$i++)
{
echo "Time: " . intval(trim($_GET['start'][$i]));
}
}
This works like a charm. However, when I run it on my actual server, I get all the variables returning as empty strings... Here is the var_dump:
["start"]=>
string(0) ""
["duration"]=>
string(0) ""
["name"]=>
string(0) ""
["setting"]=>
string(0) ""
Anyone have any idea what's happening here? I've put the same for-loop test code from above and I'm obviously not getting anything besides: "Time: "

The problem wasn't with the code or the $_GET method or the difference of servers (well a slight difference). I didn't realize that when I was using filter_var() on my main server, my arrays were being turned into strings. This bit of the sanitization code that caused the problem was not on my test server. As per the php documentation:
Value to filter. Note that scalar values are converted to string internally before they are filtered.
This is the first time I've made a function using the array capabilties of the $_GET variable so I didn't realize that when sanitizing, I was sanitizing each GET variable where in this new instance, I was sanitizing each GET array, rather than GET value.
The code I was using was:
$get_arr = $var_arr['_GET'];
$get_key = array_keys($get_arr);
$_GET[$get_key[$g_a]] = trim(filter_var($_GET[$get_key[$g_a]], FILTER_SANITIZE_SPECIAL_CHARS));
The code that I changed it to
$get_arr = $var_arr['_GET'];
$get_key = array_keys($get_arr);
$_GET[$get_key[$k]][$i] = trim(filter_var($_GET[$get_key[$k]][$i], FILTER_SANITIZE_SPECIAL_CHARS));
It's only a slight distinction, but enough of one to have me debugging this for a couple of days. Hopefully this helps someone else along the way.

Remove the [] after your vars.
http://localhost/?start=0
instead of
http://localhost/?start[]=0
your passing [] as your string so it spitting that out, remove the [].

Related

URL GET variable disappear

I have a simple page to print some data from database.
I use php GET variable, (I know its deprecated, but it's work on local network).
It's working perfectly but if 'valami' length is bigger the 'valami' variable disappearing. When the length is 1061 it does not work
In the php.ini:
max_input_vars=3000
Also the aid variable from the end of the Querystring does get seen by my code? So it does not appear to be a querystring length issue.
What is the problem?
PHP5
print_fizet.php?valami=28925|28926|28927|28928|28929|28930|28931|28932|28933|28934|28935|28936|28937|28938|28939|28940|28941|28942|28943|29055|29056|29057|29058|29059|29060|29061|29062|29073|29074|29075|29076|29077|29078|29079|29080|29092|29094|29095|29096|29097|29098|29099|29100|29101|29102|29103|29104|29105|29106|29107|29108|29109|29110|29111|29125|29142|29143|29144|29145|29146|29150|29151|29152|29168|29169|29170|29171|29172|29173|29174|29175|29176|29177|29178|29179|29180|29181|29182|29183|29184|29185|29186|29187|29188|29189|29190|29191|29192|29193|29194|29195|29196|29197|29198|29199|29200|29201|29202|29203|29204|29205|29206|29207|29208|29209|29210|29211|29212|29213|29230|29231|29232|29259|29260|29264|29265|29270|29281|29291|29292|29299|29300|29301|29303|29318|29339|29345|29346|29347|29348|29349|29350|29351|29352|29353|29360|29361|29367|29368|29369|29370|29371|29385|29386|29387|29388|29408|29409|29410|29411|29412|29413|29434|29435|29436|29490|29491|29508|29519|29523|29524|29525|29526|29527|29533|29534|29535|29545|29546|29547|29548|29549|29550|29551|29552|29554|29581&aid=23
UPDATE
When I do a
var_dump($_GET);
The output is
array(1) {
["aid"]=> string(2) "23"
}
All I get is the variable from the end of the querystring
When I make a shorter valami variable It works:
array(2) {
["valami"]=> string(509) "29197|...|29581"
["aid"]=> string(2) "23"
}
It appears you're hitting a limit for maximum length of a GET variable - possibly 1024. This is most likely due to the PHP version you use. I've found this note on php.net that may be of use to you:
https://www.php.net/manual/en/reserved.variables.get.php#101469
You can update max_input_vars length from 1000 to 3000 and so on as you want. Try below code to increase input vars length.put this code on starting of the script.
ini_set('max_input_vars', 3000);
I hope this will helpfull for you.

var_dump(json_encode($data)) returns bool(false)

After getting result from Sql query, I stored my result in a $data array, which after var_dump($data) looks somewhat like this:
array(100) {
[0]=>
object(stdClass)#3 (3) {
["qid"]=>
string(2) "19"
["q_no_on_paper"]=>
string(1) "0"
["question_text"]=>
string(139) " Consider the following statements- 1- The Centre recently unveiled the expanded version of the. . ."
}
[1]=>
object(stdClass)#4 (3) {
["qid"]=>
string(2) "16"
["q_no_on_paper"]=>
string(1) "0"
["question_text"]=>
string(138) ". There is dispute over the Tipaimukh hydraulic project between India and A. Bhutan B. Nepal C.. . ."
} ...
The array is long, so this is just a part of it.
Now when I do json_encode($data); , and then var_dump it, I get bool(false).
I tried to use json_last_error() after following the Example #1 in this PHP documentation link , it shows - No errors
Where am I going wrong? Why is it not encoding it?
EDIT:
The ["question_text"] contains some html text.
This may help you out,
Although this is not documented on the version log , non-UTF8 handling behavior has changed in 5.5, in a way that can make debugging difficult.
Passing a non UTF-8 string to json_encode() will make the function return false in PHP 5.5, while it will only nullify this string (and only this one) in previous versions.
In a Latin-1 encoded file, write this:
<?php
$a = array('é', 1);
var_dump(json_encode($a));
?>
PHP < 5.4:
string(8) "[null,1]"
PHP >= 5.5:
bool(false)
PHP 5.5 has it right of course (if encoding fails, return false) but its likely to introduce errors when updating to 5.5 because previously you could get the rest of the JSON even when one string was not in UTF8 (if this string wasn't used, you'd never notify it's nulled)
So you may encountered the later example of PHP >= 5.5
See at php.net documentation http://php.net/manual/en/function.json-encode.php#115733
I see some white space/HTML characters (like \n, \t, \r) in var_dump($data) result. This makes JSON invalid and hence json_encode returns false. Check the following index in your array...
["question_text"]=> string(139) " Consider the following statements-

Different server, different datatype with the exact same code

I'm running a code getting data from a DB server, everything is working fine locally. But when I push it online, it's not working anymore.
I think I know where the problem comes from. Here is the data I get locally:
object(stdClass)#451 (14) {
["matter_created_actionstep_c"]=>
string(1) "1"
["trust_receipt_sent_c"]=>
string(1) "1"
["scope_complete_c"]=>
string(1) "0"
["transferred_trust_to_current_c"]=>
string(1) "0"
}
And when I push it on my server, here is the result I get:
object(stdClass)#451 (14) {
["matter_created_actionstep_c"]=>
int(1)
["trust_receipt_sent_c"]=>
int(1)
["scope_complete_c"]=>
int(0)
["transferred_trust_to_current_c"]=>
int(0)
}
Are you aware of any apache configuration that would lead to this typeset change?
The only project file that is different, is the conf file:
locally:
DB_HOST=1.1.1.1
on the server:
DB_HOST=localhost
Thanks in advance.
This can result from a lot of issues. Maybe a different PHP version, maybe the data comes from a database and the version of the database driver is different, …
I would say the application is to blame. PHP is untyped, meaning in most cases nobody cares for the actual type of a variable. In your case barely anybody cares if the value is 2 or "2" as long as it behaves the same (e.g. 2==2 equals 2=="2"). If the application requires certain types in some variables, it should assure the variables contain these types! This is not the case here.
Check the application and the part of the code which writes the value into the object. This part of code should cast the value to the desired type before putting it into the variable.
Nevertheless (or if the object comes from somewhere else) it may make sense to make the code more forgiving concerning types, i.e. do not rely on a special type as long as it is not necessary. In my experience there are only few cases when type of variables really matter.

Override $argv for getopt

example.php:
$args = __FILE__.' -vvv';
$argv = explode(' ', $args);
$argc = count($argv);
$GLOBALS['argv'] = $_SERVER['argv'] = $argv;
$GLOBALS['argc'] = $_SERVER['argc'] = $argc;
var_export(getopt('v'));
$ example.php -v
> array('v' => false);
Eventually getopt does not look up to $GLOBALS to get argv. So it there any way I can override argv array?
TL;DR
No, there is no native way to do this. Depending of your goals there may be other ways to resolve the issue, but overriding is not one of them.
Operating on super-globals
Structure
To realize why it is so, you should know, that super-globals are not just "variables" to which you are referring. That means, if you are using $argv to de-reference arguments list, it does not mean that you'll access some data which is stored in "variable" $argv. Instead, you'll access to data container by "link", called $argv. However, there are different ways to access this data - $_SERVER['argv'] or $GLOBALS array at least. To illustrate, I'll go with the code:
var_dump($argv, $_SERVER['argv']);
unset($argv);
var_dump($argv, $_SERVER['argv']);
This will result in something like:
array(2) {
[0]=>
string(11) "example.php"
[1]=>
string(4) "-vvv"
}//<--------------------- derived from $argv
array(2) {
[0]=>
string(11) "example.php"
[1]=>
string(4) "-vvv"
}//<--------------------- derived from $_SERVER['argv']
NULL//<------------------ we've unset $argv, so unset a reference
array(2) {
[0]=>
string(11) "example.php"
[1]=>
string(4) "-vvv"
}//<--------------------- but data is still there and available via another reference
Internally
As you can see, the reference can be destroyed, but actual data will remain untouched. That will be stored in symbols table. Many PHP functions are accessing this structure to retrieve data, and getopt() is not an exception. So the conclusion is - yes, you can modify the reference (or even destroy it), but actual data will be still in super-globals.
getopt()
Now, about this function. Just take a look at its implementation:
/* Get argv from the global symbol table. We calculate argc ourselves
* in order to be on the safe side, even though it is also available
* from the symbol table. */
if (PG(http_globals)[TRACK_VARS_SERVER] &&
(zend_hash_find(HASH_OF(PG(http_globals)[TRACK_VARS_SERVER]), "argv", sizeof("argv"), (void **) &args) != FAILURE ||
zend_hash_find(&EG(symbol_table), "argv", sizeof("argv"), (void **) &args) != FAILURE) && Z_TYPE_PP(args) == IS_ARRAY
)
{
//...omitted
}
It's clearly stated, that it will try to search "argv" in symbol_table (if you want - here's a link to - how it will be done exactly). And that means - it will access actual data, thus, overriding it externally will have no effect.
As a result - getopt() will work with data which were gathered at script startup, and that data will contain actual parameters, no matter if you'll override them inside your script.

My PHP array of references is "magically" becoming an array of values... why?

I'm creating a wrapper function around mysqli so that my application needn't be overly complicated with database-handling code. Part of that is a bit of code to parameterize the SQL calls using mysqli::bind_param(). bind_param(), as you may know, requires references. Since it's a semi-general wrapper, I end up making this call:
call_user_func_array(array($stmt, 'bind_param'), $this->bindArgs);
and I get an error message:
Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given
The above discussion is to forstall those who would say "You don't need references at all in your example".
My "real" code is a bit more complicated than anyone wants to read, so I've boiled the code leading up to this error into the following (hopefully) illustrative example:
class myclass {
private $myarray = array();
function setArray($vals) {
foreach ($vals as $key => &$value) {
$this->myarray[] =& $value;
}
$this->dumpArray();
}
function dumpArray() {
var_dump($this->myarray);
}
}
function myfunc($vals) {
$obj = new myclass;
$obj->setArray($vals);
$obj->dumpArray();
}
myfunc(array('key1' => 'val1',
'key2' => 'val2'));
The problem appears to be that, in myfunc(), in between the call to setArray() and the call to dumpArray(), all the elements in $obj->myarray stop being references and become just values instead. This can be easily seen by looking at the output:
array(2) {
[0]=>
&string(4) "val1"
[1]=>
&string(4) "val2"
}
array(2) {
[0]=>
string(4) "val1"
[1]=>
string(4) "val2"
}
Note that the array is in the "correct" state in the first half of the output here. If it made sense to do so, I could make my bind_param() call at that point, and it would work. Unfortunately, something breaks in the latter half of the output. Note the lack of the "&" on the array value types.
What happened to my references? How can I prevent this from happening? I hate to call "PHP bug" when I'm really not a language expert, but could this be one? It does seem very odd to me. I'm using PHP 5.3.8 for my testing at the moment.
Edit:
As more than one person pointed out, the fix is to change setArray() to accept its argument by reference:
function setArray(&$vals) {
I'm adding this note to document WHY this seems to work.
PHP generally, and mysqli in particular, appear to have a slightly odd concept of what a "reference" is. Observe this example:
$a = "foo";
$b = array(&$a);
$c = array(&$a);
var_dump($b);
var_dump($c);
First of all, I'm sure you're wondering why I'm using arrays instead of scalar variables -- it's because var_dump() doesn't show any indication of whether a scalar is a reference, but it does for array members.
Anyway, at this point, $b[0] and $c[0] are both references to $a. So far, so good. Now we throw our first wrench into the works:
unset($a);
var_dump($b);
var_dump($c);
$b[0] and $c[0] are both still references to the same thing. If we change one, both will still change. But what are they references to? Some unnamed location in memory. Of course, garbage collection insures that our data is safe, and will remain so, until we stop refering to it.
For our next trick, we do this:
unset($b);
var_dump($c);
Now $c[0] is the only remaining reference to our data. And, whoa! Magically, it's no longer a "reference". Not by var_dump()'s measure, and not by mysqli::bind_param()'s measure either.
The PHP manual says that there's a separate flag, 'is_ref' on every piece of data. However, this test appears to suggest that 'is_ref' is actually equivalent to '(refcount > 1)'
For fun, you can modify this toy example as follows:
$a = array("foo");
$b = array(&$a[0]);
$c = array(&$a[0]);
var_dump($a);
var_dump($b);
var_dump($c);
Note that all three arrays have the reference mark on their members, which backs up the idea that 'is_ref' is functionally equivalent to '(refcount > 1)'.
It's beyond me why mysqli::bind_param() would care about this distinction in the first place (or perhaps it's call_user_func_array()... either way), but it would appear that what we "really" need to ensure is that the reference count is at least 2 for each member of $this->bindArgs in our call_user_func_array() call (see the very beginning of the post/question). And the easiest way to do that (in this case) is to make setArray() pass-by-reference.
Edit:
For extra fun and games, I modified my original program (not shown here) to leave its equivalent to setArray() pass-by-value, and to create a gratuitous extra array, bindArgsCopy, containing exactly the same thing as bindArgs. Which means that, yes, both arrays contained references to "temporary" data which was deallocated by the time of the second call. As predicted by the analysis above, this worked. This demonstrates that the above analysis is not an artifact of var_dump()'s inner workings (a relief to me, at least), and it also demonstrates that it's the reference count that matters, not the "temporary-ness" of the original data storage.
So. I make the following assertion: In PHP, for the purpose of call_user_func_array() (and probably more), saying that a data item is a "reference" is the same thing as saying that the item's reference count is greater than or equal to 2 (ignoring PHP's internal memory optimizations for equal-valued scalars)
Administrivia note: I'd love to give mario the site credit for the answer, as he was the first to suggest the correct answer, but since he wrote it in a comment, not an actual answer, I couldn't do it :-(
Pass the array as a reference:
function setArray(&$vals) {
foreach ($vals as $key => &$value) {
$this->myarray[] =& $value;
}
$this->dumpArray();
}
My guess (which could be wrong in some details, but is hopefully correct for the most part) as to why this makes your code work as expected is that when you pass as a value, everything's cool for the call to dumpArray() inside of setArray() because the reference to the $vals array inside setArray() still exist. But when control returns to myfunc() then that temporary variable is gone as are all references to it. So PHP dutifully changes the array to string values instead of references before deallocating the memory for it. But if you pass it as a reference from myfunc() then setArray() is using references to an array that lives on when control returns to myfunc().
Adding the & in the argument signature fixed it for me. This means the function will receive the memory address of the original array.
function setArray(&$vals) {
// ...
}
CodePad.
I just encountered this same problem in almost exactly the same situation (I'm writing a PDO wrapper for my own purposes). I suspected that PHP was changing the reference to a value once no other variables were referencing the same data, and Rick, your comments in the edit to your question confirmed that suspicion, so thank you for that.
Now, for anybody else who comes across something like this, I believe I have the simplest solution: simply set each relevant array element to a reference to itself before passing the array to call_user_func_array().
I'm not sure exactly what happens internally because it seems like that shouldn't work, but the element becomes a reference again (which you can see with var_dump()), and call_user_func_array() then passes the argument by reference as expected. This fix seems to work even if the array element is still a reference already, so you don't have to check first.
In Rick's code, it would be something like this (everything after the first argument for bind_param is by reference, so I skip the first one and fix all of the ones after that):
for ($i = 1, $count = count($this->bindArgs); $i < $count; $i++) {
$this->bindArgs[$i] = &$this->bindArgs[$i];
}
call_user_func_array(array($stmt, 'bind_param'), $this->bindArgs);

Categories