spl_object_hash returns a 32-character string that looks like a digest and is really an encoded pointer, and pointers get reused — so the identifier of a garbage-collected object can be handed to a completely different one.
$a = new stdClass();
$key = spl_object_hash($a);
unset($a);
$b = new stdClass();
var_dump(spl_object_hash($b) === $key); // may well be true
// 7.2
$id = spl_object_id($b); // an int, and honest about what it is
The collision is not theoretical — a long-running worker holding a map keyed by hash will eventually attribute one object’s state to another, and the bug is untraceable because the map looks correct. Neither function keeps the object alive, so anything using them as a registry needs to hold a reference itself. SplObjectStorage does exactly that and is the right answer nine times out of ten.