What’s In Chris’ Brain: August 2007 Edition

I have a post coming up later this week on some tips and tricks involving CakePHP best practices I’ve picked up (and had foisted upon me) but for now it’s brain dump time:

  • Can the lazyweb tell me where I can find some cool online crime statistics? I was at a friend’s birthday party last Saturday and he suggested a really cool mashup that people searching for houses would be interested in
  • I’m back to using Komodo instead of vim because, well, vim just couldn’t give me the environment I wanted. What I need is the “here are the files in your project” hanging off the main editing window, syntax checking when I’m typing away, and then show me what files I changed that are going to be checked back into version control. Komodo does this. Vim does not. Well, at least not the way I want it to.
  • Is it possible for a single incompetent programmer to cost their employer millions of dollars in lost opportunities without actually breaking anything? I think a lot of managers would go insane if they actually sat down and tracked how the progress of others was held up by a single person. That “domino” effect could cost a company a lot of money without them actually realizing it.
  • Big news out of CakePHP-land is that Mambo will be using CakePHP as the core for the next version of Mambo. Yet another reason to seriously consider CakePHP, if you haven’t already

Two-Headed Application Authentication in CakePHP 1.2

I've been working on an internal application for use at CDC that required me to have what I call "two-headed" authentication. By this I mean that there were two types of users in the system and while they could access some of the same controllers they could not always access the same actions. I know what you're saying: why not have them both use the same model instead of making my life difficult? A few reasons, one of which was that one type of user was using their email address as the login while the other had a more traditional login name.

After struggling my way through the Auth component (the API changed while I was starting to use it), and understanding the actual flow that the Auth component uses, I nailed down how to do it. Using good CDC practices (as in "do it our way you stupid Kanuckistanian!") I started off with putting my Auth stuff into the before filter:

PHP:
  1. function beforeFilter() {
  2.         parent::beforeFilter();
  3.                
  4.         $this->Auth->loginAction = 'plugin/users/login';
  5.         $this->Auth->loginRedirect = 'plugin/users/index';
  6.         $this->Auth->fields = array('username' => 'username', 'password' => 'psword');
  7.        
  8.         $this->set('current', $this->action);
  9.        
  10.         if($this->Auth->isAuthorized()) {
  11.            
  12.             $role = $this->Auth->user('role');
  13.            
  14.             if($role) {
  15.                 if ($role == 'admin' && strpost($this->action, CAKE_ADMIN) !== false) {
  16.                     $this->Auth->allow($this->action);
  17.                 }       
  18.                 if ($role == 'admin') {
  19.                     $this->set('adminNavigation', $this->adminNavigation);
  20.                 }
  21.                 
  22.                 $this->set('userNavigation', $this->userNavigation);
  23.             } else {
  24.                 $this->set('clientNavigation', $this->clientNavigation);   
  25.             }
  26.            
  27.             $this->set('userRole', $role);
  28.         }
  29.  }

Now, I had an action in a controller where a "client" can view everything but a "user" could only view a specific action. On top of that any "user" who also had an "admin" role could view any action in that controller. In that particular controller I had to put in this code:

PHP:
  1. function beforeFilter() {
  2.         parent::beforeFilter();
  3.         $this->Auth->userModel = 'Client';
  4.         $this->Auth->loginAction = 'plugin/clients/login';
  5.         $this->Auth->fields = array('username' => 'email', 'password' => 'psword');
  6.         $this->Auth->authorize = 'controller';       
  7. }

In the latest version of the Auth component (from the bleeding edge of Cake 1.2.x.x) you can tell it to use an action in your controller for authorization. This is if you're lazy like me and don't want to use the Acl (which has a number of steps you need to go through just to get it up and running) or want something very simple. So, in the above beforeFilter() we override the parent beforeFilter() settings and tell Auth that we want to use our Client model as the source for verifying login credentials.

Now, I mentioned before that I had one action in the controller that could be viewed by the User and one action only. So, I added in the isAuthorized() method to my controller that I told Auth to look for:

