Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
strategy:
matrix:
php-version:
- '8.1'
- '8.2'

steps:
-
Expand Down Expand Up @@ -52,7 +52,7 @@ jobs:
strategy:
matrix:
php-version:
- '8.1'
- '8.2'

steps:
-
Expand Down Expand Up @@ -85,8 +85,10 @@ jobs:
strategy:
matrix:
php-version:
- '8.0'
- '8.1'
- '8.2'
- '8.3'
- '8.4'
- '8.5'

steps:
-
Expand All @@ -109,11 +111,11 @@ jobs:
-
name: "Install Watchman"
run: |
wget https://github.com/facebook/watchman/releases/download/v2020.09.14.00/watchman-v2020.09.14.00-linux.zip
unzip watchman-v2020.09.14.00-linux.zip
wget https://github.com/facebook/watchman/releases/download/v2025.12.22.00/watchman-v2025.12.22.00-linux.zip
unzip watchman-v2025.12.22.00-linux.zip
sudo mkdir -p /usr/local/{bin,lib} /usr/local/var/run/watchman
sudo mv watchman-v2020.09.14.00-linux/bin/watchman /usr/local/bin/watchman
sudo mv watchman-v2020.09.14.00-linux/lib/* /usr/local/lib/
sudo mv watchman-v2025.12.22.00-linux/bin/watchman /usr/local/bin/watchman
sudo mv watchman-v2025.12.22.00-linux/lib/* /usr/local/lib/
sudo chmod 755 /usr/local/bin/watchman
sudo chmod 2777 /usr/local/var/run/watchman
-
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
/.vscode
.php_cs.cache
.php-cs-fixer.cache
/.phpactor.json
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ Amp FS Watch
![CI](https://github.com/phpactor/amp-fswatch/workflows/CI/badge.svg)

This is an [Amp](https://amphp.org/) library for asynchronously monitor paths
on your file system changes using various stategues.
on your file system changes using various stategies.

It's been created to trigger code indexing in
[Phpactor](https://github.com/phpactor/phpactor).

- Promise based API.
- Capable of automatically selecting a supported watcher for the current
environment.
- Provides realtime (e.g. ``inotify``) watchers in addition to polling ones.
Expand Down Expand Up @@ -39,9 +38,15 @@ Loop::run(function () use () {
[]
);

$process = yield $watcher->watch([$path]);
$process = $watcher->watch([$path]);

while (null !== $file = yield $process->wait()) {
if (defined('SIGINT')) {
EventLoop::onSignal(SIGINT, function () use ($process): void {
$process->stop();
});
}

while (null !== $file = $process->wait()) {
fwrite(STDOUT, sprintf('[%s] %s (%s)'."\n", date('Y-m-d H:i:s.u'), $file->path(), $file->type()));
}
});
Expand Down
36 changes: 15 additions & 21 deletions bin/watch
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
#!/usr/bin/env php
<?php

use Amp\Delayed;
use Amp\Loop;
use Phpactor\AmpFsWatch\ModifiedFile;
use Phpactor\AmpFsWatch\WatcherConfig;
use Phpactor\AmpFsWatch\Watcher\Fallback\FallbackWatcher;
use Phpactor\AmpFsWatch\Watcher\Find\FindWatcher;
use Phpactor\AmpFsWatch\Watcher\FsWatch\FsWatchWatcher;
use Phpactor\AmpFsWatch\Watcher\Inotify\InotifyWatcher;
use Phpactor\AmpFsWatch\Watcher\Null\NullWatcher;
use Phpactor\AmpFsWatch\Watcher\PatternMatching\PatternMatchingWatcher;
use Phpactor\AmpFsWatch\Watcher\PhpPollWatcher\PhpPollWatcher;
use Psr\Log\AbstractLogger;
use Revolt\EventLoop;

require __DIR__ . '/../vendor/autoload.php';

echo "This is a demo application\n";

if (!isset($argv[1])) {
echo "You must specify a path to watch";
echo 'You must specify a path to watch';
exit(1);
}

$path = $argv[1];
$logger = new class extends AbstractLogger {
public function log($level, $message, array $context = [])
public function log($level, $message, array $context = []): void
{
fwrite(STDERR, sprintf('[%s] %s', $level, $message)."\n");
fwrite(STDERR, sprintf('[%s] %s', $level, $message) . "\n");
}
};

Expand All @@ -40,18 +37,15 @@ $watcher = new PatternMatchingWatcher(new FallbackWatcher([
new FsWatchWatcher($config, $logger),
], $logger), [ '/**/*.php' ], []);

Loop::run(function () use ($watcher, $path) {
$process = yield $watcher->watch([$path]);
$process = $watcher->watch();

while (null !== $file = yield $process->wait()) {
fwrite(STDOUT, sprintf('[%s] %s (%s)'."\n", date('Y-m-d H:i:s.u'), $file->path(), $file->type()));
}
// Signals are not supported on Windows
if (defined('SIGINT')) {
EventLoop::onSignal(SIGINT, function () use ($process): void {
$process->stop();
});
}

// Signals are not supported on Windows
if(defined('SIGINT')) {
Loop::onSignal(SIGINT, function () use ($process) {
$process->stop();
Loop::stop();
});
}
});
while (null !== $file = $process->wait()) {
fwrite(STDOUT, sprintf('[%s] %s (%s)' . "\n", date('Y-m-d H:i:s.u'), $file->path(), $file->type()));
}
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
}
],
"require": {
"php": "^8.0",
"amphp/amp": "^2.4",
"amphp/process": "^1.1",
"php": "^8.2",
"amphp/amp": "^3.1",
"amphp/process": "^2",
"psr/log": "^1.1",
"webmozart/glob": "^4.4",
"symfony/filesystem": "^5.0|^6.0"
},
"require-dev": {
"amphp/phpunit-util": "^1.3",
"amphp/phpunit-util": "v3.0.0",
"ergebnis/composer-normalize": "^2.0",
"friendsofphp/php-cs-fixer": "^3.15",
"jangregor/phpstan-prophecy": "^1.0",
Expand Down
27 changes: 8 additions & 19 deletions src/SystemDetector/CommandDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,22 @@
namespace Phpactor\AmpFsWatch\SystemDetector;

use Amp\Process\Process;
use Amp\Promise;

class CommandDetector
{
/**
* @return Promise<bool>
*/
public function commandExists(string $command): Promise
public function commandExists(string $command): bool
{
return $this->checkPosixCommand($command);
}

/**
* @return Promise<bool>
*/
private function checkPosixCommand(string $command): Promise
private function checkPosixCommand(string $command): bool
{
return \Amp\call(function () use ($command) {
$process = new Process([
'command',
'-v',
$command
]);
$process = Process::start([
'command',
'-v',
$command,
]);

yield $process->start();

return 0 === yield $process->join();
});
return 0 === $process->join();
}
}
13 changes: 2 additions & 11 deletions src/Watcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,11 @@

namespace Phpactor\AmpFsWatch;

use Amp\Promise;

interface Watcher
{
/**
* @return Promise<WatcherProcess>
*/
public function watch(): Promise;

/**
* @return Promise<bool>
*/
public function isSupported(): Promise;
public function watch(): WatcherProcess;

public function isSupported(): bool;

public function describe(): string;
}
10 changes: 4 additions & 6 deletions src/Watcher/BufferedWatcher/BufferedWatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace Phpactor\AmpFsWatch\Watcher\BufferedWatcher;

use Amp\Promise;
use Phpactor\AmpFsWatch\Watcher;
use Phpactor\AmpFsWatch\WatcherProcess;

class BufferedWatcher implements Watcher
{
Expand All @@ -17,15 +17,13 @@ public function __construct(Watcher $innerWatcher, int $interval)
$this->interval = $interval;
}

public function watch(): Promise
public function watch(): WatcherProcess
{
return \Amp\call(function () {
return new BufferedWatcherProcess(yield $this->innerWatcher->watch(), $this->interval);
});
return new BufferedWatcherProcess($this->innerWatcher->watch(), $this->interval);
}


public function isSupported(): Promise
public function isSupported(): bool
{
return $this->innerWatcher->isSupported();
}
Expand Down
35 changes: 16 additions & 19 deletions src/Watcher/BufferedWatcher/BufferedWatcherProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

namespace Phpactor\AmpFsWatch\Watcher\BufferedWatcher;

use Amp\Delayed;
use Amp\Promise;
use Phpactor\AmpFsWatch\ModifiedFile;
use Phpactor\AmpFsWatch\WatcherProcess;
use Throwable;
use function Amp\async;
use function Amp\delay;

class BufferedWatcherProcess implements WatcherProcess
{
Expand All @@ -28,9 +28,9 @@ public function __construct(WatcherProcess $innerProcess, int $interval = 500)
$this->innerProcess = $innerProcess;
$this->interval = $interval;

\Amp\asyncCall(function () {
async(function (): void {
try {
while (null !== $modifiedFile = yield $this->innerProcess->wait()) {
while (null !== $modifiedFile = $this->innerProcess->wait()) {
assert($modifiedFile instanceof ModifiedFile);
$this->buffer[$modifiedFile->path()] = $modifiedFile;
}
Expand All @@ -47,23 +47,20 @@ public function stop(): void
$this->innerProcess->stop();
}


public function wait(): Promise
public function wait(): ?ModifiedFile
{
return \Amp\call(function () {
while ($this->running || !empty($this->buffer || null !== $this->error)) {
if ($this->error) {
$error = $this->error;
$this->error = null;
throw $error;
}
if ($this->buffer) {
return array_shift($this->buffer);
}
yield new Delayed($this->interval);
while ($this->running || !empty($this->buffer || null !== $this->error)) {
if ($this->error) {
$error = $this->error;
$this->error = null;
throw $error;
}
if ($this->buffer) {
return array_shift($this->buffer);
}
delay($this->interval / 1000);
}

return null;
});
return null;
}
}
Loading
Loading