Skip to content

[Config] additions from ResourceWatcher #5744

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 15 commits into from
Closed
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
165 changes: 148 additions & 17 deletions src/Symfony/Component/Config/Resource/DirectoryResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DirectoryResource implements ResourceInterface, \Serializable
class DirectoryResource implements ResourceInterface
{
private $resource;
private $pattern;
Expand All @@ -33,6 +33,82 @@ public function __construct($resource, $pattern = null)
$this->pattern = $pattern;
}

/**
* Returns the list of filtered file and directory children of directory resource.
*
* @return array An array of files
*/
public function getFilteredChildren()
{
if (!$this->exists()) {
return array();
}

$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->resource, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST
);

$children = array();
foreach ($iterator as $file) {
// if regex filtering is enabled only return matching files
if ($file->isFile() && !$this->hasFile($file)) {
continue;
}

// always monitor directories for changes, except the .. entries
// (otherwise deleted files wouldn't get detected)
if ($file->isDir() && '/..' === substr($file, -3)) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@everzet is the check for .. really needed as you are using \FilesystemIterator::SKIP_DOTS ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, removing this continue does not break the testsuite

continue;
}

$children[] = $file;
}

return $children;
}

/**
* Returns child resources that matches directory filters.
*
* @return array
*/
public function getFilteredResources()
{
if (!$this->exists()) {
return array();
}

$iterator = new \DirectoryIterator($this->resource);

$resources = array();
foreach ($iterator as $file) {
// if regex filtering is enabled only return matching files
if ($file->isFile() && !$this->hasFile($file)) {
continue;
}

// always monitor directories for changes, except the .. entries
// (otherwise deleted files wouldn't get detected)
if ($file->isDir() && '/..' === substr($file, -3)) {
continue;
}

// if file is dot - continue
if ($file->isDot()) {
continue;
}

if ($file->isFile()) {
$resources[] = new FileResource($file->getRealPath());
} elseif ($file->isDir()) {
$resources[] = new DirectoryResource($file->getRealPath());
}
}

return $resources;
}

/**
* Returns a string representation of the Resource.
*
Expand All @@ -53,11 +129,62 @@ public function getResource()
return $this->resource;
}

/**
* Returns check pattern.
*
* @return mixed
*/
public function getPattern()
{
return $this->pattern;
}

/**
* Checks that passed file exists in resource and matches resource filters.
*
* @param SplFileInfo|string $file
*
* @return Boolean
*/
public function hasFile($file)
{
if (!$file instanceof \SplFileInfo) {
$file = new \SplFileInfo($file);
}

if (0 !== strpos($file->getRealPath(), realpath($this->resource))) {
return false;
}

if ($this->pattern) {
return (bool) preg_match($this->pattern, $file->getBasename());
}

return true;
}

/**
* Returns resource mtime.
*
* @return integer
*/
public function getModificationTime()
{
if (!$this->exists()) {
return -1;
}

clearstatcache(true, $this->resource);
$newestMTime = filemtime($this->resource);

foreach ($this->getFilteredChildren() as $file) {
clearstatcache(true, (string) $file);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@everzet is it really needed to clear the stat cache each time ? @fabpot reported a performance impact with these changes, and the only difference when calling isFresh on resources is that clearstatcache has now been applied on it.

$newestMTime = max($file->getMTime(), $newestMTime);
}

return $newestMTime;
}

/**
* Returns true if the resource has not been updated since the given timestamp.
*
Expand All @@ -67,27 +194,31 @@ public function getPattern()
*/
public function isFresh($timestamp)
{
if (!is_dir($this->resource)) {
if (!$this->exists()) {
return false;
}

$newestMTime = filemtime($this->resource);
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->resource), \RecursiveIteratorIterator::SELF_FIRST) as $file) {
// if regex filtering is enabled only check matching files
if ($this->pattern && $file->isFile() && !preg_match($this->pattern, $file->getBasename())) {
continue;
}

// always monitor directories for changes, except the .. entries
// (otherwise deleted files wouldn't get detected)
if ($file->isDir() && '/..' === substr($file, -3)) {
continue;
}
return $this->getModificationTime() < $timestamp;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be <=?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Baachi The existing logic is also using <

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, but the FileResource class use <=.
So i would suggest, that we use <= here, too.

}

$newestMTime = max($file->getMTime(), $newestMTime);
}
/**
* Returns true if the resource exists in the filesystem.
*
* @return Boolean
*/
public function exists()
{
return is_dir($this->resource);
}

return $newestMTime < $timestamp;
/**
* Returns unique resource ID.
*
* @return string
*/
public function getId()
{
return md5('d'.$this->resource.$this->pattern);
}

public function serialize()
Expand Down
44 changes: 40 additions & 4 deletions src/Symfony/Component/Config/Resource/FileResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FileResource implements ResourceInterface, \Serializable
class FileResource implements ResourceInterface
{
private $resource;

Expand All @@ -29,7 +29,7 @@ class FileResource implements ResourceInterface, \Serializable
*/
public function __construct($resource)
{
$this->resource = realpath($resource);
$this->resource = file_exists($resource) ? realpath($resource) : $resource;
}

/**
Expand All @@ -52,6 +52,22 @@ public function getResource()
return $this->resource;
}

/**
* Returns resource mtime.
*
* @return integer
*/
public function getModificationTime()
{
if (!$this->exists()) {
return -1;
}

clearstatcache(true, $this->resource);

return filemtime($this->resource);
}

/**
* Returns true if the resource has not been updated since the given timestamp.
*
Expand All @@ -61,11 +77,31 @@ public function getResource()
*/
public function isFresh($timestamp)
{
if (!file_exists($this->resource)) {
if (!$this->exists()) {
return false;
}

return filemtime($this->resource) < $timestamp;
return $this->getModificationTime() <= $timestamp;
}

/**
* Returns true if the resource exists in the filesystem.
*
* @return Boolean
*/
public function exists()
{
return is_file($this->resource);
}

/**
* Returns unique resource ID.
*
* @return string
*/
public function getId()
{
return md5('f'.$this->resource);
}

public function serialize()
Expand Down
23 changes: 22 additions & 1 deletion src/Symfony/Component/Config/Resource/ResourceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ResourceInterface
interface ResourceInterface extends \Serializable
{
/**
* Returns a string representation of the Resource.
Expand All @@ -34,10 +34,31 @@ public function __toString();
*/
public function isFresh($timestamp);

/**
* Returns resource mtime.
*
* @return integer
*/
public function getModificationTime();

/**
* Returns true if the resource exists in the filesystem.
*
* @return Boolean
*/
public function exists();

/**
* Returns the resource tied to this Resource.
*
* @return mixed The resource
*/
public function getResource();

/**
* Returns unique resource ID.
*
* @return string
*/
public function getId();
}
Loading