Skip to content

Commit 4d7dae5

Browse files
committed
Add watch method to watch filesystem for changes
1 parent 5440d67 commit 4d7dae5

17 files changed

+758
-0
lines changed

src/Symfony/Component/Filesystem/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
-----
66

77
* support for passing a `null` value to `Filesystem::isAbsolutePath()` is deprecated and will be removed in 5.0
8+
* added `watch()` method to watch filesystem for changes
89

910
4.3.0
1011
-----

src/Symfony/Component/Filesystem/Filesystem.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
1515
use Symfony\Component\Filesystem\Exception\InvalidArgumentException;
1616
use Symfony\Component\Filesystem\Exception\IOException;
17+
use Symfony\Component\Filesystem\Watcher\FileChangeWatcher;
18+
use Symfony\Component\Filesystem\Watcher\INotifyWatcher;
1719

1820
/**
1921
* Provides basic utility to manipulate the file system.
@@ -725,6 +727,25 @@ public function appendToFile($filename, $content)
725727
}
726728
}
727729

730+
/**
731+
* Watches a file or directory for any changes, and calls $callback when any changes are detected.
732+
*
733+
* @param mixed $path The path to watch for changes. Can be a path to a file or directory, iterator or array with paths
734+
* @param callable $callback The callback to execute when a change is detected
735+
*
736+
* @throws \InvalidArgumentException
737+
*/
738+
public function watch($path, callable $callback)
739+
{
740+
if (\extension_loaded('inotify')) {
741+
$watcher = new INotifyWatcher();
742+
} else {
743+
$watcher = new FileChangeWatcher();
744+
}
745+
746+
$watcher->watch($path, $callback);
747+
}
748+
728749
private function toIterable($files): iterable
729750
{
730751
return \is_array($files) || $files instanceof \Traversable ? $files : [$files];
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Filesystem\Tests\Fixtures;
13+
14+
use Symfony\Component\Filesystem\Watcher\FileChangeEvent;
15+
use Symfony\Component\Filesystem\Watcher\Resource\ResourceInterface;
16+
17+
class ChangeFileResource implements ResourceInterface
18+
{
19+
private $path;
20+
21+
public function __construct(string $path)
22+
{
23+
$this->path = $path;
24+
}
25+
26+
public function detectChanges(): array
27+
{
28+
return [new FileChangeEvent($this->path, FileChangeEvent::FILE_CHANGED)];
29+
}
30+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Filesystem\Tests\Watcher;
13+
14+
use Symfony\Component\Filesystem\Tests\FilesystemTestCase;
15+
use Symfony\Component\Filesystem\Tests\Fixtures\ChangeFileResource;
16+
use Symfony\Component\Filesystem\Watcher\FileChangeEvent;
17+
use Symfony\Component\Filesystem\Watcher\FileChangeWatcher;
18+
use Symfony\Component\Filesystem\Watcher\Resource\ResourceInterface;
19+
20+
class FileSystemWatchTest extends FilesystemTestCase
21+
{
22+
public function testWatch()
23+
{
24+
$workspace = $this->workspace;
25+
26+
$locator = new class($workspace) {
27+
private $workspace;
28+
29+
public function __construct($workspace)
30+
{
31+
$this->workspace = $workspace;
32+
}
33+
34+
public function locate($path): ?ResourceInterface
35+
{
36+
return new ChangeFileResource($this->workspace.'/foobar.txt');
37+
}
38+
};
39+
40+
$watcher = new FileChangeWatcher();
41+
$ref = new \ReflectionProperty($watcher, 'locator');
42+
$ref->setAccessible(true);
43+
$ref->setValue($watcher, $locator);
44+
45+
$count = 0;
46+
$watcher->watch($this->workspace, function ($file, $code) use (&$count) {
47+
$this->assertSame($this->workspace.'/foobar.txt', $file);
48+
$this->assertSame(FileChangeEvent::FILE_CHANGED, $code);
49+
++$count;
50+
51+
if (2 === $count) {
52+
return false;
53+
}
54+
});
55+
56+
$this->assertSame(2, $count);
57+
}
58+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Filesystem\Tests\Watcher\Resource;
13+
14+
use Symfony\Component\Filesystem\Tests\FilesystemTestCase;
15+
use Symfony\Component\Filesystem\Watcher\FileChangeEvent;
16+
use Symfony\Component\Filesystem\Watcher\Resource\ArrayResource;
17+
use Symfony\Component\Filesystem\Watcher\Resource\FileResource;
18+
19+
class ArrayResourceTest extends FilesystemTestCase
20+
{
21+
public function testFileChange()
22+
{
23+
$file = $this->workspace.'/foo.txt';
24+
touch($file);
25+
26+
$resource = new ArrayResource([new FileResource($file)]);
27+
28+
$this->assertSame([], $resource->detectChanges());
29+
30+
touch($file, time() + 1);
31+
32+
$this->assertEquals([new FileChangeEvent($file, FileChangeEvent::FILE_CHANGED)], $resource->detectChanges());
33+
$this->assertSame([], $resource->detectChanges());
34+
}
35+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Filesystem\Tests\Watcher\Resource;
13+
14+
use Symfony\Component\Filesystem\Tests\FilesystemTestCase;
15+
use Symfony\Component\Filesystem\Watcher\FileChangeEvent;
16+
use Symfony\Component\Filesystem\Watcher\Resource\DirectoryResource;
17+
18+
class DirectoryResourceTest extends FilesystemTestCase
19+
{
20+
public function testCreateFile()
21+
{
22+
$dir = $this->workspace.'/foo';
23+
mkdir($dir);
24+
25+
$resource = new DirectoryResource($dir);
26+
27+
$this->assertSame([], $resource->detectChanges());
28+
29+
touch($dir.'/foo.txt');
30+
31+
$this->assertEquals([new FileChangeEvent($dir.'/foo.txt', FileChangeEvent::FILE_CREATED)], $resource->detectChanges());
32+
$this->assertSame([], $resource->detectChanges());
33+
}
34+
35+
public function testDeleteFile()
36+
{
37+
$dir = $this->workspace.'/foo';
38+
mkdir($dir);
39+
40+
touch($dir.'/foo.txt');
41+
touch($dir.'/bar.txt');
42+
43+
$resource = new DirectoryResource($dir);
44+
45+
$this->assertSame([], $resource->detectChanges());
46+
47+
unlink($dir.'/foo.txt');
48+
49+
$this->assertEquals([new FileChangeEvent($dir.'/foo.txt', FileChangeEvent::FILE_DELETED)], $resource->detectChanges());
50+
$this->assertSame([], $resource->detectChanges());
51+
}
52+
53+
public function testFileChanges()
54+
{
55+
$dir = $this->workspace.'/foo';
56+
mkdir($dir);
57+
58+
touch($dir.'/foo.txt');
59+
touch($dir.'/bar.txt');
60+
61+
$resource = new DirectoryResource($dir);
62+
63+
$this->assertSame([], $resource->detectChanges());
64+
65+
touch($dir.'/foo.txt', time() + 1);
66+
67+
$this->assertEquals([new FileChangeEvent($dir.'/foo.txt', FileChangeEvent::FILE_CHANGED)], $resource->detectChanges());
68+
$this->assertSame([], $resource->detectChanges());
69+
}
70+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Filesystem\Tests\Watcher\Resource;
13+
14+
use Symfony\Component\Filesystem\Tests\FilesystemTestCase;
15+
use Symfony\Component\Filesystem\Watcher\FileChangeEvent;
16+
use Symfony\Component\Filesystem\Watcher\Resource\FileResource;
17+
18+
class FileResourceTest extends FilesystemTestCase
19+
{
20+
public function testFileChanges()
21+
{
22+
$file = $this->workspace.'/foo.txt';
23+
touch($file);
24+
25+
$resource = new FileResource($file);
26+
27+
$this->assertSame([], $resource->detectChanges());
28+
29+
touch($file, time() + 1);
30+
31+
$this->assertEquals([new FileChangeEvent($file, FileChangeEvent::FILE_CHANGED)], $resource->detectChanges());
32+
$this->assertSame([], $resource->detectChanges());
33+
}
34+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Filesystem\Tests\Watcher\Resource\Locator;
13+
14+
use Symfony\Component\Filesystem\Tests\FilesystemTestCase;
15+
use Symfony\Component\Filesystem\Watcher\Resource\ArrayResource;
16+
use Symfony\Component\Filesystem\Watcher\Resource\DirectoryResource;
17+
use Symfony\Component\Filesystem\Watcher\Resource\FileResource;
18+
use Symfony\Component\Filesystem\Watcher\Resource\Locator\FileResourceLocator;
19+
20+
class FileResourceLocatorTest extends FilesystemTestCase
21+
{
22+
public function testLocateIterator()
23+
{
24+
$locator = new FileResourceLocator();
25+
26+
$path = new \ArrayIterator([$this->createFile('foo.txt')]);
27+
28+
$this->assertEquals(new ArrayResource([new FileResource($this->workspace.'/foo.txt')]), $locator->locate($path));
29+
}
30+
31+
public function testLocateSplFileInfo()
32+
{
33+
$locator = new FileResourceLocator();
34+
35+
$path = new \SplFileInfo($this->createFile('foo.txt'));
36+
37+
$this->assertEquals(new FileResource($this->workspace.'/foo.txt'), $locator->locate($path));
38+
}
39+
40+
public function testFilePath()
41+
{
42+
$locator = new FileResourceLocator();
43+
44+
$path = $this->createFile('foo.txt');
45+
46+
$this->assertEquals(new FileResource($this->workspace.'/foo.txt'), $locator->locate($path));
47+
}
48+
49+
public function testGlob()
50+
{
51+
$locator = new FileResourceLocator();
52+
53+
$this->createFile('bar.txt');
54+
$this->createFile('foo.txt');
55+
56+
$this->assertEquals(
57+
new ArrayResource([new FileResource($this->workspace.'/bar.txt'), new FileResource($this->workspace.'/foo.txt')]),
58+
$locator->locate($this->workspace.'/*.txt')
59+
);
60+
}
61+
62+
public function testArray()
63+
{
64+
$locator = new FileResourceLocator();
65+
66+
$path = [$this->createFile('foo.txt')];
67+
68+
$this->assertEquals(new ArrayResource([new FileResource($this->workspace.'/foo.txt')]), $locator->locate($path));
69+
}
70+
71+
public function testDirectory()
72+
{
73+
$locator = new FileResourceLocator();
74+
75+
$dir = $this->createDirecty('foobar');
76+
77+
$this->assertEquals(new DirectoryResource($this->workspace.'/foobar'), $locator->locate($dir));
78+
}
79+
80+
private function createFile(string $file)
81+
{
82+
$fullPath = $this->workspace.'/'.$file;
83+
touch($fullPath);
84+
85+
return $fullPath;
86+
}
87+
88+
private function createDirecty(string $dir)
89+
{
90+
$fullPath = $this->workspace.'/'.$dir;
91+
92+
mkdir($fullPath, 0777, true);
93+
94+
return $fullPath;
95+
}
96+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy