Hacker News new | past | comments | ask | show | jobs | submit login
OK, here's the thing: this is only the entrance of the rabbit hole. (reddit.com)
264 points by steveklabnik on Oct 19, 2010 | hide | past | web | favorite | 86 comments

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.

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.

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

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

Your comment reminded me of an older comment here:


That's just silly. You want a generic framework factory factory, that you can then use to create a framework framework factory. And then you can get your framework framework.

See "panels" in drupal.

They do in Java. It's called FrameworkFramework.

Unfortunately you have to get them from a FactoryFactory.

...and apart from the well-known two hard problems.

You mean cache invalidation, naming things, and off-by-one errors?

If you don't like naming variables, use Fourth. (You'll still have to name functions, though.)

Against off-by-one errors, abstain from any from of loop.

For the real hard problems, like deciding whether your program has crashed or just takes a long time, give up.

Naming things was even harder when filenames were restricted to five characters or less.

That's why they had to name it Forth.

As a Kohana user, I can corroborate the claim that Kohana-land is miles above the world of complete garbage PHP, but the community, the libraries, the robustness of the framework itself, and support from hosting providers, etc., is still far behind (say) Rails in every case.

Touching on community, that would probably be the biggest motivator for me to move towards rails in the future, well over anything the individual languages offer.

Don't discount PHP yet; the CodeIgniter community is great (http://codeigniter.com/forums/)

I use CodeIgniter, have done for about 4 years now.

I never even think about high level stuff like "what language should I code in" anymore. It doesn't matter to the end user. CodeIgniter allows me to do anything I want, elegantly and with a small footprint.

It's a means to an end and eventually like you said, you stop caring about the "means". It's the "end" that matters - i.e. how well you execute. Using RoR (for example) as opposed to PHP has zero impact on that.

Yeah, you could argue that another language might get code down quicker or more elegantly but to the same "end". (Experience largely negates the quicker bit)

You might also argue that at scale PHP may eventually come back to bite you, but your not at scale at the moment and the majority of us should be thinking about the best way for us to deliver v1, not the best way for us to deal with 1 million users.

Completely agree - looking back at some of the code I wrote in PHP3 there were huge issues with the language but it was more that I had no idea what I was doing than an inherent problem in the language (granted, PHP5 is lightyears ahead of PHP3 and I'm certain that the improvements have made writing good code easier). Today, it has all the tools that if you learn better general coding practices (like MVC or at the very least separation of presentation from logic) it doesn't get in the way and you can just do what you want. I actually think the $$ syntax can be useful, but like anything it matters how you are using it.

I remember when "structured programming" was the new silver bullet all the guru's were talking about. This must have been the late 70's or early 80's. I was programming in DEC Basic-Plus on the RSTS/E operating system on a PDP-11. All variables were global, as I recall. We had subroutines and user defined functions with arguments but no local variable scope. We had if-then-else, but I think only a single statement was allowed for the "then" branch, so the cleanest way was often "if condition then gosub 1000 else gosub 2000" or something like that. A lot of the legacy code I had to work with was classic spaghetti-code with goto for flow control. Oh, I think we had only single-character variable names. Those were the days.

But I worked out how to adopt the new structured style to the limitations of the language, and learned a lot. Better languages make better tools, of course, but it's the hacker's attitude that really matters.

PHP seems to have a culture of poor abstraction / old libraries, which I think is largely responsible for the hatred. The language itself is... a language. There are better-for-X and worse-for-X, but languages are fairly inconsequential compared to what's already coded in the language (standard libraries, popular frameworks, your application, etc), which can be any degree of good or bad regardless of the language.

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.

Sound like pretty much every single inhouse developed or extended app after a few years/personnel replacement cycles. Yeah it sucks to work on. Apart from the register globals, it says nothing about PHP though.

Actually while I won't defend register globals in 2010, back in 1998 (am yes, I wrote PHP code in 1998) when nobody understood web development really (do we even today?) it was a handy feature, that prevented much line noise on pages that were (back then) usually composed of html with a few <? ?> tags and code in between, pretty much the way javascript was on pages until a few years ago. It was only as the php became heavier and was moved in to common include files and later into libraries that the usefulness went away (but the potential issues remained).

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.

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.

PHP's "magic quotes" feature was enabled by default for many years. This practically guaranteed issues with escaped database strings. (Unless the developer spent more time debugging that it would have taken to do it properly in the first place.)

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.

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.

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

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

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.

I laughed at TFA, largely because it's clever and lucidly written, and cringed at the thought of what sorts of horror an unskilled operator could inflict with this on the poor dumb bastards charged with maintenance; but I'd rather my language err on the side of lunacy.

The loony thing is that the indirection is by name, not by reference, which allows the indirection to be a lot less predictable. Compared to pointers, it's like the data structure equivalent of using dynamic scope instead of lexical scope.

Indeed, call me when you're using complicated string-returning lambda functions inside the brackets and maybe then I'll--well, probably just redirect you to an obfuscated PHP contest, but cheers.

It's not even really indirection. Variable variables using strings is a useful feature for quickly getting functionality up and running (although there are usually security trade-offs, especially if you accept user input as the string to be evaluated).

Edited for clarity

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

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

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.

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

I suspect it has more to do with the reputation (underserved or not) of the typical PHP programmer. This construct offers some powerful meta-programming capabilities when properly used, but in the hands of unskilled practitioners, could be turned about to write unholy and perverted softwares.

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

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


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.

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

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

In Tcl, $ is shorthand for [set foo], so that $foo == [set foo]. You can use this to do 'double indirection' if you really need to:

    set foo bar
    set bar 5
    puts [set [set foo]]
OTOH, $$foo does not work in Tcl.

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.

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.

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.

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.

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


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

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/

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.

Any of you guys use CakePHP? I've inherited a project that uses it. It seems ok, a bit like Rails...

Naming, oh god yes, the naming. Nothing is consistent with naming functions at all. My favorite example of this is the aptly named "parse_str" function:


Ah that one is my favorite. The second parameter is optional, but if you don't pass it in it's a noop. Don't really get where that came from.

Ohohoho. No... if you omit the second parameter, PHP sets variables from the query string in your local scope. Surprise! It's register_globals all over again.

Wow. That's so fantastic I can't even describe it. I can't imagine how much buggy software there is out there because of this.

If these are your main complaints with PHP, you need to look into CodeIgniter.

CodeIgniter as a request dispatcher is OK, but as a framework it's not very clean. Single instance of every library + methods that mutate the global instance (see the bundled mail library)? No way to trap database errors (unique constraint violation,etc.) and if debug is enabled it simply die()s?

You only have to look at CodeIgniter if you plan to be stuck with PHP 4.

Python equivalent: locals()[somevar]

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.

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

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.

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

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.

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.

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

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

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.

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.

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.

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?)

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.

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

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.

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

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

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

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.

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

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.

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

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

Thats wired!

Registration is open for Startup School 2019. Classes start July 22nd.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact