PHP vs. RUBY: What’s the Point?
You will no doubt have read one of the many articles out there comparing the merits of PHP against Ruby, or more commonly to my exasperation, PHP vs Rails. I hope, like me, you find such articles pointless exercises and nothing short of language trolling. It’s just not a level playing field.
Somehow developing for the web brings these two different beasts together for comparison. PHP a.k.a Personal Home Page Tools (thanks wikipedia) is a language/framework developed specifically for the web. Ruby on the other hand is a general purpose language conceived by one man Matz, in a quest to create a utopian programming language.
Starting out in PHP, you will probably create a few pages with code laced in the HTML. Then after realising how painful that can be to maintain, you will start abstracting your business logic and presentation layers using something like Smarty. You will get a bit more object orientated and no doubt grab something like Zend Framework or CodeIgniter to develop your own applications. Hopefully, that all sounds familiar.
When it comes to Ruby, so many developers ignore such a sensible path of development. I know I certainly did. Where do most people start with Ruby, myself included? Rails, of course. We watch the “build a blog” video and, presto, we are all sold. I would never discourage anyone from picking up Rails and running with it, but it’s not Ruby for beginners. When you use Rails, a lot of great developers have spent a lot of time abstracting all the horrible nitty gritty stuff away. Migrations just work, Routing just works, logging and testing and right there for you to use. Rails is a framework that gets out the way and lets you focus on the problem you want to solve.
Hello Rack
One good reason to start with Rails is, when it comes to developing for the web, Ruby on its own is nothing short of intrusive. Sure we can use the standard library CGI class, upload the file to the server, make it executable and we are done. Compare that with a PHP script.
Even DHH blogged about the immediacy of PHP. Your gratification is instant. Want to test a quick bug fix? Just hit refresh on the browser. None of this restarting mongrel, Passenger, or whatever is required.
So how can we get to that kind of instant Ruby web apps without resorting to rails s. Well, how about we use the framework Rails itself uses? Rack.
Rack is the interface between Rails apps and the HTTP protocol. It is also the basis of pretty much all Ruby web frameworks, Sinatra & merb included.
Rack incorporates all that low level code that framework developers were duplicating across projects. It basically scoops up any web server available and uses it to serve your apps (by web server we are talking mongrel, WEBrick, thin and so on).
To get started with Rack it’s simply a case of installing the gem, creating a rackup file (*.ru), and starting the app.
The hello world of Rack looks like the following (hello.ru):
class HelloWorld
def call(env)
[200, {"Content-Type" => "text/html"}, ["<h1>Hello world!</h1>"]]
end
end
run HelloWorld.new
Then in the console, rackup hello.ru. You will see a bit of server output with the port the server has started on (usually 9292). Just navigate to http://localhost:9292 and see the glory.
To dissect this simple application (and it is an application), basically we have a method named call that receives the environment and returns an array of three things, status, headers and body. By environment we are not talking staging, production etc. instead it’s the more CGI set of variables we see in PHP’s $_SERVER super global, REQUEST_METHOD etc.
The status codes are pretty self explanatory and the contents of the headers hash will also be familiar. The body we see in the hello world example is an array, or more specifically, it must respond to each. Finally, at the end of the file we see the run method spinning up an instance of our hello world application.
So the basic rules of a Rack application are it must have a method call that accepts the environment hash and returns status, headers and body, and body must respond to each.
Echo ‘Hello World’
We have seen a basic Rack application, but how does that compare to the simplicity of:
<? php echo "<h1>Hello World</h1>"; ?>
At face value, it certainly seems more convoluted, so let’s look at what Rack gives you to make it more attractive.
The Builder
When it comes to hello world PHP is pretty hard to beat. Luckily for the Rubyist in us, hello world applications are in low demand, and Rack comes with a whole lot more than wrapping servers. Rack itself ships with many micro Rack applications that will assist us in building our frameworks. They include all the helpful stuff like logging, sessions, url mapping and so on.
One of the absolute gems of Rack has to be Rack::Builder. This is a Domain Specific Language (DSL) that allows to construct and mash together Rack applications easily. Consider building a PHP application that has a public page and a secret page. In PHP we could create two files that perform these duties. An alternative would be to create a .htaccess file that directs all incoming requests to a single file like so.
[console]
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ /index.php?page=$1 [L]
[/console]
<?php
if ($_SERVER['REQUEST_URI'] == '/secret') {
echo "Shhhh";
} else {
echo "This is public";
}
?>
Not too bad, its implementation using Rack::Builder could look something like:
app = Rack::Builder.new do
map "/" do
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["This is public"]] }
end
map "/secret" do
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Shhhh"]] }
end
end
run app
Pretty neat? What we have done here is implement Builder to map urls to given actions. These actions are just Proc just now as we find our feet, they return the golden trio we seen in our hello world app.
This is infinitely scalable as well. For example, let’s look at a path such as ‘/secret/files’. Our PHP version gets hairy enough to warrant a rethink (we dont want to go down the line of adding files to relative directories do we?), in Rack we simply nest some map blocks.
map "/secret" do
map "/" do
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Shhhhh"]] }
end
map "/files" do
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Here be dragons"]] }
end
end
Hopefully, you are nearly sold on Rack. While we are still feeling the love, let’s spice it up a bit by adding some more kinky Rack toys.
Rack = Damn Sexy
We mentioned before that Rack is more that a server interface. It comes with a wealth of “components” which are themselves Rack applications. Now, we will look at how we can implement a couple of these.
I always find logging helpful when developing applications.
require 'logger'
app = Rack::Builder.new do
use Rack::CommonLogger
Logger.new('my_rack.log')
map "/" do
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["This is public"]] }
end
map "/secret" do
map "/" do
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Shhhhh"]] }
end
map "/files" do
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Here be dragons"]] }
end
end
run app
We have been talking about secret areas and dragons, so we better lock all that up. HTTP Basic Authentication is always good for securing things.
require 'logger'
app = Rack::Builder.new do
use Rack::CommonLogger
Logger.new('my_rack.log')
map "/" do
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["This is public"]] }
end
map "/secret" do
use Rack::Auth::Basic do |user, password|
user == 'super_user' && password == 'secret'
end
map "/" do
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Shhhhh"]] }
end
map "/files" do
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Here be dragons"]] }
end
end
end
run app
That was incredibly easy, I remember the days of setting up .htpasswd files and so on.
Rackup
I started this article as if it were a PHP vs Ruby nonsense. And throughout the article I have made references about how we could do this in PHP and compared it to Rack. It was all a cunning rouse, preying on the language troll in all of us. Fact is, both have merits and even comparing Rack with PHP is hardly fair.
I hope this has given you a taste of how flexible, maintainable, and joy-inspiring using Rack is. It’s a great place to start when learning Ruby because there is enough ‘magic’ to keep our interest, but not enough to obscure learning.
We have not finished there though. You will remember all the big frameworks are built on top of Rack. We can actually implement mystical middlewares using Rack that intercept the normal flow of our applications and temporarily hand control to Rack applications. Could be scary, but Rails loves it.
Unfortunately, that hasn't always been something readily available in PHP, but those frameworks are just as available to learn PHP side by side with a framework.
There is a gross misunderstanding of PHP by way means of practice. It is not always gunslingers with MySQL and echo's in the HTML. However, it has been demonstrated this way for a long time, which is not really a fair gauge of practicality. You could probably write Ruby just as poorly. When people think Ruby they immediately think Rails. When people think of PHP they don't immediately think of PHP 5's OO improvements or Lithium or Cake.
Ruby is NOT maintainable, doesn't scale well, and leads to badly written unreadable code. I mean, seriously,
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Shhhhh"]] }
??? Maintainable = False;
But, you already know that :)
Anyway, cheers for the writeup and honesty about your intentions. Good work.
This article makes a very good point about - " Your gratification is instant. "
Many of my friends started web development with *something and then start with rails and then they say that *something is better because you start early, it's easy and so, so... but point is it's instant gratification - which won't scale..
Rails is basically convention over configuration, and so one starts loving rails, when he understands those conventions is what I feel after using rails since last 2 years.
The point of this post is really good for laying some common ground between the two languages. Well done, Dave.
`Maintainable = False;`
returns true, so does that mean that you're trying to say that it IS maintainable?
It sounds stupid to point that out, but pointing out an obscure piece of code in Ruby is too. One could do the same with Python.
[p[:i]+[l[0]]+p[i:] for i in range(sz) for p in perm(l[1:])]
Here you go, python is too complicated because I don't understand it. It's not maintanable! It doesn't scall! I'm a troll from 2005!
Best <3
No false idols here, should be:
preying on the language troll in all of us.
Language articles always bring out the trolls. Perl./Python/PHP/Ruby can all do this basic http manipulation, and each can build fairly complex object-oriented (-ish) processes. It's a poor craftsman who blames his tools, or only uses a single one. Everyone should try several languages and work with the ones that speak best to them.
You mention the merit of abstraction in PHP - by the use of templating and frameworks. But then you show these Rack examples that, to me, seem to throw that thinking out and mix everything back together again - like beginner PHP.
Granted I don't know that much about Ruby, Rack or Rails - but to me those examples seem to have more in common with what setting up routes in a PHP framework is like than with the PHP 'equivalents' you show.
I know they're examples, but would you seriously build an app on code like that?
I've tried Rails (Ruby) and Django (Python), but in the end most of my customers have simple hosting with only apache/php and it stays my language of choice. I've chosen Symfony 2 for now, but there's probably 100ths of frameworks out there that each have their own vices and virtues. And whatever you say, that isn't true for Ruby or Django.
It's PHP: Hypertext Preprocessor, not Personal Home Page Tools.
Maybe we read different versions of wikipedia, but what i read there is
"While PHP originally stood for "Personal Home Page", it is now said to stand for "PHP: Hypertext Preprocessor", a recursive acronym.[8]" http://en.wikipedia.org/wiki/PHP
This is the first disappointing article I have read on any sitepoint site.
Why would I bother with all this when apache can do it for me??? Simply route to all the pages i want. If I want realm auth,
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text to send if user hits Cancel button';
exit;
} else {
echo "Hello {$_SERVER['PHP_AUTH_USER']}.";
echo "You entered {$_SERVER['PHP_AUTH_PW']} as your password.";
}
No use doing all that you did. I think you actually spoilt rack for me because, as you said, Instant gratification is what drives us.
It isn't a rack thing, but a problem with frameworks even in php. You can perform magic with .htaccess and a little ReGex knowledge.
Just my 2 cents.
Rack here (it's a heck of a lot more) is a tool for us to get quick feedback on web based Ruby apps. Save us from getting lots abstracted away in larger frameworks and focus on what we want to do.
Instead of comparing PHP to Ruby on Rails or Rack, it’d be more accurate to compare PHP “on Symfony” (or any other framework) to Ruby on Rails. With that in mind, I think that each language have some features to envy from the other. Not quite sure if I can say that one is “better” than the other, though.
Personally, I choose PHP on Symfony2 over Ruby on Rails. Works better for me, but then again, that's a matter of choice (considering your clients' needs, your team, etc.)
The gist is (and I see you think the same way) it is comparing apples and oranges
PHP is a language with a standard library. It just so happens that it is geared toward the web, but can also be used for command line purposes.
Likewise, Ruby is a language with a standard library. It isn't geared specifically for the web, but can be used as such.
But before this goes out of context again, the previous reply was a bit flippant as at no point did the article get into which is better, PHP vs Ruby, Rails vs Symphony. That kind of thing is pointless, we all know better and use the best tool for the job.
Finally, PHP is great, I have converted somewhat though and feel more at home using Ruby for some applications. However I have no intention in the near or distant future of dropping PHP from my toolkit, its far too useful. Well untill the next big thing comes along then Ill write an article comparing that to PHP :)
You mentioned rack might be able to give you instant gratification on the level of php, but you don't really get to how. After I rackup my ru file, modifying the file seems to do nothing until i re-rackup. Just wondering if there's a config switch or something.
Taking Rack::CommonLogger as an example, I assume you have to actually log activities to the Logfile. All your example did was to create the Logfile? But where would I find such detail?
Regards
Alan