BuildBinCommand.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. <?php
  2. namespace Webman\Console\Commands;
  3. use Phar;
  4. use RuntimeException;
  5. use Symfony\Component\Console\Command\Command;
  6. use Symfony\Component\Console\Input\InputArgument;
  7. use Symfony\Component\Console\Input\InputInterface;
  8. use Symfony\Component\Console\Output\OutputInterface;
  9. use ZipArchive;
  10. class BuildBinCommand extends BuildPharCommand
  11. {
  12. protected static $defaultName = 'build:bin';
  13. protected static $defaultDescription = 'build bin';
  14. /**
  15. * @return void
  16. */
  17. protected function configure()
  18. {
  19. $this->addArgument('version', InputArgument::OPTIONAL, 'PHP version');
  20. }
  21. /**
  22. * @param InputInterface $input
  23. * @param OutputInterface $output
  24. * @return int
  25. */
  26. protected function execute(InputInterface $input, OutputInterface $output): int
  27. {
  28. $this->checkEnv();
  29. $output->writeln('Phar packing...');
  30. $version = $input->getArgument('version');
  31. if (!$version) {
  32. $version = (float)PHP_VERSION;
  33. }
  34. $version = $version >= 8.0 ? $version : 8.1;
  35. $supportZip = class_exists(ZipArchive::class);
  36. $microZipFileName = $supportZip ? "php$version.micro.sfx.zip" : "php$version.micro.sfx";
  37. $pharFileName = config('plugin.webman.console.app.phar_filename', 'webman.phar');
  38. $binFileName = config('plugin.webman.console.app.bin_filename', 'webman.bin');
  39. $this->buildDir = config('plugin.webman.console.app.build_dir', base_path() . '/build');
  40. $customIni = config('plugin.webman.console.app.custom_ini', '');
  41. $binFile = "$this->buildDir/$binFileName";
  42. $pharFile = "$this->buildDir/$pharFileName";
  43. $zipFile = "$this->buildDir/$microZipFileName";
  44. $sfxFile = "$this->buildDir/php$version.micro.sfx";
  45. $customIniHeaderFile = "$this->buildDir/custominiheader.bin";
  46. // 打包
  47. $command = new BuildPharCommand();
  48. $command->execute($input, $output);
  49. // 下载 micro.sfx.zip
  50. if (!is_file($sfxFile) && !is_file($zipFile)) {
  51. $domain = 'download.workerman.net';
  52. $output->writeln("\r\nDownloading PHP$version ...");
  53. if (extension_loaded('openssl')) {
  54. $context = stream_context_create([
  55. 'ssl' => [
  56. 'verify_peer' => false,
  57. 'verify_peer_name' => false,
  58. ]
  59. ]);
  60. $client = stream_socket_client("ssl://$domain:443", $context);
  61. } else {
  62. $client = stream_socket_client("tcp://$domain:80");
  63. }
  64. fwrite($client, "GET /php/$microZipFileName HTTP/1.0\r\nAccept: text/html\r\nHost: $domain\r\nUser-Agent: webman/console\r\n\r\n");
  65. $bodyLength = 0;
  66. $bodyBuffer = '';
  67. $lastPercent = 0;
  68. while (true) {
  69. $buffer = fread($client, 65535);
  70. if ($buffer !== false) {
  71. $bodyBuffer .= $buffer;
  72. if (!$bodyLength && $pos = strpos($bodyBuffer, "\r\n\r\n")) {
  73. if (!preg_match('/Content-Length: (\d+)\r\n/', $bodyBuffer, $match)) {
  74. $output->writeln("Download php$version.micro.sfx.zip failed");
  75. return self::FAILURE;
  76. }
  77. $firstLine = substr($bodyBuffer, 9, strpos($bodyBuffer, "\r\n") - 9);
  78. if (!preg_match('/200 /', $bodyBuffer)) {
  79. $output->writeln("Download php$version.micro.sfx.zip failed, $firstLine");
  80. return self::FAILURE;
  81. }
  82. $bodyLength = (int)$match[1];
  83. $bodyBuffer = substr($bodyBuffer, $pos + 4);
  84. }
  85. }
  86. $receiveLength = strlen($bodyBuffer);
  87. $percent = ceil($receiveLength * 100 / $bodyLength);
  88. if ($percent != $lastPercent) {
  89. echo '[' . str_pad('', $percent, '=') . '>' . str_pad('', 100 - $percent) . "$percent%]";
  90. echo $percent < 100 ? "\r" : "\n";
  91. }
  92. $lastPercent = $percent;
  93. if ($bodyLength && $receiveLength >= $bodyLength) {
  94. file_put_contents($zipFile, $bodyBuffer);
  95. break;
  96. }
  97. if ($buffer === false || !is_resource($client) || feof($client)) {
  98. $output->writeln("Fail donwload PHP$version ...");
  99. return self::FAILURE;
  100. }
  101. }
  102. } else {
  103. $output->writeln("\r\nUse PHP$version ...");
  104. }
  105. // 解压
  106. if (!is_file($sfxFile) && $supportZip) {
  107. $zip = new ZipArchive;
  108. $zip->open($zipFile, ZipArchive::CHECKCONS);
  109. $zip->extractTo($this->buildDir);
  110. }
  111. // 生成二进制文件
  112. file_put_contents($binFile, file_get_contents($sfxFile));
  113. // 自定义INI
  114. if (!empty($customIni)) {
  115. if (file_exists($customIniHeaderFile)) {
  116. unlink($customIniHeaderFile);
  117. }
  118. $f = fopen($customIniHeaderFile, 'wb');
  119. fwrite($f, "\xfd\xf6\x69\xe6");
  120. fwrite($f, pack('N', strlen($customIni)));
  121. fwrite($f, $customIni);
  122. fclose($f);
  123. file_put_contents($binFile, file_get_contents($customIniHeaderFile),FILE_APPEND);
  124. unlink($customIniHeaderFile);
  125. }
  126. file_put_contents($binFile, file_get_contents($pharFile), FILE_APPEND);
  127. // 添加执行权限
  128. chmod($binFile, 0755);
  129. $output->writeln("\r\nSaved $binFileName to $binFile\r\nBuild Success!\r\n");
  130. return self::SUCCESS;
  131. }
  132. }