How to create a MongoDB ObjectID from a timestamp using PHP

This is a useful tidbit of code to have until PHP adds this feature into their MongoDB extension. I stumbled on an example on Stack Overflow illustration showing how to do this and rewrote it in what I think is a slightly more modern way that does the same thing.

ObjectId‘s are constructed using a 4-byte timestamp value, 3-byte machine identifier, 2-byte process id, and a 3-byte counter which create a 12-byte BSON type:


 --------------------------------------------------
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
|---------------|-----------|-------|--------------|
| time          | machine   | pid   | counter      |
 --------------------------------------------------

Knowing that makes this PHP code easy to understand:

MongoUtil:

/**
 * Class MongoUtil.
 */
class MongoUtil {

	/**
	 * This is needed to keep documents unique that have the same timestamp.
	 * @var integer
	 * @see $timestamp
	 */
	public static $_mongoIdFromTimestampCounter = 0;

	/**
	 * Mongo Id From Timestamp
	 * @param integer $timestamp
	 * @return MongoID
	 * @see http://docs.mongodb.org/manual/reference/object-id/
	 */
	public static function mongoIdFromTimestamp( $timestamp ) {
		// Build Binary Id
		$binaryTimestamp = pack('N', $timestamp); // unsigned long
		$machineId = substr(md5(gethostname()), 0, 3); // 3 bit machine identifier
		$binaryPID = pack('n', posix_getpid()); // unsigned short
		$counter = substr(pack('N', self::$_mongoIdFromTimestampCounter++), 1, 3); // Counter
		
		$binaryId = "{$binaryTimestamp}{$machineId}{$binaryPID}{$counter}";

		// Convert to ASCII
		$id = '';
		for ($i = 0; $i < 12; $i++) {
			$id .= sprintf("%02X", ord($binaryId[$i]));
		}

		// Return Mongo ID
		return new MongoID($id);
	}

}

Example of usage:

$date = strtotime('today');
$mongoId = MongoUtil::mongoIdFromTimestamp($date);

// Do more stuff.
Mar 31, 2014 Programming