PHP:
  1. function isAuthorized() {
  2.         if ($this->Auth->user('role')) {
  3.             if (strpos($this->action, CAKE_ADMIN) !== false && $this->Auth->user('role') == 'admin') {
  4.                 return true;
  5.             }
  6.            
  7.             if ($this->action == 'view') {
  8.                 return true;
  9.             }
  10.            
  11.             return false;
  12.         }
  13.        
  14.         return true;
  15.     }

I think the code is pretty self-explanatory.

Working with the Auth component taught me a lesson about frameworks in general: when you take the time to actually dig into the source code of a framework you will realize just how much work goes into some of the more "magic" functions. I mean, you tell CakePHP that you want to use the Auth component, set a few parameters and *BAM* it takes care of directing people to your login page and validating login names and passwords (even automatically encrypting passwords upon account creation!) against your specified model. Why wouldn't you want to use something like that?

Protecting Your PHP Code

WARNING: This post contains my own opinions and does not represent the views of my current employers

In the July 2007 issue of php|architect magazine there is an article called "Shell Shock" that advocates using products from Zend and IonCube to encode and protect your PHP applications. Here are some of the nuggets of wisdom from that article:

  • "Serious business demands closed source"
  • "Maybe your friend will let it (ed: meaning your code) sit on his server for a few years, then dig it up, make some modifications and re-release it as his own script"
  • "You prevent would-be attackers from deceiving you by first deceiving them, and you do this by veiling your plain-text source code"

This paragraph is my favourite of the whole article, and it appears at the end:

Many programmers are afraid of deception. They are
accustomed to code that is open, and they feel safer
around it. As a result, many programmers don’t even
check for errors, refusing to entertain the possibility
that the PHP functions they hold so dearly may betray
them one day. Then that day comes, and there’s an in-
explicable bug in the product that is the result of the
programmer’s failure to let go of the illusion of perfec-
tion. Perfection has a very high price. What you really
want from your code is for it to work. You want it to
keep working under varied conditions, and you want it
to keep working next week, next month and after the
next release of PHP. Give yourself all the help you can.

First I laughed when I read this article. Then I sighed. Then I got mad. Then I decided to write this blog post.

To start, I will focus on the paragraph above. What I get out of that is that if only your source was closed and hidden from prying eyes, it would not have bugs in it. Which is, of course, total nonsense. Code has bugs because it's open and they feel safer? There are two kinds of bugs: application bugs (which is the code I would write) and system bugs (in this case, bugs that that appear from PHP itself). I'm sorry, but there is nothing I can do if there is a bug in PHP that causes my application to crash except to point this bug out to the people who have the ability to fix it. That paragraph I quoted doesn't seem to make sense to me at all. I fail to see how encoding my code makes it somehow safer and less vulnerable to bugs from either the application level or system level. Perhaps I'm reading something that isn't there, or have taken that paragraph out of context.

Look, I understand that people want to make money off of the code that they create. This is how I have been making my living for almost 10 years now, writing code for companies and they pay me for doing it. Over time I have come to realize that what I am being paid for now is not just the code I create, but the knowledge that goes with that code as well. Unless I've signed a non-compete agreement, I can pretty much reproduce any code I've written in the past and probably do it better now.

Having said that, I haven't been in the business of creating an application that I sell to other people. Well, that's not entirely true. Way back in the day I wrote a parser for use with a system run by Canada Post called eParcel. You'd send it the size of the object you're trying to ship and it would spit back XML with data in it about what size box they recommend and how much it would cost to send the item where you were going. I sold exactly one copy of it. I created a license that said they could use my code on as many of their own sites as they wanted but they couldn't give the code to anyone else. I got paid for the license and that was the end of it as far as I was concerned.

