diff --git a/Makefile b/Makefile index 99c2b656..23025848 100644 --- a/Makefile +++ b/Makefile @@ -12,11 +12,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/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/Updater.php index.web.php| grep -v "^namespace" | awk '/^<\?php$$/,/\*\//{next} 1' >> index.php test/vendor: cd tests && composer install diff --git a/index.php b/index.php index b4048a7b..ada1679c 100644 --- a/index.php +++ b/index.php @@ -7,6 +7,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ + class UpdateException extends \Exception { /** @param list $data */ @@ -26,28 +27,8 @@ class LogException extends \Exception { } -class RecursiveDirectoryIteratorWithoutData extends \RecursiveFilterIterator { - 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()); - } -} - - class Updater { - private string $baseDir; + private string $nextcloudDir; private array $configValues = []; private string $currentVersion = 'unknown'; private string $buildTime; @@ -60,13 +41,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( + 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?'); @@ -87,7 +70,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'); @@ -118,19 +101,15 @@ 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; } @@ -138,7 +117,7 @@ public function getCurrentVersion() { * Returns currently used release channel */ private function getCurrentReleaseChannel(): string { - return ($this->getConfigOptionString('updater.release.channel') ?? 'stable'); + return $this->getConfigOptionString('updater.release.channel') ?? 'stable'; } /** @@ -284,7 +263,7 @@ private function getExpectedElementsList(): array { /** * Returns app directories specified in config.php * - * @return list + * @return list Paths relative to nextcloud root directory */ private function getAppDirectories(): array { $expected = []; @@ -297,10 +276,12 @@ private function getAppDirectories(): array { if (!is_array($appsPath) || !isset($appsPath['path']) || !is_string($appsPath['path'])) { throw new \Exception('Invalid configuration in apps_paths configuration key'); } - $parentDir = realpath($this->baseDir . '/../'); $appDir = basename($appsPath['path']); - if (strpos($appsPath['path'], $parentDir) === 0 && $appDir !== 'apps') { - $expected[] = $appDir; + if (strpos($appsPath['path'], $this->nextcloudDir.'/') === 0) { + $relativePath = substr($appsPath['path'], strlen($this->nextcloudDir.'/')); + if ($relativePath !== 'apps') { + $expected[] = $relativePath; + } } } } @@ -310,16 +291,44 @@ private function getAppDirectories(): array { /** * Gets the recursive directory iterator over the Nextcloud folder * - * @return \RecursiveIteratorIterator<\RecursiveDirectoryIterator> + * @param list $excludedPaths Name of root directories to skip + * @return \Generator */ - private function getRecursiveDirectoryIterator(?string $folder = null): \RecursiveIteratorIterator { - if ($folder === null) { - $folder = $this->baseDir . '/../'; + private function getRecursiveDirectoryIterator(string $folder, array $excludedPaths): \Generator { + foreach ($excludedPaths as $element) { + if (strpos($element, '/') !== false) { + throw new \Exception('Excluding subpaths is not supported yet'); + } + } + $exclusions = array_flip($excludedPaths); + + $handle = opendir($folder); + + if ($handle === false) { + throw new \Exception('Could not open '.$folder); + } + + /* Store first level children in an array to avoid trouble if changes happen while iterating */ + $children = []; + while ($name = readdir($handle)) { + if (in_array($name, ['.', '..'])) { + continue; + } + if (isset($exclusions[$name])) { + continue; + } + $children[] = $name; + } + + closedir($handle); + + foreach ($children as $name) { + $path = $folder.'/'.$name; + if (is_dir($path)) { + yield from $this->getRecursiveDirectoryIterator($path, []); + } + yield $path => new \SplFileInfo($path); } - return new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($folder, \RecursiveDirectoryIterator::SKIP_DOTS), - \RecursiveIteratorIterator::CHILD_FIRST - ); } /** @@ -330,7 +339,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(); } @@ -348,15 +357,16 @@ 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 */ - $it = new \RecursiveIteratorIterator($filter); + $excludedElements = [ + '.rnd', + '.well-known', + 'data', + ]; - foreach ($it as $path => $dir) { - if (!is_writable($path)) { - $notWritablePaths[] = $path; + $notWritablePaths = []; + foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir, $excludedElements) as $path => $fileInfo) { + if (!$fileInfo->isWritable()) { + $notWritablePaths[] = $fileInfo->getFilename(); } } if (count($notWritablePaths) > 0) { @@ -377,7 +387,7 @@ public function setMaintenanceMode(bool $state): void { if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { $configFileName = rtrim($dir, '/') . '/config.php'; } else { - $configFileName = $this->baseDir . '/../config/config.php'; + $configFileName = $this->nextcloudDir . '/config/config.php'; } $this->silentLog('[info] configFileName ' . $configFileName); @@ -422,44 +432,26 @@ public function createBackup(): void { throw new \Exception('Could not create backup folder location'); } - // Copy the backup files - $currentDir = $this->baseDir . '/../'; - - /** - * @var string $path - * @var \SplFileInfo $fileInfo - */ - foreach ($this->getRecursiveDirectoryIterator($currentDir) as $path => $fileInfo) { - $fileName = explode($currentDir, $path)[1]; - $folderStructure = explode('/', $fileName, -1); - - // Exclude the exclusions - if (isset($folderStructure[0])) { - if (array_search($folderStructure[0], $excludedElements) !== false) { - continue; - } - } else { - if (array_search($fileName, $excludedElements) !== false) { - continue; - } - } + foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir, $excludedElements) as $absolutePath => $fileInfo) { + $relativePath = explode($this->nextcloudDir, $absolutePath)[1]; + $relativeDirectory = dirname($relativePath); // Create folder if it doesn't exist - if (!file_exists($backupFolderLocation . '/' . dirname($fileName))) { - $state = mkdir($backupFolderLocation . '/' . dirname($fileName), 0750, true); + if (!file_exists($backupFolderLocation . '/' . $relativeDirectory)) { + $state = mkdir($backupFolderLocation . '/' . $relativeDirectory, 0750, true); if ($state === false) { - throw new \Exception('Could not create folder: '.$backupFolderLocation.'/'.dirname($fileName)); + throw new \Exception('Could not create folder: '.$backupFolderLocation.'/'.$relativeDirectory); } } // If it is a file copy it if ($fileInfo->isFile()) { - $state = copy($fileInfo->getRealPath(), $backupFolderLocation . $fileName); + $state = copy($fileInfo->getRealPath(), $backupFolderLocation . $relativePath); if ($state === false) { $message = sprintf( 'Could not copy "%s" to "%s"', $fileInfo->getRealPath(), - $backupFolderLocation . $fileName + $backupFolderLocation . $relativePath ); if (is_readable($fileInfo->getRealPath()) === false) { @@ -470,11 +462,11 @@ public function createBackup(): void { ); } - if (is_writable($backupFolderLocation . $fileName) === false) { + if (is_writable($backupFolderLocation . $relativePath) === false) { $message = sprintf( '%s. Destination %s is not writable', $message, - $backupFolderLocation . $fileName + $backupFolderLocation . $relativePath ); } @@ -761,7 +753,7 @@ public function extractDownload(): void { // Ensure that the downloaded version is not lower $downloadedVersion = $this->getVersionByVersionFile(dirname($downloadedFilePath) . '/nextcloud/version.php'); - $currentVersion = $this->getVersionByVersionFile($this->baseDir . '/../version.php'); + $currentVersion = $this->getVersionByVersionFile($this->nextcloudDir . '/version.php'); if (version_compare($downloadedVersion, $currentVersion, '<')) { throw new \Exception('Downloaded version is lower than installed version'); } @@ -789,14 +781,14 @@ public function replaceEntryPoints(): void { $content = "silentLog('[info] replace ' . $file); - $parentDir = dirname($this->baseDir . '/../' . $file); + $parentDir = dirname($this->nextcloudDir . '/' . $file); if (!file_exists($parentDir)) { $r = mkdir($parentDir); if ($r !== true) { throw new \Exception('Can\'t create parent directory for entry point: ' . $file); } } - $state = file_put_contents($this->baseDir . '/../' . $file, $content); + $state = file_put_contents($this->nextcloudDir . '/' . $file, $content); if ($state === false) { throw new \Exception('Can\'t replace entry point: '.$file); } @@ -814,33 +806,21 @@ private function recursiveDelete(string $folder): void { if (!file_exists($folder)) { return; } - /** @var iterable<\SplFileInfo> $iterator */ - $iterator = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($folder, \RecursiveDirectoryIterator::SKIP_DOTS), - \RecursiveIteratorIterator::CHILD_FIRST - ); $directories = []; $files = []; - foreach ($iterator as $fileInfo) { + foreach ($this->getRecursiveDirectoryIterator($folder, []) as $fileInfo) { if ($fileInfo->isDir()) { - $directories[] = $fileInfo->getRealPath(); + rmdir($fileInfo->getRealPath()); } else { if ($fileInfo->isLink()) { - $files[] = $fileInfo->getPathName(); + unlink($fileInfo->getPathName()); } else { - $files[] = $fileInfo->getRealPath(); + unlink($fileInfo->getRealPath()); } } } - foreach ($files as $file) { - unlink($file); - } - foreach ($directories as $dir) { - rmdir($dir); - } - $state = rmdir($folder); if ($state === false) { throw new \Exception('Could not rmdir ' . $folder); @@ -855,7 +835,7 @@ private function recursiveDelete(string $folder): void { public function deleteOldFiles(): void { $this->silentLog('[info] deleteOldFiles()'); - $shippedAppsFile = $this->baseDir . '/../core/shipped.json'; + $shippedAppsFile = $this->nextcloudDir . '/core/shipped.json'; $shippedAppsFileContent = file_get_contents($shippedAppsFile); if ($shippedAppsFileContent === false) { throw new \Exception('core/shipped.json is not available'); @@ -881,10 +861,10 @@ public function deleteOldFiles(): void { $shippedApps = array_merge($shippedApps, $newShippedApps); /** @var string $app */ foreach ($shippedApps as $app) { - $this->recursiveDelete($this->baseDir . '/../apps/' . $app); + $this->recursiveDelete($this->nextcloudDir . '/apps/' . $app); } - $configSampleFile = $this->baseDir . '/../config/config.sample.php'; + $configSampleFile = $this->nextcloudDir . '/config/config.sample.php'; if (file_exists($configSampleFile)) { $this->silentLog('[info] config sample exists'); @@ -895,7 +875,7 @@ public function deleteOldFiles(): void { } } - $themesReadme = $this->baseDir . '/../themes/README'; + $themesReadme = $this->nextcloudDir . '/themes/README'; if (file_exists($themesReadme)) { $this->silentLog('[info] themes README exists'); @@ -905,7 +885,7 @@ public function deleteOldFiles(): void { throw new \Exception('Could not delete themes README'); } } - $this->recursiveDelete($this->baseDir . '/../themes/example/'); + $this->recursiveDelete($this->nextcloudDir . '/themes/example/'); // Delete the rest $excludedElements = [ @@ -915,32 +895,14 @@ public function deleteOldFiles(): void { 'status.php', 'remote.php', 'public.php', - 'ocs/v1.php', - 'ocs/v2.php', + 'ocs', 'config', 'themes', 'apps', 'updater', ]; $excludedElements = array_merge($excludedElements, $this->getAppDirectories()); - /** - * @var string $path - * @var \SplFileInfo $fileInfo - */ - foreach ($this->getRecursiveDirectoryIterator() as $path => $fileInfo) { - $currentDir = $this->baseDir . '/../'; - $fileName = explode($currentDir, $path)[1]; - $folderStructure = explode('/', $fileName, -1); - // Exclude the exclusions - if (isset($folderStructure[0])) { - if (array_search($folderStructure[0], $excludedElements) !== false) { - continue; - } - } else { - if (array_search($fileName, $excludedElements) !== false) { - continue; - } - } + foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir, $excludedElements) as $path => $fileInfo) { if ($fileInfo->isFile() || $fileInfo->isLink()) { $state = unlink($path); if ($state === false) { @@ -958,44 +920,29 @@ 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 list $excludedElements Name of root directories to skip * @throws \Exception */ private function moveWithExclusions(string $dataLocation, array $excludedElements): void { - /** - * @var string $path - * @var \SplFileInfo $fileInfo - */ - foreach ($this->getRecursiveDirectoryIterator($dataLocation) as $path => $fileInfo) { + foreach ($this->getRecursiveDirectoryIterator($dataLocation, $excludedElements) as $path => $fileInfo) { $fileName = explode($dataLocation, $path)[1]; - $folderStructure = explode('/', $fileName, -1); - - // Exclude the exclusions - if (isset($folderStructure[0])) { - if (array_search($folderStructure[0], $excludedElements) !== false) { - continue; - } - } else { - if (array_search($fileName, $excludedElements) !== false) { - continue; - } - } if ($fileInfo->isFile()) { - if (!file_exists($this->baseDir . '/../' . dirname($fileName))) { - $state = mkdir($this->baseDir . '/../' . dirname($fileName), 0755, true); + if (!file_exists($this->nextcloudDir . '/' . dirname($fileName))) { + $state = mkdir($this->nextcloudDir . '/' . dirname($fileName), 0755, true); if ($state === false) { - throw new \Exception('Could not mkdir ' . $this->baseDir . '/../' . dirname($fileName)); + throw new \Exception('Could not mkdir ' . $this->nextcloudDir . '/' . dirname($fileName)); } } - $state = rename($path, $this->baseDir . '/../' . $fileName); + $state = rename($path, $this->nextcloudDir . '/' . $fileName); if ($state === false) { throw new \Exception( sprintf( 'Could not rename %s to %s', $path, - $this->baseDir . '/../' . $fileName + $this->nextcloudDir . '/' . $fileName ) ); } @@ -1024,16 +971,19 @@ public function moveNewVersionInPlace(): void { 'status.php', 'remote.php', 'public.php', - 'ocs/v1.php', - 'ocs/v2.php', + 'ocs', ]; $storageLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOptionMandatoryString('instanceid') . '/downloads/nextcloud/'; $this->silentLog('[info] storage location: ' . $storageLocation); + + // Rename apps and other stuff $this->moveWithExclusions($storageLocation, $excludedElements); - // Rename everything except the updater files + // Rename everything except the updater (It will not move what was already moved as it’s not in $storageLocation anymore) $this->moveWithExclusions($storageLocation, ['updater']); + // The updater folder is moved last in finalize() + $this->silentLog('[info] end of moveNewVersionInPlace()'); } @@ -1278,6 +1228,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(); } @@ -1364,6 +1315,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(); @@ -1379,6 +1331,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(); @@ -1394,6 +1347,7 @@ public function isAuthenticated(): bool { $updater->rollbackChanges($step); } http_response_code(500); + header('Content-Type: application/json'); echo(json_encode(['proceed' => false, 'response' => $message])); } diff --git a/index.web.php b/index.web.php index 48db2b1d..b88d6193 100644 --- a/index.web.php +++ b/index.web.php @@ -54,6 +54,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(); } @@ -140,6 +141,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(); @@ -155,6 +157,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(); @@ -170,6 +173,7 @@ public function isAuthenticated(): bool { $updater->rollbackChanges($step); } http_response_code(500); + header('Content-Type: application/json'); echo(json_encode(['proceed' => false, 'response' => $message])); } diff --git a/lib/RecursiveDirectoryIteratorWithoutData.php b/lib/RecursiveDirectoryIteratorWithoutData.php deleted file mode 100644 index 3d9ddbdb..00000000 --- a/lib/RecursiveDirectoryIteratorWithoutData.php +++ /dev/null @@ -1,29 +0,0 @@ -current(); - if (!$current) { - return false; - } - - return !(in_array($current->getFilename(), $excludes, true) || $current->isDir()); - } -} diff --git a/lib/Updater.php b/lib/Updater.php index 031ba6f7..5fb21b89 100644 --- a/lib/Updater.php +++ b/lib/Updater.php @@ -10,7 +10,7 @@ namespace NC\Updater; class Updater { - private string $baseDir; + private string $nextcloudDir; private array $configValues = []; private string $currentVersion = 'unknown'; private string $buildTime; @@ -23,13 +23,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( + 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?'); @@ -50,7 +52,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'); @@ -81,19 +83,15 @@ 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; } @@ -101,7 +99,7 @@ public function getCurrentVersion() { * Returns currently used release channel */ private function getCurrentReleaseChannel(): string { - return ($this->getConfigOptionString('updater.release.channel') ?? 'stable'); + return $this->getConfigOptionString('updater.release.channel') ?? 'stable'; } /** @@ -247,7 +245,7 @@ private function getExpectedElementsList(): array { /** * Returns app directories specified in config.php * - * @return list + * @return list Paths relative to nextcloud root directory */ private function getAppDirectories(): array { $expected = []; @@ -260,10 +258,12 @@ private function getAppDirectories(): array { if (!is_array($appsPath) || !isset($appsPath['path']) || !is_string($appsPath['path'])) { throw new \Exception('Invalid configuration in apps_paths configuration key'); } - $parentDir = realpath($this->baseDir . '/../'); $appDir = basename($appsPath['path']); - if (strpos($appsPath['path'], $parentDir) === 0 && $appDir !== 'apps') { - $expected[] = $appDir; + if (strpos($appsPath['path'], $this->nextcloudDir.'/') === 0) { + $relativePath = substr($appsPath['path'], strlen($this->nextcloudDir.'/')); + if ($relativePath !== 'apps') { + $expected[] = $relativePath; + } } } } @@ -273,16 +273,44 @@ private function getAppDirectories(): array { /** * Gets the recursive directory iterator over the Nextcloud folder * - * @return \RecursiveIteratorIterator<\RecursiveDirectoryIterator> + * @param list $excludedPaths Name of root directories to skip + * @return \Generator */ - private function getRecursiveDirectoryIterator(?string $folder = null): \RecursiveIteratorIterator { - if ($folder === null) { - $folder = $this->baseDir . '/../'; - } - return new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($folder, \RecursiveDirectoryIterator::SKIP_DOTS), - \RecursiveIteratorIterator::CHILD_FIRST - ); + private function getRecursiveDirectoryIterator(string $folder, array $excludedPaths): \Generator { + foreach ($excludedPaths as $element) { + if (strpos($element, '/') !== false) { + throw new \Exception('Excluding subpaths is not supported yet'); + } + } + $exclusions = array_flip($excludedPaths); + + $handle = opendir($folder); + + if ($handle === false) { + throw new \Exception('Could not open '.$folder); + } + + /* Store first level children in an array to avoid trouble if changes happen while iterating */ + $children = []; + while ($name = readdir($handle)) { + if (in_array($name, ['.', '..'])) { + continue; + } + if (isset($exclusions[$name])) { + continue; + } + $children[] = $name; + } + + closedir($handle); + + foreach ($children as $name) { + $path = $folder.'/'.$name; + if (is_dir($path)) { + yield from $this->getRecursiveDirectoryIterator($path, []); + } + yield $path => new \SplFileInfo($path); + } } /** @@ -293,7 +321,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(); } @@ -311,15 +339,16 @@ 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 */ - $it = new \RecursiveIteratorIterator($filter); + $excludedElements = [ + '.rnd', + '.well-known', + 'data', + ]; - foreach ($it as $path => $dir) { - if (!is_writable($path)) { - $notWritablePaths[] = $path; + $notWritablePaths = []; + foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir, $excludedElements) as $path => $fileInfo) { + if (!$fileInfo->isWritable()) { + $notWritablePaths[] = $fileInfo->getFilename(); } } if (count($notWritablePaths) > 0) { @@ -340,7 +369,7 @@ public function setMaintenanceMode(bool $state): void { if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { $configFileName = rtrim($dir, '/') . '/config.php'; } else { - $configFileName = $this->baseDir . '/../config/config.php'; + $configFileName = $this->nextcloudDir . '/config/config.php'; } $this->silentLog('[info] configFileName ' . $configFileName); @@ -385,44 +414,26 @@ public function createBackup(): void { throw new \Exception('Could not create backup folder location'); } - // Copy the backup files - $currentDir = $this->baseDir . '/../'; - - /** - * @var string $path - * @var \SplFileInfo $fileInfo - */ - foreach ($this->getRecursiveDirectoryIterator($currentDir) as $path => $fileInfo) { - $fileName = explode($currentDir, $path)[1]; - $folderStructure = explode('/', $fileName, -1); - - // Exclude the exclusions - if (isset($folderStructure[0])) { - if (array_search($folderStructure[0], $excludedElements) !== false) { - continue; - } - } else { - if (array_search($fileName, $excludedElements) !== false) { - continue; - } - } + foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir, $excludedElements) as $absolutePath => $fileInfo) { + $relativePath = explode($this->nextcloudDir, $absolutePath)[1]; + $relativeDirectory = dirname($relativePath); // Create folder if it doesn't exist - if (!file_exists($backupFolderLocation . '/' . dirname($fileName))) { - $state = mkdir($backupFolderLocation . '/' . dirname($fileName), 0750, true); + if (!file_exists($backupFolderLocation . '/' . $relativeDirectory)) { + $state = mkdir($backupFolderLocation . '/' . $relativeDirectory, 0750, true); if ($state === false) { - throw new \Exception('Could not create folder: '.$backupFolderLocation.'/'.dirname($fileName)); + throw new \Exception('Could not create folder: '.$backupFolderLocation.'/'.$relativeDirectory); } } // If it is a file copy it if ($fileInfo->isFile()) { - $state = copy($fileInfo->getRealPath(), $backupFolderLocation . $fileName); + $state = copy($fileInfo->getRealPath(), $backupFolderLocation . $relativePath); if ($state === false) { $message = sprintf( 'Could not copy "%s" to "%s"', $fileInfo->getRealPath(), - $backupFolderLocation . $fileName + $backupFolderLocation . $relativePath ); if (is_readable($fileInfo->getRealPath()) === false) { @@ -433,11 +444,11 @@ public function createBackup(): void { ); } - if (is_writable($backupFolderLocation . $fileName) === false) { + if (is_writable($backupFolderLocation . $relativePath) === false) { $message = sprintf( '%s. Destination %s is not writable', $message, - $backupFolderLocation . $fileName + $backupFolderLocation . $relativePath ); } @@ -724,7 +735,7 @@ public function extractDownload(): void { // Ensure that the downloaded version is not lower $downloadedVersion = $this->getVersionByVersionFile(dirname($downloadedFilePath) . '/nextcloud/version.php'); - $currentVersion = $this->getVersionByVersionFile($this->baseDir . '/../version.php'); + $currentVersion = $this->getVersionByVersionFile($this->nextcloudDir . '/version.php'); if (version_compare($downloadedVersion, $currentVersion, '<')) { throw new \Exception('Downloaded version is lower than installed version'); } @@ -752,14 +763,14 @@ public function replaceEntryPoints(): void { $content = "silentLog('[info] replace ' . $file); - $parentDir = dirname($this->baseDir . '/../' . $file); + $parentDir = dirname($this->nextcloudDir . '/' . $file); if (!file_exists($parentDir)) { $r = mkdir($parentDir); if ($r !== true) { throw new \Exception('Can\'t create parent directory for entry point: ' . $file); } } - $state = file_put_contents($this->baseDir . '/../' . $file, $content); + $state = file_put_contents($this->nextcloudDir . '/' . $file, $content); if ($state === false) { throw new \Exception('Can\'t replace entry point: '.$file); } @@ -777,33 +788,21 @@ private function recursiveDelete(string $folder): void { if (!file_exists($folder)) { return; } - /** @var iterable<\SplFileInfo> $iterator */ - $iterator = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($folder, \RecursiveDirectoryIterator::SKIP_DOTS), - \RecursiveIteratorIterator::CHILD_FIRST - ); $directories = []; $files = []; - foreach ($iterator as $fileInfo) { + foreach ($this->getRecursiveDirectoryIterator($folder, []) as $fileInfo) { if ($fileInfo->isDir()) { - $directories[] = $fileInfo->getRealPath(); + rmdir($fileInfo->getRealPath()); } else { if ($fileInfo->isLink()) { - $files[] = $fileInfo->getPathName(); + unlink($fileInfo->getPathName()); } else { - $files[] = $fileInfo->getRealPath(); + unlink($fileInfo->getRealPath()); } } } - foreach ($files as $file) { - unlink($file); - } - foreach ($directories as $dir) { - rmdir($dir); - } - $state = rmdir($folder); if ($state === false) { throw new \Exception('Could not rmdir ' . $folder); @@ -818,7 +817,7 @@ private function recursiveDelete(string $folder): void { public function deleteOldFiles(): void { $this->silentLog('[info] deleteOldFiles()'); - $shippedAppsFile = $this->baseDir . '/../core/shipped.json'; + $shippedAppsFile = $this->nextcloudDir . '/core/shipped.json'; $shippedAppsFileContent = file_get_contents($shippedAppsFile); if ($shippedAppsFileContent === false) { throw new \Exception('core/shipped.json is not available'); @@ -844,10 +843,10 @@ public function deleteOldFiles(): void { $shippedApps = array_merge($shippedApps, $newShippedApps); /** @var string $app */ foreach ($shippedApps as $app) { - $this->recursiveDelete($this->baseDir . '/../apps/' . $app); + $this->recursiveDelete($this->nextcloudDir . '/apps/' . $app); } - $configSampleFile = $this->baseDir . '/../config/config.sample.php'; + $configSampleFile = $this->nextcloudDir . '/config/config.sample.php'; if (file_exists($configSampleFile)) { $this->silentLog('[info] config sample exists'); @@ -858,7 +857,7 @@ public function deleteOldFiles(): void { } } - $themesReadme = $this->baseDir . '/../themes/README'; + $themesReadme = $this->nextcloudDir . '/themes/README'; if (file_exists($themesReadme)) { $this->silentLog('[info] themes README exists'); @@ -868,7 +867,7 @@ public function deleteOldFiles(): void { throw new \Exception('Could not delete themes README'); } } - $this->recursiveDelete($this->baseDir . '/../themes/example/'); + $this->recursiveDelete($this->nextcloudDir . '/themes/example/'); // Delete the rest $excludedElements = [ @@ -878,32 +877,14 @@ public function deleteOldFiles(): void { 'status.php', 'remote.php', 'public.php', - 'ocs/v1.php', - 'ocs/v2.php', + 'ocs', 'config', 'themes', 'apps', 'updater', ]; $excludedElements = array_merge($excludedElements, $this->getAppDirectories()); - /** - * @var string $path - * @var \SplFileInfo $fileInfo - */ - foreach ($this->getRecursiveDirectoryIterator() as $path => $fileInfo) { - $currentDir = $this->baseDir . '/../'; - $fileName = explode($currentDir, $path)[1]; - $folderStructure = explode('/', $fileName, -1); - // Exclude the exclusions - if (isset($folderStructure[0])) { - if (array_search($folderStructure[0], $excludedElements) !== false) { - continue; - } - } else { - if (array_search($fileName, $excludedElements) !== false) { - continue; - } - } + foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir, $excludedElements) as $path => $fileInfo) { if ($fileInfo->isFile() || $fileInfo->isLink()) { $state = unlink($path); if ($state === false) { @@ -921,44 +902,29 @@ 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 list $excludedElements Name of root directories to skip * @throws \Exception */ private function moveWithExclusions(string $dataLocation, array $excludedElements): void { - /** - * @var string $path - * @var \SplFileInfo $fileInfo - */ - foreach ($this->getRecursiveDirectoryIterator($dataLocation) as $path => $fileInfo) { + foreach ($this->getRecursiveDirectoryIterator($dataLocation, $excludedElements) as $path => $fileInfo) { $fileName = explode($dataLocation, $path)[1]; - $folderStructure = explode('/', $fileName, -1); - - // Exclude the exclusions - if (isset($folderStructure[0])) { - if (array_search($folderStructure[0], $excludedElements) !== false) { - continue; - } - } else { - if (array_search($fileName, $excludedElements) !== false) { - continue; - } - } if ($fileInfo->isFile()) { - if (!file_exists($this->baseDir . '/../' . dirname($fileName))) { - $state = mkdir($this->baseDir . '/../' . dirname($fileName), 0755, true); + if (!file_exists($this->nextcloudDir . '/' . dirname($fileName))) { + $state = mkdir($this->nextcloudDir . '/' . dirname($fileName), 0755, true); if ($state === false) { - throw new \Exception('Could not mkdir ' . $this->baseDir . '/../' . dirname($fileName)); + throw new \Exception('Could not mkdir ' . $this->nextcloudDir . '/' . dirname($fileName)); } } - $state = rename($path, $this->baseDir . '/../' . $fileName); + $state = rename($path, $this->nextcloudDir . '/' . $fileName); if ($state === false) { throw new \Exception( sprintf( 'Could not rename %s to %s', $path, - $this->baseDir . '/../' . $fileName + $this->nextcloudDir . '/' . $fileName ) ); } @@ -987,16 +953,19 @@ public function moveNewVersionInPlace(): void { 'status.php', 'remote.php', 'public.php', - 'ocs/v1.php', - 'ocs/v2.php', + 'ocs', ]; $storageLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOptionMandatoryString('instanceid') . '/downloads/nextcloud/'; $this->silentLog('[info] storage location: ' . $storageLocation); + + // Rename apps and other stuff $this->moveWithExclusions($storageLocation, $excludedElements); - // Rename everything except the updater files + // Rename everything except the updater (It will not move what was already moved as it’s not in $storageLocation anymore) $this->moveWithExclusions($storageLocation, ['updater']); + // The updater folder is moved last in finalize() + $this->silentLog('[info] end of moveNewVersionInPlace()'); } diff --git a/tests/features/master.feature b/tests/features/master.feature index 4de8b06b..f1e40bd6 100644 --- a/tests/features/master.feature +++ b/tests/features/master.feature @@ -9,6 +9,6 @@ Feature: CLI updater - master base And the version number is decreased in the config.php to enforce upgrade When the CLI updater is run successfully And the output should contain "Update successful" - Then the installed version should be 31.0 + Then the installed version should be 32.0 And maintenance mode should be off And upgrade is not required diff --git a/updater.phar b/updater.phar index ad43f18a..3c086b87 100755 Binary files a/updater.phar and b/updater.phar differ diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index 51e734a7..6d29bff6 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -32,6 +32,11 @@ class InstalledVersions */ private static $installed; + /** + * @var bool + */ + private static $installedIsLocalDir; + /** * @var bool|null */ @@ -309,6 +314,12 @@ public static function reload($data) { self::$installed = $data; self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; } /** @@ -322,19 +333,27 @@ private static function getInstalled() } $installed = array(); + $copiedLocalDir = false; if (self::$canGetVendors) { + $selfDir = strtr(__DIR__, '\\', '/'); foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ $required = require $vendorDir.'/composer/installed.php'; - $installed[] = self::$installedByVendor[$vendorDir] = $required; - if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { - self::$installed = $installed[count($installed) - 1]; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; } } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } } } @@ -350,7 +369,7 @@ private static function getInstalled() } } - if (self::$installed !== array()) { + if (self::$installed !== array() && !$copiedLocalDir) { $installed[] = self::$installed; } diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 101e9c60..9e980904 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -24,7 +24,6 @@ '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\\UpdateCommand' => $baseDir . '/lib/UpdateCommand.php', 'NC\\Updater\\UpdateException' => $baseDir . '/lib/UpdateException.php', 'NC\\Updater\\Updater' => $baseDir . '/lib/Updater.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 7cd88b34..0a268bc1 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -119,7 +119,6 @@ class ComposerStaticInit936ba63ded5d1b8248cdb4d5673af0ea '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\\UpdateCommand' => __DIR__ . '/../..' . '/lib/UpdateCommand.php', 'NC\\Updater\\UpdateException' => __DIR__ . '/../..' . '/lib/UpdateException.php', 'NC\\Updater\\Updater' => __DIR__ . '/../..' . '/lib/Updater.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index a2bab66c..12dbac5a 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -62,24 +62,24 @@ }, { "name": "nextcloud/coding-standard", - "version": "v1.1.1", - "version_normalized": "1.1.1.0", + "version": "v1.2.1", + "version_normalized": "1.2.1.0", "source": { "type": "git", "url": "https://github.com/nextcloud/coding-standard.git", - "reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb" + "reference": "cf5f18d989ec62fb4cdc7fc92a36baf34b3d829e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/55def702fb9a37a219511e1d8c6fe8e37164c1fb", - "reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/cf5f18d989ec62fb4cdc7fc92a36baf34b3d829e", + "reference": "cf5f18d989ec62fb4cdc7fc92a36baf34b3d829e", "shasum": "" }, "require": { "php": "^7.3|^8.0", "php-cs-fixer/shim": "^3.17" }, - "time": "2023-06-01T12:05:01+00:00", + "time": "2024-02-01T14:54:37+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -100,23 +100,23 @@ "description": "Nextcloud coding standards for the php cs fixer", "support": { "issues": "https://github.com/nextcloud/coding-standard/issues", - "source": "https://github.com/nextcloud/coding-standard/tree/v1.1.1" + "source": "https://github.com/nextcloud/coding-standard/tree/v1.2.1" }, "install-path": "../nextcloud/coding-standard" }, { "name": "php-cs-fixer/shim", - "version": "v3.17.0", - "version_normalized": "3.17.0.0", + "version": "v3.51.0", + "version_normalized": "3.51.0.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/shim.git", - "reference": "f51b4aed90565c447136f1d015798f6f7c82490f" + "reference": "a792394f7f3934f75a4df9dca544796c6f503027" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/f51b4aed90565c447136f1d015798f6f7c82490f", - "reference": "f51b4aed90565c447136f1d015798f6f7c82490f", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/a792394f7f3934f75a4df9dca544796c6f503027", + "reference": "a792394f7f3934f75a4df9dca544796c6f503027", "shasum": "" }, "require": { @@ -131,7 +131,7 @@ "ext-dom": "For handling output formats in XML", "ext-mbstring": "For handling non-UTF8 characters." }, - "time": "2023-05-22T20:00:38+00:00", + "time": "2024-02-28T19:51:07+00:00", "bin": [ "php-cs-fixer", "php-cs-fixer.phar" @@ -155,7 +155,7 @@ "description": "A tool to automatically fix PHP code style", "support": { "issues": "https://github.com/PHP-CS-Fixer/shim/issues", - "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.17.0" + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.51.0" }, "install-path": "../php-cs-fixer/shim" }, diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 6aecd5e3..c68e281a 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => '__root__', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'de0582d00a183fca1d5645d354e88b94b9b75a90', + 'reference' => 'c504bf596664d9b1fb3f4142da00084a05e93e0d', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -13,7 +13,7 @@ '__root__' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'de0582d00a183fca1d5645d354e88b94b9b75a90', + 'reference' => 'c504bf596664d9b1fb3f4142da00084a05e93e0d', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -31,22 +31,22 @@ 'friendsofphp/php-cs-fixer' => array( 'dev_requirement' => true, 'replaced' => array( - 0 => 'v3.17.0', + 0 => 'v3.51.0', ), ), 'nextcloud/coding-standard' => array( - 'pretty_version' => 'v1.1.1', - 'version' => '1.1.1.0', - 'reference' => '55def702fb9a37a219511e1d8c6fe8e37164c1fb', + 'pretty_version' => 'v1.2.1', + 'version' => '1.2.1.0', + 'reference' => 'cf5f18d989ec62fb4cdc7fc92a36baf34b3d829e', 'type' => 'library', 'install_path' => __DIR__ . '/../nextcloud/coding-standard', 'aliases' => array(), 'dev_requirement' => true, ), 'php-cs-fixer/shim' => array( - 'pretty_version' => 'v3.17.0', - 'version' => '3.17.0.0', - 'reference' => 'f51b4aed90565c447136f1d015798f6f7c82490f', + 'pretty_version' => 'v3.51.0', + 'version' => '3.51.0.0', + 'reference' => 'a792394f7f3934f75a4df9dca544796c6f503027', 'type' => 'application', 'install_path' => __DIR__ . '/../php-cs-fixer/shim', 'aliases' => array(), diff --git a/vendor/nextcloud/coding-standard/CHANGELOG.md b/vendor/nextcloud/coding-standard/CHANGELOG.md index 88758a3b..a71c8bc6 100644 --- a/vendor/nextcloud/coding-standard/CHANGELOG.md +++ b/vendor/nextcloud/coding-standard/CHANGELOG.md @@ -1,6 +1,33 @@ # Changelog All notable changes to this project will be documented in this file. +## 1.2.1 - 2024-02-01 +### Fix +* fix: Remove `fully_qualified_strict_types` again by @nickvergessen in https://github.com/nextcloud/coding-standard/pull/16 + +## 1.2.0 - 2024-02-01 +### Added +- `array_syntax`: Force short syntax for array +- `list_syntax`: Same for list +- ~~`fully_qualified_strict_types`: Remove namespace from classname when there is a `use` statement, and add missing backslash for global namespace~~ - Removed in 1.2.1 due to issues +- `no_leading_import_slash`: Remove leading slash from `use` statement +- `nullable_type_declaration_for_default_null_value`: Add missing `?` on type declaration for parameters defaulting to `null`. This will most likely be needed to avoid warnings in PHP 8.4. +- `yoda_style`: forbid yoda style comparision. This replaces `null === $a` by `$a === null`. + +## 1.1.1 - 2023-06-23 +### Changed +* feat: use php-cs-fixer/shim by @kesselb in https://github.com/nextcloud/coding-standard/pull/13 + +## 1.1.0 - 2023-04-13 +### Changed +* Order imports alphabetically by @come-nc in https://github.com/nextcloud/coding-standard/pull/10 +* fix(rules): Replace deprecated braces rules by @nickvergessen in https://github.com/nextcloud/coding-standard/pull/12 + +## 1.0.0 – 2021-11-10 +### Breaking change +* Update php-cs-fixer to 3.x +* See https://github.com/nextcloud/coding-standard#upgrade-from-v0x-to-v10 for instructions. + ## 0.5.0 – 2021-01-11 ### Added - New rule: short list syntax diff --git a/vendor/nextcloud/coding-standard/README.md b/vendor/nextcloud/coding-standard/README.md index 62729e3d..646b6a9f 100644 --- a/vendor/nextcloud/coding-standard/README.md +++ b/vendor/nextcloud/coding-standard/README.md @@ -46,7 +46,7 @@ For convenience you may add it to the `scripts` section of your `composer.json`: } ``` -*Note: Don't forget to exclude .php_cs.dist in your build scripts.* +*Note: Don't forget to exclude `.php-cs-fixer.dist.php` and `.php-cs-fixer.cache` in your build scripts.* ## Upgrade from v0.x to v1.0 diff --git a/vendor/nextcloud/coding-standard/src/Config.php b/vendor/nextcloud/coding-standard/src/Config.php index c6b229a0..c888eaff 100644 --- a/vendor/nextcloud/coding-standard/src/Config.php +++ b/vendor/nextcloud/coding-standard/src/Config.php @@ -18,6 +18,7 @@ public function getRules() : array { '@PSR2' => true, 'align_multiline_comment' => true, 'array_indentation' => true, + 'array_syntax' => true, 'binary_operator_spaces' => [ 'default' => 'single_space', ], @@ -35,16 +36,19 @@ public function getRules() : array { ], 'indentation_type' => true, 'line_ending' => true, + 'list_syntax' => true, 'lowercase_keywords' => true, 'method_argument_space' => [ 'on_multiline' => 'ignore', ], 'no_closing_tag' => true, + 'no_leading_import_slash' => true, 'no_spaces_after_function_name' => true, 'no_spaces_inside_parenthesis' => true, 'no_trailing_whitespace' => true, 'no_trailing_whitespace_in_comment' => true, 'no_unused_imports' => true, + 'nullable_type_declaration_for_default_null_value' => true, 'ordered_imports' => [ 'imports_order' => ['class', 'function', 'const'], 'sort_algorithm' => 'alpha' @@ -57,6 +61,7 @@ public function getRules() : array { 'visibility_required' => [ 'elements' => ['property', 'method', 'const'] ], + 'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false], ]; } } diff --git a/vendor/php-cs-fixer/shim/logo.md b/vendor/php-cs-fixer/shim/logo.md index c5f7b399..c2877f71 100644 --- a/vendor/php-cs-fixer/shim/logo.md +++ b/vendor/php-cs-fixer/shim/logo.md @@ -1,3 +1,3 @@ -The logo is © 2010-2022 Sensio Labs. +The logo is © 2010+ Sensio Labs. Original resolution can be found at https://github.com/PHP-CS-Fixer/logo . diff --git a/vendor/php-cs-fixer/shim/php-cs-fixer.phar b/vendor/php-cs-fixer/shim/php-cs-fixer.phar index 8dc906a1..8182b0f7 100755 Binary files a/vendor/php-cs-fixer/shim/php-cs-fixer.phar and b/vendor/php-cs-fixer/shim/php-cs-fixer.phar differ diff --git a/vendor/php-cs-fixer/shim/php-cs-fixer.phar.asc b/vendor/php-cs-fixer/shim/php-cs-fixer.phar.asc index 65fbde63..aa833e9a 100644 Binary files a/vendor/php-cs-fixer/shim/php-cs-fixer.phar.asc and b/vendor/php-cs-fixer/shim/php-cs-fixer.phar.asc differ