I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 636 posts at DZone. You can read more from them at their website. View Full User Profile

PHP 2.x frameworks and Ruby on Rails

05.06.2010
| 10337 views |
  • submit to reddit

Developers that choose dynamic languages as their tool for building websites and web applications are often divided between two different stacks: the Apache and PHP duo, and the Ruby on Rails platform (although this is a simplification of the big landscape. Paul Graham uses Lisp.) In the last years both these environments have evolved very much, also thanks to the on-going competition. Let's try to take a look at these two technologies from a different point of view of the usual one, which is mostly X has this feature that lets you set array values faster than Y! Beautiful!

It has been said ad nauseam that comparing PHP and Ruby on Rails is unfair as one is a programming language and the other is a full-stack framework. In spite of this, we can compare PHP frameworks (which have learned a lot from their first releases) and Ruby on Rails, again not by piling up a list of features, which would be also cluttered by different extensions and plugins. I'm talking about the different approaches taken by PHP and Ruby framework developers.

Choice

First of all, you can't talk about Ruby frameworks: you should talk about Ruby on Rails. The main Rails alternative was Merb, an alternative stack (better architected I'll say) than Ruby on Rails, which introduced a lot of new concepts in Ruby platforms. For example, being ORM-agnostic and do not assume that every single application should employ the Active Record pattern.

The result was that Merb got merged in Rails 3, now in beta:

 On December 23rd, we decided to end the duplication and the paradox of choice. -- http://rubyonrails.org/merb

It seems that for the Rails world being able to choose between two different solutions is very bad (I'm starting to sound like Terry Chay.) I agree that dividing the efforts of developer between two (or more) different frameworks is not so clever, but there are also other choices than totally incorporating other projects. Different projects mean possibly different directions, which let the developers experiment with new paradigms and use cases (see OSGi in Java with some implementations used for embedded applications, others for IDEs, and so on).

The other publicly available Ruby frameworks are all trying to be lightweight (less than 4K) or are making an arms race to find out who can let you write an hello world application with the shortest command. The exception is Sinatra, but it is not an MVC framework.

The PHP world has taken a different approach to manage the multitude of frameworks that have appeared in response to the raise of Rails (no pun intended): interoperability. Standards for using components from multiple frameworks are emerging. Symfony 2 will use components from Zend Framework, while Zend Framework 2 will integrate Doctrine (2), the standard object-relational mapper included in Symfony.

With the various "2.0" versions of frameworks, most projects are learning from both mistakes made as well as from the usage patters of the developers adopting them. One of those lessons, to my mind, is that no one framework can do it all well and by themselves. I fully expect to see the next generation of frameworks making it trivial to pull features from other frameworks and libraries in order to fill out functionality.  -- http://weierophinney.net/matthew/archives/232-Symfony-Live-2010.html

Or we can merge everything together (and God knows how many frameworks are out there. You can guess I'm primarily a PHP guy.) It's the same old plea for orthogonality: independent components that you can swap to form a gazillion of different combinations. I can use Zend with Doctrine 1, or Zend with Doctrine 2, or Symfony with Doctrine 2, or plain old PHP scripts with Doctrine 2, or Symfony with simple PDO, and so on.

Magic

It's no mistery that Ruby on Rails makes a point of convention over configuration. Hopefully, everything is decided by convention unless you specify differently: table names from class names, object fields from table columns, and so on.

A bit of automation is not bad while setting up a framework, although I prefer to generate a common setup that I can customize later. However, I often thought that Rails takes this too far. When I generated my first project in Ruby on Rails, I opened up my classic Post class (the same old create-your-forum-with-two-commands example), and it was like this:

class Post < ActiveRecord::Base
end

Oh thanks, now I'm enlightened. No fields, no scope, no getters or setters. What is going on here? Where is the business logic?

The motto of the second iteration of PHP frameworks is "less magic" (and "less WTF moments"). The problem with pushing conventions all over an application is that when something goes wrong, it becomes very difficult to find out what is not working correctly. It's like having an IDE deploying your application on the live server at the push of a button: apart from the fact that you have no clue on how to introduce customizations in the automated process, you're never going to know what that button does, and if the deployment fails you're left with a standard error message. Netbeans is an IDE that gets it right: it generates an Ant script (usually build.xml) with a list of targets such as build and test, and its buttons simply run this script. You can look at build.xml or even add debugging statement if there are issues, while hacking in a framework's automagic code open a big a can of worms.

Another example of less magic in PHP (and also in Rails, as Active Record will not be forced upon the developers in 3.x) is the current transition from Active Record with public properties to DataMapper-based entities that expose getters. In Doctrine 2, the use of getters lets the framework override them in custom subclasses that are saved in a folder specified via configuration. These subclasses are proxies - they take the place of entitites that may not be accessed and when the getters are called, perform lazy loading of the fields. And you can hack them for debugging if something does not add up.

Sure, in PHP the same can be obtained with __get() and __set(), but then we'll have the following use case:

echo $post->title; // giant database query ensues

And when you lose the distinction between properties and methods, you have some magic that is going to bite.

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Sean Sandy replied on Thu, 2010/05/06 - 5:59am

Doesn't doctrine actually use __get and __set and __call to map the properties to a value (which is processed by a function if it exists)? I see no problem with that last example as long as it doesn't do the giant database query every time that $post->title is called... It has to be done at some point to get the data no?

Giorgio Sironi replied on Sat, 2010/05/08 - 6:30am

Doctrine 1 does, while Doctrine 2 forbids the use of public properties favoring getters. Ideally the data would be loaded with the original query that brought back the object you're working on, but in both Doctrine 1 and Doctrine 2 a lazy loading process will be applied if they are not included. Lazy loading has an heavy impact on performance, because you do lot of little queries instead of a unique one; however if it happens while you're calling a getter of a proxy subclass, you're usually aware of it, while if it happens while you're printing a property it's not so transparent. __get() and __set() have been discussed many times as problematic because of this supposed transparency.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.