Skip to content
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

Improve iterators #516

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
93 changes: 49 additions & 44 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,25 @@ class LogException extends \Exception {
}


class RecursiveDirectoryIteratorWithoutData extends \RecursiveFilterIterator {
public function accept(): bool {
$excludes = [
'.rnd',
'.well-known',
'data',
'..',
];
class RecursiveDirectoryIteratorFilter extends \RecursiveFilterIterator {
private array $excludedPaths;

/** @var \SplFileInfo|false */
$current = $this->current();
if (!$current) {
return false;
}
public function __construct(
\RecursiveDirectoryIterator $iterator,
array $excludedPaths = ['data'],
) {
parent::__construct($iterator);
$this->excludedPaths = array_flip($excludedPaths);
}

return !(in_array($current->getFilename(), $excludes, true) || $current->isDir());
public function accept(): bool {
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;
Expand All @@ -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?');
Expand All @@ -102,7 +101,7 @@ public function __construct(string $baseDir) {
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');
Expand Down Expand Up @@ -133,27 +132,23 @@ public function __construct(string $baseDir) {

/**
* 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;
}

/**
* Returns currently used release channel
*/
private function getCurrentReleaseChannel(): string {
return ($this->getConfigOptionString('updater.release.channel') ?? 'stable');
return $this->getConfigOptionString('updater.release.channel') ?? 'stable';
}

/**
Expand Down Expand Up @@ -323,16 +318,19 @@ private function getAppDirectories(): array {
/**
* 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);
}

/**
Expand All @@ -343,7 +341,7 @@ public function checkForExpectedFilesAndFolders(): void {

$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();
}
Expand All @@ -361,15 +359,21 @@ public function checkForExpectedFilesAndFolders(): void {
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,
];

$it = new \DirectoryIterator($this->nextcloudDir);

foreach ($it as $path => $dir) {
if (!is_writable($path)) {
$notWritablePaths[] = $path;
$notWritablePaths = [];
foreach ($it as $path => $fileInfo) {
if ($fileInfo->isDot() || isset($excludedPaths[$fileInfo->getFilename()])) {
continue;
}
if (!$fileInfo->isWritable()) {
$notWritablePaths[] = $fileInfo->getFilename();
}
}
if (count($notWritablePaths) > 0) {
Expand Down Expand Up @@ -442,7 +446,7 @@ public function createBackup(): void {
* @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);

Expand Down Expand Up @@ -971,8 +975,9 @@ public function deleteOldFiles(): void {
}

/**
* 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 {
Expand Down
4 changes: 4 additions & 0 deletions index.web.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public function isAuthenticated(): bool {
if (isset($_POST['step'])) {
// mark step as failed
http_response_code(500);
header('Content-Type: application/json');
echo(json_encode(['proceed' => false, 'response' => $e->getMessage()]));
die();
}
Expand Down Expand Up @@ -154,6 +155,7 @@ public function isAuthenticated(): bool {
break;
}
$updater->endStep($step);
header('Content-Type: application/json');
echo(json_encode(['proceed' => true]));
} catch (UpdateException $e) {
$data = $e->getData();
Expand All @@ -169,6 +171,7 @@ public function isAuthenticated(): bool {
$updater->rollbackChanges($step);
}
http_response_code(500);
header('Content-Type: application/json');
echo(json_encode(['proceed' => false, 'response' => $data]));
} catch (\Exception $e) {
$message = $e->getMessage();
Expand All @@ -184,6 +187,7 @@ public function isAuthenticated(): bool {
$updater->rollbackChanges($step);
}
http_response_code(500);
header('Content-Type: application/json');
echo(json_encode(['proceed' => false, 'response' => $message]));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,18 @@

namespace NC\Updater;

class RecursiveDirectoryIteratorWithoutData extends \RecursiveFilterIterator {
public function accept(): bool {
$excludes = [
'.rnd',
'.well-known',
'data',
'..',
];
class RecursiveDirectoryIteratorFilter extends \RecursiveFilterIterator {
private array $excludedPaths;

/** @var \SplFileInfo|false */
$current = $this->current();
if (!$current) {
return false;
}
public function __construct(
\RecursiveDirectoryIterator $iterator,
array $excludedPaths = ['data'],
) {
parent::__construct($iterator);
$this->excludedPaths = array_flip($excludedPaths);
}

return !(in_array($current->getFilename(), $excludes, true) || $current->isDir());
public function accept(): bool {
return !isset($this->excludedPaths[$this->current()->getFilename()]);
}
}
Loading