diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
new file mode 100644
index 00000000..db50b9b8
--- /dev/null
+++ b/.github/workflows/phpunit.yml
@@ -0,0 +1,66 @@
+# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+# SPDX-License-Identifier: MIT
+
+name: PHPUnit
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+ - master
+ - stable*
+
+permissions:
+ contents: read
+
+concurrency:
+ group: phpunit-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+jobs:
+ phpunit:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php-versions: ["8.2", "8.3", "8.4"]
+
+ name: PHPUnit PHP ${{ matrix.php-versions }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ with:
+ persist-credentials: false
+
+ - name: Set up php ${{ matrix.php-versions }}
+ uses: shivammathur/setup-php@ec406be512d7077f68eed36e63f4d91bc006edc4 # v2.35.4
+ with:
+ php-version: ${{ matrix.php-versions }}
+ extensions: ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib
+ coverage: none
+ ini-file: development
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Install dependencies
+ run: |
+ composer i
+ composer bin phpunit install
+
+ - name: PHPUnit
+ run: composer run test:unit
+
+ summary:
+ permissions:
+ contents: none
+ runs-on: ubuntu-latest-low
+ needs: phpunit
+
+ if: always()
+
+ name: phpunit-summary
+
+ steps:
+ - name: Summary status
+ run: if ${{ needs.phpunit.result != 'success' && needs.phpunit.result != 'skipped' }}; then exit 1; fi
diff --git a/.gitignore b/.gitignore
index 75a13176..4eacecc9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@
/vendor/symfony/service-contracts/Test
/vendor-bin/**/vendor
.php-cs-fixer.cache
+.phpunit.result.cache
diff --git a/Makefile b/Makefile
index 273f7e7c..2a6503c7 100644
--- a/Makefile
+++ b/Makefile
@@ -21,9 +21,15 @@ index.php: lib/UpdateException.php lib/LogException.php lib/Updater.php index.we
test/vendor:
composer bin tests install
+vendor/bin/phpunit:
+ composer bin phpunit install
+
test: updater.phar test/vendor
cd tests && ../vendor/bin/behat
+test-unit: vendor/bin/phpunit
+ composer run test:unit
+
test-cli: updater.phar test/vendor
cd tests && ../vendor/bin/behat features/cli.feature
diff --git a/REUSE.toml b/REUSE.toml
index ccd18bd4..6a863295 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -24,7 +24,7 @@ SPDX-FileCopyrightText = "2020 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "MIT"
[[annotations]]
-path = ["vendor-bin/box/**", "vendor-bin/psalm/**", "vendor-bin/rector/**"]
+path = ["vendor-bin/box/**", "vendor-bin/phpunit/**", "vendor-bin/psalm/**", "vendor-bin/rector/**"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2023 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "AGPL-3.0-or-later"
diff --git a/composer.json b/composer.json
index d05b7108..85dba635 100644
--- a/composer.json
+++ b/composer.json
@@ -26,7 +26,8 @@
"psalm": "psalm --threads=$(nproc)",
"psalm:ci": "psalm --threads=1",
"psalm:fix": "- --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType",
- "rector": "rector && composer run cs:fix"
+ "rector": "rector && composer run cs:fix",
+ "test:unit": "phpunit -c phpunit.xml"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8"
diff --git a/index.php b/index.php
index 277bfa7e..fe86f322 100644
--- a/index.php
+++ b/index.php
@@ -81,7 +81,7 @@ public function __construct(
throw new \Exception('Could not read data directory from config.php.');
}
- $versionFileName = $this->nextcloudDir . '/version.php';
+ $versionFileName = $this->buildPath('version.php');
if (!file_exists($versionFileName)) {
// fallback to version in config.php
$version = $this->getConfigOptionString('version');
@@ -111,6 +111,17 @@ public function __construct(
$this->buildTime = $buildTime;
}
+ /**
+ * Builds an absolute path by joining the Nextcloud root directory with the provided relative path.
+ * Handles leading and trailing slashes to prevent double-slash issues.
+ *
+ * @param string $suffix Relative path to append (with or without leading slash)
+ * @return string The absolute path
+ */
+ public function buildPath(string $suffix): string {
+ return rtrim($this->nextcloudDir, '/') . '/' . ltrim($suffix, '/');
+ }
+
/**
* @return array{array, string}
*/
@@ -121,7 +132,7 @@ private function readConfigFile(): array {
throw new \Exception('Configuration not found in ' . $dir);
}
} else {
- $configFileName = $this->nextcloudDir . '/config/config.php';
+ $configFileName = $this->buildPath('config/config.php');
}
if (!file_exists($configFileName)) {
@@ -335,8 +346,8 @@ private function getAppDirectories(): array {
throw new \Exception('Invalid configuration in apps_paths configuration key');
}
- if (str_starts_with($appsPath['path'], $this->nextcloudDir . '/')) {
- $relativePath = substr($appsPath['path'], strlen($this->nextcloudDir . '/'));
+ if (str_starts_with($appsPath['path'], $this->buildPath(''))) {
+ $relativePath = substr($appsPath['path'], strlen($this->buildPath('')));
if ($relativePath !== 'apps') {
$expected[] = $relativePath;
}
@@ -436,13 +447,13 @@ public function checkWritePermissions(): void {
}
// Special handling for included default theme
- foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir . '/themes/example', $excludedElements) as $fileInfo) {
+ foreach ($this->getRecursiveDirectoryIterator($this->buildPath('themes/example'), $excludedElements) as $fileInfo) {
if (!$fileInfo->isWritable()) {
$notWritablePaths[] = $fileInfo->getFilename();
}
}
- $themesReadmeFileInfo = new \SplFileInfo($this->nextcloudDir . '/themes/README');
+ $themesReadmeFileInfo = new \SplFileInfo($this->buildPath('themes/README'));
if (!$themesReadmeFileInfo->isWritable()) {
$notWritablePaths[] = $themesReadmeFileInfo->getFilename();
}
@@ -502,14 +513,14 @@ public function createBackup(): void {
}
foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir, $excludedElements) as $absolutePath => $fileInfo) {
- $relativePath = explode($this->nextcloudDir, $absolutePath)[1];
+ $relativePath = ltrim(substr($absolutePath, strlen($this->nextcloudDir)), '/');
$relativeDirectory = dirname($relativePath);
// Create folder if it doesn't exist
- if (!file_exists($backupFolderLocation . '/' . $relativeDirectory)) {
- $state = mkdir($backupFolderLocation . '/' . $relativeDirectory, 0750, true);
+ if (!file_exists($backupFolderLocation . $relativeDirectory)) {
+ $state = mkdir($backupFolderLocation . $relativeDirectory, 0750, true);
if ($state === false) {
- throw new \Exception('Could not create folder: ' . $backupFolderLocation . '/' . $relativeDirectory);
+ throw new \Exception('Could not create folder: ' . $backupFolderLocation . $relativeDirectory);
}
}
@@ -949,7 +960,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->nextcloudDir . '/version.php');
+ $currentVersion = $this->getVersionByVersionFile($this->buildPath('version.php'));
if (version_compare($downloadedVersion, $currentVersion, '<')) {
throw new \Exception('Downloaded version is lower than installed version');
}
@@ -977,7 +988,7 @@ public function replaceEntryPoints(): void {
$content = "silentLog('[info] replace ' . $file);
- $parentDir = dirname($this->nextcloudDir . '/' . $file);
+ $parentDir = dirname($this->buildPath($file));
if (!file_exists($parentDir)) {
$r = mkdir($parentDir);
if (!$r) {
@@ -985,7 +996,7 @@ public function replaceEntryPoints(): void {
}
}
- $state = file_put_contents($this->nextcloudDir . '/' . $file, $content);
+ $state = file_put_contents($this->buildPath($file), $content);
if ($state === false) {
throw new \Exception("Can't replace entry point: " . $file);
}
@@ -1028,7 +1039,7 @@ private function recursiveDelete(string $folder): void {
public function deleteOldFiles(): void {
$this->silentLog('[info] deleteOldFiles()');
- $shippedAppsFile = $this->nextcloudDir . '/core/shipped.json';
+ $shippedAppsFile = $this->buildPath('core/shipped.json');
$shippedAppsFileContent = file_get_contents($shippedAppsFile);
if ($shippedAppsFileContent === false) {
throw new \Exception('core/shipped.json is not available');
@@ -1056,10 +1067,10 @@ public function deleteOldFiles(): void {
$shippedApps = array_merge($shippedApps, $newShippedApps);
/** @var string $app */
foreach ($shippedApps as $app) {
- $this->recursiveDelete($this->nextcloudDir . '/apps/' . $app);
+ $this->recursiveDelete($this->buildPath('apps/' . $app));
}
- $configSampleFile = $this->nextcloudDir . '/config/config.sample.php';
+ $configSampleFile = $this->buildPath('config/config.sample.php');
if (file_exists($configSampleFile)) {
$this->silentLog('[info] config sample exists');
@@ -1070,7 +1081,7 @@ public function deleteOldFiles(): void {
}
}
- $themesReadme = $this->nextcloudDir . '/themes/README';
+ $themesReadme = $this->buildPath('themes/README');
if (file_exists($themesReadme)) {
$this->silentLog('[info] themes README exists');
@@ -1081,7 +1092,7 @@ public function deleteOldFiles(): void {
}
}
- $this->recursiveDelete($this->nextcloudDir . '/themes/example/');
+ $this->recursiveDelete($this->buildPath('themes/example/'));
// Delete the rest
$excludedElements = [
@@ -1128,23 +1139,23 @@ private function moveWithExclusions(string $dataLocation, array $excludedElement
}
- $fileName = explode($dataLocation, $path)[1];
+ $fileName = substr($path, strlen($dataLocation));
if ($fileInfo->isFile()) {
- if (!file_exists($this->nextcloudDir . '/' . dirname($fileName))) {
- $state = mkdir($this->nextcloudDir . '/' . dirname($fileName), 0755, true);
+ if (!file_exists($this->buildPath(dirname($fileName)))) {
+ $state = mkdir($this->buildPath(dirname($fileName)), 0755, true);
if ($state === false) {
- throw new \Exception('Could not mkdir ' . $this->nextcloudDir . '/' . dirname($fileName));
+ throw new \Exception('Could not mkdir ' . $this->buildPath(dirname($fileName)));
}
}
- $state = @rename($path, $this->nextcloudDir . '/' . $fileName);
+ $state = @rename($path, $this->buildPath($fileName));
if ($state === false) {
throw new \Exception(
sprintf(
'Could not rename %s to %s',
$path,
- $this->nextcloudDir . '/' . $fileName
+ $this->buildPath($fileName)
)
);
}
@@ -1217,7 +1228,7 @@ public function finalize(): void {
$user_ini_additional_lines = implode(PHP_EOL, $user_ini_additional_lines);
}
- $result = file_put_contents($this->nextcloudDir . '/.user.ini', PHP_EOL . '; Additional settings from config.php:' . PHP_EOL . $user_ini_additional_lines . PHP_EOL, FILE_APPEND);
+ $result = file_put_contents($this->buildPath('.user.ini'), PHP_EOL . '; Additional settings from config.php:' . PHP_EOL . $user_ini_additional_lines . PHP_EOL, FILE_APPEND);
if ($result === false) {
throw new \Exception('Could not append to .user.ini');
}
diff --git a/lib/Updater.php b/lib/Updater.php
index 9a915277..edbe65a3 100644
--- a/lib/Updater.php
+++ b/lib/Updater.php
@@ -65,7 +65,7 @@ public function __construct(
throw new \Exception('Could not read data directory from config.php.');
}
- $versionFileName = $this->nextcloudDir . '/version.php';
+ $versionFileName = $this->buildPath('version.php');
if (!file_exists($versionFileName)) {
// fallback to version in config.php
$version = $this->getConfigOptionString('version');
@@ -95,6 +95,17 @@ public function __construct(
$this->buildTime = $buildTime;
}
+ /**
+ * Builds an absolute path by joining the Nextcloud root directory with the provided relative path.
+ * Handles leading and trailing slashes to prevent double-slash issues.
+ *
+ * @param string $suffix Relative path to append (with or without leading slash)
+ * @return string The absolute path
+ */
+ public function buildPath(string $suffix): string {
+ return rtrim($this->nextcloudDir, '/') . '/' . ltrim($suffix, '/');
+ }
+
/**
* @return array{array, string}
*/
@@ -105,7 +116,7 @@ private function readConfigFile(): array {
throw new \Exception('Configuration not found in ' . $dir);
}
} else {
- $configFileName = $this->nextcloudDir . '/config/config.php';
+ $configFileName = $this->buildPath('config/config.php');
}
if (!file_exists($configFileName)) {
@@ -319,8 +330,8 @@ private function getAppDirectories(): array {
throw new \Exception('Invalid configuration in apps_paths configuration key');
}
- if (str_starts_with($appsPath['path'], $this->nextcloudDir . '/')) {
- $relativePath = substr($appsPath['path'], strlen($this->nextcloudDir . '/'));
+ if (str_starts_with($appsPath['path'], $this->buildPath(''))) {
+ $relativePath = substr($appsPath['path'], strlen($this->buildPath('')));
if ($relativePath !== 'apps') {
$expected[] = $relativePath;
}
@@ -420,13 +431,13 @@ public function checkWritePermissions(): void {
}
// Special handling for included default theme
- foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir . '/themes/example', $excludedElements) as $fileInfo) {
+ foreach ($this->getRecursiveDirectoryIterator($this->buildPath('themes/example'), $excludedElements) as $fileInfo) {
if (!$fileInfo->isWritable()) {
$notWritablePaths[] = $fileInfo->getFilename();
}
}
- $themesReadmeFileInfo = new \SplFileInfo($this->nextcloudDir . '/themes/README');
+ $themesReadmeFileInfo = new \SplFileInfo($this->buildPath('themes/README'));
if (!$themesReadmeFileInfo->isWritable()) {
$notWritablePaths[] = $themesReadmeFileInfo->getFilename();
}
@@ -486,14 +497,14 @@ public function createBackup(): void {
}
foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir, $excludedElements) as $absolutePath => $fileInfo) {
- $relativePath = explode($this->nextcloudDir, $absolutePath)[1];
+ $relativePath = ltrim(substr($absolutePath, strlen($this->nextcloudDir)), '/');
$relativeDirectory = dirname($relativePath);
// Create folder if it doesn't exist
- if (!file_exists($backupFolderLocation . '/' . $relativeDirectory)) {
- $state = mkdir($backupFolderLocation . '/' . $relativeDirectory, 0750, true);
+ if (!file_exists($backupFolderLocation . $relativeDirectory)) {
+ $state = mkdir($backupFolderLocation . $relativeDirectory, 0750, true);
if ($state === false) {
- throw new \Exception('Could not create folder: ' . $backupFolderLocation . '/' . $relativeDirectory);
+ throw new \Exception('Could not create folder: ' . $backupFolderLocation . $relativeDirectory);
}
}
@@ -933,7 +944,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->nextcloudDir . '/version.php');
+ $currentVersion = $this->getVersionByVersionFile($this->buildPath('version.php'));
if (version_compare($downloadedVersion, $currentVersion, '<')) {
throw new \Exception('Downloaded version is lower than installed version');
}
@@ -961,7 +972,7 @@ public function replaceEntryPoints(): void {
$content = "silentLog('[info] replace ' . $file);
- $parentDir = dirname($this->nextcloudDir . '/' . $file);
+ $parentDir = dirname($this->buildPath($file));
if (!file_exists($parentDir)) {
$r = mkdir($parentDir);
if (!$r) {
@@ -969,7 +980,7 @@ public function replaceEntryPoints(): void {
}
}
- $state = file_put_contents($this->nextcloudDir . '/' . $file, $content);
+ $state = file_put_contents($this->buildPath($file), $content);
if ($state === false) {
throw new \Exception("Can't replace entry point: " . $file);
}
@@ -1012,7 +1023,7 @@ private function recursiveDelete(string $folder): void {
public function deleteOldFiles(): void {
$this->silentLog('[info] deleteOldFiles()');
- $shippedAppsFile = $this->nextcloudDir . '/core/shipped.json';
+ $shippedAppsFile = $this->buildPath('core/shipped.json');
$shippedAppsFileContent = file_get_contents($shippedAppsFile);
if ($shippedAppsFileContent === false) {
throw new \Exception('core/shipped.json is not available');
@@ -1040,10 +1051,10 @@ public function deleteOldFiles(): void {
$shippedApps = array_merge($shippedApps, $newShippedApps);
/** @var string $app */
foreach ($shippedApps as $app) {
- $this->recursiveDelete($this->nextcloudDir . '/apps/' . $app);
+ $this->recursiveDelete($this->buildPath('apps/' . $app));
}
- $configSampleFile = $this->nextcloudDir . '/config/config.sample.php';
+ $configSampleFile = $this->buildPath('config/config.sample.php');
if (file_exists($configSampleFile)) {
$this->silentLog('[info] config sample exists');
@@ -1054,7 +1065,7 @@ public function deleteOldFiles(): void {
}
}
- $themesReadme = $this->nextcloudDir . '/themes/README';
+ $themesReadme = $this->buildPath('themes/README');
if (file_exists($themesReadme)) {
$this->silentLog('[info] themes README exists');
@@ -1065,7 +1076,7 @@ public function deleteOldFiles(): void {
}
}
- $this->recursiveDelete($this->nextcloudDir . '/themes/example/');
+ $this->recursiveDelete($this->buildPath('themes/example/'));
// Delete the rest
$excludedElements = [
@@ -1112,23 +1123,23 @@ private function moveWithExclusions(string $dataLocation, array $excludedElement
}
- $fileName = explode($dataLocation, $path)[1];
+ $fileName = substr($path, strlen($dataLocation));
if ($fileInfo->isFile()) {
- if (!file_exists($this->nextcloudDir . '/' . dirname($fileName))) {
- $state = mkdir($this->nextcloudDir . '/' . dirname($fileName), 0755, true);
+ if (!file_exists($this->buildPath(dirname($fileName)))) {
+ $state = mkdir($this->buildPath(dirname($fileName)), 0755, true);
if ($state === false) {
- throw new \Exception('Could not mkdir ' . $this->nextcloudDir . '/' . dirname($fileName));
+ throw new \Exception('Could not mkdir ' . $this->buildPath(dirname($fileName)));
}
}
- $state = @rename($path, $this->nextcloudDir . '/' . $fileName);
+ $state = @rename($path, $this->buildPath($fileName));
if ($state === false) {
throw new \Exception(
sprintf(
'Could not rename %s to %s',
$path,
- $this->nextcloudDir . '/' . $fileName
+ $this->buildPath($fileName)
)
);
}
@@ -1201,7 +1212,7 @@ public function finalize(): void {
$user_ini_additional_lines = implode(PHP_EOL, $user_ini_additional_lines);
}
- $result = file_put_contents($this->nextcloudDir . '/.user.ini', PHP_EOL . '; Additional settings from config.php:' . PHP_EOL . $user_ini_additional_lines . PHP_EOL, FILE_APPEND);
+ $result = file_put_contents($this->buildPath('.user.ini'), PHP_EOL . '; Additional settings from config.php:' . PHP_EOL . $user_ini_additional_lines . PHP_EOL, FILE_APPEND);
if ($result === false) {
throw new \Exception('Could not append to .user.ini');
}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 00000000..8e2a09a2
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ tests/unit
+
+
+
diff --git a/tests/unit/UpdaterTest.php b/tests/unit/UpdaterTest.php
new file mode 100644
index 00000000..9b9736fd
--- /dev/null
+++ b/tests/unit/UpdaterTest.php
@@ -0,0 +1,196 @@
+tempDir = sys_get_temp_dir() . '/nextcloud-updater-test-' . uniqid();
+ $this->dataDir = sys_get_temp_dir() . '/nextcloud-updater-data-' . uniqid();
+ $this->configDir = sys_get_temp_dir() . '/nextcloud-config-test-' . uniqid();
+
+ mkdir($this->tempDir . '/updater', 0755, true);
+ mkdir($this->dataDir, 0755, true);
+ mkdir($this->configDir, 0755, true);
+
+ // Write a minimal config.php with required keys
+ file_put_contents($this->configDir . '/config.php', ' "' . $this->dataDir . '",'
+ . '"version" => "30.0.0.1",'
+ . '"instanceid" => "testid",'
+ . '];');
+
+ putenv('NEXTCLOUD_CONFIG_DIR=' . $this->configDir);
+ }
+
+ protected function tearDown(): void {
+ putenv('NEXTCLOUD_CONFIG_DIR=');
+
+ $this->removeDirectory($this->configDir);
+ $this->removeDirectory($this->dataDir);
+ $this->removeDirectory($this->tempDir);
+ }
+
+ private function removeDirectory(string $dir): void {
+ if ($dir === '' || !is_dir($dir)) {
+ return;
+ }
+ $items = new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
+ \RecursiveIteratorIterator::CHILD_FIRST
+ );
+ foreach ($items as $item) {
+ if ($item->isDir()) {
+ rmdir($item->getRealPath());
+ } else {
+ unlink($item->getRealPath());
+ }
+ }
+ rmdir($dir);
+ }
+
+ private function makeUpdater(): Updater {
+ return new Updater($this->tempDir . '/updater');
+ }
+
+ // -------------------------------------------------------------------------
+ // buildPath() unit tests
+ // -------------------------------------------------------------------------
+
+ public function testBuildPathWithSimpleSuffix(): void {
+ $updater = $this->makeUpdater();
+ $this->assertSame($this->tempDir . '/config/config.php', $updater->buildPath('config/config.php'));
+ }
+
+ public function testBuildPathWithLeadingSlash(): void {
+ $updater = $this->makeUpdater();
+ // A suffix with a leading slash must produce the same result as without
+ $this->assertSame(
+ $updater->buildPath('config/config.php'),
+ $updater->buildPath('/config/config.php')
+ );
+ }
+
+ public function testBuildPathWithMultipleLeadingSlashes(): void {
+ $updater = $this->makeUpdater();
+ // Multiple leading slashes must all be stripped
+ $this->assertSame(
+ $updater->buildPath('config/config.php'),
+ $updater->buildPath('///config/config.php')
+ );
+ }
+
+ public function testBuildPathResultHasNoDoubleSlash(): void {
+ $updater = $this->makeUpdater();
+ $this->assertStringNotContainsString('//', $updater->buildPath('/some/path'));
+ }
+
+ public function testBuildPathPreservesRelativePathStructure(): void {
+ $updater = $this->makeUpdater();
+ $this->assertSame($this->tempDir . '/apps/myapp', $updater->buildPath('apps/myapp'));
+ }
+
+ // -------------------------------------------------------------------------
+ // Constructor validation tests
+ // -------------------------------------------------------------------------
+
+ public function testConstructorThrowsOnEmptyBaseDir(): void {
+ putenv('NEXTCLOUD_CONFIG_DIR=');
+ $this->expectException(\Exception::class);
+ new Updater('');
+ }
+
+ public function testConstructorThrowsOnInvalidBaseDir(): void {
+ $this->expectException(\Exception::class);
+ // Use a path whose parent doesn't exist
+ new Updater('/nonexistent/path/that/does/not/exist/updater');
+ }
+
+ // -------------------------------------------------------------------------
+ // Regression tests for issue #711
+ // https://github.com/nextcloud/updater/issues/711
+ //
+ // The bug: createBackup() used explode($nextcloudDir, $absolutePath)[1]
+ // to extract the relative path. When the install-directory name appears
+ // anywhere inside the file path (e.g. Nextcloud installed at /nextcloud
+ // with a file called nextcloud.html), explode() splits on EVERY occurrence
+ // of the delimiter, returning a directory instead of the full file path.
+ // PHP's copy() then fails with "cannot be a directory".
+ //
+ // The fix: ltrim(substr($absolutePath, strlen($nextcloudDir)), '/')
+ // -------------------------------------------------------------------------
+
+ /**
+ * Pure logic regression: shows exactly why explode() was wrong and that
+ * the substr() replacement is correct.
+ */
+ public function testRelativePathExtractionForIssue711(): void {
+ // Simulate Nextcloud installed at /nextcloud (as in the bug report)
+ $installDir = '/nextcloud';
+ // File deep inside core/doc with the same name as the install dir
+ $filePath = '/nextcloud/core/doc/admin/configuration_files/external_storage/nextcloud.html';
+
+ // Old (buggy) approach: explode splits on ALL occurrences of $installDir.
+ // '/nextcloud' appears at the start AND inside '.../external_storage/nextcloud.html'
+ // (because '/nextcloud.html' begins with '/nextcloud').
+ $oldRelativePath = explode($installDir, $filePath)[1];
+ $this->assertSame(
+ '/core/doc/admin/configuration_files/external_storage',
+ $oldRelativePath,
+ 'The old explode() approach incorrectly returns a directory path'
+ );
+ $this->assertStringNotContainsString('nextcloud.html', $oldRelativePath);
+
+ // New (fixed) approach: substr() always takes exactly the right suffix.
+ $newRelativePath = ltrim(substr($filePath, strlen($installDir)), '/');
+ $this->assertSame(
+ 'core/doc/admin/configuration_files/external_storage/nextcloud.html',
+ $newRelativePath,
+ 'The substr() approach correctly returns the full relative file path'
+ );
+ }
+
+ /**
+ * Integration regression: createBackup() must copy a deeply nested file
+ * that has the install-directory name as part of its own name, without
+ * throwing and without producing a double-slash in the destination path.
+ *
+ * @see https://github.com/nextcloud/updater/issues/711
+ */
+ public function testCreateBackupCopiesNestedFilesCorrectly(): void {
+ // Reproduce the directory structure from the bug report:
+ // core/doc/admin/configuration_files/external_storage/nextcloud.html
+ $nestedDir = $this->tempDir . '/core/doc/admin/configuration_files/external_storage';
+ mkdir($nestedDir, 0755, true);
+ file_put_contents($nestedDir . '/nextcloud.html', 'nextcloud docs');
+
+ $updater = $this->makeUpdater();
+ // Must not throw
+ $updater->createBackup();
+
+ // Find the backup dir (contains a timestamp so we use glob)
+ $backupPattern = $this->dataDir . '/updater-testid/backups/nextcloud-30.0.0.1-*';
+ $backupDirs = glob($backupPattern, GLOB_ONLYDIR);
+ $this->assertCount(1, $backupDirs, 'Expected exactly one backup directory');
+
+ // Backup folder ends with '/' by design; build the expected file path
+ $backedUpFile = rtrim($backupDirs[0], '/') . '/core/doc/admin/configuration_files/external_storage/nextcloud.html';
+
+ $this->assertFileExists($backedUpFile, 'Backed-up file must exist at the correct nested path');
+ $this->assertStringNotContainsString('//', $backedUpFile, 'Backup path must not contain double slashes');
+ $this->assertSame('nextcloud docs', file_get_contents($backedUpFile));
+ }
+}
diff --git a/vendor-bin/phpunit/composer.json b/vendor-bin/phpunit/composer.json
new file mode 100644
index 00000000..d0c6fafa
--- /dev/null
+++ b/vendor-bin/phpunit/composer.json
@@ -0,0 +1,10 @@
+{
+ "require-dev": {
+ "phpunit/phpunit": "^11.5"
+ },
+ "config": {
+ "platform": {
+ "php": "8.2"
+ }
+ }
+}
diff --git a/vendor-bin/phpunit/composer.lock b/vendor-bin/phpunit/composer.lock
new file mode 100644
index 00000000..d63f5698
--- /dev/null
+++ b/vendor-bin/phpunit/composer.lock
@@ -0,0 +1,1803 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "2d32e5d858a15b0462bdf724a01645a5",
+ "packages": [],
+ "packages-dev": [
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-01T08:46:24+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+ "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0"
+ },
+ "time": "2025-12-06T11:56:16+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "11.0.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56",
+ "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^5.7.0",
+ "php": ">=8.2",
+ "phpunit/php-file-iterator": "^5.1.0",
+ "phpunit/php-text-template": "^4.0.1",
+ "sebastian/code-unit-reverse-lookup": "^4.0.1",
+ "sebastian/complexity": "^4.0.1",
+ "sebastian/environment": "^7.2.1",
+ "sebastian/lines-of-code": "^3.0.1",
+ "sebastian/version": "^5.0.2",
+ "theseer/tokenizer": "^1.3.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5.46"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-12-24T07:01:01+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "5.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903",
+ "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-02-02T13:52:54+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "5.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2",
+ "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "security": "https://github.com/sebastianbergmann/php-invoker/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:07:44+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964",
+ "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:08:43+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3",
+ "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "security": "https://github.com/sebastianbergmann/php-timer/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:09:35+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "11.5.55",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00",
+ "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.4",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=8.2",
+ "phpunit/php-code-coverage": "^11.0.12",
+ "phpunit/php-file-iterator": "^5.1.1",
+ "phpunit/php-invoker": "^5.0.1",
+ "phpunit/php-text-template": "^4.0.1",
+ "phpunit/php-timer": "^7.0.1",
+ "sebastian/cli-parser": "^3.0.2",
+ "sebastian/code-unit": "^3.0.3",
+ "sebastian/comparator": "^6.3.3",
+ "sebastian/diff": "^6.0.2",
+ "sebastian/environment": "^7.2.1",
+ "sebastian/exporter": "^6.3.2",
+ "sebastian/global-state": "^7.0.2",
+ "sebastian/object-enumerator": "^6.0.1",
+ "sebastian/recursion-context": "^6.0.3",
+ "sebastian/type": "^5.1.3",
+ "sebastian/version": "^5.0.2",
+ "staabm/side-effects-detector": "^1.0.5"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-02-18T12:37:06+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180",
+ "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:41:36+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64",
+ "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-19T07:56:08+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "183a9b2632194febd219bb9246eee421dad8d45e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e",
+ "reference": "183a9b2632194febd219bb9246eee421dad8d45e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:45:54+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "6.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9",
+ "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/diff": "^6.0",
+ "sebastian/exporter": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.4"
+ },
+ "suggest": {
+ "ext-bcmath": "For comparing BcMath\\Number objects"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.3-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-01-24T09:26:40+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "ee41d384ab1906c68852636b6de493846e13e5a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0",
+ "reference": "ee41d384ab1906c68852636b6de493846e13e5a0",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:49:50+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:53:05+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "7.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4",
+ "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/environment",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-05-21T11:55:47+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "6.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "70a298763b40b213ec087c51c739efcaa90bcd74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74",
+ "reference": "70a298763b40b213ec087c51c739efcaa90bcd74",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.3-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-09-24T06:12:51+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "7.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "3be331570a721f9a4b5917f4209773de17f747d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7",
+ "reference": "3be331570a721f9a4b5917f4209773de17f747d7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:57:36+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a",
+ "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:58:38+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "f5b498e631a74204185071eb41f33f38d64608aa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa",
+ "reference": "f5b498e631a74204185071eb41f33f38d64608aa",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:00:13+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9",
+ "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "security": "https://github.com/sebastianbergmann/object-reflector/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:01:32+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "6.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc",
+ "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-13T04:42:22+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "5.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449",
+ "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "security": "https://github.com/sebastianbergmann/type/security/policy",
+ "source": "https://github.com/sebastianbergmann/type/tree/5.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-09T06:55:48+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "5.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874",
+ "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "security": "https://github.com/sebastianbergmann/version/security/policy",
+ "source": "https://github.com/sebastianbergmann/version/tree/5.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-09T05:16:32+00:00"
+ },
+ {
+ "name": "staabm/side-effects-detector",
+ "version": "1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/staabm/side-effects-detector.git",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^1.12.6",
+ "phpunit/phpunit": "^9.6.21",
+ "symfony/var-dumper": "^5.4.43",
+ "tomasvotruba/type-coverage": "1.0.0",
+ "tomasvotruba/unused-public": "1.0.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A static analysis tool to detect side effects in PHP code",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/staabm/side-effects-detector/issues",
+ "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/staabm",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-20T05:08:20+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
+ "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2025-11-17T20:03:58+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {},
+ "platform-dev": {},
+ "platform-overrides": {
+ "php": "8.2"
+ },
+ "plugin-api-version": "2.9.0"
+}
diff --git a/vendor-bin/tests/composer.lock b/vendor-bin/tests/composer.lock
index 81b94f54..f9101e69 100644
--- a/vendor-bin/tests/composer.lock
+++ b/vendor-bin/tests/composer.lock
@@ -9,16 +9,16 @@
"packages-dev": [
{
"name": "behat/behat",
- "version": "v3.27.0",
+ "version": "v3.29.0",
"source": {
"type": "git",
"url": "https://github.com/Behat/Behat.git",
- "reference": "3282ad774358e4eaf533855e9a1f48559894d1b5"
+ "reference": "51bdf81639a14645c5d2c06926f4aa37d204921b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Behat/Behat/zipball/3282ad774358e4eaf533855e9a1f48559894d1b5",
- "reference": "3282ad774358e4eaf533855e9a1f48559894d1b5",
+ "url": "https://api.github.com/repos/Behat/Behat/zipball/51bdf81639a14645c5d2c06926f4aa37d204921b",
+ "reference": "51bdf81639a14645c5d2c06926f4aa37d204921b",
"shasum": ""
},
"require": {
@@ -37,8 +37,8 @@
"symfony/yaml": "^5.4 || ^6.4 || ^7.0"
},
"require-dev": {
- "friendsofphp/php-cs-fixer": "^3.68",
"opis/json-schema": "^2.5",
+ "php-cs-fixer/shim": "^3.89",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^9.6",
"rector/rector": "2.1.7",
@@ -54,11 +54,6 @@
"bin/behat"
],
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.x-dev"
- }
- },
"autoload": {
"psr-4": {
"Behat\\Hook\\": "src/Behat/Hook/",
@@ -98,22 +93,36 @@
],
"support": {
"issues": "https://github.com/Behat/Behat/issues",
- "source": "https://github.com/Behat/Behat/tree/v3.27.0"
+ "source": "https://github.com/Behat/Behat/tree/v3.29.0"
},
- "time": "2025-11-23T12:12:41+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/acoulton",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/carlos-granados",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/stof",
+ "type": "github"
+ }
+ ],
+ "time": "2025-12-11T09:51:30+00:00"
},
{
"name": "behat/gherkin",
- "version": "v4.15.0",
+ "version": "v4.16.1",
"source": {
"type": "git",
"url": "https://github.com/Behat/Gherkin.git",
- "reference": "05a7459283e8e6af0d46ec25b8bb5960ca3cfa7b"
+ "reference": "e26037937dfd48528746764dd870bc5d0836665f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Behat/Gherkin/zipball/05a7459283e8e6af0d46ec25b8bb5960ca3cfa7b",
- "reference": "05a7459283e8e6af0d46ec25b8bb5960ca3cfa7b",
+ "url": "https://api.github.com/repos/Behat/Gherkin/zipball/e26037937dfd48528746764dd870bc5d0836665f",
+ "reference": "e26037937dfd48528746764dd870bc5d0836665f",
"shasum": ""
},
"require": {
@@ -121,7 +130,7 @@
"php": ">=8.1 <8.6"
},
"require-dev": {
- "cucumber/gherkin-monorepo": "dev-gherkin-v36.0.0",
+ "cucumber/gherkin-monorepo": "dev-gherkin-v37.0.0",
"friendsofphp/php-cs-fixer": "^3.77",
"mikey179/vfsstream": "^1.6",
"phpstan/extension-installer": "^1",
@@ -167,9 +176,23 @@
],
"support": {
"issues": "https://github.com/Behat/Gherkin/issues",
- "source": "https://github.com/Behat/Gherkin/tree/v4.15.0"
+ "source": "https://github.com/Behat/Gherkin/tree/v4.16.1"
},
- "time": "2025-11-05T15:34:04+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/acoulton",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/carlos-granados",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/stof",
+ "type": "github"
+ }
+ ],
+ "time": "2025-12-08T16:12:58+00:00"
},
{
"name": "composer/pcre",
@@ -318,16 +341,16 @@
},
{
"name": "nikic/php-parser",
- "version": "v5.6.2",
+ "version": "v5.7.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "3a454ca033b9e06b63282ce19562e892747449bb"
+ "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
- "reference": "3a454ca033b9e06b63282ce19562e892747449bb",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+ "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82",
"shasum": ""
},
"require": {
@@ -370,9 +393,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0"
},
- "time": "2025-10-21T19:32:17+00:00"
+ "time": "2025-12-06T11:56:16+00:00"
},
{
"name": "psr/container",
@@ -529,16 +552,16 @@
},
{
"name": "symfony/config",
- "version": "v7.4.0",
+ "version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
- "reference": "f76c74e93bce2b9285f2dad7fbd06fa8182a7a41"
+ "reference": "6c17162555bfb58957a55bb0e43e00035b6ae3d5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/config/zipball/f76c74e93bce2b9285f2dad7fbd06fa8182a7a41",
- "reference": "f76c74e93bce2b9285f2dad7fbd06fa8182a7a41",
+ "url": "https://api.github.com/repos/symfony/config/zipball/6c17162555bfb58957a55bb0e43e00035b6ae3d5",
+ "reference": "6c17162555bfb58957a55bb0e43e00035b6ae3d5",
"shasum": ""
},
"require": {
@@ -584,7 +607,7 @@
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/config/tree/v7.4.0"
+ "source": "https://github.com/symfony/config/tree/v7.4.7"
},
"funding": [
{
@@ -604,20 +627,20 @@
"type": "tidelift"
}
],
- "time": "2025-11-27T13:27:24+00:00"
+ "time": "2026-03-06T10:41:14+00:00"
},
{
"name": "symfony/console",
- "version": "v7.4.0",
+ "version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8"
+ "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8",
- "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8",
+ "url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d",
+ "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d",
"shasum": ""
},
"require": {
@@ -682,7 +705,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v7.4.0"
+ "source": "https://github.com/symfony/console/tree/v7.4.7"
},
"funding": [
{
@@ -702,20 +725,20 @@
"type": "tidelift"
}
],
- "time": "2025-11-27T13:27:24+00:00"
+ "time": "2026-03-06T14:06:20+00:00"
},
{
"name": "symfony/dependency-injection",
- "version": "v7.4.0",
+ "version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
- "reference": "3972ca7bbd649467b21a54870721b9e9f3652f9b"
+ "reference": "0f651e58f4917fb0e2cd261ccbfe3d71e6e0f5db"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/3972ca7bbd649467b21a54870721b9e9f3652f9b",
- "reference": "3972ca7bbd649467b21a54870721b9e9f3652f9b",
+ "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/0f651e58f4917fb0e2cd261ccbfe3d71e6e0f5db",
+ "reference": "0f651e58f4917fb0e2cd261ccbfe3d71e6e0f5db",
"shasum": ""
},
"require": {
@@ -766,7 +789,7 @@
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/dependency-injection/tree/v7.4.0"
+ "source": "https://github.com/symfony/dependency-injection/tree/v7.4.7"
},
"funding": [
{
@@ -786,7 +809,7 @@
"type": "tidelift"
}
],
- "time": "2025-11-27T13:27:24+00:00"
+ "time": "2026-03-03T07:48:48+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -857,16 +880,16 @@
},
{
"name": "symfony/event-dispatcher",
- "version": "v7.4.0",
+ "version": "v7.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d"
+ "reference": "dc2c0eba1af673e736bb851d747d266108aea746"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d",
- "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/dc2c0eba1af673e736bb851d747d266108aea746",
+ "reference": "dc2c0eba1af673e736bb851d747d266108aea746",
"shasum": ""
},
"require": {
@@ -918,7 +941,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.0"
+ "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.4"
},
"funding": [
{
@@ -938,7 +961,7 @@
"type": "tidelift"
}
],
- "time": "2025-10-28T09:38:46+00:00"
+ "time": "2026-01-05T11:45:34+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -1018,16 +1041,16 @@
},
{
"name": "symfony/filesystem",
- "version": "v7.4.0",
+ "version": "v7.4.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "d551b38811096d0be9c4691d406991b47c0c630a"
+ "reference": "3ebc794fa5315e59fd122561623c2e2e4280538e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a",
- "reference": "d551b38811096d0be9c4691d406991b47c0c630a",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/3ebc794fa5315e59fd122561623c2e2e4280538e",
+ "reference": "3ebc794fa5315e59fd122561623c2e2e4280538e",
"shasum": ""
},
"require": {
@@ -1064,7 +1087,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/filesystem/tree/v7.4.0"
+ "source": "https://github.com/symfony/filesystem/tree/v7.4.6"
},
"funding": [
{
@@ -1084,7 +1107,7 @@
"type": "tidelift"
}
],
- "time": "2025-11-27T13:27:24+00:00"
+ "time": "2026-02-25T16:50:00+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -1510,16 +1533,16 @@
},
{
"name": "symfony/string",
- "version": "v7.4.0",
+ "version": "v7.4.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003"
+ "reference": "9f209231affa85aa930a5e46e6eb03381424b30b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/d50e862cb0a0e0886f73ca1f31b865efbb795003",
- "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003",
+ "url": "https://api.github.com/repos/symfony/string/zipball/9f209231affa85aa930a5e46e6eb03381424b30b",
+ "reference": "9f209231affa85aa930a5e46e6eb03381424b30b",
"shasum": ""
},
"require": {
@@ -1577,7 +1600,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v7.4.0"
+ "source": "https://github.com/symfony/string/tree/v7.4.6"
},
"funding": [
{
@@ -1597,20 +1620,20 @@
"type": "tidelift"
}
],
- "time": "2025-11-27T13:27:24+00:00"
+ "time": "2026-02-09T09:33:46+00:00"
},
{
"name": "symfony/translation",
- "version": "v7.4.0",
+ "version": "v7.4.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68"
+ "reference": "1888cf064399868af3784b9e043240f1d89d25ce"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/2d01ca0da3f092f91eeedb46f24aa30d2fca8f68",
- "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/1888cf064399868af3784b9e043240f1d89d25ce",
+ "reference": "1888cf064399868af3784b9e043240f1d89d25ce",
"shasum": ""
},
"require": {
@@ -1677,7 +1700,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/translation/tree/v7.4.0"
+ "source": "https://github.com/symfony/translation/tree/v7.4.6"
},
"funding": [
{
@@ -1697,7 +1720,7 @@
"type": "tidelift"
}
],
- "time": "2025-11-27T13:27:24+00:00"
+ "time": "2026-02-17T07:53:42+00:00"
},
{
"name": "symfony/translation-contracts",
@@ -1864,16 +1887,16 @@
},
{
"name": "symfony/yaml",
- "version": "v7.4.0",
+ "version": "v7.4.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810"
+ "reference": "58751048de17bae71c5aa0d13cb19d79bca26391"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/6c84a4b55aee4cd02034d1c528e83f69ddf63810",
- "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/58751048de17bae71c5aa0d13cb19d79bca26391",
+ "reference": "58751048de17bae71c5aa0d13cb19d79bca26391",
"shasum": ""
},
"require": {
@@ -1916,7 +1939,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/yaml/tree/v7.4.0"
+ "source": "https://github.com/symfony/yaml/tree/v7.4.6"
},
"funding": [
{
@@ -1936,7 +1959,7 @@
"type": "tidelift"
}
],
- "time": "2025-11-16T10:14:42+00:00"
+ "time": "2026-02-09T09:33:46+00:00"
}
],
"aliases": [],
@@ -1949,5 +1972,5 @@
"platform-overrides": {
"php": "8.2"
},
- "plugin-api-version": "2.6.0"
+ "plugin-api-version": "2.9.0"
}
diff --git a/vendor/bamarni/composer-bin-plugin/.github/workflows/phpstan.yml b/vendor/bamarni/composer-bin-plugin/.github/workflows/phpstan.yml
deleted file mode 100644
index ebb57672..00000000
--- a/vendor/bamarni/composer-bin-plugin/.github/workflows/phpstan.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: "Static analysis"
-
-on:
- push:
- branches:
- - "main"
- - "master"
- pull_request: null
-
-jobs:
- static-analysis:
- runs-on: "ubuntu-latest"
- name: "PHPStan on PHP ${{ matrix.php }}"
- strategy:
- fail-fast: false
- matrix:
- php:
- - "8.1"
- steps:
- - name: "Check out repository code"
- uses: "actions/checkout@v2"
-
- - name: "Setup PHP"
- uses: "shivammathur/setup-php@v2"
- with:
- php-version: "${{ matrix.php }}"
- tools: "composer"
-
- - name: "Install Composer dependencies"
- uses: "ramsey/composer-install@v2"
- with:
- dependency-versions: "highest"
-
- - name: "Perform static analysis"
- run: "make phpstan"
diff --git a/vendor/bamarni/composer-bin-plugin/.github/workflows/tests.yaml b/vendor/bamarni/composer-bin-plugin/.github/workflows/tests.yaml
deleted file mode 100644
index 5bec38a4..00000000
--- a/vendor/bamarni/composer-bin-plugin/.github/workflows/tests.yaml
+++ /dev/null
@@ -1,80 +0,0 @@
-name: "Tests"
-
-on:
- push:
- branches:
- - "main"
- - "master"
- pull_request: null
-
-jobs:
- unit-tests:
- runs-on: "ubuntu-latest"
- name: "Unit Tests on PHP ${{ matrix.php }} and ${{ matrix.tools }}"
- strategy:
- fail-fast: false
- matrix:
- php:
- - "7.2"
- - "7.3"
- - "7.4"
- - "8.0"
- - "8.1"
- tools: [ "composer" ]
- dependency-versions: [ "highest" ]
- include:
- - php: "7.2"
- tools: "composer:v2.0"
- dependency-versions: "lowest"
-
- steps:
- - name: "Check out repository code"
- uses: "actions/checkout@v2"
-
- - name: "Setup PHP"
- uses: "shivammathur/setup-php@v2"
- with:
- php-version: "${{ matrix.php }}"
- tools: "${{ matrix.tools }}"
-
- - name: "Install Composer dependencies"
- uses: "ramsey/composer-install@v2"
- with:
- dependency-versions: "${{ matrix.dependency-versions }}"
-
- - name: "Validate composer.json"
- run: "composer validate --strict --no-check-lock"
-
- - name: "Run tests"
- run: "vendor/bin/phpunit --group default"
-
- e2e-tests:
- runs-on: "ubuntu-latest"
- name: "E2E Tests on PHP ${{ matrix.php }}"
- strategy:
- fail-fast: false
- matrix:
- php:
- - "8.1"
-
- steps:
- - name: "Check out repository code"
- uses: "actions/checkout@v2"
-
- - name: "Setup PHP"
- uses: "shivammathur/setup-php@v2"
- with:
- php-version: "${{ matrix.php }}"
- tools: "composer"
-
- - name: "Correct bin plugin version for e2e scenarios (PR-only)"
- if: github.event_name == 'pull_request'
- run: find e2e -maxdepth 1 -mindepth 1 -type d -exec bash -c "cd {} && composer require --dev bamarni/composer-bin-plugin:dev-${GITHUB_SHA} --no-update" \;
-
- - name: "Install Composer dependencies"
- uses: "ramsey/composer-install@v2"
- with:
- dependency-versions: "highest"
-
- - name: "Run tests"
- run: "vendor/bin/phpunit --group e2e"
diff --git a/vendor/bamarni/composer-bin-plugin/.makefile/touch.sh b/vendor/bamarni/composer-bin-plugin/.makefile/touch.sh
deleted file mode 100755
index 5c0a7759..00000000
--- a/vendor/bamarni/composer-bin-plugin/.makefile/touch.sh
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env bash
-#
-# Takes a given string, e.g. 'bin/console' or 'docker-compose exec php bin/console'
-# and split it by words. For each words, if the target is a file, it is touched.
-#
-# This allows to implement a similar rule to:
-#
-# ```Makefile
-# bin/php-cs-fixer: vendor
-# touch $@
-# ```
-#
-# Indeed when the rule `bin/php-cs-fixer` is replaced with a docker-compose
-# equivalent, it will not play out as nicely.
-#
-# Arguments:
-# $1 - {string} Command potentially containing a file
-#
-
-set -Eeuo pipefail;
-
-
-readonly ERROR_COLOR="\e[41m";
-readonly NO_COLOR="\e[0m";
-
-
-if [ $# -ne 1 ]; then
- printf "${ERROR_COLOR}Illegal number of parameters.${NO_COLOR}\n";
-
- exit 1;
-fi
-
-
-readonly FILES="$1";
-
-
-#######################################
-# Touch the given file path if the target is a file and do not create the file
-# if does not exist.
-#
-# Globals:
-# None
-#
-# Arguments:
-# $1 - {string} File path
-#
-# Returns:
-# None
-#######################################
-touch_file() {
- local file="$1";
-
- if [ -e ${file} ]; then
- touch -c ${file};
- fi
-}
-
-for file in ${FILES}
-do
- touch_file ${file};
-done
diff --git a/vendor/bamarni/composer-bin-plugin/.phive/phars.xml b/vendor/bamarni/composer-bin-plugin/.phive/phars.xml
deleted file mode 100644
index 335086e3..00000000
--- a/vendor/bamarni/composer-bin-plugin/.phive/phars.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/vendor/bamarni/composer-bin-plugin/composer.json b/vendor/bamarni/composer-bin-plugin/composer.json
index 5b3809ba..4b76ff1b 100644
--- a/vendor/bamarni/composer-bin-plugin/composer.json
+++ b/vendor/bamarni/composer-bin-plugin/composer.json
@@ -17,11 +17,11 @@
},
"require-dev": {
"ext-json": "*",
- "composer/composer": "^2.0",
+ "composer/composer": "^2.2.26",
"phpstan/extension-installer": "^1.1",
- "phpstan/phpstan": "^1.8",
- "phpstan/phpstan-phpunit": "^1.1",
- "phpunit/phpunit": "^8.5 || ^9.5",
+ "phpstan/phpstan": "^1.8 || ^2.0",
+ "phpstan/phpstan-phpunit": "^1.1 || ^2.0",
+ "phpunit/phpunit": "^8.5 || ^9.6 || ^10.0",
"symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
"symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
"symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0"
@@ -38,9 +38,9 @@
},
"config": {
"allow-plugins": {
- "phpstan/extension-installer": true,
"ergebnis/composer-normalize": true,
- "infection/extension-installer": true
+ "infection/extension-installer": true,
+ "phpstan/extension-installer": true
},
"sort-packages": true
},
diff --git a/vendor/bamarni/composer-bin-plugin/src/BamarniBinPlugin.php b/vendor/bamarni/composer-bin-plugin/src/BamarniBinPlugin.php
index 1d21c93b..19e36e0a 100644
--- a/vendor/bamarni/composer-bin-plugin/src/BamarniBinPlugin.php
+++ b/vendor/bamarni/composer-bin-plugin/src/BamarniBinPlugin.php
@@ -24,6 +24,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
+
use function count;
use function in_array;
use function sprintf;
diff --git a/vendor/bamarni/composer-bin-plugin/src/Command/BinCommand.php b/vendor/bamarni/composer-bin-plugin/src/Command/BinCommand.php
index 9edb4c3e..69c4aa99 100644
--- a/vendor/bamarni/composer-bin-plugin/src/Command/BinCommand.php
+++ b/vendor/bamarni/composer-bin-plugin/src/Command/BinCommand.php
@@ -19,6 +19,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
+
use function chdir;
use function count;
use function file_exists;
@@ -30,6 +31,7 @@
use function mkdir;
use function putenv;
use function sprintf;
+
use const GLOB_ONLYDIR;
/**
@@ -96,6 +98,7 @@ public function isProxyCommand(): bool
public function execute(InputInterface $input, OutputInterface $output): int
{
// Switch to requireComposer() once Composer 2.3 is set as the minimum
+ // @phpstan-ignore function.alreadyNarrowedType
$composer = method_exists($this, 'requireComposer')
? $this->requireComposer()
: $this->getComposer();
diff --git a/vendor/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php b/vendor/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php
index 33145078..8985a7cd 100644
--- a/vendor/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php
+++ b/vendor/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php
@@ -5,6 +5,7 @@
namespace Bamarni\Composer\Bin\Command;
use RuntimeException;
+
use function sprintf;
final class CouldNotCreateNamespaceDir extends RuntimeException
diff --git a/vendor/bamarni/composer-bin-plugin/src/Config/Config.php b/vendor/bamarni/composer-bin-plugin/src/Config/Config.php
index fa62b3a1..629ab868 100644
--- a/vendor/bamarni/composer-bin-plugin/src/Config/Config.php
+++ b/vendor/bamarni/composer-bin-plugin/src/Config/Config.php
@@ -5,6 +5,7 @@
namespace Bamarni\Composer\Bin\Config;
use Composer\Composer;
+
use function array_key_exists;
use function array_merge;
use function function_exists;
diff --git a/vendor/bamarni/composer-bin-plugin/src/Input/BinInputFactory.php b/vendor/bamarni/composer-bin-plugin/src/Input/BinInputFactory.php
index dab96f68..105afae9 100644
--- a/vendor/bamarni/composer-bin-plugin/src/Input/BinInputFactory.php
+++ b/vendor/bamarni/composer-bin-plugin/src/Input/BinInputFactory.php
@@ -6,6 +6,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\StringInput;
+
use function array_filter;
use function array_map;
use function implode;
@@ -61,7 +62,7 @@ public static function createInput(
public static function createNamespaceInput(InputInterface $previousInput): InputInterface
{
$matchResult = preg_match(
- '/^(.+?\s?)(--(?: .+)?)?$/',
+ '/^([^\s]+)(.*?\s?)(--(?: .+)?)?$/',
$previousInput->__toString(),
$matches
);
@@ -74,9 +75,10 @@ public static function createNamespaceInput(InputInterface $previousInput): Inpu
array_map(
'trim',
[
- $matches[1],
'--working-dir=.',
- $matches[2] ?? '',
+ $matches[1],
+ $matches[2],
+ $matches[3] ?? '',
]
)
);
diff --git a/vendor/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php b/vendor/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php
index 2418d9c8..7f44ff5e 100644
--- a/vendor/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php
+++ b/vendor/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php
@@ -6,6 +6,7 @@
use RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
+
use function sprintf;
final class InvalidBinInput extends RuntimeException
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index 00778138..3ae12af1 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -25,7 +25,6 @@
'NC\\Updater\\UpdateCommand' => $baseDir . '/lib/UpdateCommand.php',
'NC\\Updater\\UpdateException' => $baseDir . '/lib/UpdateException.php',
'NC\\Updater\\Updater' => $baseDir . '/lib/Updater.php',
- 'NC\\Updater\\Version' => $baseDir . '/lib/Version.php',
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php',
'Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php',
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 173bf8d5..fe175374 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -103,7 +103,6 @@ class ComposerStaticInitcbbead1010db4afef500f7adc2b6cac3
'NC\\Updater\\UpdateCommand' => __DIR__ . '/../..' . '/lib/UpdateCommand.php',
'NC\\Updater\\UpdateException' => __DIR__ . '/../..' . '/lib/UpdateException.php',
'NC\\Updater\\Updater' => __DIR__ . '/../..' . '/lib/Updater.php',
- 'NC\\Updater\\Version' => __DIR__ . '/../..' . '/lib/Version.php',
'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'Psr\\Container\\ContainerExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerExceptionInterface.php',
'Psr\\Container\\ContainerInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerInterface.php',
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 55853f22..d44e57cc 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -2,17 +2,17 @@
"packages": [
{
"name": "bamarni/composer-bin-plugin",
- "version": "1.8.2",
- "version_normalized": "1.8.2.0",
+ "version": "1.9.1",
+ "version_normalized": "1.9.1.0",
"source": {
"type": "git",
"url": "https://github.com/bamarni/composer-bin-plugin.git",
- "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880"
+ "reference": "641d0663f5ac270b1aeec4337b7856f76204df47"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
- "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
+ "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/641d0663f5ac270b1aeec4337b7856f76204df47",
+ "reference": "641d0663f5ac270b1aeec4337b7856f76204df47",
"shasum": ""
},
"require": {
@@ -20,17 +20,17 @@
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
- "composer/composer": "^2.0",
+ "composer/composer": "^2.2.26",
"ext-json": "*",
"phpstan/extension-installer": "^1.1",
- "phpstan/phpstan": "^1.8",
- "phpstan/phpstan-phpunit": "^1.1",
- "phpunit/phpunit": "^8.5 || ^9.5",
+ "phpstan/phpstan": "^1.8 || ^2.0",
+ "phpstan/phpstan-phpunit": "^1.1 || ^2.0",
+ "phpunit/phpunit": "^8.5 || ^9.6 || ^10.0",
"symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
"symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
"symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0"
},
- "time": "2022-10-31T08:38:03+00:00",
+ "time": "2026-02-04T10:18:12+00:00",
"type": "composer-plugin",
"extra": {
"class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin"
@@ -56,7 +56,7 @@
],
"support": {
"issues": "https://github.com/bamarni/composer-bin-plugin/issues",
- "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2"
+ "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.9.1"
},
"install-path": "../bamarni/composer-bin-plugin"
},
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index a121f657..0e1e0fbc 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -1,9 +1,9 @@
array(
'name' => '__root__',
- 'pretty_version' => 'dev-master',
- 'version' => 'dev-master',
- 'reference' => '0deb35c1f4fd23fea55c03db40f07c72603adc43',
+ 'pretty_version' => 'dev-copilot/add-string-concat-method',
+ 'version' => 'dev-copilot/add-string-concat-method',
+ 'reference' => 'dc688b2b097ffaa4f596e943d3626d5b65e6016a',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -11,18 +11,18 @@
),
'versions' => array(
'__root__' => array(
- 'pretty_version' => 'dev-master',
- 'version' => 'dev-master',
- 'reference' => '0deb35c1f4fd23fea55c03db40f07c72603adc43',
+ 'pretty_version' => 'dev-copilot/add-string-concat-method',
+ 'version' => 'dev-copilot/add-string-concat-method',
+ 'reference' => 'dc688b2b097ffaa4f596e943d3626d5b65e6016a',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'bamarni/composer-bin-plugin' => array(
- 'pretty_version' => '1.8.2',
- 'version' => '1.8.2.0',
- 'reference' => '92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880',
+ 'pretty_version' => '1.9.1',
+ 'version' => '1.9.1.0',
+ 'reference' => '641d0663f5ac270b1aeec4337b7856f76204df47',
'type' => 'composer-plugin',
'install_path' => __DIR__ . '/../bamarni/composer-bin-plugin',
'aliases' => array(),