1: <?php
2: namespace NGS;
3:
4: require_once(__DIR__.'/Utils.php');
5:
6: use NGS\Utils;
7:
8: 9: 10:
11: class UUID
12: {
13: 14: 15:
16: private $value;
17:
18: 19: 20: 21: 22: 23:
24: public function __construct($value = null)
25: {
26: if (null === $value) {
27: $this->value = self::_v4();
28: }
29: elseif (is_string($value)) {
30: if (!self::isValid($value)) {
31: throw new \InvalidArgumentException('UUID could not be constructed from invalid value: "'.$value.'"');
32: }
33: $this->value = $value;
34: }
35: elseif ($value instanceof \NGS\UUID) {
36: $this->value = $value->value;
37: }
38: else {
39: throw new \InvalidArgumentException('UUID could not be constructed from type "'.gettype($value).'"');
40: }
41: }
42:
43: 44: 45: 46: 47: 48: 49: 50:
51: public static function toArray(array $items, $allowNullValues=false)
52: {
53: $results = array();
54: try {
55: foreach ($items as $key => $val) {
56: if ($allowNullValues && $val===null) {
57: $results[] = null;
58: } elseif ($val === null) {
59: throw new \InvalidArgumentException('Null value found in provided array');
60: } elseif(!$val instanceof \NGS\UUID) {
61: $results[] = new \NGS\UUID($val);
62: } else {
63: $results[] = $val;
64: }
65: }
66: }
67: catch(\Exception $e) {
68: throw new \InvalidArgumentException('Element at index '.$key.' could not be converted to UUID!', 42, $e);
69: }
70: return $results;
71: }
72:
73: 74: 75: 76: 77: 78:
79: public function __get($name)
80: {
81: if($name==='value') {
82: return $this->value;
83: }
84: else {
85: throw new \InvalidArgumentException('UUID: Cannot get undefined property "'.$name.'"');
86: }
87: }
88:
89: 90: 91: 92: 93: 94: 95:
96: public static function v3($namespace, $name)
97: {
98: if ($namespace instanceof \NGS\UUID) {
99: $namespace = $namespace->value;
100: }
101: elseif (!is_string($namespace)) {
102: throw new \InvalidArgumentException('Cannot create uuid v3 from invalid namespace type: "'.Utils::getType($namespace).'"');
103: }
104: if(!is_string($name)) {
105: throw new \InvalidArgumentException('Cannot create uuid v3 from invalid name type: "'.Utils::getType($namespace).'"');
106: }
107: return new UUID(self::_v3($namespace, $name));
108: }
109:
110: 111: 112: 113:
114: public static function v4()
115: {
116: return new UUID(self::_v4());
117: }
118:
119: 120: 121: 122: 123: 124: 125:
126: public static function v5($namespace, $name)
127: {
128: if ($namespace instanceof \NGS\UUID) {
129: $namespace = $namespace->value;
130: }
131: elseif (!is_string($namespace)) {
132: throw new \InvalidArgumentException('Cannot create uuid v5 from invalid namespace type: "'.Utils::getType($namespace).'"');
133: }
134: if(!is_string($name)) {
135: throw new \InvalidArgumentException('Cannot create uuid v5 from invalid name type: "'.Utils::getType($namespace).'"');
136: }
137: return new UUID(self::_v5($namespace, $name));
138: }
139:
140: 141: 142: 143:
144: public function __toString()
145: {
146: return $this->value;
147: }
148:
149: 150: 151: 152: 153:
154: public static function isValid($uuid)
155: {
156: if (!is_string($uuid)) {
157: throw new \InvalidArgumentException('UUID value was not a string, type was: '.\NGS\Utils::gettype($uuid));
158: }
159: return preg_match('/^\{?[0-9a-f]{8}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?'.
160: '[0-9a-f]{4}\-?[0-9a-f]{12}\}?$/i', $uuid) === 1;
161: }
162:
163: 164: 165: 166: 167: 168: 169: 170: 171: 172:
173: private static function _v3($namespace, $name)
174: {
175: if(!self::isValid($namespace)) {
176: throw new \InvalidArgumentException('Cannot create uuid v3 from invalid namespace with value: "'.$namespace.'"');
177: }
178:
179:
180: $nhex = str_replace(array('-','{','}'), '', $namespace);
181:
182:
183: $nstr = '';
184:
185:
186: for($i = 0; $i < strlen($nhex); $i+=2)
187: {
188: $nstr .= chr(hexdec($nhex[$i].$nhex[$i+1]));
189: }
190:
191:
192: $hash = md5($nstr . $name);
193:
194: return sprintf('%08s-%04s-%04x-%04x-%12s',
195:
196:
197: substr($hash, 0, 8),
198:
199:
200: substr($hash, 8, 4),
201:
202:
203:
204: (hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x3000,
205:
206:
207:
208:
209: (hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,
210:
211:
212: substr($hash, 20, 12)
213: );
214: }
215:
216: 217: 218: 219: 220: 221:
222: private static function _v4()
223: {
224: return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
225:
226:
227: mt_rand(0, 0xffff), mt_rand(0, 0xffff),
228:
229:
230: mt_rand(0, 0xffff),
231:
232:
233:
234: mt_rand(0, 0x0fff) | 0x4000,
235:
236:
237:
238:
239: mt_rand(0, 0x3fff) | 0x8000,
240:
241:
242: mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
243: );
244: }
245:
246: 247: 248: 249: 250: 251: 252: 253: 254: 255:
256: private static function _v5($namespace, $name)
257: {
258: if(!self::isValid($namespace)) {
259: throw new \InvalidArgumentException('Cannot create uuid v5 from invalid namespace with value: "'.$namespace.'"');
260: }
261:
262:
263: $nhex = str_replace(array('-','{','}'), '', $namespace);
264:
265:
266: $nstr = '';
267:
268:
269: for($i = 0; $i < strlen($nhex); $i+=2)
270: {
271: $nstr .= chr(hexdec($nhex[$i].$nhex[$i+1]));
272: }
273:
274:
275: $hash = sha1($nstr . $name);
276:
277: return sprintf('%08s-%04s-%04x-%04x-%12s',
278:
279:
280: substr($hash, 0, 8),
281:
282:
283: substr($hash, 8, 4),
284:
285:
286:
287: (hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x5000,
288:
289:
290:
291:
292: (hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,
293:
294:
295: substr($hash, 20, 12)
296: );
297: }
298: }
299: