1<?php 2/* 3 * Copyright 2008-2010 GuardTime AS 4 * 5 * This file is part of the GuardTime PHP SDK. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20/** 21 * @package util 22 */ 23 24/** 25 * Collection of miscellaneous commonly used utility functions. 26 * 27 * @package util 28 */ 29class GTUtil { 30 31 /** 32 * Computes the greatest common divisor (GCD) of two integers. 33 * 34 * Greatest common divisor is the largest integer that divides both numbers 35 * without remainder. 36 * 37 * <code> 38 * echo GTUtil::gcd(4, 8); 39 * 40 * // output: 41 * // 4 42 * </code> 43 * 44 * @static 45 * @param int $a the first integer 46 * @param int $b the second integer 47 * @return int the greatest common divisor of a and b or 0 if both are 0 48 */ 49 public static function gcd($a, $b) { 50 51 $a = abs((int) $a); 52 $b = abs((int) $b); 53 54 while ($a > 0) { 55 $c = $b % $a; 56 $b = $a; 57 $a = $c; 58 } 59 60 return $b; 61 62 } 63 64 /** 65 * Computes the least common multiple (LCM) of two integers. 66 * 67 * Least common multiple is the smallest positive integer that can be divided 68 * by both numbers without a remainder. 69 * 70 * <code> 71 * echo GTUtil::lcm(4, 6); 72 * 73 * // output: 74 * // 12 75 * </code> 76 * 77 * @static 78 * @throws GTException when the result is too big to fit into {@code int} 79 * @param int $a the first integer 80 * @param int $b the second integer 81 * @return int the least common multiple of a and b, or 0 if either a or b is 0 82 */ 83 public static function lcm($a, $b) { 84 85 $a = (int) $a; 86 $b = (int) $b; 87 88 if ($a == 0 || $b == 0) { 89 return 0; 90 } 91 92 $a = abs($a) / GTUtil::gcd($a, $b); 93 $b = abs($b); 94 95 if ($a > PHP_INT_MAX / $b) { 96 throw new GTException("Integer overflow"); 97 } 98 99 return $a * $b; 100 } 101 102 /** 103 * Writes data to file. 104 * 105 * Example: 106 * 107 * <code> 108 * GTUtil::write('file.txt', array(1, 2, 3)); 109 * </code> 110 * 111 * @static 112 * @throws GTException 113 * @param string $file file name to write to 114 * @param array $bytes byte array that contains the bytes to write 115 * @return void 116 * 117 * @see read 118 */ 119 public static function write($file, array $bytes) { 120 121 if (empty($file)) { 122 throw new GTException("parameter file is required"); 123 } 124 125 if ($bytes == null) { 126 throw new GTException("parameter bytes is required"); 127 } 128 129 if (!is_array($bytes)) { 130 throw new GTException("parameter bytes must be an array"); 131 } 132 133 $fp = fopen($file, 'wb+'); 134 135 if (!$fp) { 136 throw new GTException("Unable to open file {$file} for writing"); 137 } 138 139 if (!fwrite($fp, GTUtil::fromByteArray($bytes))) { 140 throw new GTException("Unable to write to bytes to file {$file}"); 141 } 142 143 if (!fclose($fp)) { 144 throw new GTException("Unable to close file after writing {$file}"); 145 } 146 147 } 148 149 /** 150 * Touches specified file. 151 * 152 * @static 153 * @throws GTException 154 * @param $file the file to touch 155 * @return void 156 */ 157 public static function touch($file) { 158 159 if (empty($file)) { 160 throw new GTException("parameter file is required"); 161 } 162 163 touch($file); 164 } 165 166 /** 167 * Reads data from file. 168 * 169 * @static 170 * @throws GTException 171 * @param string $file file name to read from 172 * @return array byte array read from $file 173 * 174 * @see write 175 */ 176 public static function read($file) { 177 178 if (empty($file)) { 179 throw new GTException("parameter file is required"); 180 } 181 182 if (!is_file($file)) { 183 throw new GTException("file {$file} does not exist"); 184 } 185 186 if (!is_readable($file)) { 187 throw new GTException("file {$file} is not readable"); 188 } 189 190 $fp = fopen($file, 'rb'); 191 192 if (!$fp) { 193 throw new GTException("Unable to open file {$file} for reading"); 194 } 195 196 $length = filesize($file); 197 198 if ($length > 0) { 199 $data = fread($fp, $length); 200 201 } else { 202 $data = ""; 203 204 } 205 206 if ($data === false) { 207 throw new GTException("Unable to read from file {$file}"); 208 } 209 210 fclose($fp); 211 212 return GTUtil::toByteArray($data); 213 } 214 215 /** 216 * Wrapper method for OpenSSL asn1parse. 217 * 218 * @static 219 * @param string $file file containing a valid ASN.1 DER object 220 * @return void 221 */ 222 public static function printAsn1($file) { 223 passthru("openssl asn1parse -i -inform DER -in {$file}"); 224 } 225 226 /** 227 * Decodes an ASN.1 formatted time to unix timestamp. 228 * 229 * @static 230 * @throws GTException 231 * @param string $time ASN.1 formatted time 232 * @param string $timezone timezone to use 233 * @return int php unix timestamp 234 */ 235 public static function decodeTime($time, $timezone = null) { 236 237 if ($timezone != null) { 238 239 $old_timezone = date_default_timezone_get(); 240 $new_timezone = date_default_timezone_set($timezone); 241 242 if ($new_timezone === false) { 243 throw new GTException("Unable to set timezone to {$timezone}"); 244 } 245 246 } 247 248 if ($time == null) { 249 throw new GTException("parameter time must not be empty"); 250 } 251 252 if (strlen($time) != 15) { 253 throw new GTException("parameter time has invalid length"); 254 } 255 256 $tokens = array( 257 'year' => substr($time, 0, 4), 258 'month' => substr($time, 4, 2), 259 'day' => substr($time, 6, 2), 260 'hour' => substr($time, 8, 2), 261 'minute' => substr($time, 10, 2), 262 'second' => substr($time, 12, 2) 263 ); 264 265 foreach ($tokens as $name => $value) { 266 if (!ctype_digit($value)) { 267 throw new GTException("Invalid time encoding, {$name} = {$value}"); 268 } 269 } 270 271 $time = mktime( 272 (int) $tokens['hour'], 273 (int) $tokens['minute'], 274 (int) $tokens['second'], 275 (int) $tokens['month'], 276 (int) $tokens['day'], 277 (int) $tokens['year'] 278 ); 279 280 if ($timezone != null) { 281 date_default_timezone_set($old_timezone); 282 } 283 284 return $time; 285 } 286 287 /** 288 * Formats a PHP unix timestamp in a more human readable format (%Y-%m-%d %H:%M:%S UTC) 289 * 290 * @static 291 * @throws GTException 292 * @param $time php unix timestamp 293 * @param $timezone the timezone to use 294 * @return string formatted datetime 295 */ 296 public static function formatTime($time, $timezone = null) { 297 298 if ($timezone != null) { 299 300 $old_timezone = date_default_timezone_get(); 301 $new_timezone = date_default_timezone_set($timezone); 302 303 if ($new_timezone === false) { 304 throw new GTException("Unable to set timezone to {$timezone}"); 305 } 306 307 } 308 309 $formatted = strftime('%Y-%m-%d %H:%M:%S UTC', $time); 310 311 if ($timezone != null) { 312 date_default_timezone_set($old_timezone); 313 } 314 315 return $formatted; 316 } 317 318 /** 319 * Pads $array by prepending $value until the size of $array equals $length. 320 * 321 * @static 322 * @param $array the array to pad 323 * @param $length the length to pad to 324 * @param $value padding value 325 * @return void 326 */ 327 public static function lpad(array &$array, $length, $value) { 328 while (count($array) < $length) { 329 array_unshift($array, $value); 330 } 331 } 332 333 /** 334 * Pads $array by appending $value until the size of $array equals $length. 335 * 336 * @static 337 * @param $array the array to pad 338 * @param $length the length to pad to 339 * @param $value padding value 340 * @return void 341 */ 342 public static function rpad(array &$array, $length, $value) { 343 while (count($array) < $length) { 344 array_push($array, $value); 345 } 346 } 347 348 /** 349 * Converts a string into an array of characters. 350 * 351 * <code> 352 * $result = GTUtil::toArray('foo'); 353 * 354 * print_r($result); 355 * 356 * // output: 357 * // Array 358 * // ( 359 * // [0] => f 360 * // [1] => o 361 * // [2] => o 362 * // ) 363 * </code> 364 * 365 * @static 366 * @param string $string the input string 367 * @return array an array built from characters in string 368 */ 369 public static function toArray($string) { 370 371 $result = array(); 372 373 if (empty($string)) { 374 return $result; 375 } 376 377 for ($i = 0; $i < strlen($string); $i++) { 378 array_push($result, $string{$i}); 379 } 380 381 return $result; 382 383 } 384 385 /** 386 * Converts an array of characters into a string 387 * 388 * <code> 389 * echo GTUtil::fromArray(array('f', 'o', 'o')); 390 * 391 * // output: 392 * // foo 393 * </code> 394 * 395 * @static 396 * @param $array the input array 397 * @return string a string built from characters in array 398 */ 399 public static function fromArray(array $array = null) { 400 401 $result = ""; 402 403 if (empty($array)) { 404 return $result; 405 } 406 407 for ($i = 0; $i < count($array); $i++) { 408 $result .= $array[$i]; 409 } 410 411 return $result; 412 413 } 414 415 /** 416 * Converts a string into an array of bytes. 417 * 418 * Each byte in the resulting array will represent the ASCII value of each 419 * character in the input string 420 * 421 * <code> 422 * $result = GTUtil::toByteArray('foo'); 423 * 424 * print_r($result); 425 * 426 * // output: 427 * // Array 428 * // ( 429 * // [0] => 102 430 * // [1] => 111 431 * // [2] => 111 432 * // ) 433 * </code> 434 * 435 * @static 436 * @param $string the input string 437 * @return array an array of bytes representing the characters in string 438 */ 439 public static function toByteArray($string) { 440 441 $result = array(); 442 443 if (empty($string)) { 444 return $result; 445 } 446 447 for ($i = 0; $i < strlen($string); $i++) { 448 array_push($result, ord($string{$i})); 449 } 450 451 return $result; 452 453 } 454 455 /** 456 * Converts a byte array into a string. 457 * 458 * Each character in the string will represent the ASCII code of each 459 * byte in the input array 460 * 461 * <code> 462 * $bytes = array( 463 * ord('f'), 464 * ord('o'), 465 * ord('o') 466 * ); 467 * 468 * echo GTUtil::fromByteArray($bytes); 469 * 470 * // output: 471 * // foo 472 * </code> 473 * 474 * @static 475 * @param $array the input array 476 * @return string a string built from bytes in the array 477 */ 478 public static function fromByteArray(array $array = null) { 479 480 $result = ""; 481 482 if (empty($array)) { 483 return $result; 484 } 485 486 for ($i = 0; $i < count($array); $i++) { 487 $result .= chr($array[$i]); 488 } 489 490 return $result; 491 492 } 493 494 /** 495 * Reads a short (2 bytes) integer from the given byte array. 496 * 497 * @static 498 * @throws GTException 499 * @param array $array the byte array to read from 500 * @param int $position the position in the array to read from 501 * @return int short integer from the given byte array 502 */ 503 public static function readShort(array $array, $position) { 504 505 if ($position + 2 > count($array) - 1) { 506 throw new GTException("Array index out of bounds"); 507 } 508 509 $integer = new GTBigInteger( 510 array_slice($array, $position, 2) 511 ); 512 513 return (int) $integer->getValue(); 514 } 515 516 /** 517 * Reads an integer (4 bytes) from the given byte array. 518 * 519 * @static 520 * @throws GTException 521 * @param array $array the byte array to read from 522 * @param int $position the position in the array to read from 523 * @return int 4 byte integer from the given byte array 524 */ 525 public static function readInt(array $array, $position) { 526 527 if ($position + 4 > count($array) - 1) { 528 throw new GTException("Array index out of bounds"); 529 } 530 531 $integer = new GTBigInteger( 532 array_slice($array, $position, 4) 533 ); 534 535 return (int) $integer->getValue(); 536 } 537 538 /** 539 * Reads a GTBigInteger (8 bytes) from the given byte array. 540 * 541 * @static 542 * @throws GTException 543 * @param array $array the byte array to read from 544 * @param int $position the position in the array to read from 545 * @return GTBigInteger 8 byte GTBigInteger from the given byte array 546 */ 547 public static function readLong(array $array, $position) { 548 549 if ($position + 8 > count($array) - 1) { 550 throw new GTException("Array index out of bounds"); 551 } 552 553 return new GTBigInteger( 554 array_slice($array, $position, 8) 555 ); 556 } 557 558 /** 559 * Computes and adds CRC32 to the given byte array. 560 * 561 * @static 562 * @param $bytes input byte array 563 * @return array bytes with added CRC32 checksum 564 */ 565 public static function addCrc32(array $bytes) { 566 567 $checksum = crc32(GTUtil::fromByteArray($bytes)); 568 $checksum = sprintf("%u", $checksum); // make unsigned, needed for 32 bit PHP 569 $checksum = new GTBigInteger($checksum); 570 $checksum = $checksum->toBytes(); 571 572 // remove leading 0x0-s 573 while (count($checksum) > 4) { 574 array_shift($checksum); 575 } 576 577 foreach ($checksum as $byte) { 578 array_push($bytes, $byte); 579 } 580 581 return $bytes; 582 } 583 584 /** 585 * Gets OpenSSL version as string. 586 * 587 * @static 588 * @return string OpenSSL version as string, example: 0.9.8 589 */ 590 public static function getOpensslVersion() { 591 592 $version = new GTBigInteger(OPENSSL_VERSION_NUMBER); 593 594 $mask = new GTBigInteger(0xF); 595 596 $major = $version->shiftRight(28)->bitAnd($mask)->getValue(); 597 $minor = $version->shiftRight(20)->bitAnd($mask)->getValue(); 598 $fix = $version->shiftRight(12)->bitAnd($mask)->getValue(); 599 600 return "{$major}.{$minor}.{$fix}"; 601 } 602 603} 604 605?> 606