title: Generate a readable random unique ID
body: <p>I need to generate unique transaction IDs. The system allows digits, letters, hyphens, and underscores, but not full stops.</p>
  
  <p>The function I’m currently using is,</p>
  
  <pre><code>generate_id() {
      $id = uniqid('P_', true);
      $id = str_replace('.', '-', $id);
      return $id;
  }
  </code></pre>
  
  <p>Sample output:</p>
  
  <blockquote>
    <p>P_551171d3dd8a93-57457934</p>
  </blockquote>
  
  <p>However, I have recently found that (a) <code>uniqid()</code> isn’t necessarily unique, and (b) my customer occasionally needs to retype these IDs, compare them, search for them, etc. The current IDs are not very readable, and are difficult to eyeball. (I had thought that these were purely for internal use in the payment API. I only recently realised that the customer was actually reading the things.)</p>
  
  <p>So I was thinking of changing to this,</p>
  
  <pre><code>function generate_id() {
      $s = '';
      for ($i = 0; $i &lt; 5; $i++) {
          if ($s) {
              $s .= '-';
          }
          $s .= bin2hex(openssl_random_pseudo_bytes(2));
      }
      return $s;
  }
  </code></pre>
  
  <p>Sample output:</p>
  
  <blockquote>
    <p>ced7-1cef-5331-193c-907d</p>
  </blockquote>
  
  <p><code>openssl_random_pseudo_bytes()</code> is the <em>recommended</em> way to generate random unique identifiers, but I fear that doing it four times and concatinating (which I’m doing only because that’s the easiest way to split it up with hyphens for ease of reading) might compromise it. Should I generate one long string and then manipulate it to add hyphens?</p>
  
  <p>Or, indeed, should I do something else altogether? I don’t really mind what the transaction ID looks like, so long as it’s unique and relatively easy to eyeball or retype.</p>