Pathological PHP

You know what annoys me? People with crazy ideas. Especially when they pimp them like crazy.

That's why this tutorial on "code separation" from a web forum I occasionally visit annoys me so much. The author links to this thing like his life depends on it. Whenever somebody has the nerve to post a code snippet that has both HTML and PHP in it, he brings it up. Even if the code is just echoing a variable inside an HTML block. It's ridiculous.

Don't get the wrong idea - separation of concerns is obviously a good thing. If you're outputting HTML and querying the database in the same file, you're doing things wrong. But this guy takes it to absurd lengths and insists that you should never have any PHP code mixed in with your HTML. Not even echo statements.

The real kicker is the content of this "tutorial". It's basically a half-baked template system that does nothing but string replacement of pre-defined placeholders. At best, it grossly over simplifies the problem. I suppose it does demonstrate that it's possible to output a page without having HTML and PHP in the same file (as if anyone really doubted that), but that's about it.

The thing that really bothers me about this approach is that the author promotes it results in code that's easier to understand than code that has both PHP and HTML in it. Except that it's not. The guy apparently just has a pathological fear of having two different languages in the same file. it's completely irrational.

The problem with his approach is that it doesn't actually solve the problem, but just moves it. Sure, basic replacement like that it's fine for simple cases, but as soon as the requirements for your markup get more complicated, things blow up. For example, how do you do conditionals? Well, you have another template file and you do a check in your controller for which one to inject into the page. What about loops? Well, you have a template file for the loop content and you run the actual loop in your controller, build up the output, and inject that into the page.

The net result? What would normally be a fairly simple page consisting of one template with a loop and two conditionals is now spread across six template (one main template, one for the loop body, and two for each if statement) and pushes all the display logic into the controller. So instead of one "messy" template to sort through, you now have a seven-file maze that accomplishes the same thing.

I find it difficult to see how this is any sort of improvement. At best, it's just trading one type of complexity for another in the name of some abstract principle that mixing code and markup is evil. Of course, if you want to follow that principle, you could always go the sane route and just use something like Smarty instead. But let's be honest - that's just using PHP code with a slightly different syntax. It may be useful in some cases, but it's not really fundamentally different from just writing your template files in PHP.

Personally, I've come to be a believer in keeping things simple. PHP is a template system. It was originally designed as a template system. It's good at it. So there's no need to introduce additional layers of template abstraction - just stick with PHP. There may be cases where things like Smarty are useful, but they're far from necessary. And the half-baked templating systems like those advocated in that tutorial are just intellectual abortions. There's no need to reinvent a less functional version of the wheel when you can just use a working, tested wheel.

More Mercurial hooks - well that was confusing

OK, that was a little confusing.

So after my initial attempt at a Mercurial hook to connect my public repository to my Mantis instance, I decided to rearrange things a little. Initially, I was using a "commit" hook on my local machine. However, for the public bug tracker with the public repository, it made more sense to put the hook on the server. So I switched it to an "incoming" hook in my hgweb.config.

Actually converting the script to work with my Linux based web host was pretty easy. Just one command, really:

#!/bin/bash

basedir='/path/to/base/hg/install/dir'
mantis='/path/to/mantis/wwwroot/scripts/checkin.php'
hg="$basedir/hg"

$hg log -vr $HG_NODE --style $basedir/mercurial/templates/multiline | php $mantis

Nice and simple, right?

The hard/annoying part was in the style template file. Seem I wanted my commit message to have multiple lines and indent the file list, like this:

Changeset 85:5f7504e02f1c by Peter Geer, Mon Jan 17 23:12:46 2011 -0500
Updated plugin for PHP5 and fixed blog root when taken from defined constant (fix bug 0000021).
lib/blog.php
plugins/menubar_breadcrumbs.php

Problem was, it just didn't work. I was using exactly the same template text as in the old hook script, but I was getting everything on the same line. By switching from "\n" to "\r\n" as my newline escape code, I was able to get newlines to work, but the indentation still didn't.

Guess what fixed it - switching from double to single quotes.

changeset = 'Changeset {branches}{rev}:{node|short} by {author|person}, {date|date}\n{desc}\n{files} '
file = ' {file}\n'

Seriously. That's it. No mention of that in the manual - they're supposed to be the same. In fact, the example of this in the manual uses double-quotes.

Oddly enough, using double-quotes worked just fine on the command line. I'm wondering if it has something to do with the fact that I'm using hgweb or something. Either way, that really sucked.

New job - at home

Well, it's been a while since I've posted anything here. That's because I've been busy with a new job.

The new job is doing LAMP development for a major online community site. True to form, I'm not (yet) going to name the company. That policy has served me well so far. However, I will share some details of my new position and some of the challenges I've faced with it, both expected and unexpected.