Was I worried they would give my code to someone else? Not really. I figured if they wanted to do it bad enough, they would do it and no license would stop them from doing so. How many unregistered copies of the various versions of Microsoft's operating systems are out there? See my point? Microsoft made all their money selling programs that ran on their operating system. Did it matter that there were so many unauthorized copies running out there? Sure, but not as much because they were making money elsewhere.

So, encoding / encrypting / hiding your source code won't stop those who really want to get at it. I'm pretty sure a quick search on google will let me find tools to break through the encryption and then I've got what I want. And where does that leave you? The same place as those who didn't encrypt their source code. I know this will come as a blow to the ego of many programmers, but the stuff you've written probably isn't so earth-shattering as you think it is. Besides, somebody out there probably already holds a patent on what you've dreamed up and also probably has a lawyer or two laying around. ;)

Another reason I dislike encoding / encrypting is that it prevents you from fixing flaws in the software you've purchased if you are able to do so. Real world example: my previous employer Verticalscope purchased licenses of a mailing list manager that came highly recommended by an employee. It was written in PHP. There were some bugs in the application, bugs that could've fixed if they had access to the source code. Bugs that could be seen in the error logs. Instead, it was encrypted with IonCode's encoder and they were at the mercy of the creators of the application as to if and when bugs would be fixed. Time and money wasted...a lot more money than what the product cost them.

If you charge money for your application and it is good, PEOPLE WILL PAY YOU FOR IT EVEN IF THEY COULD STEAL IT. If you want to be like the record industry and treat all your customers like potential criminals, then you shouldn't be surprised when people get mad at you and do everything they can to steal your application and distribute it as far and wide as they can. What you want people to do is pay you for what you know about getting that application running (otherwise known as a service contract) and let the people who want to do it themselves (for whatever reason) be on their own when it comes to support or changes.

Some people will steal your stuff no matter what, so you can always build that into the price of your product (don't think Microsoft isn't doing that?) if you are so concerned. I say don't worry about your "intellectual property" in the form of your PHP application being stolen if you are making it available to other people. You're smart enough to find another ways to make money off it, right?

Now, I believe in respecting the "intellectual property rights" of others because ripping off someone's stuff and claiming it as your own is just about as dishonest an intellectual act as there is. I just feel that deception gives you a false sense of security. If your stuff is good, people will want it and many of them will pay you for it too. Even if they can get the source code and fix it themselves if a problem occurs.

Trust me, even if my sole source of income was selling a PHP application that I had written I would feel the same. The author of that article probably makes his living selling applications he wrote, and has chosen the "security by obscurity" path when it comes to securing his application. Encode your stuff if you want, but be aware that the minute you choose to do that you are telling your customers "I don't trust you" and I have a hard time understanding a business model that assumes people are going to want to steal the stuff you sell. My policy would be simple: if you buy it from me, I'll be happy to give you as much support as I reasonable can. If you steal it from me or can't prove that you bought a copy from me (which is pretty much the same thing) then you can expect a hearty "screw you, call me when you are willing to pay for your copy" in response to your requests. I'm sure my regular visitors will agree that seems to be more in line with my personality.

Opinionated Software, The Podcast

A while back I was contacted by Cal Evans over at the Zend Developer Zone to contribute to a new podcast he was starting up called PHP Abstract. Very cool idea, a small 5 to 7 minute podcast by PHP developers where they talk about a topic of interest to them (and hoepfully to you). Well, being one to never miss out on an oppurtinity to promote myself I agreed to contribute what I could. I settled on a brief talk about opinionated software. Also known as "convention over configuration" by the Ruby on Rails crowd.

Go over and have a listen and don't be shy in sharing your feedback via the comments there or here. Opionated software / convention over configuration / tools with rules is a powerful yet misunderstood programming practice.

Pretty Please, Make Your Code Testable

A lot of open source applications suffer from the twin demons of lack of extensive documentation and not enough tests. Yes, I've mentioned many times in this blog how important I feel having tests built right into your application is. Not only do you approach creating brand-new functionality differently when you write the tests before you write a line of code, you make it incredibly easy to extend existing code because when you change something, all you have to do is run the tests again. CakePHP is aiming to have 100% coverage for tests. It's a noble goal because I've come around to the idea that if you can't test your code automatically, you shouldn't be writing your code that way.

