Old School PHP Scripts: Numword, the number to word converter

The Numword class (via Github) will rarely find a use, but its creation was primarily for fun. A friend of mine asked me if there was a PHP function that will turn a number into its word equivalent (example, 100 becomes one-hundred). As none existed, I felt like this would be a fun task to attempt, and so the Numword class was born. Numword supports the basic range of numbers and the ability to convert up to centillion (which is mind blowingly large).

The easiest way to convert a number is to use the single() method. This method accepts a single number argument and returns the word equivalent. You may also use the multiple() method which accepts an array of numbers. Do note however, that large numbers must be passed as a string, else it will blow up because of PHPs 32 bit integers.

// one-thousand, two-hundred thirty-four
Numword::single(1234);
// eight-billion, two-hundred thirty-four-million, seven-hundred eighty-thousand, two-hundred thirty-four
Numword::single('8234780234');

Some other convenient methods are block() and currency(). The block() method will parse out any numbers within a string of text, and convert them. While the currency() method is self explanatory, it converts currency.

// I am twenty-five years, fifteen days and sixty-two minutes years old.
Numword::block('I am 25 years, 15 days and 62 minutes years old.');
// one-thousand, three-hundred thirty-seven dollar(s) & fifteen cent(s)
Numword::currency('$1,337.15');

Awesome right? Furthermore, the currency() method is rather smart, in that it parses out the dollar sign, commas, and periods depending on the current locale based on setlocale(). You can also translate the strings used in currency() by passing an array as the second argument. But before we do that, lets go over translating the whole class.

Translating the strings in Numword is extremely easy, but also tedious. If you only need to translate for a single language, then you can overwrite the static properties. If you need to translate for multiple languages (user language selection system), then you will still need to overwrite the properties, but create some kind of system to know which language to use and when (possibly via includes). Here's an example translation of German; zero through nine respectively.

Numword::$digits = array('null', 'eins', 'zwei', 'drei', 'vier', 'fünf', 'sechs', 'sieben', 'acht', 'neun');

And to translate the currency strings, you can do something like:

Numword::currency('£48,530.38', array('dollar' => 'pound(s)', 'cent' => 'pence'));

Numword isn't as extensible as I would like, but since it is merely a fun project, the need for heavy translation and locale awareness settings aren't needed. You can always base your own class on Numword :). Hope you enjoyed!

Refactoring is fun

For over a year now I have been eager to redo the backend (built on CakePHP) of this site. I kept putting it off, until last week when I was fixing bugs that I realized it would be more beneficial to just rebuild the whole site. I wanted to redo both the PHP and the database tables, as the old tables were from a previous installation. Here's just a quick list of things I wanted to change:

  • Separate blog tags into its own table and setup a HABTM (was a column in each entry)
  • Remove ENUM fields from the database tables and use class constants
  • Use slugs for all dynamic URLs
  • Use the model's counterCache system instead of a count find()
  • Add a blog series system (example)
  • Fix the bugs in my comments and contact forms
  • Rebuild the code/script section and remove the old "versioning" system (since I use Github now)
  • Build an admin panel

So to begin this huge task, I created new database tables based on the architecture I wanted. Once done, I created all the models for each of these new tables (and made sure to keep the old models for importing). The next step was to create CakePHP shells that use the old models to generate the new data structure and save it into the new tables (while keeping legacy IDs intact). This database changed fixed the dislikes I had with the old table columns (by removing ENUMs), added the slug and ID fields where necessary, and removed the old and useless tables I don't have a need for. First task complete.

Now that the fun step was over, it was time to refactor all the old controllers and views. Most of the old controllers were re-usable (like blog, pages, comments and feeds), all I simply had to do was make sure the new column names were being used and add support for new features (blog series, etc). The most time consuming part in this process was splitting up the old resources controller into two new controllers: code and snippets. Since the code section of my site was re-built from the ground up, these controllers also had to be rebuilt. The new code structure only uses 3 tables compared to the previous 5, win! However, I still had a problem with old legacy URLs. The solution I went with, was to allow the old URLs to redirect to the new ones using a jump controller (which also powers my tiny URL system), as well as allowing the URLs to work with a slug or ID (very important). Example, all of these links lead to the same place.

http://milesj.me/code/cakephp/forum
http://milesj.me/resources/script/forum-plugin
http://milesj.me/c/13

At this point, I was extremely pleased with the refactoring process. The next and last step was to create an admin panel system for all types of content on the site (I didn't have one before). I decided to place all of this code within a plugin, as I didn't want to litter my controllers with admin_ methods and it gave me more control of security and management as it was self-contained. I was expecting this process to take weeks to finish, but I completed it in less than 8 hours, win again! I used the technique of having a single view template for both the add() and edit() methods and was able to re-use a lot of code (DRY for the win). I highly suggest this approach for anyone who needs an admin system.

All in all, the process wasn't as back breaking and time consuming as estimated. I basically rebuilt the whole site in under 2 weeks, working about 1 hour a day. If you are interested, here's a quick list of all the changes.

  • Importing of old data into new database tables
  • Refactor of old models, controllers and views
  • Moving tags into a HATBM table and model
  • Adding slugs to all URLs
  • New blog archiving system for date ranges, tags and topics
  • New blog series feature
  • Adding counterCache for comments and tags
  • Adding a jump controller to deal with legacy URLs and tiny URLs
  • Splitting of old resources controller into code and snippets
  • Rebuilding the code packages and versioning system

I wouldn't doubt it if I forgot something! But whats next you ask? The worst part of all, updating my documentation. Now that will take some time.