
OK, here's the thing: this is only the entrance of the rabbit hole. - steveklabnik
http://www.reddit.com/r/programming/comments/dst56/today_i_learned_about_php_variable_variables/c12np38
======
michaelchisari
I started using PHP in the late 90's, back when it was pretty useful in the
sense of being able to embed code directly in web pages, so I liked it in a
very general sense. Then I started building larger apps, and I started to get
the kind of attitude about it that leads very many programmers to declare that
"PHP sucks" or "I hate PHP." Then, I started building even larger apps, and I
cursed PHP for every line of code I had to write.

Then, something happened. Yes, the language matured, but I also started using
application frameworks in PHP, which addressed many of the issues I had with
PHP, and then I built an MVC framework from scratch, based on what I'd
learned.

Now, I just don't care. I'm not going to defend PHP as a _good_ language, not
even it's creator will do that, but when people go on about how terrible PHP
is, my first thought is "well, then use it more, and eventually you'll stop
caring."

I stick with PHP, specifically for open source, because of it's install
density, because it is extremely flexible, and because I know all it's weird
quirks and inconsistencies so well that they no longer bother me. I wouldn't
mind switching to a more structured and consistent language if it came up, but
I don't see PHP as a limitation for anything I want to accomplish, and haven't
for a very long time.

And anyways, terrible code is terrible code, and when I think about having to
delve into projects that are spaghetti, or procedural, or have no separation
of concerns, I don't blame the language for my frustration.

~~~
treeface
I'll even go so far as to say that coding in PHP is downright pleasant when
you use a framework like CodeIgniter or Kohana.

At the same time, I have to use Joomla at work and I sometimes catch myself
with a partially-tied-off noose around my neck.

It really comes down to what framework you choose.

~~~
gaius
Any problem in computer science can be solved by adding a layer of
abstraction, apart from the problem of too many layers of abstraction.

~~~
joshu
That would be solved if only we had a framework framework.

~~~
Ygor
Your comment reminded me of an older comment here:

