Wednesday, August 26, 2009

PHP Is a Husk

I thought there was hope. I really did. Maybe it was naivete, or simply denial because it's the language I use day-in and day-out. But there is no hope for PHP.

No, I don't think the project will die; I'm sure it will continue to grow. It's no new revelation that the language has a lot of problems. Even when we finally got prepared statements with the MySQLi interface, there were a lot of problems with the way it worked such that writing a thorough DBAL for it was nearly impossible. But lately I've been pretty positive about the direction PHP has been going--with 5.3 came namespaces, lambdas and closures, among other things.

And then I started playing around with these new features. The namespace delimiter is odd, but big deal. Lambdas and closures, though, are broken and useless for anything but the most basic applications.

For instance, I've been messing around a little with Perl and an awesome OO extension for it called Moose. Moose has a ton of features, but one particular feature is Roles. Roles, as defined by the Moose devs, are somewhere between abstract classes and mixins, with multiple inheritance--but more flexible and easier to develop with. You know, pure awesome.

So I set out to try my hand at making something similar to Roles with PHP's new lambdas and closures. That should give me all the power I need to hack something together, right? This is the best I could come up with:


// class used to enable roles for other classes
class Role {
// used by the class the role is applied to
// must be in that class's __call() method
static public function call($obj,$funcName,$args) {
$func = $obj->$funcName;
array_unshift($args,$obj);
call_user_func_array($func,$args);
}

// applies a role to the object.
// for best results, use in the constructor.
static public function apply($obj,$name) {
$mixin = new $name();
foreach ($mixin as $funcName=>$func) {
$obj->$funcName = $func;
}
}
}

// my example role that I want to apply to other classes
class MyRole {
public $yar = 'role\'s test string';

public function __construct() {

// prints a string, including a var from the object
// that is using this role.
$this->roleFunction = function($obj) {
print('This is a role function! '.$obj->blah."\n");
};

// prints the object that is using this role.
// this will also show the methods it has received
// from the role.
$this->printr = function($obj) {
print_r($obj);
};

// a function that gets overridden by Test class's
// native method by the same name.
$this->overriddenFunction = function($obj) {
print("You'll never see this.\n");
};
}
}

// my class that I want to apply a role to. Note the
// call to Role::apply() in the constructor.
class Test {
public $blah = 'test string';

public function __construct() {
Role::apply($this,'MyRole');
}

// overrides MyRole's version, as it should
public function overriddenFunction() {
print("I beat the role's method!\n");
}

// necessary mess to get $object->roleMethod() to work
public function __call($funcName,$args) {
Role::call($this,$funcName,$args);
}
}

$test = new Test();
$test->printr();


The above will print_r the Test object, as it should, including the public attributes that hold the lambdas from the Role. Neat, right?

Well, not really. This is so messy, so impractical. The limitations are ridiculous. For example, the following works:


$someFunc = function() { print('Hello, blog!'); }
$someFunc();


But this doesn't:


$obj = new StdClass();
$obj->someFunc = function() { print('Hello, blog!'); }
$obj->someFunc();


Whoops! That object method doesn't exist! And what about this:


class MyNeatoClass {
public $someVar = 'Hello, blog!';

public function __construct() {
$this->someMethod = function() { print($this->someVar); }
$this->otherMethod = function() use ($this) { print($this->someVar); }
}
}


Neither the lambda nor the closure works. $this cannot be used in lamdas or closures. So, when it comes to using these new features with objects, they're quite nearly completely useless. These are just a couple examples of what makes my above attempt at creating Roles broken and useless.

And as I mentioned before, this isn't the first time the PHP devs have botched up new features. They seem to be committed to adding "big boy" features, which should be a good thing, but not to building them correctly. This leaves developers--who actually know what these features are and how to use them in other languages--frustrated and disgusted. For what audience are they coding? Do they think Grandpa Jo's 14-year-old nephew, who "builds websites", is going to use closures? No, real developers are going to try to use them, and then give up, because they're useless.

This trend is obviously going to continue. PHP had the opportunity to recover from past mistakes with recent new features. They have not learned a thing from past mistakes. PHP, you are dead to me.

14 comments:

Anonymous said...

True dat! Use python, or ruby.
If you can. Otherwise suck it all up and stick with big old php.
IT SUCKS ASSSSSSSSSSSS!!!!!! so much..
Yeah right php-6 will kick ass.
Will it be able to do this finally ?
$element_from_array = my_fun()[1];
or
$element_from_array = (my_fun())[1];
this is biggest LOL like ever!
No luck with PHP5...

marilyn c. cole said...

This is interesting. I haven't used PHP much, but I'm applying for a job right now that will use it. Got any recommendations for cool books or websites that helped you learn?

Unknown said...

PHP will always exist. I hate to acknowledge though, that, my first programming experience was with PHP.

I guess more hardcore programmers from the 80's say that about Basic too though.

Common Lisp is awesome, Erlang is awesome, Python is awesome. But for some odd reason none of those languages have a robust enough collection of libraries to even come close to matching that of PHP's.

