Skip to content

Commit c6a4f10

Browse files
committed
Use different fs watchers
1 parent 677ffef commit c6a4f10

File tree

15 files changed

+168
-57
lines changed

15 files changed

+168
-57
lines changed

.travis.yml

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,6 @@ language: php
22

33
jobs:
44
include:
5-
- stage: "PHP7.2 - lowest"
6-
php: 7.2
7-
script:
8-
- composer update -n --prefer-dist --prefer-lowest --no-suggest
9-
- composer dump-autoload
10-
- composer ci:tests
11-
- composer ci:php:psalm
12-
13-
- stage: "PHP7.3 - highest"
14-
php: 7.3
15-
script:
16-
- composer update -n --prefer-dist --no-suggest
17-
- composer dump-autoload
18-
- composer ci:tests
19-
- composer ci:php:psalm
20-
215
- stage: "PHP7.4 - highest"
226
php: 7.4
237
script:

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
## 0.6.0 (2020-05-11)
1+
## 0.6.0 (2020-05-20)
22
* Fix: don't use child process for resource watching
3+
* Feature: add fswatch support
4+
* Fix: min required PHP version is set to 7.4
35

46
## 0.5.2 (2019-12-07)
57
* Fix: use predefined const for PHP binary [#59](https://github.com/seregazhuk/php-watcher/pull/59)

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ PHP-watcher does not require any additional changes to your code or method of
3737
* [Default executable](#default-executable)
3838
* [Gracefully reloading down your script](#gracefully-reloading-down-your-script)
3939
* [Automatic restart](#automatic-restart)
40+
* [Performance](#performance)
4041
* [Spinner](#spinner)
4142

4243
## Installation
@@ -235,6 +236,14 @@ script crashes PHP-watcher will notify you about that.
235236

236237
![app exit](images/exit.svg)
237238

239+
## Performance
240+
241+
The watcher can use different strategies to monitor your file system changes. Under the hood it
242+
detects the environment and chooses the best suitable strategy.
243+
244+
By default, it uses [yosymfony/resource-watcher](https://github.com/yosymfony/resource-watcher
245+
) which
246+
238247
## Spinner
239248

240249
By default the watcher outputs a nice spinner which indicates that the process is running

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
}
2525
],
2626
"require": {
27-
"php": "^7.2",
27+
"php": "^7.4",
2828
"ext-json": "*",
2929
"ext-pcntl": "*",
3030
"yosymfony/resource-watcher": "^2.0",
@@ -34,7 +34,8 @@
3434
"react/child-process": "^0.6.1",
3535
"react/stream": "^1.0.0",
3636
"symfony/finder": "^4.3 || ^5.0",
37-
"alecrabbit/php-cli-snake": "^0.5"
37+
"alecrabbit/php-cli-snake": "^0.5",
38+
"seregazhuk/reactphp-fswatch": "^0.1.0"
3839
},
3940
"autoload": {
4041
"psr-4": {

phpunit.xml.dist

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616
<filter>
1717
<whitelist processUncoveredFilesFromWhitelist="true">
1818
<directory suffix=".php">src</directory>
19-
<exclude>
20-
<file>src/Filesystem/watcher.php</file>
21-
</exclude>
2219
</whitelist>
2320
</filter>
2421
</phpunit>

src/Filesystem/ChangesListener.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
<?php
22

33
declare(strict_types=1);
4-
namespace seregazhuk\PhpWatcher\Filesystem;
54

6-
use seregazhuk\PhpWatcher\Config\WatchList;
5+
namespace seregazhuk\PhpWatcher\Filesystem;
76

87
interface ChangesListener
98
{
10-
public function start(WatchList $watchList): void;
9+
public function start(): void;
1110

1211
public function onChange(callable $callback): void;
12+
13+
public function stop(): void;
1314
}

src/Filesystem/Factory.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace seregazhuk\PhpWatcher\Filesystem;
6+
7+
use React\EventLoop\LoopInterface;
8+
use seregazhuk\PhpWatcher\Config\WatchList;
9+
use seregazhuk\PhpWatcher\Filesystem\FsWatchBased\ChangesListener as FsWatchBased;
10+
use seregazhuk\PhpWatcher\Filesystem\ResourceWatcherBased\ChangesListener as ResourceBased;
11+
12+
final class Factory
13+
{
14+
public static function create(WatchList $watchList, LoopInterface $loop): ChangesListener
15+
{
16+
if (FsWatchBased::isAvailable()) {
17+
return new FsWatchBased($watchList, $loop);
18+
}
19+
20+
return new ResourceBased($watchList, $loop);
21+
}
22+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace seregazhuk\PhpWatcher\Filesystem\FsWatchBased;
6+
7+
use Evenement\EventEmitter;
8+
use React\EventLoop\LoopInterface;
9+
use seregazhuk\PhpWatcher\Config\WatchList;
10+
use seregazhuk\PhpWatcher\Filesystem\ChangesListener as ChangesListenerInterface;
11+
use Seregazhuk\ReactFsWatch\FsWatch;
12+
13+
final class ChangesListener extends EventEmitter implements ChangesListenerInterface
14+
{
15+
private FsWatch $fsWatch;
16+
17+
public function __construct(WatchList $watchList, LoopInterface $loop)
18+
{
19+
$this->fsWatch = new FsWatch($this->makeOptions($watchList), $loop);
20+
}
21+
22+
public static function isAvailable(): bool
23+
{
24+
return FsWatch::isAvailable();
25+
}
26+
27+
public function start(): void
28+
{
29+
$this->fsWatch->run();
30+
$this->fsWatch->on(
31+
'change',
32+
function () {
33+
$this->emit('change');
34+
}
35+
);
36+
}
37+
38+
public function onChange(callable $callback): void
39+
{
40+
$this->on('change', $callback);
41+
}
42+
43+
public function stop(): void
44+
{
45+
$this->fsWatch->stop();
46+
}
47+
48+
private function makeOptions(WatchList $watchList): string
49+
{
50+
$options = [];
51+
52+
// first come paths
53+
if ($watchList->paths()) {
54+
$options[] = implode(' ', $watchList->paths());
55+
}
56+
57+
// then we ignore
58+
if ($watchList->ignore()) {
59+
$options[] = '-e ' . implode(' ', $watchList->ignore());
60+
}
61+
62+
// then include
63+
if ($watchList->fileExtensions()) {
64+
$options = array_merge($options, $this->makeIncludeOptions($watchList));
65+
}
66+
67+
$options[] = '-I'; // Case-insensitive
68+
69+
return implode(' ', $options);
70+
}
71+
72+
private function makeIncludeOptions(WatchList $watchList): array
73+
{
74+
$options = [];
75+
// Before including we need to ignore everything
76+
if (empty($watchList->ignore())) {
77+
$options[] = '-e ".*"';
78+
}
79+
80+
$regexpWithExtensions = array_map(
81+
static function ($extension) {
82+
return str_replace('*.', '.', $extension) . '$';
83+
},
84+
$watchList->fileExtensions()
85+
);
86+
$options[] = '-i ' . implode(' ', $regexpWithExtensions);
87+
return $options;
88+
}
89+
}
Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,35 @@
1-
<?php declare(strict_types=1);
1+
<?php
2+
3+
declare(strict_types=1);
24

35
namespace seregazhuk\PhpWatcher\Filesystem\ResourceWatcherBased;
46

57
use Evenement\EventEmitter;
68
use React\EventLoop\LoopInterface;
79
use seregazhuk\PhpWatcher\Config\WatchList;
10+
use seregazhuk\PhpWatcher\Filesystem\ChangesListener as ChangesListenerInterface;
11+
use Yosymfony\ResourceWatcher\ResourceWatcher;
812

9-
final class ChangesListener extends EventEmitter implements
10-
\seregazhuk\PhpWatcher\Filesystem\ChangesListener
13+
final class ChangesListener extends EventEmitter implements ChangesListenerInterface
1114
{
1215
private const INTERVAL = 0.15;
1316

14-
private $loop;
17+
private LoopInterface $loop;
18+
19+
private ResourceWatcher $watcher;
1520

16-
public function __construct(LoopInterface $loop)
21+
public function __construct(WatchList $watchList, LoopInterface $loop)
1722
{
1823
$this->loop = $loop;
24+
$this->watcher = ResourceWatcherBuilder::create($watchList);
1925
}
2026

21-
public function start(WatchList $watchList): void
27+
public function start(): void
2228
{
23-
$watcher = ResourceWatcherBuilder::create($watchList);
24-
2529
$this->loop->addPeriodicTimer(
2630
self::INTERVAL,
27-
function () use ($watcher) {
28-
if ($watcher->findChanges()->hasChanges()) {
31+
function () {
32+
if ($this->watcher->findChanges()->hasChanges()) {
2933
$this->emit('change');
3034
}
3135
}
@@ -36,4 +40,8 @@ public function onChange(callable $callback): void
3640
{
3741
$this->on('change', $callback);
3842
}
43+
44+
public function stop(): void
45+
{
46+
}
3947
}

src/Watcher.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
namespace seregazhuk\PhpWatcher;
66

77
use React\EventLoop\LoopInterface;
8-
use seregazhuk\PhpWatcher\Config\WatchList;
9-
use seregazhuk\PhpWatcher\Filesystem\ResourceWatcherBased\ChangesListener;
8+
use seregazhuk\PhpWatcher\Filesystem\ChangesListener;
109

1110
final class Watcher
1211
{
13-
private $loop;
12+
private LoopInterface $loop;
1413

15-
private $filesystemListener;
14+
private ChangesListener $filesystemListener;
1615

1716
public function __construct(LoopInterface $loop, ChangesListener $filesystemListener)
1817
{
@@ -22,13 +21,12 @@ public function __construct(LoopInterface $loop, ChangesListener $filesystemList
2221

2322
public function startWatching(
2423
ProcessRunner $processRunner,
25-
WatchList $watchList,
2624
int $signal,
2725
float $delayToRestart
2826
): void {
2927
$processRunner->start();
3028

31-
$this->filesystemListener->start($watchList);
29+
$this->filesystemListener->start();
3230
$this->filesystemListener->onChange(
3331
static function () use ($processRunner, $signal, $delayToRestart) {
3432
$processRunner->stop($signal);

0 commit comments

Comments
 (0)