Skip to content

[2.1] Resource watcher component #391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
55cc03b
[ResourceWatcher] resource change event created
everzet Mar 25, 2011
9eec6a1
[ResourceWatcher] resource event listener interface described
everzet Mar 25, 2011
2435265
[ResourceWatcher] created basic resource event listener object
everzet Mar 25, 2011
27afac8
[ResourceWatcher] resource state checker interface described
everzet Mar 25, 2011
d1249df
[ResourceWatcher] created recursive iterator resource state checker
everzet Mar 25, 2011
c8bfa18
[ResourceWatcher] resources tracker interface described
everzet Mar 25, 2011
73b3ea0
[ResourceWatcher] recursive iterator resources tracker created
everzet Mar 25, 2011
150bc5a
[ResourceWatcher] resource watcher object created
everzet Mar 25, 2011
f78ff91
[ResourceWatcher] inotify tracker added
everzet Mar 25, 2011
fe664a6
[ResourceWatcher] use best tracker by default
everzet Mar 25, 2011
d40d2e0
[ResourceWatcher] renamed Event::handes to Event::supports to be cons…
everzet Mar 25, 2011
d661a16
[ResourceWatcher] fixed non-empty directory events
everzet Aug 20, 2011
dcb58fa
[Config] updated resources API to be more explicit
everzet Nov 19, 2011
d9b88bd
[Config] moved DirectoryResource childs retrieving to the special get…
everzet Nov 19, 2011
62a116d
[ResourceWatcher] cleaned code little bit
everzet Nov 19, 2011
a714bed
[ResourceWatcher] refactored recursive state checkers into 2 separate…
everzet Nov 19, 2011
7a5587d
[Config] getFilteredChildResources() method added to DirectoryResource
everzet Nov 19, 2011
c6d85a6
[ResourceWatcher] refactored recursive iterator tracker to support ne…
everzet Nov 19, 2011
7429646
[Config] updated DirectoryResource tests
everzet Nov 25, 2011
ebd6fd8
[Config] update FileResourceTest
everzet Nov 25, 2011
0ab201d
[ResourceWatcher] removed InotifyTracker as i have no ability to test…
everzet Nov 25, 2011
816373d
[Config] added new methods and their tests to File and Directory reso…
everzet Nov 29, 2011
bc8e437
[ResourceWatcher] updated ResourceWatcher and TrackerInterface
everzet Nov 29, 2011
40e7f4e
[ResourceWatcher] added time information to events
everzet Nov 29, 2011
6752b30
[ResourceWatcher] updated FileStateChecker to match FileResource API
everzet Nov 29, 2011
e73385b
[ResourceWatcher] refactored DirectoryStateChecker
everzet Nov 29, 2011
554a48d
[ResourceWatcher] refactored RecursiveIteratorTracker
everzet Nov 29, 2011
cd9a2a7
[ResourceWatcher] updated StateCheckerInterface
everzet Nov 29, 2011
51172e2
[ResourceWatcher] return of the InotifyTracker
everzet Nov 29, 2011
f0f9fd9
[ResourceWatcher] removed unneeded code
fabpot Dec 5, 2011
cb8a3d1
[ResourceWatcher] added an exception when inotify is not available
fabpot Dec 5, 2011
879ddff
[Config] made ResourceInterface extends Serializable
fabpot Dec 5, 2011
a62c413
[ResourceWatcher] removed dead code
fabpot Dec 5, 2011
fbb5849
[ResourceWatcher] added the possibility to watch files that do not ex…
fabpot Dec 5, 2011
51b8613
[ResourceWatcher] added exception classes for the component
fabpot Dec 29, 2011
11611e9
-
fabpot Dec 29, 2011
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
Prev Previous commit
Next Next commit
[ResourceWatcher] resource watcher object created
  • Loading branch information
everzet committed Nov 25, 2011
commit 150bc5a738cbc2cf61fdd1ef154d0fe10ea9ac6b
151 changes: 151 additions & 0 deletions src/Symfony/Component/ResourceWatcher/ResourceWatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\ResourceWatcher;

use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\ResourceWatcher\Event\Event;
use Symfony\Component\ResourceWatcher\Event\EventListener;
use Symfony\Component\ResourceWatcher\Event\EventListenerInterface;
use Symfony\Component\ResourceWatcher\Tracker\TrackerInterface;

/**
* Resources changes watcher.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class ResourceWatcher
{
private $tracker;
private $watching = true;
private $listeners = array();

/**
* Initializes path watcher.
*
* @param TrackerInterface $tracker
*/
public function __construct(TrackerInterface $tracker)
{
$this->tracker = $tracker;
}

/**
* Track resource with watcher.
*
* @param ResourceInterface $resource resource to track
* @param callable $callback event callback
* @param integer $eventsMask event types bitmask
*/
public function track(ResourceInterface $resource, $callback, $eventsMask = Event::ALL)
{
if (!is_callable($callback)) {
throw new \InvalidArgumentException('Second argument to track() should be callable.');
}

$this->addListener(new EventListener($resource, $callback, $eventsMask));
}

/**
* Adds resource event listener to watcher.
*
* @param EventListenerInterface $listener resource event listener
*/
public function addListener(EventListenerInterface $listener)
{
if (!$this->getTracker()->isResourceTracked($listener->getResource())) {
$this->getTracker()->track($listener->getResource());
}

$trackingId = $this->getTracker()->getResourceTrackingId($listener->getResource());

if (!isset($this->listeners[$trackingId])) {
$this->listeners[$trackingId] = array();
}

$this->listeners[$trackingId][] = $listener;
}

