Skip to content

Commit cdbec19

Browse files
committed
Add watch option to lint commands
1 parent 89d1b65 commit cdbec19

20 files changed

+828
-4
lines changed

src/Symfony/Bridge/Twig/Command/LintCommand.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use Symfony\Component\Console\Input\InputOption;
1818
use Symfony\Component\Console\Output\OutputInterface;
1919
use Symfony\Component\Console\Style\SymfonyStyle;
20+
use Symfony\Component\Filesystem\Filesystem;
21+
use Symfony\Component\Filesystem\Watcher\FileChangeEvent;
2022
use Symfony\Component\Finder\Finder;
2123
use Twig\Environment;
2224
use Twig\Error\Error;
@@ -47,6 +49,7 @@ protected function configure()
4749
$this
4850
->setDescription('Lints a template and outputs encountered errors')
4951
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
52+
->addOption('watch', null, InputOption::VALUE_NONE, 'Watch the files or directories for changes')
5053
->addArgument('filename', InputArgument::IS_ARRAY)
5154
->setHelp(<<<'EOF'
5255
The <info>%command.name%</info> command lints a template and outputs to STDOUT
@@ -90,7 +93,24 @@ protected function execute(InputInterface $input, OutputInterface $output)
9093

9194
$filesInfo = $this->getFilesInfo($filenames);
9295

93-
return $this->display($input, $output, $io, $filesInfo);
96+
$display = $this->display($input, $output, $io, $filesInfo);
97+
98+
if (!$input->getOption('watch')) {
99+
return $display;
100+
}
101+
102+
if (!class_exists(Filesystem::class)) {
103+
throw new \RuntimeException('The symfony/filesystem package is required in order to watch files for changes.');
104+
}
105+
106+
$io->note('Watching files for changes');
107+
108+
$fs = new Filesystem();
109+
$fs->watch($filenames, function (string $file, int $event) use ($io, $input, $output) {
110+
if (FileChangeEvent::FILE_DELETED !== $event) {
111+
$this->display($input, $output, $io, array($this->validate(file_get_contents($file), $file)));
112+
}
113+
});
94114
}
95115

96116
private function getFilesInfo(array $filenames)

src/Symfony/Component/Filesystem/Filesystem.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111

1212
namespace Symfony\Component\Filesystem;
1313

14+
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
1415
use Symfony\Component\Filesystem\Exception\InvalidArgumentException;
1516
use Symfony\Component\Filesystem\Exception\IOException;
16-
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
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.
@@ -724,6 +726,27 @@ public function appendToFile($filename, $content)
724726
}
725727
}
726728

729+
/**
730+
* Watches a file or directory for any changes, and calls $callback when any changes is detected.
731+
*
732+
* @param mixed $path The path to watch for changes. Can be a path to a file or directory, iterator or array with paths
733+
* @param callable $callback The callback to execute when a change is detected
734+
* @param int $timeout The number of watch iterations to run before the watch is halted. -1 to continue watching for infinity
735+
* @param int $wait The number of seconds delay between detecting changes
736+
*
737+
* @throws \InvalidArgumentException
738+
*/
739+
public function watch($path, callable $callback, int $timeout = -1, int $wait = 1)
740+
{
741+
if (extension_loaded('inotify')) {
742+
$watcher = new INotifyWatcher();
743+
} else {
744+
$watcher = new FileChangeWatcher(null, $timeout, $wait);
745+
}
746+
747+
$watcher->watch($path, $callback);
748+
}
749+
727750
private function toIterable($files): iterable
728751
{
729752
return is_array($files) || $files instanceof \Traversable ? $files : array($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 array(new FileChangeEvent($this->path, FileChangeEvent::FILE_CHANGED));
29+
}
30+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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\Locator\LocatorInterface;
19+
use Symfony\Component\Filesystem\Watcher\Resource\ResourceInterface;
20+
21+
class FileSystemWatchTest extends FilesystemTestCase
22+
{
23+
public function testWatch()
24+
{
25+
$workspace = $this->workspace;
26+
27+
$locator = new class($workspace) implements LocatorInterface {
28+
private $workspace;
29+
30+
public function __construct($workspace)
31+
{
32+
$this->workspace = $workspace;
33+
}
34+
35+
public function locate($path): ?ResourceInterface
36+
{
37+
return new ChangeFileResource($this->workspace.'/foobar.txt');
38+
}
39+
};
40+
41+
$watcher = new FileChangeWatcher($locator, 2);
42+
43+
$count = 0;
44+
$watcher->watch($this->workspace, function ($file, $code) use (&$count) {
45+
$this->assertSame($this->workspace.'/foobar.txt', $file);
46+
$this->assertSame(FileChangeEvent::FILE_CHANGED, $code);
47+
++$count;
48+
});
49+
50+
$this->assertSame(2, $count);
51+
}
52+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the symfony project.
7+
*
8+
* @author pierre
9+
* @copyright Copyright (c) 2018
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(array(new FileResource($file)));
27+
28+
$this->assertSame(array(), $resource->detectChanges());
29+
30+
touch($file, time() + 1);
31+
32+
$this->assertEquals(array(new FileChangeEvent($file, FileChangeEvent::FILE_CHANGED)), $resource->detectChanges());
33+
$this->assertSame(array(), $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+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the symfony project.
7+
*
8+
* @author pierre
9+
* @copyright Copyright (c) 2018
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(array(), $resource->detectChanges());
28+
29+
touch($dir.'/foo.txt');
30+
31+
$this->assertEquals(array(new FileChangeEvent($dir.'/foo.txt', FileChangeEvent::FILE_CREATED)), $resource->detectChanges());
32+
$this->assertSame(array(), $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(array(), $resource->detectChanges());
46+
47+
unlink($dir.'/foo.txt');
48+
49+
$this->assertEquals(array(new FileChangeEvent($dir.'/foo.txt', FileChangeEvent::FILE_DELETED)), $resource->detectChanges());
50+
$this->assertSame(array(), $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(array(), $resource->detectChanges());
64+
65+
touch($dir.'/foo.txt', time() + 1);
66+
67+
$this->assertEquals(array(new FileChangeEvent($dir.'/foo.txt', FileChangeEvent::FILE_CHANGED)), $resource->detectChanges());
68+
$this->assertSame(array(), $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+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the symfony project.
7+
*
8+
* @author pierre
9+
* @copyright Copyright (c) 2018
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(array(), $resource->detectChanges());
28+
29+
touch($file, time() + 1);
30+
31+
$this->assertEquals(array(new FileChangeEvent($file, FileChangeEvent::FILE_CHANGED)), $resource->detectChanges());
32+
$this->assertSame(array(), $resource->detectChanges());
33+
}
34+
}

0 commit comments

Comments
 (0)