[http://discuss.joelonsoftware.com/default.asp?joel.3.219431....](http://discuss.joelonsoftware.com/default.asp?joel.3.219431.12)

------
alanh
Here on HN everyone seems to like a good story (despite that maxim about
anecdotes and data).

This year, I had to maintain legacy PHP code powering a custom calendar &
training-session signup app. Notable _horrors_ I encountered:

\- Despite being written by a programmer in the employ of a major Californian
company _providing security solutions to corporate customers_ , all passwords
were stored in plaintext in the database. The database itself was password-
protected by a guessable variation on the company’s name (seriously).

\- The ancient register_globals directive was on — not just _on_ , but _relied
upon_. As in, when I moved the code to a server with the directive off (e.g.
any modern hosts), all the forms broke. For those who haven’t heard of this,
er, “feature,” it means that any variable in the request, be it a cookie name,
a GET parameter, or a POST key (that is, the name of an input field on a web
form, typically) _automatically becomes a global variable in the PHP script_
set to the provided value. So I could manipulate my DOM, add an <input> field
named, say, "is_admin", set its contents to "1", and voilà! $is_admin is now
truthy.

\- Nothing was ever properly escaped. Not the database, not email headers, not
page output.

\- And of course, code and HTML were strewn together willy-nilly, with lots of
repetition and insane spaghetti-code execution. Let’s just say it was _non-
trivial_ to skin all the pages in the app.

I know and you know PHP doesn’t _force_ anyone to commit these sins. But for
backwards-compatibility reasons, it still permits them… and I find it hard to
believe absolute stinking dangerous messes like this are more common in other
modern platforms.

~~~
ars
Despite you saying "backward compatibility", the only one of those things that
has anything whatsoever to do with php is register globals. All the other ones
are programmer choice, and have nothing to do with "backward compatibility".
But it does sound like quite a horror story.

~~~
bad_user
When using Ruby, you're using Ruby on Rails 95% of the time ... which means
database access is most often than not properly escaped (it's the API's
default, and according to the path of least resistance, people rarely do
string concatenation for sql queries).

When using Python's Django (mostly a default nowadays) all string output in
templates is escaped by default (you've got to make it explicit if you don't
want it). Also in Django all forms submitted are protected against csrf
attacks by default.

In no case above are you allowed to do "; drop table users", since most ORM's
that were made by people with brains can detect that you want to do a DDL when
a SELECT is expected. You've got to use the raw Python DB API for that, and
that's not easier than just going along with what the framework gives you.

E.g. Django 1.2 added raw queries, but this will trigger an error since it is
not a plain SELECT:

    
    
        Users.objects.raw("DELETE FROM users")
    

Without a framework like Django, you can't really do web development in Python
without blowing your brains out.

Every other popular web platform isn't like PHP which is a language
specifically made with clear hooks for processing requests / spitting out HTML
(right there, in the language).

Heck, you can't even write a PHP script that doesn't contain a "<?php" tag.

I'm not saying that bad code isn't universal, but PHP makes it a whole lot
easier to do stupid things and not in a Lisp-esque way.

------
Eliezer
Call me loony but I see nothing surprising here. It's just indirection... I
guess I've probably got some Elder Thing blood in me.

~~~
jbm
For what it's worth, I used variable variables when making a DB class for a
project I was working on last year.

It isn't something you can easily debug and it is complicated. That said, it
isn't the gates to hell that it is represented as. It's a tool, and it can
make code cleaner if you want it to.

function __tostring(){ $items = ['title','header','comment'] foreach($items as
$item){ echo $this->$item; } }

Make that list of class variables much longer and you start to see the
benefits in certain cases.

There are times when I do miss variable variables.

~~~
bad_user
Python has it in a more object-oriented way ...

    
    
        def print_string(self):
            items = ['title', 'header', 'comment']
            for item in items:
                print getattr(self, item)
    

Where "getattr" is just a pretty protocol for calling ...

    
    
        self.__dict__[item]
    

This extends to whatever you want, including members of namespaces (like
declared classes, functions, other namespaces, etc...)

~~~
hagy
There is actually a little more to getattr in that __dict__ just has
attributes local to that instance where as getattr (also instance.attribute
syntax) also accesses class attributes, non-bound methods, and properties.
Additionally, these non-local attributes can also be inherited from parent
classes.

------
noahlt
Can anyone explain why this is so terrible? To me, it seems like Lisp, but
backwards: in Lisp, you have symbols, which normally evaluate to their values
unless they are quoted. In PHP, you have strings, which normally evaluate to
themselves unless they are 'unquoted' with $.

~~~
spoondan
The feature isn't bad, but there are several warts. The '$' is not a prefix
operator on strings. The following is a syntax error:

    
    
        $"test";
    

It turns out that '$' can only operate on an identifier or another '$'
expression. The '${...}' syntax, aside from being more readable and less
likely to be accidentally encountered, is more like an operator:

    
    
        ${"test"}; // equivalent to $test
    

The decision to keep (and only warn against) unescaped strings past the
introduction of an incompatible feature (constants) is inexcusable. It creates
oddities:

    
    
        $test = 'foo';
        $foo = 42;
        echo $test . " ${test} " . ${test} . " " . $$test . "\n";
    

This generates a notice and prints, "foo foo foo 42". But then:

    
    
        $test = 'foo';
        $foo = 42;
        define('test', 'foo');
        echo $test . " ${test} " . ${test} . " " . $$test . "\n";
    

This will print, "foo foo 42 42". (Note also the difference between the
meaning of ${...} within string interpolation versus outside of it.) Is this
all understandable? Sure. Is it good? No.

~~~
wvenable
> The decision to keep (and only warn against) unescaped strings past the
> introduction of an incompatible feature (constants) is inexcusable.

It's so easy to turn those warnings into exceptions. I don't think I've seen
any project in the wild that does anything meaningful with unescaped strings.

------
mquander
Shrug. Seems pretty well in the spirit of PHP; lots of flexibility with almost
no structure.

~~~
folz
I agree. While I see absolutely no reason to justify using this in production,
it makes sense syntactically.

~~~
konad
$this->$something

------
julius_geezer
Tcl has always allowed one to set the variable 'b' to 'c' with

set a b

set $a c

And while it took some getting used to, one could do it in a disciplined
fashion, with a proc, and so create/set only certain variables in a calling
proc. One did not have to spatter the whole scope with who knew what variables
when pulling stuff out of an associative array.

~~~
bradleyland
Many languages support "variable variables", but not as directly as Tcl and
PHP. Any language that has the ability to evaluate strings as code can do it.

    
    
      # Ruby
      a = "some text"
      b = "a"
      eval b # => "some text"
    
      # Python
      a = "some text"
      b = "a"
      eval(b) # => "some text"
    
      # Javascript
      a = "some text";
      b = "a";
      eval(b) // => "some text"
    

I've found the the more "quick and dirty" a language is, the simpler it is to
use variable variables. And I don't mean quick and dirty as an insult. Bash
supports this, and while I consider it quick and dirty, you can pry bash from
my cold dead hands.

    
    
      # Bash
      a="some text"
      b="a"
      echo ${!b} # => "some text"
    

Having said all that, using variable variables is a great way to piss off
whoever has to maintain your code, so use them sparingly, and only when
appropriate. There is little or no opportunity for context with variable
variables. To add insult to injury, variable variables can result in gaping
security holes as well. Think of the implications were an attacker able to
substitute the value of the variable variable in any situation where the
output is sent to the client. The attacker could randomly guess at variable
names, dumping all manner of information. Anything in the global scope. Yikes!

There are far better ways to do this in modern languages. For example, use a
hash or dictionary construct. A Ruby example:

    
    
      # Ruby
      myhash = { :a => "some text" }
      b = "a"
      myhash[b.to_sym] # => "some text"
    

A Python example using a dict:

    
    
      # Python
      mydict = { 'a' : "some text" }
      b = "a"
      mydict[b] # => "some text"
    

A hash or dictionary gives you the opportunity to give your collection a
meaningful name, scopes the evaluation to that object (so you can't
arbitrarily retrieve variable data), and provides context.

~~~
invisible
And make sure that register_globals is off for all that is holy.

------
invisible
I hate reading reddit comments on things like these (because, really, PHP is
not insane, crazy, ass-backwards, etc. nor are the contributors lazy or
homicidal). There are some good comments, yes, but you can spend a year making
fun of ANY language if you want it hard enough.

------
catshirt
I'm pretty sure the point of this post isn't to share nested variables with
you guys (note: respective post titles), I think it's just for the terrifying
function created towards the bottom of the highlighted response (and it's
explanation).

Right, we knew about nested variables, but if the derivative of that function
doesn't make you lol as you're reading along... well, then, I guess don't vote
up.

~~~
steveklabnik
You're correct, at least with why I posted this here. Metaprogramming is
awesome, but I had no idea that PHP worked this way... it's simultaneously
awesome and horrifying.

------
dools
I don't see why this went on so long. so you can have variable variables, and
by using curly braces you can do inline evaluation of a expression.

It's no different from just evaluating any expression, no matter how complex,
that returns a string, storing it in a variable then sticking a $ sign in
front of it.

This is not a rabbit hole. It's just a bucket someone threw up into and then
posted onto reddit.

------
bkrausz
I'm going to try to preempt any "PHP sucks" comments with my Quora response:

[http://www.quora.com/What-are-PHP-s-main-flaws-(and-good-
par...](http://www.quora.com/What-are-PHP-s-main-flaws-\(and-good-
parts\)/answer/Brian-Krausz)

~~~
IgorPartola
IMHO, PHP's main flaws are not in the types of features discussed by OP. It's
mostly an issue of naming and namespacing. Another thing that is kind of wrong
about it is the URL => file semantics (when used as a web framework). Other
frameworks do this better which results in clean URL's and more structured
codebases.

Amongst other things that PHP gets right is the date/time handling (two
functions to go between datetimes and UNIX timestamps, support for timezones).

~~~
invisible
PHP is a language (just like Ruby and Python are to Rails and Django).

Here are some PHP frameworks (that handle URL => resource instead of URL =>
file): <http://www.phpframeworks.com/>

~~~
IgorPartola
Yes I am aware, but I see PHP as somewhere in between a general purpose
scripting language and a web framework. It does have features that are very
specific to web development: parsing of query strings and post data and
cookies, built-in headers function, templating. That last one is funny since
that's where the language came from: a DSL for putting some dynamic content
into HTML. Nowadays people consider it clunky so things like Smarty were built
in PHP to handle templating better.

------
codexon
Python equivalent: locals()[somevar]

------
InclinedPlane
Languages that don't provide the flexibility to easily build enough layers of
indirection to screw you are generally limited in ways that require heap tons
of boilerplate garbage code that screw you more subtly.

~~~
apl
Not necessarily, though. I think Python, for instance, strikes a fantastic
balance.

