# Using Superglobals for variable variables works

- Thread: https://externals.io/message/14446
- Original archive: https://news-web.php.net/php.internals/14446
- Started by Michael Virnstein on 2005-01-28T22:31:55+00:00
- Messages: 19
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #14446 — Michael Virnstein — 2005-01-28T22:31:55+00:00

URL: https://externals.io/message/14446#14446

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

---

## #14447 — Andi Gutmans — 2005-01-28T22:54:01+00:00

URL: https://externals.io/message/14446#14447 · In reply to: #14446

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:
>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

---

## #14448 — Michael Virnstein — 2005-01-28T23:30:52+00:00

URL: https://externals.io/message/14446#14448 · In reply to: #14447

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:
> 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:
> 
> > […]

---

## #14449 — Michael Virnstein — 2005-01-28T23:42:31+00:00

URL: https://externals.io/message/14446#14449 · In reply to: #14448

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:
> 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:
> 
> > […]

---

## #14450 — Sara Golemon — 2005-01-28T23:46:48+00:00

URL: https://externals.io/message/14446#14450 · In reply to: #14448

> 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

---

## #14452 — Michael Virnstein — 2005-01-28T23:53:44+00:00

URL: https://externals.io/message/14446#14452 · In reply to: #14450

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:
> > […]
> 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

---

## #14453 — Michael Virnstein — 2005-01-30T19:31:02+00:00

URL: https://externals.io/message/14446#14453 · In reply to: #14452

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

---

## #14457 — Michael Virnstein — 2005-01-31T18:27:31+00:00

URL: https://externals.io/message/14446#14457 · In reply to: #14453

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".

---

## #14459 — Jason Sweat — 2005-01-31T18:39:51+00:00

URL: https://externals.io/message/14446#14459 · In reply to: #14457

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/

---

## #14460 — Sara Golemon — 2005-01-31T21:16:14+00:00

URL: https://externals.io/message/14446#14460 · In reply to: #14457

> 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

---

## #14461 — Sara Golemon — 2005-01-31T22:24:41+00:00

URL: https://externals.io/message/14446#14461 · In reply to: #14460

> 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:

---

## #14465 — Xuefer Tinys — 2005-02-01T04:00:37+00:00

URL: https://externals.io/message/14446#14465 · In reply to: #14461

$_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?



> > […]
> "Are
> > […]
> for
> > […]
> 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:

---

## #14466 — M. Sokolewicz — 2005-02-01T10:41:16+00:00

URL: https://externals.io/message/14446#14466 · In reply to: #14465

AFAICS, it should make every local-variable (non-global) lookup slower. 
Since it needs to do a lookup in the globals table first.

Xuefer Tinys wrote:
> $_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?
> 
> 
> 
> 
> > […]

---

## #14467 — Sara Golemon — 2005-02-01T14:59:15+00:00

URL: https://externals.io/message/14446#14467 · In reply to: #14466

> > […]
> AFAICS, it should make every local-variable (non-global) lookup slower. 
> Since it needs to do a lookup in the globals table first.
>
Slows the global ones too, though that can be reduced (while slightly 
worsening the non-global lookups) by not bothering to lookup the auto_global 
table if we're in the global scope already...

-Sara

---

## #14598 — Michael Virnstein — 2005-02-03T22:12:04+00:00

URL: https://externals.io/message/14446#14598 · In reply to: #14461

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

---

## #14616 — Sara Golemon — 2005-02-03T23:52:23+00:00

URL: https://externals.io/message/14446#14616 · In reply to: #14598

> 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?
>
opline->op2.u.EA.type is set at compile time.

In the case of $_GET, the compiler knows that _GET is a superglobal so it 
sets it to ZEND_FETCH_GLOBAL
In the case of $$g (where $g was previously set to '_GET') the compiler 
doesn't know what $g is yet (since it's only really set at runtime), so it 
has no reason to set it to ZEND_FETCH_GLOBAL.

Could it be intelligent enough to see that in:
$g = '_GET';
$$g is always going to be '_GET' and therefor $$g and $_GET are really the 
same thing?
Perhaps.

