PHP - Force eval() to run in global scope? - php

This is not a duplicate, as my requirements are that the eval() statement MUST be in the function. Also, I do not require that the function “set_myvar” return anything at all.
Say I have this code (ignore how silly it is; it's just hypothetical):
$myvar = null;
function set_myvar(){
eval("$myvar = 'Hello world';");
}
set_myvar();
echo $myvar;
Can I force eval() to be called in the global scope, so that the program echoes "Hello world"?

Mandatory disclaimer: in my self-inflated opinion, eval() is a code smell. If you need it
for something other than a quick&dirty hack, somehow, somewhere, you're doing something wrong.
That said... as I see it, you need two runs of eval().
Your current code is, say:
$program = '...';
eval($program);
And program will set some variables, have some side effects, and maybe be slow as well.
We don't want to run it twice; we just need to discover how it affects the scope, to replicate it at leisure.
What you can do is add to $program a small piece which will use get_defined_vars() or var_export() to provide a representation of the environment that is ordered and machine parsable, and return that:
$program .= "return get_defined_vars();"
Once you have it, you can have an assignment that has no more side effects than setting the variables you need. All you need to do now is execute it in the global scope.
But with this method you also have a list of the names of the variables in that environment:
$names = array_keys($returnedVars);
You can then extract those names, and add a "global" at the start:
$program2
is now "global $var1, $var2, $var3...\n extract($returnedVars);" with environment coming from the return value of the first eval().
And by eval()'ing that second program, the variables should be injected into the global scope
with a minimum of fuss.

Related

pass variable to function using same name

Is it considered wrong to pass a variable to a function where the variable passed has the same name as the variable in the function itself? I never see anyone do this, but wondered if there was a reason why (other than readability). Example:
$variable = "value";
function myFunction($variable) {
return $variable;
}
No there is not problem with doing this except some naming confusion. Because,
$variable = "value"; // this declaration belong to a global scope
// but $variable passing through as argument, makes it scope within that function declaration only
function myFunction($variable) {
return $variable;
}
You might have expect that if you call the function like myFunction() i.e. without any parameter, it will return you "value", but absolutely not, cause $variable passed as argument is not the global one.
But if you try to use the global $variable within the myFunction, then it will cause some conflict, that is why no one do this.
Aside from making variables a bit confusing to track, there is nothing wrong with this. They exist in different scopes.
It is up to you but vice versa, if you want to pass intentionally a variable into a function.. then you have to use this:
$variable = "value";
function myFunction($variable) {
global $variable;
return $variable;
}
echo myFunction('another value');
One more point. When creating functions you may set a default value for your params to avoid errors:
function myFunction($variable="default value") {
return $variable;
}
echo myFunction();
There is nothing wrong with it at all, and I would have thought it's quite common.
However, you should always be choosing the best name for a variable, or a parameter, in each situation, not applying one rule to your whole code.
For instance, in a database lookup function, it might be obvious what $id refers to, so it would be a sensible parameter name; but where you are calling it, you will likely have lots of IDs of various sorts, so a more descriptive variable name should be used. On the other hand, each time you build an SQL string, it's probably the only such variable in scope, so giving it a consistent name keeps your code tidy and easy to read.
A common observation is that all code is read more often than it is written (yes, even your personal website that you wouldn't dare share with the world). Variable names should be chosen with that in mind: what makes this code easy to read, understand, and maintain?
You can use global and add default value in param variable.
$variable = "value";
function myFunction($variable = '') {
global $variable;
return $variable;
}

Variable variable string constructed for "$GLOBALS" works within global scope, but not function scope

An important note: $GLOBALS are dirty and evil. Don't use them. Ever. Never ever ever.
Please focus on the fact that it doesn't work and not why you would be doing this in the first place, it is purely a theoretical question about a technical exercise.
This is a rather weird one. I'm attempting to construct a variable variable using a string named $GLOBALS.
From the global scope
Let's see what we get when var_dump()ing this in the global scope.
$g = sprintf('%s%s%s%s%s%s%s', chr(71), chr(76), chr(79), chr(66), chr(65), chr(76), chr(83));
var_dump($$g);
The result is an array of global variables, which you can see here. Great! So, let's try this in a function.
From a function scope
First, let's just make sure that we can actually run an $GLOBALS check within a function.
function globalAllTheThings()
{
var_dump($GLOBALS);
}
globalAllTheThings();
The result is: it works!! You can see this here.
Now, let's try the first test that we used in the global scope, within the function, and see what happens.
function globalAllTheThings()
{
$g = sprintf('%s%s%s%s%s%s%s', chr(71), chr(76), chr(79), chr(66), chr(65), chr(76), chr(83));
var_dump($$g);
}
globalAllTheThings();
For simplicity's sake
You can also try this without the weird obfuscation (don't ask).
function globalAllTheThings()
{
$g = 'GLOBALS';
var_dump($$g);
}
globalAllTheThings();
It returns NULL. What's that about?? Why does it return NULL, and what can I do to get this working. Why, you ask? For educational purposes of course, and for science!
Because the manual says so:
Warning
Please note that variable variables cannot be used with PHP's Superglobal arrays within functions or class methods. The variable $this is also a special variable that cannot be referenced dynamically.
http://php.net/manual/en/language.variables.variable.php
It's simply "special". PHP is "special". Superglobals don't play by the same rules as regular variables to begin with. Someone forgot to or decided against making them compatible with variable variables in functions. Period.

Which is the better way to reference a variable outside the function scope?

I can change $var in my function in one of two ways: either pass it by reference or using the global keyword.
$var1 = 10;
function test1() {
global $var1;
$var1++;
}
function test2(&$var) {
$var++;
}
Both approaches have the same result, but is there any difference between them? Which one is preferred and which one is faster?
1. None of them is preferred.
Unless you have a special reason to do otherwise, the preferred would be
$var1 = 10;
$var1 = test3($var1);
function test3($var)
{
return $var + 1;
}
Introducing coupling between different parts of your program (if using a global) is something you should always reasonably try to avoid.
In addition, if there is no concrete reason to make your function accept its argument by reference you should also avoid doing that. Only a very miniscule fraction of all functions behave this way, so if nothing else you are risking confusion among the developers who use this code for no real benefit.
2. You do not need to think about which one is faster.
Unless you have profiled your application under real world scenarios and have found that this function is a bottleneck (which of course it will never be in this simple form), then optimizing for performance at the expense of writing clear and maintainable code is not only pointless, but also detrimental.
As a bonus, I should mention that using a reference might actually make the function slower.
Since global variables pollute the namespace (i.e. can be used inadvertently and/or by another function with the same idea), references are preferable.
However, in many cases (where the data structures are more complex), you should be using objects instead, like this:
class Counter {
private $val = 10;
public function increment() {
$this->val++;
}
}
The speed of any of these solutions does not matter and will be dwarfed by any actual computation.
Preferred way is avoiding globals. The reason is that if you put a variable in global scope, you lose control over it - since projects grow, you might forget what the name of your global variable is and you can overwrite it accidentally somewhere causing an incredible headache for yourself.
From performance point of view - reference is faster and it's also much safer to use because you define in method's signature whether a reference is being used or not, making the actual function call easy as you don't have to pass the variable by reference explicitly.

PHP global or $GLOBALS

Is there a best practice / recommendation when I want to use a variable declared outside of a function when it comes to using:
global $myVar
$GLOBALS['myVar']
Thank you.
Well, you should only use globals in limited circumstances, but to answer your question:
global is potentially marginally faster (it will rarely make a difference).
$GLOBALS (not $GLOBAL) is more readable, because every time you see it, you know you are accessing/changing a global variable. This can be crucial in avoiding nasty bugs.
Inside the function, if you want to unset a global variable, you must use unset($GLOBALS['varname']), not global $varname; unset($varname);.
As to points 1 and 2, I'll quote Sara Golemon here:
What does that mean for your use of the $GLOBALS array? That's right, the global keyword is technically faster. Now, I want to be really clear about one thing here. The minor speed affordance given by using your globals as localized [compiled variables] needs to be seriously weighed against the maintainability of looking at your code in five years and knowing that $foo came from the global scope. something_using($GLOBALS['foo']); will ALWAYS be clearer to you down the line than global $foo; /* buncha code */ something_using($foo); Don't be penny-wise and pound foolish..
What you should really do is pass the variable to the function instead of using a global at all.
An example how to change a variable outside of the function via passing it as reference parameter:
function myFunc(&$myVar)
{
$myVar = 10;
}
$foo = 0;
myFunc($foo);
var_dump($foo); // yields 10
Use global at the top of your function. That way, you can easily see what globals are used.
global $var; is equivalent to $var =& $GLOBALS['var'].
Some people suggested that it is faster than using $GLOBALS, however it's not necessarily the case. If you use the variable only once, $GLOBALS will be faster, because you won't waste time for assignment.
However, if you do use the variable multiple times, using global (or the equivalent assignment) is faster, because search the array for the var key only once.
That's it about speed. However, the speed difference is really small, and readability is more important. However, different people have different preferences about readability -- I prefer global, some other people answering here prefer $GLOBALS, so it's up to you to decide what looks better.

How to set a variable in the caller scope, like the extract() function

I know that directly setting a variable in the scope of caller is probably not a good idea.
However, the PHP extract() function does exactly that! I would like to write my own version of extract() but cannot figure out how to actually go about setting the variables in the caller. Any ideas?
The closest I have come is modifying the caller's args using debug_backtrace(), but this is not exactly the same thing...
You can't modify local variables in a parent scope - the method which extract() uses is not exposed by PHP.
Also, what you get back from debug_stacktrace() isn't magically linked to the real stack. You can't modify it and hope your modifications are live!
You could only do it in a PHP extension. If you call an internal PHP function, it will not run in a new PHP scope (i.e., no new symbol table will be created). Therefore, you can modify the "parent scope" by changing the global EG(active_symbol_table).
Basically, the core of the function would do something like extract does, the core of which is:
if (!EG(active_symbol_table)) {
zend_rebuild_symbol_table(TSRMLS_C);
}
//loop through the given array
ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table),
Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, data, 1, 0);
There are, however, a few nuances. See the implementation of extract, but keep in mind a function that did what you wanted wouldn't need to be as complex; most of the code in extract is there to deal with the several options it accepts.
You can abuse the $GLOBALS scope to read and write variables from the caller of your function. See below sample function, which reads and write variables from the caller scope.
And yes, I know its dirty to abuse the $GLOBAL scope, but hey, we're here to fix problems ain't we? :)
function set_first_name($firstname) {
/* check if $firstname is defined in caller */
if(array_key_exists('firstname', $GLOBALS)) {
$firstname_was = $GLOBALS['firstname'];
} else {
$firstname_was = 'undefined';
}
/* set $firstname in caller */
$GLOBALS['firstname'] = $firstname;
/* show onscreen confirmation for debugging */
echo '<br>firstname was ' . $firstname_was . ' and now is: ' . $firstname;
}
set_first_name('John');
set_first_name('Michael');
The function returns the following output:
<br>firstname was undefined and now is: John
<br>firstname was John and now is: Michael
It depends on how badly you need to do this. If it's only for source beauty, find another way. If, for some reason, you really need to mess with parent scope, there's always a way.
SOLUTION 1
The safest method would be to actually use extract itself for this job, since it knows the trick. Say you want to make a function that extracts elements of an array but with all the names backwards - pretty weird! -, let's do this with a simple array-to-array transformation:
function backwardNames($x) {
$out = [];
foreach($x as $key=>$val) {
$rev = strrev($key);
$out[$rev] = $val;
}
return $out;
}
extract(backwardNames($myArray));
No magic here.
SOLUTION 2
If you need more than what extract does, use eval and var_export. YES I KNOW I KNOW everybody calm down please. No, eval is not evil. Eval is a power tool and it can be dangerous if you use it without care - so use it with care. (There is no way to go wrong if you only eval something that's been generated by var_export - it doesn't give any way to intrusions even if you put values in your array from an untrusted source. Array elements behave well.)
function makeMyVariables() {
$vars = [
"a" => 4,
"b" => 5,
];
$out = var_export($vars,1);
$out = "extract(".$out.");";
return $out;
}
eval(makeMyVariables()); // this is how you call it
// now $a is 4, $b is 5
This is almost the same, except that you can do a lot more in eval. And it's significantly slower, of course.
However, indeed, there is no way to do it with a single call.

Categories