Merge pull request #496 from nextcloud/backport/495/stable25

[stable25] fix: update testing range for 25 and update dependencies
This commit is contained in:
Arthur Schiwon 2023-09-19 13:59:37 +02:00 committed by GitHub
commit 739dff4779
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
111 changed files with 10132 additions and 3469 deletions

View File

@ -16,27 +16,32 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ["8.0"]
php-versions: ["8.1"]
name: check-same-code-base
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@c5fc0d8281aba02c7fda07d3a70cc5371548067d # v2
with:
php-version: ${{ matrix.php-versions }}
coverage: none
ini-file: development
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies
run: composer i
- name: check-same-code-base
run: make check-same-code-base
- name: check-updater-phar
run: |
make box
./box info -l updater.phar | grep -v "^Signature Hash" | grep -v "^ Version.php " | grep -v "^Contents: " >updater.old.txt
./vendor/bin/box info -l updater.phar | grep -v "^Signature Hash" | grep -v "^ Version.php " | grep -v "^Contents: " | grep -v "^ installed.php " > updater.old.txt
make updater.phar
./box info -l updater.phar | grep -v "^Signature Hash" | grep -v "^ Version.php " | grep -v "^Contents: " >updater.txt
./vendor/bin/box info -l updater.phar | grep -v "^Signature Hash" | grep -v "^ Version.php " | grep -v "^Contents: " | grep -v "^ installed.php " > updater.txt
diff updater.txt updater.old.txt

View File

@ -3,19 +3,17 @@
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
name: Lint
name: Lint php-cs
on:
pull_request:
push:
branches:
- main
- master
- stable*
on: pull_request
permissions:
contents: read
concurrency:
group: lint-php-cs-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
@ -24,13 +22,16 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@v2
- name: Set up php
uses: shivammathur/setup-php@c5fc0d8281aba02c7fda07d3a70cc5371548067d # v2
with:
php-version: "7.4"
php-version: 8.1
coverage: none
ini-file: development
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies
run: composer i

View File

@ -3,7 +3,7 @@
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
name: Lint
name: Lint php
on:
pull_request:
@ -16,24 +16,31 @@ on:
permissions:
contents: read
concurrency:
group: lint-php-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
php-lint:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ["7.4", "8.0", "8.1"]
php-versions: [ "7.4", "8.0", "8.1" ]
name: php-lint
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@c5fc0d8281aba02c7fda07d3a70cc5371548067d # v2
with:
php-version: ${{ matrix.php-versions }}
coverage: none
ini-file: development
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Lint
run: composer run lint

View File

@ -1,30 +1,39 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
name: Static analysis
on:
pull_request:
push:
branches:
- master
- main
- stable*
concurrency:
group: psalm-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
static-psalm-analysis:
static-analysis:
runs-on: ubuntu-latest
strategy:
matrix:
ocp-version: [ 'dev-master' ]
name: Nextcloud ${{ matrix.ocp-version }}
name: Nextcloud
steps:
- name: Checkout
uses: actions/checkout@master
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Set up php
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@c5fc0d8281aba02c7fda07d3a70cc5371548067d # v2
with:
php-version: 7.4
php-version: 8.1
coverage: none
ini-file: development
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies
run: composer i

View File

@ -12,11 +12,11 @@ permissions:
contents: read
jobs:
php-lint:
test-cli:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ["7.4"]
php-versions: ["8.1"]
name: test-cli
@ -28,7 +28,12 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: apcu,ctype,curl,dom,fileinfo,ftp,gd,intl,json,ldap,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip
coverage: none
ini-file: development
- name: Install dependencies
run: composer i
- name: test-cli
run: make test-cli

View File

@ -16,19 +16,24 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ["7.4", "8.0", "8.1"]
php-versions: ["8.0", "8.1", "8.2"]
name: test-master
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@c5fc0d8281aba02c7fda07d3a70cc5371548067d # v2
with:
php-version: ${{ matrix.php-versions }}
extensions: apcu,ctype,curl,dom,fileinfo,ftp,gd,intl,json,ldap,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip
coverage: none
ini-file: development
- name: Install dependencies
run: composer i
- name: test-master
run: make test-master

View File

@ -16,25 +16,25 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ["7.4", "8.0"]
nextcloud-versions: ["19", "20", "21"]
exclude:
- php-versions: "8.0"
nextcloud-versions: "19"
- php-versions: "8.0"
nextcloud-versions: "20"
php-versions: ["7.4", "8.0", "8.1"]
nextcloud-versions: ["25"]
name: test-stable
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@c5fc0d8281aba02c7fda07d3a70cc5371548067d # v2
with:
php-version: ${{ matrix.php-versions }}
extensions: apcu,ctype,curl,dom,fileinfo,ftp,gd,intl,json,ldap,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip
coverage: none
ini-file: development
- name: Install dependencies
run: composer i
- name: test-stable${{ matrix.nextcloud-versions }}
run: make test-stable${{ matrix.nextcloud-versions }}

14
.gitignore vendored
View File

@ -1,8 +1,10 @@
box
lib/Version.php
tests/data
tests/vendor
vendor/symfony/console/Tests
vendor/symfony/debug/Tests
/box
/lib/Version.php
/tests/data
/tests/vendor
/vendor/bamarni/composer-bin-plugin/e2e
/vendor/bin
/vendor/symfony/console/Tests
/vendor/symfony/debug/Tests
/vendor-bin/**/vendor
.php-cs-fixer.cache

View File