Anyhow, back on topic. I've been trying to write some tests for a component that is, well, a little alpha at this point. Digging into the code has revealed that it is hopelessly tied into another component. This is only a bad thing because it makes isolating the original component for testing purposes pretty much a hopeless task that I would delegate to my minions if I had any. Which I don't. Not yet anyway.

I am by no means an expert tester as I have been known to be quite hypocritical at times with my own code in that I fail to create tests to verify that the crap I'm actually writing is working the way that it is supposed to. But I can say that when I was working on the IPTV project I wrote all sorts of tests for the command-line scripts that I wrote and that the coverage was good. I can quibble with CakePHP's choice of using SimpleTest for the built-in testing suite (I'm a PHPUnit guy myself) but at least they have testing built right in. Bake even creates the shells of the tests for you. But anyway, I am straying once again off-topic.

What does code that is easy to test look like? I think it boils down to two ideas:

  • It isn't tied into another component for it's existence
  • The code itself uses method, function and variable names that mean you don't have to actually read the comments to figure out what's going on

It isn't tied into another component for it's existence. This is the number one problem I see in a lot of code that needs to be tested. Component A relies on functions inside component B...and it's impossible to pretend to send info from component B to component A. There's a concept I learned from my favourite programming book "The Pragmatic Programmer" called the Law of Demeter. Basically, it's the idea that a an object should assume as little as possible about the other objects that it has to interact with you. The downside (as mentioned in the Wikipedia entry) is that you end up having to write a lot of wrapper code so you can keep those objects from knowing too much about each other. But the upshot is that you will have to learn the awesome testing concept known as Mock Objects in order to make your tests work. Here's an example of what I'm talking about:

We have object A that uses object B, which is a database abstraction component. Now, as long as Object A doesn't use Object B to access methods or information from Object C (think of object chaining: $this->A->B->foo() is bad for testing) then you can easily test Object A by creating a mock object that represents Object B (and controlling the responses from Object B under testing). Otherwise, how can you simulate the results of your call to $this->A->B->foo()? You can't.

The code itself uses method, function and variable names that mean you don't have to actually read the comments to figure out what's going on. I'm of the programming school where you comment WHY you did something as opposed to WHAT you actually did. If you're doing some weird of bit shifting followed by a little-used algorithm for figuring out the best path for travelling along 7 points, well, I expect the comments to say WHY you're doing what you've done and it will be up to me to decipher the code itself so I understand HOW you did it. Luckily for me, CakePHP is filled with lots of well-named methods and variables and I'd like to think I've got enough programming experience that I can figure out HOW something is done but I often want to know WHY we're doing that. I've had several Skype conversations with my fellow CakePHP developers asking why something was being done so I could figure out how to modify it without breaking stuff. I added functionality to the email component so it would actually send out stuff via SMTP (it currently doesn't do it) and found that the code itself was pretty readable and I didn't require a lot of extra work to figure out how to extend it the way I wanted to go. That, my friends, is a good example of code that is easy to read and easy to understand.

Phew, that's a lot of stuff to dump out of my brain into the blog late on a Friday night. So, to bring things around full circle I think it's very important to keep in mind that when you are writing code you also need to be thinking about how you would actually test that code. Even more importantly, you need to think about what happens if someone other than you has to come along and has to change anything. When your contributing to a project with the impact like CakePHP can have, well, you better damn well make sure that if you never contribute another line of code to the project that other people can take what you've done and keep building on it. It's fun to do all sorts of fun tricks in code (some of the stuff I saw during a job interview once reminded me of why I don't like cute code tricks) when it's just your own stuff. But to preserve my sanity, please make your code testable so that when I have to go and add something else to it I don't start screaming your name and pestering you via instant message, shaking a virtual fist at you.

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