Moving From Herding Elephants To Handling Snakes

Since the summer, something had been bothering me about PHP. I couldn’t put my finger on it but over the holidays I finally figured out what was wrong. After ten years of working with PHP, I’m bored with it. Every professional programming job I’ve ever had has involved PHP as the main language, with a smattering of things like SQL, Perl, Ruby and Javascript. But it’s always PHP at the core.

So what is there left for me to do with PHP? I’m not about to write my own PHP framework as I have one I already like, one that I use for my simulation baseball league’s web site. I’m not a C programmer, so diving into the internals of PHP to contribute that way is a no go. No, it’s time to put PHP aside (so to speak) and pour my energies into something else.

A while back I tried the Rails thing. I liked it…because it was so different from PHP. Ruby has such elegant syntax, reading like English in many ways. I even got asked to create a training course for Ruby on Rails, but never got far with it (sorry Marco). Once I started poking around more about Rails, I started to see things that made me uneasy about it: issues about performance in high-traffic environments, the attitude of some of the more senior “core” guys in Rails, and the zealotry displayed by users of it.

Slam PHP all you want for it’s syntax ($object->chaining->looks->weird()), it’s inconsistency in function parameter order, the still-ongoing debate about how to implement namespaces, and the combo of powerful functionality combined with a low barrier of entry (thereby infuriating programming snobs). But it gets shit done, plain and simple. That’s how Rasmus Lerdorf started off using PHP, that’s how it will always be. Elegance in code and proper programming techniques are the domain of the developer, not the language in my not-so-humble opinion.