The first innovation with regard to this job is it's location - my house. Yes, it's a 100% telecommute, work-from-home position. In fact, it's a totally distributed team, so everybody on the development works from home. And while that's great, so far I've actually found it to be one of the biggest challenges with the job. The challenge isn't motivating myself to actually work, which my mother thought it would be (seriously, she was concerned about that). In fact, quite the opposite. Until recently, I've had more problems with spending too much time working.

There are a three main reasons for this. Part of it is because I was still learning our system and felt like I had to "catch up" to the rest of the team. Another part is simply schedule diversity. We communicate primarily via Skype chatrooms, so we can when people are online. And since we have developers literally from California in the west to Russia in the east, many of them work non-standard schedules, there's always somebody "on", which adds some subconscious pressure. And lastly, of course, part of it is probably emotional damage from my last job. At that (highly dysfunctional) company, we were allowed to work from home on occasion, but if we did it too often, the CEO would make needling comments to the VP of Engineering suggesting that "working form home" was a code-word for "taking the day off". So I think I've partly been stuck in the mindset of trying to prove that, "No, really, I am actually working!"

The other new feature of this position is the sheer size of the operation. And I mean that in pretty much every conceivable way - the amount of traffic, the infrastructure, the size of the code-base, the size of the development team. In fact, pretty much the only thing that isn't big (to me) is the size of the overall company. By way of comparison:

  • The largest site I'd work on was when I was at ebaumsworld.com, where we had something like 30 servers in our environment and were ranked about 1200 in the world by Alexa. The current job has nearly 100 web servers alone and is ranked in the top 150 in the world by Alexa.
  • The largest code-base I'd worked on was at my last job - a little over 200K total LOC when you include unit tests. The new job has over 500K lines of just PHP code (that's not counting CSS or JavaScript) in just the web portion of the tree.
  • The largest group I'd worked in was when I was with the county IT department - about 30 people total, about 9 of them programmers, no collaborative projects to speak of. My last two teams have been very tight 4-man operations. At the new job, we have about 30 people on the tech team, all of them programmers and all of them very good.

So, needless to say, I've been very busy figuring things out in my new position. I've been there for about three months now and I think I'm finally getting comfortable. I'm still learning the code-base, of course (nobody understands the entire thing - it's just too big), but I'm getting more confident and feel like I'm making progress. So in other words, so far, so good.

And most importantly, I'm much happier than I was at my last job. It's nice to feel like you can trust your colleagues and that the work you're doing is helpful. Amazing how increased morale boosts productivity.

Developer interviews

At my current company, we've been trying to hire a new back-end PHP developer for some time. As the senior engineer on staff, and the only back-end person, it's my job to handle the technical portion of the screening process, including resume screening and technical interview. Unfortunately, I have lots of other work to do, so I never had time to come up with a really good procedure to test candidates. In addition, most of the interviews are over the phone with people in other cities, which makes it difficult to have them write code without some advanced preparation - which, again, I haven't had time to do.

So, as a compromise, I came up with a standard list of questions that I ask candidates. I originally wrote this list with the intention of using it for junior developers (1 to 3 years of experience), thinking that anyone with 5+ years of experience would breeze through it. However, I've found that a disturbingly large percentage of more experienced developers fumble more than one of the questions. Unfortunately, I'm not yet sure if that's just because we're getting poor quality applicants, or if it's because my standards are too high.

Therefore, in the interests of helping my fellow developers - both those looking for new opportunities and those who are interviewing the first group - as well as in the hopes of getting some good feedback, I present my list of interview questions. Note that I do not include answers. There are two reasons for this:
1) There is not always a single "right" answer - it is perfectly valid for the question to turn into a conversation.
2) If you're an interviewer and you don't already know the answer, you shouldn't be asking the question; if you're an interviewee and you don't know the answer, you should look it up and learn something new.
With each question I've included some notes on the question's intent. Some of these are conceptual, some are simply tests of knowledge, and some are spring-boards to further discussion.

  1. Solve the FizzBuzz problem. How would you solve it in an object-oriented fashion? Using recursion?
  2. OK, I hardly ever ask this question; you really need to be in the same room to make it work. However, it's still useful to see if the candidate can program and to see how they think. For the object-oriented and recursive versions, I'm not so much looking for the right answer as I am for the approach - and I tell them as much. In particular, I'm looking to see where the candidate goes with the open-ended requirement for object-orientation. Do they go for procedural code that just happens to use some objects? Or do they go straight to polymorphic classes, maybe with a factory pattern thrown in?
  3. Define "inheritance" and "polymorphism". Give an example of when you might use each.
  4. This is your "OOP 101" question. Any half-competent developer should be able to describe inheritance and give the stock "shape" or "animal" example. Polymorphism is a bit more slippery - I often don't get a good answer to that part of the question. In fact, a lot of people seem to think that polymorphism is basically the same thing as method overloading and don't really make the connection to inheritance.
  5. What is the difference between an "abstract class" and an "interface"?
  6. I actually ask this because one of our products is built primarily in FLEX, and interfaces abound in our ActionScript code. With this, I'm really interested in seeing if the candidates understands the idea of explicitly specifying a contract in the code, as opposed to relying on, e.g., PHP's loose typing to make things "just work".
  7. What is "SQL injection" and how might you protect against it? What about XSS/CSRF?
  8. This is, of course, the basic security question. On the SQL injection side, I'm specifically looking for awareness of prepared statements, e.g. in MySQLi or PDO. More generallly, I'm looking for an awareness of filtering an validation. On the script injection side, I like to see what candidates say they use to filter input. An awareness of standard escaping and sanitizing function (strip_tags(), htmlentities(), etc.) is always good - knowledge of the filter extension gets bonus points.
  9. In JavaScript, what does it mean to say that functions are "first class" or "first order" objects?
  10. This is a lead-in to determine how much, if anything, the candidate knows about the more functional aspects of JavaScript - anonymous functions, closures, etc. Surprisingly (at least to me), hardly anyone seems to be familiar with the terminology of "first class" entities. So far I've only had two people know what I was talking about right off the bat.
  11. What is the difference between NULL and the empty string in SQL?
  12. This one is more of a trivia question to test the depth of knowledge of SQL. Bonus points if the candidate mentions SQL's three-valued logic in the follow-up question: "What is the result of the comparison NULL = ''"?
  13. What is an "index" in a database and how would you decide what columns to index?
  14. This one is just to check that the candidate has some basic knowledge of database performance considerations.
  15. Given a sample HR database that contains the following tables:
  16. Employees (employee_id INTEGER PRIMARY KEY, employee_name TEXT, ...)
    Departments (department_id INTEGER PRIMARY KEY, department_name TEXT, ...)

    What would you have to add to the schema to associate employees with departments?

    This is the basic SQL competency question. The typical answer is to add a foreign key from Employees to Departments, at which point I spring the "what if an employee needs to be in two departments" scenario and see if they come up with a join table. Also, it gives a good opportunity to see if they understand when to use inner versus outer joins. It's surprising how many people struggle with this question.
  17. Describe a project you worked on that was particularly enjoyable or interesting. What did you like about it?
  18. I stole this from an interview I did once where I was the applicant. It can be enlightening to see what catches a person's interest. If nothing else, it can help you get an idea of where their head is; are they a hard-core CS person, a touchy-feely UI person, or something else entirely?

