NodeRequestIdGenerator.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Rpc\IdGenerator;
  12. use DateTime;
  13. use Hyperf\Codec\Base62;
  14. use Hyperf\Contract\IdGeneratorInterface;
  15. class NodeRequestIdGenerator implements IdGeneratorInterface
  16. {
  17. private ?string $node = null;
  18. public function generate(): string
  19. {
  20. return $this->getNode() . Base62::encode(intval(microtime(true) * 1000));
  21. }
  22. /**
  23. * Retrieves the node mac address and request time from ID.
  24. */
  25. public function decode(string $id): array
  26. {
  27. $len = strlen(Base62::encode(intval(microtime(true) * 1000)));
  28. $macStr = substr($id, 0, -$len);
  29. $microtime = Base62::decode(substr($id, -$len)) / 1000;
  30. $node = str_pad(sprintf('%x', Base62::decode($macStr)), 12, '0', STR_PAD_LEFT);
  31. return [
  32. 'node' => trim(preg_replace('/(..)/', '\1:', $node), ':'),
  33. 'time' => DateTime::createFromFormat('U.u', (string) $microtime),
  34. ];
  35. }
  36. /**
  37. * Returns the system node ID.
  38. */
  39. public function getNode(): string
  40. {
  41. if (! $this->node) {
  42. $str = $this->getMacAddress() ?: $this->getIfconfig() ?: $this->randomBytes();
  43. $this->node = Base62::encode(hexdec($str));
  44. }
  45. return $this->node;
  46. }
  47. protected function randomBytes(): string
  48. {
  49. $node = hexdec(bin2hex(random_bytes(6)));
  50. /**
  51. * Set the multicast bit.
  52. * @see https://tools.ietf.org/html/rfc4122#section-4.5
  53. */
  54. $node = $node | 0x010000000000;
  55. return str_pad(dechex($node), 12, '0', STR_PAD_LEFT);
  56. }
  57. /**
  58. * Returns the network interface configuration for the system.
  59. *
  60. * @codeCoverageIgnore
  61. */
  62. protected function getIfconfig(): string
  63. {
  64. if (str_contains(strtolower(ini_get('disable_functions')), 'passthru')) {
  65. return '';
  66. }
  67. ob_start();
  68. switch (strtoupper(substr(php_uname('a'), 0, 3))) {
  69. case 'WIN':
  70. passthru('ipconfig /all 2>&1');
  71. break;
  72. case 'DAR':
  73. passthru('ifconfig 2>&1');
  74. break;
  75. case 'FRE':
  76. passthru('netstat -i -f link 2>&1');
  77. break;
  78. case 'LIN':
  79. default:
  80. passthru('netstat -ie 2>&1');
  81. break;
  82. }
  83. $output = ob_get_clean();
  84. $pattern = '/[^:]([0-9A-Fa-f]{2}([:-])[0-9A-Fa-f]{2}(\2[0-9A-Fa-f]{2}){4})[^:]/';
  85. if (preg_match_all($pattern, $output, $matches)) {
  86. return str_replace([':', '-'], '', $matches[1][0]);
  87. }
  88. return '';
  89. }
  90. /**
  91. * Returns mac address from the first system interface via the sysfs interface.
  92. */
  93. protected function getMacAddress(): string
  94. {
  95. if (strtoupper(php_uname('s')) !== 'LINUX') {
  96. return '';
  97. }
  98. foreach (glob('/sys/class/net/*/address', GLOB_NOSORT) as $addressPath) {
  99. $mac = trim(file_get_contents($addressPath));
  100. if (
  101. // Localhost adapter
  102. $mac !== '00:00:00:00:00:00'
  103. // Must match MAC address
  104. && preg_match('/^([0-9a-f]{2}:){5}[0-9a-f]{2}$/i', $mac)
  105. ) {
  106. return str_replace(':', '', $mac);
  107. }
  108. }
  109. return '';
  110. }
  111. }