To boot, every freaking host under the sun supports the LAMP stack... Luckily I don't do client work any more so I don't worry about it.

Lucas Oman said...

Marilyn, I'm sorry! Kidding. But not really.

PHP's online documentation is probably one of the nicest features of the community. It's an excellent resource and will guide a seasoned programmer to whatever she needs.

Also, because it's a script language, there is an immeasurable amount of source code out there, so learning by example is easy. Don't look at the Wordpress source. It'll teach you all the things you shouldn't do with PHP, like evaluating user input as code. You know, the things that compromise your entire server.

Travis said...

Marilyn,

My advice is to get involved (using / developing / testing) a framework as soon as you're comfy with PHP syntax. It's life changing. The big ones are Zend (lots of modules, can do things like interact with Lucene super easily), Symfony (seems the most enterprisey), CodeIgnitor (the hot one of the year), or CakePHP (provides the most RPT capabilities).

To the author: I'm a CS degree owning PHP dev that really wants to learn another language. Seems to be python or ruby. Care to comment between the two?

Lucas Oman said...

Travis,

I absolutely love Ruby. I've not done anything with Python. From what I understand, they are both very similar.

Ruby's community does not seem to be flourishing as much as Python's. Also, with Google's recent stamp of approval on Python, I'd say that language is more of a sure-thing than Ruby.

sli said...

@Travis:
As a long time Python developer, my vote is (obviously) going to Python. But when it comes down to deciding between Python or Ruby, it's pretty much only personal preference at this point. Both have rich libraries and a lot of handy third-party packages, both have thriving communities, and both are powerful.

The real differences come mainly from how the syntax looks. Ruby doesn't force indentation like Python, but the forced indentation is something I've come to love. It makes your Python code readable by any decent Python programmer. Again, personal preference.

Something to help decide would be an audit of what you plan to do with the language. Myself, I prefer Django over Ruby on Rails because I like how Django works. It's very familiar. Again, this is just personal preference.

But since I'm a programmer, not a designer, I use Drupal for my web apps these days. :P

marilyn c. cole said...

Thanks for the tips, Lucas and Travis.

I'll agree with Lucas and sli that python and ruby have a lot of commonalities. Like sli, I also love python's forced indentation. Rails is pretty amazing for quick implementation of interactive web apps, though, and pretty popular to boot. I think I like python better if I'm not using Rails -- it just feels cleaner and clearer to me -- though I haven't tried Django.

Anonymous said...

ahh yes, throw away the entire language b/c one new feature isn't as fully implemented as you like..

thats reasonable. oops did you drop your pacifier again.

every language has its limitations and quirks PHP is neither exceptional or the exception to this.

Lucas Oman said...

Anonymous,

I cite a string of botched features that show a complete lack of concern or pride in the project. How can that bode well for the PHP community?

Sure the features don't work as they should, but what's disturbing is that it shows what sort of attitude the PHP core devs have. THAT is why PHP is going to do nothing but decline.

I tried using my son's pacifier, but it brought me no comfort. Any other suggestions?

perigrin said...

Lucas,

Awesome that you enjoy Roles so much. You should also check out some of the advanced delegation and introspection that Moose provides too specifically things like MooseX::AttributeHelpers (which will shortly be replaced by a similar feature in Moose core). This is in my opinion where Moose shines. It provides not only a nice sugar layer for automating away routine stuff, but a powerful introspection layer based on decades old technologies first explored in CLOS and SmallTalk.

It might help to look at another implementation of Roles when looking at how to implement a similar feature in PHP. Joose is a port of Moose's sugar and introspection to Javascript. It might be worth looking at how they implemented Roles to give a better clue where things might or might not be going wrong (Sorry I don't know PHP beyond a very basic level so I can't really help better than that).

Anonymous said...

Very mediocre post. Including the comments.

"Use php, python, no, use ruby instead! Facebook uses PHP, YouTube Python, blah blah".

Kids, kids, behave! I'm a Java programmer, 28 years old, I develop enterprise applications, and I think scripting languages are for kids and web developers who don't know anything about programming and software engineering.

I've worked with Perl and Python developers for the last couple of years... they struggle to understand design patterns and don't even know what a domain object is. Come one people!

Lucas Oman said...

Anonymous,

In my experience, Java programmers like to look down on people who don't use Java because they don't follow the rules that Java programmers have invented for themselves.

As someone who studied Computer Science at Rice U., I'm quite confident that script languages can pull off software just as involved, complex, and well-designed as any compiled language. Sure, there may be a performance hit, but to think that there's a difference between your code and my code just because yours ends up as a binary (oh wait, not really), well, that's just ignorant.

Understanding the term "domain object" is as simple as performing a Google search. I hardly think something so superficial is a valid test of programming ability. Anyone can read a dictionary.

Sean Huber said...

You should check out http://github.com/huberry/phuby - Ruby style mixins, proc objects with dynamic binding (including $this), and other rubyisms.