Using Superglobals for variable variables works

php.internals

Michael Virnstein

21 years ago
Hi PHP-Devs, i don't know if it is a bug or a new feature of php5. The php manual says, that using superglobals for variable variables isn't working, so something like this shouldn't work: <?php $a = 'hello world'; echo ${$_GET['test']}; ?> Actually it does work. Is this intended now? Would be nice, but if it is a simple bug and i can't rely on it to work in the future, i won't use it. Hope it is a feature though. ;) Regards, Michael

Andi Gutmans

21 years ago
The idea is that the following doesn't work by design: $a = "_GET"; var_dump($$a); At 11:31 PM 1/28/2005 +0100, Michael Virnstein wrote:

Michael Virnstein

21 years ago
but this does also work, atm. i'm using PHP 5.0.2 on Windows XP with apache 1.3 and if i call my php page with http://localhost/test.php?test=works it works. //test.php <?php $a = '_GET'; echo ${$a}['test']; var_dump($$a); ?> the output is: works array(1) { ["test"]=> string(5) "works" } Andi Gutmans wrote:

Michael Virnstein

21 years ago
ok, sorry, now i have it. i read the german manual version first and that says that you can't use what i said. The german is wrong and the english version of the manual has it right. It says: "Please note that variable variables cannot be used with PHP's Superglobal arrays within functions or class methods." i tested it and it is true, you can't used it inside functions, because the variable would not reference to the superglobal variable this way. <?php function test() { $a = '_GET'; var_dump($$a); // $$a has no reference to the superglobal $_GET } test(); ?> the output is NULL Anyway, thanks for your time. ;) Regards, Michael Michael Virnstein wrote:

Sara Golemon

21 years ago
> but this does also work, atm. i'm using PHP 5.0.2 on Windows XP with > apache 1.3 and if i call my php page with > http://localhost/test.php?test=works it works. > > //test.php > <?php > $a = '_GET'; > echo ${$a}['test']; > var_dump($$a); > ?> >
It works when you're already at the global scope, but try this: <?php function foo() { var_dump($_GET); $g = '_GET'; var_dump($$g); } foo(); ?> The first var_dump() will work right because $_GET is a superglobal and ZE picks it out of the global context correctly, the second var_dump() will yield a NULL and an undefined index since it will try to access it from the local symbol table and not find it. Declaring a variable as superglobals is just a hint to the scripting engine to retreive it from the global symbol table rather than the active symbol table (Superglobals, by definition live in the global symbol table). Your example worked because at the time the variable variable was resolved, the global symbol table WAS the active symbol table. -Sara

Michael Virnstein

21 years ago
yep, figured it out already. Problem is the german version of the manual. It is wrong and doesn't say anything about functions and methods. It simply says you can't use superglobals for variable variable and uses a illogical example ${$_GET}, whatever that should mean ;). The english manual is right though and says you can't use it in functions and methods. Thanks anyway, Michael Sara Golemon wrote:

Michael Virnstein

21 years ago
one thing though. Will this behaviour change in future versions? I think it is somehow odd and inconsistent to not be able to use them with variable variables inside functions and methods. It's an unnecessary restriction and will keep ppl using the old $HTTP_*_VARS. I also see no reason for it to stay that way and i think it wasn't intended by the developers in the first place but turned out to be that way afterwards. So I hope it'll change, would make more sense. Regards, Michael

Michael Virnstein

21 years ago
Atm it isn't possible to use a construct like $var = ${'_GET'}; inside a function or method. Will this behaviour change in future versions of PHP? I think it is somehow odd and inconsistent to not be able to use the superglobals that way, while it is possible outside of functions and methods and also with any other variable, e.g. the $HTTP_*_VARS. Imo it's an unnecessary restriction and will keep ppl using the old $HTTP_*_VARS. I also see no reason for it to stay that way and i think it wasn't intended by you, the developers, in the first place but turned out to work that way afterwards. So I really hope it'll change, would make much more sense. What do you think? Regards, Michael P.S.: Sorry for the "double" post, but i thought this is worth an own "topic".

Jason Sweat

21 years ago
On Mon, 31 Jan 2005 19:27:31 +0100, Michael Virnstein <michael.virnstein@brodos.de> wrote:
> Atm it isn't possible to use a construct like $var = ${'_GET'}; inside a > function or method. Will this behaviour change in future versions of > PHP? I think it is somehow odd and inconsistent to not be able to use > the superglobals that way, while it is possible outside of functions and > methods and also with any other variable, e.g. the $HTTP_*_VARS. > Imo it's an unnecessary restriction and will keep ppl using the old > $HTTP_*_VARS. I also see no reason for it to stay that way and i think > it wasn't intended by you, the developers, in the first place but turned > out to work that way afterwards. > So I really hope it'll change, would make much more sense. What do you > think?
Try $_GET['foo'] = 'bar'; function x($val, $sg) { return $GLOBALS[$sg][$val]; } echo x('foo','_GET'); Regards, Jason http://blog.casey-sweat.us/

Sara Golemon

