Advertisement
Guest User

Untitled

a guest
Feb 11th, 2016
43
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.06 KB | None | 0 0
  1. <?php
  2. /**
  3. * Fancy ID generator that creates 20-character string identifiers with the following properties:
  4. *
  5. * 1. They're based on timestamp so that they sort *after* any existing ids.
  6. * 2. They contain 72-bits of random data after the timestamp so that IDs won't collide with other clients' IDs.
  7. * 3. They sort *lexicographically* (so the timestamp is converted to characters that will sort properly).
  8. * 4. They're monotonically increasing. Even if you generate more than one in the same timestamp, the
  9. * latter ones will sort after the former ones. We do this by using the previous random bits
  10. * but "incrementing" them by 1 (only in the case of a timestamp collision).
  11. */
  12. class PushId
  13. {
  14. /**
  15. * Modeled after base64 web-safe chars, but ordered by ASCII.
  16. *
  17. * @var string
  18. */
  19. const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
  20. /**
  21. * Timestamp of last push, used to prevent local collisions if you push twice in one ms.
  22. *
  23. * @var int
  24. */
  25. private static $lastPushTime = 0;
  26. /**
  27. * We generate 72-bits of randomness which get turned into 12 characters and appended to the
  28. * timestamp to prevent collisions with other clients. We store the last characters we
  29. * generated because in the event of a collision, we'll use those same characters except
  30. * "incremented" by one.
  31. *
  32. * @var array
  33. */
  34. private static $lastRandChars = [];
  35. /**
  36. * @return string
  37. */
  38. public static function generate()
  39. {
  40. $now = (int) microtime(true) * 1000;
  41. $isDuplicateTime = ($now === static::$lastPushTime);
  42. static::$lastPushTime = $now;
  43. $timeStampChars = new \SplFixedArray(8);
  44. for ($i = 7; $i >= 0; $i--) {
  45. $timeStampChars[$i] = substr(self::PUSH_CHARS, $now % 64, 1);
  46. // NOTE: Can't use << here because javascript will convert to int and lose the upper bits.
  47. $now = (int) floor($now / 64);
  48. }
  49. static::assert($now === 0, 'We should have converted the entire timestamp.');
  50. $id = implode('', $timeStampChars->toArray());
  51. if (!$isDuplicateTime) {
  52. for ($i = 0; $i < 12; $i++) {
  53. static::$lastRandChars[$i] = (int) floor(rand(0, 63));
  54. }
  55. } else {
  56. // If the timestamp hasn't changed since last push, use the same random number, except incremented by 1.
  57. for ($i = 11; $i >= 0 && static::$lastRandChars[$i] === 63; $i--) {
  58. static::$lastRandChars[$i] = 0;
  59. }
  60. static::$lastRandChars[$i]++;
  61. }
  62.  
  63. for ($i = 0; $i < 12; $i++) {
  64. $id .= substr(self::PUSH_CHARS, static::$lastRandChars[$i], 1);
  65. }
  66. static::assert(strlen($id) === 20, 'Length should be 20.');
  67. return $id;
  68. }
  69.  
  70. /**
  71. * @param $condition
  72. * @param string $message
  73. * @throws \RuntimeException
  74. */
  75. private static function assert($condition, $message = '')
  76. {
  77. if ($condition !== true) {
  78. throw new \RuntimeException($message);
  79. }
  80. }
  81. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement