Aug
ActiveSupport for PHP - Ruby style
Quick introduction
One of the things I miss most from Rails is ActiveSupport, the module that modifies Ruby’s core classes (numbers, strings, more) with handy utility methods. They tie so well into the language, most Rails developers don’t realize they aren’t core methods.
Here’s a few examples:
>> 'hello_world'.camelize
=> 'HelloWorld'
>> 7.days.ago
=> Sun Aug 05 20:53:12 -0400 2007
>> 'purple people eater'.ends_with?('eater')
=> true
Aside: For a near-complete list of ActiveSupport goodies, check out ErrTheBlog’s teriffic Rails Rubyisms Advent article.
The PHP way
Big chunks of ActiveSupport (mostly string inflection) have been ported to each of the major PHP frameworks, and an ActiveSupport extension has already been submitted to PEAR.
In nearly all of these cases, the functionality has been ported, but the elegance of ActiveSupport is lost in translation. Here’s the camelize example again, this time using Symfony:
sfInflector::camelize('hello_world');
As with pretty much all PHP code, it’s interpreted right-to-left instead of left-to-right, like Ruby. I prefer the latter.
The Ruby way (in PHP)
So, armed with an afternoon and a full pot of coffee, I wrote my own Ruby-like ActiveSupport library for PHP5. Here’s what it looks like:
require_once('ActiveSupport.php');
_(14)->ordinalize(); # Outputs "14th"
_(7.3)->megabytes(); # Returns number of bytes in 7.3 megabytes
_("an example sentence")->endsWith("sentence"); # Returns true
What the hell?
Okay, there’s some trickery going on here, so I’ll provide a quick explanation.
The underscore shortcut you see above is a quick way of instantiating one of four core ActiveSupport types: ActiveSupport_Integer, ActiveSupport_Numeric, ActiveSupport_String, or ActiveSupport_Array. The class you get depends on the type of the parameter you pass in. Each of these classes have been armed the same utility methods in their Rails counterpart.
What about chaining?
Ruby’s chained time/date expressions are my favourite, and they haven’t been forgotten. You can chain ActiveSupport function calls by ending them with an underscore:
_(2)->weeks_()->ago(); # Returns appropriate timestamp
_(14)->days_()->before(time()); # Same value as above, written differently
By default, all ActiveSupport function calls return primitive types (integer, float, etc) in order to play nice with traditional PHP. But when you append an underscore to the function name, you get the corresponding ActiveSupport type instead. So where _(2)->weeks() returns an integer, _(2)->weeks_() returns an object of class ActiveSupport_Numeric.
And there you have it
Want to give it a try? Download the (mostly complete) source code here (MIT License).
I’m not sure how practical this library actually is, but developing it was an interesting exercise. I was able to learn a lot about PHP’s limitations, as well as its flexibility.
As always, feedback is appreciated.

Commentary
the only comment really that I have is the use of the _() function namespace, as there are some other projects that use that, like wordpress for example. I find alot of people use that style of function naming for gettext type of stuff, since its so small. maybe it would be better to just provide a simple way to get back an instance of the ActiveSupport class, maybe even come up with a different name so that its not so long.
Since you are essentially creating a factory you could just do something more PHPy like $instance = ActiveSupport::factory(your arugment);
something like that who knows.
Hey Kenrick,
Thanks for the comments. Yeah, I had toyed with a number of different entry points instead of _(). Another thought was to have users of the library register their own shortcut function.
Ben I’ve just had to make a similar move from 2 years of solid Rails development to a PHP project. It’s nice to see others thinking in Ruby even though they have to write PHP.
Hey Flinn,
I’d been keeping up with your blog, actually. It’s funny, because once I learned I’d be developing in PHP, I treaded down a similar path of attempting to re-write ActiveRecord and other familiar Railsisms. More often than not, it doesn’t work out.
BTW – You should open up commenting on your blog, or put up a “Contact Me” page of some kind.
I agree that the gettext thing would be the biggest hurdle here. A lot of popular hosting companies
- such as Dreamhost -have the gettext extension enabled by default, so any attempt to create a _() function will generate “Cannot redeclare” errors.(I know this because, well, I’ve tried to do it before.)
The obvious answer would be to do some function_exists magic. I’ve been working on something like this for Scatterpress, and and ended up with a bunch of N(), S(), and O() functions for Numeric, String, and Object types. It’s a nifty exercise, but the syntactic juice isn’t quite worth the squeeze.
Our solution has been to embrace the inversion, and change 2.days.ago into since(2, “days”)
As someone who programs both it is nice to see other solutions that people are coming up with. Going back to PHP from Rails always leaves me desiring the simplicity. CakePHP had a good helper class and inflection, but it still isn’t as simple as writing ‘hello_world’.camelize. You have to deal with the PHP way, so it would be $html->camelize(‘hello_world’).
Needless to say, I like your train of thought – and reversing things to fit more to the Ruby way (in reverse).
Hey Matt,
You know what? When I first started writing this library, I similarly used n(), s(), and a() methods instead of _(). I guess I liked the single type-agnostic entry point, similar to JQuery’s $() method.
BTW, I might have to steal your since() method – alternatively, how about days(2), or days(ago(2))?
Hey,
In a quick check, I found a little bug, so I rewrote the endsWith method. The new code is below:
public function endsWith($string) { $string = preg_quote($string, ”/”); return preg_match(”/$string$/”, $this->native); }
Hope this is useful.
Regards.
Hey Fernando,
Thanks for the patch – I’m encouraged to know someone’s poking through the code.
Hey Ben, yeah I spent a long time porting ActiveRecord to PHP and came to that same realization. It just doesn’t work. Rails is what it is because of Ruby. I’ve been a week into PHP and I miss Ruby. Btw, I have comments off on my blog because of the endless spam. I’m sure I’ll get around to adding an annoying captcha to re-enable comments.
Sorry to say, it’s still ugly PHP way.
Ruby is the language for Rails, it could not be developed in any other language!