How to know where the variable was initialized or last modified - php

Is there any way in PHP to know where a variable was initialized or assigned the value for first time OR where it was last modified?
I think it should be possible to know this because PHP gives some such hint in errors. Like: Can not redeclare abc() (previously declared in /path/to/file.php)
EDIT:
I need this because:
function abc() {
global $page; //this should be int.
if($page == 2) { ... }
}
But when this function is run; I get error Can not convert object into int. This is because some where in my code $page is overriden by any object. I need to find that place where it was modified. Or I'll have to dig through entire code.

You can find out which object is being assigned to your $page variable using the native php function get_class() :
get_class($page);
You would normally want to debug this using a unit test where you would test that the variable $page is numeric.
If you just want to debug on-screen try:
if(is_object($page)){
die(get_class($page));
}
or via an exception:
if(is_object($page)){
throw new Exception('$page must be numeric object given of type : '.get_class($page));
}
This will help you find which object is being assigned to your variable and just point you in the right direction.

If you want to know where a variable was defined then you are out of luck; there is no way to find that out, other than actually looking through your code and finding it.
Your problem seems to be that you are allowing a variable into a function (using global). Because the variable is defined outside the function it can be modified at any time. If you want to always know what the variable contains then you should pass it as an argument instead:
$page = 2;
function abc($page) {
if($page == 2) { ... }
}
$test = abc($page);
If you want to make sure that the value of the variable is a number then you can find that out by simply validating the argument (or the global variable, for that matter):
if (is_int($page) or ctype_digit($page)) {
echo 'The $page variable is a number';
} else {
echo 'The $page variable is NOT a number';
}

Related

Variable assignment in function call

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.

PHP. Pass variable by reference vs string. How to works with these two different arguments?

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.

Why do people set default parameters in PHP functions?

Example:
function example($x = "")
{
Do something
}
Isn't $x empty by default already? Why set it explicitly?
Isn't $x empty by default already?
If no default is specified, $x is not an empty string by default, but an undefined variable. There is a difference between "" and NULL or undefined.
However, setting the default allows you to omit the parameter when calling the function, without it throwing a warning.
<?php
function test1($x = "DEFAULT") {
echo $x;
}
function test2($x) {
echo $x;
}
// Call each without the parameter $x:
test1();
// DEFAULT
test2();
// Output:
PHP Warning: Missing argument 1 for test2(), called in /home/mjb/test.php on line 10 and defined in /home/user/test.php on line 5
PHP Notice: Undefined variable: x in /home/user/test.php on line 6
The main reason is that setting a default on the declaration makes the argument optional:
$a = example();
$b = example(5);
One reason is so when you reuse the function you dont have to explicitly set the variable. This happens a lot when a default is set to true or false. That way a function can seem to be overloaded like you can do in other oop languages. If that variable didn't contain a default value, you'd always have to set that value or your function would throw an error, however, by setting the variable to a default value you wouldn't have to necessarily set the value of the variable. Hope that helps some :)
Once of the reasons I use default values is so that I do not have to declare the variable when calling function ie:
function something(debug=false){
doing something here;
if ($debug === true){
echo 'SOMETHING';
}else{
return true;
}
}
This way you can debug something bu simply adding the variable to the function call but if you dont' add it the functions assumes it is false. This is valuable in my $_GET security function that I am using to encrypt my $_GET strings when I turn on debug the $_GET is decoded and dumped as an array inside a
<pre>print_r($_GET);</pre>
so that I can see what the values are in the $_GET but the string is still encrypted in the address bar.
Hope that helps

What does & sign mean in front of a variable?

I'm 'dissecting' PunBB, and one of its functions checks the structure of BBCode tags and fix simple mistakes where possible:
function preparse_tags($text, &$errors, $is_signature = false)
What does the & in front of the $error variable mean?
It means pass the variable by reference, rather than passing the value of the variable. This means any changes to that parameter in the preparse_tags function remain when the program flow returns to the calling code.
function passByReference(&$test) {
$test = "Changed!";
}
function passByValue($test) {
$test = "a change here will not affect the original variable";
}
$test = 'Unchanged';
echo $test . PHP_EOL;
passByValue($test);
echo $test . PHP_EOL;
passByReference($test);
echo $test . PHP_EOL;
Output:
Unchanged
Unchanged
Changed!
It does pass by reference rather than pass by value.
This allows for the function to change variables outside of its own scope, in the scope of the calling function.
For instance:
function addOne( &$val ) {
$val++;
}
$a = 1;
addOne($a);
echo $a; // Will echo '2'.
In the case of the preparse_tags function, it allows the function to return the parsed tags, but allow the calling parent to get any errors without having to check the format/type of the returned value.
It accepts a reference to a variable as the parameter.
This means that any changes that the function makes to the parameter (eg, $errors = "Error!") will affect the variable passed by the calling function.
It means that the variable passed in the errors position will be modified by the called function. See this for a detailed look.

Find the name of a calling var

