Question: I was wondering if using `substr(md5(rand()), 0, 17);` would be safe enough for a password reset link? If I was to generate a longer string would that make it any safer? Is MD5 at all safe? Or should I do `$token = sha1(uniqid($username, true));`?

Answer: 
The use of `substr()` or `md5()` is secondary to the use of `rand()`.

The whole point of using password reset tokens is that they're unpredictable and `rand()` is known to be weak due to the underlying LCG model.

It would be a better idea to use the system's entropy source instead, e.g.:

```
$rand = openssl_random_pseudo_bytes(8); // take 8 random bytes
$token = substr(md5($rand), 0, 17);

```

It takes bytes from the system's random source, e.g. `/dev/urandom` on Linux or the corresponding system for Windows.

**Note** that if you don't have any particular size constraint you might as well choose a full `sha1()` output and take 16 random bytes.

Also, you should treat password reset tokens as if they were (temporary, time limited) passwords when you store them in your database; I would suggest to send above token to the user and then use [`password_hash()`](http://php.net/password_hash) before you write them in the database. At a later stage you check the given token (assuming it's not expired) using `password_verify()`.
For a random hash it's safe enough. Your issue would be collisions and the first 17 characters of a MD5 on a random value should be random enough to avoid them on a light duty project.

I would pick `uniqid()` with extra entropy over `rand()` (and maybe even `mt_rand()`).

I wouldn't use `MD5` or `SHA1` for storing your passwords, however.