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);
Related
We know we can't store a function in variable
<?php
$k = echo("hello");
?>
or
$k = unset($_SESSION['name']);
They all will give error. because we can't store a function in variable directly, but how we can use.
$row = mysqli_fetch_assoc($result);
I know mysqli_fetch_assoc() function output an array which is stored in variable $row . but how a general rule of php is violated. and we are able to store function in a variable
echo and unset are not really functions. If you check the documentation, you'll see that it says:
Return Values
No value is returned
Since no value is returned, you can't assign them to variables.
mysqli_fetch_assoc() is an ordinary function. And like any other function, you can assign its return value to a variable, or pass it as an argument to another function, or use it in an expression.
You would have a hard time programming in PHP if you couldn't assign function values to variables. You couldn't write things like:
$max = max($var1, $var2);
$current_time = time();
We know we can't store a function in a variable
This is not true, but I'll get back to it later.
You are misunderstanding the syntax below:
$variable = functionName();
This does not mean that the function is stored in a variable. It means that the value returned by the function is stored in the variable.
So there is nothing special about:
$row = mysqli_fetch_assoc($result);
You should also know as I hinted at in the beginning, that we can in fact store a function in a variable:
//store the function in variable $func
$func = function($a, $b){return $a + $b;}
//execute the function stored in variable $func
echo $func(2,8); //prints 10
These types of functions -- functions without names -- care called anonymous functions or closures. See the manual
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 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;
I'm writing my own debug functions and I need some help to fix the code below.
I'm trying to print a variable and its name, the file where the variable and the function was declared and the line of the function call. The first part I did, the variable, the variable name, the file and the line is printed correctly.
At the code, a($variable) works good.
The problem is I'd like this function accepts a string too, out of a variable. But PHP returns with a fatal error (PHP Fatal error: Only variables can be passed by reference in ...). At the code, a('text out').
So, how can I fix this code to accept a variable or a string correctly?
code (edited):
function a(&$var){
$backtrace = debug_backtrace();
$call = array_shift($backtrace);
$line = $call['line'];
$file = $call['file'];
echo name($var)."<br>".$var."<br>".$line."<br>".$file;
}
$variable='text in';
a($variable);
a('text out');
I need pass the variable by reference to use this function below (the function get the variable name correctly, works with arrays too):
function name(&$var, $scope=false, $prefix='unique', $suffix='value'){
if($scope) $vals = $scope;
else $vals = $GLOBALS;
$old = $var;
$var = $new = $prefix.rand().$suffix;
$vname = FALSE;
foreach($vals as $key => $val) {
if($val === $new) $vname = $key;
}
$var = $old;
return $vname;
}
The way your code is currently implementing pass by reference is perfect by design, but also by design cannot be changed to have two a() methods - one accepting a variable by reference and the other as a string-literal.
If the desire to pass a string literal instead of assigning it to a variable first is really needed, I would suggest creating a second convenience method named a_str() that actually accepts a string-literal instead of a variable by reference. This method's sole-purpose would be to relay the variable(s) to the original a() method - thereby declaring a variable to pass by reference.
function a_str($var) {
a($var);
}
The only thing to remember is, use a($variable); when passing by reference and a_str('some text'); when not.
Here is the same convenience-method for your name() function:
function name_str($var, $scope=false, $prefix='unique', $suffix='value'){
return name($var, $scope, $prefix, $suffix);
}
The only way to do what you are asking without writing an additional function like #newfurniturey suggests is plain and simply opening and parsing the file where your function was called as text (e.g. with fopen), using the data from debug_backtrace. This will be expensive in terms of performance, but it might be ok if used only for debugging purposes; and using this method you will no longer need a reference in your function, which means you can freely accept a literal as the parameter.
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.