diff --git a/src/DI/ContainerLoader.php b/src/DI/ContainerLoader.php index 185ca6e19..41a35c7bc 100644 --- a/src/DI/ContainerLoader.php +++ b/src/DI/ContainerLoader.php @@ -78,8 +78,12 @@ private function loadFile($class, $generator) throw new Nette\IOException("Unable to acquire exclusive lock on '$file.lock'."); } - if (!is_file($file) || $this->isExpired($file)) { - list($toWrite[$file], $toWrite["$file.meta"]) = $this->generate($class, $generator); + if (!is_file($file) || $this->isExpired($file, $updatedMeta)) { + if (isset($updatedMeta)) { + $toWrite["$file.meta"] = $updatedMeta; + } else { + list($toWrite[$file], $toWrite["$file.meta"]) = $this->generate($class, $generator); + } foreach ($toWrite as $name => $content) { if (file_put_contents("$name.tmp", $content) !== strlen($content) || !rename("$name.tmp", $name)) { @@ -98,11 +102,13 @@ private function loadFile($class, $generator) } - private function isExpired($file) + private function isExpired($file, &$updatedMeta = NULL) { if ($this->autoRebuild) { - $meta = @unserialize(file_get_contents("$file.meta")); // @ - file may not exist - return empty($meta[0]) || DependencyChecker::isExpired(...$meta); + $meta = $orig = @unserialize((string) file_get_contents("$file.meta")); // @ - file may not exist + return empty($meta[0]) + || DependencyChecker::isExpired(...$meta) + || ($orig !== $meta && $updatedMeta = serialize($meta)); } return FALSE; } diff --git a/src/DI/DependencyChecker.php b/src/DI/DependencyChecker.php index 8bbc1f05c..f1c6cae30 100644 --- a/src/DI/DependencyChecker.php +++ b/src/DI/DependencyChecker.php @@ -80,13 +80,14 @@ public function export() * Are dependencies expired? * @return bool */ - public static function isExpired($version, $files, $phpFiles, $classes, $functions, $hash) + public static function isExpired($version, $files, &$phpFiles, $classes, $functions, $hash) { $current = @array_map('filemtime', array_combine($tmp = array_keys($files), $tmp)); // @ - files may not exist - $currentClass = @array_map('filemtime', array_combine($tmp = array_keys($phpFiles), $tmp)); // @ - files may not exist + $origPhpFiles = $phpFiles; + $phpFiles = @array_map('filemtime', array_combine($tmp = array_keys($phpFiles), $tmp)); // @ - files may not exist return $version !== self::VERSION || $files !== $current - || ($phpFiles !== $currentClass && $hash !== self::calculateHash($classes, $functions)); + || ($phpFiles !== $origPhpFiles && $hash !== self::calculateHash($classes, $functions)); } diff --git a/tests/DI/ContainerLoader.isExpired.phpt b/tests/DI/ContainerLoader.isExpired.phpt new file mode 100644 index 000000000..2ad2c2928 --- /dev/null +++ b/tests/DI/ContainerLoader.isExpired.phpt @@ -0,0 +1,56 @@ +loadFile('class1', function () {}); +}); + +// ensure files are created +$file = (new ReflectionClass('class1'))->getFileName(); +Assert::true(is_file($file)); +Assert::true(is_file("$file.meta")); + + +// load again, nothing was modified +Assert::with($loader, function () use ($file) { + Assert::false($this->isExpired($file, $newMeta)); + Assert::null($newMeta); +}); + + +// alter filemtime in files +$meta = file_get_contents("$file.meta"); +$altered = unserialize($meta); +$altered[1][__FILE__] = 123; +file_put_contents("$file.meta", serialize($altered)); + +Assert::with($loader, function () use ($file) { + Assert::true($this->isExpired($file, $newMeta)); + Assert::null($newMeta); +}); + + +// alter filemtime in classes +$altered = unserialize($meta); +$altered[2][key($altered[2])] = 123; +file_put_contents("$file.meta", serialize($altered)); + +Assert::with($loader, function () use ($file, $meta) { + Assert::true($this->isExpired($file, $newMeta)); + Assert::same($meta, $newMeta); +}); diff --git a/tests/DI/ContainerLoader.loadFile.phpt b/tests/DI/ContainerLoader.loadFile.phpt new file mode 100644 index 000000000..8583a643b --- /dev/null +++ b/tests/DI/ContainerLoader.loadFile.phpt @@ -0,0 +1,32 @@ +loadFile('class1', function () {}); +}); + +// ensure files are created +$file = (new ReflectionClass('class1'))->getFileName(); +Assert::true(is_file($file)); +Assert::true(is_file("$file.meta")); + +// load again +file_put_contents($file, ''); // remove file to avoid class redeclare error +Assert::with($loader, function () { + $this->loadFile('class1', function () { Assert::fail('Should not be recreated'); }); +});