Personally, I consider this list to be inadequate at best. But as I said, I haven't had time to develop an adequate test and this is at least better than asking "So how well do you know X?", if for no other reason than it's harder to BS an acceptable answer. Any thoughts or opinions are welcome.

Command-line shortcuts

I came across an interesting little program the other day. It's called Go. It's a Python script for quickly navigating directories via what are essentially command-line shortcuts. I discovered it while perusing the settings in my Komodo Edit preferences - it was mentioned in the settings for the Fast Open extension, which I believe is included by default as of Komodo 5.1.
komodo-fast-open.png
The beautiful thing about Go is how simple it is. You run a simple command to set an alias and from there on out, you can just type go alias and it will change you to that directory. You can also add paths after the alias, such as go alias/dir1/dir2 to switch to a subdirectory of the alias. Great for switching between deep hierarchies, like how Windows programs like to bury things three levels under your "Documents" directory.

However, as I played around with Go, I did come across a few annoyances. The biggest one, or course, was that it didn't work under Powershell. The go.bat wrapper script would run...and do nothing. The current directory stayed the same. Turns out this was because go uses a driver-based system for changing directory which is based on your current shell. The Windows driver was using batch file syntax and running cmd.exe. Powershell does this in a new process, so naturally the current directory wasn't changing.

So, in the spirit of open-source, I decided to fix that problem. And while I was at it, I fixed a couple of other things and implemented some of the feature requests posted on the Google Code issue tracker. Here's the quick list:

  • Added support for Powershell.
  • Added built-in shortcut "-" pointing to the OLDPWD environment variable. On Windows, Go handles setting this variable (on UNIX, "cd" takes care of that).
  • When invoked without any argument, change to home directory.
  • Resolve unique prefixes of shortcut, e.g "pr" resolves to "projects" if there's no other shortcut starting with "pr".
  • Made -o option work without having the win32api bindings installed.

Below are a patch file for the official Go 1.2.1 release (apply with patch -p2 < go-posh.patch) as well as a fully patched setup archive for those who just want to get up and running. Note that, to enable Powershell support, prior to running the Go setup, you'll need to set the SHELL environment variable so that Go knows to use Powershell. You can do this by adding $env:SHELL = "powershell" to your Powershell profile script.

Patch file: go-posh-1.2.1.patch
Full Go archive: go-posh-1.2.1.zip

Edit: Fixed support for "-" so that it actually works like it does in UNIX.

Edit again: I've added a few more small features and also created a project for this patch in my bug tracker.