Anyone has an idea if this is at all possible with PHP?
function foo($var) {
// the code here should output the value of the variable
// and the name the variable has when calling this function
}
$hello = "World";
foo($hello);
Would give me this output
varName = $hello
varValue = World
EDIT
Since most people here 'accuse' me of bad practices and global variables stuff i'm going to elaborate a little further on why we are looking for this behaviour.
the reason we are looking at this kind of behaviour is that we want to make assigning variables to our Views easier.
Most of the time we are doing this to assign variables to our view
$this->view->assign('products', $products);
$this->view->assign('members', $members);
While it would be easier and more readable to just be able to do the following and let the view be responsible to determining the variable name the assigned data gets in our views.
$this->view->assign($products);
$this->view->assign($members);
Short answer: impossible.
Long answer: you could dig through apd, bytekit, runkit, the Reflection API and debug_backtrace to see if any obscure combination would allow you to achieve this behavior.
However, the easiest way is to simply pass the variable name along with the actual variable, like you already do. It's short, it's easy to grasp, it's flexible when you need the variable to have a different name and it is way faster than any possible code that might be able to achieve the other desired behavior.
Keep it simple
removed irrelevant parts after OP edited the question
Regardless of my doubt that this is even possible, I think that forcing a programmer on how to name his variables is generally a bad idea. You will have to answer questions like
Why can't I name my variable $arrProducts instead of $products ?
You would also get into serious trouble if you want to put the return value of a function into the view. Imagine the following code in which (for whatever reason) the category needs to be lowercase:
$this->view->assign(strtolower($category));
This would not work with what you're planning.
My answer therefore: Stick to the 'verbose' way you're working, it is a lot easier to read and maintain.
If you can't live with that, you could still add a magic function to the view:
public function __set($name, $value) {
$this->assign($name, $value);
}
Then you can write
$this->view->product = $product;
I don't think there is any language where this is possible. That's simply not how variables work. There is a difference between a variable and the value it holds. Inside the function foo, you have the value, but the variable that held the value is not available. Instead, you have a new variable $var to hold that value.
Look at it like this: a variable is like a bucket with a name on it. The content (value) of the variable is what's inside the bucket. When you call a function, it comes with its own buckets (parameter names), and you pour the content of your bucket into those (well, the metaphor breaks down here because the value is copied and still available outside). Inside the function, there is no way to know about the bucket that used to hold the content.
What you're asking isn't possible. Even if it was, it would likely be considered bad practice as its the sort of thing that could easily get exploited.
If you're determined to achieve something like this, the closest you can get would be to pass the variable name as a string and reference it in the function from the $GLOBALS array.
eg
function this_aint_a_good_idea_really($var) {
print "Variable name: {$var}\n";
print "Variable contents: {$GLOBALS[$var]}\n";
}
$hello="World";
this_aint_a_good_idea_really('hello');
But as I say, that isn't really a good idea, nor is it very useful. (Frankly, almost any time you resort to using global variables, you're probably doing something wrong)
Its not impossible, you can find where a function was invoked from debug_backtrace() then tokenize a copy of the running script to extract the parameter expressions (what if the calling line is foo("hello $user, " . $indirect($user,5))?),
however whatever reason you have for trying to achieve this - its the wrong reason.
C.
Okay, time for some ugly hacks, but this is what I've got so far, I'll try to work on it a little later
<?php
class foo
{
//Public so we can test it later
public $bar;
function foo()
{
//Init the array
$this->bar = array();
}
function assign($__baz)
{
//Try to figure out the context
$context = debug_backtrace();
//assign the local array with the name and the value
//Alternately you can initialize the variable localy
//using $$__baz = $context[1]['object']->$__baz;
$this->bar[$__baz] = $context[1]['object']->$__baz;
}
}
//We need to have a calling context of a class in order for this to work
class a
{
function a()
{
}
function foobar()
{
$s = "testing";
$w = new foo();
//Reassign local variables to the class
foreach(get_defined_vars() as $name => $val)
{
$this->$name = $val;
}
//Assign the variable
$w->assign('s');
//test it
echo $w->bar['s'];
}
}
//Testrun
$a = new a();
$a->foobar();
impossible - the max. ammount of information you can get is what you see when dumping
debug_backtrace();
Maybe what you want to do is the other way around, a hackish solution like this works fine:
<?php
function assign($val)
{
global $$val;
echo $$val;
}
$hello = "Some value";
assign('hello');
Ouputs: Some value
What you wish to do, PHP does not intend for. There is no conventional way to accomplish this. In fact, only quite extravagant solutions are available. One that remains as close to PHP as I can think of is creating a new class.
You could call it NamedVariable, or something, and as its constructor it takes the variable name and the value. You'd initiate it as $products = new NamedVariable('products', $productData); then use it as $this->view->assign($products);. Of course, your declaration line is now quite long, you're involving yet another - and quite obscure - class into your code base, and now the assign method has to know about NamedVariable to extract both the variable name and value.
As most other members have answered, you are better off suffering through this slight lack of syntactic sugar. Mind you, another approach would be to create a script that recognizes instances of assign()'s and rewrites the source code. This would now involve some extra step before you ran your code, though, and for PHP that's silly. You might even configure your IDE to automatically populate the assign()'s. Whatever you choose, PHP natively intends no solution.
This solution uses the GLOBALS variable. To solve scope issues, the variable is passed by reference, and the value modified to be unique.
function get_var_name(&$var, $scope=FALSE) {
if($scope) $vals = $scope;
else $vals = $GLOBALS;
$old = $var;
$var = $new = 'unique'.rand().'value';
$vname = FALSE;
foreach ($vals as $key => $val) {
if($val === $new) $vname = $key;
}
$var = $old;
return $vname;
}
$testvar = "name";
echo get_var_name($testvar); // "testvar"
function testfunction() {
$var_in_function = "variable value";
return get_var_name($var_in_function, get_defined_vars());
}
echo testfunction(); // "var_in_function"
class testclass {
public $testproperty;
public function __constructor() {
$this->testproperty = "property value";
}
}
$testobj = new testclass();
echo get_var_name($testobj->testproperty, $testobj); // "testproperty"

Categories