/**
* Returns true if watcher is currently watching on tracked resources (started).
*
* @return Boolean
*/
public function isWatching()
{
return $this->watching;
}

/**
* Starts wathing on tracked resources.
*
* @param integer $checkInterval check interval in microseconds
* @param integer $timeLimit maximum watching time limit in microseconds
*/
public function start($checkInterval = 1000000, $timeLimit = null)
{
$totalTime = 0;
$this->watching = true;

while ($this->watching) {
usleep($checkInterval);
$totalTime += $checkInterval;

if (null !== $timeLimit && $totalTime > $timeLimit) {
break;
}

if (count($events = $this->getTracker()->checkChanges())) {
$this->notifyListeners($events);
}
}

$this->watching = false;
}

/**
* Stop watching on tracked resources.
*/
public function stop()
{
$this->watching = false;
}

/**
* Returns current tracker instance.
*
* @return TrackerInterface
*/
protected function getTracker()
{
return $this->tracker;
}

/**
* Notifies all registered resource event listeners about their events.
*
* @param array $events array of resource events
*/
private function notifyListeners(array $events)
{
foreach ($events as $event) {
$trackingId = $event->getTrackingId();

if (isset($this->listeners[$trackingId])) {
foreach ($this->listeners[$trackingId] as $listener) {
if ($listener->handles($event)) {
call_user_func($listener->getCallback(), $event);
}
}
}
}
}
}
160 changes: 160 additions & 0 deletions tests/Symfony/Tests/Component/ResourceWatcher/ResourceWatcherTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Tests\Component\ResourceWatcher;

use Symfony\Component\ResourceWatcher\ResourceWatcher;
use Symfony\Component\ResourceWatcher\Event\Event;

class ResourceWatcherTest extends \PHPUnit_Framework_TestCase
{
public function testUntrackedResourceTrack()
{
$tracker = $this
->getMockBuilder('Symfony\Component\ResourceWatcher\Tracker\TrackerInterface')
->getMock();

$resource = $this
->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')
->getMock();

$tracker
->expects($this->once())
->method('isResourceTracked')
->with($resource)
->will($this->returnValue(false));
$tracker
->expects($this->once())
->method('track')
->with($resource)
->will($this->returnValue(null));
$tracker
->expects($this->once())
->method('getResourceTrackingId')
->with($resource);

$watcher = new ResourceWatcher($tracker);
$watcher->track($resource, function(){});
}

public function testTrackedResourceTrack()
{
$tracker = $this
->getMockBuilder('Symfony\Component\ResourceWatcher\Tracker\TrackerInterface')
->getMock();

$resource = $this
->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')
->getMock();

$tracker
->expects($this->once())
->method('isResourceTracked')
->with($resource)
->will($this->returnValue(true));
$tracker
->expects($this->never())
->method('track');
$tracker
->expects($this->once())
->method('getResourceTrackingId')
->with($resource);

$watcher = new ResourceWatcher($tracker);
$watcher->track($resource, function(){});
}

public function testWatching()
{
$tracker = $this
->getMockBuilder('Symfony\Component\ResourceWatcher\Tracker\TrackerInterface')
->getMock();

$resourceMockBuilder = $this
->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface');

$resource1 = $resourceMockBuilder->getMock();
$resource2 = $resourceMockBuilder->getMock();

$listenerMockBuilder = $this
->getMockBuilder('Symfony\Component\ResourceWatcher\Event\EventListenerInterface');

$listener1 = $listenerMockBuilder->getMock();
$listener2 = $listenerMockBuilder->getMock();
$listener3 = $listenerMockBuilder->getMock();

$listener1
->expects($this->exactly(3))
->method('getResource')
->will($this->returnValue($resource1));
$listener2
->expects($this->exactly(3))
->method('getResource')
->will($this->returnValue($resource2));
$listener3
->expects($this->exactly(2))
->method('getResource')
->will($this->returnValue($resource2));

$tracker
->expects($this->exactly(3))
->method('isResourceTracked')
->will($this->onConsecutiveCalls(false, false, true));
$tracker
->expects($this->exactly(2))
->method('track');
$tracker
->expects($this->exactly(3))
->method('getResourceTrackingId')
->will($this->onConsecutiveCalls(1, 2, 2));

$watcher = new ResourceWatcher($tracker);
$watcher->addListener($listener1);
$watcher->addListener($listener2);
$watcher->addListener($listener3);

$listener1
->expects($this->once())
->method('handles')
->will($this->returnValue(true));
$listener2
->expects($this->exactly(2))
->method('handles')
->will($this->onConsecutiveCalls(false, false));
$listener3
->expects($this->exactly(2))
->method('handles')
->will($this->onConsecutiveCalls(true, true));

$listener1
->expects($this->once())
->method('getCallback')
->will($this->returnValue(function($e){}));
$listener2
->expects($this->never())
->method('getCallback');
$listener3
->expects($this->exactly(2))
->method('getCallback')
->will($this->returnValue(function($e){}));

$tracker
->expects($this->once())
->method('checkChanges')
->will($this->returnValue(array(
new Event(1, $resource1, 1),
new Event(2, $resource2, 1),
new Event(2, $resource2, 1),
)));

$watcher->start(1, 1);
}
}