MD5 is not the best hash to use nowadays for security nowadays; hashes can be computed too quickly (though the biggest flaw with md5 is the ease in generating collisions). Its only 128 bits (16^32 = 2^128 ~ 10^38); try sha-256 (2^256 ~ 10^77; its 2^128 times more keys) or sha-512. Key strengthening is a good practice (e.g., it takes 20 times longer to generate a rainbow table); but still 20 times longer isn't that long (e.g., use 20 machines and it takes just as long), but is done best with a random salt. Would be much better to key strengthen say 100000 times.
$password = "hello";
$salt = random_str(); // generate some relatively short random str
$password = sha256($password);
$password = sha256($salt + $password);
$sep = "|";
$password_scheme = "SHA256x100k";
$password = $salt + $sep + $password_scheme + $sep + $password;
Using a pseudo-code language where
+ concatenates strings and random_str() is a function that generates a short random string. The purpose of the random salt is that if an attacker sees your source code, sees your password-hashes, they need to do a generate a separate rainbow table for each different salt (or one for each password). So now, instead of just having to generate one rainbow table to get all the users passwords, they have to generate a rainbow table to get just one password. Also its a good idea to document the password scheme in the hash, so you may update it as necessary e.g., migrate from SHA256x100k to SHA256x1k if you need to be less CPU intensive or decide to move to a different hash later.
Granted its not the best idea to come up with your own custom crypto-method if you aren't a crypto expert. You definitely leave the opportunity for subtle security holes like timing attacks even with seemingly secure algorithms. bcrypt is probably your best bet.
Note: MD5 is particularly vulnerable to collision attacks, but you don't really have to worry about collisions in preimage attacks (which is the method of attack against password hashes). An attacker got a list of password hashes (h) somehow, and learned the hash routine (md5 x 20 or salted sha256 x 100k), and is trying to get any message m, such that hash_routine(m) = h, to let them into your system.
What collision vulnerability makes you worry about is that if you have a hash(m1) = hash(m2) when m1 != m2; so if you download something where people have checked that file m1 is safe and want to make sure that you actually downloaded m1 you compare hash(m1) to the public md5 hash. If there's a malicious version m2 with the same md5 hash then you cannot be sure m1 is safe by checking its md5 sum.