~~~
InclinedPlane
The best languages are those that are both powerful as well as finely tuned to
lead users to the "pit of success" (i.e. the most natural use of the language
tends to be the best and most useful). Some languages are powerful and
difficult to use well (assembly), others work well in a narrow niche but are
otherwise limited (VB6), while the cream are both powerful and highly usable
(Python probably being the best example, modern C# perhaps as well), you can
cut your fingers off quite easily if you try but the language semantics and
conventions make it obvious that you will cut your fingers off.

------
limmeau
I liked the ecclectic choice of metasyntactic variable names: garply, grault,
corge, and pouet.

------
andjones
Give the Programmer as Much Control as Possible.

See: <http://paulgraham.com/langdes.html>

Let the programmer shoot himself (or herself) in the foot. I do not believe a
language's job is to hold a programmer's hand. A programmer will write good
code or bad code. A language cannot change that.

Variable variables allow for easy class reflection. I'm curious to hear other
uses of variable variables.

~~~
apl
In a perfect world, inhabited by fantastic engineers and no one else, aye. So
Graham's slogan may hold true for LISP hackers, or possibly even the dynamic
languages crowd (Perl, Python, Ruby and their ilk).

PHP, however, is primarily used by either incompetent or inexperienced or
deadline-driven people. Under such circumstances you want a language that
protects your foot with six layers of hardened steel. If Python was the
prevalent language for these groups, I suspect we'd see a lot less security
holes and general terribleness. Alas, PHP dominates that market, and people
shoot themselves in the foot all the damn time.

To cut a long story short: Give an experienced engineer a powerful, flexible
language such as modern PHP, and he will make good use of it. Give a 15-year-
old dude in his bedroom the very same language, and hilarity ensues.

~~~
eru
And flexibility does not have to mean suckage. Lots of warts in PHP don't help
with flexibility at all.

------
jjguy
can we recruit that cat from reddit to HN? I want that guy around here.

~~~
stevelosh
<http://news.ycombinator.com/user?id=masklinn>

~~~
Osiris
Yeah, in fact as soon as I read "I just learned about variable variables
yesterday" at the top of the post, I immediately thought about the article
that I read yesterday right here on HN that specifically mentioned variable
variables. It sounds probable that's where he found it.

------
lanstein
The real problems with PHP crop up when you get to the margins, like when
you're trying to tune various routines, particular wrt memory usage.
Sometimes, it's more efficient to take arguments as (&$foo), and sometimes,
it's not. This is why I've written (PHP 5.2.x no less) abortions like:

    
    
        public function catalogue($str, &$str2, &$str3) {
    

depending on whether the parameter is a frequently referenced string, and the
type of the parameter altogether. It seems like it's completely trial and
error with these sorts of things, and when you note that it takes (iirc) ~70MB
to store a million one-char elements in an array, it really matters.

Also, sometimes, PHP will patch through returning results of a function, but
sometimes, it's actually cheaper to create a temporary, a la

    
    
      $str = strtolower($foo);
      return $str;
    

These are just a couple of gripes off the top of my head.

~~~
wvenable
PHP has copy-on-write semantics for strings and arrays -- your sort of
reference optimizations seem a little strange to me. That is, unless you're
not intending to making copies of structures when you change them inside of
functions, in that case using references is not a strange optimization but
just the correct choice.

~~~
lanstein
I know PHP uses COW, so theoretically, reading from a variable should have no
impact on memory usage whatsoever. This has not proven to be the case, I've
called get_peak_memory_usage() and played with passing the params explicitly
by reference or not. I assume it has something to do with the fact that
strings/arrays are not passed by reference, and explicitly declaring the
reference is cheaper sometimes and more expensive sometimes depending on
refcount(maybe?)

~~~
wvenable
I'm not entirely sure how much I'd trust get_peak_memory_usage() for this kind
of debugging. I'd be more inclined to profile this with xdebug before making
these kinds of code changes.

------
eogas
HN, you are one smug bunch. I, for one, thought the comment was great.

~~~
jbm
I think you're misunderstanding the reaction. It _was_ a funny/cool comment.
Definitely worth being here.

The thread on reddit, however, indicated that many people were ignorant in
regards to viable use cases for variable variables.

I code almost exclusively in Python/Django nowadays but I can honestly think
of situations where I wish I had a simple ability to do functions like
"$a->$b()" to clean up a piece of code.

~~~
lanstein
Not to nitpick, but that's a variable function call, not a variable variable.

------
grandalf
The one thing I dislike about how a lot of php code is written is the way that
requires are used as function calls (but without any declaration of what
variables the required code is expecting to find).

Oddly, Rails partials work this way too. They can see the controller's
instance variables and are often written without any sort of defined
interface, which makes for confusing, brittle code. Fortunately Rails offers
the option to pass in :locals (just as php offers the option of simply using
functions instead of require).

~~~
jasonlotito
Wait, what? A lot of PHP code use requires as functions? Honestly surprised
here. I've never seen this.

~~~
grandalf
sugarcrm, phpbb, and others do it. It's very ugly. I have not seen either
codebase in several years though so it may have changed.

------
code_duck
I've never seen anything accomplished with 'variable variables' that could not
be done more cleanly with an associative array.

------
robryan
For whatever anyone says about variable variables I would have loved to have
had them when using VB.NET. Turned out the only way you can replicate the
functionality is to have a whole heap of reflection code.

------
demallien
Am I missing something? Isn't this just a rather awkward 'eval'?

------
fleitz
$$var is awesome, it makes it really easy to put function pointers in a db
record. Was using this back in 2002.

------
IMBild
Thats wired!

