Watcher.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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\Watcher;
  12. use Hyperf\Codec\Json;
  13. use Hyperf\Contract\ConfigInterface;
  14. use Hyperf\Coroutine\Coroutine;
  15. use Hyperf\Engine\Channel;
  16. use Hyperf\Support\Exception\InvalidArgumentException;
  17. use Hyperf\Support\Filesystem\FileNotFoundException;
  18. use Hyperf\Support\Filesystem\Filesystem;
  19. use Hyperf\Watcher\Driver\DriverInterface;
  20. use PhpParser\PrettyPrinter\Standard;
  21. use Psr\Container\ContainerInterface;
  22. use Symfony\Component\Console\Output\OutputInterface;
  23. use Throwable;
  24. use function Hyperf\Support\make;
  25. class Watcher
  26. {
  27. protected DriverInterface $driver;
  28. protected Filesystem $filesystem;
  29. protected array $autoload;
  30. protected ConfigInterface $config;
  31. protected Standard $printer;
  32. protected Channel $channel;
  33. protected string $path = BASE_PATH . '/runtime/container/collectors.cache';
  34. public function __construct(protected ContainerInterface $container, protected Option $option, protected OutputInterface $output)
  35. {
  36. $this->driver = $this->getDriver();
  37. $this->filesystem = new Filesystem();
  38. $json = Json::decode($this->filesystem->get(BASE_PATH . '/composer.json'));
  39. $this->autoload = array_flip($json['autoload']['psr-4'] ?? []);
  40. $this->config = $container->get(ConfigInterface::class);
  41. $this->printer = new Standard();
  42. $this->channel = new Channel(1);
  43. $this->channel->push(true);
  44. }
  45. public function run()
  46. {
  47. $this->dumpAutoload();
  48. $this->restart(true);
  49. $channel = new Channel(999);
  50. Coroutine::create(function () use ($channel) {
  51. $this->driver->watch($channel);
  52. });
  53. $result = [];
  54. while (true) {
  55. $file = $channel->pop(0.001);
  56. if ($file === false) {
  57. if (count($result) > 0) {
  58. $result = [];
  59. $this->restart(false);
  60. }
  61. } else {
  62. $ret = exec(sprintf('%s %s/vendor/hyperf/watcher/collector-reload.php %s', $this->option->getBin(), BASE_PATH, $file));
  63. if ($ret['code'] === 0) {
  64. $this->output->writeln('Class reload success.');
  65. } else {
  66. $this->output->writeln('Class reload failed.');
  67. $this->output->writeln($ret['output'] ?? '');
  68. }
  69. $result[] = $file;
  70. }
  71. }
  72. }
  73. public function dumpAutoload()
  74. {
  75. $ret = exec('composer dump-autoload -o --no-scripts -d ' . BASE_PATH);
  76. $this->output->writeln($ret['output'] ?? '');
  77. }
  78. public function restart($isStart = true)
  79. {
  80. if (! $this->option->isRestart()) {
  81. return;
  82. }
  83. $file = $this->config->get('server.settings.pid_file');
  84. if (empty($file)) {
  85. throw new FileNotFoundException('The config of pid_file is not found.');
  86. }
  87. $daemonize = $this->config->get('server.settings.daemonize', false);
  88. if ($daemonize) {
  89. throw new InvalidArgumentException('Please set `server.settings.daemonize` to false');
  90. }
  91. if (! $isStart && $this->filesystem->exists($file)) {
  92. $pid = $this->filesystem->get($file);
  93. try {
  94. $this->output->writeln('Stop server...');
  95. if (posix_kill((int) $pid, 0)) {
  96. posix_kill((int) $pid, SIGTERM);
  97. }
  98. } catch (Throwable) {
  99. $this->output->writeln('Stop server failed. Please execute `composer dump-autoload -o`');
  100. }
  101. }
  102. Coroutine::create(function () {
  103. $this->channel->pop();
  104. $this->output->writeln('Start server ...');
  105. $descriptorspec = [
  106. 0 => STDIN,
  107. 1 => STDOUT,
  108. 2 => STDERR,
  109. ];
  110. proc_open($this->option->getBin() . ' ' . BASE_PATH . '/' . $this->option->getCommand(), $descriptorspec, $pipes);
  111. $this->output->writeln('Stop server success.');
  112. $this->channel->push(1);
  113. });
  114. }
  115. protected function getDriver()
  116. {
  117. $driver = $this->option->getDriver();
  118. if (! class_exists($driver)) {
  119. throw new \InvalidArgumentException('Driver not support.');
  120. }
  121. return make($driver, ['option' => $this->option]);
  122. }
  123. }