Terminal.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php
  2. /**
  3. * +----------------------------------------------------------------------
  4. * | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  5. * +----------------------------------------------------------------------
  6. * | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
  7. * +----------------------------------------------------------------------
  8. * | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  9. * +----------------------------------------------------------------------
  10. * | Author: CRMEB Team <admin@crmeb.com>
  11. * +----------------------------------------------------------------------
  12. */
  13. namespace crmeb\utils;
  14. use think\Response;
  15. use think\console\Output;
  16. /**
  17. * 执行命令
  18. * Class Terminal
  19. * @author 等风来
  20. * @email 136327134@qq.com
  21. * @date 2023/4/13
  22. * @package crmeb\utils
  23. */
  24. class Terminal
  25. {
  26. /**
  27. * 命令
  28. * @var \string[][]
  29. */
  30. protected $command = [
  31. 'npm-build' => [
  32. 'run_root' => '',
  33. 'command' => 'npm run build',
  34. ],
  35. 'npm-install' => [
  36. 'run_root' => '',
  37. 'command' => 'npm run install',
  38. ],
  39. ];
  40. /**
  41. * 执行内容保存地址
  42. * @var string
  43. */
  44. protected $outputFile;
  45. /**
  46. * 执行状态
  47. * @var integer
  48. */
  49. protected $procStatus;
  50. /**
  51. * 响应内容
  52. * @var string
  53. */
  54. protected $outputContent;
  55. /**
  56. * @var
  57. */
  58. protected $output;
  59. /**
  60. * Terminal constructor.
  61. */
  62. public function __construct()
  63. {
  64. $this->command['npm-build']['run_root'] = str_replace('DS', DS, config('app.admin_template_path'));
  65. $this->command['npm-install']['run_root'] = str_replace('DS', DS, config('app.admin_template_path'));
  66. $outputDir = root_path() . 'runtime' . DIRECTORY_SEPARATOR . 'terminal';
  67. $this->outputFile = $outputDir . DIRECTORY_SEPARATOR . 'exec.log';
  68. if (!is_dir($outputDir)) {
  69. mkdir($outputDir, 0755, true);
  70. }
  71. file_put_contents($this->outputFile, '');
  72. }
  73. /**
  74. * @param Output $output
  75. * @author 等风来
  76. * @email 136327134@qq.com
  77. * @date 2023/4/13
  78. */
  79. public function setOutput(Output $output)
  80. {
  81. $this->output = $output;
  82. }
  83. /**
  84. * @return string
  85. * @author 等风来
  86. * @email 136327134@qq.com
  87. * @date 2023/4/13
  88. */
  89. public function adminTemplatePath()
  90. {
  91. return $this->command['npm-install']['run_root'];
  92. }
  93. /**
  94. * 执行
  95. * @param string $name
  96. * @return string
  97. * @author 等风来
  98. * @email 136327134@qq.com
  99. * @date 2023/4/13
  100. */
  101. public function run(string $name)
  102. {
  103. if (!function_exists('proc_open')) {
  104. throw new \RuntimeException('缺少proc_open函数无法运行');
  105. }
  106. if (!isset($this->command[$name])) {
  107. throw new \RuntimeException('运行的命令不存在');
  108. }
  109. $command = $this->command[$name];
  110. $descriptorspec = [0 => ['pipe', 'r'], 1 => ['file', $this->outputFile, 'w'], 2 => ['file', $this->outputFile, 'w']];
  111. $process = proc_open($command['command'], $descriptorspec, $pipes, $command['run_root'], null, ['suppress_errors' => true]);
  112. if (is_resource($process)) {
  113. while ($this->getProcStatus($process)) {
  114. $contents = file_get_contents($this->outputFile);
  115. if (strlen($contents) && $this->outputContent != $contents) {
  116. $newOutput = str_replace($this->outputContent, '', $contents);
  117. if (preg_match('/\r\n|\r|\n/', $newOutput)) {
  118. $this->echoOutputFlag($newOutput);
  119. $this->outputContent = $contents;
  120. }
  121. }
  122. usleep(500000);
  123. }
  124. foreach ($pipes as $pipe) {
  125. fclose($pipe);
  126. }
  127. proc_close($process);
  128. }
  129. return $this->output('run success');
  130. }
  131. /**
  132. * 判断状态
  133. * @param $process
  134. * @return bool
  135. * @author 等风来
  136. * @email 136327134@qq.com
  137. * @date 2023/4/13
  138. */
  139. public function getProcStatus($process): bool
  140. {
  141. $status = proc_get_status($process);
  142. if ($status['running']) {
  143. $this->procStatus = 1;
  144. return true;
  145. } elseif ($this->procStatus === 1) {
  146. $this->procStatus = 0;
  147. $this->output('exit: ' . $status['exitcode']);
  148. if ($status['exitcode'] === 0) {
  149. $this->echoOutputFlag('success');
  150. } else {
  151. $this->echoOutputFlag('error');
  152. }
  153. return true;
  154. } else {
  155. return false;
  156. }
  157. }
  158. /**
  159. * 直接输入响应
  160. * @param string $message
  161. * @author 等风来
  162. * @email 136327134@qq.com
  163. * @date 2023/4/13
  164. */
  165. public function echoOutputFlag(string $message)
  166. {
  167. if ($this->output && $this->output instanceof Output) {
  168. $this->output->info($message);
  169. } else {
  170. echo $this->output($message);
  171. @ob_flush();
  172. }
  173. }
  174. /**
  175. * 返回响应内容
  176. * @param string $data
  177. * @return string
  178. * @author 等风来
  179. * @email 136327134@qq.com
  180. * @date 2023/4/13
  181. */
  182. private function output($data)
  183. {
  184. $data = [
  185. 'message' => $data,
  186. ];
  187. return Response::create($data, 'json')->getContent();
  188. }
  189. }