uuid.inc 3.65 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
<?php

/**
 * @file
 * Handling of universally unique identifiers.
 */

/**
 * Interface that defines a UUID backend.
 */
interface UuidInterface {

  /**
   * Generates a Universally Unique IDentifier (UUID).
   *
   * @return
   *   A 32 byte integer represented as a hex string formatted with 4 hypens.
   */
  public function generate();

}

/**
 * Factory class for UUIDs.
 *
 * Determines which UUID implementation to use, and uses that to generate
 * and validate UUIDs.
 */
class Uuid {

  /**
   * Holds the UUID implementation.
   */
  protected $plugin;

  /**
   * This constructor instantiates the correct UUID object.
   */
  public function __construct() {
    $class = $this->determinePlugin();
    $this->plugin = new $class();
  }

  /**
   * Generates an universally unique identifier.
   *
   * @see UuidInterface::generate()
   */
  public function generate() {
    return $this->plugin->generate();
  }

  /**
   * Check that a string appears to be in the format of a UUID.
   *
   * Plugins should not implement validation, since UUIDs should be in a
   * consistent format across all plugins.
   *
   * @param $uuid
   *   The string to test.
   *
   * @return
   *   TRUE if the string is well formed.
   */
  public function isValid($uuid) {
    return preg_match("/^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/", $uuid);
  }

  /**
   * Determines the optimal implementation to use for generating UUIDs.
   *
   * The selection is made based on the enabled PHP extensions with the
   * most performant available option chosen.
   *
   * @return
   *  The class name for the optimal UUID generator.
   */
  protected function determinePlugin() {
    static $plugin;
    if (!empty($plugin)) {
      return $plugin;
    }

    $plugin = 'UuidPhp';

    // Debian/Ubuntu uses the (broken) OSSP extension as their UUID
    // implementation. The OSSP implementation is not compatible with the
    // PECL functions.
    if (function_exists('uuid_create') && !function_exists('uuid_make')) {
      $plugin = 'UuidPecl';
    }
    // Try to use the COM implementation for Windows users.
    elseif (function_exists('com_create_guid')) {
      $plugin = 'UuidCom';
    }
    return $plugin;
  }
}

/**
 * UUID implementation using the PECL extension.
 */
class UuidPecl implements UuidInterface {
  public function generate() {
    return uuid_create(UUID_TYPE_DEFAULT);
  }
}

/**
 * UUID implementation using the Windows internal GUID extension.
 *
 * @see http://php.net/com_create_guid
 */
class UuidCom implements UuidInterface {
  public function generate() {
    // Remove {} wrapper and make lower case to keep result consistent.
    return drupal_strtolower(trim(com_create_guid(), '{}'));
  }
}

/**
 * Generates an UUID v4 using PHP code.
 *
 * Loosely based on Ruby's UUIDTools generate_random logic.
 *
 * @see http://uuidtools.rubyforge.org/api/classes/UUIDTools/UUID.html
 */
class UuidPhp implements UuidInterface {
  public function generate() {
    $hex = substr(hash('sha256', drupal_random_bytes(16)), 0, 32);

    // The field names refer to RFC 4122 section 4.1.2.
    $time_low = substr($hex, 0, 8);
    $time_mid = substr($hex, 8, 4);

    $time_hi_and_version = base_convert(substr($hex, 12, 4), 16, 10);
    $time_hi_and_version &= 0x0FFF;
    $time_hi_and_version |= (4 << 12);

    $clock_seq_hi_and_reserved = base_convert(substr($hex, 16, 4), 16, 10);
    $clock_seq_hi_and_reserved &= 0x3F;
    $clock_seq_hi_and_reserved |= 0x80;

    $clock_seq_low = substr($hex, 20, 2);
    $nodes = substr($hex, 20);

    $uuid = sprintf('%s-%s-%04x-%02x%02x-%s',
      $time_low, $time_mid,
      $time_hi_and_version, $clock_seq_hi_and_reserved,
      $clock_seq_low, $nodes);

    return $uuid;
  }
}