FindNewerDriver.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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\Driver;
  12. use Hyperf\Engine\Channel;
  13. use Hyperf\Stringable\Str;
  14. use Hyperf\Watcher\Option;
  15. use InvalidArgumentException;
  16. use function Hyperf\Watcher\exec;
  17. class FindNewerDriver extends AbstractDriver
  18. {
  19. protected string $tmpFile = '/tmp/hyperf_find.php';
  20. protected bool $scanning = false;
  21. protected int $count = 0;
  22. public function __construct(protected Option $option)
  23. {
  24. parent::__construct($option);
  25. $ret = exec('which find');
  26. if (empty($ret['output'])) {
  27. throw new InvalidArgumentException('find not exists.');
  28. }
  29. // create two files
  30. exec('echo 1 > ' . $this->getToModifyFile());
  31. exec('echo 1 > ' . $this->getToScanFile());
  32. }
  33. public function watch(Channel $channel): void
  34. {
  35. $seconds = $this->option->getScanIntervalSeconds();
  36. $this->timerId = $this->timer->tick($seconds, function () use ($channel) {
  37. if ($this->scanning) {
  38. return;
  39. }
  40. $this->scanning = true;
  41. $changedFiles = $this->scan();
  42. ++$this->count;
  43. // update mtime
  44. if ($changedFiles) {
  45. exec('echo 1 > ' . $this->getToModifyFile());
  46. exec('echo 1 > ' . $this->getToScanFile());
  47. }
  48. foreach ($changedFiles as $file) {
  49. $channel->push($file);
  50. $this->scanning = false;
  51. return;
  52. }
  53. $this->scanning = false;
  54. });
  55. }
  56. protected function find(array $targets, array $ext = []): array
  57. {
  58. $changedFiles = [];
  59. $shell = '';
  60. $len = count($targets);
  61. // merge find command
  62. for ($i = 0; $i < $len; ++$i) {
  63. $dest = $targets[$i];
  64. $symbol = ($i == $len - 1) ? '' : '&';
  65. $file = $this->getToScanFile();
  66. $shell = $shell . sprintf('find %s -newer %s -type f', $dest, $file) . $symbol;
  67. }
  68. $ret = exec($shell);
  69. if ($ret['code'] === 0 && strlen($ret['output'])) {
  70. $stdout = $ret['output'];
  71. $lineArr = explode(PHP_EOL, $stdout);
  72. foreach ($lineArr as $pathName) {
  73. if (empty($pathName)) {
  74. continue;
  75. }
  76. if (! empty($ext) && ! Str::endsWith($pathName, $ext)) {
  77. continue;
  78. }
  79. $changedFiles[] = $pathName;
  80. }
  81. }
  82. return $changedFiles;
  83. }
  84. protected function scan(): array
  85. {
  86. $ext = $this->option->getExt();
  87. $dirs = array_map(fn ($dir) => BASE_PATH . '/' . $dir, $this->option->getWatchDir());
  88. $files = array_map(fn ($file) => BASE_PATH . '/' . $file, $this->option->getWatchFile());
  89. if ($files) {
  90. $dirs[] = implode(' ', $files);
  91. }
  92. return $this->find($dirs, $ext);
  93. }
  94. protected function getToModifyFile(): string
  95. {
  96. return $this->tmpFile . ($this->count % 2);
  97. }
  98. protected function getToScanFile(): string
  99. {
  100. return $this->tmpFile . (($this->count + 1) % 2);
  101. }
  102. }