I know the title is harsh but it’s so true. At least it is according to nowadays php’s implementation of classes and objects, which do not permit to “reopen” and add or redefine new methods to an existing class definition, which is the basis upon Rails (and I presume lots more of applications and frameworks) is built.
This ruby feature managed to freak me out when I first read about it. Redefining an existing class? Who would think of it as a good idea? Where is the maintainability and the good practices of object oriented programming? How can we rely on some class behaving as expected if some piece of code is changing its internal behaviour?
But then I got the answer – when used within reason, this allows you to extend existing classes. Extend. Morph. Adapt. Improve. Refine. Lots of concepts which started jumping around me and made me understand all in a sudden how Rails plugins worked so seamlessly, without having to do any extra include, or without having to touch Rails very core files, or defining hooks at certain places. If you don’t like some aspect of Active Record, you could write a plugin which overwrites that behaviour – only that one. The rest remains the same.
The aesthetics is somehow shocking for php-only programmers. It’s something like this:
puts “I’m hello_world”
Any instance of A running hello_world will output “I’m hello_world”
But if you add this anywhere after the definition of A
puts “I’m hello_world v2″
and run hello_world again, you will get “I’m hello_world v2″. Which definitely is impossible to do in PHP – you would get a Fatal error: Cannot redeclare class error as soon as you tried to “reopen” a class and add or redefine some methods.
While this is not a problem for the average use of php, it turns to be the opposite when you want to do something smart with php5′s new and shiny objects model. Something like, for example, building a cool framework like Rails.
There have been some attempts already, like for example CakePHP or Symfony. I just have experience with CakePHP and while it is magnifically built (given that it maintains compatibility with php4 and php5) it will never reach the whole expressivity and power that Rails has.
A quick example is the AppModel class. AppModel is the CakePHP’s equivalent of ActiveRecord::base. In Rails, when you declare a model you just extend ActiveRecord. Simple as that; you don’t need extra stuff in the middle. In CakePHP you need to extend AppModel, and if you want to modify AppModel in your application, you have to write a new AppModel.php – which cake will load instead of the default, empty one. Yes, AppModel is an empty class whose sole purpose is extending Model (which has the real ActiveRecord-like methods). That doesn’t sound very flexible.
So at the end, AppModel is not more than a simple intermediate step for overcoming the limitation of php’s inability to redefine the underlying class, that is, Model. We need to add an extra class in the middle for each level that you anticipate the user of your classes will want to redefine or extend.
Meanwhile, in Rails you would just add some code in the plugins directory with the new functions for ActiveRecord, and your models would still extend ActiveRecord::base. No extra levels of hierarchy whatsoever.
Another interesting example is the famous acts_as_taggable plugin, which allows programmers to add the ability to tag items (i.e. ActiveRecord models) just by adding a single line of code to the model (acts_as_taggable), using ruby’s ability to reopen classes and add a series of new methods to existing models.
Even more, there are some Ruby core classes, like Date, to which Rails adds new methods, while they still belong to the Date object, not an artificial AppDate intermediate object in the middle, and without modifying Ruby’s core files. Isn’t it beatiful?
So that’s why I say that PHP is not flexible and will never have a real Rails-like framework :-)
Hope it’s clearer now…