21 years ago
> Atm it isn't possible to use a construct like $var = ${'_GET'}; inside a > function or method. Will this behaviour change in future versions of > PHP? I think it is somehow odd and inconsistent to not be able to use > the superglobals that way, while it is possible outside of functions and > methods and also with any other variable, e.g. the $HTTP_*_VARS. > Imo it's an unnecessary restriction and will keep ppl using the old > $HTTP_*_VARS. I also see no reason for it to stay that way and i think > it wasn't intended by you, the developers, in the first place but turned > out to work that way afterwards. > So I really hope it'll change, would make much more sense. What do you > think? >
It comes down to how autoglobals are resolved. When a script containg $_GET is parsed, ZE says "Oh, _GET is a registered autoglobal, I'll flag that for fetching from the global symbol table". Important factoid here: The target hashtable is resolved at compile time. All other variable lookups (not entirely true, but run with me here) are resolved at run-time roughly thus: Check (hashtable) for (expression) In the case of $foo these break down as: Check (active_symbol_table) for ('foo' index) In the case of $bar['foo'] they become: Check (Check (active_symbole_table) for ('bar' index')) for ('foo' index) So in order for ZE to resolve the autoglobals correctly during runtime it has to ask two questions for every *part* of every variable resolution: "Are we checking against the active symbol table? Is the index we're looking for in the autoglobal registry?" If so, replace active_symbol_table with the global symbol_table, otherwise do the lookup as normal. Putting this extra bundle of steps completely negates the speed-benefit of resolving autoglobals at compile time. Given that, I certainly wouldn't hold my breath waiting for that functionality to change. -Sara P.S. - This is why: $a['b']['c']['d']['e']['f'][] = 1; $a['b']['c']['d']['e']['f'][] = 2; $a['b']['c']['d']['e']['f'][] = 3; $a['b']['c']['d']['e']['f'][] = 4; is slower than: $b = &$a['b']['c']['d']['e']['f']; $b[] = 1; $b[] = 2; $b[] = 3; $b[] = 4; Since in the first example the engine is doing 24 hash table lookups as opposed to 11 in the second example. And yes, $a['b']['c']['d']['e']['f'] = range(1,4); would be even faster.... it's called an example :p

Sara Golemon

21 years ago
> So in order for ZE to resolve the autoglobals correctly during runtime it > has to ask two questions for every *part* of every variable resolution:
"Are
> we checking against the active symbol table? Is the index we're looking
for
> in the autoglobal registry?" If so, replace active_symbol_table with the > global symbol_table, otherwise do the lookup as normal. >
I knew this statement felt wrong when I wrote it so I dove in and looked at the code again....It's not actually necessary to test the what scope we're in because of the way the parser is laid out... however it does still add an extra hashtable lookup that shouldn't be necessary, so my original position stands as is for now. If someone would like to benchmark the following patch (Or point out any fatal flaws in it) I'd be interrested in the results, but even if there's no perceptible difference over a million itterations, I'd probably still vote -1 on changing it. -Sara Index: Zend/zend_execute.c =================================================================== RCS file: /repository/ZendEngine2/zend_execute.c,v retrieving revision 1.692 diff -u -r1.692 zend_execute.c --- Zend/zend_execute.c 22 Jan 2005 02:29:18 -0000 1.692 +++ Zend/zend_execute.c 31 Jan 2005 22:13:50 -0000 @@ -1001,6 +1001,10 @@ { switch (opline->op2.u.EA.type) { case ZEND_FETCH_LOCAL: + if (zend_hash_exists(CG(auto_globals), variable->value.str.val, variable->value.str.len + 1)) { + /* Dynamically resolved auto global */ + return &EG(symbol_table); + } return EG(active_symbol_table); break; case ZEND_FETCH_GLOBAL:

Xuefer Tinys

21 years ago
$_GET is solved at compile time, which is good, but this make other variables bad at execution time? how much does this patch slow execution down? it's one more hash lookup. affect speed of $var or $$var? or both? On Mon, 31 Jan 2005 14:24:41 -0800, Sara Golemon <pollita@php.net> wrote:

Michael Virnstein

21 years ago
Sara Golemon wrote:
> Index: Zend/zend_execute.c > =================================================================== > RCS file: /repository/ZendEngine2/zend_execute.c,v > retrieving revision 1.692 > diff -u -r1.692 zend_execute.c > --- Zend/zend_execute.c 22 Jan 2005 02:29:18 -0000 1.692 > +++ Zend/zend_execute.c 31 Jan 2005 22:13:50 -0000 > @@ -1001,6 +1001,10 @@ > { > switch (opline->op2.u.EA.type) { > case ZEND_FETCH_LOCAL: > + if (zend_hash_exists(CG(auto_globals), > variable->value.str.val, variable->value.str.len + 1)) { > + /* Dynamically resolved auto global */ > + return &EG(symbol_table); > + } > return EG(active_symbol_table); > break; > case ZEND_FETCH_GLOBAL:
i must admit, that i don't know very much about the zend engine and the php core in general, but if "opline->op2.u.EA.type" "knows" if the variable is a local one, a global one or a static one, why hasn't it the value of ZEND_FETCH_GLOBAL for superglobals in the first place? Michael

Andi Gutmans

21 years ago
Superglobals are compiled into global fetches. However, we didn't want to and don't want to slow down the average fetch by checking if it's a superglobal first (i.e. variable variable). This also includes $this which is evaluted at compile-time. I think this is quite fair. Andi At 11:12 PM 2/3/2005 +0100, Michael Virnstein wrote: