Improve iterators

Signed-off-by: Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>
This commit is contained in:
Benjamin Gaussorgues 2023-11-09 12:16:02 +01:00
parent 347765df8d
commit 6c2206ee94
No known key found for this signature in database
GPG Key ID: 5DAC1CAFAA6DB883
8 changed files with 106 additions and 96 deletions

View File

@ -10,11 +10,11 @@ updater.phar: updater.php lib/*.php buildVersionFile.php
clean:
rm updater.phar index.php
index.php: lib/UpdateException.php lib/LogException.php lib/RecursiveDirectoryIteratorWithoutData.php lib/Updater.php index.web.php
index.php: lib/UpdateException.php lib/LogException.php lib/RecursiveDirectoryIteratorFilter.php lib/Updater.php index.web.php
# First put openining php tag and license
awk '/^<\?php$$/,/\*\//' index.web.php > index.php
# Then concat all files while filtering php tag and license
cat lib/UpdateException.php lib/LogException.php lib/RecursiveDirectoryIteratorWithoutData.php lib/Updater.php index.web.php| grep -v "^namespace" | awk '/^<\?php$$/,/\*\//{next} 1' >> index.php
cat lib/UpdateException.php lib/LogException.php lib/RecursiveDirectoryIteratorFilter.php lib/Updater.php index.web.php| grep -v "^namespace" | awk '/^<\?php$$/,/\*\//{next} 1' >> index.php
test/vendor:
cd tests && composer install

View File

@ -41,28 +41,25 @@ class LogException extends \Exception {
}
class RecursiveDirectoryIteratorWithoutData extends \RecursiveFilterIterator {
class RecursiveDirectoryIteratorFilter extends \RecursiveFilterIterator {
private array $excludedPaths;
public function __construct(
\RecursiveDirectoryIterator $iterator,
array $excludedPaths = ['data'],
) {
parent::__construct($iterator);
$this->excludedPaths = array_flip($excludedPaths);
}
public function accept(): bool {
$excludes = [
'.rnd',
'.well-known',
'data',
'..',
];
/** @var \SplFileInfo|false */
$current = $this->current();
if (!$current) {
return false;
}
return !(in_array($current->getFilename(), $excludes, true) || $current->isDir());
return !isset($this->excludedPaths[$this->current()->getFilename()]);
}
}
class Updater {
private string $baseDir;
private string $nextcloudDir;
private array $configValues = [];
private string $currentVersion = 'unknown';
private string $buildTime;
@ -75,13 +72,15 @@ class Updater {
* @param string $baseDir the absolute path to the /updater/ directory in the Nextcloud root
* @throws \Exception
*/
public function __construct(string $baseDir) {
$this->baseDir = $baseDir;
public function __construct(
private string $baseDir
) {
$this->nextcloudDir = realpath(dirname($baseDir));
if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
$configFileName = rtrim($dir, '/') . '/config.php';
$configFileName = realpath($dir . '/config.php');
} else {
$configFileName = $this->baseDir . '/../config/config.php';
$configFileName = $this->nextcloudDir . '/config/config.php';
}
if (!file_exists($configFileName)) {
throw new \Exception('Could not find config.php. Is this file in the "updater" subfolder of Nextcloud?');
@ -102,7 +101,7 @@ class Updater {
throw new \Exception('Could not read data directory from config.php.');
}
$versionFileName = $this->baseDir . '/../version.php';
$versionFileName = $this->nextcloudDir . '/version.php';
if (!file_exists($versionFileName)) {
// fallback to version in config.php
$version = $this->getConfigOptionString('version');
@ -133,19 +132,15 @@ class Updater {
/**
* Returns whether the web updater is disabled
*
* @return bool
*/
public function isDisabled() {
public function isDisabled(): bool {
return $this->disabled;
}
/**
* Returns current version or "unknown" if this could not be determined.
*
* @return string
*/
public function getCurrentVersion() {
public function getCurrentVersion(): string {
return $this->currentVersion;
}
@ -153,7 +148,7 @@ class Updater {
* Returns currently used release channel
*/
private function getCurrentReleaseChannel(): string {
return ($this->getConfigOptionString('updater.release.channel') ?? 'stable');
return $this->getConfigOptionString('updater.release.channel') ?? 'stable';
}
/**
@ -323,16 +318,19 @@ class Updater {
/**
* Gets the recursive directory iterator over the Nextcloud folder
*
* @return \RecursiveIteratorIterator<\RecursiveDirectoryIterator>
* @return \RecursiveIteratorIterator<\RecursiveDirectoryIterator|RecursiveDirectoryIteratorFilter>
*/
private function getRecursiveDirectoryIterator(?string $folder = null): \RecursiveIteratorIterator {
private function getRecursiveDirectoryIterator(?string $folder = null, array $excludedPaths = []): \RecursiveIteratorIterator {
if ($folder === null) {
$folder = $this->baseDir . '/../';
}
return new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($folder, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
$iterator = new \RecursiveDirectoryIterator($folder, \FilesystemIterator::SKIP_DOTS);
if (!empty($excludedPaths)) {
$iterator = new RecursiveDirectoryIteratorFilter($iterator, $excludedPaths);
}
return new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
}
/**
@ -343,7 +341,7 @@ class Updater {
$expectedElements = $this->getExpectedElementsList();
$unexpectedElements = [];
foreach (new \DirectoryIterator($this->baseDir . '/../') as $fileInfo) {
foreach (new \DirectoryIterator($this->nextcloudDir) as $fileInfo) {
if (array_search($fileInfo->getFilename(), $expectedElements) === false) {
$unexpectedElements[] = $fileInfo->getFilename();
}
@ -361,15 +359,21 @@ class Updater {
public function checkWritePermissions(): void {
$this->silentLog('[info] checkWritePermissions()');
$notWritablePaths = array();
$dir = new \RecursiveDirectoryIterator($this->baseDir . '/../');
$filter = new RecursiveDirectoryIteratorWithoutData($dir);
/** @var iterable<string, \SplFileInfo> */
$it = new \RecursiveIteratorIterator($filter);
$excludedPaths = [
'.rnd' => true,
'.well-known' => true,
'data' => true,
];
foreach ($it as $path => $dir) {
if (!is_writable($path)) {
$notWritablePaths[] = $path;
$it = new \DirectoryIterator($this->nextcloudDir);
$notWritablePaths = [];
foreach ($it as $path => $fileInfo) {
if ($fileInfo->isDot() || isset($excludedPaths[$fileInfo->getFilename()])) {
continue;
}
if (!$fileInfo->isWritable()) {
$notWritablePaths[] = $fileInfo->getFilename();
}
}
if (count($notWritablePaths) > 0) {
@ -442,7 +446,7 @@ class Updater {
* @var string $path
* @var \SplFileInfo $fileInfo
*/
foreach ($this->getRecursiveDirectoryIterator($currentDir) as $path => $fileInfo) {
foreach ($this->getRecursiveDirectoryIterator($currentDir, $excludedElements) as $path => $fileInfo) {
$fileName = explode($currentDir, $path)[1];
$folderStructure = explode('/', $fileName, -1);
@ -971,8 +975,9 @@ EOF;
}
/**
* Moves the specified filed except the excluded elements to the correct position
* Moves the specified files except the excluded elements to the correct position
*
* @param string[] $excludedElements
* @throws \Exception
*/
private function moveWithExclusions(string $dataLocation, array $excludedElements): void {

View File

@ -22,21 +22,18 @@
namespace NC\Updater;
class RecursiveDirectoryIteratorWithoutData extends \RecursiveFilterIterator {
class RecursiveDirectoryIteratorFilter extends \RecursiveFilterIterator {
private array $excludedPaths;
public function __construct(
\RecursiveDirectoryIterator $iterator,
array $excludedPaths = ['data'],
) {
parent::__construct($iterator);
$this->excludedPaths = array_flip($excludedPaths);
}
public function accept(): bool {
$excludes = [
'.rnd',
'.well-known',
'data',
'..',
];
/** @var \SplFileInfo|false */
$current = $this->current();
if (!$current) {
return false;
}
return !(in_array($current->getFilename(), $excludes, true) || $current->isDir());
return !isset($this->excludedPaths[$this->current()->getFilename()]);
}
}

View File

@ -24,7 +24,7 @@
namespace NC\Updater;
class Updater {
private string $baseDir;
private string $nextcloudDir;
private array $configValues = [];
private string $currentVersion = 'unknown';
private string $buildTime;
@ -37,13 +37,15 @@ class Updater {
* @param string $baseDir the absolute path to the /updater/ directory in the Nextcloud root
* @throws \Exception
*/
public function __construct(string $baseDir) {
$this->baseDir = $baseDir;
public function __construct(
private string $baseDir
) {
$this->nextcloudDir = realpath(dirname($baseDir));
if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
$configFileName = rtrim($dir, '/') . '/config.php';
$configFileName = realpath($dir . '/config.php');
} else {
$configFileName = $this->baseDir . '/../config/config.php';
$configFileName = $this->nextcloudDir . '/config/config.php';
}
if (!file_exists($configFileName)) {
throw new \Exception('Could not find config.php. Is this file in the "updater" subfolder of Nextcloud?');
@ -64,7 +66,7 @@ class Updater {
throw new \Exception('Could not read data directory from config.php.');
}
$versionFileName = $this->baseDir . '/../version.php';
$versionFileName = $this->nextcloudDir . '/version.php';
if (!file_exists($versionFileName)) {
// fallback to version in config.php
$version = $this->getConfigOptionString('version');
@ -95,19 +97,15 @@ class Updater {
/**
* Returns whether the web updater is disabled
*
* @return bool
*/
public function isDisabled() {
public function isDisabled(): bool {
return $this->disabled;
}
/**
* Returns current version or "unknown" if this could not be determined.
*
* @return string
*/
public function getCurrentVersion() {
public function getCurrentVersion(): string {
return $this->currentVersion;
}
@ -115,7 +113,7 @@ class Updater {
* Returns currently used release channel
*/
private function getCurrentReleaseChannel(): string {
return ($this->getConfigOptionString('updater.release.channel') ?? 'stable');
return $this->getConfigOptionString('updater.release.channel') ?? 'stable';
}
/**
@ -285,16 +283,19 @@ class Updater {
/**
* Gets the recursive directory iterator over the Nextcloud folder
*
* @return \RecursiveIteratorIterator<\RecursiveDirectoryIterator>
* @return \RecursiveIteratorIterator<\RecursiveDirectoryIterator|RecursiveDirectoryIteratorFilter>
*/
private function getRecursiveDirectoryIterator(?string $folder = null): \RecursiveIteratorIterator {
private function getRecursiveDirectoryIterator(?string $folder = null, array $excludedPaths = []): \RecursiveIteratorIterator {
if ($folder === null) {
$folder = $this->baseDir . '/../';
}
return new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($folder, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
$iterator = new \RecursiveDirectoryIterator($folder, \FilesystemIterator::SKIP_DOTS);
if (!empty($excludedPaths)) {
$iterator = new RecursiveDirectoryIteratorFilter($iterator, $excludedPaths);
}
return new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
}
/**
@ -305,7 +306,7 @@ class Updater {
$expectedElements = $this->getExpectedElementsList();
$unexpectedElements = [];
foreach (new \DirectoryIterator($this->baseDir . '/../') as $fileInfo) {
foreach (new \DirectoryIterator($this->nextcloudDir) as $fileInfo) {
if (array_search($fileInfo->getFilename(), $expectedElements) === false) {
$unexpectedElements[] = $fileInfo->getFilename();
}
@ -323,15 +324,21 @@ class Updater {
public function checkWritePermissions(): void {
$this->silentLog('[info] checkWritePermissions()');
$notWritablePaths = array();
$dir = new \RecursiveDirectoryIterator($this->baseDir . '/../');
$filter = new RecursiveDirectoryIteratorWithoutData($dir);
/** @var iterable<string, \SplFileInfo> */
$it = new \RecursiveIteratorIterator($filter);
$excludedPaths = [
'.rnd' => true,
'.well-known' => true,
'data' => true,
];
foreach ($it as $path => $dir) {
if (!is_writable($path)) {
$notWritablePaths[] = $path;
$it = new \DirectoryIterator($this->nextcloudDir);
$notWritablePaths = [];
foreach ($it as $path => $fileInfo) {
if ($fileInfo->isDot() || isset($excludedPaths[$fileInfo->getFilename()])) {
continue;
}
if (!$fileInfo->isWritable()) {
$notWritablePaths[] = $fileInfo->getFilename();
}
}
if (count($notWritablePaths) > 0) {
@ -404,7 +411,7 @@ class Updater {
* @var string $path
* @var \SplFileInfo $fileInfo
*/
foreach ($this->getRecursiveDirectoryIterator($currentDir) as $path => $fileInfo) {
foreach ($this->getRecursiveDirectoryIterator($currentDir, $excludedElements) as $path => $fileInfo) {
$fileName = explode($currentDir, $path)[1];
$folderStructure = explode('/', $fileName, -1);
@ -933,8 +940,9 @@ EOF;
}
/**
* Moves the specified filed except the excluded elements to the correct position
* Moves the specified files except the excluded elements to the correct position
*
* @param string[] $excludedElements
* @throws \Exception
*/
private function moveWithExclusions(string $dataLocation, array $excludedElements): void {

Binary file not shown.

View File

@ -24,7 +24,7 @@ return array(
'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'NC\\Updater\\CommandApplication' => $baseDir . '/lib/CommandApplication.php',
'NC\\Updater\\LogException' => $baseDir . '/lib/LogException.php',
'NC\\Updater\\RecursiveDirectoryIteratorWithoutData' => $baseDir . '/lib/RecursiveDirectoryIteratorWithoutData.php',
'NC\\Updater\\RecursiveDirectoryIteratorFilter' => $baseDir . '/lib/RecursiveDirectoryIteratorFilter.php',
'NC\\Updater\\UpdateCommand' => $baseDir . '/lib/UpdateCommand.php',
'NC\\Updater\\UpdateException' => $baseDir . '/lib/UpdateException.php',
'NC\\Updater\\Updater' => $baseDir . '/lib/Updater.php',

View File

@ -95,7 +95,7 @@ class ComposerStaticInitba7c5c8f0885d00c3b669d0399f96c80
'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'NC\\Updater\\CommandApplication' => __DIR__ . '/../..' . '/lib/CommandApplication.php',
'NC\\Updater\\LogException' => __DIR__ . '/../..' . '/lib/LogException.php',
'NC\\Updater\\RecursiveDirectoryIteratorWithoutData' => __DIR__ . '/../..' . '/lib/RecursiveDirectoryIteratorWithoutData.php',
'NC\\Updater\\RecursiveDirectoryIteratorFilter' => __DIR__ . '/../..' . '/lib/RecursiveDirectoryIteratorFilter.php',
'NC\\Updater\\UpdateCommand' => __DIR__ . '/../..' . '/lib/UpdateCommand.php',
'NC\\Updater\\UpdateException' => __DIR__ . '/../..' . '/lib/UpdateException.php',
'NC\\Updater\\Updater' => __DIR__ . '/../..' . '/lib/Updater.php',

View File

@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '14ccc22088938656fb221e3d7f7e8928a58332ab',
'reference' => '782b428fc1077036feb12e897047850d9855a1cd',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -13,7 +13,7 @@
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '14ccc22088938656fb221e3d7f7e8928a58332ab',
'reference' => '782b428fc1077036feb12e897047850d9855a1cd',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),