But what about:
$g = '_';
$g .= 'G';
$g .= 'E';
$g .= 'T';

Or the enigmatic:
$g = '_TRG';
$g = str_rot13($g);

Building the run-time intelligence into a compiler would be.... well do I 
really need to spell that one out?

-Sara

---

## #14619 — Michael Virnstein — 2005-02-04T00:19:56+00:00

URL: https://externals.io/message/14446#14619 · In reply to: #14616

ok, i see. But why does it work with variables that are set as global, 
e.g. the $HTTP_*_VARS:

<?php
function test() {
     global $HTTP_GET_VARS;
     $a = 'HTTP_GET_VARS';
     var_dump($$a);
}
test();
?>

this works inside a function. is it because of the global keyword? If 
so, why can't there be a "magic" "global $_GET, $_POST, $_SESSION ..." 
set in every function, for every superglobal, instead of the way it is 
handled now? The thing i don't get is, why the superglobals behave 
differently than "normal" global variables at all. Ok, you have 
explained the technical reasons, but that's not what i mean. For me as a 
php user (developing in php), the only difference should be, that i 
don't have to use the global keyword. Everything else seems like a bug 
or design flaw to me. Sure i can work around the restriction somehow, 
but this should not be the final solution.
Sorry to bother you further. ;)

Michael

Sara Golemon wrote:
> > […]
> 
> opline->op2.u.EA.type is set at compile time.
> 
> In the case of $_GET, the compiler knows that _GET is a superglobal so it 
> sets it to ZEND_FETCH_GLOBAL
> In the case of $$g (where $g was previously set to '_GET') the compiler 
> doesn't know what $g is yet (since it's only really set at runtime), so it 
> has no reason to set it to ZEND_FETCH_GLOBAL.
> 
> Could it be intelligent enough to see that in:
> $g = '_GET';
> $$g is always going to be '_GET' and therefor $$g and $_GET are really the 
> same thing?
> Perhaps.
> 
> But what about:
> $g = '_';
> $g .= 'G';
> $g .= 'E';
> $g .= 'T';
> 
> Or the enigmatic:
> $g = '_TRG';
> $g = str_rot13($g);
> 
> Building the run-time intelligence into a compiler would be.... well do I 
> really need to spell that one out?
> 
> -Sara

---

## #14621 — Sara Golemon — 2005-02-04T00:31:25+00:00

URL: https://externals.io/message/14446#14621 · In reply to: #14619

> ok, i see. But why does it work with variables that are set as global, 
> e.g. the $HTTP_*_VARS:
>
> <?php
> function test() {
>     global $HTTP_GET_VARS;
>     $a = 'HTTP_GET_VARS';
>     var_dump($$a);
> }
> test();
> ?>
>
global $foo;
is the equivalent of:
$foo = &$GLOBALS['foo'];

So when you access $$a, you're getting 'HTTP_GET_VARS' from the LOCAL symbol 
table (where it exists as a reference of the global symtable version).

> this works inside a function. is it because of the global keyword? If so, 
> why can't there be a "magic" "global $_GET, $_POST, $_SESSION ..." set in 
> every function, for every superglobal, instead of the way it is handled 
> now? The thing i don't get is, why the superglobals behave differently 
> than "normal" global variables at all.
In a word: efficiency.

There's an expression: "Fast, Clean, Cheap: Pick Two".

The current implementation is Fast and Cheap, but as this thread has 
highlighted, it's not entirely clean.

> Ok, you have explained the technical reasons, but that's not what i mean. 
> For me as a php user (developing in php), the only difference should be, 
> that i don't have to use the global keyword. Everything else seems like a 
> bug or design flaw to me.
>
You won't hear a lot of argument from me.  I just care less that it is the 
way it is.

-Sara

---

## #14624 — Michael Virnstein — 2005-02-04T00:40:49+00:00

URL: https://externals.io/message/14446#14624 · In reply to: #14621

Sara Golemon wrote:
> You won't hear a lot of argument from me.  I just care less that it is the 
> way it is.
> 
> -Sara 

:p. Ok, probably there will be some redesign when PHP 6 is on it's way, 
perhaps not. :) Anyway, thanks for your time. ;)

Michael
