I have a function in PHP, which has some arguments default to null, so that I can easily call it with less than the full number of arguments.
The problem is, that when I use a null-defaulted argument directly, I get the given argument, but when I try to copy that value to another variable, the variable only gets the default value of null.
It looks like this:
// inside my MySQLI wrapper class...
public function bind(&$stmt, $types = "", &$arg1, &$arg2 = null, &$arg3 = null /* this goes up to 20; it's auto-generated by another script */)
{
echo "dumping...";
var_dump($arg1); // var_dump shows value from function call (string(0))
var_dump($arg2); // ditto
echo "...dumped";
if ($arg2 != null) $foo = $arg2; var_dump($foo); echo "foo"; // var_dump shows that $foo is NULL
/* ... */
}
I call the function like this, from another script:
(It's a dummy script dealing with trucks and cars.)
$make = "";
$model = "";
$year = 0;
$license = "";
list($error, $message) = $mysql->bind($stmt, "", $make, $model, $year, $license);
My bind() function is a wrapper to MySQLI's bind_param() and bind_result() functions.
I've only included the top couple lines, because it's failing at that point already, before it even gets to the actual logic.
Right now, it just looks like it's a bug in PHP, because this doesn't follow what I know about how variables, arguments, default arguments, and references work.
Furthermore, this problem only appears to manifest itself in my real code, and doesn't appear in my simple php file that I coded up to test this.
Further info:
$foo gets assigned NULL, when $arg2 is an empty string, "", and properly gets assigned when it is a non-empty string. Empty strings are still valid strings, so why is PHP doing this?
The problem is the != comparison. What happens is that PHP type-juggles at least one of your variables, and as such, "" != null evaluates to false. The table part-way down this page shows what will happen for comparisons between different types. A type-strict form !== is needed:
if ($arg2 !== null)
$foo = $arg2;
Related
I have a method in a class with this signature:
public static function execute($query, $data = array(), &$result = NULL, $onlyOne = true)
The point is to execute a SQL query with data (if relevant) and modify the result variable (if set to true) and finally returning only first row if the caller is interested in it only. It handles SQL errors etc. and overall is a convenience thing as I try to reduce the sql queries in the script to single line or two (checking the result).
Now I have a problem with this:
Db::execute("query with :data", array(":data" => "data"), ($row = true));
The PHP complains about the $row not being a variable:
Strict standards: Only variables should be passed by reference
As I understand the PHP scopes the scope is function-wide and not block-wide (as in C++ for example). Therefore the $row should be a variable in my function scope and available for passing as reference (and not just a temporary inside the function call). If I declare it beforehand it works but it inflates the code somewhat. Is there a way to keep the declaration of that variable in the function call like this?
EDIT: To add to the accepted answer. The reference can be assigned any default value so I changed my signature to:
public static function execute($query, $data = array(), &$result = -1, $onlyOne = true)
which allows me to call it using:
Db::execute("query with :data", array(":data" => "data"), $row);
without pre-declaring the result variable. Inside I simply check if the $result is not -1 (or I could check if it is NULL signifying new empty variable) to fill the result in.
Db::execute("query with :data", array(":data" => "data"), ($row = true));
^^^^^^^^^^^^^
That's an assignment. In PHP, the result of an assignment is the value being assigned, so you're passing true in to your function. That argument is defined as a reference, and you can't create a reference to true.
You'd have to do:
$row = true;
DB::execute(....., $row);
I accidentally copied the function header when calling a function in my code, and for some reason the code still works. Why is this?
$data = Utilities::multi_curl($substance_year_combo_groups, $files = false, $download_folder = null, $file_name = null, $pop = false, $handle_key = 'results');
Obviously, it's supposed to be written like this:
$data = Utilities::multi_curl($substance_year_combo_groups, false, null, null, false, 'results');
But I can see in my debugging that the last parameter indeed is 'results'. Shouldn't a pure variable assignment just be evaluated as true?
The $handle_key is null by default in the function header.
Already answered, but to include a reference from PHP: Assignment Operators Manual
The value of an assignment expression is the value assigned. That is, the value of "$a = 3" is 3.
The arguments get evaluated, and the results of those evaluations are passed in.
Remember that in PHP assignments have a "return value", which is the value that was assigned.
$foo = 'bar';
echo $foo;
$result = some_function($foo = 'blah');
echo $foo;
This code will echo out barblah, and pass blah into some_function as the argument.
This is the exact same mechanism that allows:
$a = $b = $c = $d = 42;
to work, and assigns 42 to all four variables.
The result of the assignment operation is the value being assigned. For instance, I can do this:
if($result = do_something_that_may_fail()) {}
Whatever the do_something_that_may_fail() method returns will be assigned to $result and, if that anything that doesn't evaluate to false, the if block will be executed. A byproduct of this is that you can still reference $result inside of the if block.
The same thing is happening in your method call, the values are being assigned and the value itself is being sent to the method.
I inherited a php codebase that contains some variable assignments in function calls:
<?php
function some_func($foo, $state) {
....
}
some_func("random stuff", $state = true);
...
some_func("other stuff", $state = false);
...
?>
I did some research and some tests, but I can't find out what the defined behaviour for this code is in PHP.
How is the value of the second argument to some_func() computed? The content of the 4state variable (true on first call, false on second)? Or is it the outcome of the assignment (i.e. assigning true/false to the variable $state was successful, so some_func received true?
What is the value of the $state variable in the global scope? The result of the assignment, i.e. true after the first call, false after the second?
I too had to work with a codebase that had function calls similar to this. Luckily, I had access to developers that wrote the code. Here is what I learned.
Scenario 1:
Simply a way to document the code. You know the variable name that you are passing into the function.
Scenario 2:
Here is a link: http://www.php.net/manual/en/language.references.pass.php
If you see, they do specifically call out your case:
foo($a = 5); // Expression, not variable
A 'dummy' pass-by-ref. Depending on your version of PHP, it may throw a warning. I was getting this: Strict Standards: Only variables should be passed by reference in ...
Now let me go into detail of what is happening in this situation.
The dangerous thing is that your example that you have provided wont display the "gotcha!" behavior. In a case like this, your $arg2 that you are echoing outside of the function will always be what the expression in the function call set it to be. Furthermore, the function that is being called will also be sent a "copy" of that value, and work with that. I say "copy" because even though the function is requiring a pass-by-ref, it is actually getting a copy, similar to what a normal function parameter would get.
If you modify the $arg2 that is inside of the function it WILL NOT modify the $arg2 that is outside of the function, as you would expect from a function that is pass-by-ref.
To assign a variable at function call time, you have to pass it as a reference (&$var):
function my_function($arg1, &$arg2) {
if ($arg1 == true) {
$arg2 = true;
}
}
my_function(true, $arg2 = false);
echo $arg2;
outputs 1 (true)
my_function(false, $arg2 = false);
echo $arg2;
outputs 0 (false)
How is the value of the second argument to some_func() computed?
It's not "computed" but explicitly setup : $state = true / false and then passed as argument to some_func().
What is the value of the $state variable in the global scope?
$state does not exist in the global scope.
I have a PHP function, like this:
function($foo = 12345, $bar = false){}
What I want to do, is call this function with the default argument of $foo passed, but $bar set to true, like this (more or less)
function(DEFAULT_VALUE, true);
How do I do it? How do I pass an argument as a function's default value without knowing that value?
Thanks in advance!
This is not natively possible in PHP. There are workarounds like using arrays to pass all parameters instead of a row of arguments, but they have massive downsides.
The best manual workaround that I can think of is defining a constant with an arbitrary value that can't collide with a real value. For example, for a parameter that can never be -1:
define("DEFAULT_ARGUMENT", -1);
and test for that:
function($foo = DEFAULT_ARGUMENT, $bar = false){}
put them the other way round:
function($bar = false, $foo = 12345){}
function(true);
The usual approach to this is that if (is_null($foo)) the function replaces it with the default. Use null, empty string, etc. to "skip" arguments. This is how most built-in PHP functions that need to skip arguments do it.
<?php
function($foo = null, $bar = false)
{
if (is_null($foo))
{
$foo = 12345;
}
}
?>
PHP can't do exactly that, so you'll have to work around it. Since you already need the function in its current form, just define another:
function blah_foo($bar)
{
blah(12345, $bar);
}
function blah($foo = 12345, $bar = false) { }
i think, it's better sorted based on the argument that the most frequently changed, $bar for example, we put it first,
function foo( $bar = false, $foo = 12345){
// blah blah...
}
so when you want to pass $bar to true, and $foo to 12345, you do this,
foo(true);
I'm building a small abstract class that's supposed to make certain tasks easier.
For example:
$var = class::get('id');
would run check if there's pointer id in the $_GET, returning a string or array according to parameters. This should also work for post and request and maby more.
I'm doing it in the way there's function for all the superglobals. I'm using get as example:
get function gets a pointer as parameter, it calls fetchdata function and uses the pointer and "$_GET" as the parameters.
fetchdata is supposed to just blindly use the string it got as superglobal and point to it with the other param. Then check if it exists there and return either the value or false to get function, that returns the value/false to caller.
Only problem is to get the string work as superglobal when you don't know what it is. I did this before with a switch that checked the param and in case it was "get", it set $_GET to value of another variable. However I don't want to do it like that, I want it to be easy to add more functions without having to touch the fetchdata.
I tried $method = eval($method), but it didn't work. ($method = "$_GET"), any suggestions?
EDIT: Sorry if I didn't put it clear enough. I have a variable X with string value "$_GET", how can I make it so X gets values from the source described in the string?
So simply it's
$X = $_GET if X has value "$_GET"
$X = $_POST if X has value "$_POST"
I just don't know what value X has, but it needs to get data from superglobal with the same name than its value.
According to this page in the manual:
Note: Variable variables
Superglobals cannot be used as variable variables inside functions or class methods.
This means you can't do this inside a function or method (which you would be able to do with other variables) :
$var = '_GET';
${$var}[$key]
Instead of passing a string to fetchdata(), could you not pass $_GET itself? I think PHP will not copy a variable unless you modify it ('copy on write'), so this shouldn't use memory unnecessarily.
Otherwise there are only nine superglobals, so a switch-case as you have suggested isn't unreasonable.
You could do this with eval() if you really had to, something like:
eval('return $_GET;');
I think that would be unnecessary and a bad idea though; it is slow and you need to be extremely careful about letting untrusted strings anywhere near it.
Don't use eval. Just use reference.
//test value for cli
$_GET['test'] = 'test';
/**
* #link http://php.net/manual/en/filter.constants.php reuse the filter constants
*/
function superglobalValue($key, $input = null) {
if ($input === INPUT_POST)
$X = &$_POST;
else
$X = &$_GET;
return (isset($X[$key]) ? $X[$key] : false);
}
function getArrayValue(&$array, $key) {
return (array_key_exists($key, $array) ? $array[$key] : false);
}
//test dump
var_dump(
superglobalValue('test', INPUT_GET),
superglobalValue('test', INPUT_POST),
getArrayValue($_GET, 'test'),
getArrayValue($_POST, 'test')
);
$_GET, $_POST and $_REQUEST dont have any null values by default, only string or array. So I used isset there instead of array_key_exists.
Param order: I always put required params before optional when I can, and the data objects before the manipulation/subjective params. Thats why key is first param for superglobalValue and second param for getArrayValue.
I'm not quite sure what you're trying to achieve, but you could have a look at the __callStatic magic method
class example{
protected static $supers = array('GET', 'POST', 'SERVER', 'COOKIE');
public static function __callStatic($functionName, $arguments){
$index = arguments[0];
$desiredSuper = strtoupper($functionName);
if(in_array($desiredSuper, self::$supers)){
doStuff ( $_{$desiredSuper}[$index] );
}
else{
throw new Exception("$desiredSupper is not an allowed superGlobal");
}
}
}
you could then do:
example::get('id'); //wo do stuff to $_GET['id']
example::server('REQUEST_METHOD'); //Will do stuff to $_SERVER['REQUEST_METHOD']
example::foo('bar'); //throws Exception: 'FOO is not an allowed superGlobal'
Php manual on magic methods: http://ca.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.methods
Edit
I just noticed your edit, you could try:
$X = {$X};
You can use $_REQUEST["var"] instead of $_GET["var"] or $_POST["var"].
A more complicated way would be to test if the variable exists in the GET array, if it doesnt then its POST. If it does its GET.
$var = null;
if (isset($_GET["varname"]))
{
$var = $_GET["varname"];
}
else
{
$var = $_POST["varname"];
}
If you want a variable to be accessible globally, you can add it tot he $GLOBALS array.
$GLOBALS['test']='test';
Now you can fetch $GLOBALS['test'] anywhere.