Sure, Ruby is nice and Rails has all those cool magic functions but some stuff I read by Zed Shaw (the creator of Mongrel put into words a lot of what I had felt about Rails: just not for me, I guess.

So what’s left? Not gonna go the Java route: too much pain and suffering lies down that path. So, I’m left looking at a language that is rock solid, has a great object model, has a good web application framework to help me get up to speed and lots and lots and LOTS of great documentation and tutorials: Python.

I won’t be giving up getting PAID to do PHP code any time soon because I have a great job with a great bunch of guys, and I get to work from home. But I just feel it’s time to focus my energies on something else. Python is very similar to Ruby, and let’s be honest here: I’ve got enough programming experience that there shouldn’t be much of a learning curve for me to figure out Python. Python code just looks nice and clean, less writing to get more done. That’s what I’m really looking for these days.

The simball web site continues to grow and get new features, so I will be leveraging CakePHP to the hilt in that respect. It’s an exciting time for CakePHP as they are heading towards 1.2 and preparing for 2.0. But I am not sure what my contribution to the project will be any more, which hasn’t been much lately but I am interested in helping push the CLI tools forward as not only can they help me, they can help other developers as well. But it is time for a change. Maybe one of these days I find a job programming in Python, or even manage to push Python in through the back door where I currently work. But the time is absolutely right for me to start learning Python and make it a tool I want to use on a regular basis instead of it continually being on my list of “things to do when I feel up to it”.

I’ve got my copy of “Dive Into Python” in PDF, been going through the Python tutorials (wow, the interactive interpreter is great) and getting ready to bust out a Django install. But make no mistake: PHP pays the bills, but it’s time to start using other people’s tools to get the job done. Time to quit fooling myself that I’m going to make some sort of earth-shattering contribution to PHP, and save my brainpower for learning Python the way I’ve learned PHP.

Wish me luck!

Article Tags >> ||

The Story and Rebirth Of Zend_Service_Audioscrobbler, Part 2

So, dear readers, I received some nice emails and comments from those you have used Zend_Service_Audioscrobbler, along with having a nice email conversation with Wil Sinclair from Zend (who is involved with Zend Framework). Wil told me that backwards compatibility for the 1.5 version is important so I can’t just rip everything up like I want to. Well, I could but it probably wouldn’t be accepted. I’d have to write some letter to convince the Elder Gods Of Zend Framework of why I needed to break backwards compatibility. In Wil’s words: “‘I find it embarrassing and want to destroy the evidence’ will not be considered a good enough reason. “.

I had a good chuckle at that, but Wil did offer up a very good suggestion on how to go about the refactor that I hadn’t thought of: use a _call() method in the class to intercept all the old calls and translate them into new calls. I would also get it to trigger a warning that the old method would be deprecated in a future release as a way to get people to upgrade to the newer ones. Once I have some code, I will show it. Thanks Wil!

Article Tags >> || ||

The Story and Rebirth Of Zend_Service_Audioscrobbler, Part 1

Many moons ago I set out (along with my friend Derek) to make a contribution to Zend Framework in the form of an add-on to let people easily access the Audioscrobbler web service. We worked really hard, implemented all the features of the web service at that time. Imagine our surprise when it got accepted as part of the 1.0 release of Zend Framework! Awesome! It even has it's own entry in the manual and everything.

However, I have a confession to make, although it will not come as a surprise to anyone who reads my blog on a regular basis: the code is a complete piece of shit. There. I said it. How do I know that it's terrible and needs refactoring in a fierce way? Check out this lovely snippet of code:

PHP:
  1. //////////////////////////////////////////////////////////
  2.     ///////////////////////  USER  ///////////////////////////
  3.     //////////////////////////////////////////////////////////
  4.  
  5.     /**
  6.     * Utility function to get Audioscrobbler profile information (eg: Name, Gender)
  7.     * @return array containing information
  8.     */
  9.     public function userGetProfileInformation()
  10.     {
  11.         $service = "/{$this->get('version')}/user/{$this->get('user')}/profile.xml";
  12.         return $this->getInfo($service);
  13.     }
  14.  
  15.     /**
  16.      * Utility function get this user's 50 most played artists
  17.      * @return array containing info
  18.     */
  19.     public function userGetTopArtists()
  20.     {
  21.         $service = "/{$this->get('version')}/user/{$this->get('user')}/topartists.xml";
  22.         return $this->getInfo($service);
  23.     }
  24.  
  25.     /**
  26.      * Utility function to get this user's 50 most played albums
  27.      * @return SimpleXML object containing result set
  28.     */
  29.     public function userGetTopAlbums()
  30.     {
  31.         $service = "/{$this->get('version')}/user/{$this->get('user')}/topalbums.xml";
  32.         return $this->getInfo($service);
  33.     }

It goes on and on and freakin' on like this. SEVENTEEN methods just for dealing with user stuff. This is insane. Going back over the comments when I was building this thing I realized I totally ignored created elegant code and instead just 'banged out something that worked'. So, I'm going back to the drawing board and are going to refactor this puppy so it makes sense. First up, let's talk about dealing with users. Wouldn't it be better if we had something like this:

PHP:
  1. public function user($action) {
  2.      $service = "/{$this->get('version')}/user/{$this->get('user')}/{$action}.xml";
  3.      return $this->getInfo($service);
  4. }

Now, THAT looks like nice and elegant. All I have to do is establish the convention on how to connect to various user-related web services. That's as simple as comments in the file itself. Now, to replicate what I was doing before, here are how simple the calls could be:

PHP:
  1. $zsa = new Zend_Service_Audioscrobbler();
  2. $zsa->set('user', 'chartjes');
  3. $zsa->set('version', '1.0');
  4. $userProfile = $zsa->user('profile');
  5. $userTopArtists = $zsa->user('topartists');
  6. $userTopAlbums = $zsa->user('topalbums');

I think I just got rid of something like 200 lines of code...and that's just in the user section. Clearly, I majorly screwed it up when I did it the first. Luckily for me, there is built-in testing for all this stuff so I can refactor and test as I go.

Article Tags >> || || || ||

How Many Times Has This Happened To You?

I had recently upgraded to a new laptop (white MacBook) and had just gotten stuff setup up the way I liked. So I continued doing development work for my latest work-related project and it was time to push the changes up to the staging server. Oh wait, I hadn't installed Capistrano yet. So I installed that and found, oooh, we moved up to version 2.1. Cool! Progress is good.

So I go to deploy and WTF!?!??! It won't work!??! No error messages except to say the remote command failed. I dig around on google. The amount of documentation available for Capistrano 2.0 (or 2.1 for that matter) makes CakePHP's slowly-growing documentation look like Wikipedia. God damn it, why would you release something that breaks so many things and then don't tell people WHY it won't work. Maybe I'm doing something wrong, but I'll be damned if I can figure out what it is.

So, on to the backup plan. Phing, despite it's use of XML which I am not huge fan of in terms of it's use as a configuration file, but at least it's PHP and there is lots of documentation on how to use it. Now, where did I put Travis Swicegood's e-mail address...

Article Tags >> || ||

More XML-DB Fun!

We had a nice corporate retreat last week, an opportunity for all the members of XMLTeam to get together (and in some cases meet face-to-face for the first time) and discuss what's going on and figure out priorities over the next 12 months. If you're expecting some company secrets here, you'll be disappointed. :)

One of the things that got bandied around was whether or not we would continue using eXist to store the XML documents that the application is using or go with a database-driven solution. So, my partner on this project was tasked with figuring out how to update existing elements and attributes in this documents using eXist only.

How can I put this? Oh, I know: traversing through large complicated XML documents to find one element that you need to update when the location could be in multiple places sucks! Part of the reason for this may be my lack of XPath foo within PHP itself. Besides, if eXist can handle this for us then why shouldn't we leverage this? I liken it to being able to do an UPDATE statement in a database. I don't do a SELECT where I grab the whole data set, then drill down through the data looking for one specific row. You do an update with the proper key. So, Paul (that's the co-worker) got to work figuring out for me while I refactored code to be ready for it. He got one script working (yay!) so I hacked away and got a very simple one to work today. Here it is (with some info changed to protect the status of the project:

CODE:
  1. xquery version "1.0";
  2. declare namespace sportsml="http://iptc.org/std/SportsML/2006-10-18/";
  3. declare namespace xts="http://www.xmlteam.com";
  4. declare namespace request="http://exist-db.org/xquery/request";
  5. declare namespace xmldb="http://exist-db.org/xquery/xmldb";
  6. declare namespace util="http://exist-db.org/xquery/util";
  7.  
  8. declare variable $league := request:get-parameter("league", "");
  9. declare variable $league-name := request:get-parameter("league-name", "");
  10. declare variable $team-key := request:get-parameter("team-key", "");
  11. declare variable $colors := request:get-parameter("colors", "");
  12. declare variable $location := request:get-parameter("location", "");
  13. declare variable $name := request:get-parameter("name", "");
  14. declare variable $nickname := request:get-parameter("nickname", "");
  15. declare variable $abbreviation := request:get-parameter("abbreviation", "");
  16.  
  17. declare variable $league-doc := concat('/db/', $league, '/dynamic-resource-file/', $league, '.xml');
  18.  
  19. <dummy>
  20. {
  21. if (doc($league-doc)/sports-content/statistic/group//team-metadata/@team-key=$team-key) then
  22.  
  23. for $team-metadata in doc($league-doc)/sports-content/statistic/group//team-metadata[@team-key=$team-key]
  24. return
  25. update replace $team-metadata with
  26. <team -metadata>
  27. {attribute team-key {$team-key}}
  28. {attribute colors {$colors}}
  29. <name>
  30. {attribute first {$location}}
  31. {attribute full {$name}}
  32. {attribute nickname {$nickname}}
  33. {attribute abbreviation {$abbreviation}}
  34. </name>
  35. <affiliation>
  36. {attribute membership-type {"league"}}
  37. {attribute membership-key {$league}}
  38. {attribute membership-name {$league-name}}
  39. </affiliation>
  40. </team>
  41.  
  42. else
  43.  
  44. return
  45. }
  46. </dummy>

So, this sits in eXist, which is running on top of Tomcat and I send the data in by POST-ing to the script. I was all set to start using cURL for this task but a quick search on the net found this blog posting by Wez Furlong showing how to use streams to do what most people automatically associate with having to use cURL for. I liked it so much I went and refactored some other code that was using cURL to use that stuff instead. It's always nice to find out that something you've been doing can still be done, but with fewer lines of code and *still* be elegant.

I'm off to look closer at streams in PHP 5 and see what other hidden treats are there...

Article Tags >> || ||
Want to advertise on this blog? Send email to chartjes@littlehart.net
GTcars Canadian Car Audio TurboDodge Audi Forum
Mustang Forum Dodge Intrepid Miata Turbo
GTscene Pontiac Bonneville