@ -1,13 +1,9 @@
.PHONY: updater.phar
box:
curl -L https://github.com/box-project/box/releases/download/3.11.1/box.phar -o box
chmod +x box
updater.phar: box updater.php lib/*.php buildVersionFile.php
updater.phar: updater.php lib/*.php buildVersionFile.php
php buildVersionFile.php
composer dump-autoload
./box compile -c box.json
composer run box
chmod +x updater.phar
rm lib/Version.php
@ -29,14 +25,11 @@ test: updater.phar test/vendor
test-cli: updater.phar test/vendor
cd tests && vendor/behat/behat/bin/behat features/cli.feature
test-stable19: updater.phar test/vendor
cd tests && vendor/behat/behat/bin/behat features/stable19.feature
test-stable24: updater.phar test/vendor
cd tests && vendor/behat/behat/bin/behat features/stable24.feature
test-stable20: updater.phar test/vendor
cd tests && vendor/behat/behat/bin/behat features/stable20.feature
test-stable21: updater.phar test/vendor
cd tests && vendor/behat/behat/bin/behat features/stable21.feature
test-stable25: updater.phar test/vendor
cd tests && vendor/behat/behat/bin/behat features/stable25.feature
test-master: updater.phar test/vendor
cd tests && vendor/behat/behat/bin/behat features/master.feature

View File

@ -19,16 +19,23 @@
"symfony/console": "^4.4"
},
"scripts": {
"box": "box compile -c box.json",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"lint": "find . -name \\*.php -not -path './vendor*' -not -path './build/*' -not -path './node_modules/*' -print0 | xargs -0 -n1 php -l",
"post-install-cmd": ["@composer bin all install --ansi"],
"post-update-cmd": ["@composer bin all update --ansi"],
"psalm": "psalm",
"psalm:fix": "psalm --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType",
"psalm:update-baseline": "psalm --threads=1 --update-baseline"
"psalm": "psalm --threads=$(nproc)",
"psalm:ci": "psalm --threads=1",
"psalm:fix": "- --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.5"
"bamarni/composer-bin-plugin": "^1.8",
"nextcloud/coding-standard": "^1.1"
},
"extra": {
"bamarni-bin": {
"bin-links": true,
"target-directory": "vendor-bin",
"forward-command": true
}
}
}

182
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "41e003a0aea3705ba595ca045d13224e",
"content-hash": "64735a3802f11a7dfce0eea6c7285653",
"packages": [
{
"name": "psr/container",
@ -56,16 +56,16 @@
},
{
"name": "symfony/console",
"version": "v4.4.43",
"version": "v4.4.49",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "8a2628d2d5639f35113dc1b833ecd91e1ed1cf46"
"reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/8a2628d2d5639f35113dc1b833ecd91e1ed1cf46",
"reference": "8a2628d2d5639f35113dc1b833ecd91e1ed1cf46",
"url": "https://api.github.com/repos/symfony/console/zipball/33fa45ffc81fdcc1ca368d4946da859c8cdb58d9",
"reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9",
"shasum": ""
},
"require": {
@ -126,7 +126,7 @@
"description": "Eases the creation of beautiful and testable command line interfaces",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/console/tree/v4.4.43"
"source": "https://github.com/symfony/console/tree/v4.4.49"
},
"funding": [
{
@ -142,7 +142,7 @@
"type": "tidelift"
}
],
"time": "2022-06-23T12:22:25+00:00"
"time": "2022-11-05T17:10:16+00:00"
},
{
"name": "symfony/deprecation-contracts",
@ -213,16 +213,16 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.26.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
"reference": "42292d99c55abe617799667f454222c54c60e229"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "42292d99c55abe617799667f454222c54c60e229",
"shasum": ""
},
"require": {
@ -237,7 +237,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.26-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -276,7 +276,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
},
"funding": [
{
@ -292,20 +292,20 @@
"type": "tidelift"
}
],
"time": "2022-05-24T11:49:31+00:00"
"time": "2023-07-28T09:04:16+00:00"
},
{
"name": "symfony/polyfill-php73",
"version": "v1.26.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
"reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5",
"reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5",
"shasum": ""
},
"require": {
@ -314,7 +314,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.26-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -355,7 +355,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
"source": "https://github.com/symfony/polyfill-php73/tree/v1.28.0"
},
"funding": [
{
@ -371,20 +371,20 @@
"type": "tidelift"
}
],
"time": "2022-05-24T11:49:31+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.26.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": ""
},
"require": {
@ -393,7 +393,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.26-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -438,7 +438,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
},
"funding": [
{
@ -454,7 +454,7 @@
"type": "tidelift"
}
],
"time": "2022-05-10T07:21:04+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/service-contracts",
@ -543,29 +543,36 @@
"packages-dev": [
{
"name": "bamarni/composer-bin-plugin",
"version": "v1.5.0",
"version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/bamarni/composer-bin-plugin.git",
"reference": "49934ffea764864788334c1485fbb08a4b852031"
"reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/49934ffea764864788334c1485fbb08a4b852031",
"reference": "49934ffea764864788334c1485fbb08a4b852031",
"url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
"reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": "^5.5.9 || ^7.0 || ^8.0"
"composer-plugin-api": "^2.0",
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"composer/composer": "^1.0 || ^2.0",
"symfony/console": "^2.5 || ^3.0 || ^4.0"
"composer/composer": "^2.0",
"ext-json": "*",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
"phpunit/phpunit": "^8.5 || ^9.5",
"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"
},
"type": "composer-plugin",
"extra": {
"class": "Bamarni\\Composer\\Bin\\Plugin"
"class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin"
},
"autoload": {
"psr-4": {
@ -585,7 +592,104 @@
"isolation",
"tool"
],
"time": "2022-02-22T21:01:25+00:00"
"support": {
"issues": "https://github.com/bamarni/composer-bin-plugin/issues",
"source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2"
},
"time": "2022-10-31T08:38:03+00:00"
},
{
"name": "nextcloud/coding-standard",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/nextcloud/coding-standard.git",
"reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/55def702fb9a37a219511e1d8c6fe8e37164c1fb",
"reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb",
"shasum": ""
},
"require": {
"php": "^7.3|^8.0",
"php-cs-fixer/shim": "^3.17"
},
"type": "library",
"autoload": {
"psr-4": {
"Nextcloud\\CodingStandard\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christoph Wurst",
"email": "christoph@winzerhof-wurst.at"
}
],
"description": "Nextcloud coding standards for the php cs fixer",
"support": {
"issues": "https://github.com/nextcloud/coding-standard/issues",
"source": "https://github.com/nextcloud/coding-standard/tree/v1.1.1"
},
"time": "2023-06-01T12:05:01+00:00"
},
{
"name": "php-cs-fixer/shim",
"version": "v3.27.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/shim.git",
"reference": "fc5c89dc53f46e533cca327d699802017022c3a7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/fc5c89dc53f46e533cca327d699802017022c3a7",
"reference": "fc5c89dc53f46e533cca327d699802017022c3a7",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-tokenizer": "*",
"php": "^7.4 || ^8.0"
},
"replace": {
"friendsofphp/php-cs-fixer": "self.version"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
"ext-mbstring": "For handling non-UTF8 characters."
},
"bin": [
"php-cs-fixer",
"php-cs-fixer.phar"
],
"type": "application",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Dariusz Rumiński",
"email": "dariusz.ruminski@gmail.com"
}
],
"description": "A tool to automatically fix PHP code style",
"support": {
"issues": "https://github.com/PHP-CS-Fixer/shim/issues",
"source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.27.0"
},
"time": "2023-09-17T14:38:32+00:00"
}
],
"aliases": [],
@ -598,5 +702,5 @@
"platform-overrides": {
"php": "7.4"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

View File

@ -21,6 +21,7 @@
*
*/
class UpdateException extends \Exception {
protected $data;
@ -47,7 +48,13 @@ class RecursiveDirectoryIteratorWithoutData extends \RecursiveFilterIterator {
'data',
'..',
];
return !(in_array($this->current()->getFilename(), $excludes, true) || $this->current()->isDir());
$current = $this->current();
if (!$current) {
return false;
}
return !(in_array($current->getFilename(), $excludes, true) || $current->isDir());
}
}
@ -248,9 +255,13 @@ class Updater {
'index.html',
'indie.json',
'.user.ini',
'composer.json',
'composer.lock',
'console.php',
'cron.php',
'index.php',
'package.json',
'package-lock.json',
'public.php',
'remote.php',
'status.php',
@ -465,7 +476,7 @@ class Updater {
$this->silentLog('[info] end of createBackup()');
}
private function getChangelogURL($versionString) {
private function getChangelogURL(string $versionString) {
$this->silentLog('[info] getChangelogURL()');
$changelogWebsite = 'https://nextcloud.com/changelog/';
$changelogURL = $changelogWebsite . '#' . str_replace('.', '-', $versionString);
@ -643,7 +654,7 @@ class Updater {
}
$response = $this->getUpdateServerResponse();
if (!isset($response['signature'])) {
if (empty($response['signature'])) {
throw new \Exception('No signature specified for defined update');
}
@ -1829,13 +1840,13 @@ if (strpos($updaterUrl, 'index.php') === false) {
</div>
</li>
</ul>
<?php else : ?>
<?php else: ?>
<div id="login" class="section">
<h2>Authentication</h2>
<p>To login you need to provide the unhashed value of "updater.secret" in your config file.</p>
<p>If you don't know that value, you can access this updater directly via the Nextcloud admin screen or generate
your own secret:</p>
<code>php -r '$password = trim(shell_exec("openssl rand -base64 48")); if (strlen($password) === 64) {$hash = password_hash($password, PASSWORD_DEFAULT) . "\n"; echo "Insert as \"updater.secret\": ".$hash; echo "The plaintext value is: ".$password."\n";} else {echo "Could not execute OpenSSL.\n";};'</code>
<code>php -r '$password = trim(shell_exec("openssl rand -base64 48"));if(strlen($password) === 64) {$hash = password_hash($password, PASSWORD_DEFAULT) . "\n"; echo "Insert as \"updater.secret\": ".$hash; echo "The plaintext value is: ".$password."\n";}else{echo "Could not execute OpenSSL.\n";};'</code>
<form method="post" name="login">
<fieldset>
<input type="password" name="updater-secret-input" value=""
@ -1854,8 +1865,7 @@ if (strpos($updaterUrl, 'index.php') === false) {
</div>
</body>
<?php if ($auth->isAuthenticated()) : ?>
<?php if ($auth->isAuthenticated()): ?>
<script>
function escapeHTML(s) {
return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('>').join('&gt;').split('"').join('&quot;').split('\'').join('&#039;');
@ -2243,3 +2253,4 @@ if (strpos($updaterUrl, 'index.php') === false) {
<?php endif; ?>
</html>

View File

@ -31,6 +31,12 @@ class RecursiveDirectoryIteratorWithoutData extends \RecursiveFilterIterator {
'data',
'..',
];
return !(in_array($this->current()->getFilename(), $excludes, true) || $this->current()->isDir());
$current = $this->current();
if (!$current) {
return false;
}
return !(in_array($current->getFilename(), $excludes, true) || $current->isDir());
}
}

View File

@ -219,9 +219,13 @@ class Updater {
'index.html',
'indie.json',
'.user.ini',
'composer.json',
'composer.lock',
'console.php',
'cron.php',
'index.php',
'package.json',
'package-lock.json',
'public.php',
'remote.php',
'status.php',
@ -436,7 +440,7 @@ class Updater {
$this->silentLog('[info] end of createBackup()');
}
private function getChangelogURL($versionString) {
private function getChangelogURL(string $versionString) {
$this->silentLog('[info] getChangelogURL()');
$changelogWebsite = 'https://nextcloud.com/changelog/';
$changelogURL = $changelogWebsite . '#' . str_replace('.', '-', $versionString);
@ -614,7 +618,7 @@ class Updater {
}
$response = $this->getUpdateServerResponse();
if (!isset($response['signature'])) {
if (empty($response['signature'])) {
throw new \Exception('No signature specified for defined update');
}

View File

@ -2,6 +2,6 @@
"require-dev": {
},
"require": {
"behat/behat": "3.0.*"
"behat/behat": "3.11.*"
}
}

1515
tests/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -55,7 +55,7 @@ class FeatureContext implements SnippetAcceptingContext {
}
/**
* @Given /the current (installed )?version is ([0-9.]+((beta|RC)[0-9]?)?|stable[0-9]+|master)/
* @Given /the current (installed )?version is ([0-9.]+((beta|RC|rc)[0-9]?)?|stable[0-9]+|master)/
*/
public function theCurrentInstalledVersionIs($installed, $version) {
if ($this->skipIt) {
@ -83,7 +83,7 @@ class FeatureContext implements SnippetAcceptingContext {
if (!file_exists($this->tmpDownloadDir . $filename)) {
$fp = fopen($this->tmpDownloadDir . $filename, 'w+');
$url = $this->downloadURL . $filename;
if (strpos($version, 'RC') !== false || strpos($version, 'beta') !== false) {
if (str_contains($version, 'RC') || str_contains($version, 'rc') || str_contains($version, 'beta')) {
$url = $this->prereleasesDownloadURL . 'nextcloud-' . $version . '.zip';
} elseif (strpos($version, 'stable') !== false || strpos($version, 'master') !== false) {
$url = $this->dailyDownloadURL . $version . '.zip';
@ -96,7 +96,7 @@ class FeatureContext implements SnippetAcceptingContext {
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode !== 200) {
throw new \Exception('Download failed - HTTP code: ' . $httpCode);
throw new \Exception('Download failed for ' . $url . ' - HTTP code: ' . $httpCode);
}
curl_close($ch);
fclose($fp);
@ -189,55 +189,55 @@ class FeatureContext implements SnippetAcceptingContext {
*/
public function getSignatureForVersion($version) {
$signatures = [
'19.0.0beta4' => 'Cum4wIKpCRHNZuOQ/SfDYsIp39/4/Z3EIiMLTV7vFLf1pjsn+q1FRwk7HbT0ileU
9eGbmpJHrmbNFk73g6k2YLOeosznMD89+AtRrRRn7C+sJmXx90+Eejs1aWWBRi8j
wnC4PNYGZCqV10z+bMWNFWuZlkO2o2c+o8I/mIM93trRgnciFzA2YYAILnz4vZ5L
ZPLKMJmGqSbhxocFUKayXoyeUY1jv/rStYijfdLgcqY4EudstDJjFaLPsKLuTRQW
Ha/+m01i2qnkHvWuai0qaD7qYKMn21SeWfguSl07uH9LiL5wskOUvFzFy1KRL9lb
zjM6gntkGWUI0mO2YFzbbA==',
'19.0.1' => 'uTYzr6YDYDK68A8fZ42IOzQEmilMNmsX4L1bypJkkAN/UOBP5ZK8tc/hgSrmXd4t
g0f7/c39nYo9CfMs8swPYCqTt7mbgx+z/LxlVJXNJeQkYEtLIA+kczQKNprY/c4E
wNk/dR5uVMqmlkAhQVXwJ8M4D30t1wJ8235XRLYtOfxowE+OQLLkYaDNEyU0pJK5
jCKI42xQJyO1bn0FAvGR5zqyWeHSXJKr5fleDw/L8ozPCZIgppGbigvwhOWE+Exs
bsf53THWtncb19OL626j8RbgQ3EuN4uSU7cp1pGqkg/kzQdBxR7TXcOqNMw8tiPC
CsKuFqRI37eQhbI/i2nrjg==',
'19.0.2' => 'MfRJPu59KkLbJSICRyTyvtH4jLV6rwD5cTR3VTF7T5TqSwHLcOKDi7yEZK9qZKTm
qelJt17cUknifzJ+LKXwE26xua5SYA6KKGL1WIcaDKIJm8UMJhE3w7cMcxi79FVn
4Gs1jOc+TxTaM7hD32QzqIqLfYXmfXBjkV6zdNmqEXP8yHErdazU6+FSYbA5Z+JH
+LA46bRlAqZgf0NsfblCCpE5kAFlgMbZ4lpU6SfuNBWvIoX5k4iTtDvflQlpg58S
zY8iDQ+AZ2ttFoQ4MjfyTzM1VM0WDL5f70phVg4F+jG/JPxo9Qpyhq1Hy30ANPa0
dNqXHF6Re45NNc0vwNHjEA==',
'19.0.3' => 'THIS IS AN INVALID SIGNATURE',
'20.0.0' => 't9y2V2n1FSBQmXd1UawMRWvOJU98fEhTkYCBkoILHnJtkXWeobw5rNcsQ5R5oop4
+5Wlxqw520/M+SQF5+MUwpMfB3QJabFJbXm3kFKDbXeNjIcTmrfmcqiVpCGa3qtY
kqAtCh15IPYKR3dSHDH3pOSWlJpnL6rlOYZZSEWYwBIF1BQ9fTjW1i2mat0Fl2OE
CN6Aim4OoaZz4U7coPSi6pyRKcsV2KAaxlHIHzj71EjqV7gaoZRhzSnN/NomPu6D
hfOAOW47o1GsZXt4euDJMpye1h8PE1csaaeCvc0gla7C6KE4LfTO0nLTu/yTNcB9
HNkEiZqVg/EjiuZHcSXd/g==',
'20.0.0RC1' => 'fLz3hBBrPXlJIbDkzAme++HDLayz/ES0bjb42ZtSur+9+UrwyVQdfNsVKQXYtZTr
akGbqmefYpQfVITDRz4Z5weMp/ti7mC9pQR7gjtCN9QmrG/34iURuJlJB8Ty6Mrk
Y6d4C8HjQ4CW2lvEc1/PvxZRI8SfMx+KQFuquJUKNmGInG6DOjtiCQ/mqcSaFt/9
JNJS7NBPkNM7QFJHeedEBdL2Q8Ssu9DdM464tNa0jBo6xrkvinwZLUkULFKeNNIP
SIEb7x+TVLDhCgNSLU1J9qPmscaAGZaYZK6UH3wM51T7pBaL69gIP8AP3HgTzgLK
RO/f0KLHqrWDl86w/ybMpg==',
'20.0.1' => 'EDf7pKNahd9y/R6Gz69/i6DL/JcWddlqAvpibgvkKbBCX0cr5Oa7PZQzA26U2Ad8
fdLgOjnBIKhSo10gn1D8/5pHjuIltFqUepdKFyvx3G/+1znA+S8VfKbDgItwHf1D
gaTtuEcHuONoX7yyCib5kr6YQX4Cx0ICJmrS+KBB0CHzGwAo8Cj98havcDo8vYr0
wR+I/lo/45qAgfUrcNge2ZJdtlZj6VHn/I9zRhz2Q+MDNMRrKxZ/XN/8KhdDerTS
54QfoEoYbVRlLfjc45meKENdxM6/HK8XZckXkJI1Zc6XyubWsSYwnNWY1/GV5Zhd
mxDr4jUlvGQX1jFuzd4nLQ==',
'20.0.8' => 'i49DH/MdR7skQ3wq4+iU/YJOhAqRwEygGQqiku66FXrJ5PDLdb8mWcKeYPkntPTb
jzi9gzOLWWQaqGYVXm4uqgYedU7gaUawP73aCZqUTSw+so/ktP4VNomWxDbOP+T1
qERrTdOOvC1N5wdqchvRIriMgtYIQU3ezwHGECw/+Ps7r4UJLMfBFn56NVqluUkh
yxgneDwLXdjHXIQ4DVsO4pKHkJmRyEzSvqDn0IxHvtELnV6qNAGjFghH5FC3Tnqu
Gm/nFuznVsq84U0YQKVVAA+ci0yXLB6cQCRwz5H3Qfn45v6ZivL7xjrk9aX423yK
CN45I0sFnm6aWxGPN1GegA==',
'21.0.0' => 'Jd0TVUqo3cVPKWaN7/5dEbIcJKcS9Z0V334sBaJAfXe+YgV6lRmPHrGuTK34+tUc
l9/OHJrazOO7tEDRdtM4zpFE7y9kHsqTTR2MTEVLwIZ5JsgTPHE2/im0wlWLk3UI
4Oo5TPt1EOgNbYHfEBY8FPKCL6JyT2ySBaQKYdfJMgkxUZw00BMjplhNUgXEhvd2
f8tPNZ3cs7hyr0SQFx96gyFcCQ4+5KohnLGqZnNSyV7ovHbYEzXUCmmULlm02zSu
V367c1FDks7iKs/V96u21NuF14IQzI0mEzMzysPINrKbHC+OU1BKHKOHqRJjkHwQ
fNJsXi16UkYMGUXyQWQXHg==',
'24.0.1' => 'AGjMXl1X1hRSZv++TOhoS5GzQ5LovzG6uCESqEVgSR+Xd+l82lCUNvJ6saGYp8xk
wL3OnDOnNVcT11xV9Xybt9JLU9tmXf3hf4/HNeyufWKr6AgUENrG7p4dx+tzLk5v
fYtOdoqnhyNNLkrshWcEd7COiaK73O4IlivdOyEZkp/L16RlK5wcs4wAy+M/ot6G
vodwhgcFEbTxA4rRgPQgAk1jw/IKOBb5mMqR0DwZXZGDrnt32+++fTVhMBIVe1hA
XvRlbXYM64mwjbLN0jsrj4yXvaq081NRcHgDUnx7crmgZPVcPs94FFx/sJ9m0y0W
S43iWfUbYsoYueTboeC29A==',
'24.0.2' => 'GskL4HWBPnCHgd0G/+Tk1gLtZLODgxrRbGa11BQxfpXHyn9OD88p2ul2rhQv3HDE
el2QsNc3lzIvuecR+q2tgsjbQR89eK+46SbhogrcUky0HV94gCa6B9Nvenh8/kX8
PRba/ewR1i6CvBVaXJ2xFc9haS1Dv6buBYm4w17F+cnLNvHnWcMVzNPuxRS8dWIw
Oy/tVM+rghS6KYpCXOkyw9jpf5nWR7PdaCITslmhDDDjPXr6fJz31tG1JDQuP4Qt
sEqM0y0G2jhBWLbptxP0ntVjJfITRAoCkpyJTgX4b9oem47YNotSD7yBGqbZWcuN
5+D+D9LWH+PgwZOANwlRZw==',
'24.0.3' => 'THIS IS AN INVALID SIGNATURE',
'25.0.0rc1' => 'AakoZm/DZWLUR33BmXolD7tAGCx5eGvGxYRMPKW5JfTqWo5z+oOz5v00+ERVZeg0
BHwWbiDyr3gZWf6orK6ZK2kNNmnM3v6T1r1ffBAnHlWpJ8+fIoczK+6mrFDTAx0z
7njXkXOcXxnr0yxG5L1cnRlnRxZju4OjmOu9cHfi7RAc9d7gwpLvWE3nkK5jovMG
yzIrcPoCRBuBve4ltzN3DSCa3+r4C+/9uLrvGc1hSzE5WMCdwear4Lt2Eryauaim
UOrN0ZRLcZDJjiV/N7abGYLBhuspeNSs4d7s/M/ofv1mQ0nRzU4QDYZ8URKey2i5
B2+18hf/XUUd4L+LVkrtUw==',
'25.0.0' => 'Bcs9lgaMcvg8tRVikUE6/BMyrXWKdeG7dyKZ36ctznJ9jYaijAKcAlGwQnmgAyX3
UM/6EwponQgBJokt1zHOZjAWsvT56DTLlYpQxGTPcBZSoJacLwgoEMTIhJ7CWM+o
OHuT8cN4uDfv/t0+qA+ciUC7ju0dw53T0LUjUwGja8GZSkJGl9bR+/tuPHWpv0rD
C054a4ucH7pHBhZvuGKPZL4V+1StuXsP1QwcGMC48CEJOyAx1IfGLhh8J9GM4DgF
5Mp9w1w/nnxIACgkVidZh3/abym57rHuKgFzxGl7Yd3G4EBm8B3bYwuk3128UAB8
11/RHVStB7WEj855c6j9wA==',
'25.0.1' => 'gPMcRBmMW875VGEEtP/qfpjbS8kYwVsW0DwK1DdDBrW+DduIUiwiOM4PVi99pVq2
zJ01A6pWPwx9adIiqnm7tBTgBYtrmS6S4KY4DWp+kx7ndflvO/xIH1B8PyvLuFXM
2FKj5i45YhUv8rTJ3jgmNj6UrfFB0CJTo+slHAPwTWooF4IsZUjTw97MjpnYkLIQ
vyuADZWp4CFxQq5FTmgaFmdRuSbZuHgt9bbUvXE+CzPKS/OxfdxbHbwrBLuyFsbE
wdEGUgjkWJRHIyK8UX/5XccUETH2C82l0cwTVILWMvieBPGtRH8matFpxeJW0jzX
4wgr7yW+fOGMm4OFpbQR5A==',
'25.0.7' => 'dIMJO1TcrQ05IkpSHWsAgj0VOV9PNnvxFrwBzaxgi9nkTZjrlQxeswzrozNRlgOz
YO9QQT+jC4dG5SFu/wKAaF0cmYuAdJx4vz2DNgMKrOfODzXgshLk+vZtdyCtOtlq
hOlAeuPilB9K3Q+b4dVjrcv6op/dSEQBhaXI46QvCuvfB1EKLfUAWbLsxCMbr4Cd
Hsav3i+wHleTOL8F7Qc33gDCVtpgqWlyXJG1omEiD9D/Kj+SMTo+s9iwOwW2b0vw
81qbX21xwzPS1vA18qI0JZnd0sdMRGTYvPZJr/Wn2MuMajcMD+94A9W3ij/BygoE
3uimJONAqLSFY6KEnNuoIQ==',
'26.0.0beta4' => 'lJVljfemugXaSV2Xt8MF9OGaj7VVa24S4GUFM0BABUzWAf0yRRGWcQAyk0Mg6ZBU
GvoprwbVhV3+UUGxdj9pQAFYcMqpfcQPdcPWLxk3lx3xuGj79ItyJXRwdahh+4wa
JnUAl/70n6Gp6c7F86aJxacUl7KeRbCgg4h5KGGw3aRZJb2R/Mm3TbFwsE1OIDMg
R0xZcevYS2lSJzjqY1M1QjfCZO4qq2XMDSEN1G58HMd2+QDWqa63QG7/wCF1ufLw
V9tAyTb/fjsj/oUM0wa2bePC3wk/ai8RjWXbZNX2g5YQAxgkCQjfdvb5RNVvmuCL
AJBwc9EthsEKMVkKj9R41g==',
'26.0.0' => 'C7bAPCDo+ZrmIKkxXeJmInOINo2RI0zqxBmNk5bcMjXviPjPeE8SrHbhDcPLHMsp
wUM//AMxbYUKtKBHPYqvw28O5kQhDe9gyCGo5zyTeFDjpgNgQvxa6TxSyl5O03aD
CehZjUf8mnOyaSkJcmkCqJokl2uCXoB9r7pTZwGEURvjv7UOHe6rmKgJAWBeCb1D
ddE/CLFhszZSGGdZRnhYIR5aRkFxUWXiBqtCVbpPf0VjzYIlBzRXDh4s/8JjsPeW
gsVtwgwxZ3CfzIoVeFeq7LQep+bCstdTvCZjs7GmvS6SgRlDvAOm82EBGY1rVQKD
t87PcaZyrupEfAIfD9uQRA==',
];
if (isset($signatures[$version])) {

View File

@ -1,84 +1,84 @@
Feature: CLI updater
Scenario: No update is available - 20.0.0
Given the current version is 20.0.0
Scenario: No update is available - 25.0.0
Given the current version is 25.0.0
When the CLI updater is run
Then the output should contain "Could not find config.php. Is this file in the "updater" subfolder of Nextcloud?"
Scenario: No update is available - 20.0.0
Given the current installed version is 20.0.0
Scenario: No update is available - 25.0.0
Given the current installed version is 25.0.0
And there is no update available
When the CLI updater is run successfully
Then the installed version should be 20.0.0
Then the installed version should be 25.0.0
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available - 20.0.0 to 20.0.1
Given the current installed version is 20.0.0
And there is an update to version 20.0.1 available
Scenario: Update is available - 25.0.0 to 25.0.1
Given the current installed version is 25.0.0
And there is an update to version 25.0.1 available
When the CLI updater is run successfully
Then the installed version should be 20.0.1
Then the installed version should be 25.0.1
And maintenance mode should be off
And upgrade is not required
Scenario: Invalid update is available - 20.0.0 to 20.0.503
Given the current installed version is 20.0.0
And there is an update to version 20.0.503 available
Scenario: Invalid update is available - 25.0.0 to 25.0.503
Given the current installed version is 25.0.0
And there is an update to version 25.0.503 available
When the CLI updater is run
Then the return code should not be 0
And the output should contain "Download failed - Not Found (HTTP 404)"
And the installed version should be 20.0.0
And the installed version should be 25.0.0
And maintenance mode should be off
And upgrade is not required
Scenario: Update without valid signature is being offered - 19.0.0 to 19.0.3
Given the current installed version is 19.0.0
# This works because 19.0.3 is in the signature list with an invalid signature
And there is an update to version 19.0.3 available
Scenario: Update without valid signature is being offered - 24.0.0 to 24.0.3
Given the current installed version is 24.0.0
# This works because 24.0.3 is in the signature list with an invalid signature
And there is an update to version 24.0.3 available
When the CLI updater is run
Then the return code should not be 0
And the output should contain "Signature of update is not valid"
And the installed version should be 19.0.0
And the installed version should be 24.0.0
And maintenance mode should be off
And upgrade is not required
Scenario: Update to older version - 20.0.0 to 19.0.2
Given the current installed version is 20.0.0
And there is an update to version 19.0.2 available
Scenario: Update to older version - 25.0.0 to 24.0.2
Given the current installed version is 25.0.0
And there is an update to version 24.0.2 available
When the CLI updater is run
Then the return code should not be 0
And the output should contain "Downloaded version is lower than installed version"
And the installed version should be 20.0.0
And the installed version should be 25.0.0
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available but autoupdate is disabled - 20.0.0 to 20.0.1
Given the current installed version is 20.0.0
Scenario: Update is available but autoupdate is disabled - 25.0.0 to 25.0.1
Given the current installed version is 25.0.0
And the autoupdater is disabled
And there is an update to version 20.0.1 available
And there is an update to version 25.0.1 available
When the CLI updater is run
Then the installed version should be 20.0.0
Then the installed version should be 25.0.0
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available and apps2 folder is there and configured - 20.0.0 to 20.0.1
Given the current installed version is 20.0.0
And there is an update to version 20.0.1 available
Scenario: Update is available and apps2 folder is there and configured - 25.0.0 to 25.0.1
Given the current installed version is 25.0.0
And there is an update to version 25.0.1 available
And there is a folder called "apps2"
And there is a config for a secondary apps directory called "apps2"
When the CLI updater is run successfully
Then the installed version should be 20.0.1
Then the installed version should be 25.0.1
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available and apps2 folder is there and not configured - 20.0.0 to 20.0.1
Given the current installed version is 20.0.0
And there is an update to version 20.0.1 available
Scenario: Update is available and apps2 folder is there and not configured - 25.0.0 to 25.0.1
Given the current installed version is 25.0.0
And there is an update to version 25.0.1 available
And there is a folder called "apps2"
When the CLI updater is run
Then the return code should not be 0
And the output should contain "The following extra files have been found"
And the output should contain "apps2"
And the installed version should be 20.0.0
And the installed version should be 25.0.0
And maintenance mode should be off
And upgrade is not required

View File

@ -7,6 +7,6 @@ Feature: CLI updater - master base
And the version number is decreased in the config.php to enforce upgrade
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 26.0
Then the installed version should be 28.0
And maintenance mode should be off
And upgrade is not required

View File

@ -1,32 +0,0 @@
Feature: CLI updater - stable19 base
Scenario: Update is available - 19.0.0 beta 3 to 19.0.0 beta 4
Given the current installed version is 19.0.0beta3
And there is an update to prerelease version "19.0.0beta4" available
And the version number is decreased in the config.php to enforce upgrade
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 19.0
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available - 19.0.0 to 19.0.1
Given the current installed version is 19.0.0
And there is an update to version 19.0.1 available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 19.0.1
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available - 19.0.1 to 20.0.0
Given the current installed version is 19.0.1
And PHP is at least in version 7.2
And the current channel is "beta"
And there is an update to version 20.0.0 available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 20.0.0
And maintenance mode should be off
And upgrade is not required

View File

@ -1,10 +0,0 @@
Feature: CLI updater - stable21 base
Scenario: Update is available - 21.0.0 RC 1 to 21.0.0
Given the current installed version is 21.0.0RC1
And there is an update to version 21.0.0 available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 21.0
And maintenance mode should be off
And upgrade is not required

View File

@ -0,0 +1,22 @@
Feature: CLI updater - stable24 base
Scenario: Update is available - 24.0.0 to 24.0.1
Given the current installed version is 24.0.0
And there is an update to version 24.0.1 available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 24.0.1
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available - 24.0.1 to 25.0.0
Given the current installed version is 24.0.1
And PHP is at least in version 8.0
And the current channel is "beta"
And there is an update to version 25.0.0 available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 25.0.0
And maintenance mode should be off
And upgrade is not required

View File

@ -1,54 +1,54 @@
Feature: CLI updater - stable20 base
Feature: CLI updater - stable25 base
Scenario: Update is available - 20.0.0 beta 1 to 20.0.0 RC 1
Given the current installed version is 20.0.0beta1
And there is an update to prerelease version "20.0.0RC1" available
Scenario: Update is available - 25.0.0 beta 1 to 25.0.0 rc 1
Given the current installed version is 25.0.0beta1
And there is an update to prerelease version "25.0.0rc1" available
And the version number is decreased in the config.php to enforce upgrade
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 20.0
Then the installed version should be 25.0
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available but unexpected folder found - 20.0.7 to 20.0.8
Given the current installed version is 20.0.7
And there is an update to version 20.0.8 available
Scenario: Update is available but unexpected folder found - 25.0.6 to 25.0.7
Given the current installed version is 25.0.6
And there is an update to version 25.0.7 available
And there is a folder called "test123"
When the CLI updater is run
Then the return code should not be 0
And the output should contain "The following extra files have been found"
Then the installed version should be 20.0.7
Then the installed version should be 25.0.6
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available and .well-known folder exist - 20.0.7 to 20.0.8
Given the current installed version is 20.0.7
And there is an update to version 20.0.8 available
Scenario: Update is available and .well-known folder exist - 25.0.6 to 25.0.7
Given the current installed version is 25.0.6
And there is an update to version 25.0.7 available
And there is a folder called ".well-known"
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 20.0.8
Then the installed version should be 25.0.7
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available and .rnd file exist - 20.0.7 to 20.0.10
Given the current installed version is 20.0.7
And there is an update to version 20.0.8 available
Scenario: Update is available and .rnd file exist - 25.0.6 to 25.0.7
Given the current installed version is 25.0.6
And there is an update to version 25.0.7 available
And there is a folder called ".rnd"
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 20.0.8
Then the installed version should be 25.0.7
And maintenance mode should be off
And upgrade is not required
Scenario: Update is available - 20.0.0 to beta
Given the current installed version is 20.0.0RC1
And PHP is at least in version 7.3
Scenario: Update is available - 25.0.0 to beta
Given the current installed version is 25.0.0rc1
And PHP is at least in version 8.0
And the current channel is "beta"
And there is an update to version 21.0.0 available
And there is an update to version 26.0.0 available
When the CLI updater is run successfully
And the output should contain "Update successful"
Then the installed version should be 21.0
Then the installed version should be 26.0
And maintenance mode should be off
And upgrade is not required

Binary file not shown.

View File

@ -0,0 +1,10 @@
{
"require-dev": {
"humbug/box": "^3.16"
},
"config": {
"platform": {
"php": "7.4"
}
}
}

3632
vendor-bin/box/composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,10 @@
{
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.27"
},
"config": {
"platform": {
"php": "7.4"
},
"sort-packages": true
},
"require-dev": {
"nextcloud/coding-standard": "^1.0",
"vimeo/psalm": "^4.26"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
{
"require": {
"vimeo/psalm": "^4.6"
},
"config": {
"platform": {
"php": "7.4"
},
"allow-plugins": {
"composer/package-versions-deprecated": true
}
}
}

2247
vendor-bin/psalm/composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

2
vendor/autoload.php vendored
View File

@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitd7f5ddc48e4715174279359c16c76340::getLoader();
return ComposerAutoloaderInitba7c5c8f0885d00c3b669d0399f96c80::getLoader();

View File

@ -0,0 +1,35 @@
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"

View File

@ -0,0 +1,80 @@
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"

View File

@ -0,0 +1,61 @@
#!/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

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="composer-normalize" version="^2.28.3" installed="2.28.3" location="./tools/composer-normalize" copy="false"/>
<phar name="infection" version="^0.26.13" installed="0.26.13" location="./tools/infection" copy="false"/>
<phar name="php-cs-fixer" version="^3.8.0" installed="3.8.0" location="./tools/php-cs-fixer" copy="false"/>
</phive>

View File

@ -0,0 +1,17 @@
<?php declare(strict_types=1);
$finder = PhpCsFixer\Finder::create()
->files()
->in(['src', 'tests']);
$config = new PhpCsFixer\Config();
return $config
->setRules([
'@PSR12' => true,
'strict_param' => true,
'array_syntax' => ['syntax' => 'short'],
'no_unused_imports' => true,
])
->setRiskyAllowed(true)
->setFinder($finder);

View File

@ -0,0 +1,121 @@
# See https://tech.davis-hansson.com/p/make/
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# General variables
TOUCH = bash .makefile/touch.sh
# PHP variables
COMPOSER=composer
COVERAGE_DIR = dist/coverage
INFECTION_BIN = tools/infection
INFECTION = php -d zend.enable_gc=0 $(INFECTION_BIN) --skip-initial-tests --coverage=$(COVERAGE_DIR) --only-covered --threads=4 --min-msi=100 --min-covered-msi=100 --ansi
PHPUNIT_BIN = vendor/bin/phpunit
PHPUNIT = php -d zend.enable_gc=0 $(PHPUNIT_BIN)
PHPUNIT_COVERAGE = XDEBUG_MODE=coverage $(PHPUNIT) --group default --coverage-xml=$(COVERAGE_DIR)/coverage-xml --log-junit=$(COVERAGE_DIR)/phpunit.junit.xml
PHPSTAN_BIN = vendor/bin/phpstan
PHPSTAN = $(PHPSTAN_BIN) analyse --level=5 src tests
PHP_CS_FIXER_BIN = tools/php-cs-fixer
PHP_CS_FIXER = $(PHP_CS_FIXER_BIN) fix --ansi --verbose --config=.php-cs-fixer.php
COMPOSER_NORMALIZE_BIN=tools/composer-normalize
COMPOSER_NORMALIZE = ./$(COMPOSER_NORMALIZE_BIN)
.DEFAULT_GOAL := default
#
# Command
#---------------------------------------------------------------------------
.PHONY: help
help: ## Shows the help
help:
@printf "\033[33mUsage:\033[0m\n make TARGET\n\n\033[32m#\n# Commands\n#---------------------------------------------------------------------------\033[0m\n"
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//' | awk 'BEGIN {FS = ":"}; {printf "\033[33m%s:\033[0m%s\n", $$1, $$2}'
.PHONY: default
default: ## Runs the default task: CS fix and all the tests
default: cs test
.PHONY: cs
cs: ## Runs PHP-CS-Fixer
cs: $(PHP_CS_FIXER_BIN) $(COMPOSER_NORMALIZE_BIN)
$(PHP_CS_FIXER)
$(COMPOSER_NORMALIZE)
.PHONY: phpstan
phpstan: ## Runs PHPStan
phpstan:
$(PHPSTAN)
.PHONY: infection
infection: ## Runs infection
infection: $(INFECTION_BIN) $(COVERAGE_DIR) vendor
if [ -d $(COVERAGE_DIR)/coverage-xml ]; then $(INFECTION); fi
.PHONY: test
test: ## Runs all the tests
test: validate-package phpstan $(COVERAGE_DIR) e2e #infection include infection later
.PHONY: validate-package
validate-package: ## Validates the Composer package
validate-package: vendor
$(COMPOSER) validate --strict
.PHONY: coverage
coverage: ## Runs PHPUnit with code coverage
coverage: $(PHPUNIT_BIN) vendor
$(PHPUNIT_COVERAGE)
.PHONY: unit-test
unit-test: ## Runs PHPUnit (default group)
unit-test: $(PHPUNIT_BIN) vendor
$(PHPUNIT) --group default
.PHONY: e2e
e2e: ## Runs PHPUnit end-to-end tests
e2e: $(PHPUNIT_BIN) vendor
$(PHPUNIT) --group e2e
#
# Rules
#---------------------------------------------------------------------------
# Vendor does not depend on the composer.lock since the later is not tracked
# or committed.
vendor: composer.json
$(COMPOSER) update
$(TOUCH) "$@"
$(PHPUNIT_BIN): vendor
$(TOUCH) "$@"
$(INFECTION_BIN): ./.phive/phars.xml
phive install infection
$(TOUCH) "$@"
$(COMPOSER_NORMALIZE_BIN): ./.phive/phars.xml
phive install composer-normalize
$(TOUCH) "$@"
$(COVERAGE_DIR): $(PHPUNIT_BIN) src tests phpunit.xml.dist
$(PHPUNIT_COVERAGE)
$(TOUCH) "$@"
$(PHP_CS_FIXER_BIN): vendor
phive install php-cs-fixer
$(TOUCH) "$@"
$(PHPSTAN_BIN): vendor
$(TOUCH) "$@"

View File

@ -1,239 +1,248 @@
# Composer bin plugin — Isolate your bin dependencies
[![Package version](http://img.shields.io/packagist/v/bamarni/composer-bin-plugin.svg?style=flat-square)](https://packagist.org/packages/bamarni/composer-bin-plugin)
[![Build Status](https://img.shields.io/travis/bamarni/composer-bin-plugin.svg?branch=master&style=flat-square)](https://travis-ci.org/bamarni/composer-bin-plugin?branch=master)
[![License](https://img.shields.io/badge/license-MIT-red.svg?style=flat-square)](LICENSE)
## Table of Contents
1. [Why?](#why)
1. [How does this plugin work?](#how-does-this-plugin-work)
1. [Why? A hard problem with a simple solution.](#why-a-hard-problem-with-a-simple-solution)
1. [Usage; How does this plugin work?](#usage-how-does-this-plugin-work)
1. [Installation](#installation)
1. [Usage](#usage)
1. [Example](#example)
1. [The `all` bin namespace](#the-all-bin-namespace)
1. [What happens when symlink conflicts?](#what-happens-when-symlink-conflicts)
1. [Tips](#tips)
1. [Configuration](#configuration)
1. [Bin links (`bin-links`)](#bin-links-bin-links)
1. [Target directory (`target-directory`)](#target-directory-target-directory)
1. [Forward command (`forward-command`)](#forward-command-forward-command)
1. [Tips & Tricks](#tips--tricks)
1. [Auto-installation](#auto-installation)
1. [Disable links](#disable-links)
1. [Change directory](#change-directory)
1. [Forward mode](#forward-mode)
1. [Reduce clutter](#reduce-clutter)
1. [GitHub Actions integration](#github-actions-integration)
1. [Related plugins](#related-plugins)
1. [Backward Compatibility Promise](#backward-compatibility-promise)
1. [Contributing](#contributing)
## Why?
## Why? A hard problem with a simple solution.
In PHP, with Composer, your dependencies are flattened, which might result in conflicts. Most of the time those
conflicts are legitimate and should be properly resolved. However you may have dev tools that you want to manage
via Composer for convenience, but should not influence your project dependencies or for which conflicts don't make
sense. For example: [EtsyPhan][1] and [PhpMetrics][2]. Installing one of those static analysis tools should not change
your application dependencies, neither should it be a problem to install both of them at the same time.
When managing your dependencies with [Composer][composer], your dependencies are
flattened with compatible versions, or when not possible, result in conflict
errors.
There is cases however when adding a tool as a dependency, for example [PHPStan][phpstan]*
or [Rector][rector] could have undesired effects due to the dependencies they
are bringing. For example if phpstan depends on `nikic/php-parser` 4.x and rector
3.x, you cannot install both tools at the same time (despite the fact that from
a usage perspective, they do not need to be compatible). Another example, maybe
you can no longer add a non-dev dependency because a dependency brought by PHPStan
is not compatible with it.
There is nothing special or exceptional about this problem: this is how dependencies
work in PHP with Composer. It is however annoying in the case highlighted above,
because the conflicts should not be: it is a limitation of Composer because it
cannot infer how you are using each dependency.
One way to solve the problem above, is to install those dependencies in a
different `composer.json` file. It comes with its caveats, for example if you
were to do that with [PHPUnit][phpunit], you may find yourself in the scenario
where PHPUnit will not be able to execute your tests because your code is not
compatible with it and Composer is not able to tell since the PHPUnit dependency
sits alone in its own `composer.json`. It is the very problem Composer aim to
solve. As a rule of thumb, **you should limit this approach to tools which do not
autoload your code.**
However, managing several `composer.json` kind be a bit annoying. This plugin
aims at helping you doing this.
## How does this plugin work?
*: You will in practice not have this problem with PHPStan as the Composer package
`phpstan/phpstan` is shipping a scoped PHAR (scoped via [PHP-Scoper][php-scoper])
which provides not only a package with no dependencies but as well that has no
risk of conflicting/crash when autoloading your code.
It allows you to install your *bin vendors* in isolated locations, and still link them to your
[bin-dir][3] (if you want to).
This is done by registering a `bin` command, which can be used to run Composer commands inside a namespace.
## Usage; How does this plugin work?
This plugin registers a `bin <bin-namespace-name>` command that allows you to
interact with the `vendor-bin/<bin-namespace-name>/composer.json`* file.
For example:
```bash
$ composer bin php-cs-fixer require --dev friendsofphp/php-cs-fixer
# Equivalent to manually doing:
$ mkdir vendor-bin/php-cs-fixer
$ cd vendor-bin/php-cs-fixer && composer require --dev friendsofphp/php-cs-fixer
```
You also have a special `all` namespace to interact with all the bin namespaces:
```bash
# Runs "composer update" for each bin namespace
$ composer bin all update
```
## Installation
# Globally
$ composer global require bamarni/composer-bin-plugin
# In your project
$ composer require --dev bamarni/composer-bin-plugin
## Usage
$ composer bin [namespace] [composer_command]
$ composer global bin [namespace] [composer_command]
### Example
Let's install [Behat][4] and [PhpSpec][5] inside a `bdd` bin namespace, [EtsyPhan][1] in `etsy-phan` and [PhpMetrics][2]
in `phpmetrics`:
$ composer bin bdd require behat/behat phpspec/phpspec
$ composer bin etsy-phan require etsy/phan
$ composer bin phpmetrics require phpmetrics/phpmetrics
This command creates the following directory structure :
.
├── composer.json
├── composer.lock
├── vendor/
│   └── bin
│ ├── behat -> ../../vendor-bin/bdd/vendor/behat/behat/bin/behat
│ ├── phpspec -> ../../vendor-bin/bdd/vendor/phpspec/phpspec/bin/phpspec
│ ├── phan -> ../../vendor-bin/etsy-phan/vendor/etsy/phan/phan
│ └── phpmetrics -> ../../vendor-bin/phpmetrics/vendor/phpmetrics/phpmetrics/bin/phpmetrics
└── vendor-bin/
└── bdd
│ ├── composer.json
│ ├── composer.lock
│ └── vendor/
│ ├── behat/
│ ├── phpspec/
│ └── ...
└── etsy-phan
│ ├── composer.json
│ ├── composer.lock
│ └── vendor/
│ ├── etsy/
│ └── ...
└── phpmetrics
├── composer.json
├── composer.lock
└── vendor/
├── phpmetrics/
└── ...
You can continue to run `vendor/bin/behat`, `vendor/bin/phpspec` and co. as before but they will be properly isolated.
Also, `composer.json` and `composer.lock` files in each namespace will allow you to take advantage of automated dependency
management as normally provided by Composer.
### The `all` bin namespace
The `all` bin namespace has a special meaning. It runs a command for all existing bin namespaces. For instance, the
following command would update all your bins :
$ composer bin all update
Changed current directory to vendor-bin/phpspec
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
Changed current directory to vendor-bin/phpunit
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
### What happens when symlink conflicts?
If we take the case described in the [example section](#example), there might be more binaries linked due to
the dependencies. For example [PhpMetrics][2] depends on [Nikic PHP-Parser][6] and as such you will also have `php-parse`
in `.vendor/bin/`:
.
├── composer.json
├── composer.lock
├── vendor/
│   └── bin
│ ├── phpmetrics -> ../../vendor-bin/phpmetrics/vendor/phpmetrics/phpmetrics/bin/phpmetrics
│ └── php-parse -> ../../vendor-bin/phpmetrics/vendor/nikic/PHP-Parser/bin/php-parsee
└── vendor-bin/
└── phpmetrics
├── composer.json
├── composer.lock
└── vendor/
├── phpmetrics/
├── nikic/
└── ...
But what happens if another bin-namespace has a dependency using [Nikic PHP-Parser][6]? In that situation symlinks would
collides and are not created (only the colliding ones).
## Tips
### Auto-installation
For convenience, you can add the following script in your `composer.json` :
```json
{
"scripts": {
"bin": "echo 'bin not installed'",
"post-install-cmd": ["@composer bin all install --ansi"],
"post-update-cmd": ["@composer bin all update --ansi"]
}
}
```bash
$ composer require --dev bamarni/composer-bin-plugin
```
This makes sure all your bins are installed during `composer install` and updated during `composer update`.
### Disable links
By default, binaries of the sub namespaces are linked to the root one like described in [example](#example). If you
wish to disable that behaviour, you can do so by adding a little setting in the extra config:
```json
{
"extra": {
"bamarni-bin": {
"bin-links": false
}
}
}
```
### Change directory
By default, the packages are looked for in the `vendor-bin` directory. The location can be changed using `target-directory` value in the extra config:
```json
{
"extra": {
"bamarni-bin": {
"target-directory": "ci/vendor"
}
}
}
```
### Forward mode
There is a `forward mode` which is disabled by default. This can be activated by using the `forward-command` value in the extra config.
## Configuration
```json
{
...
"extra": {
"bamarni-bin": {
"bin-links": false,
"target-directory": "vendor-bin",
"forward-command": true
}
}
}
```
If this mode is activated, all your `composer install` and `composer update` commands are forwared to all bin directories.
This is an replacement for the tasks shown in section [Auto-installation](#auto-installation).
### Bin links (`bin-links`)
In 1.x: enabled by default.
In 2.x: disabled by default.
When installing a Composer package, Composer may add "bin links" to a bin
directory. For example, by default when installing `phpunit/phpunit`, it will
add a symlink `vendor/bin/phpunit` pointing to the PHPUnit script somewhere in
`vendor/phpunit/phpunit`.
In 1.x, BamarniBinPlugin behaves the same way for "bin dependencies", i.e. when
executing `composer bin php-cs-fixer require --dev friendsofphp/php-cs-fixer`,
it will add a bin link `vendor/bin/php-cs-fixer -> vendor-bin/php-cs-fixer/vendor/friendsofphp/php-cs-fixer`.
This is however a bit tricky and cannot provide consistent behaviour. For example
when installing several packages with the same bin, (e.g. with the case above installing
another tool that uses PHP-CS-Fixer as a dependency in another bin namespace),
the symlink may or may not be overridden, or not created at all. Since it is not
possible to control this behaviour, neither provide an intuitive or deterministic
approach, it is recommended to set this setting to `false` which will be the
default in 2.x.
It does mean that instead of using `vendor/bin/php-cs-fixer` you will have to
use `vendor-bin/php-cs-fixer/vendor/friendsofphp/php-cs-fixer/path/tophp-cs-fixer`
(in which case setting an alias via a Composer script or something is recommended).
### Target directory (`target-directory`)
Defaults to `vendor-bin`, can be overridden to anything you wish.
### Forward command (`forward-command`)
Disabled by default in 1.x, will be enabled by default in 2.x. If this mode is
enabled, all your `composer install` and `composer update` commands are forwarded
to _all_ bin directories.
This is a replacement for the tasks shown in section [Auto-installation](#auto-installation).
## Tips & Tricks
### Auto-installation
You can easily forward a command upon a `composer install` to forward the install
to all (in which case having `extra.bamarni-bin.forward_command = true` is more
adapted) or a specific of bin namespace:
```json
{
"scripts": {
"bin": "echo 'bin not installed'",
"post-install-cmd": ["@composer bin php-cs-fixer install --ansi"]
}
}
```
You can customise this as you wish leveraging the [Composer script events][composer-script-events]).
### Reduce clutter
You can add following line to your `.gitignore` file in order to avoid committing dependencies of your tools.
You can add the following line to your `.gitignore` file in order to avoid
committing dependencies of your tools.
```.gitignore
/vendor-bin/**/vendor
# .gitignore
/vendor-bin/**/vendor/
```
Updating each tool can create many not legible changes in `composer.lock` files. You can use `.gitattributes` file in order
to inform git that it shouldn't show diffs of `composer.lock` files.
Updating each tool can create many not legible changes in `composer.lock` files.
You can use a `.gitattributes` file in order to inform git that it shouldn't show
diffs of `composer.lock` files.
```.gitattributes
vendor-bin/**/composer.lock binary
# .gitattributes
/vendor-bin/**/composer.lock binary
```
### GitHub Actions integration
There is currently no way to leverage `ramsey/composer-install` to install all
namespace bins. However it is unlikely you need this in the CI and not locally,
in which case [forwarding the command](#forward-command-forward-command) should
be good enough.
If you still need to install specific bin namespaces, you can do it by setting
the `working-directory`:
```yaml
#...
- name: "Install PHP-CS-Fixer Composer dependencies"
uses: "ramsey/composer-install@v2"
with:
working-directory: "vendor-bin/php-cs-fixer"
```
## Related plugins
* [theofidry/composer-inheritance-plugin][7]: Opinionated version of [Wikimedia composer-merge-plugin][8] to work in pair with this plugin.
* [theofidry/composer-inheritance-plugin][theofidry-composer-inheritance-plugin]: Opinionated version
of [Wikimedia composer-merge-plugin][wikimedia-composer-merge-plugin] to work in pair with this plugin.
[1]: https://github.com/etsy/phan
[2]: https://github.com/phpmetrics/PhpMetrics
[3]: https://getcomposer.org/doc/06-config.md#bin-dir
[4]: http://behat.org
[5]: http://phpspec.net
[6]: https://github.com/nikic/PHP-Parser
[7]: https://github.com/theofidry/composer-inheritance-plugin
[8]: https://github.com/wikimedia/composer-merge-plugin
## Backward Compatibility Promise
The backward compatibility promise only applies to the following API:
- The commands registered by the plugin
- The behaviour of the commands (but not their logging/output)
- The Composer configuration
The plugin implementation is considered to be strictly internal and its code may
change at any time in a non back-ward compatible way.
## Contributing
A makefile is available to help out:
```bash
$ make # Runs all checks
$ make help # List all available commands
```
**Note:** you do need to install [phive][phive] first.
[composer]: https://getcomposer.org
[composer-script-events]: https://getcomposer.org/doc/articles/scripts.md#command-events
[phive]: https://phar.io/
[php-scoper]: https://github.com/humbug/php-scoper
[phpstan]: https://phpstan.org/
[phpunit]: https://github.com/sebastianbergmann/phpunit
[rector]: https://github.com/rectorphp/rector
[symfony-bc-policy]: https://symfony.com/doc/current/contributing/code/bc.html
[theofidry-composer-inheritance-plugin]: https://github.com/theofidry/composer-inheritance-plugin
[wikimedia-composer-merge-plugin]: https://github.com/wikimedia/composer-merge-plugin

View File

@ -1,7 +1,8 @@
{
"name": "bamarni/composer-bin-plugin",
"type": "composer-plugin",
"description": "No conflicts for your bin dependencies",
"license": "MIT",
"type": "composer-plugin",
"keywords": [
"composer",
"dependency",
@ -10,20 +11,20 @@
"conflict",
"executable"
],
"license": "MIT",
"require": {
"php": "^5.5.9 || ^7.0 || ^8.0",
"composer-plugin-api": "^1.0 || ^2.0"
"php": "^7.2.5 || ^8.0",
"composer-plugin-api": "^2.0"
},
"require-dev": {
"composer/composer": "^1.0 || ^2.0",
"symfony/console": "^2.5 || ^3.0 || ^4.0"
},
"config": {
"sort-packages": true
},
"extra": {
"class": "Bamarni\\Composer\\Bin\\Plugin"
"ext-json": "*",
"composer/composer": "^2.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
"phpunit/phpunit": "^8.5 || ^9.5",
"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"
},
"autoload": {
"psr-4": {
@ -35,12 +36,15 @@
"Bamarni\\Composer\\Bin\\Tests\\": "tests"
}
},
"scripts": {
"post-install-cmd": [
"@composer bin phpunit install"
],
"post-update-cmd": [
"@post-install-cmd"
]
"config": {
"allow-plugins": {
"phpstan/extension-installer": true,
"ergebnis/composer-normalize": true,
"infection/extension-installer": true
},
"sort-packages": true
},
"extra": {
"class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin"
}
}

View File

@ -0,0 +1,14 @@
{
"$schema": "vendor/infection/infection/resources/schema.json",
"source": {
"directories": [
"src"
]
},
"logs": {
"text": "dist/infection.txt"
},
"mutators": {
"@default": true
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin\ApplicationFactory;
use Composer\Console\Application;
final class FreshInstanceApplicationFactory implements NamespaceApplicationFactory
{
public function create(Application $existingApplication): Application
{
return new Application();
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin\ApplicationFactory;
use Composer\Console\Application;
interface NamespaceApplicationFactory
{
public function create(Application $existingApplication): Application;
}

View File

@ -0,0 +1,184 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin;
use Bamarni\Composer\Bin\Command\BinCommand;
use Bamarni\Composer\Bin\CommandProvider as BamarniCommandProvider;
use Bamarni\Composer\Bin\Config\Config;
use Bamarni\Composer\Bin\Input\BinInputFactory;
use Composer\Composer;
use Composer\Console\Application;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\IO\ConsoleIO;
use Composer\IO\IOInterface;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Plugin\PluginInterface;
use Composer\Plugin\Capable;
use Composer\Script\Event;
use Composer\Script\ScriptEvents;
use Symfony\Component\Console\Command\Command;
use Composer\Plugin\Capability\CommandProvider as ComposerPluginCommandProvider;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
use function count;
use function in_array;
use function sprintf;
/**
* @final Will be final in 2.x.
*/
class BamarniBinPlugin implements PluginInterface, Capable, EventSubscriberInterface
{
private const FORWARDED_COMMANDS = ['install', 'update'];
/**
* @var Composer
*/
protected $composer;
/**
* @var IOInterface
*/
protected $io;
/**
* @var Logger
*/
private $logger;
private $forwarded = false;
public function activate(Composer $composer, IOInterface $io): void
{
$this->composer = $composer;
$this->io = $io;
$this->logger = new Logger($io);
}
public function getCapabilities(): array
{
return [
ComposerPluginCommandProvider::class => BamarniCommandProvider::class,
];
}
public function deactivate(Composer $composer, IOInterface $io): void
{
}
public function uninstall(Composer $composer, IOInterface $io): void
{
}
public static function getSubscribedEvents(): array
{
return [
PluginEvents::COMMAND => 'onCommandEvent',
ScriptEvents::POST_AUTOLOAD_DUMP => 'onPostAutoloadDump',
];
}
public function onPostAutoloadDump(Event $event): void
{
$this->logger->logDebug('Calling onPostAutoloadDump().');
$eventIO = $event->getIO();
if (!($eventIO instanceof ConsoleIO)) {
return;
}
// This is a bit convoluted but Event does not expose the input unlike
// CommandEvent.
$publicIO = PublicIO::fromConsoleIO($eventIO);
$eventInput = $publicIO->getInput();
$this->onEvent(
$eventInput->getArgument('command'),
$eventInput,
$publicIO->getOutput()
);
}
public function onCommandEvent(CommandEvent $event): bool
{
$this->logger->logDebug('Calling onCommandEvent().');
return $this->onEvent(
$event->getCommandName(),
$event->getInput(),
$event->getOutput()
);
}
private function onEvent(
string $commandName,
InputInterface $input,
OutputInterface $output
): bool {
$config = Config::fromComposer($this->composer);
$deprecations = $config->getDeprecations();
if (count($deprecations) > 0) {
foreach ($deprecations as $deprecation) {
$this->logger->logStandard($deprecation);
}
}
if ($config->isCommandForwarded()
&& in_array($commandName, self::FORWARDED_COMMANDS, true)
) {
return $this->onForwardedCommand($input, $output);
}
return true;
}
protected function onForwardedCommand(
InputInterface $input,
OutputInterface $output
): bool {
if ($this->forwarded) {
$this->logger->logDebug('Command already forwarded within the process: skipping.');
return true;
}
$this->forwarded = true;
$this->logger->logStandard('The command is being forwarded.');
$this->logger->logDebug(
sprintf(
'Original input: <comment>%s</comment>.',
$input->__toString()
)
);
// Note that the input & output of $io should be the same as the event
// input & output.
$io = $this->io;
$application = new Application();
$command = new BinCommand();
$command->setComposer($this->composer);
$command->setApplication($application);
$command->setIO($io);
$forwardedCommandInput = BinInputFactory::createForwardedCommandInput($input);
try {
return Command::SUCCESS === $command->run(
$forwardedCommandInput,
$output
);
} catch (Throwable $throwable) {
return false;
}
}
}

View File

@ -1,186 +0,0 @@
<?php
namespace Bamarni\Composer\Bin;
use Composer\Console\Application as ComposerApplication;
use Composer\Factory;
use Composer\IO\IOInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Command\BaseCommand;
use Composer\Json\JsonFile;
class BinCommand extends BaseCommand
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this
->setName('bin')
->setDescription('Run a command inside a bin namespace')
->setDefinition([
new InputArgument('namespace', InputArgument::REQUIRED),
new InputArgument('args', InputArgument::REQUIRED | InputArgument::IS_ARRAY),
])
->ignoreValidationErrors()
;
}
/**
* {@inheritDoc}
*/
public function execute(InputInterface $input, OutputInterface $output)
{
$config = new Config($this->getComposer());
$this->resetComposers($application = $this->getApplication());
/** @var ComposerApplication $application */
if ($config->binLinksAreEnabled()) {
putenv('COMPOSER_BIN_DIR='.$this->createConfig()->get('bin-dir'));
}
$vendorRoot = $config->getTargetDirectory();
$namespace = $input->getArgument('namespace');
$input = new StringInput(preg_replace(
sprintf('/bin\s+(--ansi\s)?%s(\s.+)/', preg_quote($namespace, '/')),
'$1$2',
(string) $input,
1
));
return ('all' !== $namespace)
? $this->executeInNamespace($application, $vendorRoot.'/'.$namespace, $input, $output)
: $this->executeAllNamespaces($application, $vendorRoot, $input, $output)
;
}
/**
* @param ComposerApplication $application
* @param string $binVendorRoot
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int Exit code
*/
private function executeAllNamespaces(ComposerApplication $application, $binVendorRoot, InputInterface $input, OutputInterface $output)
{
$binRoots = glob($binVendorRoot.'/*', GLOB_ONLYDIR);
if (empty($binRoots)) {
$this->getIO()->writeError('<warning>Couldn\'t find any bin namespace.</warning>');
return 0; // Is a valid scenario: the user may not have setup any bin namespace yet
}
$originalWorkingDir = getcwd();
$exitCode = 0;
foreach ($binRoots as $namespace) {
$output->writeln(
sprintf('Run in namespace <comment>%s</comment>', $namespace),
OutputInterface::VERBOSITY_VERBOSE
);
$exitCode += $this->executeInNamespace($application, $namespace, $input, $output);
chdir($originalWorkingDir);
$this->resetComposers($application);
}
return min($exitCode, 255);
}
/**
* @param ComposerApplication $application
* @param string $namespace
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int Exit code
*/
private function executeInNamespace(ComposerApplication $application, $namespace, InputInterface $input, OutputInterface $output)
{
if (!file_exists($namespace)) {
mkdir($namespace, 0777, true);
}
$this->chdir($namespace);
// some plugins require access to composer file e.g. Symfony Flex
if (!file_exists(Factory::getComposerFile())) {
file_put_contents(Factory::getComposerFile(), '{}');
}
$input = new StringInput((string) $input . ' --working-dir=.');
$this->getIO()->writeError(
sprintf('<info>Run with <comment>%s</comment></info>', $input->__toString()),
true,
IOInterface::VERBOSE
);
return $application->doRun($input, $output);
}
/**
* {@inheritDoc}
*/
public function isProxyCommand()
{
return true;
}
/**
* Resets all Composer references in the application.
*
* @param ComposerApplication $application
* @return void
*/
private function resetComposers(ComposerApplication $application)
{
$application->resetComposer();
foreach ($this->getApplication()->all() as $command) {
if ($command instanceof BaseCommand) {
$command->resetComposer();
}
}
}
/**
* @param $dir
* @return void
*/
private function chdir($dir)
{
chdir($dir);
$this->getIO()->writeError(
sprintf('<info>Changed current directory to %s</info>', $dir),
true,
IOInterface::VERBOSE
);
}
/**
* @return \Composer\Config
* @throws \Composer\Json\JsonValidationException
* @throws \Seld\JsonLint\ParsingException
*/
private function createConfig()
{
$config = Factory::createConfig();
$file = new JsonFile(Factory::getComposerFile());
if (!$file->exists()) {
return $config;
}
$file->validateSchema(JsonFile::LAX_SCHEMA);
$config->merge($file->read());
return $config;
}
}

View File

@ -0,0 +1,330 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin\Command;
use Bamarni\Composer\Bin\Config\Config;
use Bamarni\Composer\Bin\Config\ConfigFactory;
use Bamarni\Composer\Bin\ApplicationFactory\FreshInstanceApplicationFactory;
use Bamarni\Composer\Bin\Input\BinInputFactory;
use Bamarni\Composer\Bin\Logger;
use Bamarni\Composer\Bin\ApplicationFactory\NamespaceApplicationFactory;
use Composer\Command\BaseCommand;
use Composer\Factory;
use Composer\IO\IOInterface;
use Composer\IO\NullIO;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
use function chdir;
use function count;
use function file_exists;
use function file_put_contents;
use function getcwd;
use function glob;
use function method_exists;
use function min;
use function mkdir;
use function putenv;
use function sprintf;
use const GLOB_ONLYDIR;
/**
* @final Will be final in 2.x.
*/
class BinCommand extends BaseCommand
{
private const ALL_NAMESPACES = 'all';
private const NAMESPACE_ARG = 'namespace';
/**
* @var NamespaceApplicationFactory
*/
private $applicationFactory;
/**
* @var Logger
*/
private $logger;
public function __construct(
?NamespaceApplicationFactory $applicationFactory = null,
?Logger $logger = null
) {
parent::__construct('bin');
$this->applicationFactory = $applicationFactory ?? new FreshInstanceApplicationFactory();
$this->logger = $logger ?? new Logger(new NullIO());
}
protected function configure(): void
{
$this
->setDescription('Run a command inside a bin namespace')
->addArgument(
self::NAMESPACE_ARG,
InputArgument::REQUIRED
)
->ignoreValidationErrors();
}
public function setIO(IOInterface $io): void
{
parent::setIO($io);
$this->logger = new Logger($io);
}
public function getIO(): IOInterface
{
$io = parent::getIO();
$this->logger = new Logger($io);
return $io;
}
public function isProxyCommand(): bool
{
return true;
}
public function execute(InputInterface $input, OutputInterface $output): int
{
// Switch to requireComposer() once Composer 2.3 is set as the minimum
$composer = method_exists($this, 'requireComposer')
? $this->requireComposer()
: $this->getComposer();
$config = Config::fromComposer($composer);
$currentWorkingDir = getcwd();
$this->logger->logDebug(
sprintf(
'Current working directory: <comment>%s</comment>',
$currentWorkingDir
)
);
// Ensures Composer is reset we are setting some environment variables
// & co. so a fresh Composer instance is required.
$this->resetComposers();
$this->configureBinLinksDir($config);
$vendorRoot = $config->getTargetDirectory();
$namespace = $input->getArgument(self::NAMESPACE_ARG);
$binInput = BinInputFactory::createInput(
$namespace,
$input
);
return (self::ALL_NAMESPACES !== $namespace)
? $this->executeInNamespace(
$currentWorkingDir,
$vendorRoot.'/'.$namespace,
$binInput,
$output
)
: $this->executeAllNamespaces(
$currentWorkingDir,
$vendorRoot,
$binInput,
$output
);
}
/**
* @return list<string>
*/
private static function getBinNamespaces(string $binVendorRoot): array
{
return glob($binVendorRoot.'/*', GLOB_ONLYDIR);
}
private function executeAllNamespaces(
string $originalWorkingDir,
string $binVendorRoot,
InputInterface $input,
OutputInterface $output
): int {
$namespaces = self::getBinNamespaces($binVendorRoot);
if (count($namespaces) === 0) {
$this->logger->logStandard('<warning>Could not find any bin namespace.</warning>');
// Is a valid scenario: the user may not have set up any bin
// namespace yet
return 0;
}
$exitCode = 0;
foreach ($namespaces as $namespace) {
$exitCode += $this->executeInNamespace(
$originalWorkingDir,
$namespace,
$input,
$output
);
}
return min($exitCode, 255);
}
private function executeInNamespace(
string $originalWorkingDir,
string $namespace,
InputInterface $input,
OutputInterface $output
): int {
$this->logger->logStandard(
sprintf(
'Checking namespace <comment>%s</comment>',
$namespace
)
);
try {
self::createNamespaceDirIfDoesNotExist($namespace);
} catch (CouldNotCreateNamespaceDir $exception) {
$this->logger->logStandard(
sprintf(
'<warning>%s</warning>',
$exception->getMessage()
)
);
return 1;
}
// Use a new application: this avoids a variety of issues:
// - A command may be added in a namespace which may cause side effects
// when executed in another namespace afterwards (since it is the same
// process).
// - Different plugins may be registered in the namespace in which case
// an already executed application will not pick that up.
$namespaceApplication = $this->applicationFactory->create(
$this->getApplication()
);
// It is important to clean up the state either for follow-up plugins
// or for example the execution in the next namespace.
$cleanUp = function () use ($originalWorkingDir): void {
$this->chdir($originalWorkingDir);
$this->resetComposers();
};
$this->chdir($namespace);
$this->ensureComposerFileExists();
$namespaceInput = BinInputFactory::createNamespaceInput($input);
$this->logger->logDebug(
sprintf(
'Running <info>`@composer %s`</info>.',
$namespaceInput->__toString()
)
);
try {
$exitCode = $namespaceApplication->doRun($namespaceInput, $output);
} catch (Throwable $executionFailed) {
// Ensure we do the cleanup even in case of failure
$cleanUp();
throw $executionFailed;
}
$cleanUp();
return $exitCode;
}
/**
* @throws CouldNotCreateNamespaceDir
*/
private static function createNamespaceDirIfDoesNotExist(string $namespace): void
{
if (file_exists($namespace)) {
return;
}
$mkdirResult = mkdir($namespace, 0777, true);
if (!$mkdirResult && !is_dir($namespace)) {
throw CouldNotCreateNamespaceDir::forNamespace($namespace);
}
}
private function configureBinLinksDir(Config $config): void
{
if (!$config->binLinksAreEnabled()) {
return;
}
$binDir = ConfigFactory::createConfig()->get('bin-dir');
putenv(
sprintf(
'COMPOSER_BIN_DIR=%s',
$binDir
)
);
$this->logger->logDebug(
sprintf(
'Configuring bin directory to <comment>%s</comment>.',
$binDir
)
);
}
private function ensureComposerFileExists(): void
{
// Some plugins require access to the Composer file e.g. Symfony Flex
$namespaceComposerFile = Factory::getComposerFile();
if (file_exists($namespaceComposerFile)) {
return;
}
file_put_contents($namespaceComposerFile, '{}');
$this->logger->logDebug(
sprintf(
'Created the file <comment>%s</comment>.',
$namespaceComposerFile
)
);
}
private function resetComposers(): void
{
$this->getApplication()->resetComposer();
foreach ($this->getApplication()->all() as $command) {
if ($command instanceof BaseCommand) {
$command->resetComposer();
}
}
}
private function chdir(string $dir): void
{
chdir($dir);
$this->logger->logDebug(
sprintf(
'Changed current directory to <comment>%s</comment>.',
$dir
)
);
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin\Command;
use RuntimeException;
use function sprintf;
final class CouldNotCreateNamespaceDir extends RuntimeException
{
public static function forNamespace(string $namespace): self
{
return new self(
sprintf(
'Could not create the directory "%s".',
$namespace
)
);
}
}

View File

@ -1,16 +1,19 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin;
use Bamarni\Composer\Bin\Command\BinCommand;
use Composer\Plugin\Capability\CommandProvider as CommandProviderCapability;
/**
* @final Will be final in 2.x.
*/
class CommandProvider implements CommandProviderCapability
{
/**
* {@inheritDoc}
*/
public function getCommands()
public function getCommands(): array
{
return [new BinCommand];
return [new BinCommand()];
}
}

View File

@ -1,47 +0,0 @@
<?php
namespace Bamarni\Composer\Bin;
use Composer\Composer;
final class Config
{
private $config;
public function __construct(Composer $composer)
{
$extra = $composer->getPackage()->getExtra();
$this->config = array_merge(
[
'bin-links' => true,
'target-directory' => 'vendor-bin',
'forward-command' => false,
],
isset($extra['bamarni-bin']) ? $extra['bamarni-bin'] : []
);
}
/**
* @return bool
*/
public function binLinksAreEnabled()
{
return true === $this->config['bin-links'];
}
/**
* @return string
*/
public function getTargetDirectory()
{
return $this->config['target-directory'];
}
/**
* @return bool
*/
public function isCommandForwarded()
{
return $this->config['forward-command'];
}
}

View File

@ -0,0 +1,156 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin\Config;
use Composer\Composer;
use function array_key_exists;
use function array_merge;
use function function_exists;
use function is_bool;
use function is_string;
use function sprintf;
final class Config
{
public const EXTRA_CONFIG_KEY = 'bamarni-bin';
public const BIN_LINKS_ENABLED = 'bin-links';
public const TARGET_DIRECTORY = 'target-directory';
public const FORWARD_COMMAND = 'forward-command';
private const DEFAULT_CONFIG = [
self::BIN_LINKS_ENABLED => true,
self::TARGET_DIRECTORY => 'vendor-bin',
self::FORWARD_COMMAND => false,
];
/**
* @var bool
*/
private $binLinks;
/**
* @var string
*/
private $targetDirectory;
/**
* @var bool
*/
private $forwardCommand;
/**
* @var list<string>
*/
private $deprecations = [];
/**
* @throws InvalidBamarniComposerExtraConfig
*/
public static function fromComposer(Composer $composer): self
{
return new self($composer->getPackage()->getExtra());
}
/**
* @param mixed[] $extra
*
* @throws InvalidBamarniComposerExtraConfig
*/
public function __construct(array $extra)
{
$userExtra = $extra[self::EXTRA_CONFIG_KEY] ?? [];
$config = array_merge(self::DEFAULT_CONFIG, $userExtra);
$getType = function_exists('get_debug_type') ? 'get_debug_type' : 'gettype';
$binLinks = $config[self::BIN_LINKS_ENABLED];
if (!is_bool($binLinks)) {
throw new InvalidBamarniComposerExtraConfig(
sprintf(
'Expected setting "extra.%s.%s" to be a boolean value. Got "%s".',
self::EXTRA_CONFIG_KEY,
self::BIN_LINKS_ENABLED,
$getType($binLinks)
)
);
}
$binLinksSetExplicitly = array_key_exists(self::BIN_LINKS_ENABLED, $userExtra);
if ($binLinks && !$binLinksSetExplicitly) {
$this->deprecations[] = sprintf(
'The setting "extra.%s.%s" will be set to "false" from 2.x onwards. If you wish to keep it to "true", you need to set it explicitly.',
self::EXTRA_CONFIG_KEY,
self::BIN_LINKS_ENABLED
);
}
$targetDirectory = $config[self::TARGET_DIRECTORY];
if (!is_string($targetDirectory)) {
throw new InvalidBamarniComposerExtraConfig(
sprintf(
'Expected setting "extra.%s.%s" to be a string. Got "%s".',
self::EXTRA_CONFIG_KEY,
self::TARGET_DIRECTORY,
$getType($targetDirectory)
)
);
}
$forwardCommand = $config[self::FORWARD_COMMAND];
if (!is_bool($forwardCommand)) {
throw new InvalidBamarniComposerExtraConfig(
sprintf(
'Expected setting "extra.%s.%s" to be a boolean value. Got "%s".',
self::EXTRA_CONFIG_KEY,
self::FORWARD_COMMAND,
gettype($forwardCommand)
)
);
}
$forwardCommandSetExplicitly = array_key_exists(self::FORWARD_COMMAND, $userExtra);
if (!$forwardCommand && !$forwardCommandSetExplicitly) {
$this->deprecations[] = sprintf(
'The setting "extra.%s.%s" will be set to "true" from 2.x onwards. If you wish to keep it to "false", you need to set it explicitly.',
self::EXTRA_CONFIG_KEY,
self::FORWARD_COMMAND
);
}
$this->binLinks = $binLinks;
$this->targetDirectory = $targetDirectory;
$this->forwardCommand = $forwardCommand;
}
public function binLinksAreEnabled(): bool
{
return $this->binLinks;
}
public function getTargetDirectory(): string
{
return $this->targetDirectory;
}
public function isCommandForwarded(): bool
{
return $this->forwardCommand;
}
/**
* @return list<string>
*/
public function getDeprecations(): array
{
return $this->deprecations;
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin\Config;
use Composer\Config as ComposerConfig;
use Composer\Factory;
use Composer\Json\JsonFile;
use Composer\Json\JsonValidationException;
use Seld\JsonLint\ParsingException;
final class ConfigFactory
{
/**
* @throws JsonValidationException
* @throws ParsingException
*/
public static function createConfig(): ComposerConfig
{
$config = Factory::createConfig();
$file = new JsonFile(Factory::getComposerFile());
if (!$file->exists()) {
return $config;
}
$file->validateSchema(JsonFile::LAX_SCHEMA);
$config->merge($file->read());
return $config;
}
private function __construct()
{
}
}

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin\Config;
use UnexpectedValueException;
final class InvalidBamarniComposerExtraConfig extends UnexpectedValueException
{
}

View File

@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin\Input;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\StringInput;
use function array_filter;
use function array_map;
use function implode;
use function preg_match;
use function preg_quote;
use function sprintf;
final class BinInputFactory
{
/**
* Extracts the input to execute in the bin namespace.
*
* For example: `bin namespace-name update --prefer-lowest` => `update --prefer-lowest`
*
* Note that no input definition is bound in the resulting input.
*/
public static function createInput(
string $namespace,
InputInterface $previousInput
): InputInterface {
$matchResult = preg_match(
sprintf(
'/^(?<preBinOptions>.+)?bin (?:(?<preBinOptions2>.+?) )?(?:%1$s|\'%1$s\') (?<binCommand>.+?)(?<extraInput> -- .*)?$/',
preg_quote($namespace, '/')
),
$previousInput->__toString(),
$matches
);
if (1 !== $matchResult) {
throw InvalidBinInput::forBinInput($previousInput);
}
$inputParts = array_filter(
array_map(
'trim',
[
$matches['binCommand'],
$matches['preBinOptions2'] ?? '',
$matches['preBinOptions'] ?? '',
$matches['extraInput'] ?? '',
]
)
);
// Move the options present _before_ bin namespaceName to after, but
// before the end of option marker (--) if present.
$reorderedInput = implode(' ', $inputParts);
return new StringInput($reorderedInput);
}
public static function createNamespaceInput(InputInterface $previousInput): InputInterface
{
$matchResult = preg_match(
'/^(.+?\s?)(--(?: .+)?)?$/',
$previousInput->__toString(),
$matches
);
if (1 !== $matchResult) {
throw InvalidBinInput::forNamespaceInput($previousInput);
}
$inputParts = array_filter(
array_map(
'trim',
[
$matches[1],
'--working-dir=.',
$matches[2] ?? '',
]
)
);
$newInput = implode(' ', $inputParts);
return new StringInput($newInput);
}
public static function createForwardedCommandInput(InputInterface $input): InputInterface
{
return new StringInput(
sprintf(
'bin all %s',
$input->__toString()
)
);
}
private function __construct()
{
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin\Input;
use RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use function sprintf;
final class InvalidBinInput extends RuntimeException
{
public static function forBinInput(InputInterface $input): self
{
return new self(
sprintf(
'Could not parse the input "%s". Expected the input to be in the format "bin <namespaceName> <commandToExecuteInBinNamespace>", for example "bin all update --prefer-lowest".',
$input->__toString()
)
);
}
public static function forNamespaceInput(InputInterface $input): self
{
return new self(
sprintf(
'Could not parse the input (executed within the namespace) "%s".',
$input->__toString()
)
);
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin;
use Composer\IO\IOInterface;
final class Logger
{
/**
* @var IOInterface
*/
private $io;
public function __construct(IOInterface $io)
{
$this->io = $io;
}
public function logStandard(string $message): void
{
$this->log($message, false);
}
public function logDebug(string $message): void
{
$this->log($message, true);
}
private function log(string $message, bool $debug): void
{
$verbosity = $debug
? IOInterface::VERBOSE
: IOInterface::NORMAL;
$this->io->writeError('[bamarni-bin] '.$message, true, $verbosity);
}
}

View File

@ -1,130 +0,0 @@
<?php
namespace Bamarni\Composer\Bin;
use Composer\Composer;
use Composer\Console\Application;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\IO\IOInterface;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Plugin\PluginInterface;
use Composer\Plugin\Capable;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
class Plugin implements PluginInterface, Capable, EventSubscriberInterface
{
/**
* @var Composer
*/
protected $composer;
/**
* @var IOInterface
*/
protected $io;
/**
* @return void
*/
public function activate(Composer $composer, IOInterface $io)
{
$this->composer = $composer;
$this->io = $io;
}
/**
* @return string[]
*/
public function getCapabilities()
{
return [
'Composer\Plugin\Capability\CommandProvider' => 'Bamarni\Composer\Bin\CommandProvider',
];
}
/**
* @return void
*/
public function deactivate(Composer $composer, IOInterface $io)
{
}
/**
* @return void
*/
public function uninstall(Composer $composer, IOInterface $io)
{
}
/**
* @return string[]
*/
public static function getSubscribedEvents()
{
return [
PluginEvents::COMMAND => 'onCommandEvent',
];
}
/**
* @param CommandEvent $event
* @return bool
*/
public function onCommandEvent(CommandEvent $event)
{
$config = new Config($this->composer);
if ($config->isCommandForwarded()) {
switch ($event->getCommandName()) {
case 'update':
case 'install':
return $this->onCommandEventInstallUpdate($event);
}
}
return true;
}
/**
* @param CommandEvent $event
* @return bool
*/
protected function onCommandEventInstallUpdate(CommandEvent $event)
{
$command = new BinCommand();
$command->setComposer($this->composer);
$command->setApplication(new Application());
$arguments = [
'command' => $command->getName(),
'namespace' => 'all',
'args' => [],
];
foreach (array_filter($event->getInput()->getArguments()) as $argument) {
$arguments['args'][] = $argument;
}
foreach (array_keys(array_filter($event->getInput()->getOptions())) as $option) {
$arguments['args'][] = '--' . $option;
}
$definition = new InputDefinition();
$definition->addArgument(new InputArgument('command', InputArgument::REQUIRED));
$definition->addArguments($command->getDefinition()->getArguments());
$definition->addOptions($command->getDefinition()->getOptions());
$input = new ArrayInput($arguments, $definition);
try {
$returnCode = $command->run($input, $event->getOutput());
} catch (\Exception $e) {
return false;
}
return $returnCode === 0;
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Bamarni\Composer\Bin;
use Composer\IO\ConsoleIO;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class PublicIO extends ConsoleIO
{
public static function fromConsoleIO(ConsoleIO $io): self
{
return new self(
$io->input,
$io->output,
$io->helperSet
);
}
public function getInput(): InputInterface
{
return $this->input;
}
public function getOutput(): OutputInterface
{
return $this->output;
}
}

View File

@ -1,120 +0,0 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../../vendor-bin/coding-standard/vendor/friendsofphp/php-cs-fixer/php-cs-fixer)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
include("phpvfscomposer://" . __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/friendsofphp/php-cs-fixer/php-cs-fixer');
exit(0);
}
}
include __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/friendsofphp/php-cs-fixer/php-cs-fixer';

120
vendor/bin/php-parse vendored
View File

@ -1,120 +0,0 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../../vendor-bin/coding-standard/vendor/nikic/php-parser/bin/php-parse)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
include("phpvfscomposer://" . __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/nikic/php-parser/bin/php-parse');
exit(0);
}
}
include __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/nikic/php-parser/bin/php-parse';

120
vendor/bin/psalm vendored
View File

@ -1,120 +0,0 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../../vendor-bin/coding-standard/vendor/vimeo/psalm/psalm)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
include("phpvfscomposer://" . __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/vimeo/psalm/psalm');
exit(0);
}
}
include __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/vimeo/psalm/psalm';

View File

@ -1,120 +0,0 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../../vendor-bin/coding-standard/vendor/vimeo/psalm/psalm-language-server)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
include("phpvfscomposer://" . __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/vimeo/psalm/psalm-language-server');
exit(0);
}
}
include __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/vimeo/psalm/psalm-language-server';

View File

@ -1,120 +0,0 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../../vendor-bin/coding-standard/vendor/vimeo/psalm/psalm-plugin)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
include("phpvfscomposer://" . __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/vimeo/psalm/psalm-plugin');
exit(0);
}
}
include __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/vimeo/psalm/psalm-plugin';

View File

@ -1,120 +0,0 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../../vendor-bin/coding-standard/vendor/vimeo/psalm/psalm-refactor)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
include("phpvfscomposer://" . __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/vimeo/psalm/psalm-refactor');
exit(0);
}
}
include __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/vimeo/psalm/psalm-refactor';

120
vendor/bin/psalter vendored
View File

@ -1,120 +0,0 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../../vendor-bin/coding-standard/vendor/vimeo/psalm/psalter)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
include("phpvfscomposer://" . __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/vimeo/psalm/psalter');
exit(0);
}
}
include __DIR__ . '/../..'.'/vendor-bin/coding-standard/vendor/vimeo/psalm/psalter';

View File

@ -45,35 +45,34 @@ class ClassLoader
/** @var \Closure(string):void */
private static $includeFile;
/** @var ?string */
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
@ -81,8 +80,7 @@ class ClassLoader
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
* @var array<string, string>
*/
private $classMap = array();
@ -90,21 +88,20 @@ class ClassLoader
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
/** @var string|null */
private $apcuPrefix;
/**
* @var self[]
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
@ -113,7 +110,7 @@ class ClassLoader
}
/**
* @return string[]
* @return array<string, list<string>>
*/
public function getPrefixes()
{
@ -125,8 +122,7 @@ class ClassLoader
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
@ -134,8 +130,7 @@ class ClassLoader
}
/**
* @return array[]
* @psalm-return array<string, string>
* @return list<string>
*/
public function getFallbackDirs()
{
@ -143,8 +138,7 @@ class ClassLoader
}
/**
* @return array[]
* @psalm-return array<string, string>
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
@ -152,8 +146,7 @@ class ClassLoader
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
@ -161,8 +154,7 @@ class ClassLoader
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
@ -179,24 +171,25 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
$paths
);
}
@ -205,19 +198,19 @@ class ClassLoader
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
$this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
$paths
);
}
}
@ -226,9 +219,9 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
@ -236,17 +229,18 @@ class ClassLoader
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
$paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
@ -256,18 +250,18 @@ class ClassLoader
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
$this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
$paths
);
}
}
@ -276,8 +270,8 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
@ -294,8 +288,8 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
@ -481,9 +475,9 @@ class ClassLoader
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return self[]
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{

View File

@ -98,7 +98,7 @@ class InstalledVersions
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
@ -119,7 +119,7 @@ class InstalledVersions
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
@ -328,7 +328,9 @@ class InstalledVersions
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
@ -340,12 +342,17 @@ class InstalledVersions
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
if (self::$installed !== array()) {
$installed[] = self::$installed;
}
return $installed;
}

View File

@ -7,10 +7,19 @@ $baseDir = dirname($vendorDir);
return array(
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Bamarni\\Composer\\Bin\\BinCommand' => $vendorDir . '/bamarni/composer-bin-plugin/src/BinCommand.php',
'Bamarni\\Composer\\Bin\\ApplicationFactory\\FreshInstanceApplicationFactory' => $vendorDir . '/bamarni/composer-bin-plugin/src/ApplicationFactory/FreshInstanceApplicationFactory.php',
'Bamarni\\Composer\\Bin\\ApplicationFactory\\NamespaceApplicationFactory' => $vendorDir . '/bamarni/composer-bin-plugin/src/ApplicationFactory/NamespaceApplicationFactory.php',
'Bamarni\\Composer\\Bin\\BamarniBinPlugin' => $vendorDir . '/bamarni/composer-bin-plugin/src/BamarniBinPlugin.php',
'Bamarni\\Composer\\Bin\\CommandProvider' => $vendorDir . '/bamarni/composer-bin-plugin/src/CommandProvider.php',
'Bamarni\\Composer\\Bin\\Config' => $vendorDir . '/bamarni/composer-bin-plugin/src/Config.php',
'Bamarni\\Composer\\Bin\\Plugin' => $vendorDir . '/bamarni/composer-bin-plugin/src/Plugin.php',
'Bamarni\\Composer\\Bin\\Command\\BinCommand' => $vendorDir . '/bamarni/composer-bin-plugin/src/Command/BinCommand.php',
'Bamarni\\Composer\\Bin\\Command\\CouldNotCreateNamespaceDir' => $vendorDir . '/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php',
'Bamarni\\Composer\\Bin\\Config\\Config' => $vendorDir . '/bamarni/composer-bin-plugin/src/Config/Config.php',
'Bamarni\\Composer\\Bin\\Config\\ConfigFactory' => $vendorDir . '/bamarni/composer-bin-plugin/src/Config/ConfigFactory.php',
'Bamarni\\Composer\\Bin\\Config\\InvalidBamarniComposerExtraConfig' => $vendorDir . '/bamarni/composer-bin-plugin/src/Config/InvalidBamarniComposerExtraConfig.php',
'Bamarni\\Composer\\Bin\\Input\\BinInputFactory' => $vendorDir . '/bamarni/composer-bin-plugin/src/Input/BinInputFactory.php',
'Bamarni\\Composer\\Bin\\Input\\InvalidBinInput' => $vendorDir . '/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php',
'Bamarni\\Composer\\Bin\\Logger' => $vendorDir . '/bamarni/composer-bin-plugin/src/Logger.php',
'Bamarni\\Composer\\Bin\\PublicIO' => $vendorDir . '/bamarni/composer-bin-plugin/src/PublicIO.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'NC\\Updater\\CommandApplication' => $baseDir . '/lib/CommandApplication.php',
@ -20,6 +29,7 @@ return array(
'NC\\Updater\\UpdateException' => $baseDir . '/lib/UpdateException.php',
'NC\\Updater\\Updater' => $baseDir . '/lib/Updater.php',
'NC\\Updater\\Version' => $baseDir . '/lib/Version.php',
'Nextcloud\\CodingStandard\\Config' => $vendorDir . '/nextcloud/coding-standard/src/Config.php',
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php',
'Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php',

View File

@ -12,6 +12,7 @@ return array(
'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Nextcloud\\CodingStandard\\' => array($vendorDir . '/nextcloud/coding-standard/src'),
'NC\\Updater\\' => array($baseDir . '/lib'),
'Bamarni\\Composer\\Bin\\' => array($vendorDir . '/bamarni/composer-bin-plugin/src'),
);

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitd7f5ddc48e4715174279359c16c76340
class ComposerAutoloaderInitba7c5c8f0885d00c3b669d0399f96c80
{
private static $loader;
@ -24,17 +24,17 @@ class ComposerAutoloaderInitd7f5ddc48e4715174279359c16c76340
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInitd7f5ddc48e4715174279359c16c76340', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInitba7c5c8f0885d00c3b669d0399f96c80', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInitd7f5ddc48e4715174279359c16c76340', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInitba7c5c8f0885d00c3b669d0399f96c80', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitd7f5ddc48e4715174279359c16c76340::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInitba7c5c8f0885d00c3b669d0399f96c80::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInitd7f5ddc48e4715174279359c16c76340::$files;
$filesToLoad = \Composer\Autoload\ComposerStaticInitba7c5c8f0885d00c3b669d0399f96c80::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

View File

@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInitd7f5ddc48e4715174279359c16c76340
class ComposerStaticInitba7c5c8f0885d00c3b669d0399f96c80
{
public static $files = array (
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
@ -28,6 +28,7 @@ class ComposerStaticInitd7f5ddc48e4715174279359c16c76340
),
'N' =>
array (
'Nextcloud\\CodingStandard\\' => 25,
'NC\\Updater\\' => 11,
),
'B' =>
@ -61,6 +62,10 @@ class ComposerStaticInitd7f5ddc48e4715174279359c16c76340
array (
0 => __DIR__ . '/..' . '/psr/container/src',
),
'Nextcloud\\CodingStandard\\' =>
array (
0 => __DIR__ . '/..' . '/nextcloud/coding-standard/src',
),
'NC\\Updater\\' =>
array (
0 => __DIR__ . '/../..' . '/lib',
@ -73,10 +78,19 @@ class ComposerStaticInitd7f5ddc48e4715174279359c16c76340
public static $classMap = array (
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Bamarni\\Composer\\Bin\\BinCommand' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/BinCommand.php',
'Bamarni\\Composer\\Bin\\ApplicationFactory\\FreshInstanceApplicationFactory' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/ApplicationFactory/FreshInstanceApplicationFactory.php',
'Bamarni\\Composer\\Bin\\ApplicationFactory\\NamespaceApplicationFactory' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/ApplicationFactory/NamespaceApplicationFactory.php',
'Bamarni\\Composer\\Bin\\BamarniBinPlugin' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/BamarniBinPlugin.php',
'Bamarni\\Composer\\Bin\\CommandProvider' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/CommandProvider.php',
'Bamarni\\Composer\\Bin\\Config' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Config.php',
'Bamarni\\Composer\\Bin\\Plugin' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Plugin.php',
'Bamarni\\Composer\\Bin\\Command\\BinCommand' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Command/BinCommand.php',
'Bamarni\\Composer\\Bin\\Command\\CouldNotCreateNamespaceDir' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php',
'Bamarni\\Composer\\Bin\\Config\\Config' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Config/Config.php',
'Bamarni\\Composer\\Bin\\Config\\ConfigFactory' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Config/ConfigFactory.php',
'Bamarni\\Composer\\Bin\\Config\\InvalidBamarniComposerExtraConfig' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Config/InvalidBamarniComposerExtraConfig.php',
'Bamarni\\Composer\\Bin\\Input\\BinInputFactory' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Input/BinInputFactory.php',
'Bamarni\\Composer\\Bin\\Input\\InvalidBinInput' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php',
'Bamarni\\Composer\\Bin\\Logger' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/Logger.php',
'Bamarni\\Composer\\Bin\\PublicIO' => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src/PublicIO.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'NC\\Updater\\CommandApplication' => __DIR__ . '/../..' . '/lib/CommandApplication.php',
@ -86,6 +100,7 @@ class ComposerStaticInitd7f5ddc48e4715174279359c16c76340
'NC\\Updater\\UpdateException' => __DIR__ . '/../..' . '/lib/UpdateException.php',
'NC\\Updater\\Updater' => __DIR__ . '/../..' . '/lib/Updater.php',
'NC\\Updater\\Version' => __DIR__ . '/../..' . '/lib/Version.php',
'Nextcloud\\CodingStandard\\Config' => __DIR__ . '/..' . '/nextcloud/coding-standard/src/Config.php',
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Psr\\Container\\ContainerExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerExceptionInterface.php',
'Psr\\Container\\ContainerInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerInterface.php',
@ -194,9 +209,9 @@ class ComposerStaticInitd7f5ddc48e4715174279359c16c76340
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitd7f5ddc48e4715174279359c16c76340::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitd7f5ddc48e4715174279359c16c76340::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitd7f5ddc48e4715174279359c16c76340::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInitba7c5c8f0885d00c3b669d0399f96c80::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitba7c5c8f0885d00c3b669d0399f96c80::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitba7c5c8f0885d00c3b669d0399f96c80::$classMap;
}, null, ClassLoader::class);
}

View File

@ -2,31 +2,38 @@
"packages": [
{
"name": "bamarni/composer-bin-plugin",
"version": "v1.5.0",
"version_normalized": "1.5.0.0",
"version": "1.8.2",
"version_normalized": "1.8.2.0",
"source": {
"type": "git",
"url": "https://github.com/bamarni/composer-bin-plugin.git",
"reference": "49934ffea764864788334c1485fbb08a4b852031"
"reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/49934ffea764864788334c1485fbb08a4b852031",
"reference": "49934ffea764864788334c1485fbb08a4b852031",
"url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
"reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": "^5.5.9 || ^7.0 || ^8.0"
"composer-plugin-api": "^2.0",
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"composer/composer": "^1.0 || ^2.0",
"symfony/console": "^2.5 || ^3.0 || ^4.0"
"composer/composer": "^2.0",
"ext-json": "*",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
"phpunit/phpunit": "^8.5 || ^9.5",
"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-02-22T21:01:25+00:00",
"time": "2022-10-31T08:38:03+00:00",
"type": "composer-plugin",
"extra": {
"class": "Bamarni\\Composer\\Bin\\Plugin"
"class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin"
},
"installation-source": "dist",
"autoload": {
@ -47,8 +54,111 @@
"isolation",
"tool"
],
"support": {
"issues": "https://github.com/bamarni/composer-bin-plugin/issues",
"source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2"
},
"install-path": "../bamarni/composer-bin-plugin"
},
{
"name": "nextcloud/coding-standard",
"version": "v1.1.1",
"version_normalized": "1.1.1.0",
"source": {
"type": "git",
"url": "https://github.com/nextcloud/coding-standard.git",
"reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/55def702fb9a37a219511e1d8c6fe8e37164c1fb",
"reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb",
"shasum": ""
},
"require": {
"php": "^7.3|^8.0",
"php-cs-fixer/shim": "^3.17"
},
"time": "2023-06-01T12:05:01+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Nextcloud\\CodingStandard\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christoph Wurst",
"email": "christoph@winzerhof-wurst.at"
}
],
"description": "Nextcloud coding standards for the php cs fixer",
"support": {
"issues": "https://github.com/nextcloud/coding-standard/issues",
"source": "https://github.com/nextcloud/coding-standard/tree/v1.1.1"
},
"install-path": "../nextcloud/coding-standard"
},
{
"name": "php-cs-fixer/shim",
"version": "v3.27.0",
"version_normalized": "3.27.0.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/shim.git",
"reference": "fc5c89dc53f46e533cca327d699802017022c3a7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/fc5c89dc53f46e533cca327d699802017022c3a7",
"reference": "fc5c89dc53f46e533cca327d699802017022c3a7",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-tokenizer": "*",
"php": "^7.4 || ^8.0"
},
"replace": {
"friendsofphp/php-cs-fixer": "self.version"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
"ext-mbstring": "For handling non-UTF8 characters."
},
"time": "2023-09-17T14:38:32+00:00",
"bin": [
"php-cs-fixer",
"php-cs-fixer.phar"
],
"type": "application",
"installation-source": "dist",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Dariusz Rumiński",
"email": "dariusz.ruminski@gmail.com"
}
],
"description": "A tool to automatically fix PHP code style",
"support": {
"issues": "https://github.com/PHP-CS-Fixer/shim/issues",
"source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.27.0"
},
"install-path": "../php-cs-fixer/shim"
},
{
"name": "psr/container",
"version": "1.1.2",
@ -102,17 +212,17 @@
},
{
"name": "symfony/console",
"version": "v4.4.43",
"version_normalized": "4.4.43.0",
"version": "v4.4.49",
"version_normalized": "4.4.49.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "8a2628d2d5639f35113dc1b833ecd91e1ed1cf46"
"reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/8a2628d2d5639f35113dc1b833ecd91e1ed1cf46",
"reference": "8a2628d2d5639f35113dc1b833ecd91e1ed1cf46",
"url": "https://api.github.com/repos/symfony/console/zipball/33fa45ffc81fdcc1ca368d4946da859c8cdb58d9",
"reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9",
"shasum": ""
},
"require": {
@ -147,7 +257,7 @@
"symfony/lock": "",
"symfony/process": ""
},
"time": "2022-06-23T12:22:25+00:00",
"time": "2022-11-05T17:10:16+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -175,7 +285,7 @@
"description": "Eases the creation of beautiful and testable command line interfaces",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/console/tree/v4.4.43"
"source": "https://github.com/symfony/console/tree/v4.4.49"
},
"funding": [
{
@ -265,17 +375,17 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.26.0",
"version_normalized": "1.26.0.0",
"version": "v1.28.0",
"version_normalized": "1.28.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
"reference": "42292d99c55abe617799667f454222c54c60e229"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "42292d99c55abe617799667f454222c54c60e229",
"shasum": ""
},
"require": {
@ -287,11 +397,11 @@
"suggest": {
"ext-mbstring": "For best performance"
},
"time": "2022-05-24T11:49:31+00:00",
"time": "2023-07-28T09:04:16+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.26-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -331,7 +441,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
},
"funding": [
{
@ -351,27 +461,27 @@
},
{
"name": "symfony/polyfill-php73",
"version": "v1.26.0",
"version_normalized": "1.26.0.0",
"version": "v1.28.0",
"version_normalized": "1.28.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
"reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5",
"reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2022-05-24T11:49:31+00:00",
"time": "2023-01-26T09:26:14+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.26-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -413,7 +523,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
"source": "https://github.com/symfony/polyfill-php73/tree/v1.28.0"
},
"funding": [
{
@ -433,27 +543,27 @@
},
{
"name": "symfony/polyfill-php80",
"version": "v1.26.0",
"version_normalized": "1.26.0.0",
"version": "v1.28.0",
"version_normalized": "1.28.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2022-05-10T07:21:04+00:00",
"time": "2023-01-26T09:26:14+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.26-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -499,7 +609,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
},
"funding": [
{
@ -606,6 +716,8 @@
],
"dev": true,
"dev-package-names": [
"bamarni/composer-bin-plugin"
"bamarni/composer-bin-plugin",
"nextcloud/coding-standard",
"php-cs-fixer/shim"
]
}

View File

@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '6ed28216e532ab799fee9ff6d87910353f46ad32',
'reference' => '2df6d29bfc56049682a84b2a4671b95e82314ae4',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -13,21 +13,45 @@
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '6ed28216e532ab799fee9ff6d87910353f46ad32',
'reference' => '2df6d29bfc56049682a84b2a4671b95e82314ae4',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'bamarni/composer-bin-plugin' => array(
'pretty_version' => 'v1.5.0',
'version' => '1.5.0.0',
'reference' => '49934ffea764864788334c1485fbb08a4b852031',
'pretty_version' => '1.8.2',
'version' => '1.8.2.0',
'reference' => '92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880',
'type' => 'composer-plugin',
'install_path' => __DIR__ . '/../bamarni/composer-bin-plugin',
'aliases' => array(),
'dev_requirement' => true,
),
'friendsofphp/php-cs-fixer' => array(
'dev_requirement' => true,
'replaced' => array(
0 => 'v3.27.0',
),
),
'nextcloud/coding-standard' => array(
'pretty_version' => 'v1.1.1',
'version' => '1.1.1.0',
'reference' => '55def702fb9a37a219511e1d8c6fe8e37164c1fb',
'type' => 'library',
'install_path' => __DIR__ . '/../nextcloud/coding-standard',
'aliases' => array(),
'dev_requirement' => true,
),
'php-cs-fixer/shim' => array(
'pretty_version' => 'v3.27.0',
'version' => '3.27.0.0',
'reference' => 'fc5c89dc53f46e533cca327d699802017022c3a7',
'type' => 'application',
'install_path' => __DIR__ . '/../php-cs-fixer/shim',
'aliases' => array(),
'dev_requirement' => true,
),
'psr/container' => array(
'pretty_version' => '1.1.2',
'version' => '1.1.2.0',
@ -44,9 +68,9 @@
),
),
'symfony/console' => array(
'pretty_version' => 'v4.4.43',
'version' => '4.4.43.0',
'reference' => '8a2628d2d5639f35113dc1b833ecd91e1ed1cf46',
'pretty_version' => 'v4.4.49',
'version' => '4.4.49.0',
'reference' => '33fa45ffc81fdcc1ca368d4946da859c8cdb58d9',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/console',
'aliases' => array(),
@ -62,27 +86,27 @@
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => '9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e',
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '42292d99c55abe617799667f454222c54c60e229',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-php73' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => 'e440d35fa0286f77fb45b79a03fedbeda9307e85',
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => 'fe2f306d1d9d346a7fee353d0d5012e401e984b5',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php73',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
'pretty_version' => 'v1.28.0',
'version' => '1.28.0.0',
'reference' => '6caa57379c4aec19c0a12a38b59b26487dcfe4b5',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),

View File

@ -0,0 +1,22 @@
name: Lint
on:
push:
branches:
- master
- stable*
pull_request:
jobs:
lint-php:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Dependencies
run: composer install --prefer-dist
- name: Load problem matcher for php -l
uses: korelstar/phplint-problem-matcher@v1
- name: PHP syntax check
run: "find src/ -type f -name '*.php' -print0 | xargs -0 -L1 -P4 -- php -l -f"
- name: PHP Coding Standards Fixer
run: vendor/bin/php-cs-fixer fix --dry-run --diff

View File

@ -0,0 +1,8 @@
composer.phar
/composer.lock
/vendor/
/.idea
.php_cs.cache
.php-cs-fixer.cache

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
require_once './vendor/autoload.php';
use Nextcloud\CodingStandard\Config;
$config = new Config();
$config
->getFinder()
->ignoreVCSIgnored(true)
->notPath('vendor')
->in(__DIR__);
return $config;

View File

@ -0,0 +1,14 @@
# Changelog
All notable changes to this project will be documented in this file.
## 0.5.0 2021-01-11
### Added
- New rule: short list syntax
- php7.2 support (back, for apps that support Nextclod 20 - 21)
## 0.4.0 2020-12-14
### Added
- php8 support
- New rule: binary operators should be surrounded by a single space
### Changed
- php-cs-fixer updated to the latest version

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Christoph Wurst
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,56 @@
# Nextcloud Coding Standard
Nextcloud coding standards for the [php cs fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer).
## Installation
Add the package to your dev dependencies
```bash
composer require --dev nextcloud/coding-standard
```
and create a `.php-cs-fixer.dist.php` like
```php
<?php
declare(strict_types=1);
require_once './vendor/autoload.php';
use Nextcloud\CodingStandard\Config;
$config = new Config();
$config
->getFinder()
->ignoreVCSIgnored(true)
->notPath('build')
->notPath('l10n')
->notPath('src')
->notPath('vendor')
->in(__DIR__);
return $config;
```
To run the fixer you first have to [install it](https://github.com/FriendsOfPhp/PHP-CS-Fixer#installation). Then you can run `php-cs-fixer fix` to apply all automated fixes.
For convenience you may add it to the `scripts` section of your `composer.json`:
```json
{
"scripts": {
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix"
}
}
```
*Note: Don't forget to exclude .php_cs.dist in your build scripts.*
## Upgrade from v0.x to v1.0
With v1.0 php-cs-fixer was updated from v2 to v3. You'll have to adjust your app slightly:
* Rename `.php_cs.dist` to `.php-cs-fixer.dist.php`
* Add `.php-cs-fixer.cache` to your ignore files

View File

@ -0,0 +1,21 @@
{
"name": "nextcloud/coding-standard",
"description": "Nextcloud coding standards for the php cs fixer",
"type": "library",
"require": {
"php": "^7.3|^8.0",
"php-cs-fixer/shim": "^3.17"
},
"license": "MIT",
"authors": [
{
"name": "Christoph Wurst",
"email": "christoph@winzerhof-wurst.at"
}
],
"autoload": {
"psr-4": {
"Nextcloud\\CodingStandard\\": "src"
}
}
}

View File

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace Nextcloud\CodingStandard;
use PhpCsFixer\Config as Base;
class Config extends Base {
public function __construct($name = 'default') {
parent::__construct($name);
$this->setIndent("\t");
}
public function getRules() : array {
return [
'@PSR1' => true,
'@PSR2' => true,
'align_multiline_comment' => true,
'array_indentation' => true,
'binary_operator_spaces' => [
'default' => 'single_space',
],
'blank_line_after_namespace' => true,
'blank_line_after_opening_tag' => true,
'curly_braces_position' => [
'classes_opening_brace' => 'same_line',
'functions_opening_brace' => 'same_line',
],
'elseif' => true,
'encoding' => true,
'full_opening_tag' => true,
'function_declaration' => [
'closure_function_spacing' => 'one',
],
'indentation_type' => true,
'line_ending' => true,
'lowercase_keywords' => true,
'method_argument_space' => [
'on_multiline' => 'ignore',
],
'no_closing_tag' => true,
'no_spaces_after_function_name' => true,
'no_spaces_inside_parenthesis' => true,
'no_trailing_whitespace' => true,
'no_trailing_whitespace_in_comment' => true,
'no_unused_imports' => true,
'ordered_imports' => [
'imports_order' => ['class', 'function', 'const'],
'sort_algorithm' => 'alpha'
],
'single_blank_line_at_eof' => true,
'single_class_element_per_statement' => true,
'single_import_per_statement' => true,
'single_line_after_imports' => true,
'switch_case_space' => true,
'visibility_required' => [
'elements' => ['property', 'method', 'const']
],
];
}
}

19
vendor/php-cs-fixer/shim/LICENSE vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2012+ Fabien Potencier, Dariusz Rumiński
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

11
vendor/php-cs-fixer/shim/README.md vendored Normal file
View File

@ -0,0 +1,11 @@
<p align="center">
<a href="https://cs.symfony.com">
<img src="./logo.png" title="PHP CS Fixer" alt="PHP CS Fixer logo">
</a>
</p>
PHP Coding Standards Fixer
==========================
This is a *shim* package of [PHP CS Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer/).
For any related topic, please visit the [main repo](https://github.com/FriendsOfPHP/PHP-CS-Fixer/) page.

35
vendor/php-cs-fixer/shim/composer.json vendored Normal file
View File

@ -0,0 +1,35 @@
{
"name": "php-cs-fixer/shim",
"description": "A tool to automatically fix PHP code style",
"license": "MIT",
"type": "application",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Dariusz Rumiński",
"email": "dariusz.ruminski@gmail.com"
}
],
"require": {
"php": "^7.4 || ^8.0",
"ext-json": "*",
"ext-tokenizer": "*"
},
"replace": {
"friendsofphp/php-cs-fixer": "self.version"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
"ext-mbstring": "For handling non-UTF8 characters."
},
"bin": [
"php-cs-fixer",
"php-cs-fixer.phar"
],
"config": {
"sort-packages": true
}
}

3
vendor/php-cs-fixer/shim/logo.md vendored Normal file
View File

@ -0,0 +1,3 @@
The logo is © 2010-2022 Sensio Labs.
Original resolution can be found at https://github.com/PHP-CS-Fixer/logo .

BIN
vendor/php-cs-fixer/shim/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

18
vendor/php-cs-fixer/shim/php-cs-fixer vendored Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env php
<?php
/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
declare(strict_types=1);
Phar::loadPhar(__DIR__ . '/php-cs-fixer.phar', 'php-cs-fixer.phar');
require 'phar://php-cs-fixer.phar/php-cs-fixer';

BIN
vendor/php-cs-fixer/shim/php-cs-fixer.phar vendored Executable file

Binary file not shown.

Binary file not shown.

View File

@ -254,7 +254,9 @@ class Application implements ResetInterface
$alternative = $alternatives[0];
$style = new SymfonyStyle($input, $output);
$style->block(sprintf("\nCommand \"%s\" is not defined.\n", $name), null, 'error');
$output->writeln('');
$formattedBlock = (new FormatterHelper())->formatBlock(sprintf('Command "%s" is not defined.', $name), 'error', true);
$output->writeln($formattedBlock);
if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
if (null !== $this->dispatcher) {
$event = new ConsoleErrorEvent($input, $output, $e);
@ -955,11 +957,21 @@ class Application implements ResetInterface
}
switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
case -1: $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); break;
case 1: $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); break;
case 2: $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); break;
case 3: $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); break;
default: $shellVerbosity = 0; break;
case -1:
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
break;
case 1:
$output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
break;
case 2:
$output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
break;
case 3:
$output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
break;
default:
$shellVerbosity = 0;
break;
}
if (true === $input->hasParameterOption(['--quiet', '-q'], true)) {

View File

@ -195,7 +195,7 @@ class Command
*
* @return int The command exit code
*
* @throws \Exception When binding input fails. Bypass this by calling {@link ignoreValidationErrors()}.
* @throws ExceptionInterface When input binding fails. Bypass this by calling {@link ignoreValidationErrors()}.
*
* @see setCode()
* @see execute()

View File

@ -55,7 +55,7 @@ class AddConsoleCommandPass implements CompilerPassInterface
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class));
}
$commandName = $class::getDefaultName() !== null ? str_replace('%', '%%', $class::getDefaultName()) : null;
$commandName = null !== $class::getDefaultName() ? str_replace('%', '%%', $class::getDefaultName()) : null;
}
if (null === $commandName) {

View File

@ -131,7 +131,7 @@ class ApplicationDescription
}
if ($namespacedCommands) {
ksort($namespacedCommands);
ksort($namespacedCommands, \SORT_STRING);
foreach ($namespacedCommands as $key => $commandsSet) {
ksort($commandsSet);
$sortedCommands[$key] = $commandsSet;

View File

@ -12,7 +12,7 @@
namespace Symfony\Component\Console\Exception;
/**
* Represents an incorrect option name typed in the console.
* Represents an incorrect option name or value typed in the console.
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/

View File

@ -135,6 +135,8 @@ abstract class Helper implements HelperInterface
$string = $formatter->format($string);
// remove already formatted characters
$string = preg_replace("/\033\[[^m]*m/", '', $string);
// remove terminal hyperlinks
$string = preg_replace('/\\033]8;[^;]*;[^\\033]*\\033\\\\/', '', $string);
$formatter->setDecorated($isDecorated);
return $string;

View File

@ -641,7 +641,7 @@ class Table
{
$unmergedRows = [];
foreach ($rows[$line] as $column => $cell) {
if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
if (null !== $cell && !$cell instanceof TableCell && !\is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) {
throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', \gettype($cell)));
}
if ($cell instanceof TableCell && $cell->getRowspan() > 1) {

View File

@ -110,7 +110,7 @@ class ConsoleLogger extends AbstractLogger
$replacements = [];
foreach ($context as $key => $val) {
if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
if (null === $val || \is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
$replacements["{{$key}}"] = $val;
} elseif ($val instanceof \DateTimeInterface) {
$replacements["{{$key}}"] = $val->format(\DateTime::RFC3339);

View File

@ -430,18 +430,18 @@ class SymfonyStyle extends OutputStyle
$chars = substr(str_replace(\PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2);
if (!isset($chars[0])) {
$this->newLine(); //empty history, so we should start with a new line.
$this->newLine(); // empty history, so we should start with a new line.
return;
}
//Prepend new line for each non LF chars (This means no blank line was output before)
// Prepend new line for each non LF chars (This means no blank line was output before)
$this->newLine(2 - substr_count($chars, "\n"));
}
private function autoPrependText(): void
{
$fetched = $this->bufferedOutput->fetch();
//Prepend new line if last char isn't EOL:
// Prepend new line if last char isn't EOL:
if (!str_ends_with($fetched, "\n")) {
$this->newLine();
}

View File

@ -1,4 +1,4 @@
Copyright (c) 2015-2019 Fabien Potencier
Copyright (c) 2015-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -69,7 +69,7 @@ final class Mbstring
{
public const MB_CASE_FOLD = \PHP_INT_MAX;
private const CASE_FOLD = [
private const SIMPLE_CASE_FOLD = [
['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"],
['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'],
];
@ -80,7 +80,7 @@ final class Mbstring
public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
{
if (\is_array($fromEncoding) || ($fromEncoding !== null && false !== strpos($fromEncoding, ','))) {
if (\is_array($fromEncoding) || (null !== $fromEncoding && false !== strpos($fromEncoding, ','))) {
$fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
} else {
$fromEncoding = self::getEncoding($fromEncoding);
@ -102,7 +102,7 @@ final class Mbstring
$fromEncoding = 'Windows-1252';
}
if ('UTF-8' !== $fromEncoding) {
$s = \iconv($fromEncoding, 'UTF-8//IGNORE', $s);
$s = iconv($fromEncoding, 'UTF-8//IGNORE', $s);
}
return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s);
@ -113,7 +113,7 @@ final class Mbstring
$fromEncoding = 'UTF-8';
}
return \iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
return iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
}
public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars)
@ -130,7 +130,7 @@ final class Mbstring
public static function mb_decode_mimeheader($s)
{
return \iconv_mime_decode($s, 2, self::$internalEncoding);
return iconv_mime_decode($s, 2, self::$internalEncoding);
}
public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
@ -140,7 +140,7 @@ final class Mbstring
public static function mb_decode_numericentity($s, $convmap, $encoding = null)
{
if (null !== $s && !is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);
return null;
@ -150,7 +150,7 @@ final class Mbstring
return false;
}
if (null !== $encoding && !is_scalar($encoding)) {
if (null !== $encoding && !\is_scalar($encoding)) {
trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);
return ''; // Instead of null (cf. mb_encode_numericentity).
@ -166,10 +166,10 @@ final class Mbstring
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s);
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = \iconv($encoding, 'UTF-8//IGNORE', $s);
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}
$cnt = floor(\count($convmap) / 4) * 4;
@ -195,12 +195,12 @@ final class Mbstring
return $s;
}
return \iconv('UTF-8', $encoding.'//IGNORE', $s);
return iconv('UTF-8', $encoding.'//IGNORE', $s);
}
public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
{
if (null !== $s && !is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);
return null;
@ -210,13 +210,13 @@ final class Mbstring
return false;
}
if (null !== $encoding && !is_scalar($encoding)) {
if (null !== $encoding && !\is_scalar($encoding)) {
trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);
return null; // Instead of '' (cf. mb_decode_numericentity).
}
if (null !== $is_hex && !is_scalar($is_hex)) {
if (null !== $is_hex && !\is_scalar($is_hex)) {
trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING);
return null;
@ -232,10 +232,10 @@ final class Mbstring
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s);
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = \iconv($encoding, 'UTF-8//IGNORE', $s);
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}
static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
@ -265,7 +265,7 @@ final class Mbstring
return $result;
}
return \iconv('UTF-8', $encoding.'//IGNORE', $result);
return iconv('UTF-8', $encoding.'//IGNORE', $result);
}
public static function mb_convert_case($s, $mode, $encoding = null)
@ -280,10 +280,10 @@ final class Mbstring
if ('UTF-8' === $encoding) {
$encoding = null;
if (!preg_match('//u', $s)) {
$s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s);
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
}
} else {
$s = \iconv($encoding, 'UTF-8//IGNORE', $s);
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}
if (\MB_CASE_TITLE == $mode) {
@ -301,7 +301,11 @@ final class Mbstring
$map = $upper;
} else {
if (self::MB_CASE_FOLD === $mode) {
$s = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $s);
static $caseFolding = null;
if (null === $caseFolding) {
$caseFolding = self::getData('caseFolding');
}
$s = strtr($s, $caseFolding);
}
static $lower = null;
@ -343,7 +347,7 @@ final class Mbstring
return $s;
}
return \iconv('UTF-8', $encoding.'//IGNORE', $s);
return iconv('UTF-8', $encoding.'//IGNORE', $s);
}
public static function mb_internal_encoding($encoding = null)
@ -354,7 +358,7 @@ final class Mbstring
$normalizedEncoding = self::getEncoding($encoding);
if ('UTF-8' === $normalizedEncoding || false !== @\iconv($normalizedEncoding, $normalizedEncoding, ' ')) {
if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) {
self::$internalEncoding = $normalizedEncoding;
return true;
@ -406,6 +410,12 @@ final class Mbstring
public static function mb_check_encoding($var = null, $encoding = null)
{
if (PHP_VERSION_ID < 70200 && \is_array($var)) {
trigger_error('mb_check_encoding() expects parameter 1 to be string, array given', \E_USER_WARNING);
return null;
}
if (null === $encoding) {
if (null === $var) {
return false;
@ -413,7 +423,21 @@ final class Mbstring
$encoding = self::$internalEncoding;
}
return self::mb_detect_encoding($var, [$encoding]) || false !== @\iconv($encoding, $encoding, $var);
if (!\is_array($var)) {
return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var);
}
foreach ($var as $key => $value) {
if (!self::mb_check_encoding($key, $encoding)) {
return false;
}
if (!self::mb_check_encoding($value, $encoding)) {
return false;
}
}
return true;
}
public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
@ -488,7 +512,7 @@ final class Mbstring
return \strlen($s);
}
return @\iconv_strlen($s, $encoding);
return @iconv_strlen($s, $encoding);
}
public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
@ -509,7 +533,7 @@ final class Mbstring
return 0;
}
return \iconv_strpos($haystack, $needle, $offset, $encoding);
return iconv_strpos($haystack, $needle, $offset, $encoding);
}
public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
@ -533,7 +557,7 @@ final class Mbstring
}
$pos = '' !== $needle || 80000 > \PHP_VERSION_ID
? \iconv_strrpos($haystack, $needle, $encoding)
? iconv_strrpos($haystack, $needle, $encoding)
: self::mb_strlen($haystack, $encoding);
return false !== $pos ? $offset + $pos : false;
@ -541,7 +565,7 @@ final class Mbstring
public static function mb_str_split($string, $split_length = 1, $encoding = null)
{
if (null !== $string && !is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) {
if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) {
trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING);
return null;
@ -550,6 +574,7 @@ final class Mbstring
if (1 > $split_length = (int) $split_length) {
if (80000 > \PHP_VERSION_ID) {
trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING);
return false;
}
@ -617,7 +642,7 @@ final class Mbstring
}
if ($start < 0) {
$start = \iconv_strlen($s, $encoding) + $start;
$start = iconv_strlen($s, $encoding) + $start;
if ($start < 0) {
$start = 0;
}
@ -626,19 +651,21 @@ final class Mbstring
if (null === $length) {
$length = 2147483647;
} elseif ($length < 0) {
$length = \iconv_strlen($s, $encoding) + $length - $start;
$length = iconv_strlen($s, $encoding) + $length - $start;
if ($length < 0) {
return '';
}
}
return (string) \iconv_substr($s, $start, $length, $encoding);
return (string) iconv_substr($s, $start, $length, $encoding);
}
public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
{
$haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
$needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
[$haystack, $needle] = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], [
self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding),
self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding),
]);
return self::mb_strpos($haystack, $needle, $offset, $encoding);
}
@ -657,7 +684,7 @@ final class Mbstring
$pos = strrpos($haystack, $needle);
} else {
$needle = self::mb_substr($needle, 0, 1, $encoding);
$pos = \iconv_strrpos($haystack, $needle, $encoding);
$pos = iconv_strrpos($haystack, $needle, $encoding);
}
return self::getSubpart($pos, $part, $haystack, $encoding);
@ -673,8 +700,11 @@ final class Mbstring
public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
{
$haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
$needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
$haystack = self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding);
$needle = self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding);
$haystack = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $haystack);
$needle = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $needle);
return self::mb_strrpos($haystack, $needle, $offset, $encoding);
}
@ -736,12 +766,12 @@ final class Mbstring
$encoding = self::getEncoding($encoding);
if ('UTF-8' !== $encoding) {
$s = \iconv($encoding, 'UTF-8//IGNORE', $s);
$s = iconv($encoding, 'UTF-8//IGNORE', $s);
}
$s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
return ($wide << 1) + \iconv_strlen($s, 'UTF-8');
return ($wide << 1) + iconv_strlen($s, 'UTF-8');
}
public static function mb_substr_count($haystack, $needle, $encoding = null)
@ -797,6 +827,50 @@ final class Mbstring
return $code;
}
public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, string $encoding = null): string
{
if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH');
}
if (null === $encoding) {
$encoding = self::mb_internal_encoding();
}
try {
$validEncoding = @self::mb_check_encoding('', $encoding);
} catch (\ValueError $e) {
throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding));
}
// BC for PHP 7.3 and lower
if (!$validEncoding) {
throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding));
}
if (self::mb_strlen($pad_string, $encoding) <= 0) {
throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string');
}
$paddingRequired = $length - self::mb_strlen($string, $encoding);
if ($paddingRequired < 1) {
return $string;
}
switch ($pad_type) {
case \STR_PAD_LEFT:
return self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding).$string;
case \STR_PAD_RIGHT:
return $string.self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding);
default:
$leftPaddingLength = floor($paddingRequired / 2);
$rightPaddingLength = $paddingRequired - $leftPaddingLength;
return self::mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding).$string.self::mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding);
}
}
private static function getSubpart($pos, $part, $haystack, $encoding)
{
if (false === $pos) {

View File

@ -0,0 +1,119 @@
<?php
return [
'İ' => 'i̇',
'µ' => 'μ',
'ſ' => 's',
'ͅ' => 'ι',
'ς' => 'σ',
'ϐ' => 'β',
'ϑ' => 'θ',
'ϕ' => 'φ',
'ϖ' => 'π',
'ϰ' => 'κ',
'ϱ' => 'ρ',
'ϵ' => 'ε',
'ẛ' => 'ṡ',
'' => 'ι',
'ß' => 'ss',
'ʼn' => 'ʼn',
'ǰ' => 'ǰ',
'ΐ' => 'ΐ',
'ΰ' => 'ΰ',
'և' => 'եւ',
'ẖ' => 'ẖ',
'ẗ' => 'ẗ',
'ẘ' => 'ẘ',
'ẙ' => 'ẙ',
'ẚ' => 'aʾ',
'ẞ' => 'ss',
'ὐ' => 'ὐ',
'ὒ' => 'ὒ',
'ὔ' => 'ὔ',
'ὖ' => 'ὖ',
'ᾀ' => 'ἀι',
'ᾁ' => 'ἁι',
'ᾂ' => 'ἂι',
'ᾃ' => 'ἃι',
'ᾄ' => 'ἄι',
'ᾅ' => 'ἅι',
'ᾆ' => 'ἆι',
'ᾇ' => 'ἇι',
'ᾈ' => 'ἀι',
'ᾉ' => 'ἁι',
'ᾊ' => 'ἂι',
'ᾋ' => 'ἃι',
'ᾌ' => 'ἄι',
'ᾍ' => 'ἅι',
'ᾎ' => 'ἆι',
'ᾏ' => 'ἇι',
'ᾐ' => 'ἠι',
'ᾑ' => 'ἡι',
'ᾒ' => 'ἢι',
'ᾓ' => 'ἣι',
'ᾔ' => 'ἤι',
'ᾕ' => 'ἥι',
'ᾖ' => 'ἦι',
'ᾗ' => 'ἧι',
'ᾘ' => 'ἠι',
'ᾙ' => 'ἡι',
'ᾚ' => 'ἢι',
'ᾛ' => 'ἣι',
'ᾜ' => 'ἤι',
'ᾝ' => 'ἥι',
'ᾞ' => 'ἦι',
'ᾟ' => 'ἧι',
'ᾠ' => 'ὠι',
'ᾡ' => 'ὡι',
'ᾢ' => 'ὢι',
'ᾣ' => 'ὣι',
'ᾤ' => 'ὤι',
'ᾥ' => 'ὥι',
'ᾦ' => 'ὦι',
'ᾧ' => 'ὧι',
'ᾨ' => 'ὠι',
'ᾩ' => 'ὡι',
'ᾪ' => 'ὢι',
'ᾫ' => 'ὣι',
'ᾬ' => 'ὤι',
'ᾭ' => 'ὥι',
'ᾮ' => 'ὦι',
'ᾯ' => 'ὧι',
'ᾲ' => 'ὰι',
'ᾳ' => 'αι',
'ᾴ' => 'άι',
'ᾶ' => 'ᾶ',
'ᾷ' => 'ᾶι',
'ᾼ' => 'αι',
'ῂ' => 'ὴι',
'ῃ' => 'ηι',
'ῄ' => 'ήι',
'ῆ' => 'ῆ',
'ῇ' => 'ῆι',
'ῌ' => 'ηι',
'ῒ' => 'ῒ',
'ῖ' => 'ῖ',
'ῗ' => 'ῗ',
'ῢ' => 'ῢ',
'ῤ' => 'ῤ',
'ῦ' => 'ῦ',
'ῧ' => 'ῧ',
'ῲ' => 'ὼι',
'ῳ' => 'ωι',
'ῴ' => 'ώι',
'ῶ' => 'ῶ',
'ῷ' => 'ῶι',
'ῼ' => 'ωι',
'ff' => 'ff',
'fi' => 'fi',
'fl' => 'fl',
'ffi' => 'ffi',
'ffl' => 'ffl',
'ſt' => 'st',
'st' => 'st',
'ﬓ' => 'մն',
'ﬔ' => 'մե',
'ﬕ' => 'մի',
'ﬖ' => 'վն',
'ﬗ' => 'մխ',
];

View File

@ -132,6 +132,10 @@ if (!function_exists('mb_str_split')) {
function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); }
}
if (!function_exists('mb_str_pad')) {
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
}
if (extension_loaded('mbstring')) {
return;
}

Some files were not shown because too many files have changed in this diff Show More