e-x-a.org » PHP is just cute

masklinn, 508 points, 4 years ago

(taken from reddit around Nov 2014)

OK, here’s the thing: this is only the entrance of the rabbit hole.

If you understand what this expression really does, you realize that you’re gazing upon the entrance to R’lyeh.

Do you think you don’t need your soul anymore? If you do, follow me into the lair of the Elder Gods. But be warned, you will die a lot inside.

The first thing to understand is what $ is. $ is actually a shorthand for ${} and means “return the value of the variable whose name is contained in this”.

That variable name is a string.

A bare word is a PHP string. Let that sink for a second, because we’ll come back to it later: $foo really is the concatenation of $ the variable-indirection character (think *foo) and foo which is a string. foo === “foo” in PHP, even though in raw source code you’ll probably get a warning. If those are enabled.

Now what does $$foo mean? Why ${${foo}}, or “give me the variable whose name is the value of the variable whose name is foo”.

Because we’re manipulating strings, we could also write ${${f.o.o}}, or

$t = o;
${${"f$t$t"}}

This also means that

class FooClass {
}
$thing = "bar";
$foo = new FooClass();
$foo->bar = "baz";
echo $foo->$thing;

is valid. And prints “baz”. And yes, $foo->$thing can be written ${foo}->${thing}. And you can recurse. The braces actually hold entirely arbitrary PHP expressions. As long as these expressions return strings, it’ll work:

class FooClass {
}
$foo = new FooClass();
$foo->bar = "qux";
$thing = "bar";
$qux = "th";
$grault = "ing";
echo $foo->${${$foo->bar}.${grault}}

For those following at home, this thing actually prints “qux”.

Then you can add conditionals:

class FooClass {
}
$foo = new FooClass();
$foo->bar = "qux";
$foo->wheee = "waldo";
$thing = "bar";
$qux = "th";
$grault = "ing";
$corge = "gnu";
$thgnu = "wheee";
$garply = true;
echo $foo->${${$foo->bar}.${$garply?grault:corge}}, "\n";
$garply = false;
echo $foo->${${$foo->bar}.${$garply?grault:corge}}, "\n";

What does that yield?

qux waldo

And if that’s too simple, then just make the condition random:

class FooClass {
}
$foo = new FooClass();
$foo->bar = "qux";
$thing = "bar";
$qux = "th";
$grault = "ing";
$corge = "gnu";
echo $foo->${${$foo->bar}.${(rand(0, 9)<5)?grault:''}}, "\n";

Yeah this will print qux half the time, and crash the other half. Want to add equality tests? knock yourself out: ($foo->${${$foo->bar}.((${pouet}.${machin}===$pouet.${machin})?${machin}:${$pouet.$machin})});.

And that’s where the second realization hits: you know how foo is just a string right? Then wouldn’t foo() be “a string with parens”?

Well it happens that no:

function foo() { return "foo"; }
echo "foo"();
$ php test.php
Parse error: syntax error, unexpected '(', expecting ',' or ';' in test.php on line 4

Unless you put the string in a variable itself:

function foo() { return "foo"; }
$bar = "foo";
echo $bar();

this will print foo. That’s actually what PHP’s own create_function does. And yes, I can see the dread in your eyes already.

Your fears are real.

The $bar here is the same it’s always been. You can also write it ${bar}…

function th() { return "Yep, it's working"; }
class FooClass {
}
$foo = new FooClass();
$foo->bar = "qux";
$thing = "bar";
$qux = "th";
$grault = "ing";
echo ${$foo->${${$foo->bar}.((${qux}.${grault}===$qux.${grault})?${grault}:${$qux.$grault})}}();

I always said sanity was overrated.

I’ll leave you with the finest example of this, the Cthulhu of PHP code:

class FooClass {
}
$foo = new FooClass();
$foo->bar = "qux";
$thing = "bar";
$qux = "th";
$grault = "ing";
function th($waldo){
    global $qux, $grault, $thing;
    return ${$qux.$grault}.$waldo;
}
echo ${($foo->${${$foo->bar}.((${qux}.${grault}===$qux.${grault})?${grault}:${$qux.$grault})})}(($foo->${${$foo->bar}.((${qux}.${grault}===$qux.${grault})?${grault}:${$qux.$grault})}));

Please pay special attention to the th function, it is the greatest thing since Chicxulub.