diff --git a/.circleci/config.yml b/.circleci/config.yml index 6fa41ab98b..3673f29275 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,13 +18,13 @@ test_74_steps: &test74steps steps: - checkout - run: cp .docker/zz-php.ini /usr/local/etc/php/conf.d/ - - run: composer install -n + - run: composer -n install - run: mkdir -p /tmp/results - - run: composer lint - - run: composer unit -- --log-junit /tmp/results/unit.junit.xml - - run: composer functional -- --log-junit /tmp/results/functional.junit.xml + - run: composer -n lint + - run: composer -n unit -- --log-junit /tmp/results/unit.junit.xml + - run: composer -n functional -- --log-junit /tmp/results/functional.junit.xml # @todo was getting missing key_value table failure when this comes before functional. See https://circleci.com/gh/drush-ops/drush/8828. - - run: composer integration -- --log-junit /tmp/results/integration.junit.xml + - run: composer -n integration -- --log-junit /tmp/results/integration.junit.xml - store_test_results: path: /tmp/results - store_artifacts: @@ -42,8 +42,8 @@ jobs: steps: - checkout - run: cp .docker/zz-php.ini /usr/local/etc/php/conf.d/ - - run: composer install -n - - run: composer cs + - run: composer -n install + - run: composer -n cs # Mergeable test: # FAIL if merging test branch with 11.x produces conflicts @@ -73,7 +73,7 @@ jobs: docker: - image: wodby/php:7.4 environment: - - UNISH_DB_URL=sqlite://sut/sites/dev/files/.ht.sqlite + - "UNISH_DB_URL=sqlite://:memory:" <<: *test74steps test_74_drupal9_postgres: @@ -89,8 +89,8 @@ jobs: POSTGRES_USER: unish <<: *test74steps - # Drupal 9.1.0 for Security test coverage for drupal/core - test_74_drupal91_security: + # Drupal 9.2.8 for Security test coverage for drupal/core + test_74_drupal92_security: <<: *defaults docker: - image: wodby/php:7.4 @@ -101,19 +101,18 @@ jobs: steps: - checkout - run: cp .docker/zz-php.ini /usr/local/etc/php/conf.d/ - - run: composer require --dev drupal/core-recommended:9.1.0 --no-update - - run: composer require symfony/polyfill-php80:"1.23 as 1.20" --no-update + - run: composer -n require --dev drupal/core-recommended:9.2.8 --no-update + - run: composer -n require symfony/polyfill-php80:"1.23 as 1.20" --no-update - run: php --version - - run: composer update - - run: composer phpunit -- --testsuite integration --filter=testInsecureDrupalPackage --stop-on-skipped + - run: composer -n update + - run: composer -n phpunit -- --testsuite integration --filter=testInsecureDrupalPackage --stop-on-skipped # PHP 8 test with Drupal tip # Determines whether a newer version of a dependency has broken Drush. - test_81_drupal93_highest: + test_81_drupal10_highest: <<: *defaults docker: -# @todo After release change to wodby/php:latest - - image: wodby/php:8.1-rc-dev + - image: wodby/php:latest environment: - MYSQL_HOST=127.0.0.1 - UNISH_DB_URL=mysql://root:@127.0.0.1 @@ -122,14 +121,18 @@ jobs: - checkout - run: cp .docker/zz-php.ini /usr/local/etc/php/conf.d/ - run: php --version - - run: composer config platform.php --unset - - run: composer require --dev drupal/core-recommended:9.3.x-dev --no-update - - run: composer update + - run: composer -n config platform.php --unset + - run: composer -n require --dev drupal/core-recommended:10.0.x-dev --no-update + - run: composer -n update - run: mkdir -p /tmp/results - - run: composer lint - - run: composer unit -- --log-junit /tmp/results/unit.junit.xml - - run: composer functional -- --log-junit /tmp/results/functional.junit.xml - - run: composer integration -- --log-junit /tmp/results/integration.junit.xml + - run: composer -n lint + - run: composer -n unit -- --log-junit /tmp/results/unit.junit.xml + - run: composer -n functional -- --log-junit /tmp/results/functional.junit.xml + - run: composer -n integration -- --log-junit /tmp/results/integration.junit.xml + - store_test_results: + path: /tmp/results + - store_artifacts: + path: /tmp/results workflows: version: 2 @@ -137,11 +140,11 @@ workflows: jobs: - code_style - check_mergable - - test_74_drupal91_security: + - test_74_drupal92_security: <<: *requires - test_74_drupal9_mysql: <<: *requires - - test_81_drupal93_highest: + - test_81_drupal10_highest: <<: *requires - test_74_drupal9_sqlite: <<: *requires diff --git a/.ddev/config.yaml b/.ddev/config.yaml index d25d9b975a..cd4d76057f 100644 --- a/.ddev/config.yaml +++ b/.ddev/config.yaml @@ -17,4 +17,6 @@ composer_version: "" disable_settings_management: true web_environment: - UNISH_DB_URL=mysql://root:root@db +# - "UNISH_DB_URL=sqlite://:memory:" +# - UNISH_DB_URL=pgsql://postgres:@localhost - DRUSH_OPTIONS_URI=$DDEV_PRIMARY_URL diff --git a/.docker/zz-php.ini b/.docker/zz-php.ini index 1b2cb8baa5..10b9fbc77c 100644 --- a/.docker/zz-php.ini +++ b/.docker/zz-php.ini @@ -3,3 +3,4 @@ variables_order = GPCS error_reporting = E_ALL & ~E_DEPRECATED date.timezone = "UTC" sendmail_path = "true" +memory_limit = 256M diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 88bd837261..04b54197ed 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,12 +1,10 @@ name: Build static sites -# Controls when the action will run. Triggers the workflow on push or pull request +# Controls when the action will run. Triggers the workflow on push # events but only for the 11.x branch on: push: branches: [11.x] -# pull_request: -# branches: [11.x] jobs: build: @@ -15,12 +13,15 @@ jobs: steps: - name: Checkout 11.x uses: actions/checkout@v2 + with: + fetch-depth: 0 - name: Checkout gh-pages branch uses: actions/checkout@v2 with: ref: gh-pages path: gh-pages + fetch-depth: 0 - name: Set up Python 3.8 uses: actions/setup-python@v2 @@ -30,7 +31,7 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - pip install mkdocs-material pymdown-extensions git+https://gitlab.com/blacs30/mkdocs-edit-url.git + pip install mkdocs-material mkdocs-git-authors-plugin mkdocs-git-revision-date-localized-plugin pymdown-extensions git+https://gitlab.com/blacs30/mkdocs-edit-url.git - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000..d47c6a0fce --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,36 @@ +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# +# We are always looking for more maintainers! Please submit a couple PRs in an +# area and then submit a PR to add yourself to this file :). +# + +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @global-owner1 and @global-owner2 will be requested for +# review when someone opens a pull request. +* @weitzman @greg-1-anderson + +/src/Commands/Config/ @bircher +/src/Drupal/Commands/config/ @bircher + +/src/Drupal/Commands/core/MigrateRunnerCommands.php @claudiu-cristea +/src/Drupal/Migrate/ @claudiu-cristea + +/src/Drupal/Commands/field/ @DieterHolvoet + +/src/Commands/core/RunServerCommands.php @grugnog + +/includes/batch.inc @jonhattan +/src/Drupal/Commands/core/BatchCommands.php @jonhattan + +/src/Drupal/Commands/core/CLICommands.php @damiankloip +/src/Psysh @damiankloip + +/src/Commands/core/UpdateDBCommands.php @pfrenssen + +/src/Drupal/Commands/core/LocaleCommands.php @Sutharsan + +/src/Drupal/Commands/core/QueueCommands.php @davereid + + diff --git a/appveyor.yml b/appveyor.yml index a7833b904f..d2aecb76d1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,17 +20,12 @@ init: # Inspired by https://github.com/Codeception/base/blob/master/appveyor.yml and https://github.com/phpmd/phpmd/blob/master/appveyor.yml install: - ps: Set-Service wuauserv -StartupType Manual - - ps: appveyor-retry cinst -y curl - - SET PATH=C:\Program Files\curl;%PATH% - #which is only needed by the test suite. - - cinst --limit-output -y which - - SET PATH=C:\Program Files\which;%PATH% - git clone -q https://github.com/acquia/DevDesktopCommon.git #For tar, cksum, ... - SET PATH=%APPVEYOR_BUILD_FOLDER%/DevDesktopCommon/bintools-win/msys/bin;%PATH% - SET PATH=C:\Program Files\MySql\MySQL Server 5.7\bin\;%PATH% - choco search php --exact --all-versions -r #Install PHP per https://blog.wyrihaximus.net/2016/11/running-php-unit-tests-on-windows-using-appveyor-and-chocolatey/ - - ps: appveyor-retry cinst --limit-output --ignore-checksums -y php --version 7.4.26 + - ps: appveyor-retry cinst --limit-output --ignore-checksums -y php --version 7.4.27 - cd c:\tools\php74 - copy php.ini-production php.ini diff --git a/composer.json b/composer.json index b3116b8fc8..be126d8923 100644 --- a/composer.json +++ b/composer.json @@ -32,25 +32,24 @@ "require": { "php": ">=7.4", "ext-dom": "*", - "chi-teck/drupal-code-generator": "^2.3", + "chi-teck/drupal-code-generator": "^2.4", "composer/semver": "^1.4 || ^3", - "consolidation/annotated-command": "^4.4", + "consolidation/annotated-command": "^4.5", "consolidation/config": "^1.2", "consolidation/filter-via-dot-access-data": "^1", "consolidation/robo": "^3", - "consolidation/site-alias": "^3.0.0@stable", - "consolidation/site-process": "^4", + "consolidation/site-alias": "^3.1.3", + "consolidation/site-process": "^4.1.3", "enlightn/security-checker": "^1", - "grasmash/yaml-expander": "^1.1.1", "guzzlehttp/guzzle": "^6.3 || ^7.0", "league/container": "^3.4", "psr/log": "~1.0", "psy/psysh": "~0.11", - "symfony/event-dispatcher": "^4.0", - "symfony/finder": "^4.0 || ^5", + "symfony/event-dispatcher": "^4.0 || ^5.0 || ^6.0", + "symfony/finder": "^4.0 || ^5 || ^6", "symfony/polyfill-php80": "^1.23", - "symfony/var-dumper": "^4.0 || ^5.0", - "symfony/yaml": "^4.0", + "symfony/var-dumper": "^4.0 || ^5.0 || ^6.0", + "symfony/yaml": "^4.0 || ^5.0 || ^6.0", "webflo/drupal-finder": "^1.2", "webmozart/path-util": "^2.1.0" }, @@ -58,23 +57,22 @@ "composer/installers": "^1.7", "cweagans/composer-patches": "~1.0", "david-garcia/phpwhois": "4.3.0", - "drupal/core-recommended": "^9", - "drupal/semver_example": "2.2.0", + "drupal/core-recommended": "^9 || ^10", + "drupal/semver_example": "2.3.0", "phpunit/phpunit": ">=7.5.20", - "rector/rector": "^0.11.58", - "squizlabs/php_codesniffer": "^2.7 || ^3", + "rector/rector": "^0.12", + "squizlabs/php_codesniffer": "^3.6", "vlucas/phpdotenv": "^2.4", "yoast/phpunit-polyfills": "^0.2.0" }, "conflict": { - "drupal/core": "<= 8", + "drupal/core": "< 9.2", "drupal/migrate_run": "*", "drupal/migrate_tools": "<= 5" }, "autoload": { "psr-4": { - "Drush\\": "src/", - "Drush\\Internal\\": "src/internal-forks" + "Drush\\": "src/" } }, "autoload-dev": { @@ -88,6 +86,10 @@ "files": ["tests/load.environment.php"] }, "config": { + "allow-plugins": { + "composer/installers": true, + "cweagans/composer-patches": true + }, "optimize-autoloader": true, "preferred-install": "dist", "sort-packages": true, @@ -112,7 +114,7 @@ "api": "php $HOME/bin/doctum.phar --ansi --ignore-parse-errors update doctum-config.php", "doctum-install": "mkdir -p $HOME/bin && curl --output $HOME/bin/doctum.phar https://doctum.long-term.support/releases/latest/doctum.phar && chmod +x $HOME/bin/doctum.phar", "mk:docs": "./drush --uri=dev -v mk:docs", - "rector": "rector process src/Boot src/Preflight src/Runtime src/SiteAlias src/Symfony src/Config", + "rector": "rector process", "sut": "./drush --uri=dev", "sut:si": "./drush --uri=dev site:install testing --sites-subdir=dev --db-url=${UNISH_DB_URL:-mysql://root:password@mariadb}/unish_dev -v", "phpunit": "php -d sendmail_path='true' vendor/bin/phpunit --colors=always --configuration tests", diff --git a/composer.lock b/composer.lock index 28f3781f0c..0deb693837 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "87e752a74905a62d75a08cf4bf0a8954", + "content-hash": "7e54876426a30fe150ea012e9d53de25", "packages": [ { "name": "chi-teck/drupal-code-generator", - "version": "2.3.0", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/Chi-teck/drupal-code-generator.git", - "reference": "3508f0bb67ba905d6c0c59d1b51e7bc78876ad71" + "reference": "872086aeeee1c933adc403d98c35dc029e26ae7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Chi-teck/drupal-code-generator/zipball/3508f0bb67ba905d6c0c59d1b51e7bc78876ad71", - "reference": "3508f0bb67ba905d6c0c59d1b51e7bc78876ad71", + "url": "https://api.github.com/repos/Chi-teck/drupal-code-generator/zipball/872086aeeee1c933adc403d98c35dc029e26ae7c", + "reference": "872086aeeee1c933adc403d98c35dc029e26ae7c", "shasum": "" }, "require": { @@ -64,22 +64,22 @@ "description": "Drupal code generator", "support": { "issues": "https://github.com/Chi-teck/drupal-code-generator/issues", - "source": "https://github.com/Chi-teck/drupal-code-generator/tree/2.3.0" + "source": "https://github.com/Chi-teck/drupal-code-generator/tree/2.4.0" }, - "time": "2021-11-24T08:04:06+00:00" + "time": "2022-01-18T04:19:55+00:00" }, { "name": "composer/semver", - "version": "3.2.5", + "version": "3.2.6", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9" + "reference": "83e511e247de329283478496f7a1e114c9517506" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/31f3ea725711245195f62e54ffa402d8ef2fdba9", - "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9", + "url": "https://api.github.com/repos/composer/semver/zipball/83e511e247de329283478496f7a1e114c9517506", + "reference": "83e511e247de329283478496f7a1e114c9517506", "shasum": "" }, "require": { @@ -131,7 +131,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.2.5" + "source": "https://github.com/composer/semver/tree/3.2.6" }, "funding": [ { @@ -147,31 +147,32 @@ "type": "tidelift" } ], - "time": "2021-05-24T12:41:47+00:00" + "time": "2021-10-25T11:34:17+00:00" }, { "name": "consolidation/annotated-command", - "version": "4.4.0", + "version": "4.5.1", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "308f6ac178566a1ce9aa90ed908dac90a2c1e707" + "reference": "701a7abe8505abe89520837be798e15a3953a367" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/308f6ac178566a1ce9aa90ed908dac90a2c1e707", - "reference": "308f6ac178566a1ce9aa90ed908dac90a2c1e707", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/701a7abe8505abe89520837be798e15a3953a367", + "reference": "701a7abe8505abe89520837be798e15a3953a367", "shasum": "" }, "require": { "consolidation/output-formatters": "^4.1.1", "php": ">=7.1.3", "psr/log": "^1|^2", - "symfony/console": "^4.4.8|~5.1.0", - "symfony/event-dispatcher": "^4.4.8|^5", - "symfony/finder": "^4.4.8|^5" + "symfony/console": "^4.4.8|^5|^6", + "symfony/event-dispatcher": "^4.4.8|^5|^6", + "symfony/finder": "^4.4.8|^5|^6" }, "require-dev": { + "composer-runtime-api": "^2.0", "phpunit/phpunit": "^7.5.20 || ^8 || ^9", "squizlabs/php_codesniffer": "^3", "yoast/phpunit-polyfills": "^0.2.0" @@ -200,9 +201,9 @@ "description": "Initialize Symfony Console commands from annotated command class methods.", "support": { "issues": "https://github.com/consolidation/annotated-command/issues", - "source": "https://github.com/consolidation/annotated-command/tree/4.4.0" + "source": "https://github.com/consolidation/annotated-command/tree/4.5.1" }, - "time": "2021-09-30T01:08:15+00:00" + "time": "2021-12-30T04:00:37+00:00" }, { "name": "consolidation/config", @@ -361,22 +362,22 @@ }, { "name": "consolidation/log", - "version": "2.0.2", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/consolidation/log.git", - "reference": "82a2aaaa621a7b976e50a745a8d249d5085ee2b1" + "reference": "fc9ec5476ba13a31778695bd2d4f2fa0b0684356" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/82a2aaaa621a7b976e50a745a8d249d5085ee2b1", - "reference": "82a2aaaa621a7b976e50a745a8d249d5085ee2b1", + "url": "https://api.github.com/repos/consolidation/log/zipball/fc9ec5476ba13a31778695bd2d4f2fa0b0684356", + "reference": "fc9ec5476ba13a31778695bd2d4f2fa0b0684356", "shasum": "" }, "require": { "php": ">=7.1.3", "psr/log": "^1.0", - "symfony/console": "^4|^5" + "symfony/console": "^4 || ^5 || ^6" }, "require-dev": { "phpunit/phpunit": ">=7.5.20", @@ -407,36 +408,36 @@ "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", "support": { "issues": "https://github.com/consolidation/log/issues", - "source": "https://github.com/consolidation/log/tree/2.0.2" + "source": "https://github.com/consolidation/log/tree/2.0.4" }, - "time": "2020-12-10T16:26:23+00:00" + "time": "2021-12-30T19:05:18+00:00" }, { "name": "consolidation/output-formatters", - "version": "4.1.2", + "version": "4.2.1", "source": { "type": "git", "url": "https://github.com/consolidation/output-formatters.git", - "reference": "5821e6ae076bf690058a4de6c94dce97398a69c9" + "reference": "4413d7c732afb5d7bdac565c41aa9c8c49c48888" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/5821e6ae076bf690058a4de6c94dce97398a69c9", - "reference": "5821e6ae076bf690058a4de6c94dce97398a69c9", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/4413d7c732afb5d7bdac565c41aa9c8c49c48888", + "reference": "4413d7c732afb5d7bdac565c41aa9c8c49c48888", "shasum": "" }, "require": { "dflydev/dot-access-data": "^1.1.0", "php": ">=7.1.3", - "symfony/console": "^4|^5", - "symfony/finder": "^4|^5" + "symfony/console": "^4|^5|^6", + "symfony/finder": "^4|^5|^6" }, "require-dev": { "php-coveralls/php-coveralls": "^2.4.2", "phpunit/phpunit": ">=7", "squizlabs/php_codesniffer": "^3", - "symfony/var-dumper": "^4", - "symfony/yaml": "^4", + "symfony/var-dumper": "^4|^5|^6", + "symfony/yaml": "^4|^5|^6", "yoast/phpunit-polyfills": "^0.2.0" }, "suggest": { @@ -466,30 +467,30 @@ "description": "Format text by applying transformations provided by plug-in formatters.", "support": { "issues": "https://github.com/consolidation/output-formatters/issues", - "source": "https://github.com/consolidation/output-formatters/tree/4.1.2" + "source": "https://github.com/consolidation/output-formatters/tree/4.2.1" }, - "time": "2020-12-12T19:04:59+00:00" + "time": "2021-12-30T03:58:00+00:00" }, { "name": "consolidation/robo", - "version": "3.0.5", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "a9ce34c3d0fb3e8619fc4a6f72c51678caf1b99b" + "reference": "36dce2965a67abe5cf91f2bc36d2582a64a11258" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/a9ce34c3d0fb3e8619fc4a6f72c51678caf1b99b", - "reference": "a9ce34c3d0fb3e8619fc4a6f72c51678caf1b99b", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/36dce2965a67abe5cf91f2bc36d2582a64a11258", + "reference": "36dce2965a67abe5cf91f2bc36d2582a64a11258", "shasum": "" }, "require": { - "consolidation/annotated-command": "^4.2.4", + "consolidation/annotated-command": "^4.3", "consolidation/config": "^1.2.1|^2.0.1", "consolidation/log": "^1.1.1|^2.0.2", "consolidation/output-formatters": "^4.1.2", - "consolidation/self-update": "^1.2", + "consolidation/self-update": "^2.0", "league/container": "^3.3.1", "php": ">=7.1.3", "symfony/console": "^4.4.19 || ^5", @@ -507,7 +508,7 @@ "patchwork/jsqueeze": "^2", "pear/archive_tar": "^1.4.4", "phpunit/phpunit": "^7.5.20 | ^8", - "squizlabs/php_codesniffer": "^3", + "squizlabs/php_codesniffer": "^3.6", "yoast/phpunit-polyfills": "^0.2.0" }, "suggest": { @@ -565,28 +566,29 @@ "description": "Modern task runner", "support": { "issues": "https://github.com/consolidation/Robo/issues", - "source": "https://github.com/consolidation/Robo/tree/3.0.5" + "source": "https://github.com/consolidation/Robo/tree/3.0.6" }, - "time": "2021-08-29T21:11:31+00:00" + "time": "2021-10-05T23:56:45+00:00" }, { "name": "consolidation/self-update", - "version": "1.2.0", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/consolidation/self-update.git", - "reference": "dba6b2c0708f20fa3ba8008a2353b637578849b4" + "reference": "117dcc9494dc970a6ae307103c41d654e6253bc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/self-update/zipball/dba6b2c0708f20fa3ba8008a2353b637578849b4", - "reference": "dba6b2c0708f20fa3ba8008a2353b637578849b4", + "url": "https://api.github.com/repos/consolidation/self-update/zipball/117dcc9494dc970a6ae307103c41d654e6253bc4", + "reference": "117dcc9494dc970a6ae307103c41d654e6253bc4", "shasum": "" }, "require": { + "composer/semver": "^3.2", "php": ">=5.5.0", - "symfony/console": "^2.8|^3|^4|^5", - "symfony/filesystem": "^2.5|^3|^4|^5" + "symfony/console": "^2.8 || ^3 || ^4 || ^5 || ^6", + "symfony/filesystem": "^2.5 || ^3 || ^4 || ^5 || ^6" }, "bin": [ "scripts/release" @@ -594,7 +596,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "2.x-dev" } }, "autoload": { @@ -619,22 +621,22 @@ "description": "Provides a self:update command for Symfony Console applications.", "support": { "issues": "https://github.com/consolidation/self-update/issues", - "source": "https://github.com/consolidation/self-update/tree/1.2.0" + "source": "https://github.com/consolidation/self-update/tree/2.0.3" }, - "time": "2020-04-13T02:49:20+00:00" + "time": "2021-12-30T19:08:32+00:00" }, { "name": "consolidation/site-alias", - "version": "3.1.1", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/consolidation/site-alias.git", - "reference": "e824b57253d9174f4a500f87e6d0e1e497c2a50a" + "reference": "e2784362e98f315c996fb2b9ed80a9118a0ba8b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/site-alias/zipball/e824b57253d9174f4a500f87e6d0e1e497c2a50a", - "reference": "e824b57253d9174f4a500f87e6d0e1e497c2a50a", + "url": "https://api.github.com/repos/consolidation/site-alias/zipball/e2784362e98f315c996fb2b9ed80a9118a0ba8b7", + "reference": "e2784362e98f315c996fb2b9ed80a9118a0ba8b7", "shasum": "" }, "require": { @@ -677,22 +679,22 @@ "description": "Manage alias records for local and remote sites.", "support": { "issues": "https://github.com/consolidation/site-alias/issues", - "source": "https://github.com/consolidation/site-alias/tree/3.1.1" + "source": "https://github.com/consolidation/site-alias/tree/3.1.3" }, - "time": "2021-09-21T00:30:48+00:00" + "time": "2022-01-03T19:00:28+00:00" }, { "name": "consolidation/site-process", - "version": "4.1.0", + "version": "4.1.3", "source": { "type": "git", "url": "https://github.com/consolidation/site-process.git", - "reference": "ef57711d7049f7606ce936ded16ad93f1ad7f02c" + "reference": "ca41dc82b280bccdf1b231d5599c7d506fba5c04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/site-process/zipball/ef57711d7049f7606ce936ded16ad93f1ad7f02c", - "reference": "ef57711d7049f7606ce936ded16ad93f1ad7f02c", + "url": "https://api.github.com/repos/consolidation/site-process/zipball/ca41dc82b280bccdf1b231d5599c7d506fba5c04", + "reference": "ca41dc82b280bccdf1b231d5599c7d506fba5c04", "shasum": "" }, "require": { @@ -700,7 +702,7 @@ "consolidation/site-alias": "^3", "php": ">=7.1.3", "symfony/console": "^2.8.52|^3|^4.4|^5", - "symfony/process": "^4.3.4" + "symfony/process": "^4.3.4|^5" }, "require-dev": { "phpunit/phpunit": "^7.5.20|^8.5.14", @@ -735,9 +737,9 @@ "description": "A thin wrapper around the Symfony Process Component that allows applications to use the Site Alias library to specify the target for a remote call.", "support": { "issues": "https://github.com/consolidation/site-process/issues", - "source": "https://github.com/consolidation/site-process/tree/4.1.0" + "source": "https://github.com/consolidation/site-process/tree/4.1.3" }, - "time": "2021-02-21T02:53:33+00:00" + "time": "2022-01-18T23:04:54+00:00" }, { "name": "dflydev/dot-access-data", @@ -919,58 +921,6 @@ }, "time": "2017-12-21T22:14:55+00:00" }, - { - "name": "grasmash/yaml-expander", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/yaml-expander.git", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/yaml-expander/zipball/3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4.8|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\YamlExpander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in a yaml file.", - "support": { - "issues": "https://github.com/grasmash/yaml-expander/issues", - "source": "https://github.com/grasmash/yaml-expander/tree/master" - }, - "time": "2017-12-16T16:06:03+00:00" - }, { "name": "guzzlehttp/guzzle", "version": "6.5.5", @@ -1044,16 +994,16 @@ }, { "name": "guzzlehttp/promises", - "version": "1.4.1", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", "shasum": "" }, "require": { @@ -1065,7 +1015,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { @@ -1081,10 +1031,25 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle promises library", @@ -1093,22 +1058,36 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.1" + "source": "https://github.com/guzzle/promises/tree/1.5.1" }, - "time": "2021-03-07T09:25:29+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:56:57+00:00" }, { "name": "guzzlehttp/psr7", - "version": "1.8.2", + "version": "1.8.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91" + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", "shasum": "" }, "require": { @@ -1145,13 +1124,34 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, { "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" } ], @@ -1168,9 +1168,23 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.2" + "source": "https://github.com/guzzle/psr7/tree/1.8.3" }, - "time": "2021-04-26T09:17:50+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2021-10-05T13:56:00+00:00" }, { "name": "league/container", @@ -1253,16 +1267,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.13.0", + "version": "v4.13.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "50953a2691a922aa1769461637869a0a2faa3f53" + "reference": "210577fe3cf7badcc5814d99455df46564f3c077" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53", - "reference": "50953a2691a922aa1769461637869a0a2faa3f53", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", + "reference": "210577fe3cf7badcc5814d99455df46564f3c077", "shasum": "" }, "require": { @@ -1303,9 +1317,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" }, - "time": "2021-09-20T12:20:58+00:00" + "time": "2021-11-30T19:35:32+00:00" }, { "name": "psr/container", @@ -1579,16 +1593,16 @@ }, { "name": "symfony/console", - "version": "v4.4.30", + "version": "v4.4.34", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a3f7189a0665ee33b50e9e228c46f50f5acbed22" + "reference": "329b3a75cc6b16d435ba1b1a41df54a53382a3f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a3f7189a0665ee33b50e9e228c46f50f5acbed22", - "reference": "a3f7189a0665ee33b50e9e228c46f50f5acbed22", + "url": "https://api.github.com/repos/symfony/console/zipball/329b3a75cc6b16d435ba1b1a41df54a53382a3f0", + "reference": "329b3a75cc6b16d435ba1b1a41df54a53382a3f0", "shasum": "" }, "require": { @@ -1649,7 +1663,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.30" + "source": "https://github.com/symfony/console/tree/v4.4.34" }, "funding": [ { @@ -1665,20 +1679,87 @@ "type": "tidelift" } ], - "time": "2021-08-25T19:27:26+00:00" + "time": "2021-11-04T12:23:33+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-12T14:48:14+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.4.30", + "version": "v4.4.34", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "2fe81680070043c4c80e7cedceb797e34f377bac" + "reference": "1a024b45369c9d55d76b6b8a241bd20c9ea1cbd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2fe81680070043c4c80e7cedceb797e34f377bac", - "reference": "2fe81680070043c4c80e7cedceb797e34f377bac", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1a024b45369c9d55d76b6b8a241bd20c9ea1cbd8", + "reference": "1a024b45369c9d55d76b6b8a241bd20c9ea1cbd8", "shasum": "" }, "require": { @@ -1733,7 +1814,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.30" + "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.34" }, "funding": [ { @@ -1749,20 +1830,20 @@ "type": "tidelift" } ], - "time": "2021-08-04T20:31:23+00:00" + "time": "2021-11-15T14:42:25+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v1.1.9", + "version": "v1.1.11", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7" + "reference": "01e9a4efac0ee33a05dfdf93b346f62e7d0e998c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/84e23fdcd2517bf37aecbd16967e83f0caee25a7", - "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/01e9a4efac0ee33a05dfdf93b346f62e7d0e998c", + "reference": "01e9a4efac0ee33a05dfdf93b346f62e7d0e998c", "shasum": "" }, "require": { @@ -1775,7 +1856,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-main": "1.1-dev" }, "thanks": { "name": "symfony/contracts", @@ -1812,7 +1893,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.9" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.11" }, "funding": [ { @@ -1828,25 +1909,26 @@ "type": "tidelift" } ], - "time": "2020-07-06T13:19:58+00:00" + "time": "2021-03-23T15:25:38+00:00" }, { "name": "symfony/filesystem", - "version": "v4.4.27", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "517fb795794faf29086a77d99eb8f35e457837a7" + "reference": "731f917dc31edcffec2c6a777f3698c33bea8f01" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/517fb795794faf29086a77d99eb8f35e457837a7", - "reference": "517fb795794faf29086a77d99eb8f35e457837a7", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/731f917dc31edcffec2c6a777f3698c33bea8f01", + "reference": "731f917dc31edcffec2c6a777f3698c33bea8f01", "shasum": "" }, "require": { - "php": ">=7.1.3", + "php": ">=7.2.5", "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", "symfony/polyfill-php80": "^1.16" }, "type": "library", @@ -1875,7 +1957,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v4.4.27" + "source": "https://github.com/symfony/filesystem/tree/v5.4.0" }, "funding": [ { @@ -1891,24 +1973,25 @@ "type": "tidelift" } ], - "time": "2021-07-21T12:19:41+00:00" + "time": "2021-10-28T13:39:27+00:00" }, { "name": "symfony/finder", - "version": "v5.3.7", + "version": "v5.4.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93" + "reference": "e77046c252be48c48a40816187ed527703c8f76c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/a10000ada1e600d109a6c7632e9ac42e8bf2fb93", - "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93", + "url": "https://api.github.com/repos/symfony/finder/zipball/e77046c252be48c48a40816187ed527703c8f76c", + "reference": "e77046c252be48c48a40816187ed527703c8f76c", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-php80": "^1.16" }, "type": "library", @@ -1937,7 +2020,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.3.7" + "source": "https://github.com/symfony/finder/tree/v5.4.2" }, "funding": [ { @@ -1953,7 +2036,7 @@ "type": "tidelift" } ], - "time": "2021-08-04T21:20:46+00:00" + "time": "2021-12-15T11:06:13+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2036,16 +2119,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.23.1", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "16880ba9c5ebe3642d1995ab866db29270b36535" + "reference": "81b86b50cf841a64252b439e738e97f4a34e2783" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/16880ba9c5ebe3642d1995ab866db29270b36535", - "reference": "16880ba9c5ebe3642d1995ab866db29270b36535", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783", + "reference": "81b86b50cf841a64252b439e738e97f4a34e2783", "shasum": "" }, "require": { @@ -2097,7 +2180,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.1" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.24.0" }, "funding": [ { @@ -2113,7 +2196,7 @@ "type": "tidelift" } ], - "time": "2021-05-27T12:26:48+00:00" + "time": "2021-11-23T21:10:46+00:00" }, { "name": "symfony/polyfill-intl-idn", @@ -2368,7 +2451,7 @@ }, { "name": "symfony/polyfill-php72", - "version": "v1.23.0", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", @@ -2424,7 +2507,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.23.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.24.0" }, "funding": [ { @@ -2444,16 +2527,16 @@ }, { "name": "symfony/polyfill-php73", - "version": "v1.23.0", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", - "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", "shasum": "" }, "require": { @@ -2503,7 +2586,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.24.0" }, "funding": [ { @@ -2519,7 +2602,7 @@ "type": "tidelift" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2021-06-05T21:20:04+00:00" }, { "name": "symfony/polyfill-php80", @@ -2606,16 +2689,16 @@ }, { "name": "symfony/process", - "version": "v4.4.30", + "version": "v4.4.35", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "13d3161ef63a8ec21eeccaaf9a4d7f784a87a97d" + "reference": "c2098705326addae6e6742151dfade47ac71da1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/13d3161ef63a8ec21eeccaaf9a4d7f784a87a97d", - "reference": "13d3161ef63a8ec21eeccaaf9a4d7f784a87a97d", + "url": "https://api.github.com/repos/symfony/process/zipball/c2098705326addae6e6742151dfade47ac71da1b", + "reference": "c2098705326addae6e6742151dfade47ac71da1b", "shasum": "" }, "require": { @@ -2648,7 +2731,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v4.4.30" + "source": "https://github.com/symfony/process/tree/v4.4.35" }, "funding": [ { @@ -2664,25 +2747,29 @@ "type": "tidelift" } ], - "time": "2021-08-04T20:31:23+00:00" + "time": "2021-11-22T22:36:24+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", - "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", "shasum": "" }, "require": { "php": ">=7.2.5", - "psr/container": "^1.1" + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1" + }, + "conflict": { + "ext-psr": "<1.1|>=2" }, "suggest": { "symfony/service-implementation": "" @@ -2690,7 +2777,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -2727,7 +2814,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.4.0" + "source": "https://github.com/symfony/service-contracts/tree/v2.5.0" }, "funding": [ { @@ -2743,20 +2830,20 @@ "type": "tidelift" } ], - "time": "2021-04-01T10:43:52+00:00" + "time": "2021-11-04T16:48:04+00:00" }, { "name": "symfony/string", - "version": "v5.3.7", + "version": "v5.4.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5" + "reference": "e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/8d224396e28d30f81969f083a58763b8b9ceb0a5", - "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5", + "url": "https://api.github.com/repos/symfony/string/zipball/e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d", + "reference": "e6a5d5ecf6589c5247d18e0e74e30b11dfd51a3d", "shasum": "" }, "require": { @@ -2767,11 +2854,14 @@ "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php80": "~1.15" }, + "conflict": { + "symfony/translation-contracts": ">=3.0" + }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0", - "symfony/http-client": "^4.4|^5.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0" + "symfony/var-exporter": "^4.4|^5.0|^6.0" }, "type": "library", "autoload": { @@ -2810,7 +2900,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.3.7" + "source": "https://github.com/symfony/string/tree/v5.4.2" }, "funding": [ { @@ -2826,20 +2916,20 @@ "type": "tidelift" } ], - "time": "2021-08-26T08:00:08+00:00" + "time": "2021-12-16T21:52:00+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.3.8", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "eaaea4098be1c90c8285543e1356a09c8aa5c8da" + "reference": "89ab66eaef230c9cd1992de2e9a1b26652b127b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/eaaea4098be1c90c8285543e1356a09c8aa5c8da", - "reference": "eaaea4098be1c90c8285543e1356a09c8aa5c8da", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/89ab66eaef230c9cd1992de2e9a1b26652b127b9", + "reference": "89ab66eaef230c9cd1992de2e9a1b26652b127b9", "shasum": "" }, "require": { @@ -2853,8 +2943,9 @@ }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", + "symfony/console": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/uid": "^5.1|^6.0", "twig/twig": "^2.13|^3.0.4" }, "suggest": { @@ -2898,7 +2989,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.3.8" + "source": "https://github.com/symfony/var-dumper/tree/v5.4.0" }, "funding": [ { @@ -2914,20 +3005,20 @@ "type": "tidelift" } ], - "time": "2021-09-24T15:59:58+00:00" + "time": "2021-11-29T15:30:56+00:00" }, { "name": "symfony/yaml", - "version": "v4.4.29", + "version": "v4.4.34", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "3abcc4db06d4e776825eaa3ed8ad924d5bc7432a" + "reference": "2c309e258adeb9970229042be39b360d34986fad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/3abcc4db06d4e776825eaa3ed8ad924d5bc7432a", - "reference": "3abcc4db06d4e776825eaa3ed8ad924d5bc7432a", + "url": "https://api.github.com/repos/symfony/yaml/zipball/2c309e258adeb9970229042be39b360d34986fad", + "reference": "2c309e258adeb9970229042be39b360d34986fad", "shasum": "" }, "require": { @@ -2969,7 +3060,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v4.4.29" + "source": "https://github.com/symfony/yaml/tree/v4.4.34" }, "funding": [ { @@ -2985,7 +3076,7 @@ "type": "tidelift" } ], - "time": "2021-07-27T16:19:30+00:00" + "time": "2021-11-18T18:49:23+00:00" }, { "name": "twig/twig", @@ -3216,6 +3307,7 @@ "issues": "https://github.com/webmozart/path-util/issues", "source": "https://github.com/webmozart/path-util/tree/2.3.0" }, + "abandoned": "symfony/filesystem", "time": "2015-12-17T08:42:14+00:00" } ], @@ -3849,16 +3941,16 @@ }, { "name": "drupal/core", - "version": "9.3.x-dev", + "version": "9.3.0", "source": { "type": "git", "url": "https://github.com/drupal/core.git", - "reference": "4d5164bf86cfcde10135505c5966397d000f7e32" + "reference": "1e1bf0e841e11029b21775dd125332d7ffd6fb47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core/zipball/4d5164bf86cfcde10135505c5966397d000f7e32", - "reference": "4d5164bf86cfcde10135505c5966397d000f7e32", + "url": "https://api.github.com/repos/drupal/core/zipball/1e1bf0e841e11029b21775dd125332d7ffd6fb47", + "reference": "1e1bf0e841e11029b21775dd125332d7ffd6fb47", "shasum": "" }, "require": { @@ -3866,7 +3958,7 @@ "composer/semver": "^3.0", "doctrine/annotations": "^1.12", "doctrine/reflection": "^1.1", - "egulias/email-validator": "^2.0", + "egulias/email-validator": "^2.1.22|^3.0", "ext-date": "*", "ext-dom": "*", "ext-filter": "*", @@ -3894,7 +3986,7 @@ "symfony/event-dispatcher": "^4.4", "symfony/http-foundation": "^4.4.7", "symfony/http-kernel": "^4.4", - "symfony/mime": "^5.3.0", + "symfony/mime": "^5.4", "symfony/polyfill-iconv": "^1.0", "symfony/polyfill-php80": "^1.16", "symfony/process": "^4.4", @@ -3923,6 +4015,7 @@ "drupal/book": "self.version", "drupal/breakpoint": "self.version", "drupal/ckeditor": "self.version", + "drupal/ckeditor5": "self.version", "drupal/claro": "self.version", "drupal/classy": "self.version", "drupal/color": "self.version", @@ -4099,39 +4192,39 @@ ], "description": "Drupal is an open source content management platform powering millions of websites and applications.", "support": { - "source": "https://github.com/drupal/core/tree/9.3.x" + "source": "https://github.com/drupal/core/tree/9.3.0" }, - "time": "2021-10-01T12:45:01+00:00" + "time": "2021-12-08T22:09:38+00:00" }, { "name": "drupal/core-recommended", - "version": "9.3.x-dev", + "version": "9.3.0", "source": { "type": "git", "url": "https://github.com/drupal/core-recommended.git", - "reference": "0763c9db8e41012509356c5eb59485a0215808ce" + "reference": "d65aaa36a8cab54332787a20676f81928f675bb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core-recommended/zipball/0763c9db8e41012509356c5eb59485a0215808ce", - "reference": "0763c9db8e41012509356c5eb59485a0215808ce", + "url": "https://api.github.com/repos/drupal/core-recommended/zipball/d65aaa36a8cab54332787a20676f81928f675bb3", + "reference": "d65aaa36a8cab54332787a20676f81928f675bb3", "shasum": "" }, "require": { "asm89/stack-cors": "1.3.0", - "composer/semver": "3.2.5", + "composer/semver": "3.2.6", "doctrine/annotations": "1.13.2", "doctrine/lexer": "1.2.1", "doctrine/reflection": "1.2.2", - "drupal/core": "9.3.x-dev", - "egulias/email-validator": "2.1.25", + "drupal/core": "9.3.0", + "egulias/email-validator": "3.1.2", "guzzlehttp/guzzle": "6.5.5", - "guzzlehttp/promises": "1.4.1", - "guzzlehttp/psr7": "1.8.2", + "guzzlehttp/promises": "1.5.1", + "guzzlehttp/psr7": "1.8.3", "laminas/laminas-diactoros": "2.8.0", "laminas/laminas-escaper": "2.9.0", "laminas/laminas-feed": "2.15.0", - "laminas/laminas-stdlib": "3.6.0", + "laminas/laminas-stdlib": "3.6.1", "masterminds/html5": "2.7.5", "pear/archive_tar": "1.4.14", "pear/console_getopt": "v1.4.3", @@ -4144,34 +4237,34 @@ "psr/log": "1.1.4", "ralouphie/getallheaders": "3.0.3", "stack/builder": "v1.0.6", - "symfony-cmf/routing": "2.3.3", - "symfony/console": "v4.4.30", + "symfony-cmf/routing": "2.3.4", + "symfony/console": "v4.4.34", "symfony/debug": "v4.4.31", - "symfony/dependency-injection": "v4.4.31", - "symfony/deprecation-contracts": "v2.4.0", - "symfony/error-handler": "v4.4.30", - "symfony/event-dispatcher": "v4.4.30", - "symfony/event-dispatcher-contracts": "v1.1.9", - "symfony/http-client-contracts": "v2.4.0", - "symfony/http-foundation": "v4.4.30", - "symfony/http-kernel": "v4.4.32", - "symfony/mime": "v5.3.8", + "symfony/dependency-injection": "v4.4.34", + "symfony/deprecation-contracts": "v2.5.0", + "symfony/error-handler": "v4.4.34", + "symfony/event-dispatcher": "v4.4.34", + "symfony/event-dispatcher-contracts": "v1.1.11", + "symfony/http-client-contracts": "v2.5.0", + "symfony/http-foundation": "v4.4.34", + "symfony/http-kernel": "v4.4.35", + "symfony/mime": "v5.4.0", "symfony/polyfill-ctype": "v1.23.0", "symfony/polyfill-iconv": "v1.23.0", "symfony/polyfill-intl-idn": "v1.23.0", "symfony/polyfill-intl-normalizer": "v1.23.0", "symfony/polyfill-mbstring": "v1.23.1", "symfony/polyfill-php80": "v1.23.1", - "symfony/process": "v4.4.30", - "symfony/psr-http-message-bridge": "v2.1.1", - "symfony/routing": "v4.4.30", - "symfony/serializer": "v4.4.31", - "symfony/service-contracts": "v2.4.0", - "symfony/translation": "v4.4.32", - "symfony/translation-contracts": "v2.4.0", - "symfony/validator": "v4.4.31", - "symfony/var-dumper": "v5.3.8", - "symfony/yaml": "v4.4.29", + "symfony/process": "v4.4.35", + "symfony/psr-http-message-bridge": "v2.1.2", + "symfony/routing": "v4.4.34", + "symfony/serializer": "v4.4.35", + "symfony/service-contracts": "v2.5.0", + "symfony/translation": "v4.4.34", + "symfony/translation-contracts": "v2.5.0", + "symfony/validator": "v4.4.35", + "symfony/var-dumper": "v5.4.0", + "symfony/yaml": "v4.4.34", "twig/twig": "v2.14.7", "typo3/phar-stream-wrapper": "v3.1.7" }, @@ -4185,32 +4278,32 @@ ], "description": "Locked core dependencies; require this project INSTEAD OF drupal/core.", "support": { - "source": "https://github.com/drupal/core-recommended/tree/9.3.x" + "source": "https://github.com/drupal/core-recommended/tree/9.3.0" }, - "time": "2021-09-29T09:09:29+00:00" + "time": "2021-12-08T22:09:38+00:00" }, { "name": "drupal/semver_example", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/semver_example.git", - "reference": "2.2.0" + "reference": "2.3.0" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/semver_example-2.2.0.zip", - "reference": "2.2.0", - "shasum": "8cb04baee5ca950a96ef825434b1da3b5bc985a2" + "url": "https://ftp.drupal.org/files/projects/semver_example-2.3.0.zip", + "reference": "2.3.0", + "shasum": "3de7ff51a8ce4bdf71c2f32059631f5bd8d71458" }, "require": { - "drupal/core": "^8 || ^9" + "drupal/core": ">=8" }, "type": "drupal-module", "extra": { "drupal": { - "version": "2.2.0", - "datestamp": "1593188229", + "version": "2.3.0", + "datestamp": "1642787442", "security-coverage": { "status": "not-covered", "message": "Project has not opted into security advisory coverage!" @@ -4230,6 +4323,10 @@ "name": "dww", "homepage": "https://www.drupal.org/user/46549" }, + { + "name": "moshe weitzman", + "homepage": "https://www.drupal.org/user/23" + }, { "name": "tedbow", "homepage": "https://www.drupal.org/user/240860" @@ -4243,27 +4340,27 @@ }, { "name": "egulias/email-validator", - "version": "2.1.25", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4" + "reference": "ee0db30118f661fb166bcffbf5d82032df484697" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/0dbf5d78455d4d6a41d186da50adc1122ec066f4", - "reference": "0dbf5d78455d4d6a41d186da50adc1122ec066f4", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ee0db30118f661fb166bcffbf5d82032df484697", + "reference": "ee0db30118f661fb166bcffbf5d82032df484697", "shasum": "" }, "require": { - "doctrine/lexer": "^1.0.1", - "php": ">=5.5", - "symfony/polyfill-intl-idn": "^1.10" + "doctrine/lexer": "^1.2", + "php": ">=7.2", + "symfony/polyfill-intl-idn": "^1.15" }, "require-dev": { - "dominicsayers/isemail": "^3.0.7", - "phpunit/phpunit": "^4.8.36|^7.5.15", - "satooshi/php-coveralls": "^1.0.1" + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^8.5.8|^9.3.3", + "vimeo/psalm": "^4" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -4271,7 +4368,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -4299,7 +4396,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/2.1.25" + "source": "https://github.com/egulias/EmailValidator/tree/3.1.2" }, "funding": [ { @@ -4307,7 +4404,7 @@ "type": "github" } ], - "time": "2020-12-29T14:50:06+00:00" + "time": "2021-10-11T09:18:27+00:00" }, { "name": "jakeasmith/http_build_url", @@ -4586,16 +4683,16 @@ }, { "name": "laminas/laminas-stdlib", - "version": "3.6.0", + "version": "3.6.1", "source": { "type": "git", "url": "https://github.com/laminas/laminas-stdlib.git", - "reference": "c53d8537f108fac3fae652677a19735db730ba46" + "reference": "db581851a092246ad99e12d4fddf105184924c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/c53d8537f108fac3fae652677a19735db730ba46", - "reference": "c53d8537f108fac3fae652677a19735db730ba46", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/db581851a092246ad99e12d4fddf105184924c71", + "reference": "db581851a092246ad99e12d4fddf105184924c71", "shasum": "" }, "require": { @@ -4641,7 +4738,7 @@ "type": "community_bridge" } ], - "time": "2021-09-02T16:11:32+00:00" + "time": "2021-11-10T11:33:52+00:00" }, { "name": "masterminds/html5", @@ -5231,16 +5328,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.2.2", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", "shasum": "" }, "require": { @@ -5251,7 +5348,8 @@ "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2" + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -5281,22 +5379,22 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" }, - "time": "2020-09-03T19:13:55+00:00" + "time": "2021-10-19T17:43:47+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.5.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f" + "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", "shasum": "" }, "require": { @@ -5331,22 +5429,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" }, - "time": "2021-09-17T15:28:14+00:00" + "time": "2022-01-04T19:58:01+00:00" }, { "name": "phpspec/prophecy", - "version": "1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", "shasum": "" }, "require": { @@ -5398,22 +5496,22 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.14.0" + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" }, - "time": "2021-09-10T09:02:12+00:00" + "time": "2021-12-08T12:19:24+00:00" }, { "name": "phpstan/phpstan", - "version": "0.12.99", + "version": "1.3.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" + "reference": "151a51f6149855785fbd883e79768c0abc96b75f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/151a51f6149855785fbd883e79768c0abc96b75f", + "reference": "151a51f6149855785fbd883e79768c0abc96b75f", "shasum": "" }, "require": { @@ -5429,7 +5527,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.12-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -5444,7 +5542,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/0.12.99" + "source": "https://github.com/phpstan/phpstan/tree/1.3.3" }, "funding": [ { @@ -5464,27 +5562,27 @@ "type": "tidelift" } ], - "time": "2021-09-12T20:09:55+00:00" + "time": "2022-01-07T09:49:03+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.7", + "version": "9.2.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", - "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687", + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.12.0", + "nikic/php-parser": "^4.13.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -5533,7 +5631,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10" }, "funding": [ { @@ -5541,20 +5639,20 @@ "type": "github" } ], - "time": "2021-09-17T05:39:03+00:00" + "time": "2021-12-05T09:12:13+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.5", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { @@ -5593,7 +5691,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { @@ -5601,7 +5699,7 @@ "type": "github" } ], - "time": "2020-09-28T05:57:25+00:00" + "time": "2021-12-02T12:48:52+00:00" }, { "name": "phpunit/php-invoker", @@ -5786,16 +5884,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.10", + "version": "9.5.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" + "reference": "2406855036db1102126125537adb1406f7242fdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", - "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2406855036db1102126125537adb1406f7242fdd", + "reference": "2406855036db1102126125537adb1406f7242fdd", "shasum": "" }, "require": { @@ -5873,11 +5971,11 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.11" }, "funding": [ { - "url": "https://phpunit.de/donate.html", + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { @@ -5885,7 +5983,7 @@ "type": "github" } ], - "time": "2021-09-25T07:38:51+00:00" + "time": "2021-12-25T07:07:57+00:00" }, { "name": "psr/cache", @@ -5993,25 +6091,24 @@ }, { "name": "rector/rector", - "version": "0.11.58", + "version": "0.12.12", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "15a8647cf05641db35f091edf0a558ffa9619fee" + "reference": "efcc6f135a76bfd031c31fc182ce7a6fd02b3ce5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/15a8647cf05641db35f091edf0a558ffa9619fee", - "reference": "15a8647cf05641db35f091edf0a558ffa9619fee", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/efcc6f135a76bfd031c31fc182ce7a6fd02b3ce5", + "reference": "efcc6f135a76bfd031c31fc182ce7a6fd02b3ce5", "shasum": "" }, "require": { "php": "^7.1|^8.0", - "phpstan/phpstan": "0.12.99" + "phpstan/phpstan": "^1.3" }, "conflict": { - "phpstan/phpdoc-parser": "<=0.5.3", - "phpstan/phpstan": "<=0.12.82", + "phpstan/phpdoc-parser": "<1.2", "rector/rector-cakephp": "*", "rector/rector-doctrine": "*", "rector/rector-laravel": "*", @@ -6027,7 +6124,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "0.11-dev" + "dev-main": "0.12-dev" } }, "autoload": { @@ -6042,7 +6139,7 @@ "description": "Prefixed and PHP 7.1 downgraded version of rector/rector", "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/0.11.58" + "source": "https://github.com/rectorphp/rector/tree/0.12.12" }, "funding": [ { @@ -6050,7 +6147,7 @@ "type": "github" } ], - "time": "2021-10-12T20:26:47+00:00" + "time": "2022-01-07T16:49:17+00:00" }, { "name": "sebastian/cli-parser", @@ -6481,16 +6578,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", - "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", "shasum": "" }, "require": { @@ -6539,14 +6636,14 @@ } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" }, "funding": [ { @@ -6554,7 +6651,7 @@ "type": "github" } ], - "time": "2020-09-28T05:24:23+00:00" + "time": "2021-11-11T14:18:36+00:00" }, { "name": "sebastian/global-state", @@ -7018,16 +7115,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.6.0", + "version": "3.6.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625" + "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ffced0d2c8fa8e6cdc4d695a743271fab6c38625", - "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a", + "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a", "shasum": "" }, "require": { @@ -7070,7 +7167,7 @@ "source": "https://github.com/squizlabs/PHP_CodeSniffer", "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" }, - "time": "2021-04-09T00:54:41+00:00" + "time": "2021-12-12T21:44:58+00:00" }, { "name": "stack/builder", @@ -7128,21 +7225,21 @@ }, { "name": "symfony-cmf/routing", - "version": "2.3.3", + "version": "2.3.4", "source": { "type": "git", "url": "https://github.com/symfony-cmf/Routing.git", - "reference": "3c97e7b7709b313cecfb76d691ad4cc22acbf3f5" + "reference": "bbcdf2f6301d740454ba9ebb8adaefd436c36a6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony-cmf/Routing/zipball/3c97e7b7709b313cecfb76d691ad4cc22acbf3f5", - "reference": "3c97e7b7709b313cecfb76d691ad4cc22acbf3f5", + "url": "https://api.github.com/repos/symfony-cmf/Routing/zipball/bbcdf2f6301d740454ba9ebb8adaefd436c36a6b", + "reference": "bbcdf2f6301d740454ba9ebb8adaefd436c36a6b", "shasum": "" }, "require": { "php": "^7.2 || ^8.0", - "psr/log": "^1.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", "symfony/http-kernel": "^4.4 || ^5.0", "symfony/routing": "^4.4 || ^5.0" }, @@ -7185,9 +7282,9 @@ ], "support": { "issues": "https://github.com/symfony-cmf/Routing/issues", - "source": "https://github.com/symfony-cmf/Routing/tree/2.3.3" + "source": "https://github.com/symfony-cmf/Routing/tree/2.3.4" }, - "time": "2020-10-06T10:15:37+00:00" + "time": "2021-11-08T16:33:10+00:00" }, { "name": "symfony/debug", @@ -7259,16 +7356,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v4.4.31", + "version": "v4.4.34", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "75dd7094870feaa5be9ed2b6b1efcfc2b7d3b9b4" + "reference": "117d7f132ed7efbd535ec947709d49bec1b9d24b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/75dd7094870feaa5be9ed2b6b1efcfc2b7d3b9b4", - "reference": "75dd7094870feaa5be9ed2b6b1efcfc2b7d3b9b4", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/117d7f132ed7efbd535ec947709d49bec1b9d24b", + "reference": "117d7f132ed7efbd535ec947709d49bec1b9d24b", "shasum": "" }, "require": { @@ -7325,7 +7422,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v4.4.31" + "source": "https://github.com/symfony/dependency-injection/tree/v4.4.34" }, "funding": [ { @@ -7341,87 +7438,20 @@ "type": "tidelift" } ], - "time": "2021-09-21T06:20:06+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-03-23T23:28:01+00:00" + "time": "2021-11-15T14:42:25+00:00" }, { "name": "symfony/error-handler", - "version": "v4.4.30", + "version": "v4.4.34", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "51f98f7aa99f00f3b1da6bafe934e67ae6ba6dc5" + "reference": "17785c374645def1e884d8ec49976c156c61db4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/51f98f7aa99f00f3b1da6bafe934e67ae6ba6dc5", - "reference": "51f98f7aa99f00f3b1da6bafe934e67ae6ba6dc5", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/17785c374645def1e884d8ec49976c156c61db4d", + "reference": "17785c374645def1e884d8ec49976c156c61db4d", "shasum": "" }, "require": { @@ -7460,7 +7490,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v4.4.30" + "source": "https://github.com/symfony/error-handler/tree/v4.4.34" }, "funding": [ { @@ -7476,20 +7506,20 @@ "type": "tidelift" } ], - "time": "2021-08-27T17:42:48+00:00" + "time": "2021-11-12T14:57:39+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4" + "reference": "ec82e57b5b714dbb69300d348bd840b345e24166" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/7e82f6084d7cae521a75ef2cb5c9457bbda785f4", - "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ec82e57b5b714dbb69300d348bd840b345e24166", + "reference": "ec82e57b5b714dbb69300d348bd840b345e24166", "shasum": "" }, "require": { @@ -7501,7 +7531,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -7538,7 +7568,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.4.0" + "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.0" }, "funding": [ { @@ -7554,20 +7584,20 @@ "type": "tidelift" } ], - "time": "2021-04-11T23:07:08+00:00" + "time": "2021-11-03T09:24:47+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.4.30", + "version": "v4.4.34", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "09b3202651ab23ac8dcf455284a48a3500e56731" + "reference": "f4cbbb6fc428588ce8373802461e7fe84e6809ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/09b3202651ab23ac8dcf455284a48a3500e56731", - "reference": "09b3202651ab23ac8dcf455284a48a3500e56731", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f4cbbb6fc428588ce8373802461e7fe84e6809ab", + "reference": "f4cbbb6fc428588ce8373802461e7fe84e6809ab", "shasum": "" }, "require": { @@ -7606,7 +7636,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v4.4.30" + "source": "https://github.com/symfony/http-foundation/tree/v4.4.34" }, "funding": [ { @@ -7622,20 +7652,20 @@ "type": "tidelift" } ], - "time": "2021-08-26T15:51:23+00:00" + "time": "2021-11-04T12:23:33+00:00" }, { "name": "symfony/http-kernel", - "version": "v4.4.32", + "version": "v4.4.35", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "f7bda3ea8f05ae90627400e58af5179b25ce0f38" + "reference": "fb793f1381c34b79a43596a532a6a49bd729c9db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f7bda3ea8f05ae90627400e58af5179b25ce0f38", - "reference": "f7bda3ea8f05ae90627400e58af5179b25ce0f38", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/fb793f1381c34b79a43596a532a6a49bd729c9db", + "reference": "fb793f1381c34b79a43596a532a6a49bd729c9db", "shasum": "" }, "require": { @@ -7710,7 +7740,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v4.4.32" + "source": "https://github.com/symfony/http-kernel/tree/v4.4.35" }, "funding": [ { @@ -7726,25 +7756,25 @@ "type": "tidelift" } ], - "time": "2021-09-28T10:20:04+00:00" + "time": "2021-11-24T08:40:10+00:00" }, { "name": "symfony/mime", - "version": "v5.3.8", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "a756033d0a7e53db389618653ae991eba5a19a11" + "reference": "d4365000217b67c01acff407573906ff91bcfb34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/a756033d0a7e53db389618653ae991eba5a19a11", - "reference": "a756033d0a7e53db389618653ae991eba5a19a11", + "url": "https://api.github.com/repos/symfony/mime/zipball/d4365000217b67c01acff407573906ff91bcfb34", + "reference": "d4365000217b67c01acff407573906ff91bcfb34", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php80": "^1.16" @@ -7758,10 +7788,10 @@ "require-dev": { "egulias/email-validator": "^2.1.10|^3.1", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/property-access": "^4.4|^5.1", - "symfony/property-info": "^4.4|^5.1", - "symfony/serializer": "^5.2" + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/property-access": "^4.4|^5.1|^6.0", + "symfony/property-info": "^4.4|^5.1|^6.0", + "symfony/serializer": "^5.2|^6.0" }, "type": "library", "autoload": { @@ -7793,7 +7823,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v5.3.8" + "source": "https://github.com/symfony/mime/tree/v5.4.0" }, "funding": [ { @@ -7809,7 +7839,7 @@ "type": "tidelift" } ], - "time": "2021-09-10T12:30:38+00:00" + "time": "2021-11-23T10:19:22+00:00" }, { "name": "symfony/polyfill-iconv", @@ -7893,32 +7923,32 @@ }, { "name": "symfony/psr-http-message-bridge", - "version": "v2.1.1", + "version": "v2.1.2", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "c9012994c4b4fb23e7c57dd86b763a417a04feba" + "reference": "22b37c8a3f6b5d94e9cdbd88e1270d96e2f97b34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/c9012994c4b4fb23e7c57dd86b763a417a04feba", - "reference": "c9012994c4b4fb23e7c57dd86b763a417a04feba", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/22b37c8a3f6b5d94e9cdbd88e1270d96e2f97b34", + "reference": "22b37c8a3f6b5d94e9cdbd88e1270d96e2f97b34", "shasum": "" }, "require": { "php": ">=7.1", "psr/http-message": "^1.0", - "symfony/http-foundation": "^4.4 || ^5.0" + "symfony/http-foundation": "^4.4 || ^5.0 || ^6.0" }, "require-dev": { "nyholm/psr7": "^1.1", "psr/log": "^1.1 || ^2 || ^3", - "symfony/browser-kit": "^4.4 || ^5.0", - "symfony/config": "^4.4 || ^5.0", - "symfony/event-dispatcher": "^4.4 || ^5.0", - "symfony/framework-bundle": "^4.4 || ^5.0", - "symfony/http-kernel": "^4.4 || ^5.0", - "symfony/phpunit-bridge": "^4.4.19 || ^5.2" + "symfony/browser-kit": "^4.4 || ^5.0 || ^6.0", + "symfony/config": "^4.4 || ^5.0 || ^6.0", + "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0", + "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0", + "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0", + "symfony/phpunit-bridge": "^5.4@dev || ^6.0" }, "suggest": { "nyholm/psr7": "For a super lightweight PSR-7/17 implementation" @@ -7961,7 +7991,7 @@ ], "support": { "issues": "https://github.com/symfony/psr-http-message-bridge/issues", - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.1.1" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.1.2" }, "funding": [ { @@ -7977,20 +8007,20 @@ "type": "tidelift" } ], - "time": "2021-07-27T17:25:39+00:00" + "time": "2021-11-05T13:13:39+00:00" }, { "name": "symfony/routing", - "version": "v4.4.30", + "version": "v4.4.34", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "9ddf033927ad9f30ba2bfd167a7b342cafa13e8e" + "reference": "fc9dda0c8496f8ef0a89805c2eabfc43b8cef366" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/9ddf033927ad9f30ba2bfd167a7b342cafa13e8e", - "reference": "9ddf033927ad9f30ba2bfd167a7b342cafa13e8e", + "url": "https://api.github.com/repos/symfony/routing/zipball/fc9dda0c8496f8ef0a89805c2eabfc43b8cef366", + "reference": "fc9dda0c8496f8ef0a89805c2eabfc43b8cef366", "shasum": "" }, "require": { @@ -8050,7 +8080,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v4.4.30" + "source": "https://github.com/symfony/routing/tree/v4.4.34" }, "funding": [ { @@ -8066,20 +8096,20 @@ "type": "tidelift" } ], - "time": "2021-08-04T21:41:01+00:00" + "time": "2021-11-04T12:23:33+00:00" }, { "name": "symfony/serializer", - "version": "v4.4.31", + "version": "v4.4.35", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "a10b610f75349dbee1d3ad05c7a2cbf4f1872e34" + "reference": "1b2ae02cb1b923987947e013688c51954a80b751" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/a10b610f75349dbee1d3ad05c7a2cbf4f1872e34", - "reference": "a10b610f75349dbee1d3ad05c7a2cbf4f1872e34", + "url": "https://api.github.com/repos/symfony/serializer/zipball/1b2ae02cb1b923987947e013688c51954a80b751", + "reference": "1b2ae02cb1b923987947e013688c51954a80b751", "shasum": "" }, "require": { @@ -8144,7 +8174,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v4.4.31" + "source": "https://github.com/symfony/serializer/tree/v4.4.35" }, "funding": [ { @@ -8160,20 +8190,20 @@ "type": "tidelift" } ], - "time": "2021-09-17T08:50:49+00:00" + "time": "2021-11-24T08:12:42+00:00" }, { "name": "symfony/translation", - "version": "v4.4.32", + "version": "v4.4.34", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "db0ba1e85280d8ff11e38d53c70f8814d4d740f5" + "reference": "26d330720627b234803595ecfc0191eeabc65190" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/db0ba1e85280d8ff11e38d53c70f8814d4d740f5", - "reference": "db0ba1e85280d8ff11e38d53c70f8814d4d740f5", + "url": "https://api.github.com/repos/symfony/translation/zipball/26d330720627b234803595ecfc0191eeabc65190", + "reference": "26d330720627b234803595ecfc0191eeabc65190", "shasum": "" }, "require": { @@ -8233,7 +8263,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v4.4.32" + "source": "https://github.com/symfony/translation/tree/v4.4.34" }, "funding": [ { @@ -8249,20 +8279,20 @@ "type": "tidelift" } ], - "time": "2021-08-26T05:57:13+00:00" + "time": "2021-11-04T12:23:33+00:00" }, { "name": "symfony/translation-contracts", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "95c812666f3e91db75385749fe219c5e494c7f95" + "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/95c812666f3e91db75385749fe219c5e494c7f95", - "reference": "95c812666f3e91db75385749fe219c5e494c7f95", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/d28150f0f44ce854e942b671fc2620a98aae1b1e", + "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e", "shasum": "" }, "require": { @@ -8274,7 +8304,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -8311,7 +8341,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v2.4.0" + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.0" }, "funding": [ { @@ -8327,20 +8357,20 @@ "type": "tidelift" } ], - "time": "2021-03-23T23:28:01+00:00" + "time": "2021-08-17T14:20:01+00:00" }, { "name": "symfony/validator", - "version": "v4.4.31", + "version": "v4.4.35", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "9420a2e8874263a684588283e40252209d80e1a5" + "reference": "629f420d8350634fd8ed686d4472c1f10044b265" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/9420a2e8874263a684588283e40252209d80e1a5", - "reference": "9420a2e8874263a684588283e40252209d80e1a5", + "url": "https://api.github.com/repos/symfony/validator/zipball/629f420d8350634fd8ed686d4472c1f10044b265", + "reference": "629f420d8350634fd8ed686d4472c1f10044b265", "shasum": "" }, "require": { @@ -8417,7 +8447,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v4.4.31" + "source": "https://github.com/symfony/validator/tree/v4.4.35" }, "funding": [ { @@ -8433,7 +8463,7 @@ "type": "tidelift" } ], - "time": "2021-09-20T16:52:24+00:00" + "time": "2021-11-22T21:43:32+00:00" }, { "name": "theseer/tokenizer", @@ -8542,16 +8572,16 @@ }, { "name": "vlucas/phpdotenv", - "version": "v2.6.7", + "version": "v2.6.9", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "b786088918a884258c9e3e27405c6a4cf2ee246e" + "reference": "2e93cc98e2e8e869f8d9cfa61bb3a99ba4fc4141" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/b786088918a884258c9e3e27405c6a4cf2ee246e", - "reference": "b786088918a884258c9e3e27405c6a4cf2ee246e", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2e93cc98e2e8e869f8d9cfa61bb3a99ba4fc4141", + "reference": "2e93cc98e2e8e869f8d9cfa61bb3a99ba4fc4141", "shasum": "" }, "require": { @@ -8561,7 +8591,7 @@ "require-dev": { "ext-filter": "*", "ext-pcre": "*", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20" + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.21" }, "suggest": { "ext-filter": "Required to use the boolean validator.", @@ -8585,13 +8615,13 @@ "authors": [ { "name": "Graham Campbell", - "email": "graham@alt-three.com", - "homepage": "https://gjcampbell.co.uk/" + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" }, { "name": "Vance Lucas", "email": "vance@vancelucas.com", - "homepage": "https://vancelucas.com/" + "homepage": "https://github.com/vlucas" } ], "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", @@ -8602,7 +8632,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v2.6.7" + "source": "https://github.com/vlucas/phpdotenv/tree/v2.6.9" }, "funding": [ { @@ -8614,7 +8644,7 @@ "type": "tidelift" } ], - "time": "2021-01-20T14:39:13+00:00" + "time": "2021-12-12T22:59:22+00:00" }, { "name": "yoast/phpunit-polyfills", @@ -8682,9 +8712,7 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": { - "consolidation/site-alias": 0 - }, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/docs/commands.md b/docs/commands.md index 8a8f74ccbc..ed1b25564f 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -3,15 +3,56 @@ Creating a new Drush command or porting a legacy command is easy. Follow the steps below. 1. Run `drush generate drush-command-file`. -1. Drush will prompt for the machine name of the module that should "own" the file. +2. Drush will prompt for the machine name of the module that should "own" the file. 1. (optional) Drush will also prompt for the path to a legacy command file to port. See [tips on porting commands to Drush 10](https://weitzman.github.io/blog/port-to-drush9) 1. The module selected must already exist and be enabled. Use `drush generate module-standard` to create a new module. -1. Drush will then report that it created a commandfile, a drush.services.yml file and a composer.json file. Edit those files as needed. -1. Use the classes for the core Drush commands at [/src/Drupal/Commands](https://github.com/drush-ops/drush/tree/11.x/src/Drupal/Commands) as inspiration and documentation. -1. See the [dependency injection docs](dependency-injection.md) for interfaces you can implement to gain access to Drush config, Drupal site aliases, etc. -1. A commandfile that will only be used on PHP8+ can [use Attributes](https://github.com/drush-ops/drush/pull/4821) instead of Annotations. -1. Write PHPUnit tests based on [Drush Test Traits](https://github.com/drush-ops/drush/blob/11.x/docs/contribute/unish.md#drush-test-traits). -1. Once your two files are ready, run `drush cr` to get your command recognized by the Drupal container. +3. Drush will then report that it created a commandfile, a drush.services.yml file and a composer.json file. Edit those files as needed. +4. Use the classes for the core Drush commands at [/src/Drupal/Commands](https://github.com/drush-ops/drush/tree/11.x/src/Drupal/Commands) as inspiration and documentation. +5. See the [dependency injection docs](dependency-injection.md) for interfaces you can implement to gain access to Drush config, Drupal site aliases, etc. +6. Write PHPUnit tests based on [Drush Test Traits](https://github.com/drush-ops/drush/blob/11.x/docs/contribute/unish.md#drush-test-traits). +7. Once your drush.services.yml files is ready, run `drush cr` to get your command recognized by the Drupal container. + +## Attributes or Annotations +The following are both valid ways to declare a command: + +=== "PHP8 Attributes" + + ```php + use Drush\Attributes as CLI; + + #[CLI\Command(name: 'xkcd:fetch-attributes', aliases: ['xkcd-attributes'])] + #[CLI\Argument(name: 'search', description: 'Optional argument to retrieve the cartoons matching an index, keyword keyword, or "random".')] + #[CLI\Option(name: 'image-viewer', description: 'Command to use to view images (e.g. xv, firefox).')] + #[CLI\Option(name: 'google-custom-search-api-key', description: 'Google Custom Search API Key')] + #[CLI\Help(description: 'Retrieve and display xkcd cartoons (attribute variant).')] + #[CLI\Usage(name: 'drush xkcd', description: 'Retrieve and display the latest cartoon')] + #[CLI\Usage(name: 'drush xkcd sandwich', description: 'Retrieve and display cartoons about sandwiches.')] + public function fetch($search = null, $options = ['image-viewer' => 'open', 'google-custom-search-api-key' => 'AIza']) { + $this->doFetch($search, $options); + } + ``` + +=== "Annotations" + + ```php + /** + * @command xkcd:fetch + * @param $search Optional argument to retrieve the cartoons matching an index number, keyword, or "random". + * @option image-viewer Command to use to view images (e.g. xv, firefox). + * @option google-custom-search-api-key Google Custom Search API Key. + * @usage drush xkcd + * Retrieve and display the latest cartoon. + * @usage drush xkcd sandwich + * Retrieve and display cartoons about sandwiches. + * @aliases xkcd + */ + public function fetch($search = null, $options = ['image-viewer' => 'open', 'google-custom-search-api-key' => 'AIza']) { + $this->doFetch($search, $options); + } + ``` + +- A commandfile that will only be used on PHP8+ should [use PHP Attributes](https://github.com/drush-ops/drush/pull/4821) instead of Annotations. +- [See all Attributes provided by Drush core](https://www.drush.org/latest/api/Drush/Attributes.html). ## Specifying the Services File @@ -52,10 +93,10 @@ In order to alter an existing command info, follow the steps below: 1. In that class, implement the alteration logic in the `alterCommandInfo()` method. 1. Along with the alter code, it's strongly recommended to log a debug message explaining what exactly was altered. This makes things easier on others who may need to debug the interaction of the alter code with other modules. Also it's a good practice to inject the the logger in the class constructor. -For an example, see the alterer class provided by the testing 'woot' module: `tests/functional/resources/modules/d8/woot/src/WootCommandInfoAlterer.php`. +For an example, see the alterer class provided by the testing 'woot' module: `tests/fixtures/modules/woot/src/WootCommandInfoAlterer.php`. ## Symfony Console Commands -Drush lists and runs Symfony Console commands, in addition to more typical annotated commands. See [this test](https://github.com/drush-ops/drush/blob/eed106ae4510d5a2df89f8e7fd54b41ffb0aa5fa/tests/integration/AnnotatedCommandCase.php#L178-L180) and this [commandfile](https://github.com/drush-ops/drush/blob/049d2a4b36338178c0c7cf89b8e7caf9769524c9/tests/functional/resources/modules/d8/woot/src/Commands/GreetCommand.php). +Drush lists and runs Symfony Console commands, in addition to more typical annotated commands. See [this test](https://github.com/drush-ops/drush/blob/eed106ae4510d5a2df89f8e7fd54b41ffb0aa5fa/tests/integration/AnnotatedCommandCase.php#L178-L180) and this [commandfile](https://github.com/drush-ops/drush/tree/HEAD/tests/fixtures/modules/woot/src/Commands/GreetCommand.php). ## Site-Wide Drush Commands Commandfiles that are installed in a Drupal site and are not bundled inside a Drupal module are called 'site-wide' commandfiles. Site-wide commands may either be added directly to the Drupal site's repository (e.g. for site-specific policy files), or via `composer require`. See the [examples/Commands](https://github.com/drush-ops/drush/tree/11.x/examples/Commands) folder for examples. In general, it's better to use modules to carry your Drush commands, as module-based commands may [participate in Drupal's dependency injection via the drush.services.yml](#specifying-the-services-file). diff --git a/docs/dependency-injection.md b/docs/dependency-injection.md index 7a06fa749d..bdfc9b8ee9 100644 --- a/docs/dependency-injection.md +++ b/docs/dependency-injection.md @@ -57,8 +57,9 @@ use Consolidation\SiteAlias\SiteAliasManagerAwareTrait; class MyModuleCommands extends DrushCommands implements SiteAliasManagerAwareInterface { use SiteAliasManagerAwareTrait; + /** - * Prints the currenbt alias name and info. + * Prints the current alias name and info. * * @command mymodule:myAlias * @return \Consolidation\OutputFormatters\StructuredData\ListDataFromKeys diff --git a/docs/deploycommand.md b/docs/deploycommand.md index 7863109ee1..297022757d 100644 --- a/docs/deploycommand.md +++ b/docs/deploycommand.md @@ -20,7 +20,7 @@ Below are the 3 types of update functions run by this command, in order. Choose | --- | --- | --- | | [HOOK_update_n()](https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Extension!module.api.php/function/hook_update_N) | Not allowed | Low level changes. | | [HOOK_post_update_NAME()](https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Extension!module.api.php/function/hook_post_update_NAME) | Allowed | Runs *before* config is imported. | -| [HOOK_deploy_NAME()](https://github.com/drush-ops/drush/blob/11.x/tests/functional/resources/modules/d8/woot/woot.deploy.php) | Allowed | Runs *after* config is imported. | +| [HOOK_deploy_NAME()](https://github.com/drush-ops/drush/tree/HEAD/tests/fixtures/modules/woot/woot.deploy.php) | Allowed | Runs *after* config is imported. | ## Configuration diff --git a/docs/examples/example.bashrc.md b/docs/examples/example.bashrc.md deleted file mode 100644 index 3a4bcf6067..0000000000 --- a/docs/examples/example.bashrc.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -edit_url: https://github.com/drush-ops/drush/blob/11.x/examples/example.bashrc ---- -```shell ---8<-- "examples/example.bashrc" -``` diff --git a/docs/generators.md b/docs/generators.md index 55f51fc2da..3a436a25dc 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -6,11 +6,11 @@ Drush's generators reuse classes provided by the excellent [Drupal Code Generato ## Writing Custom Generators Drupal modules may supply their own Generators, just like they can supply Commands. -See [Woot module](https://github.com/drush-ops/drush/blob/11.x/tests/functional/resources/modules/d8/woot), which Drush uses for testing. Specifically, +See [Woot module](https://github.com/drush-ops/drush/blob/11.x/tests/fixtures/modules/woot), which Drush uses for testing. Specifically, - 1. Write a class similar to [ExampleGenerator](https://github.com/drush-ops/drush/tree/11.x/tests/functional/resources/modules/d8/woot/src/Generators/). Implement your custom logic in the generate() method. Typically this class is placed under the src/Generators directory. + 1. Write a class similar to [ExampleGenerator](https://github.com/drush-ops/drush/tree/11.x/tests/fixtures/modules/woot/src/Generators/). Implement your custom logic in the generate() method. Typically this class is placed under the src/Generators directory. 2. Add a .twig file to the same directory. This template specifies what gets output from the generator. - 4. Add your class to your module's drush.services.yml file ([example](https://github.com/drush-ops/drush/blob/11.x/tests/functional/resources/modules/d8/woot/drush.services.yml)). Use the tag `drush.generator.v2` instead of `drush.command`. + 4. Add your class to your module's drush.services.yml file ([example](https://github.com/drush-ops/drush/blob/11.x/tests/fixtures/modules/woot/drush.services.yml)). Use the tag `drush.generator.v2` instead of `drush.command`. 5. Perform a `drush cache-rebuild` to compile your drush.services.yml changes into the Drupal container. ## Global Generators diff --git a/docs/hooks.md b/docs/hooks.md index 2c9698f12d..ed980e05d3 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -33,9 +33,23 @@ Then, the command may ask the provided hook manager to return a list of handlers } ``` -Other command handlers may provide implementations by implementing `@hook on-event my-event` or `#[CLI/Hook(type: 'on-event', target: 'my-event')]`. +Other commandfiles may provide implementations via a PHP8 Attribute or an Annotation. -```php +=== "PHP8 Attributes" + + ```php + /** + * #[CLI/Hook(type: 'on-event', target: 'my-event')] + */ + public function hookOne() + { + return 'one'; + } + ``` + +=== "Annotations" + + ```php /** * @hook on-event my-event */ @@ -43,4 +57,4 @@ Other command handlers may provide implementations by implementing `@hook on-eve { return 'one'; } -``` + ``` diff --git a/docs/index.md b/docs/index.md index 6abee0c20d..ea8a64774b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,16 +1,16 @@ -Drush is a command line shell and Unix scripting interface for Drupal. Drush core ships with lots of [useful commands](commands/all.md) for interacting with code like modules/themes/profiles. Similarly, it runs update.php, executes SQL queries and DB migrations, and misc utilities like run cron or clear cache. Developers love the [generate command](commands/generate.md), which jump starts your coding project by writing ready-to-customize PHP and YML files. Drush can be extended by [3rd party commandfiles](https://www.drupal.org/project/project_module?f[2]=im_vid_3%3A4654). +Drush is a command line shell and Unix scripting interface for Drupal. Drush core ships with lots of [useful commands](commands/all.md) and [generators](generators/all.md). Similarly, it runs update.php, executes SQL queries, runs content migrations, and misc utilities like cron or cache rebuild. Drush can be extended by [3rd party commandfiles](https://www.drupal.org/project/project_module?f[2]=im_vid_3%3A4654). [![Latest Stable Version](https://poser.pugx.org/drush/drush/v/stable.png)](https://packagist.org/packages/drush/drush) [![Total Downloads](https://poser.pugx.org/drush/drush/downloads.png)](https://packagist.org/packages/drush/drush) [![License](https://poser.pugx.org/drush/drush/license.png)](https://packagist.org/packages/drush/drush) [![Twitter](https://img.shields.io/badge/Twitter-%40DrushCli-blue.svg)](https://twitter.com/intent/user?screen_name=DrushCli) Resources ----------- -* [Installing and Upgrading](install.md) ([Drush 8](https://docs.drush.org/en/8.x/install/)) -* [Usage](usage.md) ([Drush 8](https://docs.drush.org/en/8.x/install/)) -* [Drush Commands](commands/all.md) +* [Installing and Upgrading](install.md) +* [Commands](commands/all.md) and [Generators](generators/all.md) * [API Documentation](https://www.drush.org/latest/api) * [Drush packages available via Composer](https://packagist.org/search/?type=drupal-drush) * [A list of modules that include Drush integration](https://www.drupal.org/project/project_module?f[2]=im_vid_3%3A4654&solrsort=ds_project_latest_release+desc) -* Drush comes with a [full test suite](https://github.com/drush-ops/drush/blob/11.x/docs/contribute/unish.md) powered by [PHPUnit](https://github.com/sebastianbergmann/phpunit). Each commit gets tested by our [CI bots](https://circleci.com/gh/drush-ops/drush/). +* Drush expects all participants to abide by the [Drupal Code of Conduct](https://www.drupal.org/dcoc). +* Drush 8 is no longer supported, but [here are its docs](https://docs.drush.org/en/8.x/install/). Support ----------- @@ -18,10 +18,6 @@ Support * Report bugs and request features in the [GitHub Drush Issue Queue](https://github.com/drush-ops/drush/issues). * Use pull requests (PRs) to contribute to Drush. [Guidelines](contribute/CONTRIBUTING.md). -Code of Conduct ---------------- -The Drush project expects all participants to abide by the [Drupal Code of Conduct](https://www.drupal.org/dcoc). - FAQ ------ diff --git a/docs/install.md b/docs/install.md index 8d77d3c9e2..740d87e09c 100644 --- a/docs/install.md +++ b/docs/install.md @@ -23,56 +23,56 @@ Drupal Compatibility End Of Life Drupal versions - 7 -8.3 8.4+ 9 + 7 8 9 Drush 11 7.4+ TBD - + Drush 10 7.1+ - Nov 2021 - ✓ + Jan 2022 + ✓ Drush 9 5.6+ May 2020 - ✓ + ✓ Drush 8 5.4.5+ Nov 2022 - ✅ ✅ ⚠️ + ✅ ⚠️ Drush 7 5.3.0+ Jul 2017 - ✓ + ✓ Drush 6 5.3.0+ Dec 2015 - ✓ + ✓ Drush 5 5.2.0+ May 2015 - ✓ + ✓ diff --git a/docs/overrides/main.html b/docs/overrides/main.html index 40c1ebcde4..40bad40486 100644 --- a/docs/overrides/main.html +++ b/docs/overrides/main.html @@ -32,6 +32,19 @@ {% endblock %} + +{% block content %} + {{ super() }} + + {% if git_page_authors %} +
+ + Authors: {{ git_page_authors | default('enable mkdocs-git-authors-plugin') }} + +
+ {% endif %} +{% endblock %} + {% block disqus %}
diff --git a/docs/using-drush-configuration.md b/docs/using-drush-configuration.md index f77ea8d7ad..8f862af3ac 100644 --- a/docs/using-drush-configuration.md +++ b/docs/using-drush-configuration.md @@ -74,13 +74,6 @@ drush: ``` - View all loaded site aliases: `drush site:alias` -#### Cache directory -Specify a folder where Drush should store its file based caches. If unspecified, defaults to `$HOME/.drush`. -```yml -drush: - cache-directory: /tmp/.drush -``` - #### Backup directory Specify a folder where Drush should store backup files, including temporary sql dump files created during [sql:sync](https://www.drush.org/latest/commands/sql_sync/). If unspecified, diff --git a/drush.info b/drush.info index 9ad93d53df..43ab35b1e9 100644 --- a/drush.info +++ b/drush.info @@ -1 +1 @@ -drush_version=11.0.0-dev +drush_version=11.1.0-dev diff --git a/examples/Commands/ArtCommands.php b/examples/Commands/ArtCommands.php index c45d88f214..b1f24145b9 100644 --- a/examples/Commands/ArtCommands.php +++ b/examples/Commands/ArtCommands.php @@ -1,4 +1,5 @@ /dev/null)" - if [ $? == 0 ] - then - echo "cd $d"; - builtin cd "$d"; - else - t="$(drush site-alias $1 >/dev/null 2>/dev/null)" - if [ $? == 0 ] - then - echo "Cannot cd to remote site $s" - else - echo "Cannot cd to $s" - fi - fi - else - builtin cd "$s"; - fi -} - -# Works just like the `cddl` shell alias above, with one additional -# feature: `cdd @remote-site` works like `ssh @remote-site`, -# whereas cd above will fail unless the site alias is local. If -# you prefer this behavior, you can add `alias cd='cdd'` to your .bashrc -function cdd() { - s="$1" - if [ -z "$s" ] - then - builtin cd - elif [ "${s:0:1}" == "@" ] || [ "${s:0:1}" == "%" ] - then - d="$(drush drupal:directory $s 2>/dev/null)" - rh="$(drush sa ${s%%:*} --fields=host --format=list)" - if [ -z "$rh" ] - then - echo "cd $d" - builtin cd "$d" - else - if [ -n "$d" ] - then - c="cd \"$d\" \; bash" - drush -s ${s%%:*} ssh --tty - drush ${s%%:*} ssh --tty - else - drush ssh ${s%%:*} - fi - fi - else - builtin cd "$s" - fi -} - -# Allow `git @site gitcommand` as a shortcut for `cd @site; git gitcommand`. -# Also works on remote sites, though. -function gitd() { - s="$1" - if [ -n "$s" ] && [ ${s:0:1} == "@" ] || [ ${s:0:1} == "%" ] - then - d="$(drush drupal-directory $s 2>/dev/null)" - rh="$(drush sa ${s%%:*} --fields=host --format=list)" - if [ -n "$rh" ] - then - drush ${s%%:*} ssh "cd '$d' ; git ${@:2}" - else - echo cd "$d" \; git "${@:2}" - ( - cd "$d" - "git" "${@:2}" - ) - fi - else - "git" "$@" - fi -} - -# Get a directory listing on @site or @site:%files, etc, for local or remote sites. -function lsd() { - p=() - r= - for a in "$@" ; do - if [ ${a:0:1} == "@" ] || [ ${a:0:1} == "%" ] - then - p[${#p[@]}]="$(drush drupal:directory $a 2>/dev/null)" - if [ ${a:0:1} == "@" ] - then - rh="$(drush sa ${a%:*} --fields=host --format=list)" - if [ -n "$rh" ] - then - r=${a%:*} - fi - fi - elif [ -n "$a" ] - then - p[${#p[@]}]="$a" - fi - done - if [ -n "$r" ] - then - drush $r ssh 'ls "${p[@]}"' - else - "ls" "${p[@]}" - fi -} - -# Copy files from or to @site or @site:%files, etc; local sites only. -function cpd() { - p=() - for a in "$@" ; do - if [ ${a:0:1} == "@" ] || [ ${a:0:1} == "%" ] - then - p[${#p[@]}]="$(drush drupal:directory $a --local-only 2>/dev/null)" - elif [ -n "$a" ] - then - p[${#p[@]}]="$a" - fi - done - "cp" "${p[@]}" -} - -# This alias allows `dssh @site` to work like `drush @site ssh`. -# Ssh commands, such as `dssh @site ls /tmp`, are also supported. -function dssh() { - d="$1" - if [ ${d:0:1} == "@" ] - then - drush "$d" ssh "${@:2}" - else - "ssh" "$@" - fi -} diff --git a/includes/backend.inc b/includes/backend.inc deleted file mode 100644 index e88a0c643c..0000000000 --- a/includes/backend.inc +++ /dev/null @@ -1,118 +0,0 @@ ->>'); -define('DRUSH_BACKEND_OUTPUT_DELIMITER', DRUSH_BACKEND_OUTPUT_START . '%s<<get($cid); - $mess = $ret ? "HIT" : "MISS"; - Drush::logger()->debug(dt("Cache !mess cid: !cid", ['!mess' => $mess, '!cid' => $cid])); - return $ret; -} - -/** - * Return data from the persistent cache when given an array of cache IDs. - * - * @param array $cids - * An array of cache IDs for the data to retrieve. This is passed by - * reference, and will have the IDs successfully returned from cache removed. - * @param string $bin - * The cache bin where the data is stored. - * - * @return - * An array of the items successfully returned from cache indexed by cid. - */ -function drush_cache_get_multiple(array &$cids, $bin = 'default') { - return _drush_cache_get_object($bin)->getMultiple($cids); -} - -/** - * Store data in the persistent cache. - * - * @param string $cid - * The cache ID of the data to store. - * - * @param $data - * The data to store in the cache. - * @param string $bin - * The cache bin to store the data in. - * @param $expire - * One of the following values: - * - DRUSH_CACHE_PERMANENT: Indicates that the item should never be removed - * unless explicitly told to using drush_cache_clear_all() with a cache ID. - * - DRUSH_CACHE_TEMPORARY: Indicates that the item should be removed at - * the next general cache wipe. - * - A Unix timestamp: Indicates that the item should be kept at least until - * the given time, after which it behaves like DRUSH_CACHE_TEMPORARY. - * - * @return bool - */ -function drush_cache_set($cid, $data, $bin = 'default', $expire = DRUSH_CACHE_PERMANENT) { - if ($ret = _drush_cache_get_object($bin)->set($cid, $data, $expire)) { - Drush::logger()->debug(dt("Cache SET cid: !cid", ['!cid' => $cid])); - return $ret; - } -} - -/** - * Expire data from the cache. - * - * If called without arguments, expirable entries will be cleared from all known - * cache bins. - * - * @param string $cid - * If set, the cache ID to delete. Otherwise, all cache entries that can - * expire are deleted. - * @param string $bin - * If set, the bin $bin to delete from. Mandatory - * argument if $cid is set. - * @param bool $wildcard - * If $wildcard is TRUE, cache IDs starting with $cid are deleted in - * addition to the exact cache ID specified by $cid. If $wildcard is - * TRUE and $cid is '*' then the entire bin $bin is emptied. - */ -function drush_cache_clear_all($cid = NULL, $bin = 'default', $wildcard = FALSE) { - if (!isset($cid) && !isset($bin)) { - foreach (drush_cache_get_bins() as $bin) { - _drush_cache_get_object($bin)->clear(); - } - return; - } - return _drush_cache_get_object($bin)->clear($cid, $wildcard); -} - -/** - * Check if a cache bin is empty. - * - * A cache bin is considered empty if it does not contain any valid data for any - * cache ID. - * - * @param $bin - * The cache bin to check. - * - * @return - * TRUE if the cache bin specified is empty. - */ -function _drush_cache_is_empty($bin) { - return _drush_cache_get_object($bin)->isEmpty(); -} - -/** - * Return drush cache bins and any bins added by hook_drush_flush_caches(). - */ -function drush_cache_get_bins() { - $drush = ['default']; - return $drush; - // return array_merge(drush_command_invoke_all('drush_flush_caches'), $drush); -} diff --git a/includes/drupal.inc b/includes/drupal.inc deleted file mode 100644 index 0f3b3afd4f..0000000000 --- a/includes/drupal.inc +++ /dev/null @@ -1,47 +0,0 @@ -getRoot())) { - $bootstrap = Drush::bootstrapManager()->bootstrapObjectForRoot($drupal_root); - if ($bootstrap) { - $version = $bootstrap->getVersion($drupal_root); - } - } - } - return $version; -} - -/** - * Returns the Drupal major version number (6, 7, 8 ...) - */ -function drush_drupal_major_version($drupal_root = NULL) { - $major_version = FALSE; - if ($version = drush_drupal_version($drupal_root)) { - $version_parts = explode('.', $version); - if (is_numeric($version_parts[0])) { - $major_version = (integer)$version_parts[0]; - } - } - return $major_version; -} diff --git a/includes/drush.inc b/includes/drush.inc index fbb038fdf9..f86b8d811d 100644 --- a/includes/drush.inc +++ b/includes/drush.inc @@ -69,90 +69,6 @@ function drush_log($message, $type = LogLevel::INFO, $error = []) { Drush::logger()->log($type, $message, $error); } -/** - * Get the available global options. Used by list/help commands. All other users - * should pull options from $application. - * - * @param boolean $brief - * Return a reduced set of important options. Used by help command. - * - * @return - * An associative array containing the option definition as the key, - * and a descriptive array for each of the available options. The array - * elements for each item are: - * - * - short-form: The shortcut form for specifying the key on the commandline. - * - propagate: backend invoke will use drush_get_option to propagate this - * option, when set, to any other Drush command that is called. - * - context: The drush context where the value of this item is cached. Used - * by backend invoke to propagate values set in code. - * - never-post: If TRUE, backend invoke will never POST this item's value - * on STDIN; it will always be sent as a commandline option. - * - never-propagate: If TRUE, backend invoke will never pass this item on - * to the subcommand being executed. - * - local-context-only: Backend invoke will only pass this value on for local calls. - * - merge: For options such as $options['shell-aliases'] that consist of an array - * of items, make a merged array that contains all of the values specified for - * all of the contexts (config files) where the option is defined. The value is stored in - * the specified 'context', or in a context named after the option itself if the - * context flag is not specified. - * IMPORTANT: When the merge flag is used, the option value must be obtained via - * drush_get_context('option') rather than drush_get_option('option'). - * - merge-pathlist: For options such as --include and --config, make a merged list - * of options from all contexts; works like the 'merge' flag, but also handles string - * values separated by the PATH_SEPARATOR. - * - merge-associative: Like 'merge-pathlist', but key values are preserved. - * - propagate-cli-value: Used to tell backend invoke to include the value for - * this item as specified on the cli. This can either override 'context' - * (e.g., propagate --include from cli value instead of DRUSH_INCLUDE context), - * or for an independent global setting (e.g. --user) - * - description: The help text for this item. displayed by `drush help`. - */ -function drush_get_global_options($brief = FALSE) { - $options['root'] = ['short-form' => 'r', 'short-has-arg' => TRUE, 'never-post' => TRUE, 'description' => "Drupal root directory to use.", 'example-value' => 'path']; - $options['uri'] = ['short-form' => 'l', 'short-has-arg' => TRUE, 'never-post' => TRUE, 'description' => 'URI of the drupal site to use.', 'example-value' => 'http://example.com:8888']; - $options['verbose'] = ['short-form' => 'v', 'context' => 'DRUSH_VERBOSE', 'description' => 'Display extra information about the command.', 'symfony-conflict' => TRUE]; - $options['debug'] = ['short-form' => 'd', 'context' => 'DRUSH_DEBUG', 'description' => 'Display even more information.']; - $options['yes'] = ['short-form' => 'y', 'context' => 'DRUSH_AFFIRMATIVE', 'description' => "Assume 'yes' as answer to all prompts."]; - $options['no'] = ['short-form' => 'n', 'context' => 'DRUSH_NEGATIVE', 'description' => "Assume 'no' as answer to all prompts."]; - $options['help'] = ['short-form' => 'h', 'description' => "This help system."]; - - if (!$brief) { - $options['simulate'] = ['short-form' => 's', 'context' => 'DRUSH_SIMULATE', 'never-propagate' => TRUE, 'description' => "Simulate all relevant actions (don't actually change the system).", 'symfony-conflict' => TRUE]; - $options['pipe'] = ['short-form' => 'p', 'hidden' => TRUE, 'description' => "Emit a compact representation of the command for scripting."]; - $options['php'] = ['description' => "The absolute path to your PHP interpreter, if not 'php' in the path.", 'example-value' => '/path/to/file', 'never-propagate' => TRUE]; - $options['interactive'] = ['short-form' => 'ia', 'description' => "Force interactive mode for commands run on multiple targets (e.g. `drush @site1,@site2 cc --ia`).", 'never-propagate' => TRUE]; - $options['tty'] = ['hidden' => TRUE, 'description' => "Force allocation of tty for remote commands", 'never-propagate' => TRUE]; - $options['quiet'] = ['short-form' => 'q', 'description' => 'Suppress non-error messages.']; - $options['include'] = ['short-form' => 'i', 'short-has-arg' => TRUE, 'context' => 'DRUSH_INCLUDE', 'never-post' => TRUE, 'propagate-cli-value' => TRUE, 'merge-pathlist' => TRUE, 'description' => "A list of additional directory paths to search for Drush commands. Commandfiles should be placed in a subfolder called 'Commands'.", 'example-value' => '/path/dir']; - $options['exclude'] = ['propagate-cli-value' => TRUE, 'never-post' => TRUE, 'merge-pathlist' => TRUE, 'description' => "A list of files and directory paths to exclude from consideration when searching for drush commandfiles.", 'example-value' => '/path/dir']; - $options['config'] = ['short-form' => 'c', 'short-has-arg' => TRUE, 'context' => 'DRUSH_CONFIG', 'never-post' => TRUE, 'propagate-cli-value' => TRUE, 'merge-pathlist' => TRUE, 'description' => "Specify an additional config file to load. See example.drush.yml", 'example-value' => '/path/file']; - $options['backend'] = ['hidden' => TRUE, 'short-form' => 'b', 'never-propagate' => TRUE, 'description' => "Hide all output and return structured data."]; - $options['choice'] = ['description' => "Provide an answer to a multiple-choice prompt.", 'example-value' => 'number']; - $options['search-depth'] = ['description' => "Control the depth that drush will search for alias files.", 'example-value' => 'number']; - $options['ignored-modules'] = ['description' => "Exclude some modules from consideration when searching for drush command files.", 'example-value' => 'token,views']; - $options['no-label'] = ['description' => "Remove the site label that drush includes in multi-site command output (e.g. `drush @site1,@site2 status`)."]; - $options['label-separator'] = ['description' => "Specify the separator to use in multi-site command output (e.g. `drush @sites pm-list --label-separator=',' --format=csv`).", 'example-value' => ',']; - $options['show-invoke'] = ['description' => "Show all function names which could have been called for the current command. See drush_invoke()."]; - $options['cache-default-class'] = ['description' => "A cache backend class that implements CacheInterface. Defaults to JSONCache.", 'example-value' => 'JSONCache']; - $options['cache-class-'] = ['description' => "A cache backend class that implements CacheInterface to use for a specific cache bin.", 'example-value' => 'className']; - $options['early'] = ['description' => "Include a file (with relative or full path) and call the drush_early_hook() function (where 'hook' is the filename)"]; - $options['alias-path'] = ['context' => 'ALIAS_PATH', 'local-context-only' => TRUE, 'merge-pathlist' => TRUE, 'propagate-cli-value' => TRUE, 'description' => "Specifies the list of paths where drush will search for alias files.", 'example-value' => '/path/alias1:/path/alias2']; - $options['confirm-rollback'] = ['description' => 'Wait for confirmation before doing a rollback when something goes wrong.']; - $options['php-options'] = ['hidden' => TRUE, 'description' => "Options to pass to `php` when running drush. Only effective when specified in a site alias definition.", 'never-propagate' => TRUE, 'example-value' => '-d error_reporting="E_ALL"']; - $options['halt-on-error'] = ['propagate-cli-value' => TRUE, 'description' => "Manage recoverable errors. Values: 1=Execution halted. 0=Execution continues."]; - $options['remote-host'] = ['hidden' => TRUE, 'description' => 'Remote site to execute drush command on. Managed by site alias.', 'example-value' => 'http://example.com']; - $options['remote-user'] = ['hidden' => TRUE, 'description' => 'User account to use with a remote drush command. Managed by site alias.', 'example-value' => 'www-data']; - $options['remote-os'] = ['hidden' => TRUE, 'description' => 'The operating system used on the remote host. Managed by site alias.', 'example-value' => 'linux']; - $options['strict'] = ['propagate' => TRUE, 'description' => 'Return an error on unrecognized options. --strict=0: Allow unrecognized options.']; - $options['command-specific'] = ['hidden' => TRUE, 'merge-associative' => TRUE, 'description' => 'Command-specific options.']; - $options['path-aliases'] = ['hidden' => TRUE, 'never-propagate' => TRUE, 'description' => 'Path aliases from site alias.']; - $options['ssh-options'] = ['never-propagate' => TRUE, 'description' => 'A string of extra options that will be passed to the ssh command', 'example-value' => '-p 100']; - $options['local'] = ['propagate' => TRUE, 'description' => 'Don\'t look in global locations for commandfiles, config, and site aliases']; - } - return $options; -} - /** * Calls a given function, passing through all arguments unchanged. * diff --git a/includes/environment.inc b/includes/environment.inc deleted file mode 100644 index e5bf8dfa16..0000000000 --- a/includes/environment.inc +++ /dev/null @@ -1,45 +0,0 @@ -src examples tests - src/internal-forks - + 0 + 0 diff --git a/rector.php b/rector.php index 05fef6222e..5742cfdb37 100644 --- a/rector.php +++ b/rector.php @@ -2,11 +2,26 @@ declare(strict_types=1); +use Rector\Core\Configuration\Option; +use Rector\Set\ValueObject\SetList; use Rector\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector; use Rector\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return function (ContainerConfigurator $containerConfigurator): void { + $parameters = $containerConfigurator->parameters(); + $containerConfigurator->import(SetList::CODE_QUALITY); + $parameters->set(Option::AUTO_IMPORT_NAMES, true); + $parameters->set(Option::IMPORT_SHORT_CLASSES, false); + $src = [__DIR__ . '/src']; + $parameters->set(Option::SKIP, [ + \Rector\CodeQuality\Rector\Identical\StrlenZeroToIdenticalEmptyStringRector::class => $src, + \Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector::class => $src, + \Rector\CodeQuality\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector::class => $src, + \Rector\CodeQuality\Rector\Array_\CallableThisArrayToAnonymousFunctionRector::class => $src, + \Rector\CodeQuality\Rector\If_\CombineIfRector::class => $src, + \Rector\CodeQuality\Rector\Foreach_\UnusedForeachValueToArrayKeysRector::class => $src, + ]); $services = $containerConfigurator->services(); $services->set(ParamTypeDeclarationRector::class); $services->set(ReturnTypeDeclarationRector::class); diff --git a/src/.editorconfig b/src/.editorconfig index 64b60abd04..1f11b458d9 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -1,8 +1,8 @@ # This file is for unifying the coding style for different editors and IDEs # editorconfig.org -# PHP PSR-2 Coding Standards -# http://www.php-fig.org/psr/psr-2/ +# PHP PSR-12 Coding Standards +# http://www.php-fig.org/psr/psr-12/ [*.php] charset = utf-8 diff --git a/src/Application.php b/src/Application.php index dea7e9b4db..0a224de2cf 100644 --- a/src/Application.php +++ b/src/Application.php @@ -1,4 +1,5 @@ getDefinition() - ->addOption( - new InputOption('--remote-host', null, InputOption::VALUE_REQUIRED, 'Run on a remote server.') - ); - - $this->getDefinition() - ->addOption( - new InputOption('--remote-user', null, InputOption::VALUE_REQUIRED, 'The user to use in remote execution.') - ); - $this->getDefinition() ->addOption( new InputOption('--root', '-r', InputOption::VALUE_REQUIRED, 'The Drupal root for this site.') @@ -97,7 +88,7 @@ public function configureGlobalOptions() // TODO: Implement handling for 'pipe' $this->getDefinition() ->addOption( - new InputOption('--pipe', null, InputOption::VALUE_NONE, 'Select the canonical script-friendly output format.') + new InputOption('--pipe', null, InputOption::VALUE_NONE, 'Select the canonical script-friendly output format. Deprecated - use --format.') ); $this->getDefinition() @@ -350,7 +341,7 @@ protected function discoverCommandsFromConfiguration() } } $this->loadCommandClasses($commandList); - return array_keys($commandList); + return array_values($commandList); } /** @@ -414,4 +405,14 @@ public function renderException(\Exception $e, OutputInterface $output) $this->doRenderException($e, $output); } + + /** + * Renders a caught Throwable. Omits the command docs at end. + */ + public function renderThrowable(\Throwable $e, OutputInterface $output): void + { + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + + $this->doRenderThrowable($e, $output); + } } diff --git a/src/Attributes/Bootstrap.php b/src/Attributes/Bootstrap.php index 38545a3bad..725151c8f6 100644 --- a/src/Attributes/Bootstrap.php +++ b/src/Attributes/Bootstrap.php @@ -4,7 +4,6 @@ use Attribute; use Consolidation\AnnotatedCommand\Parser\CommandInfo; -use Drush\Boot\DrupalBoot; use Drush\Boot\DrupalBootLevels; use JetBrains\PhpStorm\ExpectedValues; diff --git a/src/Attributes/Version.php b/src/Attributes/Version.php new file mode 100644 index 0000000000..a7d9e6989c --- /dev/null +++ b/src/Attributes/Version.php @@ -0,0 +1,25 @@ +getArguments(); + $commandInfo->addAnnotation('version', $args['version']); + } +} diff --git a/src/Backend/BackendPathEvaluator.php b/src/Backend/BackendPathEvaluator.php index ebafd9a175..0272c5e234 100644 --- a/src/Backend/BackendPathEvaluator.php +++ b/src/Backend/BackendPathEvaluator.php @@ -1,4 +1,5 @@ uri; } @@ -32,18 +32,12 @@ public function setUri($uri) $this->uri = $uri; } - /** - * @return int - */ - public function getPhase() + public function getPhase(): int { return $this->phase; } - /** - * @param int $phase - */ - public function setPhase($phase) + public function setPhase(int $phase) { $this->phase = $phase; } @@ -65,14 +59,14 @@ public function reportCommandError($command) // No longer used. } - public function bootstrapPhases() + public function bootstrapPhases(): array { return [ DRUSH_BOOTSTRAP_DRUSH => 'bootstrapDrush', ]; } - public function bootstrapPhaseMap() + public function bootstrapPhaseMap(): array { return [ 'none' => DRUSH_BOOTSTRAP_DRUSH, @@ -103,7 +97,7 @@ public function bootstrapDrush() { } - protected function hasRegisteredSymfonyCommand($application, $name) + protected function hasRegisteredSymfonyCommand($application, $name): bool { try { $application->get($name); diff --git a/src/Boot/Boot.php b/src/Boot/Boot.php index 0a87effcb5..e1e06037d4 100644 --- a/src/Boot/Boot.php +++ b/src/Boot/Boot.php @@ -20,7 +20,7 @@ public function findUri($root, $uri); * * @param string $uri Site to bootstrap */ - public function setUri($uri); + public function setUri(string $uri); /** * This function determines if the specified path points to @@ -44,7 +44,7 @@ public function validRoot($path); * @return string|NULL * The version string for the current version of the software, e.g. 8.1.3 */ - public function getVersion($root); + public function getVersion(string $root); /** * Returns an array that determines what bootstrap phases @@ -64,7 +64,7 @@ public function bootstrapPhases(); * strings (e.g. "full") to the corresponding bootstrap * phase index constant (e.g. DRUSH_BOOTSTRAP_DRUPAL_FULL). */ - public function bootstrapPhaseMap(); + public function bootstrapPhaseMap(): array; /** * Convert from a phase shorthand or constant to a phase index. diff --git a/src/Boot/BootstrapHook.php b/src/Boot/BootstrapHook.php index eb3469dd36..ceaa74ce35 100644 --- a/src/Boot/BootstrapHook.php +++ b/src/Boot/BootstrapHook.php @@ -20,7 +20,7 @@ public function __construct(BootstrapManager $bootstrapManager) $this->bootstrapManager = $bootstrapManager; } - public function initialize(InputInterface $input, AnnotationData $annotationData) + public function initialize(InputInterface $input, AnnotationData $annotationData): void { // Get the @bootstrap annotation/attribute. If there isn't one, then assume NONE. $phase_long = $annotationData->get('bootstrap', 'none'); diff --git a/src/Boot/BootstrapManager.php b/src/Boot/BootstrapManager.php index 91ae8e00c1..6b7644072f 100644 --- a/src/Boot/BootstrapManager.php +++ b/src/Boot/BootstrapManager.php @@ -2,6 +2,15 @@ namespace Drush\Boot; +use Psr\Log\LoggerInterface; +use Symfony\Component\Console\Input\InputAwareInterface; +use Robo\Contract\OutputAwareInterface; +use Robo\Contract\ProgressIndicatorAwareInterface; +use Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface; +use Robo\Contract\VerbosityThresholdInterface; +use Consolidation\SiteAlias\SiteAliasManagerAwareInterface; +use Consolidation\SiteProcess\ProcessManagerAwareInterface; +use Consolidation\AnnotatedCommand\Input\StdinAwareInterface; use Consolidation\AnnotatedCommand\AnnotationData; use DrupalFinder\DrupalFinder; use Drush\Config\ConfigAwareTrait; @@ -24,12 +33,12 @@ class BootstrapManager implements LoggerAwareInterface, AutoloaderAwareInterface protected $drupalFinder; /** - * @var \Drush\Boot\Boot[] + * @var Boot[] */ protected $bootstrapCandidates = []; /** - * @var \Drush\Boot\Boot + * @var Boot */ protected $bootstrap; @@ -49,10 +58,7 @@ public function getPhase() return $this->bootstrap()->getPhase(); } - /** - * @param int $phase - */ - protected function setPhase($phase) + protected function setPhase(int $phase): void { if ($this->bootstrap) { $this->bootstrap()->setPhase($phase); @@ -65,14 +71,14 @@ protected function setPhase($phase) * @param \Drush\Boot\Boot|Array * List of boot candidates */ - public function add($candidateList) + public function add($candidateList): void { foreach (func_get_args() as $candidate) { $this->bootstrapCandidates[] = $candidate; } } - public function drupalFinder() + public function drupalFinder(): DrupalFinder { if (!isset($this->drupalFinder)) { $this->drupalFinder = new DrupalFinder(); @@ -80,7 +86,7 @@ public function drupalFinder() return $this->drupalFinder; } - public function setDrupalFinder(DrupalFinder $drupalFinder) + public function setDrupalFinder(DrupalFinder $drupalFinder): void { $this->drupalFinder = $drupalFinder; } @@ -88,7 +94,7 @@ public function setDrupalFinder(DrupalFinder $drupalFinder) /** * Return the framework root selected by the user. */ - public function getRoot() + public function getRoot(): string { return $this->drupalFinder()->getDrupalRoot(); } @@ -96,12 +102,12 @@ public function getRoot() /** * Return the composer root for the selected Drupal site. */ - public function getComposerRoot() + public function getComposerRoot(): string { return $this->drupalFinder()->getComposerRoot(); } - public function locateRoot($root, $start_path = null) + public function locateRoot($root, $start_path = null): void { // TODO: Throw if we already bootstrapped a framework? @@ -133,7 +139,7 @@ public function selectUri($cwd) return $uri; } - public function setUri($uri) + public function setUri($uri): void { // TODO: Throw if we already bootstrapped a framework? // n.b. site-install needs to set the uri. @@ -145,10 +151,8 @@ public function setUri($uri) * be the latched bootstrap object if we have started * bootstrapping; otherwise, it will be whichever bootstrap * object is best for the selected root. - * - * @return \Drush\Boot\Boot */ - public function bootstrap() + public function bootstrap(): Boot { if (!$this->bootstrap) { $this->bootstrap = $this->selectBootstrapClass(); @@ -159,7 +163,7 @@ public function bootstrap() /** * For use in testing */ - public function injectBootstrap($bootstrap) + public function injectBootstrap(Boot $bootstrap): void { $this->inflect($bootstrap); $this->bootstrap = $bootstrap; @@ -172,10 +176,8 @@ public function injectBootstrap($bootstrap) /** * Look up the best bootstrap class for the given location * from the set of available candidates. - * - * @return \Drush\Boot\Boot */ - public function bootstrapObjectForRoot($path) + public function bootstrapObjectForRoot($path): Boot { foreach ($this->bootstrapCandidates as $candidate) { if ($candidate->validRoot($path)) { @@ -198,7 +200,7 @@ public function bootstrapObjectForRoot($path) * be 'latched', and further calls to Drush::bootstrap() * will always return the same object. */ - protected function selectBootstrapClass() + protected function selectBootstrapClass(): Boot { // Once we have selected a Drupal root, we will reduce our bootstrap // candidates down to just the one used to select this site root. @@ -210,7 +212,7 @@ protected function selectBootstrapClass() * object being used, and do not allow it to change any * longer. */ - public function latch($bootstrap) + public function latch(Boot $bootstrap): void { $this->bootstrap = $bootstrap; } @@ -223,11 +225,10 @@ public function latch($bootstrap) * (optional) If TRUE, return an array of method names index by their * corresponding phase values. Otherwise return an array of phase values. * - * @return array * * @see \Drush\Boot\Boot::bootstrapPhases() */ - public function bootstrapPhases($function_names = false) + public function bootstrapPhases(bool $function_names = false): array { $result = []; @@ -253,15 +254,13 @@ public function bootstrapPhases($function_names = false) * function itself but can be useful for other code called from within this * function, to know if e.g. a caller is in the process of booting to the * specified level. If specified, it should never be lower than $phase. - * @param \Consolidation\AnnotatedCommand\AnnotationData $annotationData + * @param AnnotationData $annotationData * Optional annotation data from the command. * - * @return bool * TRUE if the specified bootstrap phase has completed. - * * @see \Drush\Boot\Boot::bootstrapPhases() */ - public function doBootstrap($phase, $phase_max = false, AnnotationData $annotationData = null) + public function doBootstrap(int $phase, $phase_max = false, AnnotationData $annotationData = null): bool { $bootstrap = $this->bootstrap(); $phases = $this->bootstrapPhases(true); @@ -301,7 +300,7 @@ public function doBootstrap($phase, $phase_max = false, AnnotationData $annotati /** * hasBootstrap determines whether the manager has a bootstrap object yet. */ - public function hasBootstrap() + public function hasBootstrap(): bool { return $this->bootstrap != null; } @@ -312,10 +311,9 @@ public function hasBootstrap() * @param int $phase * The bootstrap phase to test * - * @return bool * TRUE if the specified bootstrap phase has completed. */ - public function hasBootstrapped($phase) + public function hasBootstrapped(int $phase): bool { return $this->getPhase() >= $phase; } @@ -334,12 +332,10 @@ public function hasBootstrapped($phase) * @param int $phase * The bootstrap phase to validate to. * - * @return bool * TRUE if bootstrap is possible, FALSE if the validation failed. - * * @see \Drush\Boot\Boot::bootstrapPhases() */ - public function bootstrapValidate($phase) + public function bootstrapValidate(int $phase): bool { $bootstrap = $this->bootstrap(); $phases = $this->bootstrapPhases(true); @@ -353,11 +349,7 @@ public function bootstrapValidate($phase) } if ($phase_index > $validated_phase) { $current_phase .= 'Validate'; - if (method_exists($bootstrap, $current_phase)) { - $result_cache[$phase_index] = $bootstrap->{$current_phase}($this); - } else { - $result_cache[$phase_index] = true; - } + $result_cache[$phase_index] = method_exists($bootstrap, $current_phase) ? $bootstrap->{$current_phase}($this) : true; $validated_phase = $phase_index; } } @@ -370,17 +362,14 @@ public function bootstrapValidate($phase) * * @param string $bootstrapPhase * Name of phase to bootstrap to. Will be converted to appropriate index. - * @param \Consolidation\AnnotatedCommand\AnnotationData $annotationData * Optional annotation data from the command. * - * @return bool * TRUE if the specified bootstrap phase has completed. - * * @throws \Exception * Thrown when an unknown bootstrap phase is passed in the annotation * data. */ - public function bootstrapToPhase($bootstrapPhase, AnnotationData $annotationData = null) + public function bootstrapToPhase(string $bootstrapPhase, AnnotationData $annotationData = null): bool { $this->logger->info('Starting bootstrap to {phase}', ['phase' => $bootstrapPhase]); $phase = $this->bootstrap()->lookUpPhaseIndex($bootstrapPhase); @@ -411,13 +400,10 @@ protected function maxPhaseLimit($bootstrap_str) * * @param int $max_phase_index * Only attempt bootstrap to the specified level. - * @param \Consolidation\AnnotatedCommand\AnnotationData $annotationData * Optional annotation data from the command. - * - * @return bool * TRUE if the specified bootstrap phase has completed. */ - public function bootstrapToPhaseIndex($max_phase_index, AnnotationData $annotationData = null) + public function bootstrapToPhaseIndex(int $max_phase_index, AnnotationData $annotationData = null): bool { if ($max_phase_index == DRUSH_BOOTSTRAP_MAX) { // Try get a max phase. @@ -460,13 +446,12 @@ public function bootstrapToPhaseIndex($max_phase_index, AnnotationData $annotati * * @param int $max_phase_index * (optional) Only attempt bootstrap to the specified level. - * @param \Consolidation\AnnotatedCommand\AnnotationData $annotationData + * @param AnnotationData $annotationData * Optional annotation data from the command. * - * @return int * The maximum phase to which we bootstrapped. */ - public function bootstrapMax($max_phase_index = false, AnnotationData $annotationData = null) + public function bootstrapMax($max_phase_index = false, AnnotationData $annotationData = null): int { // Bootstrap as far as we can without throwing an error, but log for // debugging purposes. @@ -506,47 +491,47 @@ public function bootstrapMax($max_phase_index = false, AnnotationData $annotatio /** * Allow those with an instance to us to the BootstrapManager to use its logger */ - public function logger() + public function logger(): ?LoggerInterface { return $this->logger; } - public function inflect($object) + public function inflect($object): void { // See \Drush\Runtime\DependencyInjection::addDrushServices and // \Robo\Robo\addInflectors $container = $this->getContainer(); - if ($object instanceof \Robo\Contract\ConfigAwareInterface) { + if ($object instanceof ConfigAwareInterface) { $object->setConfig($container->get('config')); } - if ($object instanceof \Psr\Log\LoggerAwareInterface) { + if ($object instanceof LoggerAwareInterface) { $object->setLogger($container->get('logger')); } - if ($object instanceof \League\Container\ContainerAwareInterface) { + if ($object instanceof ContainerAwareInterface) { $object->setContainer($container->get('container')); } - if ($object instanceof \Symfony\Component\Console\Input\InputAwareInterface) { + if ($object instanceof InputAwareInterface) { $object->setInput($container->get('input')); } - if ($object instanceof \Robo\Contract\OutputAwareInterface) { + if ($object instanceof OutputAwareInterface) { $object->setOutput($container->get('output')); } - if ($object instanceof \Robo\Contract\ProgressIndicatorAwareInterface) { + if ($object instanceof ProgressIndicatorAwareInterface) { $object->setProgressIndicator($container->get('progressIndicator')); } - if ($object instanceof \Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface) { + if ($object instanceof CustomEventAwareInterface) { $object->setHookManager($container->get('hookManager')); } - if ($object instanceof \Robo\Contract\VerbosityThresholdInterface) { + if ($object instanceof VerbosityThresholdInterface) { $object->setOutputAdapter($container->get('outputAdapter')); } - if ($object instanceof \Consolidation\SiteAlias\SiteAliasManagerAwareInterface) { + if ($object instanceof SiteAliasManagerAwareInterface) { $object->setSiteAliasManager($container->get('site.alias.manager')); } - if ($object instanceof \Consolidation\SiteProcess\ProcessManagerAwareInterface) { + if ($object instanceof ProcessManagerAwareInterface) { $object->setProcessManager($container->get('process.manager')); } - if ($object instanceof \Consolidation\AnnotatedCommand\Input\StdinAwareInterface) { + if ($object instanceof StdinAwareInterface) { $object->setStdinHandler($container->get('stdinHandler')); } } diff --git a/src/Boot/DrupalBoot.php b/src/Boot/DrupalBoot.php index 5ad5dbd055..197a9e045c 100644 --- a/src/Boot/DrupalBoot.php +++ b/src/Boot/DrupalBoot.php @@ -11,7 +11,7 @@ abstract class DrupalBoot extends BaseBoot * Select the best URI for the provided cwd. Only called * if the user did not explicitly specify a URI. */ - public function findUri($root, $cwd) + public function findUri($root, $cwd): string { if (Path::isBasePath($root, $cwd)) { $siteDir = $this->scanUpForUri($root, $cwd); @@ -36,7 +36,7 @@ protected function scanUpForUri($root, $scan) return false; } $scan = $next; - if ($scan == $root) { + if ($scan === $root) { return false; } } @@ -70,7 +70,7 @@ public function confPath($require_settings = true, $reset = false) * method is called, if defined. The validate method name is the * bootstrap method name with "_validate" appended. */ - public function bootstrapPhases() + public function bootstrapPhases(): array { return parent::bootstrapPhases() + [ DRUSH_BOOTSTRAP_DRUPAL_ROOT => 'bootstrapDrupalRoot', @@ -81,7 +81,7 @@ public function bootstrapPhases() ]; } - public function bootstrapPhaseMap() + public function bootstrapPhaseMap(): array { return parent::bootstrapPhaseMap() + [ 'root' => DRUSH_BOOTSTRAP_DRUPAL_ROOT, @@ -99,7 +99,7 @@ public function bootstrapPhaseMap() * * In this function, we will check if a valid Drupal directory is available. */ - public function bootstrapDrupalRootValidate(BootstrapManager $manager) + public function bootstrapDrupalRootValidate(BootstrapManager $manager): bool { $drupal_root = $manager->getRoot(); return (bool) $drupal_root; @@ -115,7 +115,7 @@ public function bootstrapDrupalRootValidate(BootstrapManager $manager) * We can now include files from the Drupal tree, and figure * out more context about the codebase, such as the version of Drupal. */ - public function bootstrapDrupalRoot(BootstrapManager $manager) + public function bootstrapDrupalRoot(BootstrapManager $manager): void { $drupal_root = $manager->getRoot(); chdir($drupal_root); diff --git a/src/Boot/DrupalBoot8.php b/src/Boot/DrupalBoot8.php index 2e2d9cfda0..47f54a37e7 100644 --- a/src/Boot/DrupalBoot8.php +++ b/src/Boot/DrupalBoot8.php @@ -2,9 +2,11 @@ namespace Drush\Boot; +use Drupal\Core\DrupalKernelInterface; use Consolidation\AnnotatedCommand\AnnotationData; use Drupal\Core\Database\Database; use Drupal\Core\DrupalKernel; +use Drush\Config\ConfigLocator; use Drush\Drupal\DrushLoggerServiceProvider; use Drush\Drupal\DrushServiceModifier; use Drush\Drush; @@ -23,35 +25,26 @@ class DrupalBoot8 extends DrupalBoot implements AutoloaderAwareInterface protected $drupalLoggerAdapter; /** - * @var \Drupal\Core\DrupalKernelInterface + * @var DrupalKernelInterface */ protected $kernel; /** - * @var \Symfony\Component\HttpFoundation\Request + * @var Request */ protected $request; - /** - * @return \Symfony\Component\HttpFoundation\Request - */ - public function getRequest() + public function getRequest(): Request { return $this->request; } - /** - * @param \Symfony\Component\HttpFoundation\Request $request - */ - public function setRequest($request) + public function setRequest(Request $request): void { $this->request = $request; } - /** - * @return \Drupal\Core\DrupalKernelInterface - */ - public function getKernel() + public function getKernel(): DrupalKernelInterface { return $this->kernel; } @@ -65,7 +58,7 @@ public function getKernel() * (i.e., after bootstrapping Drupal), then we also need to * update the logger reference in that adapter. */ - public function setLogger(LoggerInterface $logger) + public function setLogger(LoggerInterface $logger): void { if ($this->drupalLoggerAdapter) { $this->drupalLoggerAdapter->setLogger($logger); @@ -87,17 +80,13 @@ public function validRoot($path) } } - public function getVersion($drupal_root) + public function getVersion($drupal_root): string { // Are the class constants available? if (!$this->hasAutoloader()) { - throw new \Exception('Cannot access Drupal 8 class constants - Drupal autoloader not loaded yet.'); - } - // Drush depends on bootstrap being loaded at this point. - require_once $drupal_root .'/core/includes/bootstrap.inc'; - if (defined('\Drupal::VERSION')) { - return \Drupal::VERSION; + throw new \Exception('Cannot access Drupal class constants - Drupal autoloader not loaded yet.'); } + return \Drupal::VERSION; } /** @@ -121,12 +110,12 @@ public function confPath($require_settings = true, $reset = false) return $site_path; } - public function bootstrapDrupalCore(BootstrapManager $manager, $drupal_root) + public function bootstrapDrupalCore(BootstrapManager $manager, $drupal_root): string { return Path::join($drupal_root, 'core'); } - public function bootstrapDrupalSiteValidate(BootstrapManager $manager) + public function bootstrapDrupalSiteValidate(BootstrapManager $manager): bool { parent::bootstrapDrupalSiteValidate($manager); @@ -156,13 +145,21 @@ public function bootstrapDrupalSiteValidate(BootstrapManager $manager) * Called by bootstrapDrupalSite to do the main work * of the drush drupal site bootstrap. */ - public function bootstrapDoDrupalSite(BootstrapManager $manager) + public function bootstrapDoDrupalSite(BootstrapManager $manager): void { - // Note: this reports the'default' during site:install even if we eventually install to a different multisite. + $siteConfig = $this->confPath() . '/drush.yml'; + + if (ConfigLocator::addSiteSpecificConfig(Drush::config(), $siteConfig)) { + $this->logger->debug(dt("Loaded Drush config file at !file.", ['!file' => $siteConfig])); + } else { + $this->logger->debug(dt("Could not find a Drush config file at !file.", ['!file' => $siteConfig])); + } + + // Note: this reports the 'default' site during site:install even if we eventually install to a different multisite. $this->logger->info(dt("Initialized Drupal site !site at !site_root", ['!site' => $this->getRequest()->getHttpHost(), '!site_root' => $this->confPath()])); } - public function bootstrapDrupalConfigurationValidate(BootstrapManager $manager) + public function bootstrapDrupalConfigurationValidate(BootstrapManager $manager): bool { $conf_file = $this->confPath() . '/settings.php'; if (!file_exists($conf_file)) { @@ -174,7 +171,7 @@ public function bootstrapDrupalConfigurationValidate(BootstrapManager $manager) return true; } - public function bootstrapDrupalDatabaseValidate(BootstrapManager $manager) + public function bootstrapDrupalDatabaseValidate(BootstrapManager $manager): bool { // Drupal requires PDO, and Drush requires php 5.6+ which ships with PDO // but PHP may be compiled with --disable-pdo. @@ -199,13 +196,13 @@ public function bootstrapDrupalDatabaseValidate(BootstrapManager $manager) return true; } - public function bootstrapDrupalDatabase(BootstrapManager $manager) + public function bootstrapDrupalDatabase(BootstrapManager $manager): void { // D8 omits this bootstrap level as nothing special needs to be done. parent::bootstrapDrupalDatabase($manager); } - public function bootstrapDrupalConfiguration(BootstrapManager $manager, AnnotationData $annotationData = null) + public function bootstrapDrupalConfiguration(BootstrapManager $manager, AnnotationData $annotationData = null): void { // Coax \Drupal\Core\DrupalKernel::discoverServiceProviders to add our logger. $GLOBALS['conf']['container_service_providers'][] = DrushLoggerServiceProvider::class; @@ -219,7 +216,7 @@ public function bootstrapDrupalConfiguration(BootstrapManager $manager, Annotati $request = $this->getRequest(); $kernel_factory = Kernels::getKernelFactory($kernel); $allow_dumping = $kernel !== Kernels::UPDATE; - /** @var \Drupal\Core\DrupalKernelInterface kernel */ + /** @var DrupalKernelInterface kernel */ $this->kernel = $kernel_factory($request, $classloader, 'prod', $allow_dumping, $manager->getRoot()); // Include Drush services in the container. // @see Drush\Drupal\DrupalKernel::addServiceModifier() @@ -234,7 +231,7 @@ public function bootstrapDrupalConfiguration(BootstrapManager $manager, Annotati parent::bootstrapDrupalConfiguration($manager); } - public function bootstrapDrupalFull(BootstrapManager $manager) + public function bootstrapDrupalFull(BootstrapManager $manager): void { $this->logger->debug(dt('Start bootstrap of the Drupal Kernel.')); $this->kernel->boot(); @@ -245,7 +242,7 @@ public function bootstrapDrupalFull(BootstrapManager $manager) $this->addDrupalModuleDrushCommands($manager); } - public function addDrupalModuleDrushCommands($manager) + public function addDrupalModuleDrushCommands($manager): void { $application = Drush::getApplication(); $runner = Drush::runner(); @@ -288,7 +285,7 @@ public function addDrupalModuleDrushCommands($manager) /** * {@inheritdoc} */ - public function terminate() + public function terminate(): void { parent::terminate(); diff --git a/src/Boot/DrupalBootLevels.php b/src/Boot/DrupalBootLevels.php index 224a19262b..8962dd8ad2 100644 --- a/src/Boot/DrupalBootLevels.php +++ b/src/Boot/DrupalBootLevels.php @@ -80,7 +80,7 @@ class DrupalBootLevels */ const FULL = 5; - public static function getPhaseName($index) + public static function getPhaseName($index): string { $reflection = new \ReflectionClass(self::class); return strtolower(array_search($index, $reflection->getConstants())); diff --git a/src/Boot/EmptyBoot.php b/src/Boot/EmptyBoot.php index dfdcd3f007..5869dceb3f 100644 --- a/src/Boot/EmptyBoot.php +++ b/src/Boot/EmptyBoot.php @@ -14,20 +14,19 @@ */ class EmptyBoot extends BaseBoot { - - public function validRoot($path) + public function validRoot($path): bool { return false; } - public function bootstrapPhases() + public function bootstrapPhases(): array { return [ DRUSH_BOOTSTRAP_DRUSH => '_drush_bootstrap_drush', ]; } - public function bootstrapInitPhases() + public function bootstrapInitPhases(): array { return [DRUSH_BOOTSTRAP_DRUSH]; } diff --git a/src/Boot/Kernels.php b/src/Boot/Kernels.php index dc7278a9b5..aa2e395113 100644 --- a/src/Boot/Kernels.php +++ b/src/Boot/Kernels.php @@ -11,7 +11,6 @@ */ final class Kernels { - /** * The default kernel that is used on standard requests. * @@ -36,7 +35,7 @@ final class Kernels /** * Returns the available kernels. */ - public static function availableKernels() + public static function availableKernels(): array { return [ static::DRUPAL, @@ -51,10 +50,9 @@ public static function availableKernels() * @param string $kernel * The kernel to retrieve. * - * @return callable * The factory method. */ - public static function getKernelFactory($kernel) + public static function getKernelFactory(string $kernel): array { $factories = [ Kernels::DRUPAL => [DrushDrupalKernel::class, 'createFromRequest'], diff --git a/src/Cache/CacheInterface.php b/src/Cache/CacheInterface.php deleted file mode 100644 index 5cf54469e9..0000000000 --- a/src/Cache/CacheInterface.php +++ /dev/null @@ -1,114 +0,0 @@ -cacheBackend = $cacheBackend; - } - - /** - * Test for an entry from the cache - * @param string $key - * @return boolean - */ - public function has($key) - { - $cacheItem = $this->cacheBackend->get($key); - return $this->valid($cacheItem); - } - /** - * Get an entry from the cache - * @param string $key - * @return array - */ - public function get($key) - { - $cacheItem = $this->cacheBackend->get($key); - if (!$this->valid($cacheItem)) { - return []; - } - // TODO: FileCache::get() should just return the - // data element, not the entire cacheItem. Then we - // could make it implement SimpleCacheInterface & do - // away with this adapter class. - return $cacheItem->data; - } - /** - * Store an entry in the cache - * @param string $key - * @param array $data - */ - public function set($key, $data) - { - $this->cacheBackend->set($key, $data); - } - - protected function valid($cacheItem) - { - return is_object($cacheItem) && isset($cacheItem->data); - } -} diff --git a/src/Cache/FileCache.php b/src/Cache/FileCache.php deleted file mode 100644 index 848fdd6f70..0000000000 --- a/src/Cache/FileCache.php +++ /dev/null @@ -1,175 +0,0 @@ -bin = $bin; - $this->directory = $this->cacheDirectory(); - } - - /** - * Returns the cache directory for the given bin. - * - * @param string $bin - */ - public function cacheDirectory($bin = null) - { - $bin = $bin ? $bin : $this->bin; - return Path::join(Drush::config()->cache(), $bin); - } - - public function get($cid) - { - $cids = [$cid]; - $cache = $this->getMultiple($cids); - return reset($cache); - } - - public function getMultiple(&$cids) - { - try { - $cache = []; - foreach ($cids as $cid) { - $filename = $this->getFilePath($cid); - if (!file_exists($filename)) { - return []; - } - - $item = $this->readFile($filename); - if ($item) { - $cache[$cid] = $item; - } - } - $cids = array_diff($cids, array_keys($cache)); - return $cache; - } catch (\Exception $e) { - return []; - } - } - - /** - * Returns the contents of the given filename unserialized. - * - * @param string $filename - * Absolute path to filename to read contents from. - */ - public function readFile($filename) - { - $item = file_get_contents($filename); - return $item ? unserialize($item) : false; - } - - public function set($cid, $data, $expire = DRUSH_CACHE_PERMANENT) - { - $created = time(); - - $cache = new \stdClass; - $cache->cid = $cid; - $cache->data = is_object($data) ? clone $data : $data; - $cache->created = $created; - if ($expire == DRUSH_CACHE_TEMPORARY) { - $cache->expire = $created + 2591999; - } elseif ($expire != DRUSH_CACHE_PERMANENT && $expire < 2592000) { - // Expire time is in seconds if less than 30 days, otherwise is a timestamp. - $cache->expire = $created + $expire; - } else { - $cache->expire = $expire; - } - - // Ensure the cache directory still exists, in case a backend process - // cleared the cache after the cache was initialized. - $fs = new Filesystem(); - $fs->mkdir($this->directory); - - $filename = $this->getFilePath($cid); - return $this->writeFile($filename, $cache); - } - - /** - * Serializes data and write it to the given filename. - * - * @param string $filename - * Absolute path to filename to write cache data. - * @param $cache - * Cache data to serialize and write to $filename. - */ - public function writeFile($filename, $cache) - { - return file_put_contents($filename, serialize($cache)); - } - - public function clear($cid = null, $wildcard = false) - { - $fs = new Filesystem(); - $bin_dir = $this->cacheDirectory(); - $files = []; - if (empty($cid)) { - $fs->remove($bin_dir); - } else { - if ($wildcard) { - if ($cid == '*') { - $fs->remove($bin_dir); - } else { - $files = Finder::create() - ->files() - ->name($cid) - ->in($bin_dir); - } - } else { - $files[] = $this->getFilePath($cid); - } - - $fs->remove($files); - } - } - - public function isEmpty() - { - $files = Finder::create() - ->files() - ->name() - ->exclude() - ->depth(0) - ->in($this->directory); - return empty($files); - } - - /** - * Converts a cache id to a full path. - * - * @param $cid - * The cache ID of the data to retrieve. - * - * @return - * The full path to the cache file. - */ - protected function getFilePath($cid) - { - return $this->directory . '/' . str_replace([':', '\\', '/'], '.', $cid) . self::EXTENSION; - } -} diff --git a/src/Cache/JSONCache.php b/src/Cache/JSONCache.php deleted file mode 100644 index 5f5c8ffd97..0000000000 --- a/src/Cache/JSONCache.php +++ /dev/null @@ -1,32 +0,0 @@ - and &, so we do it with str_replace(). - $json = str_replace(['<', '>', '&'], ['\u003c', '\u003e', '\u0026'], $json); - return file_put_contents($filename, $json); - } -} diff --git a/src/Command/DrushCommandInfoAlterer.php b/src/Command/DrushCommandInfoAlterer.php index 8b6effabca..4fdf1e5955 100644 --- a/src/Command/DrushCommandInfoAlterer.php +++ b/src/Command/DrushCommandInfoAlterer.php @@ -1,4 +1,5 @@ getInput(); diff --git a/src/Command/RemoteCommandProxy.php b/src/Command/RemoteCommandProxy.php index 488f4033e3..ff83f5c17a 100644 --- a/src/Command/RemoteCommandProxy.php +++ b/src/Command/RemoteCommandProxy.php @@ -1,4 +1,5 @@ ignoreValidationErrors(); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): void { $this->redispatchHook->redispatchIfRemote($input); $name = $this->getName(); diff --git a/src/Command/ServiceCommandlist.php b/src/Command/ServiceCommandlist.php index c8dabf9cce..e1815e9fde 100644 --- a/src/Command/ServiceCommandlist.php +++ b/src/Command/ServiceCommandlist.php @@ -1,4 +1,5 @@ commandList[] = $command; } - public function getCommandList() + public function getCommandList(): array { return $this->commandList; } diff --git a/src/Commands/DrushCommands.php b/src/Commands/DrushCommands.php index a9a400538d..f901645fcc 100644 --- a/src/Commands/DrushCommands.php +++ b/src/Commands/DrushCommands.php @@ -1,6 +1,10 @@ io) { // Specify our own Style class when needed. @@ -64,10 +63,8 @@ protected function io() /** * Returns a logger object. - * - * @return LoggerInterface */ - protected function logger() + protected function logger(): ?DrushLoggerManager { return $this->logger; } @@ -78,9 +75,9 @@ protected function logger() * @param string $file * Full path to a file. */ - protected function printFile($file) + protected function printFile(string $file): void { - if ((substr($file, -4) == ".htm") || (substr($file, -5) == ".html")) { + if (str_ends_with($file, ".htm") || str_ends_with($file, ".html")) { $tmp_file = drush_tempnam(basename($file)); file_put_contents($tmp_file, drush_html_to_text(file_get_contents($file))); $file = $tmp_file; @@ -102,7 +99,7 @@ protected function printFile($file) * * @hook pre-command * * - * @param \Consolidation\AnnotatedCommand\CommandData $commandData + * @param CommandData $commandData */ public function preHook(CommandData $commandData) { @@ -126,7 +123,7 @@ protected function printFileTopic(CommandData $commandData) * * @see https://stackoverflow.com/questions/32681165/how-do-you-log-all-api-calls-using-guzzle-6. */ - protected function getStack(): \GuzzleHttp\HandlerStack + protected function getStack(): HandlerStack { $stack = HandlerStack::create(); $stack->push(Middleware::log($this->logger(), new MessageFormatter(Drush::debug() ? MessageFormatter::DEBUG : MessageFormatter::SHORT))); diff --git a/src/Commands/ExampleCommands.php b/src/Commands/ExampleCommands.php index 11d4a32862..75e1ea97c0 100644 --- a/src/Commands/ExampleCommands.php +++ b/src/Commands/ExampleCommands.php @@ -1,14 +1,9 @@ 'table']) + public function exampleTable($options = ['format' => 'table']): RowsOfFields { $tableData = [ 'en' => [ 'first' => 'One', 'second' => 'Two', 'third' => 'Three' ], diff --git a/src/Commands/LegacyCommands.php b/src/Commands/LegacyCommands.php index 3d382552ed..df7086e896 100644 --- a/src/Commands/LegacyCommands.php +++ b/src/Commands/LegacyCommands.php @@ -1,8 +1,20 @@ -p 100) * @option tty Create a tty (e.g. to run an interactive program). */ - public function optionsetProcBuild($options = ['ssh-options' => self::REQ, 'tty' => false]) + public function optionsetProcBuild($options = ['ssh-options' => self::REQ, 'tty' => false]): void { } @@ -25,7 +25,7 @@ public function optionsetProcBuild($options = ['ssh-options' => self::REQ, 'tty' * @option editor A string of bash which launches user's preferred text editor. Defaults to ${VISUAL-${EDITOR-vi}}. * @option bg Launch editor in background process. */ - public function optionsetGetEditor($options = ['editor' => '', 'bg' => false]) + public function optionsetGetEditor($options = ['editor' => '', 'bg' => false]): void { } @@ -33,7 +33,7 @@ public function optionsetGetEditor($options = ['editor' => '', 'bg' => false]) * @hook option @optionset_ssh * @option ssh-options A string appended to ssh command during rsync, sql-sync, etc. */ - public function optionsetSsh($options = ['ssh-options' => self::REQ]) + public function optionsetSsh($options = ['ssh-options' => self::REQ]): void { } @@ -44,7 +44,7 @@ public function optionsetSsh($options = ['ssh-options' => self::REQ]) * @option target The name of a target within the specified database connection. * @option show-passwords Show password on the CLI. Useful for debugging. */ - public function optionsetSql($options = ['database' => 'default', 'target' => 'default', 'db-url' => self::REQ, 'show-passwords' => false]) + public function optionsetSql($options = ['database' => 'default', 'target' => 'default', 'db-url' => self::REQ, 'show-passwords' => false]): void { } @@ -63,7 +63,7 @@ public function optionsetTableSelection($options = [ 'tables-key' => self::REQ, 'skip-tables-list' => self::REQ, 'structure-tables-list' => self::REQ, - 'tables-list' => self::REQ]) + 'tables-list' => self::REQ]): void { } } diff --git a/src/Commands/ValidatorsCommands.php b/src/Commands/ValidatorsCommands.php index f6be086645..d3e77f0bee 100644 --- a/src/Commands/ValidatorsCommands.php +++ b/src/Commands/ValidatorsCommands.php @@ -1,4 +1,5 @@ get('validate-module-enabled')); $loaded = \Drupal::moduleHandler()->getModuleList(); @@ -56,13 +55,12 @@ public function validateModuleEnabled(Input $input, AnnotationData $annotationDa * Annotation value should be the name of the argument containing the path. * * @hook validate @validate-file-exists - * @param \Consolidation\AnnotatedCommand\CommandData $commandData - * @return \Consolidation\AnnotatedCommand\CommandError|null + * @return CommandError|null */ public function validateFileExists(CommandData $commandData) { $missing = []; - $arg_names = _convert_csv_to_array($commandData->annotationData()->get('validate-file-exists', null)); + $arg_names = StringUtils::csvToArray($commandData->annotationData()->get('validate-file-exists', null)); foreach ($arg_names as $arg_name) { if ($commandData->input()->hasArgument($arg_name)) { $path = $commandData->input()->getArgument($arg_name); @@ -87,13 +85,12 @@ public function validateFileExists(CommandData $commandData) * Annotation value should be extension name. If multiple, delimit by a comma. * * @hook validate @validate-php-extension - * @param \Consolidation\AnnotatedCommand\CommandData $commandData - * @return \Consolidation\AnnotatedCommand\CommandError|null + * @return CommandError|null */ public function validatePHPExtension(CommandData $commandData) { $missing = []; - $arg_names = _convert_csv_to_array($commandData->annotationData()->get('validate-php-extension', null)); + $arg_names = StringUtils::csvToArray($commandData->annotationData()->get('validate-php-extension', null)); foreach ($arg_names as $arg_name) { if (!extension_loaded($arg_name)) { $missing[] = $arg_name; @@ -112,8 +109,7 @@ public function validatePHPExtension(CommandData $commandData) * Annotation value should be the name of the argument/option containing the permission(s). * * @hook validate @validate-permissions - * @param \Consolidation\AnnotatedCommand\CommandData $commandData - * @return \Consolidation\AnnotatedCommand\CommandError|null + * @return CommandError|null */ public function validatePermissions(CommandData $commandData) { diff --git a/src/Commands/config/ConfigPullCommands.php b/src/Commands/config/ConfigPullCommands.php index 0b123dcc63..b3fa8ab7b2 100644 --- a/src/Commands/config/ConfigPullCommands.php +++ b/src/Commands/config/ConfigPullCommands.php @@ -1,4 +1,5 @@ false, 'label' => 'sync', 'runner' => null, 'format' => 'null']) + public function pull(string $source, string $destination, array $options = ['safe' => false, 'label' => 'sync', 'runner' => null, 'format' => 'null']): PropertyList { $global_options = Drush::redispatchOptions() + ['strict' => 0]; $sourceRecord = $this->siteAliasManager()->get($source); @@ -89,7 +89,7 @@ public function pull($source, $destination, $options = ['safe' => false, 'label' /** * @hook validate config-pull */ - public function validateConfigPull(CommandData $commandData) + public function validateConfigPull(CommandData $commandData): void { if ($commandData->input()->getOption('safe')) { $destinationRecord = $this->siteAliasManager()->get($commandData->input()->getArgument('destination')); diff --git a/src/Commands/core/BrowseCommands.php b/src/Commands/core/BrowseCommands.php index 8bf9c2f3a3..8ef2cbf910 100644 --- a/src/Commands/core/BrowseCommands.php +++ b/src/Commands/core/BrowseCommands.php @@ -1,4 +1,5 @@ 'json']) + public function get($cid, $bin = 'default', $options = ['format' => 'json']): PropertyList { $result = \Drupal::cache($bin)->get($cid); if (empty($result)) { @@ -69,7 +69,7 @@ public function get($cid, $bin = 'default', $options = ['format' => 'json']) * @usage drush cache:tag node:12,user:4 * Purge content associated with two cache tags. */ - public function tags($tags) + public function tags(string $tags): void { $tags = StringUtils::csvToArray($tags); Cache::invalidateTags($tags); @@ -92,7 +92,7 @@ public function tags($tags) * @usage drush cc bin entity,bootstrap * Clear the entity and bootstrap cache bins. */ - public function clear($type, array $args, $options = ['cache-clear' => true]) + public function clear(string $type, array $args, $options = ['cache-clear' => true]) { $boot_manager = Drush::bootstrapManager(); @@ -114,7 +114,7 @@ public function clear($type, array $args, $options = ['cache-clear' => true]) /** * @hook interact cache-clear */ - public function interact($input, $output) + public function interact($input, $output): void { $boot_manager = Drush::bootstrapManager(); if (empty($input->getArgument('type'))) { @@ -221,8 +221,6 @@ public function rebuild($options = ['cache-clear' => true]) require_once DRUSH_DRUPAL_CORE . '/includes/utility.inc'; $request = Drush::bootstrap()->getRequest(); - // Manually resemble early bootstrap of DrupalKernel::boot(). - require_once DRUSH_DRUPAL_CORE . '/includes/bootstrap.inc'; DrupalKernel::bootEnvironment(); // Avoid 'Only variables should be passed by reference' @@ -233,16 +231,12 @@ public function rebuild($options = ['cache-clear' => true]) // drupal_rebuild() calls drupal_flush_all_caches() itself, so we don't do it manually. drupal_rebuild($autoloader, $request); $this->logger()->success(dt('Cache rebuild complete.')); - - // As this command replaces `drush cache-clear all` for Drupal 8 users, clear - // the Drush cache as well, for consistency with that behavior. - CacheCommands::clearDrush(); } /** * @hook validate cache-clear */ - public function validate(CommandData $commandData) + public function validate(CommandData $commandData): void { $boot_manager = Drush::bootstrapManager(); $types = $this->getTypes($boot_manager->hasBootstrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)); @@ -270,7 +264,7 @@ public function validate(CommandData $commandData) /** * Types of caches available for clearing. Contrib commands can hook in their own. */ - public function getTypes($include_bootstrapped_types = false) + public function getTypes($include_bootstrapped_types = false): array { $types = [ 'drush' => [$this, 'clearDrush'], @@ -297,11 +291,10 @@ public function getTypes($include_bootstrapped_types = false) /** * Clear caches internal to Drush core. */ - public static function clearDrush() + public static function clearDrush(): void { try { - drush_cache_clear_all(null, 'default');// No longer used by Drush core, but still cleared for backward compat. - drush_cache_clear_all(null, 'factory'); // command info from annotated-command library (i.e. parsed annotations) + Drush::logger()->info(dt('Deprecation notice - Drush no longer caches anything.')); } catch (IOException $e) { // Sometimes another process writes files into a bin dir and \Drush\Cache\FileCache::clear fails. // That is not considered an error. https://github.com/drush-ops/drush/pull/4535. @@ -312,7 +305,7 @@ public static function clearDrush() /** * Clear one or more cache bins. */ - public static function clearBins($args = ['default']) + public static function clearBins($args = ['default']): void { $bins = StringUtils::csvToArray($args); foreach ($bins as $bin) { @@ -321,19 +314,19 @@ public static function clearBins($args = ['default']) } } - public static function clearThemeRegistry() + public static function clearThemeRegistry(): void { \Drupal::service('theme.registry')->reset(); } - public static function clearRouter() + public static function clearRouter(): void { - /** @var \Drupal\Core\Routing\RouteBuilderInterface $router_builder */ + /** @var RouteBuilderInterface $router_builder */ $router_builder = \Drupal::service('router.builder'); $router_builder->rebuild(); } - public static function clearCssJs() + public static function clearCssJs(): void { _drupal_flush_css_js(); \Drupal::service('asset.css.collection_optimizer')->deleteAll(); @@ -343,12 +336,12 @@ public static function clearCssJs() /** * Clears the render cache entries. */ - public static function clearRender() + public static function clearRender(): void { Cache::invalidateTags(['rendered']); } - public static function clearPlugin() + public static function clearPlugin(): void { \Drupal::getContainer()->get('plugin.cache_clearer')->clearCachedDefinitions(); } @@ -360,7 +353,7 @@ public function loadDrupalAutoloader($drupal_root) { static $autoloader = false; - $autoloadFilePath = $drupal_root .'/autoload.php'; + $autoloadFilePath = $drupal_root . '/autoload.php'; if (!$autoloader && file_exists($autoloadFilePath)) { $autoloader = require $autoloadFilePath; } diff --git a/src/Commands/core/CoreCommands.php b/src/Commands/core/CoreCommands.php index 8c34134183..29f84f2682 100644 --- a/src/Commands/core/CoreCommands.php +++ b/src/Commands/core/CoreCommands.php @@ -1,4 +1,5 @@ 'table']) + public function globalOptions($options = ['format' => 'table']): RowsOfFields { $application = Drush::getApplication(); $def = $application->getDefinition(); foreach ($def->getOptions() as $key => $value) { - $name = '--'. $key; + $name = '--' . $key; if ($value->getShortcut()) { $name = '-' . $value->getShortcut() . ', ' . $name; } @@ -46,11 +45,18 @@ public function globalOptions($options = ['format' => 'table']) // Also document the keys that are recognized by PreflightArgs. It would be possible to redundantly declare // those as global options. We don't do that for now, to avoid confusion. - $ancient = drush_get_global_options(); - foreach (['config', 'alias-path', 'include', 'local', 'strict', 'ssh-options'] as $name) { + $ancient = [ + 'config' => 'Specify an additional config file to load. See example.drush.yml. Example: /path/file', + 'alias-path' => 'Specifies additional paths where Drush will search for alias files. Example: /path/alias1:/path/alias2', + 'include' => 'Additional directories to search for Drush commands. Commandfiles should be placed in a subdirectory called Commands. Example: path/dir', + 'local' => 'Don\'t look outside the Composer project for Drush config.', + 'strict' => 'Return an error on unrecognized options. --strict=0 allows unrecognized options.', + 'ssh-options' => 'A string of extra options that will be passed to the ssh command. Example: -p 100', + ]; + foreach ($ancient as $name => $description) { $rows[] = [ 'name' => '--' . $name, - 'description' => $ancient[$name]['description'], + 'description' => $description, ]; } usort($rows, function ($a, $b) { @@ -68,10 +74,9 @@ public function globalOptions($options = ['format' => 'table']) * @field-labels * drush-version: Drush version * - * @return \Consolidation\OutputFormatters\StructuredData\PropertyList * */ - public function version($options = ['format' => 'table']) + public function version($options = ['format' => 'table']): PropertyList { return new PropertyList(['drush-version' => Drush::getVersion()]); } diff --git a/src/Commands/core/DeployCommands.php b/src/Commands/core/DeployCommands.php index a88571f7bc..6029bf88d4 100644 --- a/src/Commands/core/DeployCommands.php +++ b/src/Commands/core/DeployCommands.php @@ -1,4 +1,5 @@ siteAliasManager()->getSelf(); $redispatchOptions = Drush::redispatchOptions(); @@ -51,11 +52,11 @@ public function deploy() } /** - * @param \Consolidation\SiteProcess\ProcessManager $manager - * @param \Consolidation\SiteAlias\SiteAlias $self + * @param ProcessManager $manager + * @param SiteAlias $self * @param array $redispatchOptions */ - public function cacheRebuild(ProcessManager $manager, SiteAlias $self, array $redispatchOptions) + public function cacheRebuild(ProcessManager $manager, SiteAlias $self, array $redispatchOptions): void { // It is possible that no updates were pending and thus no caches cleared yet. $this->logger()->success("Cache rebuild start."); diff --git a/src/Commands/core/DocsCommands.php b/src/Commands/core/DocsCommands.php index db33cff46c..4a22109a74 100644 --- a/src/Commands/core/DocsCommands.php +++ b/src/Commands/core/DocsCommands.php @@ -1,4 +1,5 @@ commandData); } @@ -34,20 +35,7 @@ public function readme() * @hidden * @topic ../../../examples/git-bisect.example.sh */ - public function bisect() - { - self::printFileTopic($this->commandData); - } - - /** - * Bashrc customization examples for Drush. - * - * @command docs:bashrc - * @aliases docs-bashrc - * @hidden - * @topic ../../../examples/example.bashrc - */ - public function bashrc() + public function bisect(): void { self::printFileTopic($this->commandData); } @@ -60,7 +48,7 @@ public function bashrc() * @hidden * @topic ../../../docs/using-drush-configuration.md */ - public function config() + public function config(): void { self::printFileTopic($this->commandData); } @@ -73,7 +61,7 @@ public function config() * @hidden * @topic ../../../docs/hooks.md */ - public function hooks() + public function hooks(): void { self::printFileTopic($this->commandData); } @@ -86,7 +74,7 @@ public function hooks() * @hidden * @topic ../../../docs/config-exporting.md */ - public function configExport() + public function configExport(): void { self::printFileTopic($this->commandData); } @@ -100,7 +88,7 @@ public function configExport() * @hidden * @topic ../../../docs/output-formats-filters.md */ - public function outputFormatsFilters() + public function outputFormatsFilters(): void { self::printFileTopic($this->commandData); } @@ -113,7 +101,7 @@ public function outputFormatsFilters() * @hidden * @topic ../../../docs/site-aliases.md */ - public function siteAliases() + public function siteAliases(): void { self::printFileTopic($this->commandData); } @@ -126,7 +114,7 @@ public function siteAliases() * @hidden * @topic ../../../examples/helloworld.script */ - public function script() + public function script(): void { self::printFileTopic($this->commandData); } @@ -139,7 +127,7 @@ public function script() * @hidden * @topic ../../../docs/bootstrap.md */ - public function bootstrap() + public function bootstrap(): void { self::printFileTopic($this->commandData); } @@ -152,7 +140,7 @@ public function bootstrap() * @hidden * @topic ../../../docs/cron.md */ - public function cron() + public function cron(): void { self::printFileTopic($this->commandData); } @@ -165,7 +153,7 @@ public function cron() * @hidden * @topic ../../../docs/commands.md */ - public function commands() + public function commands(): void { self::printFileTopic($this->commandData); } @@ -178,7 +166,7 @@ public function commands() * @hidden * @topic ../../../docs/generators.md */ - public function generators() + public function generators(): void { self::printFileTopic($this->commandData); } @@ -191,7 +179,7 @@ public function generators() * @hidden * @topic ../../../examples/Commands/ArtCommands.php */ - public function exampleCommand() + public function exampleCommand(): void { self::printFileTopic($this->commandData); } @@ -203,7 +191,7 @@ public function exampleCommand() * @hidden * @topic ../../../docs/migrate.md */ - public function migrate() + public function migrate(): void { self::printFileTopic($this->commandData); } @@ -216,7 +204,7 @@ public function migrate() * @hidden * @topic ../../../examples/Commands/SyncViaHttpCommands.php */ - public function syncHttp() + public function syncHttp(): void { self::printFileTopic($this->commandData); } @@ -229,7 +217,7 @@ public function syncHttp() * @hidden * @topic ../../../examples/Commands/PolicyCommands.php */ - public function policy() + public function policy(): void { self::printFileTopic($this->commandData); } @@ -242,7 +230,7 @@ public function policy() * @hidden * @topic ../../../docs/deploycommand.md */ - public function deploy() + public function deploy(): void { self::printFileTopic($this->commandData); } diff --git a/src/Commands/core/DrupalDirectoryCommands.php b/src/Commands/core/DrupalDirectoryCommands.php index 75cb3bab35..45d1d2c51b 100644 --- a/src/Commands/core/DrupalDirectoryCommands.php +++ b/src/Commands/core/DrupalDirectoryCommands.php @@ -1,16 +1,15 @@ false]) + public function drupalDirectory(string $target = 'root', $options = ['local-only' => false]) { $path = $this->getPath($target, $options['local-only']); diff --git a/src/Commands/core/DrupliconCommands.php b/src/Commands/core/DrupliconCommands.php index e170066774..e15f862095 100644 --- a/src/Commands/core/DrupliconCommands.php +++ b/src/Commands/core/DrupliconCommands.php @@ -1,4 +1,5 @@ false]) + public function optionset($options = ['druplicon' => false]): void { } @@ -21,7 +22,7 @@ public function optionset($options = ['druplicon' => false]) * * @hook post-command * */ - public function druplicon($result, CommandData $commandData) + public function druplicon($result, CommandData $commandData): void { // If one command does a Drush::drush() to another command, // then this hook will be called multiple times. Only print diff --git a/src/Commands/core/EditCommands.php b/src/Commands/core/EditCommands.php index 333467f1a6..b827cd4e07 100644 --- a/src/Commands/core/EditCommands.php +++ b/src/Commands/core/EditCommands.php @@ -1,4 +1,5 @@ load(); @@ -47,7 +48,7 @@ public function edit($filter = null) } } - $editor = self::getEditor(); + $editor = self::getEditor($options['editor']); if (count($all) == 1) { $filepath = current($all); } else { @@ -66,7 +67,7 @@ public function edit($filter = null) $process->mustRun(); } - public function load($headers = true) + public function load($headers = true): array { $php_header = $php = $rcs_header = $rcs = $aliases_header = $aliases = $drupal_header = $drupal = []; $php = $this->phpIniFiles(); @@ -118,13 +119,13 @@ public function load($headers = true) return array_merge($php_header, $php, $bash_header, $bash, $rcs_header, $rcs, $aliases_header, $aliases, $drupal_header, $drupal); } - public static function phpIniFiles() + public static function phpIniFiles(): array { $paths[] = php_ini_loaded_file(); return $paths; } - public function bashFiles() + public function bashFiles(): array { $bashFiles = []; $home = $this->getConfig()->home(); @@ -144,12 +145,12 @@ public function bashFiles() * TODO: Also exists as InitCommands::findBashrc. Decide on class-based * way to share code like this. */ - public static function findBashrc($home) + public static function findBashrc($home): string { return $home . "/.bashrc"; } - public function complete() + public function complete(): array { return ['values' => $this->load(false)]; } diff --git a/src/Commands/core/LoginCommands.php b/src/Commands/core/LoginCommands.php index 887bf87400..2bf5c10665 100644 --- a/src/Commands/core/LoginCommands.php +++ b/src/Commands/core/LoginCommands.php @@ -1,4 +1,5 @@ null, 'uid' => null, 'mail' => null, 'browser' => true, 'redirect-port' => self::REQ]) + public function login(string $path = '', $options = ['name' => null, 'uid' => null, 'mail' => null, 'browser' => true, 'redirect-port' => self::REQ]) { // Redispatch if called against a remote-host so a browser is started on the // the *local* machine. @@ -69,6 +69,10 @@ public function login($path = '', $options = ['name' => null, 'uid' => null, 'ma $account = User::load(1); } + if ($account->isBlocked()) { + throw new \InvalidArgumentException(dt('Account !name is blocked and thus cannot login. The user:unblock command may be helpful.', ['!name' => $account->getAccountName()])); + } + $timestamp = \Drupal::time()->getRequestTime(); $link = Url::fromRoute( 'user.reset.login', diff --git a/src/Commands/core/MkCommands.php b/src/Commands/core/MkCommands.php index d4746195bb..4f6bc885d5 100644 --- a/src/Commands/core/MkCommands.php +++ b/src/Commands/core/MkCommands.php @@ -35,7 +35,7 @@ class MkCommands extends DrushCommands implements SiteAliasManagerAwareInterface * @usage drush mk:docs * Build many .md files in the docs/commands and docs/generators directories. */ - public function docs() + public function docs(): void { $dir_root = Drush::bootstrapManager()->getComposerRoot(); $destination = 'commands'; @@ -46,7 +46,7 @@ public function docs() $all = $application->all(); $namespaced = ListCommands::categorize($all); [$nav_commands, $pages_commands] = $this->writeContentFilesAndAddToNav($namespaced, $destination, $dir_root, $destination_path); - $this->writeAllMd($pages_commands, $destination_path); + $this->writeAllMd($pages_commands, $destination_path, 'All commands'); $destination = 'generators'; $destination_path = Path::join($dir_root, 'docs', $destination); @@ -57,7 +57,7 @@ public function docs() $all = $this->createAnnotatedCommands($application_generate, Drush::getApplication()); $namespaced = ListCommands::categorize($all); [$nav_generators, $pages_generators] = $this->writeContentFilesAndAddToNav($namespaced, $destination, $dir_root, $destination_path); - $this->writeAllMd($pages_generators, $destination_path); + $this->writeAllMd($pages_generators, $destination_path, 'All generators'); $this->writeYml($nav_commands, $nav_generators, $dir_root); } @@ -77,8 +77,8 @@ public function createAnnotatedCommands(Application $application_generate, Appli $annotated->setAliases($command->getAliases()); $annotated->setTopics(['docs:generators']); $values = []; - if ($command->getName() == 'entity:bundle-class') { - $values['version'] = '11.x'; + if (in_array($command->getName(), ['entity:bundle-class'])) { + $values['version'] = '11.0'; } $annotated->setAnnotationData(new AnnotationData($values)); // Hack, until we have https://github.com/consolidation/annotated-command/pull/247 @@ -100,7 +100,7 @@ protected static function appendPostAmble(): string EOT; } - protected static function appendAliases(AnnotatedCommand $command): string + protected static function appendAliases($command): string { if ($aliases = $command->getAliases()) { $body = "#### Aliases\n\n"; @@ -141,7 +141,7 @@ protected static function appendTopics(AnnotatedCommand $command, string $dir_co return ''; } - protected static function appendOptions(AnnotatedCommand $command): string + protected static function appendOptions($command): string { if ($opts = $command->getDefinition()->getOptions()) { $body = ''; @@ -159,7 +159,7 @@ protected static function appendOptions(AnnotatedCommand $command): string return ''; } - protected static function appendArguments(AnnotatedCommand $command): string + protected static function appendArguments($command): string { if ($args = $command->getDefinition()->getArguments()) { $body = "#### Arguments\n\n"; @@ -184,9 +184,12 @@ protected static function appendUsages(AnnotatedCommand $command): string return ''; } - protected static function appendPreamble(AnnotatedCommand $command, $root): string + protected static function appendPreamble($command, $root): string { - $path = Path::makeRelative($command->getAnnotationData()->get('_path'), $root); + $path = ''; + if ($command instanceof AnnotatedCommand) { + $path = Path::makeRelative($command->getAnnotationData()->get('_path'), $root); + } $edit_url = $path ? "https://github.com/drush-ops/drush/blob/11.x/$path" : ''; $body = << $page) { @@ -230,7 +233,7 @@ protected function writeAllMd(array $pages_all, string $destination_path): void $items[] = "* [$name]($basename)"; } $preamble = <<optionsHook(); } $body = self::appendPreamble($command, $dir_root); - $body .= self::appendUsages($command); + if ($command instanceof AnnotatedCommand) { + $body .= self::appendUsages($command); + } $body .= self::appendArguments($command); $body .= self::appendOptions($command); if ($command instanceof AnnotatedCommand) { diff --git a/src/Commands/core/NotifyCommands.php b/src/Commands/core/NotifyCommands.php index 9cdcc728fb..3780a55dee 100644 --- a/src/Commands/core/NotifyCommands.php +++ b/src/Commands/core/NotifyCommands.php @@ -1,4 +1,5 @@ annotationData(); @@ -50,7 +51,7 @@ public function shutdown(CommandData $commandData) * @param string $msg * Message to send via notification. */ - public static function shutdownSend($msg, CommandData $commandData) + public static function shutdownSend(string $msg, CommandData $commandData): void { self::shutdownSendText($msg, $commandData); } @@ -64,10 +65,9 @@ public static function shutdownSend($msg, CommandData $commandData) * @param string $msg * Message text for delivery. * - * @return bool * TRUE on success, FALSE on failure */ - public static function shutdownSendText($msg, CommandData $commandData) + public static function shutdownSendText(string $msg, CommandData $commandData): bool { $override = Drush::config()->get('notify.cmd'); @@ -101,10 +101,8 @@ public static function shutdownSendText($msg, CommandData $commandData) /** * Identify if the given Drush request should trigger a notification. - * - * @return bool */ - public static function isAllowed() + public static function isAllowed(): bool { $duration = Drush::config()->get('notify.duration'); $execution = time() - $_SERVER['REQUEST_TIME']; diff --git a/src/Commands/core/PhpCommands.php b/src/Commands/core/PhpCommands.php index fa4f53bfd1..3b078c6db6 100644 --- a/src/Commands/core/PhpCommands.php +++ b/src/Commands/core/PhpCommands.php @@ -1,4 +1,5 @@ self::REQ, 'include-paths' => self::REQ, 'mode' => 'akz']) + public function rsync($source, $target, array $extra, $options = ['exclude-paths' => self::REQ, 'include-paths' => self::REQ, 'mode' => 'akz']): void { // Prompt for confirmation. This is destructive. if (!$this->getConfig()->simulate()) { @@ -74,7 +75,7 @@ public function rsync($source, $target, array $extra, $options = ['exclude-paths $parameters[] = Escape::shellArg($this->targetEvaluatedPath->fullyQualifiedPath()); $ssh_options = $this->getConfig()->get('ssh.options', ''); - $exec = "rsync -e 'ssh $ssh_options'". ' '. implode(' ', array_filter($parameters)); + $exec = "rsync -e 'ssh $ssh_options'" . ' ' . implode(' ', array_filter($parameters)); $process = $this->processManager()->shell($exec); $process->run($process->showRealtime()); @@ -83,14 +84,14 @@ public function rsync($source, $target, array $extra, $options = ['exclude-paths } } - public function rsyncOptions($options) + public function rsyncOptions($options): string { $verbose = $paths = ''; // Process --include-paths and --exclude-paths options the same way foreach (['include', 'exclude'] as $include_exclude) { // Get the option --include-paths or --exclude-paths and explode to an array of paths // that we will translate into an --include or --exclude option to pass to rsync - $inc_ex_path = explode(PATH_SEPARATOR, @$options[$include_exclude . '-paths']); + $inc_ex_path = explode(PATH_SEPARATOR, (string) @$options[$include_exclude . '-paths']); foreach ($inc_ex_path as $one_path_to_inc_ex) { if (!empty($one_path_to_inc_ex)) { $paths .= ' --' . $include_exclude . '="' . $one_path_to_inc_ex . '"'; @@ -98,7 +99,7 @@ public function rsyncOptions($options) } } - $mode = '-'. $options['mode']; + $mode = '-' . $options['mode']; if ($this->output()->isVerbose()) { $mode .= 'v'; $verbose = ' --stats --progress'; @@ -117,9 +118,8 @@ public function rsyncOptions($options) * @hook command-event core:rsync * @param ConsoleCommandEvent $event * @throws \Exception - * @return void */ - public function preCommandEvent(ConsoleCommandEvent $event) + public function preCommandEvent(ConsoleCommandEvent $event): void { $input = $event->getInput(); $this->sourceEvaluatedPath = $this->injectAliasPathParameterOptions($input, 'source'); @@ -154,11 +154,10 @@ protected function injectAliasPathParameterOptions($input, $parameterName) * Validate that passed aliases are valid. * * @hook validate core-rsync - * @param \Consolidation\AnnotatedCommand\CommandData $commandData + * @param CommandData $commandData * @throws \Exception - * @return void */ - public function validate(CommandData $commandData) + public function validate(CommandData $commandData): void { if ($this->sourceEvaluatedPath->isRemote() && $this->targetEvaluatedPath->isRemote()) { $msg = dt("Cannot specify two remote aliases. Instead, use one of the following alternate options:\n\n `drush {source} rsync @self {target}`\n `drush {source} rsync @self {fulltarget}\n\nUse the second form if the site alias definitions are not available at {source}.", ['source' => $this->sourceEvaluatedPath->getSiteAlias()->name(), 'target' => $this->targetEvaluatedPath->getSiteAlias()->name(), 'fulltarget' => $this->targetEvaluatedPath->fullyQualifiedPath()]); diff --git a/src/Commands/core/RunserverCommands.php b/src/Commands/core/RunserverCommands.php index 92c97479b6..693f26b5c2 100644 --- a/src/Commands/core/RunserverCommands.php +++ b/src/Commands/core/RunserverCommands.php @@ -1,4 +1,5 @@ self::REQ /** * Determine the URI to use for this server. */ - public function uri($uri, $options) + public function uri($uri, $options): array { $drush_default = [ 'host' => '127.0.0.1', @@ -119,13 +119,12 @@ public function uri($uri, $options) /** * Parse a URI or partial URI (including just a port, host IP or path). * - * @param string $uri + * @param $uri * String that can contain partial URI. * - * @return array * URI array as returned by parse_url. */ - public function parseUri($uri) + public function parseUri(?string $uri): array { if (empty($uri)) { return []; diff --git a/src/Commands/core/SiteCommands.php b/src/Commands/core/SiteCommands.php index 51d4891f45..3cc9a9e550 100644 --- a/src/Commands/core/SiteCommands.php +++ b/src/Commands/core/SiteCommands.php @@ -1,4 +1,5 @@ getConfig()->get('runtime.site-file-current'); if ($filename) { @@ -101,9 +102,8 @@ public function siteSet($site = '@none') * @command site:alias * * @param string $site Site alias or site specification. - * @param array $options * - * @return \Consolidation\OutputFormatters\StructuredData\UnstructuredListData + * @return UnstructuredListData * @throws \Exception * @aliases sa * @filter-default-field id @@ -112,9 +112,8 @@ public function siteSet($site = '@none') * @usage drush site:alias @dev * Print an alias record for the alias 'dev'. * @topics docs:aliases - * */ - public function siteAlias($site = null, $options = ['format' => 'yaml']) + public function siteAlias($site = null, array $options = ['format' => 'yaml']) { // First check to see if the user provided a specification that matches // multiple sites. @@ -149,9 +148,8 @@ public function siteAlias($site = null, $options = ['format' => 'yaml']) * @bootstrap max * @aliases sa-convert,sac * @topics docs:aliases - * @return array */ - public function siteAliasConvert($destination, $options = ['format' => 'yaml', 'sources' => self::REQ]) + public function siteAliasConvert($destination, $options = ['format' => 'yaml', 'sources' => self::REQ]): array { /** * @todo @@ -195,7 +193,7 @@ public function siteAliasConvert($destination, $options = ['format' => 'yaml', ' /** * @hook interact site:alias-convert */ - public function interactSiteAliasConvert(Input $input, Output $output) + public function interactSiteAliasConvert(Input $input, Output $output): void { if (!$input->getArgument('destination')) { $default = Path::join($this->getConfig()->home(), '.drush/sites'); @@ -208,11 +206,9 @@ public function interactSiteAliasConvert(Input $input, Output $output) } /** - * @param array $aliasList * @param $options - * @return array */ - protected function siteAliasExportList($aliasList, $options) + protected function siteAliasExportList(array $aliasList, $options): array { $result = array_map( function ($aliasRecord) { diff --git a/src/Commands/core/SiteInstallCommands.php b/src/Commands/core/SiteInstallCommands.php index 8219aa7ddb..fc55e45882 100644 --- a/src/Commands/core/SiteInstallCommands.php +++ b/src/Commands/core/SiteInstallCommands.php @@ -1,7 +1,7 @@ self::REQ, 'db-prefix' => self::REQ, 'db-su' => self::REQ, 'db-su-pw' => self::REQ, 'account-name' => 'admin', 'account-mail' => 'admin@example.com', 'site-mail' => 'admin@example.com', 'account-pass' => self::REQ, 'locale' => 'en', 'site-name' => 'Drush Site-Install', 'site-pass' => self::REQ, 'sites-subdir' => self::REQ, 'config-dir' => self::REQ, 'existing-config' => false]) + public function install(array $profile, $options = ['db-url' => self::REQ, 'db-prefix' => self::REQ, 'db-su' => self::REQ, 'db-su-pw' => self::REQ, 'account-name' => 'admin', 'account-mail' => 'admin@example.com', 'site-mail' => 'admin@example.com', 'account-pass' => self::REQ, 'locale' => 'en', 'site-name' => 'Drush Site-Install', 'site-pass' => self::REQ, 'sites-subdir' => self::REQ, 'config-dir' => self::REQ, 'existing-config' => false]): void { $additional = $profile; $profile = array_shift($additional) ?: ''; @@ -162,7 +164,7 @@ public function install(array $profile, $options = ['db-url' => self::REQ, 'db-p } } - public function taskCallback($install_state) + public function taskCallback($install_state): void { $this->logger()->notice('Performed install task: {task}', ['task' => $install_state['active_task']]); } @@ -170,13 +172,6 @@ public function taskCallback($install_state) protected function determineProfile($profile, $options, $class_loader) { - // --config-dir fails with Standard profile and any other one that carries content entities. - // Force to minimal install profile only for drupal < 8.6. - if ($options['config-dir'] && Comparator::lessThan(self::getVersion(), '8.6')) { - $this->logger()->info(dt("Using 'minimal' install profile since --config-dir option was provided.")); - $profile = 'minimal'; - } - // Try to get profile from existing config if not provided as an argument. // @todo Arguably Drupal core [$boot->getKernel()->getInstallProfile()] could do this - https://github.com/drupal/drupal/blob/8.6.x/core/lib/Drupal/Core/DrupalKernel.php#L1606 reads from DB storage but not file storage. if (empty($profile) && $options['existing-config']) { @@ -221,31 +216,10 @@ protected function determineProfile($profile, $options, $class_loader) return $profile; } - /** - * Post installation, run the configuration import. - * - * @hook post-command site-install - */ - public function post($result, CommandData $commandData) - { - if ($config = $commandData->input()->getOption('config-dir') && Comparator::lessThan(self::getVersion(), '8.6')) { - // Set the destination site UUID to match the source UUID, to bypass a core fail-safe. - $source_storage = new FileStorage($config); - $options = array_merge(Drush::redispatchOptions(), ['yes' => true, 'strict' => 0]); - $selfRecord = $this->siteAliasManager()->getSelf(); - - $process = $this->processManager()->drush($selfRecord, 'config-set', ['system.site', 'uuid', $source_storage->read('system.site')['uuid']], $options); - $process->mustRun(); - - $process = $this->processManager()->drush($selfRecord, 'config-import', [], ['source' => $config] + $options); - $process->mustRun($process->showRealtime()); - } - } - /** * Check to see if there are any .yml files in the provided config directory. */ - protected function hasConfigFiles($config) + protected function hasConfigFiles($config): bool { $files = glob("$config/*.yml"); return !empty($files); @@ -254,7 +228,7 @@ protected function hasConfigFiles($config) /** * @hook validate site-install */ - public function validate(CommandData $commandData) + public function validate(CommandData $commandData): void { $bootstrapManager = Drush::bootstrapManager(); if ($sites_subdir = $commandData->input()->getOption('sites-subdir')) { @@ -311,7 +285,7 @@ public function validate(CommandData $commandData) * * @hook pre-command site-install */ - public function pre(CommandData $commandData) + public function pre(CommandData $commandData): void { $db_spec = []; if ($sql = SqlBase::create($commandData->input()->getOptions())) { @@ -423,9 +397,10 @@ protected function getSitesSubdirFromUri($root, $uri) // Find the dir from sites.php file $sites_file = $root . '/sites/sites.php'; if (file_exists($sites_file)) { - include $sites_file; /** @var array $sites */ - if (isset($sites) && array_key_exists($uri, $sites)) { + $sites = []; + include $sites_file; + if (!empty($sites) && array_key_exists($uri, $sites)) { return $sites[$uri]; } } @@ -436,17 +411,11 @@ protected function getSitesSubdirFromUri($root, $uri) return false; } - public static function getVersion() - { - $drupal_root = Drush::bootstrapManager()->getRoot(); - return Drush::bootstrap()->getVersion($drupal_root); - } - /** * Fake the necessary HTTP headers that the Drupal installer still needs: * @see https://github.com/drupal/drupal/blob/d260101f1ea8a6970df88d2f1899248985c499fc/core/includes/install.core.inc#L287 */ - public function serverGlobals($drupal_base_url) + public function serverGlobals($drupal_base_url): void { $drupal_base_url = parse_url($drupal_base_url); @@ -487,7 +456,7 @@ public function serverGlobals($drupal_base_url) * @param $directory * @throws \Exception */ - protected function validateConfigDir(CommandData $commandData, $directory) + protected function validateConfigDir(CommandData $commandData, $directory): void { if (!file_exists($directory)) { throw new \Exception(dt('The config source directory @config does not exist.', ['@config' => $directory])); diff --git a/src/Commands/core/SshCommands.php b/src/Commands/core/SshCommands.php index 8144326c31..2ae6f1d458 100644 --- a/src/Commands/core/SshCommands.php +++ b/src/Commands/core/SshCommands.php @@ -1,4 +1,5 @@ self::REQ, 'tty' => false]) + public function ssh(array $code, $options = ['cd' => self::REQ, 'tty' => false]): void { $alias = $this->siteAliasManager()->getSelf(); diff --git a/src/Commands/core/StatusCommands.php b/src/Commands/core/StatusCommands.php index 8fde7dfc39..843e721bba 100644 --- a/src/Commands/core/StatusCommands.php +++ b/src/Commands/core/StatusCommands.php @@ -52,7 +52,6 @@ class StatusCommands extends DrushCommands implements SiteAliasManagerAwareInter * drush-script: Drush script * drush-version: Drush version * drush-temp: Drush temp - * drush-cache-directory: Drush cache folder * drush-conf: Drush configs * drush-alias-files: Drush aliases * alias-searchpaths: Alias search paths @@ -75,10 +74,8 @@ class StatusCommands extends DrushCommands implements SiteAliasManagerAwareInter * @hidden-options project * @bootstrap max * @topics docs:readme - * - * @return \Consolidation\OutputFormatters\StructuredData\PropertyList */ - public function status($filter = '', $options = ['project' => self::REQ, 'format' => 'table']) + public function status($filter = '', $options = ['project' => self::REQ, 'format' => 'table']): PropertyList { $data = $this->getPropertyList($options); @@ -88,7 +85,7 @@ public function status($filter = '', $options = ['project' => self::REQ, 'format return $result; } - public function getPropertyList($options) + public function getPropertyList($options): array { $boot_manager = Drush::bootstrapManager(); $boot_object = Drush::bootstrap(); @@ -141,7 +138,6 @@ public function getPropertyList($options) $status_table['drush-script'] = $this->getConfig()->get('runtime.drush-script'); $status_table['drush-version'] = Drush::getVersion(); $status_table['drush-temp'] = $this->getConfig()->tmp(); - $status_table['drush-cache-directory'] = $this->getConfig()->cache(); $status_table['drush-conf'] = $this->getConfig()->configPaths(); // List available alias files $alias_files = $this->siteAliasManager()->listAllFilePaths(); @@ -162,7 +158,7 @@ public function getPropertyList($options) } // Store the paths into the '%paths' index; this will be - // used by other code, but will not be included in the output + // used by other code, but will not be included in the default output // of the drush status command. $status_table['%paths'] = $paths; @@ -180,7 +176,7 @@ public function renderStatusCell($key, $cellData, FormatterOptions $options) /** * @hook pre-command core-status */ - public function adjustStatusOptions(CommandData $commandData) + public function adjustStatusOptions(CommandData $commandData): void { $input = $commandData->input(); $args = $input->getArguments(); @@ -192,9 +188,8 @@ public function adjustStatusOptions(CommandData $commandData) /** * @param array $options * @param BootstrapManager $boot_manager - * @return array */ - public static function pathAliases(array $options, BootstrapManager $boot_manager, $boot) + public static function pathAliases(array $options, BootstrapManager $boot_manager, $boot): array { $paths = []; $site_wide = 'sites/all'; diff --git a/src/Commands/core/TopicCommands.php b/src/Commands/core/TopicCommands.php index 928eab8a07..a1721aabe6 100644 --- a/src/Commands/core/TopicCommands.php +++ b/src/Commands/core/TopicCommands.php @@ -1,6 +1,8 @@ getArgument('topic_name'); @@ -66,7 +67,7 @@ public function interact(InputInterface $input, OutputInterface $output) /** * @hook validate topic */ - public function validate(CommandData $commandData) + public function validate(CommandData $commandData): void { $topic_name = $commandData->input()->getArgument('topic_name'); if (!in_array($topic_name, array_keys(self::getAllTopics()))) { @@ -79,14 +80,14 @@ public function validate(CommandData $commandData) * * @return Command[] */ - public static function getAllTopics() + public static function getAllTopics(): array { /** @var Application $application */ $application = Drush::getApplication(); $all = $application->all(); foreach ($all as $key => $command) { if ($command instanceof AnnotatedCommand) { - /** @var \Consolidation\AnnotatedCommand\AnnotationData $annotationData */ + /** @var AnnotationData $annotationData */ $annotationData = $command->getAnnotationData(); if ($annotationData->has('topic')) { $topics[$command->getName()] = $command; diff --git a/src/Commands/core/UpdateDBCommands.php b/src/Commands/core/UpdateDBCommands.php index 1ab13535c6..f6350efc99 100644 --- a/src/Commands/core/UpdateDBCommands.php +++ b/src/Commands/core/UpdateDBCommands.php @@ -1,4 +1,5 @@ = 8.7.0. * @option post-updates Run post updates after hook_update_n and entity updates. * @bootstrap full * @topics docs:deploy * @kernel update * @aliases updb */ - public function updatedb($options = ['cache-clear' => true, 'entity-updates' => false, 'post-updates' => true]) + public function updatedb($options = ['cache-clear' => true, 'post-updates' => true]): int { $this->cache_clear = $options['cache-clear']; require_once DRUPAL_ROOT . '/core/includes/install.inc'; require_once DRUPAL_ROOT . '/core/includes/update.inc'; drupal_load_updates(); - if ($options['entity-updates'] && version_compare(drush_drupal_version(), '8.7.0', '>=')) { - $this->logger()->warning(dt('Drupal removed its automatic entity-updates API in 8.7. See https://www.drupal.org/node/3034742.')); - $options['entity-updates'] = false; - } - // Disables extensions that have a lower Drupal core major version, or too high of a PHP requirement. // Those are rare, and this function does a full rebuild. So commenting it out for now. // update_fix_compatibility(); @@ -59,16 +53,11 @@ public function updatedb($options = ['cache-clear' => true, 'entity-updates' => } $status_options = [ - // @see https://github.com/drush-ops/drush/pull/3855. - 'no-entity-updates' => !$options['entity-updates'], 'no-post-updates' => !$options['post-updates'], 'strict' => 0, ]; $status_options = array_merge(Drush::redispatchOptions(), $status_options); - // Since output needs to be checked, this option must be removed - unset($status_options['quiet']); - $process = $this->processManager()->drush($this->siteAliasManager()->getSelf(), 'updatedb:status', [], $status_options); $process->mustRun(); if ($output = $process->getOutput()) { @@ -94,46 +83,10 @@ public function updatedb($options = ['cache-clear' => true, 'entity-updates' => return $success ? self::EXIT_SUCCESS : self::EXIT_FAILURE; } - /** - * Apply pending entity schema updates. - * - * @command entity:updates - * @option cache-clear Set to 0 to suppress normal cache clearing; the caller should then clear if needed. - * @bootstrap full - * @kernel update - * @aliases entup,entity-updates - * @usage drush updatedb:status --entity-updates | grep entity-update - * Use updatedb:status to detect pending updates. - * - */ - public function entityUpdates($options = ['cache-clear' => true]) - { - if ($this->getConfig()->simulate()) { - throw new \Exception(dt('entity-updates command does not support --simulate option.')); - } - - // @todo - Do same check for updatedb as well. - if (version_compare(drush_drupal_version(), '8.7.0', '>=')) { - throw new \Exception(dt('Drupal removed its automatic entity-updates API in 8.7. See https://www.drupal.org/node/3034742.')); - } - - if ($this->entityUpdatesMain() === false) { - throw new \Exception('Entity updates not run.'); - } - - if ($options['cache-clear']) { - $process = $this->processManager()->drush($this->siteAliasManager()->getSelf(), 'cache-rebuild'); - $process->mustrun(); - } - - $this->logger()->success(dt('Finished performing updates.')); - } - /** * List any pending database updates. * * @command updatedb:status - * @option entity-updates Show entity schema updates. * @option post-updates Show post updates. * @bootstrap full * @kernel update @@ -145,9 +98,9 @@ public function entityUpdates($options = ['cache-clear' => true]) * type: Type * @default-fields module,update_id,type,description * @filter-default-field type - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields + * @return RowsOfFields */ - public function updatedbStatus($options = ['format'=> 'table', 'entity-updates' => true, 'post-updates' => true]) + public function updatedbStatus($options = ['format' => 'table', 'post-updates' => true]) { require_once DRUSH_DRUPAL_CORE . '/includes/install.inc'; drupal_load_updates(); @@ -167,10 +120,8 @@ public function updatedbStatus($options = ['format'=> 'table', 'entity-updates' * @bootstrap full * @kernel update * @hidden - * - * @return \Consolidation\OutputFormatters\StructuredData\UnstructuredListData */ - public function process($batch_id, $options = ['format' => 'json']) + public function process(string $batch_id, $options = ['format' => 'json']): UnstructuredListData { $result = drush_batch_command($batch_id); return new UnstructuredListData($result); @@ -198,7 +149,7 @@ public function process($batch_id, $options = ['format' => 'json']) * @param DrushBatchContext $context * The batch context object. */ - public static function updateDoOne($module, $number, array $dependency_map, DrushBatchContext $context) + public static function updateDoOne(string $module, int $number, array $dependency_map, DrushBatchContext $context): void { $function = $module . '_update_' . $number; @@ -292,10 +243,9 @@ public static function updateDoOne($module, $number, array $dependency_map, Drus * * @param string $function * The post-update function to execute. - * @param DrushBatchContext $context * The batch context object. */ - public static function updateDoOnePostUpdate($function, DrushBatchContext $context) + public static function updateDoOnePostUpdate(string $function, DrushBatchContext $context): void { $ret = []; @@ -378,10 +328,8 @@ public static function updateDoOnePostUpdate($function, DrushBatchContext $conte * Batch finished callback. * * @param boolean $success Whether the batch ended without a fatal error. - * @param array $results - * @param array $operations */ - public function updateFinished($success, $results, $operations) + public function updateFinished(bool $success, array $results, array $operations): void { if ($this->cache_clear) { // Flush all caches at the end of the batch operation. When Drupal @@ -395,9 +343,8 @@ public function updateFinished($success, $results, $operations) /** * Start the database update batch process. * @param $options - * @return bool */ - public function updateBatch($options) + public function updateBatch($options): bool { $start = $this->getUpdateList(); // Resolve any update dependencies to determine the actual updates that will @@ -430,16 +377,6 @@ public function updateBatch($options) } } - // Perform entity definition updates, which will update storage - // schema if needed. If module update functions need to work with specific - // entity schema they should call the entity update service for the specific - // update themselves. - // @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyEntityUpdate() - // @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyFieldUpdate() - if ($options['entity-updates'] && \Drupal::entityDefinitionUpdateManager()->needsUpdates()) { - $operations[] = [[$this, 'updateEntityDefinitions'], []]; - } - // Lastly, apply post update hooks if specified. if ($options['post-updates']) { $post_updates = \Drupal::service('update.post_update_registry')->getPendingUpdateFunctions(); @@ -488,33 +425,13 @@ public function updateBatch($options) return $success; } - public static function restoreMaintMode($status) + public static function restoreMaintMode($status): void { \Drupal::service('state')->set('system.maintenance_mode', $status); } - /** - * Apply entity schema updates. - */ - public function updateEntityDefinitions(&$context) - { - try { - \Drupal::entityDefinitionUpdateManager()->applyupdates(); - } catch (EntityStorageException $e) { - watchdog_exception('update', $e); - $variables = Error::decodeException($e); - unset($variables['backtrace']); - // The exception message is run through - // \Drupal\Component\Utility\SafeMarkup::checkPlain() by - // \Drupal\Core\Utility\Error::decodeException(). - $ret['#abort'] = ['success' => false, 'query' => t('%type: !message in %function (line %line of %file).', $variables)]; - $context['results']['core']['update_entity_definitions'] = $ret; - $context['results']['#abort'][] = 'update_entity_definitions'; - } - } - // Copy of protected \Drupal\system\Controller\DbUpdateController::getModuleUpdates. - public function getUpdateList() + public function getUpdateList(): array { $return = []; $updates = update_get_update_list(); @@ -538,7 +455,7 @@ public function getUpdateList() * * @see \Drupal\system\Controller\DbUpdateController::triggerBatch() */ - public static function cacheRebuild() + public static function cacheRebuild(): void { drupal_flush_all_caches(); \Drupal::service('kernel')->rebuildContainer(); @@ -554,7 +471,7 @@ public static function cacheRebuild() * - an array where each item is a 4 item associative array describing a pending update. * - an array listing the first update to run, keyed by module. */ - public function getUpdatedbStatus(array $options) + public function getUpdatedbStatus(array $options): array { require_once DRUPAL_ROOT . '/core/includes/update.inc'; $pending = \update_get_update_list(); @@ -572,7 +489,7 @@ public function getUpdatedbStatus(array $options) 'module' => $module, 'update_id' => $update_id, 'description' => $description, - 'type'=> 'hook_update_n' + 'type' => 'hook_update_n' ]; } if (isset($updates['start'])) { @@ -581,21 +498,6 @@ public function getUpdatedbStatus(array $options) } } - // Append row(s) for pending entity definition updates. - if ($options['entity-updates']) { - foreach (\Drupal::entityDefinitionUpdateManager() - ->getChangeSummary() as $entity_type_id => $changes) { - foreach ($changes as $change) { - $return[] = [ - 'module' => dt('@type entity type', ['@type' => $entity_type_id]), - 'update_id' => '', - 'description' => strip_tags($change), - 'type' => 'entity-update' - ]; - } - } - } - // Pending hook_post_update_X() implementations. $post_updates = \Drupal::service('update.post_update_registry')->getPendingUpdateInformation(); if ($options['post-updates']) { @@ -618,52 +520,10 @@ public function getUpdatedbStatus(array $options) return [$return, $start]; } - /** - * Apply pending entity schema updates. - */ - public function entityUpdatesMain() - { - $change_summary = \Drupal::entityDefinitionUpdateManager()->getChangeSummary(); - if (!empty($change_summary)) { - $this->output()->writeln(dt('The following updates are pending:')); - $this->io()->newLine(); - - foreach ($change_summary as $entity_type_id => $changes) { - $this->output()->writeln($entity_type_id . ' entity type : '); - foreach ($changes as $change) { - $this->output()->writeln(strip_tags($change), 2); - } - } - - if (!$this->io()->confirm(dt('Do you wish to run all pending updates?'))) { - throw new UserAbortException(); - } - - $operations[] = [[$this, 'updateEntityDefinitions'], []]; - - - $batch['operations'] = $operations; - $batch += [ - 'title' => 'Updating', - 'init_message' => 'Starting updates', - 'error_message' => 'An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.', - 'finished' => [$this, 'updateFinished'], - ]; - batch_set($batch); - - // See updateFinished() for the restore of maint mode. - $this->maintenanceModeOriginalState = \Drupal::service('state')->get('system.maintenance_mode'); - \Drupal::service('state')->set('system.maintenance_mode', true); - drush_backend_batch_process(); - } else { - $this->logger()->success(dt("No entity schema updates required")); - } - } - /** * Log messages for any requirements warnings/errors. */ - public function updateCheckRequirements() + public function updateCheckRequirements(): bool { $return = true; @@ -680,7 +540,7 @@ public function updateCheckRequirements() if (isset($requirement['severity']) && $requirement['severity'] != REQUIREMENT_OK) { $message = isset($requirement['description']) ? DrupalUtil::drushRender($requirement['description']) : ''; if (isset($requirement['value']) && $requirement['value']) { - $message .= ' (Currently using '. $requirement['title'] .' '. DrupalUtil::drushRender($requirement['value']) .')'; + $message .= ' (Currently using ' . $requirement['title'] . ' ' . DrupalUtil::drushRender($requirement['value']) . ')'; } $log_level = $requirement['severity'] === REQUIREMENT_ERROR ? LogLevel::ERROR : LogLevel::WARNING; $this->logger()->log($log_level, $message); diff --git a/src/Commands/core/XhprofCommands.php b/src/Commands/core/XhprofCommands.php index 6e3adb8d1c..af401a3d73 100644 --- a/src/Commands/core/XhprofCommands.php +++ b/src/Commands/core/XhprofCommands.php @@ -23,7 +23,6 @@ */ class XhprofCommands extends DrushCommands { - const XH_PROFILE_MEMORY = false; const XH_PROFILE_CPU = false; const XH_PROFILE_BUILTINS = true; @@ -37,7 +36,7 @@ class XhprofCommands extends DrushCommands * * @option xh-link URL to your XHProf report site. */ - public function optionsetXhProf($options = ['xh-link' => self::REQ]) + public function optionsetXhProf($options = ['xh-link' => self::REQ]): void { } @@ -46,7 +45,7 @@ public function optionsetXhProf($options = ['xh-link' => self::REQ]) * * @hook post-command * */ - public function xhprofPost($result, CommandData $commandData) + public function xhprofPost($result, CommandData $commandData): void { $config = $this->getConfig(); if (self::xhprofIsEnabled($config)) { @@ -62,7 +61,7 @@ public function xhprofPost($result, CommandData $commandData) * * @hook init * */ - public function xhprofInitialize(InputInterface $input, AnnotationData $annotationData) + public function xhprofInitialize(InputInterface $input, AnnotationData $annotationData): void { $config = $this->getConfig(); if (self::xhprofIsEnabled($config)) { @@ -74,15 +73,14 @@ public function xhprofInitialize(InputInterface $input, AnnotationData $annotati /** * Determines if any profiler could be enabled. * - * @param \Drush\Config\DrushConfig $config + * @param DrushConfig $config * - * @return bool * TRUE when xh.link configured, FALSE otherwise. * * @throws \Exception * When no available profiler extension enabled. */ - public static function xhprofIsEnabled(DrushConfig $config) + public static function xhprofIsEnabled(DrushConfig $config): bool { if (!$config->get('xh.link')) { return false; @@ -100,7 +98,7 @@ public static function xhprofIsEnabled(DrushConfig $config) /** * Determines flags. */ - public static function xhprofFlags(DrushConfig $config) + public static function xhprofFlags(DrushConfig $config): int { if (extension_loaded('tideways_xhprof')) { $map = [ @@ -132,7 +130,7 @@ public static function xhprofFlags(DrushConfig $config) /** * Enable profiling. */ - public static function xhprofEnable($flags) + public static function xhprofEnable($flags): void { if (extension_loaded('tideways_xhprof')) { \tideways_xhprof_enable($flags); diff --git a/src/Commands/generate/ApplicationFactory.php b/src/Commands/generate/ApplicationFactory.php index 330b5e1631..c4ee7a3270 100644 --- a/src/Commands/generate/ApplicationFactory.php +++ b/src/Commands/generate/ApplicationFactory.php @@ -29,7 +29,7 @@ class ApplicationFactory implements AutoloaderAwareInterface use AutoloaderAwareTrait; /** - * @var \Psr\Log\LoggerInterface + * @var LoggerInterface */ protected $logger; @@ -41,7 +41,7 @@ public function __construct(LoggerInterface $logger, $config) $this->config = $config; } - public function logger() + public function logger(): LoggerInterface { return $this->logger; } @@ -124,7 +124,7 @@ function ($generator) { return $generators; } - protected function discoverGlobalPathsDeprecated() + protected function discoverGlobalPathsDeprecated(): array { $config_paths = $this->getConfig()->get('runtime.commandfile.paths', []); foreach ($config_paths as $path) { @@ -134,7 +134,7 @@ protected function discoverGlobalPathsDeprecated() return array_filter($global_paths, 'file_exists'); } - protected function discoverPsr4Generators() + protected function discoverPsr4Generators(): array { $classes = (new RelativeNamespaceDiscovery($this->autoloader())) ->setRelativeNamespace('Drush\Generators') @@ -176,7 +176,7 @@ protected function getGenerators(array $classes): array { return array_map( function (string $class): Generator { - return new $class; + return new $class(); }, array_filter($classes, function (string $class): bool { $reflectionClass = new \ReflectionClass($class); diff --git a/src/Commands/generate/GenerateCommands.php b/src/Commands/generate/GenerateCommands.php index 60f871b047..412f5ee58b 100644 --- a/src/Commands/generate/GenerateCommands.php +++ b/src/Commands/generate/GenerateCommands.php @@ -13,7 +13,6 @@ */ class GenerateCommands extends DrushCommands implements AutoloaderAwareInterface { - use AutoloaderAwareTrait; /** @@ -32,16 +31,16 @@ class GenerateCommands extends DrushCommands implements AutoloaderAwareInterface * @option destination Absolute path to a base directory for file writing. * @usage drush generate * Pick from available generators and then run it. - * @usage drush generate controller - * Generate a controller class for your module. * @usage drush generate drush-command-file * Generate a Drush commandfile for your module. + * @usage drush generate controller --answer=Example --answer=example + * Generate a controller class and pre-fill the first two questions in the wizard. + * @usage drush generate controller -vvv --dry-run + * Learn all the potential answers so you can re-run with several --answer options. * @topics docs:generators * @bootstrap max - * - * @return int */ - public function generate($generator = '', $options = ['answer' => [], 'destination' => self::REQ, 'dry-run' => false]) + public function generate(string $generator = '', $options = ['answer' => [], 'destination' => self::REQ, 'dry-run' => false]): int { // Disallow default Symfony console commands. if ($generator == 'help' || $generator == 'list') { diff --git a/src/Commands/help/DrushHelpDocument.php b/src/Commands/help/DrushHelpDocument.php index 1d28c42e53..dfe51f9216 100644 --- a/src/Commands/help/DrushHelpDocument.php +++ b/src/Commands/help/DrushHelpDocument.php @@ -7,11 +7,10 @@ class DrushHelpDocument extends HelpDocument { - /** * @inheritdoc */ - public function generateBaseHelpDom(Command $command) + public function generateBaseHelpDom(Command $command): \DomDocument { // Global options should not appear in our help output. $command->setApplication(null); @@ -19,7 +18,7 @@ public function generateBaseHelpDom(Command $command) return parent::generateBaseHelpDom($command); } - protected function alterHelpDocument(Command $command, \DomDocument $dom) + protected function alterHelpDocument(Command $command, \DomDocument $dom): \DomDocument { return parent::alterHelpDocument($command, $dom); } diff --git a/src/Commands/help/HelpCLIFormatter.php b/src/Commands/help/HelpCLIFormatter.php index 2b9d686b82..3c63fc1e92 100644 --- a/src/Commands/help/HelpCLIFormatter.php +++ b/src/Commands/help/HelpCLIFormatter.php @@ -1,4 +1,5 @@ $option) { @@ -149,7 +145,7 @@ protected function cleanOptions(&$data) } } - public static function isGlobalOption($name) + public static function isGlobalOption($name): bool { $application = Drush::getApplication(); $def = $application->getDefinition(); diff --git a/src/Commands/help/HelpCommands.php b/src/Commands/help/HelpCommands.php index 46eb304ca8..ae9ce0565b 100644 --- a/src/Commands/help/HelpCommands.php +++ b/src/Commands/help/HelpCommands.php @@ -1,4 +1,5 @@ 'helpcli', 'include-field-labels' => false, 'table-style' => 'compact']) + public function help($command_name = '', $options = ['format' => 'helpcli', 'include-field-labels' => false, 'table-style' => 'compact']): DrushHelpDocument { $application = Drush::getApplication(); $command = $application->get($command_name); @@ -47,7 +45,7 @@ public function help($command_name = '', $options = ['format' => 'helpcli', 'inc /** * @hook validate help */ - public function validate(CommandData $commandData) + public function validate(CommandData $commandData): void { $name = $commandData->input()->getArgument('command_name'); if (empty($name)) { diff --git a/src/Commands/help/ListCommands.php b/src/Commands/help/ListCommands.php index 98a7d34def..de469fadf3 100644 --- a/src/Commands/help/ListCommands.php +++ b/src/Commands/help/ListCommands.php @@ -1,6 +1,8 @@ 'listcli', 'raw' => false, 'fil /** * @param $namespaced * @param $application - * @return \DOMDocument */ - public function buildDom($namespaced, $application) + public function buildDom($namespaced, $application): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $rootXml = $dom->createElement('symfony'); @@ -118,13 +119,7 @@ public function buildDom($namespaced, $application) return $dom; } - /** - * @param \Symfony\Component\Console\Application $application - * @param array $namespaced - * @param OutputInterface $output - * @param string $preamble - */ - public static function renderListCLI($application, $namespaced, $output, $preamble) + public static function renderListCLI(Application $application, array $namespaced, OutputInterface $output, string $preamble): void { $output->writeln($application->getHelp()); $output->writeln(''); @@ -163,16 +158,13 @@ function ($aliasName) use ($name) { $formatterManager->write($output, 'table', new RowsOfFields($rows), $formatterOptions); } - public static function getTerminalWidth() + public static function getTerminalWidth(): int { $term = new Terminal(); return $term->getWidth(); } - /** - * @param array $namespaced - */ - public function renderListRaw($namespaced) + public function renderListRaw(array $namespaced): void { $table = new Table($this->output()); $table->setStyle('compact'); @@ -186,11 +178,10 @@ public function renderListRaw($namespaced) /** * @param Command[] $all - * @param string $separator * * @return Command[] */ - public static function categorize($all, $separator = ':') + public static function categorize(array $all, string $separator = ':'): array { foreach ($all as $key => $command) { if (!in_array($key, $command->getAliases()) && !$command->isHidden()) { diff --git a/src/Commands/pm/SecurityUpdateCommands.php b/src/Commands/pm/SecurityUpdateCommands.php index 56e80a0738..208ffdf0c4 100644 --- a/src/Commands/pm/SecurityUpdateCommands.php +++ b/src/Commands/pm/SecurityUpdateCommands.php @@ -1,6 +1,8 @@ false]) /** * Emit suggested Composer command for security updates. */ - public function suggestComposerCommand($updates) + public function suggestComposerCommand($updates): void { $suggested_command = 'composer require '; foreach ($updates as $package) { @@ -100,9 +101,8 @@ public function suggestComposerCommand($updates) */ protected function fetchAdvisoryComposerJson() { - // We use the v2 branch for now, as per https://github.com/drupal-composer/drupal-security-advisories/pull/11. - $client = new \GuzzleHttp\Client(['handler' => $this->getStack()]); - $response = $client->get('https://raw.githubusercontent.com/drupal-composer/drupal-security-advisories/8.x-v2/composer.json'); + $client = new Client(['handler' => $this->getStack()]); + $response = $client->get('https://raw.githubusercontent.com/drupal-composer/drupal-security-advisories/9.x/composer.json'); $security_advisories_composer_json = json_decode($response->getBody(), true); return $security_advisories_composer_json; } @@ -110,11 +110,10 @@ protected function fetchAdvisoryComposerJson() /** * Loads the contents of the local Drupal application's composer.lock file. * - * @return array * * @throws \Exception */ - protected function loadSiteComposerLock() + protected function loadSiteComposerLock(): array { $composer_lock_file_path = self::composerLockPath(); $composer_lock_contents = file_get_contents($composer_lock_file_path); @@ -132,10 +131,8 @@ protected function loadSiteComposerLock() * The contents of the local Drupal application's composer.lock file. * @param array $security_advisories_composer_json * The composer.json array from drupal-security-advisories. - * - * @return array */ - protected function calculateSecurityUpdates($composer_lock_data, $security_advisories_composer_json, bool $excludeDev = false) + protected function calculateSecurityUpdates(array $composer_lock_data, array $security_advisories_composer_json, bool $excludeDev = false): array { $updates = []; $packages = $composer_lock_data['packages']; @@ -176,7 +173,7 @@ protected function calculateSecurityUpdates($composer_lock_data, $security_advis * @usage HTTP_PROXY=tcp://localhost:8125 pm:security * Proxy Guzzle requests through an http proxy. */ - public function securityPhp($options = ['format' => 'yaml', 'no-dev' => false]) + public function securityPhp(array $options = ['format' => 'yaml', 'no-dev' => false]) { $result = (new SecurityChecker())->check(self::composerLockPath(), $options['no-dev']); if ($result) { diff --git a/src/Commands/sql/SqlCommands.php b/src/Commands/sql/SqlCommands.php index 43179b4e22..87912873f4 100644 --- a/src/Commands/sql/SqlCommands.php +++ b/src/Commands/sql/SqlCommands.php @@ -1,4 +1,5 @@ 'yaml', 'all' => false, 'show-passwords' => false]) + public function conf($options = ['format' => 'yaml', 'all' => false, 'show-passwords' => false]): ?array { if ($options['all']) { $return = Database::getAllConnectionInfo(); @@ -63,7 +64,7 @@ public function conf($options = ['format' => 'yaml', 'all' => false, 'show-passw * @usage eval (drush sql-connect) < example.sql * Fish: Import SQL statements from a file into the current database. */ - public function connect($options = ['extra' => self::REQ]) + public function connect($options = ['extra' => self::REQ]): string { $sql = SqlBase::create($options); return $sql->connect(false); @@ -85,16 +86,12 @@ public function connect($options = ['extra' => self::REQ]) * Create the database as specified in the db-url option. * @bootstrap max configuration */ - public function create($options = ['db-su' => self::REQ, 'db-su-pw' => self::REQ]) + public function create($options = ['db-su' => self::REQ, 'db-su-pw' => self::REQ]): void { $sql = SqlBase::create($options); $db_spec = $sql->getDbSpec(); - // Prompt for confirmation. - - // @todo odd - maybe for sql-sync. - $txt_destination = (isset($db_spec['remote-host']) ? $db_spec['remote-host'] . '/' : '') . $db_spec['database']; - $this->output()->writeln(dt("Creating database !target. Any existing database will be dropped!", ['!target' => $txt_destination])); + $this->output()->writeln(dt("Creating database !target. Any existing database will be dropped!", ['!target' => $db_spec['database']])); if (!$this->getConfig()->simulate() && !$this->io()->confirm(dt('Do you really want to continue?'))) { throw new UserAbortException(); } @@ -113,7 +110,7 @@ public function create($options = ['db-su' => self::REQ, 'db-su-pw' => self::REQ * @bootstrap max configuration * @topics docs:policy */ - public function drop($options = []) + public function drop($options = []): void { $sql = SqlBase::create($options); $db_spec = $sql->getDbSpec(); @@ -142,7 +139,7 @@ public function drop($options = []) * @remote-tty * @bootstrap max configuration */ - public function cli(InputInterface $input, $options = ['extra' => self::REQ]) + public function cli(InputInterface $input, $options = ['extra' => self::REQ]): void { $sql = SqlBase::create($options); $process = $this->processManager()->shell($sql->connect(), null, $sql->getEnv()); @@ -180,7 +177,7 @@ public function cli(InputInterface $input, $options = ['extra' => self::REQ]) * @bootstrap max configuration * */ - public function query($query = '', $options = ['result-file' => null, 'file' => self::REQ, 'file-delete' => false, 'extra' => self::REQ, 'db-prefix' => false]) + public function query($query = '', $options = ['result-file' => null, 'file' => self::REQ, 'file-delete' => false, 'extra' => self::REQ, 'db-prefix' => false]): bool { $filename = $options['file']; // Enable prefix processing when db-prefix option is used. @@ -229,12 +226,11 @@ public function query($query = '', $options = ['result-file' => null, 'file' => * @field-labels * path: Path * - * @return \Consolidation\OutputFormatters\StructuredData\PropertyList * * @notes * --createdb is used by sql-sync, since including the DROP TABLE statements interferes with the import when the database is created. */ - public function dump($options = ['result-file' => self::REQ, 'create-db' => false, 'data-only' => false, 'ordered-dump' => false, 'gzip' => false, 'extra' => self::REQ, 'extra-dump' => self::REQ, 'format' => 'null']) + public function dump($options = ['result-file' => self::REQ, 'create-db' => false, 'data-only' => false, 'ordered-dump' => false, 'gzip' => false, 'extra' => self::REQ, 'extra-dump' => self::REQ, 'format' => 'null']): PropertyList { $sql = SqlBase::create($options); $return = $sql->dump(); diff --git a/src/Commands/sql/SqlSyncCommands.php b/src/Commands/sql/SqlSyncCommands.php index 265a6efa5a..1ba1cd0bbf 100644 --- a/src/Commands/sql/SqlSyncCommands.php +++ b/src/Commands/sql/SqlSyncCommands.php @@ -1,4 +1,5 @@ false, 'no-sync' => false, 'runner' => self::REQ, 'create-db' => false, 'db-su' => self::REQ, 'db-su-pw' => self::REQ, 'target-dump' => self::REQ, 'source-dump' => self::OPT, 'extra-dump' => self::REQ]) + public function sqlsync($source, $target, $options = ['no-dump' => false, 'no-sync' => false, 'runner' => self::REQ, 'create-db' => false, 'db-su' => self::REQ, 'db-su-pw' => self::REQ, 'target-dump' => self::REQ, 'source-dump' => self::OPT, 'extra-dump' => self::REQ]): void { $manager = $this->siteAliasManager(); $sourceRecord = $manager->get($source); @@ -67,7 +68,7 @@ public function sqlsync($source, $target, $options = ['no-dump' => false, 'no-sy * @hook validate sql-sync * @throws \Exception */ - public function validate(CommandData $commandData) + public function validate(CommandData $commandData): void { $source = $commandData->input()->getArgument('source'); $target = $commandData->input()->getArgument('target'); @@ -107,7 +108,7 @@ public function validate(CommandData $commandData) } } - public function databaseName(SiteAlias $record) + public function databaseName(SiteAlias $record): string { if ($this->processManager()->hasTransport($record) && $this->getConfig()->simulate()) { return 'simulated_db'; @@ -124,17 +125,15 @@ public function databaseName(SiteAlias $record) } /** - * Perform sql-dump on source unless told otherwise. + * Perform sql-dump on source unless told otherwise. Returns the path to the dump file. * * @param $options * @param $global_options * @param $sourceRecord * - * @return string - * Path to the source dump file. * @throws \Exception */ - public function dump($options, $global_options, $sourceRecord) + public function dump(array $options, array $global_options, SiteAlias $sourceRecord): string { $dump_options = $global_options + [ 'gzip' => true, @@ -148,17 +147,8 @@ public function dump($options, $global_options, $sourceRecord) if ($this->getConfig()->simulate()) { $source_dump_path = '/simulated/path/to/dump.tgz'; } else { - // First try a Drush 9.6+ return format. $json = $process->getOutputAsJson(); - if (!empty($json['path'])) { - $source_dump_path = $json['path']; - } else { - // Next, try 9.5- format. - $return = drush_backend_parse_output($process->getOutput()); - if (!$return['error_status'] || !empty($return['object'])) { - $source_dump_path = $return['object']; - } - } + $source_dump_path = $json['path']; } } else { $source_dump_path = $options['source-dump']; @@ -172,14 +162,11 @@ public function dump($options, $global_options, $sourceRecord) /** * @param array $options - * @param SiteAlias $sourceRecord - * @param SiteAlias $targetRecord * @param $source_dump_path - * @return string * Path to the target file. * @throws \Exception */ - public function rsync($options, SiteAlias $sourceRecord, SiteAlias $targetRecord, $source_dump_path) + public function rsync(array $options, SiteAlias $sourceRecord, SiteAlias $targetRecord, $source_dump_path): string { $do_rsync = !$options['no-sync']; // Determine path/to/dump on target. @@ -230,7 +217,7 @@ public function rsync($options, SiteAlias $sourceRecord, SiteAlias $targetRecord * @param $target_dump_path * @param $targetRecord */ - public function import($global_options, $target_dump_path, $targetRecord) + public function import($global_options, $target_dump_path, $targetRecord): void { $this->logger()->notice(dt('Starting to import dump file onto target database.')); $query_options = $global_options + [ diff --git a/src/Config/ConfigAwareTrait.php b/src/Config/ConfigAwareTrait.php index c0f317c84f..8c8bdb0d9c 100644 --- a/src/Config/ConfigAwareTrait.php +++ b/src/Config/ConfigAwareTrait.php @@ -1,4 +1,5 @@ config->addPlaceholder(self::USER_CONTEXT); $this->config->addPlaceholder(self::DRUPAL_CONTEXT); - $this->config->addPlaceholder(self::SITE_CONTEXT); // not implemented yet (multisite) + $this->config->addPlaceholder(self::SITE_CONTEXT); $this->config->addPlaceholder(self::ALIAS_CONTEXT); $this->config->addPlaceholder(self::PREFLIGHT_CONTEXT); $this->config->addPlaceholder(self::ENVIRONMENT_CONTEXT); @@ -118,10 +121,8 @@ public function __construct($envPrefix = '', $configFileVariant = '') /** * Put the config locator into 'local 'mode. - * - * @param bool $isLocal */ - public function setLocal($isLocal) + public function setLocal(bool $isLocal): void { $this->isLocal = $isLocal; } @@ -132,10 +133,9 @@ public function setLocal($isLocal) * then the sources will be accumulated as config files are loaded. Otherwise, * this information will not be saved. * - * @param bool $collect * @return $this */ - public function collectSources($collect = true) + public function collectSources(bool $collect = true): self { $this->sources = $collect ? [] : false; return $this; @@ -160,7 +160,7 @@ public function sources() * * @return string[] */ - public function configFilePaths() + public function configFilePaths(): array { return $this->configFilePaths; } @@ -168,7 +168,7 @@ public function configFilePaths() /** * Accumulate the sources provided by the configuration loader. */ - protected function addToSources(array $sources) + protected function addToSources(array $sources): void { if (!is_array($this->sources)) { return; @@ -179,10 +179,8 @@ protected function addToSources(array $sources) /** * Return the configuration object. Create it and load it with * all identified configuration if necessary. - * - * @return Config */ - public function config() + public function config(): ConfigInterface { return $this->config; } @@ -194,9 +192,8 @@ public function config() * obtained by commands et. al. as needed. @see Environment::exportConfigData() * * @param Environment $environent - * @return $this */ - public function addEnvironment(Environment $environment) + public function addEnvironment(Environment $environment): self { $this->config->getContext(self::ENVIRONMENT_CONTEXT)->import($environment->exportConfigData()); return $this; @@ -206,9 +203,8 @@ public function addEnvironment(Environment $environment) * Add config paths defined in preflight configuration. * * @param array $paths - * @return $this */ - public function addPreflightConfigFiles($filepaths) + public function addPreflightConfigFiles($filepaths): self { $this->addConfigPaths(self::PREFLIGHT_CONTEXT, (array) $filepaths); return $this; @@ -216,10 +212,9 @@ public function addPreflightConfigFiles($filepaths) /** * Take any configuration from the active alias record, and add it - * to our configuratino. - * @return $this + * to our configuration. */ - public function addAliasConfig($aliasConfig) + public function addAliasConfig($aliasConfig): self { $this->config->addContext(self::ALIAS_CONTEXT, $aliasConfig); return $this; @@ -231,9 +226,8 @@ public function addAliasConfig($aliasConfig) * add all of the user configuration paths. * * In 'local' mode, only the --config location is used. - * @return $this */ - public function addUserConfig($configPaths, $systemConfigPath, $userConfigDir) + public function addUserConfig($configPaths, $systemConfigPath, $userConfigDir): self { $paths = $configPaths; if (!$this->isLocal) { @@ -246,10 +240,9 @@ public function addUserConfig($configPaths, $systemConfigPath, $userConfigDir) /** * Add the Drush project directory as a configuration search location. * - * @param $drushProjectDir path to the drush project directory - * @return $this + * @param $drushProjectDir Path to the drush project directory. */ - public function addDrushConfig($drushProjectDir) + public function addDrushConfig($drushProjectDir): self { if (!$this->isLocal) { $this->addConfigPaths(self::DRUSH_CONTEXT, [ $drushProjectDir ]); @@ -262,7 +255,6 @@ public function addDrushConfig($drushProjectDir) * selected site. * * @param Path to the selected Drupal site - * @return $this */ public function addSitewideConfig($siteRoot) { @@ -291,9 +283,8 @@ public function addSitewideConfig($siteRoot) * * @param string $contextName Which context to put all configuration files in. * @param string[] $paths List of paths to search for configuration. - * @return $this */ - public function addConfigPaths($contextName, $paths) + public function addConfigPaths(string $contextName, array $paths): self { $loader = new YamlConfigLoader(); // Make all of the config values parsed so far available in evaluations. @@ -331,13 +322,9 @@ public function addConfigPaths($contextName, $paths) } /** - * Adds $configFiles config files. - * - * @param ConfigProcessor $processor - * @param ConfigLoaderInterface $loader - * @param array $configFiles + * Adds $configFiles to the list of config files. */ - protected function addConfigFiles(ConfigProcessor $processor, ConfigLoaderInterface $loader, array $configFiles) + protected function addConfigFiles(ConfigProcessor $processor, ConfigLoaderInterface $loader, array $configFiles): void { foreach ($configFiles as $configFile) { $processor->extend($loader->load($configFile)); @@ -350,11 +337,9 @@ protected function addConfigFiles(ConfigProcessor $processor, ConfigLoaderInterf * return all of the candidates that can be found. Candidates may be * either directories or files. * - * @param string[] $paths - * @param string[] $candidates * @return string[] paths */ - protected function identifyCandidates($paths, $candidates) + protected function identifyCandidates(array $paths, array $candidates): array { $configFiles = []; foreach ($paths as $path) { @@ -367,11 +352,10 @@ protected function identifyCandidates($paths, $candidates) * Search for all matching candidate locations at a single path. * Candidate locations may be either directories or files. * - * @param string $path * @param string[] $candidates * @return string[] */ - protected function identifyCandidatesAtPath($path, $candidates) + protected function identifyCandidatesAtPath(string $path, array $candidates): array { if (!is_dir($path)) { return []; @@ -390,12 +374,10 @@ protected function identifyCandidatesAtPath($path, $candidates) /** * Get the site aliases according to preflight arguments and environment. * - * @param $preflightArgs + * @param $paths * @param Environment $environment - * - * @return array */ - public function getSiteAliasPaths($paths, Environment $environment) + public function getSiteAliasPaths($paths, Environment $environment): array { // In addition to the paths passed in to us (from --alias-path // commandline options), add some site-local locations. @@ -412,19 +394,17 @@ function ($item) { }, array_unique($base_dirs) ); - $paths = array_merge($paths, $site_local_paths); - return $paths; + return array_merge($paths, $site_local_paths); } /** * Get the commandfile paths according to preflight arguments. * - * @param $preflightArgs - * - * @return array + * @param $commandPaths + * @param $root */ - public function getCommandFilePaths($commandPaths, $root) + public function getCommandFilePaths($commandPaths, $root): array { $builtin = $this->getBuiltinCommandFilePaths(); $included = $this->getIncludedCommandFilePaths($commandPaths); @@ -440,7 +420,7 @@ public function getCommandFilePaths($commandPaths, $root) /** * Return all of the built-in commandfile locations */ - protected function getBuiltinCommandFilePaths() + protected function getBuiltinCommandFilePaths(): array { return [ dirname(__DIR__), @@ -451,7 +431,7 @@ protected function getBuiltinCommandFilePaths() * Return all of the commandfile locations specified via * an 'include' option. */ - protected function getIncludedCommandFilePaths($commandPaths) + protected function getIncludedCommandFilePaths($commandPaths): array { $searchpath = []; @@ -461,7 +441,7 @@ protected function getIncludedCommandFilePaths($commandPaths) // This indicates an include path that has a namespace, // e.g. `namespace#/path`. if (is_numeric($key) && strpos($commandPath, '#') !== false) { - list($key, $commandPath) = explode('#', $commandPath, 2); + [$key, $commandPath] = explode('#', $commandPath, 2); } $sep = ($this->config->isWindows()) ? ';' : ':'; foreach (explode($sep, $commandPath) as $path) { @@ -484,7 +464,7 @@ protected function getIncludedCommandFilePaths($commandPaths) * 'dirname($root)/drush' directory that contains a composer.json * file or a 'Commands' or 'src/Commands' directory. */ - protected function getSiteCommandFilePaths($root) + protected function getSiteCommandFilePaths($root): array { $directories = ["$root/drush", dirname($root) . '/drush', "$root/sites/all/drush"]; @@ -493,10 +473,8 @@ protected function getSiteCommandFilePaths($root) /** * Sets the composer root. - * - * @param $selectedComposerRoot */ - public function setComposerRoot($selectedComposerRoot) + public function setComposerRoot($selectedComposerRoot): void { $this->composerRoot = $selectedComposerRoot; @@ -508,7 +486,7 @@ public function setComposerRoot($selectedComposerRoot) /** * Double the candidates, adding '$prefix' before each existing one. */ - public function expandCandidates($candidates, $prefix) + public function expandCandidates($candidates, $prefix): array { $additional = array_map( function ($item) use ($prefix) { @@ -528,11 +506,11 @@ function ($item) use ($prefix) { * @param array $candidates * An array filenames that are considered config files. * - * @return array - * An array. The first row is an array of files, the second row is an + * @return + * An array whose first item is an array of files, and the second item is an * array of dirs. */ - protected function findConfigFiles($paths, $candidates) + protected function findConfigFiles(array $paths, array $candidates): array { $files = []; $dirs = []; @@ -549,9 +527,39 @@ protected function findConfigFiles($paths, $candidates) // Search directories for config file candidates. $discovered_config_files = $this->identifyCandidates($dirs, $candidates); - // Merge discoverd candidates with explicitly specified config files. + // Merge discovered candidates with explicitly specified config files. $config_files = array_merge($discovered_config_files, $files); return $config_files; } + + /** + * Attempt to load site specific configuration. + * + * @param DrushConfig $config + * The config object. + * @param $siteConfig + * The site-specific config file. + * + * @return + * Whether the config exists and was processed. + */ + public static function addSiteSpecificConfig(DrushConfig $config, $siteConfig): bool + { + if (file_exists($siteConfig)) { + $loader = new YamlConfigLoader(); + $processor = new ConfigProcessor(); + $reference = $config->export(); + $context = $config->getContext(ConfigLocator::SITE_CONTEXT); + $processor->add($context->export()); + $processor->extend($loader->load($siteConfig)); + $context->import($processor->export($reference)); + $config->addContext(ConfigLocator::SITE_CONTEXT, $context); + $presetConfig = $config->get('runtime.config.paths'); + $config->set('runtime.config.paths', array_merge($presetConfig, [$siteConfig])); + return true; + } else { + return false; + } + } } diff --git a/src/Config/DrushConfig.php b/src/Config/DrushConfig.php index d0663af760..32ef70677e 100644 --- a/src/Config/DrushConfig.php +++ b/src/Config/DrushConfig.php @@ -1,11 +1,12 @@ get(\Robo\Config\Config::SIMULATE); - } - - /** - * Return 'true' if we are in backend mode. - */ - public function backend() - { - return $this->get(PreflightArgs::BACKEND); + return $this->get(Config::SIMULATE); } /** @@ -73,24 +66,4 @@ public function configPaths() { return $this->get('runtime.config.paths', []); } - - public function cache() - { - $candidates = [ - $this->get('drush.paths.cache-directory'), - Path::join($this->home(), '.drush/cache'), - Path::join($this->tmp(), 'drush-' . $this->user() . '/cache'), - ]; - - $fs = new Filesystem(); - foreach (array_filter($candidates) as $candidate) { - try { - $fs->mkdir($candidate); - return $candidate; - } catch (IOException $ioException) { - // Do nothing. Jump to the next candidate. - } - } - throw new \Exception('Cannot create the Drush cache directory. Tried next candidates: ' . implode(', ', $candidates)); - } } diff --git a/src/Config/Environment.php b/src/Config/Environment.php index cb8a07f3f4..2804f77167 100644 --- a/src/Config/Environment.php +++ b/src/Config/Environment.php @@ -1,8 +1,8 @@ drushBasePath; } @@ -219,25 +212,21 @@ public function getSiteSetAliasName() /** * User's home directory - * - * @return string */ - public function homeDir() + public function homeDir(): string { return $this->homeDir; } /** * The user's Drush configuration directory, ~/.drush - * - * @return string */ - public function userConfigPath() + public function userConfigPath(): string { return $this->homeDir() . '/.drush'; } - public function setConfigFileVariant($variant) + public function setConfigFileVariant($variant): void { $this->configFileVariant = $variant; } @@ -254,30 +243,24 @@ public function getConfigFileVariant() /** * The original working directory - * - * @return string */ - public function cwd() + public function cwd(): string { return $this->originalCwd; } /** * Return the path to Drush's vendor directory - * - * @return string */ - public function vendorPath() + public function vendorPath(): string { return $this->vendorDir; } /** * The class loader returned when the autoload.php file is included. - * - * @return \Composer\Autoload\ClassLoader */ - public function loader() + public function loader(): ?ClassLoader { return $this->loader; } @@ -285,19 +268,17 @@ public function loader() /** * Set the class loader from the autload.php file, if available. * - * @param \Composer\Autoload\ClassLoader $loader + * @param ClassLoader $loader */ - public function setLoader(ClassLoader $loader) + public function setLoader(ClassLoader $loader): void { $this->loader = $loader; } /** - * Alter our default locations based on the value of environment variables - * - * @return $this + * Alter our default locations based on the value of environment variables. */ - public function applyEnvironment() + public function applyEnvironment(): self { // Copy ETC_PREFIX and SHARE_PREFIX from environment variables if available. // This alters where we check for server-wide config and alias files. @@ -310,12 +291,9 @@ public function applyEnvironment() /** * Set the directory prefix to locate the directory that Drush will - * use as /etc (e.g. during the functional tests) - * - * @param string $etcPrefix - * @return $this + * use as /etc (e.g. during the functional tests). */ - public function setEtcPrefix($etcPrefix) + public function setEtcPrefix(string $etcPrefix): self { if (isset($etcPrefix)) { $this->etcPrefix = $etcPrefix; @@ -325,11 +303,9 @@ public function setEtcPrefix($etcPrefix) /** * Set the directory prefix to locate the directory that Drush will - * use as /user/share (e.g. during the functional tests) - * @param string $sharePrefix - * @return $this + * use as /user/share (e.g. during the functional tests). */ - public function setSharePrefix($sharePrefix) + public function setSharePrefix(string $sharePrefix): self { if (isset($sharePrefix)) { $this->sharePrefix = $sharePrefix; @@ -343,10 +319,8 @@ public function setSharePrefix($sharePrefix) * this is within the Drush application, but some Drush RPM distributions * & c. for Linux platforms slice-and-dice the contents and put the docs * elsewhere. - * - * @return string */ - public function docsPath() + public function docsPath(): ?string { if (!$this->docPrefix) { $this->docPrefix = $this->findDocsPath($this->drushBasePath); @@ -358,10 +332,9 @@ public function docsPath() * Locate the Drush documentation. This is recalculated whenever the * share prefix is changed. * - * @param string $drushBasePath - * @return string + * @return string|bool */ - protected function findDocsPath($drushBasePath) + protected function findDocsPath(string $drushBasePath) { $candidates = [ "$drushBasePath/README.md", @@ -373,10 +346,9 @@ protected function findDocsPath($drushBasePath) /** * Check a list of directories and return the first one that exists. * - * @param array $candidates * @return string|boolean */ - protected function findFromCandidates($candidates) + protected function findFromCandidates(array $candidates) { foreach ($candidates as $candidate) { if (file_exists($candidate)) { @@ -388,11 +360,8 @@ protected function findFromCandidates($candidates) /** * Return the appropriate system path prefix, unless an override is provided. - * @param string $override - * @param string $defaultPrefix - * @return string */ - protected static function systemPathPrefix($override = '', $defaultPrefix = '') + protected static function systemPathPrefix(string $override = '', string $defaultPrefix = ''): string { if ($override) { return $override; @@ -402,30 +371,24 @@ protected static function systemPathPrefix($override = '', $defaultPrefix = '') /** * Return the system configuration path (default: /etc/drush) - * - * @return string */ - public function systemConfigPath() + public function systemConfigPath(): string { return static::systemPathPrefix($this->etcPrefix, '') . '/etc/drush'; } /** * Return the system shared commandfile path (default: /usr/share/drush/commands) - * - * @return string */ - public function systemCommandFilePath() + public function systemCommandFilePath(): string { return static::systemPathPrefix($this->sharePrefix, '/usr') . '/share/drush/commands'; } /** * Determine whether current OS is a Windows variant. - * - * @return boolean */ - public static function isWindows($os = null) + public static function isWindows($os = null): bool { return strtoupper(substr($os ?: PHP_OS, 0, 3)) === 'WIN'; } @@ -433,21 +396,18 @@ public static function isWindows($os = null) /** * Verify that we are running PHP through the command line interface. * - * @return boolean * A boolean value that is true when PHP is being run through the command line, * and false if being run through cgi or mod_php. */ - public function verifyCLI() + public function verifyCLI(): bool { return (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0)); } /** * Get terminal width. - * - * @return int */ - public function calculateColumns() + public function calculateColumns(): int { return (new Terminal())->getWidth(); } @@ -455,13 +415,13 @@ public function calculateColumns() /** * Returns the filename for the file that stores the DRUPAL_SITE variable. * - * @param string $filename_prefix + * @param $filename_prefix * An arbitrary string to prefix the filename with. * * @return string|false * Returns the full path to temp file if possible, or FALSE if not. */ - protected function getSiteSetAliasFilePath($filename_prefix = 'drush-drupal-site-') + protected function getSiteSetAliasFilePath(string $filename_prefix = 'drush-drupal-site-') { $shell_pid = getenv('DRUSH_SHELL_PID'); if (!$shell_pid && function_exists('posix_getppid')) { diff --git a/src/Config/Loader/YamlConfigLoader.php b/src/Config/Loader/YamlConfigLoader.php index 4a5d7bb2bb..c1b6f030bb 100644 --- a/src/Config/Loader/YamlConfigLoader.php +++ b/src/Config/Loader/YamlConfigLoader.php @@ -2,7 +2,7 @@ namespace Drush\Config\Loader; -use Drush\Internal\Config\Yaml\Yaml; +use Symfony\Component\Yaml\Yaml; use Consolidation\Config\Loader\ConfigLoader; /** @@ -11,7 +11,7 @@ */ class YamlConfigLoader extends ConfigLoader { - public function load($path) + public function load($path): self { $this->setSourceName($path); diff --git a/src/Drupal/Commands/config/ConfigCommands.php b/src/Drupal/Commands/config/ConfigCommands.php index ac9f78a6c6..be25128081 100644 --- a/src/Drupal/Commands/config/ConfigCommands.php +++ b/src/Drupal/Commands/config/ConfigCommands.php @@ -1,6 +1,8 @@ configFactory; } @@ -58,7 +57,7 @@ public function getConfigFactory() /** * ConfigCommands constructor. * @param ConfigFactoryInterface $configFactory - * @param \Drupal\Core\Config\StorageInterface $configStorage + * @param StorageInterface $configStorage */ public function __construct($configFactory, StorageInterface $configStorage) { @@ -68,9 +67,9 @@ public function __construct($configFactory, StorageInterface $configStorage) } /** - * @param \Drupal\Core\Config\StorageInterface $exportStorage + * @param StorageInterface $exportStorage */ - public function setExportStorage(StorageInterface $exportStorage) + public function setExportStorage(StorageInterface $exportStorage): void { $this->configStorageExport = $exportStorage; } @@ -86,26 +85,17 @@ public function getConfigStorageExport() return $this->configStorage; } - /** - * @param \Drupal\Core\Config\ImportStorageTransformer $importStorageTransformer - */ - public function setImportTransformer($importStorageTransformer) + public function setImportTransformer(ImportStorageTransformer $importStorageTransformer): void { $this->importStorageTransformer = $importStorageTransformer; } - /** - * @return bool - */ - public function hasImportTransformer() + public function hasImportTransformer(): bool { return isset($this->importStorageTransformer); } - /** - * @return \Drupal\Core\Config\ImportStorageTransformer - */ - public function getImportTransformer() + public function getImportTransformer(): ImportStorageTransformer { return $this->importStorageTransformer; } @@ -137,59 +127,64 @@ public function get($config_name, $key = '', $options = ['format' => 'yaml', 'so } /** - * Set config value directly. Does not perform a config import. + * Save a config value directly. Does not perform a config import. * * @command config:set * @validate-config-name * @todo @interact-config-name deferred until we have interaction for key. * @param $config_name The config object name, for example system.site. - * @param $key The config key, for example page.front. - * @param $value The value to assign to the config key. Use - to read from STDIN. - * @option input-format Format to parse the object. Recognized values: string, yaml - * @option value The value to assign to the config key (if any). - * @hidden-options value + * @param $key The config key, for example page.front. Use ? if you are updating multiple top-level keys. + * @param $value The value to assign to the config key. Use - to read from Stdin. + * @option input-format Format to parse the object. Recognized values: string, yaml. Since JSON is a subset of YAML, $value may be in JSON format. + * @usage drush config:set system.site name MySite + * Sets a value for the key name of system.site config object. * @usage drush config:set system.site page.front '/path/to/page' * Sets the given URL path as value for the config item with key page.front of system.site config object. * @usage drush config:set system.site '[]' * Sets the given key to an empty array. + * @usage drush config:set --input-format=yaml user.role.authenticated permissions [foo,bar] + * Use a sequence as value for the key permissions of user.role.authenticated config object. + * @usage drush config:set --input-format=yaml system.site page {403: '403', front: home} + * Use a mapping as value for the key page of system.site config object. + * @usage drush config:set --input-format=yaml user.role.authenticated ? "{label: 'Auth user', weight: 5}" + * Update two top level keys (label, weight) in the system.site config object. * @aliases cset,config-set */ - public function set($config_name, $key, $value = null, $options = ['input-format' => 'string', 'value' => self::REQ]) + public function set($config_name, $key, $value, $options = ['input-format' => 'string']) { - // This hidden option is a convenient way to pass a value without passing a key. - $data = $options['value'] ?: $value; + $data = $value; if (!isset($data)) { throw new \Exception(dt('No config value specified.')); } - $config = $this->getConfigFactory()->getEditable($config_name); - // Check to see if config key already exists. - $new_key = $config->get($key) === null; - // Special flag indicating that the value has been passed via STDIN. if ($data === '-') { $data = $this->stdin()->contents(); } - // Special handling for empty array. if ($data == '[]') { $data = []; } - // Now, we parse the value. + // Parse the value if needed. switch ($options['input-format']) { case 'yaml': $parser = new Parser(); $data = $parser->parse($data, true); } - if (is_array($data) && !empty($data) && $this->io()->confirm(dt('Do you want to update or set multiple keys on !name config.', ['!name' => $config_name]))) { - foreach ($data as $data_key => $value) { - $config->set("$key.$data_key", $value); + $config = $this->getConfigFactory()->getEditable($config_name); + // Check to see if config key already exists. + $new_key = $config->get($key) === null; + $simulate = $this->getConfig()->simulate(); + + if ($key == '?' && !empty($data) && $this->io()->confirm(dt('Do you want to update or set multiple keys on !name config.', ['!name' => $config_name]))) { + foreach ($data as $data_key => $val) { + $config->set($data_key, $val); } - return $config->save(); + return $simulate ? self::EXIT_SUCCESS : $config->save(); } else { $confirmed = false; if ($config->isNew() && $this->io()->confirm(dt('!name config does not exist. Do you want to create a new config object?', ['!name' => $config_name]))) { @@ -199,7 +194,7 @@ public function set($config_name, $key, $value = null, $options = ['input-format } elseif ($this->io()->confirm(dt('Do you want to update !key key in !name config?', ['!key' => $key, '!name' => $config_name]))) { $confirmed = true; } - if ($confirmed && !$this->getConfig()->simulate()) { + if ($confirmed && !$simulate) { return $config->set($key, $data)->save(); } } @@ -224,7 +219,7 @@ public function set($config_name, $key, $value = null, $options = ['input-format * @aliases cedit,config-edit * @validate-module-enabled config */ - public function edit($config_name, $options = []) + public function edit($config_name, $options = []): void { $config = $this->getConfigFactory()->get($config_name); $active_storage = $config->getStorage(); @@ -237,7 +232,7 @@ public function edit($config_name, $options = []) // Note that `getEditor()` returns a string that contains a // %s placeholder for the config file path. - $exec = self::getEditor(); + $exec = self::getEditor($options['editor']); $cmd = sprintf($exec, Escape::shellArg($temp_storage->getFilePath($config_name))); $process = $this->processManager()->shell($cmd); $process->setTty(true); @@ -266,7 +261,7 @@ public function edit($config_name, $options = []) * Delete the 'page.front' key from the system.site object. * @aliases cdel,config-delete */ - public function delete($config_name, $key = null) + public function delete($config_name, $key = null): void { $config = $this->getConfigFactory()->getEditable($config_name); if ($key) { @@ -301,9 +296,8 @@ public function delete($config_name, $key = null) * @default-fields name,state * @aliases cst,config-status * @filter-default-field name - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields */ - public function status($options = ['state' => 'Only in DB,Only in sync dir,Different', 'prefix' => self::REQ]) + public function status($options = ['state' => 'Only in DB,Only in sync dir,Different', 'prefix' => self::REQ]): ?RowsOfFields { $config_list = array_fill_keys( $this->configFactory->listAll($options['prefix']), @@ -380,7 +374,7 @@ public function status($options = ['state' => 'Only in DB,Only in sync dir,Diffe * @param string $directory * A configuration directory. */ - public static function getDirectory($directory = null) + public static function getDirectory($directory = null): string { $return = null; // If the user provided a directory, use it. @@ -403,7 +397,7 @@ public static function getDirectory($directory = null) /** * Returns the difference in configuration between active storage and target storage. */ - public function getChanges($target_storage) + public function getChanges($target_storage): array { if ($this->hasImportTransformer()) { $target_storage = $this->getImportTransformer()->transform($target_storage); @@ -440,7 +434,7 @@ public function getStorage($directory) * * @return Table A Symfony table object. */ - public static function configChangesTable(array $config_changes, OutputInterface $output, $use_color = true) + public static function configChangesTable(array $config_changes, OutputInterface $output, $use_color = true): Table { $rows = []; foreach ($config_changes as $collection => $changes) { @@ -483,7 +477,7 @@ public static function configChangesTable(array $config_changes, OutputInterface /** * @hook interact @interact-config-name */ - public function interactConfigName($input, $output) + public function interactConfigName($input, $output): void { if (empty($input->getArgument('config_name'))) { $config_names = $this->getConfigFactory()->listAll(); @@ -499,8 +493,8 @@ public function interactConfigName($input, $output) * argument name as the value of the annotation. * * @hook validate @validate-config-name - * @param \Consolidation\AnnotatedCommand\CommandData $commandData - * @return \Consolidation\AnnotatedCommand\CommandError|null + * @param CommandData $commandData + * @return CommandError|null */ public function validateConfigName(CommandData $commandData) { @@ -522,7 +516,7 @@ public function validateConfigName(CommandData $commandData) * The destination config storage service. * @throws \Exception */ - public static function copyConfig(StorageInterface $source, StorageInterface $destination) + public static function copyConfig(StorageInterface $source, StorageInterface $destination): void { // Make sure the source and destination are on the default collection. if ($source->getCollectionName() != StorageInterface::DEFAULT_COLLECTION) { @@ -560,7 +554,7 @@ public static function copyConfig(StorageInterface $source, StorageInterface $de * @return array|bool * An array of strings containing the diff. */ - public static function getDiff(StorageInterface $destination_storage, StorageInterface $source_storage, OutputInterface $output) + public static function getDiff(StorageInterface $destination_storage, StorageInterface $source_storage, OutputInterface $output): string { // Copy active storage to a temporary directory. $temp_destination_dir = drush_tempdir(); diff --git a/src/Drupal/Commands/config/ConfigExportCommands.php b/src/Drupal/Commands/config/ConfigExportCommands.php index f9b175c145..9495c8118e 100644 --- a/src/Drupal/Commands/config/ConfigExportCommands.php +++ b/src/Drupal/Commands/config/ConfigExportCommands.php @@ -1,4 +1,5 @@ configManager; } /** - * @param \Drupal\Core\Config\StorageInterface $exportStorage + * @param StorageInterface $exportStorage */ - public function setExportStorage(StorageInterface $exportStorage) + public function setExportStorage(StorageInterface $exportStorage): void { $this->configStorageExport = $exportStorage; } - /** - * @return StorageInterface - */ - public function getConfigStorageExport() + public function getConfigStorageExport(): StorageInterface { if (isset($this->configStorageExport)) { return $this->configStorageExport; @@ -62,19 +57,13 @@ public function getConfigStorageExport() return $this->configStorage; } - /** - * @return StorageInterface - */ - public function getConfigStorage() + public function getConfigStorage(): StorageInterface { // @todo: deprecate this method. return $this->getConfigStorageExport(); } - /** - * @return StorageInterface - */ - public function getConfigStorageSync() + public function getConfigStorageSync(): StorageInterface { return $this->configStorageSync; } @@ -107,7 +96,7 @@ public function __construct(ConfigManagerInterface $configManager, StorageInterf * Export configuration; Save files in a backup directory named config-export. * @aliases cex,config-export */ - public function export($options = ['add' => false, 'commit' => false, 'message' => self::REQ, 'destination' => self::OPT, 'diff' => false, 'format' => null]) + public function export($options = ['add' => false, 'commit' => false, 'message' => self::REQ, 'destination' => self::OPT, 'diff' => false, 'format' => null]): array { // Get destination directory. $destination_dir = ConfigCommands::getDirectory($options['destination']); @@ -177,7 +166,7 @@ public function doExport($options, $destination_dir) return isset($preview) ? $preview : 'No existing configuration to diff against.'; } - public function doAddCommit($options, $destination_dir, $preview) + public function doAddCommit($options, $destination_dir, $preview): void { // Commit or add exported configuration if requested. if ($options['commit']) { @@ -189,7 +178,7 @@ public function doAddCommit($options, $destination_dir, $preview) if (!empty($uncommitted_changes)) { $process = $this->processManager()->process(['git', 'add', '-A', '.'], $destination_dir); $process->mustRun(); - $comment_file = drush_save_data_to_temp_file($options['message'] ?: 'Exported configuration.'. $preview); + $comment_file = drush_save_data_to_temp_file($options['message'] ?: 'Exported configuration.' . $preview); $process = $this->processManager()->process(['git', 'commit', "--file=$comment_file"], $destination_dir); $process->mustRun(); } @@ -200,9 +189,9 @@ public function doAddCommit($options, $destination_dir, $preview) /** * @hook validate config-export - * @param \Consolidation\AnnotatedCommand\CommandData $commandData + * @param CommandData $commandData */ - public function validate(CommandData $commandData) + public function validate(CommandData $commandData): void { $destination = $commandData->input()->getOption('destination'); diff --git a/src/Drupal/Commands/config/ConfigImportCommands.php b/src/Drupal/Commands/config/ConfigImportCommands.php index e81000c6b6..3db53e35c7 100644 --- a/src/Drupal/Commands/config/ConfigImportCommands.php +++ b/src/Drupal/Commands/config/ConfigImportCommands.php @@ -1,6 +1,10 @@ configManager; } - /** - * @return StorageInterface - */ - public function getConfigStorage() + public function getConfigStorage(): StorageInterface { return $this->configStorage; } - /** - * @return StorageInterface - */ - public function getConfigStorageSync() + public function getConfigStorageSync(): StorageInterface { return $this->configStorageSync; } - /** - * @return \Drupal\Core\Cache\CacheBackendInterface - */ - public function getConfigCache() + public function getConfigCache(): CacheBackendInterface { return $this->configCache; } - /** - * @return \Drupal\Core\Extension\ModuleHandlerInterface - */ - public function getModuleHandler() + public function getModuleHandler(): ModuleHandlerInterface { return $this->moduleHandler; } /** * Note that type hint is changing https://www.drupal.org/project/drupal/issues/3161983 - * - * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface */ - public function getEventDispatcher() + public function getEventDispatcher(): EventDispatcherInterface { return $this->eventDispatcher; } - /** - * @return \Drupal\Core\Lock\LockBackendInterface - */ - public function getLock() + public function getLock(): LockBackendInterface { return $this->lock; } - /** - * @return \Drupal\Core\Config\TypedConfigManagerInterface - */ - public function getConfigTyped() + public function getConfigTyped(): TypedConfigManagerInterface { return $this->configTyped; } - /** - * @return \Drupal\Core\Extension\ModuleInstallerInterface - */ - public function getModuleInstaller() + public function getModuleInstaller(): ModuleInstallerInterface { return $this->moduleInstaller; } - /** - * @return \Drupal\Core\Extension\ThemeHandlerInterface - */ - public function getThemeHandler() + public function getThemeHandler(): ThemeHandlerInterface { return $this->themeHandler; } - /** - * @return \Drupal\Core\StringTranslation\TranslationInterface - */ - public function getStringTranslation() + public function getStringTranslation(): TranslationInterface { return $this->stringTranslation; } - /** - * @param \Drupal\Core\Config\ImportStorageTransformer $importStorageTransformer - */ - public function setImportTransformer($importStorageTransformer) + public function setImportTransformer(ImportStorageTransformer $importStorageTransformer): void { $this->importStorageTransformer = $importStorageTransformer; } - /** - * @return bool - */ - public function hasImportTransformer() + public function hasImportTransformer(): bool { return isset($this->importStorageTransformer); } - /** - * @return \Drupal\Core\Config\ImportStorageTransformer - */ - public function getImportTransformer() + public function getImportTransformer(): ?ImportStorageTransformer { return $this->importStorageTransformer; } /** - * @return \Drupal\Core\Extension\ModuleExtensionList + * @return ModuleExtensionList */ - public function getModuleExtensionList(): \Drupal\Core\Extension\ModuleExtensionList + public function getModuleExtensionList(): ModuleExtensionList { return $this->moduleExtensionList; } @@ -189,15 +151,15 @@ public function getModuleExtensionList(): \Drupal\Core\Extension\ModuleExtension * @param ConfigManagerInterface $configManager * @param StorageInterface $configStorage * @param StorageInterface $configStorageSync - * @param \Drupal\Core\Cache\CacheBackendInterface $configCache - * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler + * @param CacheBackendInterface $configCache + * @param ModuleHandlerInterface $moduleHandler * @param $eventDispatcher - * @param \Drupal\Core\Lock\LockBackendInterface $lock - * @param \Drupal\Core\Config\TypedConfigManagerInterface $configTyped - * @param \Drupal\Core\Extension\ModuleInstallerInterface $moduleInstaller - * @param \Drupal\Core\Extension\ThemeHandlerInterface $themeHandler - * @param \Drupal\Core\StringTranslation\TranslationInterface $stringTranslation - * @param \Drupal\Core\Extension\ModuleExtensionList $moduleExtensionList + * @param LockBackendInterface $lock + * @param TypedConfigManagerInterface $configTyped + * @param ModuleInstallerInterface $moduleInstaller + * @param ThemeHandlerInterface $themeHandler + * @param TranslationInterface $stringTranslation + * @param ModuleExtensionList $moduleExtensionList */ public function __construct( ConfigManagerInterface $configManager, @@ -234,21 +196,19 @@ public function __construct( * * @command config:import * - * @param array $options * * @return bool|void * @option diff Show preview as a diff. - * @option preview Deprecated. Format for displaying proposed changes. Recognized values: list, diff. - * @option source An arbitrary directory that holds the configuration files. An alternative to label argument + * @option source An arbitrary directory that holds the configuration files. * @option partial Allows for partial config imports from the source directory. Only updates and new configs will be processed with this flag (missing configs will not be deleted). No config transformation happens. * @aliases cim,config-import * @topics docs:deploy * @bootstrap full * - * @throws \Drupal\Core\Config\StorageTransformerException - * @throws \Drush\Exceptions\UserAbortException + * @throws StorageTransformerException + * @throws UserAbortException */ - public function import($options = ['preview' => 'list', 'source' => self::REQ, 'partial' => false, 'diff' => false]) + public function import(array $options = ['source' => self::REQ, 'partial' => false, 'diff' => false]) { // Determine source directory. $source_storage_dir = ConfigCommands::getDirectory($options['source']); @@ -286,7 +246,7 @@ public function import($options = ['preview' => 'list', 'source' => self::REQ, ' return; } - if ($options['preview'] == 'list' && !$options['diff']) { + if (!$options['diff']) { $change_list = []; foreach ($storage_comparer->getAllCollectionNames() as $collection) { $change_list[$collection] = $storage_comparer->getChangelist(null, $collection); @@ -306,7 +266,7 @@ public function import($options = ['preview' => 'list', 'source' => self::REQ, ' } // Copied from submitForm() at /core/modules/config/src/Form/ConfigSync.php - public function doImport($storage_comparer) + public function doImport($storage_comparer): void { $config_importer = new ConfigImporter( $storage_comparer, @@ -362,8 +322,8 @@ public function doImport($storage_comparer) /** * @hook validate config:import - * @param \Consolidation\AnnotatedCommand\CommandData $commandData - * @return \Consolidation\AnnotatedCommand\CommandError|null + * @param CommandData $commandData + * @return CommandError|null */ public function validate(CommandData $commandData) { diff --git a/src/Drupal/Commands/core/BatchCommands.php b/src/Drupal/Commands/core/BatchCommands.php index 01c625640d..4c7f947dbe 100644 --- a/src/Drupal/Commands/core/BatchCommands.php +++ b/src/Drupal/Commands/core/BatchCommands.php @@ -1,4 +1,5 @@ 'json']) + public function process($batch_id, $options = ['format' => 'json']): UnstructuredListData { $return = drush_batch_command($batch_id); return new UnstructuredListData($return); diff --git a/src/Drupal/Commands/core/CliCommands.php b/src/Drupal/Commands/core/CliCommands.php index f5e6156185..5c651f52a0 100644 --- a/src/Drupal/Commands/core/CliCommands.php +++ b/src/Drupal/Commands/core/CliCommands.php @@ -2,6 +2,7 @@ namespace Drush\Drupal\Commands\core; +use Consolidation\AnnotatedCommand\AnnotatedCommand; use Drush\Commands\DrushCommands; use Drush\Drush; use Drush\Psysh\DrushCommand; @@ -9,13 +10,12 @@ use Drupal\Component\Assertion\Handle; use Drush\Psysh\Shell; use Drush\Runtime\Runtime; +use Drush\Utils\FsUtils; use Psy\Configuration; use Psy\VersionUpdater\Checker; -use Webmozart\PathUtil\Path; class CliCommands extends DrushCommands { - /** * Drush's PHP Shell. * @@ -24,7 +24,7 @@ class CliCommands extends DrushCommands * @hidden * @topic ../../../../docs/repl.md */ - public function docs() + public function docs(): void { self::printFileTopic($this->commandData); } @@ -39,7 +39,7 @@ public function docs() * @topics docs:repl * @remote-tty */ - public function cli(array $options = ['version-history' => false, 'cwd' => self::REQ]) + public function cli(array $options = ['version-history' => false, 'cwd' => self::REQ]): void { $configuration = new Configuration(); @@ -99,10 +99,8 @@ public function cli(array $options = ['version-history' => false, 'cwd' => self: /** * Returns a filtered list of Drush commands used for CLI commands. - * - * @return array */ - protected function getDrushCommands() + protected function getDrushCommands(): array { $application = Drush::getApplication(); $commands = $application->all(); @@ -120,7 +118,7 @@ protected function getDrushCommands() ]; $php_keywords = $this->getPhpKeywords(); - /** @var \Consolidation\AnnotatedCommand\AnnotatedCommand $command */ + /** @var AnnotatedCommand $command */ foreach ($commands as $name => $command) { $definition = $command->getDefinition(); @@ -152,7 +150,7 @@ protected function getDrushCommands() * @return array. * An array of caster callbacks keyed by class or interface. */ - protected function getCasters() + protected function getCasters(): array { return [ 'Drupal\Core\Entity\ContentEntityInterface' => 'Drush\Psysh\Caster::castContentEntity', @@ -174,9 +172,9 @@ protected function getCasters() * * @return string. */ - protected function historyPath(array $options) + protected function historyPath(array $options): string { - $cli_directory = Path::join($this->getConfig()->cache(), 'cli'); + $cli_directory = FsUtils::getBackupDirParent(); $drupal_major_version = Drush::getMajorVersion(); // If there is no drupal version (and thus no root). Just use the current @@ -213,10 +211,8 @@ protected function historyPath(array $options) * Returns a list of PHP keywords. * * This will act as a blocklist for command and alias names. - * - * @return array */ - protected function getPhpKeywords() + protected function getPhpKeywords(): array { return [ '__halt_compiler', diff --git a/src/Drupal/Commands/core/DeployHookCommands.php b/src/Drupal/Commands/core/DeployHookCommands.php index c43690ad1e..9bcbb0a761 100644 --- a/src/Drupal/Commands/core/DeployHookCommands.php +++ b/src/Drupal/Commands/core/DeployHookCommands.php @@ -21,18 +21,16 @@ class DeployHookCommands extends DrushCommands implements SiteAliasManagerAwareI /** * Get the deploy hook update registry. - * - * @return UpdateRegistry */ - public static function getRegistry() + public static function getRegistry(): UpdateRegistry { - $registry = new class( - \Drupal::service('app.root'), - \Drupal::service('site.path'), + $registry = new class ( + \Drupal::getContainer()->getParameter('app.root'), + \Drupal::getContainer()->getParameter('site.path'), array_keys(\Drupal::service('module_handler')->getModuleList()), \Drupal::service('keyvalue')->get('deploy_hook') ) extends UpdateRegistry { - public function setUpdateType($type) + public function setUpdateType(string $type): void { $this->updateType = $type; } @@ -58,9 +56,8 @@ public function setUpdateType($type) * @topics docs:deploy * * @filter-default-field hook - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields */ - public function status() + public function status(): RowsOfFields { $updates = self::getRegistry()->getPendingUpdateInformation(); $rows = []; @@ -89,7 +86,7 @@ public function status() * @topics docs:deploy * @version 10.3 */ - public function run() + public function run(): int { $pending = self::getRegistry()->getPendingUpdateFunctions(); @@ -150,10 +147,8 @@ public function run() * @param string $batch_id The batch id that will be processed. * @bootstrap full * @hidden - * - * @return \Consolidation\OutputFormatters\StructuredData\UnstructuredListData */ - public function process($batch_id, $options = ['format' => 'json']) + public function process(string $batch_id, $options = ['format' => 'json']): UnstructuredListData { $result = drush_batch_command($batch_id); return new UnstructuredListData($result); @@ -164,10 +159,9 @@ public function process($batch_id, $options = ['format' => 'json']) * * @param string $function * The deploy-hook function to execute. - * @param DrushBatchContext $context * The batch context object. */ - public static function updateDoOneDeployHook($function, DrushBatchContext $context) + public static function updateDoOneDeployHook(string $function, DrushBatchContext $context): void { $ret = []; @@ -247,10 +241,8 @@ public static function updateDoOneDeployHook($function, DrushBatchContext $conte * Batch finished callback. * * @param boolean $success Whether the batch ended without a fatal error. - * @param array $results - * @param array $operations */ - public function updateFinished($success, $results, $operations) + public function updateFinished(bool $success, array $results, array $operations): void { // In theory there is nothing to do here. } @@ -265,7 +257,7 @@ public function updateFinished($success, $results, $operations) * @topics docs:deploy * @version 10.6.1 */ - public function markComplete() + public function markComplete(): int { $pending = self::getRegistry()->getPendingUpdateFunctions(); self::getRegistry()->registerInvokedUpdates($pending); diff --git a/src/Drupal/Commands/core/DrupalCommands.php b/src/Drupal/Commands/core/DrupalCommands.php index db7a3a2286..286f8c2ce3 100644 --- a/src/Drupal/Commands/core/DrupalCommands.php +++ b/src/Drupal/Commands/core/DrupalCommands.php @@ -1,4 +1,5 @@ cron; } - /** - * @return \Drupal\Core\Extension\ModuleHandlerInterface - */ - public function getModuleHandler() + public function getModuleHandler(): ModuleHandlerInterface { return $this->moduleHandler; } - /** - * @return \Drupal\Core\Routing\RouteProviderInterface - */ - public function getRouteProvider() + public function getRouteProvider(): RouteProviderInterface { return $this->routeProvider; } /** - * @param \Drupal\Core\CronInterface $cron + * @param CronInterface $cron * @param ModuleHandlerInterface $moduleHandler * @param RouteProviderInterface $routeProvider */ @@ -71,7 +62,7 @@ public function __construct(CronInterface $cron, ModuleHandlerInterface $moduleH * @aliases cron,core-cron * @topics docs:cron */ - public function cron() + public function cron(): void { $this->getCron()->run(); } @@ -96,9 +87,8 @@ public function cron() * value: Summary * @default-fields title,severity,value * @filter-default-field severity - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields */ - public function requirements($options = ['format' => 'table', 'severity' => -1, 'ignore' => '']) + public function requirements($options = ['format' => 'table', 'severity' => -1, 'ignore' => '']): RowsOfFields { include_once DRUSH_DRUPAL_CORE . '/includes/install.inc'; $severities = [ @@ -129,12 +119,11 @@ public function requirements($options = ['format' => 'table', 'severity' => -1, $min_severity = $options['severity']; foreach ($requirements as $key => $info) { - $info += ['value' => '', 'description' => '']; $severity = array_key_exists('severity', $info) ? $info['severity'] : -1; $rows[$key] = [ 'title' => self::styleRow((string) $info['title'], $options['format'], $severity), - 'value' => self::styleRow(DrupalUtil::drushRender($info['value']), $options['format'], $severity), - 'description' => self::styleRow(DrupalUtil::drushRender($info['description']), $options['format'], $severity), + 'value' => self::styleRow(DrupalUtil::drushRender($info['value'] ?? ''), $options['format'], $severity), + 'description' => self::styleRow(DrupalUtil::drushRender($info['description'] ?? ''), $options['format'], $severity), 'sid' => self::styleRow($severity, $options['format'], $severity), 'severity' => self::styleRow(@$severities[$severity], $options['format'], $severity) ]; @@ -142,8 +131,7 @@ public function requirements($options = ['format' => 'table', 'severity' => -1, unset($rows[$key]); } } - $result = new RowsOfFields($rows); - return $result; + return new RowsOfFields($rows); } /** @@ -155,13 +143,13 @@ public function requirements($options = ['format' => 'table', 'severity' => -1, * View all routes. * @usage drush route --name=update.status * View details about the update.status route. - * @usage drush route --path=user/1 + * @usage drush route --path=/user/1 * View details about the entity.user.canonical route. * @option name A route name. * @option path An internal path. * @version 10.5 */ - public function route($options = ['name' => self::REQ, 'path' =>self::REQ, 'format' => 'yaml']) + public function route($options = ['name' => self::REQ, 'path' => self::REQ, 'format' => 'yaml']) { $route = $items = null; $provider = $this->getRouteProvider(); @@ -194,13 +182,15 @@ public function route($options = ['name' => self::REQ, 'path' =>self::REQ, 'form } return $items; } - + private static function styleRow($content, $format, $severity): ?string { - if (!in_array($format, [ + if ( + !in_array($format, [ 'sections', 'table', - ])) { + ]) + ) { return $content; } diff --git a/src/Drupal/Commands/core/EntityCommands.php b/src/Drupal/Commands/core/EntityCommands.php index 7bd556d315..f00ea8b825 100644 --- a/src/Drupal/Commands/core/EntityCommands.php +++ b/src/Drupal/Commands/core/EntityCommands.php @@ -2,13 +2,16 @@ namespace Drush\Drupal\Commands\core; +use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException; +use Drupal\Component\Plugin\Exception\PluginNotFoundException; +use Drupal\Core\Entity\EntityStorageException; +use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drush\Commands\DrushCommands; use Drush\Utils\StringUtils; class EntityCommands extends DrushCommands { - protected $entityTypeManager; /** @@ -48,7 +51,7 @@ public function __construct(EntityTypeManagerInterface $entityTypeManager) * @aliases edel,entity-delete * @throws \Exception */ - public function delete($entity_type, $ids = null, $options = ['bundle' => self::REQ, 'exclude' => self::REQ, 'chunks' => 50]) + public function delete(string $entity_type, $ids = null, array $options = ['bundle' => self::REQ, 'exclude' => self::REQ, 'chunks' => 50]): void { $query = $this->getQuery($entity_type, $ids, $options); $result = $query->execute(); @@ -77,9 +80,9 @@ public function delete($entity_type, $ids = null, $options = ['bundle' => self:: * @param string $entity_type * @param array $ids * - * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException - * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException - * @throws \Drupal\Core\Entity\EntityStorageException + * @throws InvalidPluginDefinitionException + * @throws PluginNotFoundException + * @throws EntityStorageException */ public function doDelete(string $entity_type, array $ids): void { @@ -140,9 +143,9 @@ public function loadSave(string $entity_type, $ids = null, array $options = ['bu * @param string $entity_type * @param array $ids * - * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException - * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException - * @throws \Drupal\Core\Entity\EntityStorageException + * @throws InvalidPluginDefinitionException + * @throws PluginNotFoundException + * @throws EntityStorageException */ public function doSave(string $entity_type, array $ids): void { @@ -157,15 +160,15 @@ public function doSave(string $entity_type, array $ids): void * @param string $entity_type * @param string|null $ids * @param array $options - * @return \Drupal\Core\Entity\Query\QueryInterface - * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException - * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + * @return QueryInterface + * @throws InvalidPluginDefinitionException + * @throws PluginNotFoundException */ - protected function getQuery(string $entity_type, ?string $ids, array $options): \Drupal\Core\Entity\Query\QueryInterface + protected function getQuery(string $entity_type, ?string $ids, array $options): QueryInterface { $storage = $this->entityTypeManager->getStorage($entity_type); - $query = $storage->getQuery(); - if ($ids = StringUtils::csvToArray($ids)) { + $query = $storage->getQuery()->accessCheck(false); + if ($ids = StringUtils::csvToArray((string) $ids)) { $idKey = $this->entityTypeManager->getDefinition($entity_type)->getKey('id'); $query = $query->condition($idKey, $ids, 'IN'); } elseif ($options['bundle'] || $options['exclude']) { diff --git a/src/Drupal/Commands/core/ImageCommands.php b/src/Drupal/Commands/core/ImageCommands.php index df71f6f322..7593e03717 100644 --- a/src/Drupal/Commands/core/ImageCommands.php +++ b/src/Drupal/Commands/core/ImageCommands.php @@ -10,7 +10,6 @@ class ImageCommands extends DrushCommands { - /** * Flush all derived images for a given style. * @@ -27,7 +26,7 @@ class ImageCommands extends DrushCommands * @validate-module-enabled image * @aliases if,image-flush */ - public function flush($style_names, $options = ['all' => false]) + public function flush($style_names, $options = ['all' => false]): void { foreach (ImageStyle::loadMultiple(StringUtils::csvToArray($style_names)) as $style_name => $style) { $style->flush(); @@ -38,7 +37,7 @@ public function flush($style_names, $options = ['all' => false]) /** * @hook interact image-flush */ - public function interactFlush($input, $output) + public function interactFlush($input, $output): void { $styles = array_keys(ImageStyle::loadMultiple()); $style_names = $input->getArgument('style_names'); @@ -58,7 +57,7 @@ public function interactFlush($input, $output) /** * @hook init image-flush */ - public function initFlush(InputInterface $input, AnnotationData $annotationData) + public function initFlush(InputInterface $input, AnnotationData $annotationData): void { // Needed for non-interactive calls. if ($input->getOption('all')) { diff --git a/src/Drupal/Commands/core/JsonapiCommands.php b/src/Drupal/Commands/core/JsonapiCommands.php index 4e157292c7..02ea0efbf4 100644 --- a/src/Drupal/Commands/core/JsonapiCommands.php +++ b/src/Drupal/Commands/core/JsonapiCommands.php @@ -1,4 +1,5 @@ 'json']) + public function get($url, $options = ['format' => 'json']): UnstructuredData { $kernel = Drush::bootstrap()->getKernel(); $sub_request = Request::create($url, 'GET'); diff --git a/src/Drupal/Commands/core/LanguageCommands.php b/src/Drupal/Commands/core/LanguageCommands.php index abd84bbb64..cc7245fc60 100644 --- a/src/Drupal/Commands/core/LanguageCommands.php +++ b/src/Drupal/Commands/core/LanguageCommands.php @@ -11,29 +11,22 @@ class LanguageCommands extends DrushCommands { - /** - * @var \Drupal\Core\Language\LanguageManagerInterface + * @var LanguageManagerInterface */ protected $languageManager; /** - * @var \Drupal\Core\Extension\ModuleHandlerInterface + * @var ModuleHandlerInterface */ protected $moduleHandler; - /** - * @return \Drupal\Core\Language\LanguageManagerInterface - */ - public function getLanguageManager() + public function getLanguageManager(): LanguageManagerInterface { return $this->languageManager; } - /** - * @return \Drupal\Core\Extension\ModuleHandlerInterface - */ - public function getModuleHandler() + public function getModuleHandler(): ModuleHandlerInterface { return $this->moduleHandler; } @@ -59,7 +52,7 @@ public function __construct(LanguageManagerInterface $languageManager, ModuleHan * @hidden * @throws \Exception */ - public function add($langcode, $options = ['skip-translations' => false]) + public function add($langcode, $options = ['skip-translations' => false]): void { if ($langcodes = StringUtils::csvToArray($langcode)) { $langcodes = array_unique($langcodes); @@ -102,9 +95,8 @@ public function add($langcode, $options = ['skip-translations' => false]) * locked: Locked * @default-fields language,direction,default * @filter-default-field language - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields */ - public function info() + public function info(): RowsOfFields { $rows = []; $languages = $this->getLanguageManager()->getLanguages(); @@ -126,11 +118,10 @@ public function info() * Filters valid language codes. * * @param $langcodes - * @return array * @throws \Exception * Exception when a language code is not in the standard language list. */ - private function filterValidLangcode($langcodes) + private function filterValidLangcode($langcodes): array { $standardLanguages = $this->getLanguageManager()->getStandardLanguageList(); foreach ($langcodes as $key => $langcode) { @@ -148,9 +139,8 @@ private function filterValidLangcode($langcodes) * Filters new language codes. * * @param $langcodes - * @return array */ - private function filterNewLangcode($langcodes) + private function filterNewLangcode($langcodes): array { $enabledLanguages = $this->getLanguageManager()->getLanguages(); foreach ($langcodes as $key => $langcode) { @@ -170,7 +160,7 @@ private function filterNewLangcode($langcodes) * * @param $langcodes */ - private function setBatchLanguageImport($langcodes) + private function setBatchLanguageImport($langcodes): void { $moduleHandler = $this->getModuleHandler(); $moduleHandler->loadInclude('locale', 'inc', 'locale.translation'); diff --git a/src/Drupal/Commands/core/LinkHooks.php b/src/Drupal/Commands/core/LinkHooks.php index 646e28e2e8..0606e665cc 100644 --- a/src/Drupal/Commands/core/LinkHooks.php +++ b/src/Drupal/Commands/core/LinkHooks.php @@ -46,7 +46,8 @@ public function hookOption(Command $command, AnnotationData $annotationData): vo /** @hook on-event field-create-set-options */ public function hookSetOptions(InputInterface $input): void { - if (!$this->isInstalled() + if ( + !$this->isInstalled() || $input->getOption('field-type') !== 'link' ) { return; @@ -66,7 +67,8 @@ public function hookSetOptions(InputInterface $input): void /** @hook on-event field-create-field-config */ public function hookFieldConfig(array $values, InputInterface $input): array { - if (!$this->isInstalled() + if ( + !$this->isInstalled() || $values['field_type'] !== 'link' ) { return $values; diff --git a/src/Drupal/Commands/core/LocaleCommands.php b/src/Drupal/Commands/core/LocaleCommands.php index da7eb04b44..6649a30799 100644 --- a/src/Drupal/Commands/core/LocaleCommands.php +++ b/src/Drupal/Commands/core/LocaleCommands.php @@ -17,7 +17,6 @@ class LocaleCommands extends DrushCommands { - protected $languageManager; protected $configFactory; @@ -26,26 +25,17 @@ class LocaleCommands extends DrushCommands protected $state; - /** - * @return \Drupal\Core\Language\LanguageManagerInterface - */ - protected function getLanguageManager() + protected function getLanguageManager(): LanguageManagerInterface { return $this->languageManager; } - /** - * @return \Drupal\Core\Config\ConfigFactoryInterface - */ - protected function getConfigFactory() + protected function getConfigFactory(): ConfigFactoryInterface { return $this->configFactory; } - /** - * @return \Drupal\Core\Extension\ModuleHandlerInterface - */ - public function getModuleHandler() + public function getModuleHandler(): ModuleHandlerInterface { return $this->moduleHandler; } @@ -53,7 +43,7 @@ public function getModuleHandler() /** * @return mixed */ - public function getState() + public function getState(): StateInterface { return $this->state; } @@ -73,7 +63,7 @@ public function __construct(LanguageManagerInterface $languageManager, ConfigFac * @aliases locale-check * @validate-module-enabled locale */ - public function check() + public function check(): void { $this->getModuleHandler()->loadInclude('locale', 'inc', 'locale.compare'); @@ -105,7 +95,7 @@ public function check() * @option langcodes A comma-separated list of language codes to update. If omitted, all translations will be updated. * @validate-module-enabled locale */ - public function update($options = ['langcodes' => self::REQ]) + public function update($options = ['langcodes' => self::REQ]): void { $module_handler = $this->getModuleHandler(); $module_handler->loadInclude('locale', 'fetch.inc'); @@ -175,7 +165,7 @@ public function update($options = ['langcodes' => self::REQ]) * @aliases locale-export * @validate-module-enabled locale */ - public function export($langcode = null, $options = ['template' => false, 'types' => self::REQ]) + public function export($langcode = null, $options = ['template' => false, 'types' => self::REQ]): void { $language = $this->getTranslatableLanguage($langcode); $poreader_options = []; @@ -197,7 +187,7 @@ public function export($langcode = null, $options = ['template' => false, 'types * * @hook validate locale:export */ - public function exportValidate(CommandData $commandData) + public function exportValidate(CommandData $commandData): void { $langcode = $commandData->input()->getArgument('langcode'); $template = $commandData->input()->getOption('template'); @@ -220,6 +210,7 @@ public function exportValidate(CommandData $commandData) * @param $file Path and file name of the gettext file. * @option type The type of translations to be imported. Recognized values: customized, not-customized * @option override Whether and how imported strings will override existing translations. Defaults to the Import behavior configured in the admin interface. Recognized values: none, customized, not-customized, all, + * @option autocreate-language Create the language in addition to import. * @usage drush locale-import nl drupal-8.4.2.nl.po * Import the Dutch drupal core translation. * @usage drush locale-import --type=customized nl drupal-8.4.2.nl.po @@ -233,13 +224,13 @@ public function exportValidate(CommandData $commandData) * @aliases locale-import * @throws \Exception */ - public function import($langcode, $file, $options = ['type' => 'not-customized', 'override' => self::REQ]) + public function import($langcode, $file, $options = ['type' => 'not-customized', 'override' => self::REQ, 'autocreate-language' => false]): void { if (!drush_file_not_empty($file)) { throw new \Exception(dt('File @file not found or empty.', ['@file' => $file])); } - $language = $this->getTranslatableLanguage($langcode, true); + $language = $this->getTranslatableLanguage($langcode, $options['autocreate-language']); $this->getModuleHandler()->loadInclude('locale', 'translation.inc'); $this->getModuleHandler()->loadInclude('locale', 'bulk.inc'); @@ -272,9 +263,8 @@ public function import($langcode, $file, $options = ['type' => 'not-customized', * Converts input of translation type. * * @param $type - * @return integer */ - private function convertCustomizedType($type) + private function convertCustomizedType($type): int { return $type == 'customized' ? LOCALE_CUSTOMIZED : LOCALE_NOT_CUSTOMIZED; } @@ -283,9 +273,8 @@ private function convertCustomizedType($type) * Converts input of override option. * * @param $override - * @return array */ - private function convertOverrideOption($override) + private function convertOverrideOption($override): array { $result = []; @@ -330,7 +319,7 @@ private function convertOverrideOption($override) * @return LanguageInterface|null * @throws \Exception */ - private function getTranslatableLanguage($langcode, $addLanguage = false) + private function getTranslatableLanguage(string $langcode, bool $addLanguage = false) { if (!$langcode) { return null; @@ -366,9 +355,8 @@ private function getTranslatableLanguage($langcode, $addLanguage = false) * Check if language is translatable. * * @param LanguageInterface $language - * @return bool */ - private function isTranslatable(LanguageInterface $language) + private function isTranslatable(LanguageInterface $language): bool { if ($language->isLocked()) { return false; @@ -387,12 +375,11 @@ private function isTranslatable(LanguageInterface $language) * Get PODatabaseReader options for given types. * * @param array $types - * @return array * Options list with value 'true'. * @throws \Exception * Triggered with incorrect types. */ - private function convertTypesToPoDbReaderOptions(array $types = []) + private function convertTypesToPoDbReaderOptions(array $types = []): array { $valid_convertions = [ 'not_customized' => 'not-customized', @@ -425,7 +412,7 @@ private function convertTypesToPoDbReaderOptions(array $types = []) * @param array $options The export options for PoDatabaseReader. * @return bool True if successful. */ - private function writePoFile($file_uri, LanguageInterface $language = null, array $options = []) + private function writePoFile(string $file_uri, LanguageInterface $language = null, array $options = []): bool { $reader = new PoDatabaseReader(); diff --git a/src/Drupal/Commands/core/MessengerCommands.php b/src/Drupal/Commands/core/MessengerCommands.php index fda13a9246..f91f243d3a 100644 --- a/src/Drupal/Commands/core/MessengerCommands.php +++ b/src/Drupal/Commands/core/MessengerCommands.php @@ -21,7 +21,7 @@ public function __construct(MessengerInterface $messenger) /** * @hook pre-command * */ - public function pre() + public function pre(): void { self::log(); } @@ -29,12 +29,12 @@ public function pre() /** * @hook post-command * */ - public function post() + public function post(): void { self::log(); } - public function log() + public function log(): void { if (!\Drupal::hasService('messenger')) { return; diff --git a/src/Drupal/Commands/core/MigrateRunnerCommands.php b/src/Drupal/Commands/core/MigrateRunnerCommands.php index e33cedd9c0..8015161ad8 100644 --- a/src/Drupal/Commands/core/MigrateRunnerCommands.php +++ b/src/Drupal/Commands/core/MigrateRunnerCommands.php @@ -5,8 +5,10 @@ use Consolidation\AnnotatedCommand\CommandData; use Consolidation\AnnotatedCommand\CommandError; use Consolidation\OutputFormatters\StructuredData\RowsOfFields; +use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Core\Datetime\DateFormatter; use Drupal\Core\KeyValueStore\KeyValueFactoryInterface; +use Drupal\Core\KeyValueStore\KeyValueStoreInterface; use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\MigrateMessageInterface; use Drupal\migrate\Plugin\MigrateIdMapInterface; @@ -27,45 +29,40 @@ class MigrateRunnerCommands extends DrushCommands { /** * Migration plugin manager service. - * - * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface */ - protected $migrationPluginManager; + protected ?MigrationPluginManagerInterface $migrationPluginManager; /** * Date formatter service. - * - * @var \Drupal\Core\Datetime\DateFormatter */ - protected $dateFormatter; + protected DateFormatter $dateFormatter; /** * The key-value store service. - * - * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface */ - protected $keyValue; + protected KeyValueStoreInterface $keyValue; /** * Migrate message service. - * - * @var \Drupal\migrate\MigrateMessageInterface */ - protected $migrateMessage; + protected MigrateMessageInterface $migrateMessage; /** * Constructs a new class instance. * - * @param \Drupal\Core\Datetime\DateFormatter $dateFormatter + * @param DateFormatter $dateFormatter * Date formatter service. - * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $keyValueFactory + * @param KeyValueFactoryInterface $keyValueFactory * The key-value factory service. + * @param MigrationPluginManagerInterface|null $migrationPluginManager + * The migration plugin manager service. */ - public function __construct(DateFormatter $dateFormatter, KeyValueFactoryInterface $keyValueFactory) + public function __construct(DateFormatter $dateFormatter, KeyValueFactoryInterface $keyValueFactory, ?MigrationPluginManagerInterface $migrationPluginManager = null) { parent::__construct(); $this->dateFormatter = $dateFormatter; $this->keyValue = $keyValueFactory->get('migrate_last_imported'); + $this->migrationPluginManager = $migrationPluginManager; } /** @@ -110,8 +107,8 @@ public function __construct(DateFormatter $dateFormatter, KeyValueFactoryInterfa * unprocessed: Unprocessed * last_imported: Last Imported * @default-fields id,status,total,imported,unprocessed,last_imported - * - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields + * @filter-default-field status + * @return RowsOfFields * Migrations status formatted as table. * @version 10.4 * @@ -202,7 +199,7 @@ public function status(?string $migrationIds = null, array $options = [ /** * Returns the migration source rows count. * - * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * @param MigrationInterface $migration * The migration plugin instance. * @return int|null * The migration source rows count or null if the source is uncountable or @@ -228,13 +225,13 @@ protected function getMigrationSourceRowsCount(MigrationInterface $migration): ? } /** - * Returns the number or items that needs update. + * Returns the number of items that needs update. * - * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * @param MigrationInterface $migration * The migration plugin instance. * * @return int|null - * The number or items that needs update. + * The number of items that needs update. */ protected function getMigrationNeedingUpdateCount(MigrationInterface $migration): int { @@ -245,7 +242,7 @@ protected function getMigrationNeedingUpdateCount(MigrationInterface $migration) /** * Returns the number of unprocessed items. * - * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * @param MigrationInterface $migration * The migration plugin instance. * * @return int|null @@ -263,7 +260,7 @@ protected function getMigrationUnprocessedCount(MigrationInterface $migration): /** * Returns the number of imported items. * - * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * @param MigrationInterface $migration * The migration plugin instance. * * @return int|null @@ -286,7 +283,7 @@ protected function getMigrationImportedCount(MigrationInterface $migration): ?in /** * Returns the last imported date/time if any. * - * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * @param MigrationInterface $migration * The migration plugin instance. * * @return string @@ -295,7 +292,7 @@ protected function getMigrationImportedCount(MigrationInterface $migration): ?in protected function getMigrationLastImportedTime(MigrationInterface $migration): string { if ($lastImported = $this->keyValue->get($migration->id(), '')) { - $lastImported = $this->dateFormatter->format($lastImported / 1000, 'custom', 'Y-m-d H:i:s'); + $lastImported = $this->dateFormatter->format(round($lastImported / 1000), 'custom', 'Y-m-d H:i:s'); } return $lastImported; } @@ -415,7 +412,7 @@ public function import(?string $migrationIds = null, array $options = ['all' => * If the --execute-dependencies option was given, the migration's * dependencies will also be executed first. * - * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * @param MigrationInterface $migration * The migration to execute. * @param string $migrationId * The migration ID (not used, just an artifact of array_walk()). @@ -434,7 +431,7 @@ protected function executeMigration(MigrationInterface $migration, string $migra // Remove already executed migrations. $dependencies = array_diff($dependencies, $executedMigrations); if ($dependencies) { - $requiredMigrations = $this->getMigrationPluginManager()->createInstances($dependencies); + $requiredMigrations = $this->migrationPluginManager->createInstances($dependencies); array_walk($requiredMigrations, [static::class, __FUNCTION__], $userData); } } @@ -544,11 +541,13 @@ public function rollback(?string $migrationIds = null, array $options = ['all' = * @validate-module-enabled migrate * @validate-migration-id * @version 10.4 + * + * @throws PluginException */ public function stop(string $migrationId): void { - /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ - $migration = $this->getMigrationPluginManager()->createInstance($migrationId); + /** @var MigrationInterface $migration */ + $migration = $this->migrationPluginManager->createInstance($migrationId); switch ($migration->getStatus()) { case MigrationInterface::STATUS_IDLE: $this->logger()->warning(dt('Migration @id is idle', ['@id' => $migrationId])); @@ -581,11 +580,13 @@ public function stop(string $migrationId): void * @validate-module-enabled migrate * @validate-migration-id * @version 10.4 + * + * @throws PluginException */ public function resetStatus(string $migrationId): void { - /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ - $migration = $this->getMigrationPluginManager()->createInstance($migrationId); + /** @var MigrationInterface $migration */ + $migration = $this->migrationPluginManager->createInstance($migrationId); $status = $migration->getStatus(); if ($status == MigrationInterface::STATUS_IDLE) { $this->logger()->warning(dt('Migration @id is already Idle', ['@id' => $migrationId])); @@ -603,16 +604,20 @@ public function resetStatus(string $migrationId): void * @param string $migrationId * The ID of the migration. * - * @option idlist Comma-separated list of IDs to import. As an ID may have more than one column, concatenate the columns with the colon ':' separator. + * @option idlist Comma-separated list of IDs to import. As an ID may have + * more than one column, concatenate the columns with the colon ':' + * separator. * * @usage migrate:messages article * Show all messages for the article migration * @usage migrate:messages article --idlist=5 * Show messages related to article record with source ID 5. * @usage migrate:messages node_revision --idlist=1:2,2:3,3:5 - * Show messages related to node revision records with source IDs [1,2], [2,3], and [3,5]. + * Show messages related to node revision records with source IDs [1,2], + * [2,3], and [3,5]. * @usage migrate:messages custom_node_revision --idlist=1:"r:1",2:"r:3" - * Show messages related to node revision records with source IDs [1,"r:1"], and [2,"r:3"]. + * Show messages related to node revision records with source IDs + * [1,"r:1"], and [2,"r:3"]. * * @aliases mmsg,migrate-messages * @@ -630,13 +635,15 @@ public function resetStatus(string $migrationId): void * hash: Source IDs hash * @default-fields level,source_ids,destination_ids,message,hash * - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields + * @return RowsOfFields * Migration messages status formatted as table. + * + * @throws PluginException */ public function messages(string $migrationId, array $options = ['idlist' => self::REQ]): RowsOfFields { - /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ - $migration = $this->getMigrationPluginManager()->createInstance($migrationId); + /** @var MigrationInterface $migration */ + $migration = $this->migrationPluginManager->createInstance($migrationId); $idMap = $migration->getIdMap(); $sourceIdKeys = $this->getSourceIdKeys($idMap); $table = []; @@ -646,20 +653,18 @@ public function messages(string $migrationId, array $options = ['idlist' => self return new RowsOfFields($table); } if (!empty($options['idlist'])) { - // There is not way to retreive a filtered set of messages from an - // ID map on Drupal core, right now. - // Even if using \Drush\Drupal\Migrate\MigrateIdMapFilter does the - // right thing filtering the data on the ID map, sadly its - // getMessages() method does not take it into account the iterator, - // and retrieves data directly, e.g. at SQL ID map plugin. - // On the other side Drupal core's - // \Drupal\migrate\Plugin\MigrateIdMapInterface only allows to - // filter by one source IDs set, and not by multiple, on - // getMessages(). - // For now, go over known IDs passed directly, one at a time a - // work-around, at the cost of more queries in the usual SQL ID map, - // which is likely OK for its use, to show only few source IDs - // messages. + // There is no way to retrieve a filtered set of messages from an ID + // map on Drupal core, right now. Even if using + // \Drush\Drupal\Migrate\MigrateIdMapFilter does the right thing + // filtering the data on the ID map, sadly its getMessages() method + // does not take it into account the iterator, and retrieves data + // directly, e.g. at SQL ID map plugin. On the other side Drupal + // core's \Drupal\migrate\Plugin\MigrateIdMapInterface only allows + // to filter by one source IDs set, and not by multiple, on + // getMessages(). For now, go over known IDs passed directly, one at + // a time a workaround, at the cost of more queries in the usual SQL + // ID map, which is likely OK for its use, to show only few source + // IDs messages. foreach (MigrateUtils::parseIdList($options['idlist']) as $sourceIdValues) { foreach ($idMap->getMessages($sourceIdValues) as $row) { $table[] = $this->preprocessMessageRow($row, $sourceIdKeys); @@ -667,7 +672,6 @@ public function messages(string $migrationId, array $options = ['idlist' => self } return new RowsOfFields($table); } - $table = []; foreach ($idMap->getMessages() as $row) { $table[] = $this->preprocessMessageRow($row, $sourceIdKeys); } @@ -687,7 +691,7 @@ public function messages(string $migrationId, array $options = ['idlist' => self * * @see \Drupal\migrate\Plugin\MigrateIdMapInterface::getMessages() */ - protected function preprocessMessageRow(\StdClass $row, array $sourceIdKeys) + protected function preprocessMessageRow(\StdClass $row, array $sourceIdKeys): array { unset($row->msgid); $row = (array) $row; @@ -697,10 +701,10 @@ protected function preprocessMessageRow(\StdClass $row, array $sourceIdKeys) } $sourceIds = $destinationIds = []; foreach ($row as $key => $value) { - if (substr($key, 0, 4) === 'src_') { + if (str_starts_with($key, 'src_')) { $sourceIds[$key] = $value; } - if (substr($key, 0, 5) === 'dest_') { + if (str_starts_with($key, 'dest_')) { $destinationIds[$key] = $value; } } @@ -733,13 +737,15 @@ protected function preprocessMessageRow(\StdClass $row, array $sourceIdKeys) * @default-fields machine_name,description * @version 10.4 * - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields + * @return RowsOfFields * Source fields of the given migration. + * + * @throws PluginException */ public function fieldsSource(string $migrationId): RowsOfFields { - /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ - $migration = $this->getMigrationPluginManager()->createInstance($migrationId); + /** @var MigrationInterface $migration */ + $migration = $this->migrationPluginManager->createInstance($migrationId); $source = $migration->getSourcePlugin(); $table = []; foreach ($source->fields() as $machineName => $description) { @@ -759,16 +765,16 @@ public function fieldsSource(string $migrationId): RowsOfFields * @param string|null $tags * A comma separated list of tags. * - * @return \Drupal\migrate\Plugin\MigrationInterface[][] + * @return MigrationInterface[][] * An array keyed by migration tag, each value containing an array of * migrations or an empty array if no migrations match the input criteria. * - * @throws \Drupal\Component\Plugin\Exception\PluginException + * @throws PluginException */ protected function getMigrationList(?string $migrationIds, ?string $tags): array { - $migrationIds = StringUtils::csvToArray($migrationIds); - $migrations = $this->getMigrationPluginManager()->createInstances($migrationIds); + $migrationIds = StringUtils::csvToArray((string) $migrationIds); + $migrations = $this->migrationPluginManager->createInstances($migrationIds); // Check for invalid migration IDs. if ($invalidMigrations = array_diff_key(array_flip($migrationIds), $migrations)) { @@ -782,7 +788,7 @@ protected function getMigrationList(?string $migrationIds, ?string $tags): array $sourcePlugin->checkRequirements(); } } catch (RequirementsException $exception) { - $this->logger()->debug("Migration '{$migrationId}' is skipped as its source plugin has missed requirements: " . $exception->getRequirementsString()); + $this->logger()->debug("Migration '$migrationId' is skipped as its source plugin has missed requirements: {$exception->getRequirementsString()}"); unset($migrations[$migrationId]); } } @@ -796,7 +802,7 @@ protected function getMigrationList(?string $migrationIds, ?string $tags): array $list = []; foreach ($migrations as $migrationId => $migration) { - $migrationTags = (array)$migration->getMigrationTags(); + $migrationTags = $migration->getMigrationTags(); $commonTags = array_intersect($tags, $migrationTags); if (!$commonTags) { // Skip if migration is not tagged with any of the passed tags. @@ -814,7 +820,7 @@ protected function getMigrationList(?string $migrationIds, ?string $tags): array /** * Returns the migrate message logger. * - * @return \Drupal\migrate\MigrateMessageInterface + * @return MigrateMessageInterface * The migrate message logger. */ protected function getMigrateMessage(): MigrateMessageInterface @@ -825,33 +831,10 @@ protected function getMigrateMessage(): MigrateMessageInterface return $this->migrateMessage; } - /** - * Returns the migration plugin manager service. - * - * @return \Drupal\migrate\Plugin\MigrationPluginManagerInterface - * The migration plugin manager service. - * - * @todo This service cannot be injected as the 'migrate' module might not - * be enabled and will throw the following exception: - * > Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException - * > The service "migrate_runner.commands" has a dependency on a - * > non-existent service "plugin.manager.migration". - * Unfortunately, we cannot avoid the class instantiation, via an - * annotation (as @validate-module-enabled for methods), if a specific - * module is not installed. Open a followup to tackle this issue. - */ - protected function getMigrationPluginManager(): MigrationPluginManagerInterface - { - if (!isset($this->migrationPluginManager)) { - $this->migrationPluginManager = \Drupal::service('plugin.manager.migration'); - } - return $this->migrationPluginManager; - } - /** * Get the source ID keys. * - * @param \Drupal\migrate\Plugin\MigrateIdMapInterface $idMap + * @param MigrateIdMapInterface $idMap * The migration ID map. * * @return string[] @@ -867,7 +850,7 @@ protected function getSourceIdKeys(MigrateIdMapInterface $idMap): array $idMap->rewind(); $columns = $idMap->currentSource(); $sourceIdKeys = array_map(static function ($id) { - return "src_{$id}"; + return "src_$id"; }, array_keys($columns)); return array_combine($sourceIdKeys, $sourceIdKeys); } @@ -880,16 +863,17 @@ protected function getSourceIdKeys(MigrateIdMapInterface $idMap): array * * @hook validate @validate-migration-id * - * @param \Consolidation\AnnotatedCommand\CommandData $commandData + * @param CommandData $commandData * - * @return \Consolidation\AnnotatedCommand\CommandError|null + * @return CommandError|null */ - public function validateMigrationId(CommandData $commandData) + public function validateMigrationId(CommandData $commandData): ?CommandError { $argName = $commandData->annotationData()->get('validate-migration-id') ?: 'migrationId'; $migrationId = $commandData->input()->getArgument($argName); - if (!$this->getMigrationPluginManager()->hasDefinition($migrationId)) { + if (!$this->migrationPluginManager->hasDefinition($migrationId)) { return new CommandError(dt('Migration "@id" does not exist', ['@id' => $migrationId])); } + return null; } } diff --git a/src/Drupal/Commands/core/QueueCommands.php b/src/Drupal/Commands/core/QueueCommands.php index db2fae835e..5761ccc937 100644 --- a/src/Drupal/Commands/core/QueueCommands.php +++ b/src/Drupal/Commands/core/QueueCommands.php @@ -1,9 +1,14 @@ queueService = $queueService; } - /** - * @return \Drupal\Core\Queue\QueueWorkerManager - */ - public function getWorkerManager() + public function getWorkerManager(): QueueWorkerManager { return $this->workerManager; } - /** - * @return \Drupal\Core\Queue\QueueFactory - */ - public function getQueueService() + public function getQueueService(): QueueFactory { return $this->queueService; } @@ -60,7 +58,7 @@ public function getQueueService() * @option items-limit The maximum number of items allowed to run the queue. * @option lease-time The maximum number of seconds that an item remains claimed. */ - public function run($name, $options = ['time-limit' => self::REQ, 'items-limit' => self::REQ, 'lease-time' => self::REQ]) + public function run(string $name, $options = ['time-limit' => self::REQ, 'items-limit' => self::REQ, 'lease-time' => self::REQ]): void { $time_limit = (int) $options['time-limit']; $items_limit = (int) $options['items-limit']; @@ -86,7 +84,18 @@ public function run($name, $options = ['time-limit' => self::REQ, 'items-limit' // If the worker indicates there is a problem with the whole queue, // release the item. $queue->releaseItem($item); - throw new \Exception($e->getMessage()); + throw new \Exception($e->getMessage(), $e->getCode(), $e); + } catch (DelayedRequeueException $e) { + // The worker requested the task not be immediately re-queued. + // - If the queue doesn't support ::delayItem(), we should leave the + // item's current expiry time alone. + // - If the queue does support ::delayItem(), we should allow the + // queue to update the item's expiry using the requested delay. + if ($queue instanceof DelayableQueueInterface) { + // This queue can handle a custom delay; use the duration provided + // by the exception. + $queue->delayItem($item, $e->getDelay()); + } } catch (\Exception $e) { // In case of any other kind of exception, log it and leave the // item in the queue to be processed again later. @@ -109,9 +118,8 @@ public function run($name, $options = ['time-limit' => self::REQ, 'items-limit' * class: Class * * @filter-default-field queue - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields */ - public function qList($options = ['format' => 'table']) + public function qList($options = ['format' => 'table']): RowsOfFields { $result = []; foreach (array_keys($this->getQueues()) as $name) { @@ -133,7 +141,7 @@ public function qList($options = ['format' => 'table']) * @param $name The name of the queue to run, as defined in either hook_queue_info or hook_cron_queue_info. * @validate-queue name */ - public function delete($name) + public function delete($name): void { $queue = $this->getQueue($name); $queue->deleteQueue(); @@ -146,15 +154,14 @@ public function delete($name) * Annotation value should be the name of the argument/option containing the name. * * @hook validate @validate-queue - * @param \Consolidation\AnnotatedCommand\CommandData $commandData - * @return \Consolidation\AnnotatedCommand\CommandError|null + * @param CommandData $commandData + * @return CommandError|null */ public function validateQueueName(CommandData $commandData) { $arg_name = $commandData->annotationData()->get('validate-queue', null); $name = $commandData->input()->getArgument($arg_name); - $all = array_keys(self::getQueues()); - if (!in_array($name, $all)) { + if (!array_key_exists($name, self::getQueues())) { $msg = dt('Queue not found: !name', ['!name' => $name]); return new CommandError($msg); } @@ -163,7 +170,7 @@ public function validateQueueName(CommandData $commandData) /** * {@inheritdoc} */ - public function getQueues() + public function getQueues(): array { if (!isset(static::$queues)) { static::$queues = []; @@ -176,10 +183,8 @@ public function getQueues() /** * {@inheritdoc} - * - * @return \Drupal\Core\Queue\QueueInterface */ - public function getQueue($name) + public function getQueue($name): QueueInterface { return $this->getQueueService()->get($name); } diff --git a/src/Drupal/Commands/core/RoleCommands.php b/src/Drupal/Commands/core/RoleCommands.php index 15835ea2b9..70752e0d69 100644 --- a/src/Drupal/Commands/core/RoleCommands.php +++ b/src/Drupal/Commands/core/RoleCommands.php @@ -1,4 +1,5 @@ delete(); @@ -70,7 +71,7 @@ public function delete($machine_name) * Allow anon users to post comments and access content. * @aliases rap,role-add-perm */ - public function roleAddPerm($machine_name, $permissions) + public function roleAddPerm($machine_name, $permissions): void { $perms = StringUtils::csvToArray($permissions); user_role_grant_permissions($machine_name, $perms); @@ -90,7 +91,7 @@ public function roleAddPerm($machine_name, $permissions) * Remove 2 permissions from anon users. * @aliases rmp,role-remove-perm */ - public function roleRemovePerm($machine_name, $permissions) + public function roleRemovePerm($machine_name, $permissions): void { $perms = StringUtils::csvToArray($permissions); user_role_revoke_permissions($machine_name, $perms); @@ -115,9 +116,8 @@ public function roleRemovePerm($machine_name, $permissions) * perms: Permissions * * @filter-default-field perms - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields */ - public function roleList($options = ['format' => 'yaml']) + public function roleList($options = ['format' => 'yaml']): RowsOfFields { $rows = []; $roles = Role::loadMultiple(); diff --git a/src/Drupal/Commands/core/StateCommands.php b/src/Drupal/Commands/core/StateCommands.php index 5ab5237356..5ca494beed 100644 --- a/src/Drupal/Commands/core/StateCommands.php +++ b/src/Drupal/Commands/core/StateCommands.php @@ -20,10 +20,7 @@ public function __construct(StateInterface $state) $this->state = $state; } - /** - * @return \Drupal\Core\State\StateInterface - */ - public function getState() + public function getState(): StateInterface { return $this->state; } @@ -39,10 +36,8 @@ public function getState() * @usage drush state:get drupal_css_cache_files --format=yaml * Displays an array of css files in yaml format. * @aliases sget,state-get - * - * @return \Consolidation\OutputFormatters\StructuredData\PropertyList */ - public function get($key, $options = ['format' => 'string']) + public function get(string $key, $options = ['format' => 'string']): PropertyList { $value = $this->getState()->get($key); return new PropertyList([$key => $value]); @@ -54,10 +49,8 @@ public function get($key, $options = ['format' => 'string']) * @command state:set * * @param string $key The state key, for example: system.cron_last. - * @param mixed $value The value to assign to the state key. Use - to read from STDIN. + * @param mixed $value The value to assign to the state key. Use - to read from Stdin. * @option input-format Type for the value. Other recognized values: string, integer, float, boolean, json, yaml. - * @option value For internal use only. - * @hidden-options value * @usage drush sset system.maintenance_mode 1 --input-format=integer * Put site into Maintenance mode. * @usage drush state:set system.cron_last 1406682882 --input-format=integer @@ -65,13 +58,9 @@ public function get($key, $options = ['format' => 'string']) * @usage php -r "print json_encode(array(\'drupal\', \'simpletest\'));" | drush state-set --input-format=json foo.name - * Set a key to a complex value (e.g. array) * @aliases sset,state-set - * - * @return void */ - public function set($key, $value, $options = ['input-format' => 'auto', 'value' => self::REQ]) + public function set(string $key, $value, $options = ['input-format' => 'auto']): void { - // A convenient way to pass a multiline value within a backend request. - $value = $options['value'] ?: $value; if (!isset($value)) { throw new \Exception(dt('No state value specified.')); @@ -100,10 +89,8 @@ public function set($key, $value, $options = ['input-format' => 'auto', 'value' * @usage drush state:del system.cron_last * Delete state entry for system.cron_last. * @aliases sdel,state-delete - * - * @return void */ - public function delete($key) + public function delete(string $key): void { $this->getState()->delete($key); } @@ -122,7 +109,7 @@ public static function format($value, $format) { if ($format == 'auto') { if (is_numeric($value)) { - $value = $value + 0; // http://php.net/manual/en/function.is-numeric.php#107326 + $value += 0; // http://php.net/manual/en/function.is-numeric.php#107326 $format = gettype($value); } elseif (($value == 'TRUE') || ($value == 'FALSE')) { $format = 'bool'; @@ -132,7 +119,7 @@ public static function format($value, $format) // Now, we parse the object. switch ($format) { case 'integer': - $value = (integer)$value; + $value = (int)$value; break; // from: http://php.net/gettype // for historical reasons "double" is returned in case of a float, and not simply "float" @@ -154,7 +141,7 @@ public static function format($value, $format) $value = json_decode($value, true); break; case 'yaml': - $value = Yaml::parse($value, false, true); + $value = Yaml::parse($value, false); break; } return $value; diff --git a/src/Drupal/Commands/core/TwigCommands.php b/src/Drupal/Commands/core/TwigCommands.php index 3f9fe3ea3a..c3176788c1 100644 --- a/src/Drupal/Commands/core/TwigCommands.php +++ b/src/Drupal/Commands/core/TwigCommands.php @@ -15,35 +15,29 @@ class TwigCommands extends DrushCommands { /** - * @var \Drupal\Core\Template\TwigEnvironment - */ + * @var TwigEnvironment + */ protected $twig; /** - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ + * @var ModuleHandlerInterface + */ protected $moduleHandler; - /** - * @return \Drupal\Core\Template\TwigEnvironment - */ - public function getTwig() + public function getTwig(): TwigEnvironment { return $this->twig; } - /** - * @return \Drupal\Core\Extension\ModuleHandlerInterface - */ - public function getModuleHandler() + public function getModuleHandler(): ModuleHandlerInterface { return $this->moduleHandler; } /** - * @param \Drupal\Core\Template\TwigEnvironment $twig - * @param ModuleHandlerInterface $moduleHandler - */ + * @param TwigEnvironment $twig + * @param ModuleHandlerInterface $moduleHandler + */ public function __construct(TwigEnvironment $twig, ModuleHandlerInterface $moduleHandler) { $this->twig = $twig; @@ -65,11 +59,10 @@ public function __construct(TwigEnvironment $twig, ModuleHandlerInterface $modul * compiled: Compiled * @default-fields template,compiled * @filter-output - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields * * @command twig:unused */ - public function unused($searchpaths) + public function unused($searchpaths): RowsOfFields { $unused = []; $phpstorage = PhpStorageFactory::get('twig'); @@ -104,7 +97,7 @@ public function unused($searchpaths) * @command twig:compile * @aliases twigc,twig-compile */ - public function twigCompile() + public function twigCompile(): void { require_once DRUSH_DRUPAL_CORE . "/themes/engines/twig/twig.engine"; // Scan all enabled modules and themes. diff --git a/src/Drupal/Commands/core/UserCommands.php b/src/Drupal/Commands/core/UserCommands.php index 9a76c02721..23caeb9deb 100644 --- a/src/Drupal/Commands/core/UserCommands.php +++ b/src/Drupal/Commands/core/UserCommands.php @@ -1,6 +1,8 @@ 'table', 'uid' => self::REQ, 'mail' => self::REQ]) + public function information(string $names = '', $options = ['format' => 'table', 'uid' => self::REQ, 'mail' => self::REQ]): RowsOfFields { $accounts = []; if ($mails = StringUtils::csvToArray($options['mail'])) { @@ -119,47 +119,41 @@ public function renderRolesCell($key, $cellData, FormatterOptions $options) * @command user:block * * @param string $names A comma delimited list of user names. + * @option $uid A comma delimited list of user ids to lookup (an alternative to names). + * @option $mail A comma delimited list of emails to lookup (an alternative to names). * @aliases ublk,user-block * @usage drush user:block user3 * Block the users whose name is user3 */ - public function block($names) + public function block(string $names = '', $options = ['uid' => self::REQ, 'mail' => self::REQ]): void { - if ($names = StringUtils::csvToArray($names)) { - foreach ($names as $name) { - if ($account = user_load_by_name($name)) { - $account->block(); - $account->save(); - $this->logger->success(dt('Blocked user(s): !user', ['!user' => $name])); - } else { - $this->logger->warning(dt('Unable to load user: !user', ['!user' => $name])); - } - } + $accounts = $this->getAccounts($names, $options); + foreach ($accounts as $id => $account) { + $account->block(); + $account->save(); + $this->logger->success(dt('Blocked user(s): !user', ['!user' => $account->getAccountName()])); } } /** - * UnBlock the specified user(s). + * Unblock the specified user(s). * * @command user:unblock * * @param string $names A comma delimited list of user names. + * @option $uid A comma delimited list of user ids to lookup (an alternative to names). + * @option $mail A comma delimited list of emails to lookup (an alternative to names). * @aliases uublk,user-unblock * @usage drush user:unblock user3 * Unblock the users with name user3 */ - public function unblock($names) + public function unblock(string $names = '', $options = ['uid' => self::REQ, 'mail' => self::REQ]): void { - if ($names = StringUtils::csvToArray($names)) { - foreach ($names as $name) { - if ($account = user_load_by_name($name)) { - $account->activate(); - $account->save(); - $this->logger->success(dt('Unblocked user(s): !user', ['!user' => $name])); - } else { - $this->logger->warning(dt('Unable to load user: !user', ['!user' => $name])); - } - } + $accounts = $this->getAccounts($names, $options); + foreach ($accounts as $id => $account) { + $account->activate(); + $account->save(); + $this->logger->success(dt('Unblocked user(s): !user', ['!user' => $account->getAccountName()])); } } @@ -171,25 +165,22 @@ public function unblock($names) * @validate-entity-load user_role role * @param string $role The machine name of the role to add. * @param string $names A comma delimited list of user names. + * @option $uid A comma delimited list of user ids to lookup (an alternative to names). + * @option $mail A comma delimited list of emails to lookup (an alternative to names). * @aliases urol,user-add-role * @usage drush user-add-role "editor" user3 * Add the editor role to user3 */ - public function addRole($role, $names) + public function addRole(string $role, string $names = '', $options = ['uid' => self::REQ, 'mail' => self::REQ]): void { - if ($names = StringUtils::csvToArray($names)) { - foreach ($names as $name) { - if ($account = user_load_by_name($name)) { - $account->addRole($role); - $account->save(); - $this->logger->success(dt('Added !role role to !user', [ - '!role' => $role, - '!user' => $name, - ])); - } else { - $this->logger->warning(dt('Unable to load user: !user', ['!user' => $name])); - } - } + $accounts = $this->getAccounts($names, $options); + foreach ($accounts as $id => $account) { + $account->addRole($role); + $account->save(); + $this->logger->success(dt('Added !role role to !user', [ + '!role' => $role, + '!user' => $account->getAccountName(), + ])); } } @@ -201,25 +192,22 @@ public function addRole($role, $names) * @validate-entity-load user_role role * @param string $role The name of the role to add * @param string $names A comma delimited list of user names. + * @option $uid A comma delimited list of user ids to lookup (an alternative to names). + * @option $mail A comma delimited list of emails to lookup (an alternative to names). * @aliases urrol,user-remove-role * @usage drush user:remove-role "power user" user3 * Remove the "power user" role from user3 */ - public function removeRole($role, $names) + public function removeRole(string $role, string $names = '', $options = ['uid' => self::REQ, 'mail' => self::REQ]): void { - if ($names = StringUtils::csvToArray($names)) { - foreach ($names as $name) { - if ($account = user_load_by_name($name)) { - $account->removeRole($role); - $account->save(); - $this->logger->success(dt('Removed !role role from !user', [ - '!role' => $role, - '!user' => $name, - ])); - } else { - $this->logger->warning(dt('Unable to load user: !user', ['!user' => $name])); - } - } + $accounts = $this->getAccounts($names, $options); + foreach ($accounts as $id => $account) { + $account->removeRole($role); + $account->save(); + $this->logger->success(dt('Removed !role role from !user', [ + '!role' => $role, + '!user' => $account->getAccountName(), + ])); } } @@ -235,7 +223,7 @@ public function removeRole($role, $names) * @usage drush user:create newuser --mail="person@example.com" --password="letmein" * Create a new user account with the name newuser, the email address person@example.com, and the password letmein */ - public function create($name, $options = ['password' => self::REQ, 'mail' => self::REQ]) + public function create(string $name, $options = ['password' => self::REQ, 'mail' => self::REQ]) { $new_user = [ 'name' => $name, @@ -259,7 +247,7 @@ public function create($name, $options = ['password' => self::REQ, 'mail' => sel * * @hook validate user-create */ - public function createValidate(CommandData $commandData) + public function createValidate(CommandData $commandData): void { if ($mail = $commandData->input()->getOption('mail')) { if (user_load_by_mail($mail)) { @@ -279,29 +267,26 @@ public function createValidate(CommandData $commandData) * * @param string $names A comma delimited list of user names. * @option delete-content Delete the user, and all content created by the user + * @option $uid A comma delimited list of user ids to lookup (an alternative to names). + * @option $mail A comma delimited list of emails to lookup (an alternative to names). * @aliases ucan,user-cancel * @usage drush user:cancel username * Cancel the user account with the name username and anonymize all content created by that user. * @usage drush user:cancel --delete-content username * Delete the user account with the name username and delete all content created by that user. */ - public function cancel($names, $options = ['delete-content' => false]) + public function cancel(string $names, $options = ['delete-content' => false, 'uid' => self::REQ, 'mail' => self::REQ]): void { - if ($names = StringUtils::csvToArray($names)) { - foreach ($names as $name) { - if ($account = user_load_by_name($name)) { - if ($options['delete-content']) { - $this->logger()->warning(dt('All content created by !name will be deleted.', ['!name' => $account->getAccountName()])); - } - if ($this->io()->confirm('Cancel user account?: ')) { - $method = $options['delete-content'] ? 'user_cancel_delete' : 'user_cancel_block'; - user_cancel([], $account->id(), $method); - drush_backend_batch_process(); - // Drupal logs a message for us. - } - } else { - $this->logger()->warning(dt('Unable to load user: !user', ['!user' => $name])); - } + $accounts = $this->getAccounts($names, $options); + foreach ($accounts as $id => $account) { + if ($options['delete-content']) { + $this->logger()->warning(dt('All content created by !name will be deleted.', ['!name' => $account->getAccountName()])); + } + if ($this->io()->confirm('Cancel user account?: ')) { + $method = $options['delete-content'] ? 'user_cancel_delete' : 'user_cancel_block'; + user_cancel([], $account->id(), $method); + drush_backend_batch_process(); + // Drupal logs a message for us. } } } @@ -317,7 +302,7 @@ public function cancel($names, $options = ['delete-content' => false]) * @usage drush user:password someuser "correct horse battery staple" * Set the password for the username someuser. See https://xkcd.com/936 */ - public function password($name, $password) + public function password(string $name, string $password): void { if ($account = user_load_by_name($name)) { if (!$this->getConfig()->simulate()) { @@ -334,9 +319,8 @@ public function password($name, $password) * A flatter and simpler array presentation of a Drupal $user object. * * @param $account A user account - * @return array */ - public function infoArray($account) + public function infoArray($account): array { return [ 'uid' => $account->id(), @@ -357,4 +341,50 @@ public function infoArray($account) 'uuid' => $account->uuid->value, ]; } + + /** + * Get accounts from name variables or uid & mail options. + * + * @param string $names + * @param array $options + * + * A array of loaded accounts. + * @throws \Exception + */ + protected function getAccounts(string $names = '', array $options = []): array + { + $accounts = []; + if ($mails = StringUtils::csvToArray($options['mail'])) { + foreach ($mails as $mail) { + if ($account = user_load_by_mail($mail)) { + $accounts[$account->id()] = $account; + } else { + $this->logger->warning(dt('Unable to load user: !mail', ['!mail' => $mail])); + } + } + } + if ($uids = StringUtils::csvToArray($options['uid'])) { + foreach ($uids as $uid) { + if ($account = User::load($uid)) { + $accounts[$account->id()] = $account; + } else { + $this->logger->warning(dt('Unable to load user: !uid', ['!uid' => $uid])); + } + } + } + if ($names = StringUtils::csvToArray($names)) { + foreach ($names as $name) { + if ($account = user_load_by_name($name)) { + $accounts[$account->id()] = $account; + } else { + $this->logger->warning(dt('Unable to load user: !user', ['!user' => $name])); + } + } + } + if (empty($accounts)) { + throw new \Exception(dt('Unable to find any matching user')); + } + + return $accounts; + } } diff --git a/src/Drupal/Commands/core/ViewsCommands.php b/src/Drupal/Commands/core/ViewsCommands.php index 4399de5392..2a7d18f14c 100644 --- a/src/Drupal/Commands/core/ViewsCommands.php +++ b/src/Drupal/Commands/core/ViewsCommands.php @@ -1,4 +1,5 @@ configFactory = $configFactory; } - /** - * @return \Drupal\Core\Config\ConfigFactoryInterface - */ - public function getConfigFactory() + public function getConfigFactory(): ConfigFactoryInterface { return $this->configFactory; } - /** - * @return \Drupal\Core\Extension\ModuleHandlerInterface - */ - public function getModuleHandler() + public function getModuleHandler(): ModuleHandlerInterface { return $this->moduleHandler; } - /** - * @return \Drupal\Core\Entity\EntityTypeManagerInterface - */ - public function getEntityTypeManager() + public function getEntityTypeManager(): EntityTypeManagerInterface { return $this->entityTypeManager; } - /** - * @return \Drupal\Core\Render\RendererInterface - */ - public function getRenderer() + public function getRenderer(): RendererInterface { return $this->renderer; } @@ -75,7 +63,7 @@ public function getRenderer() * @validate-module-enabled views * @aliases vd,views-dev */ - public function dev() + public function dev(): void { $settings = [ 'ui.show.listing_filters' => true, @@ -141,7 +129,7 @@ public function dev() * @validate-module-enabled views * * @filter-default-field machine-name - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields + * @return RowsOfFields */ public function vlist($options = ['name' => self::REQ, 'tags' => self::REQ, 'status' => self::REQ, 'format' => 'table']) { @@ -152,11 +140,11 @@ public function vlist($options = ['name' => self::REQ, 'tags' => self::REQ, 'sta // Get the --name option. $name = StringUtils::csvToArray($options['name']); - $with_name = !empty($name) ? true : false; + $with_name = !empty($name); // Get the --tags option. - $tags = \_convert_csv_to_array($options['tags']); - $with_tags = !empty($tags) ? true : false; + $tags = StringUtils::csvToArray($options['tags']); + $with_tags = !empty($tags); // Get the --status option. Store user input apart to reuse it after. $status = $options['status']; @@ -233,7 +221,7 @@ public function vlist($options = ['name' => self::REQ, 'tags' => self::REQ, 'sta * * @return string */ - public function execute($view_name, $display = null, $view_args = null, $options = ['count' => 0, 'show-admin-links' => false]) + public function execute(string $view_name, $display = null, $view_args = null, $options = ['count' => 0, 'show-admin-links' => false]) { $view = Views::getView($view_name); @@ -268,7 +256,7 @@ public function execute($view_name, $display = null, $view_args = null, $options * @aliases va,views-analyze * @validate-module-enabled views * - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields + * @return RowsOfFields */ public function analyze() { @@ -305,7 +293,7 @@ public function analyze() * Enable the frontpage and taxonomy_term views. * @aliases ven,views-enable */ - public function enable($views) + public function enable(string $views): void { $view_names = StringUtils::csvToArray($views); if ($views = $this->getEntityTypeManager()->getStorage('view')->loadMultiple($view_names)) { @@ -327,7 +315,7 @@ public function enable($views) * Disable the frontpage and taxonomy_term views. * @aliases vdis,views-disable */ - public function disable($views) + public function disable(string $views): void { $view_names = StringUtils::csvToArray($views); if ($views = $this->getEntityTypeManager()->getStorage('view')->loadMultiple($view_names)) { @@ -344,7 +332,7 @@ public function disable($views) * * @hook on-event cache-clear */ - public function cacheClear(&$types, $include_bootstrapped_types) + public function cacheClear(&$types, $include_bootstrapped_types): void { if ($include_bootstrapped_types && $this->getModuleHandler()->moduleExists('views')) { $types['views'] = 'views_invalidate_cache'; diff --git a/src/Drupal/Commands/core/WatchdogCommands.php b/src/Drupal/Commands/core/WatchdogCommands.php index c3da155195..bf43f378ac 100644 --- a/src/Drupal/Commands/core/WatchdogCommands.php +++ b/src/Drupal/Commands/core/WatchdogCommands.php @@ -1,4 +1,5 @@ 'table', 'count' => 10, 'severity' => self::REQ, 'type' => self::REQ, 'extended' => false]) { @@ -97,9 +97,8 @@ public function show($substring = '', $options = ['format' => 'table', 'count' = * date: Date * username: Username * @default-fields wid,date,type,severity,message - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields */ - public function watchdogList($substring = '', $options = ['format' => 'table', 'count' => 10, 'extended' => false]) + public function watchdogList($substring = '', $options = ['format' => 'table', 'count' => 10, 'extended' => false]): RowsOfFields { return $this->show($substring, $options); } @@ -136,7 +135,7 @@ public function watchdogList($substring = '', $options = ['format' => 'table', ' * @filter-default-field message * @version 10.6 */ - public function tail(OutputInterface $output, $substring = '', $options = ['format' => 'table', 'severity' => self::REQ, 'type' => self::REQ, 'extended' => false]) + public function tail(OutputInterface $output, $substring = '', $options = ['format' => 'table', 'severity' => self::REQ, 'type' => self::REQ, 'extended' => false]): void { $where = $this->where($options['type'], $options['severity'], $substring); if (empty($where['where'])) { @@ -176,9 +175,9 @@ public function tail(OutputInterface $output, $substring = '', $options = ['form /** * @hook interact watchdog-list - * @throws \Drush\Exceptions\UserAbortException + * @throws UserAbortException */ - public function interactList($input, $output) + public function interactList($input, $output): void { $choices['-- types --'] = dt('== message types =='); @@ -219,9 +218,8 @@ public function interactList($input, $output) * Delete all messages of type cron. * @aliases wd-del,wd-delete,wd,watchdog-delete * @validate-module-enabled dblog - * @return void */ - public function delete($substring = '', $options = ['severity' => self::REQ, 'type' => self::REQ]) + public function delete($substring = '', $options = ['severity' => self::REQ, 'type' => self::REQ]): void { if ($substring == 'all') { $this->output()->writeln(dt('All watchdog messages will be deleted.')); @@ -230,7 +228,7 @@ public function delete($substring = '', $options = ['severity' => self::REQ, 'ty } $ret = Database::getConnection()->truncate('watchdog')->execute(); $this->logger()->success(dt('All watchdog messages have been deleted.')); - } else if (is_numeric($substring)) { + } elseif (is_numeric($substring)) { $this->output()->writeln(dt('Watchdog message #!wid will be deleted.', ['!wid' => $substring])); if (!$this->io()->confirm(dt('Do you want to continue?'))) { throw new UserAbortException(); @@ -242,7 +240,7 @@ public function delete($substring = '', $options = ['severity' => self::REQ, 'ty throw new \Exception(dt('Watchdog message #!wid does not exist.', ['!wid' => $substring])); } } else { - if ((empty($substring))&&(!isset($options['type']))&&(!isset($options['severity']))) { + if ((empty($substring)) && (!isset($options['type'])) && (!isset($options['severity']))) { throw new \Exception(dt('No options provided.')); } $where = $this->where($options['type'], $options['severity'], $substring, 'OR'); @@ -264,10 +262,8 @@ public function delete($substring = '', $options = ['severity' => self::REQ, 'ty * @param $id Watchdog Id * @aliases wd-one,watchdog-show-one * @validate-module-enabled dblog - * - * @return \Consolidation\OutputFormatters\StructuredData\PropertyList */ - public function showOne($id, $options = ['format' => 'yaml']) + public function showOne($id, $options = ['format' => 'yaml']): PropertyList { $rsc = Database::getConnection()->select('watchdog', 'w') ->fields('w') @@ -295,13 +291,13 @@ public function showOne($id, $options = ['format' => 'yaml']) * @return * An array with structure ('where' => string, 'args' => array()) */ - protected function where($type = null, $severity = null, $filter = null, $criteria = 'AND') + protected function where($type = null, $severity = null, $filter = null, $criteria = 'AND'): array { $args = []; $conditions = []; if ($type) { $types = $this->messageTypes(); - if (array_search($type, $types) === false) { + if (!in_array($type, $types)) { $msg = "Unrecognized message type: !type.\nRecognized types are: !types."; throw new \Exception(dt($msg, ['!type' => $type, '!types' => implode(', ', $types)])); } @@ -329,7 +325,7 @@ protected function where($type = null, $severity = null, $filter = null, $criter } if ($filter) { $conditions[] = "message LIKE :filter"; - $args[':filter'] = '%'.$filter.'%'; + $args[':filter'] = '%' . $filter . '%'; } $where = implode(" $criteria ", $conditions); @@ -378,11 +374,7 @@ protected function formatResult($result, $extended = false) unset($result->referer); } // Username. - if ($account = User::load($result->uid)) { - $result->username = $account->name; - } else { - $result->username = dt('Anonymous'); - } + $result->username = ($account = User::load($result->uid)) ? $account->name : dt('Anonymous'); unset($result->uid); $message_length = PHP_INT_MAX; } diff --git a/src/Drupal/Commands/core/drush.services.yml b/src/Drupal/Commands/core/drush.services.yml index 1126b4e31d..27af0c22d1 100644 --- a/src/Drupal/Commands/core/drush.services.yml +++ b/src/Drupal/Commands/core/drush.services.yml @@ -25,20 +25,6 @@ services: class: \Drush\Drupal\Commands\core\EntityDisplayCommands tags: - { name: drush.command } - field.create.commands: - class: \Drush\Drupal\Commands\core\FieldCreateCommands - arguments: - - '@plugin.manager.field.field_type' - - '@plugin.manager.field.widget' - - '@plugin.manager.entity_reference_selection' - - '@entity_type.manager' - - '@entity_type.bundle.info' - - '@module_handler' - - '@entity_field.manager' - calls: - - [ setContentTranslationManager, [ '@?content_translation.manager' ] ] - tags: - - { name: drush.command } link.hooks: class: \Drush\Drupal\Commands\core\LinkHooks arguments: @@ -70,7 +56,7 @@ services: - { name: drush.command } migrate_runner.commands: class: Drush\Drupal\Commands\core\MigrateRunnerCommands - arguments: ['@date.formatter', '@keyvalue'] + arguments: ['@date.formatter', '@keyvalue', '@?plugin.manager.migration'] tags: - { name: drush.command } queue.commands: diff --git a/src/Drupal/Commands/field/EntityTypeBundleAskTrait.php b/src/Drupal/Commands/field/EntityTypeBundleAskTrait.php new file mode 100644 index 0000000000..e6565fb60c --- /dev/null +++ b/src/Drupal/Commands/field/EntityTypeBundleAskTrait.php @@ -0,0 +1,73 @@ +entityTypeManager->getDefinitions(), + function (EntityTypeInterface $entityType) { + return $entityType->entityClassImplements(FieldableEntityInterface::class); + } + ); + $choices = []; + + foreach ($entityTypeDefinitions as $entityTypeDefinition) { + $choices[$entityTypeDefinition->id()] = $this->input->getOption('show-machine-names') + ? $entityTypeDefinition->id() + : $entityTypeDefinition->getLabel(); + } + + if (!$answer = $this->io()->choice('Entity type', $choices)) { + throw new \InvalidArgumentException(t('The entityType argument is required.')); + } + + return $answer; + } + + protected function askBundle(): ?string + { + $entityTypeId = $this->input->getArgument('entityType'); + $entityTypeDefinition = $this->entityTypeManager->getDefinition($entityTypeId); + $bundleEntityType = $entityTypeDefinition->getBundleEntityType(); + $bundleInfo = $this->entityTypeBundleInfo->getBundleInfo($entityTypeId); + $choices = []; + + if ($bundleEntityType && $bundleInfo === []) { + throw new \InvalidArgumentException( + t('Entity type with id \':entityType\' does not have any bundles.', [':entityType' => $entityTypeId]) + ); + } + + if (!$bundleEntityType && count($bundleInfo) === 1) { + // eg. User + return $entityTypeId; + } + + foreach ($bundleInfo as $bundle => $data) { + $label = $this->input->getOption('show-machine-names') ? $bundle : $data['label']; + $choices[$bundle] = $label; + } + + if (!$answer = $this->io()->choice('Bundle', $choices)) { + throw new \InvalidArgumentException(t('The bundle argument is required.')); + } + + return $answer; + } +} diff --git a/src/Drupal/Commands/field/EntityTypeBundleValidationTrait.php b/src/Drupal/Commands/field/EntityTypeBundleValidationTrait.php new file mode 100644 index 0000000000..f0a0b7a795 --- /dev/null +++ b/src/Drupal/Commands/field/EntityTypeBundleValidationTrait.php @@ -0,0 +1,48 @@ +entityTypeManager->hasDefinition($entityTypeId)) { + throw new \InvalidArgumentException( + t("Entity type with id ':entityType' does not exist.", [':entityType' => $entityTypeId]) + ); + } + } + + protected function validateBundle(string $entityTypeId, string $bundle): void + { + if (!$entityTypeDefinition = $this->entityTypeManager->getDefinition($entityTypeId)) { + return; + } + + $bundleEntityType = $entityTypeDefinition->getBundleEntityType(); + + if ($bundleEntityType === null && $bundle === $entityTypeId) { + return; + } + + $bundleDefinition = $this->entityTypeManager + ->getStorage($bundleEntityType) + ->load($bundle); + + if (!$bundleDefinition) { + throw new \InvalidArgumentException( + t("Bundle ':bundle' does not exist on entity type with id ':entityType'.", [ + ':bundle' => $bundle, + ':entityType' => $entityTypeId, + ]) + ); + } + } +} diff --git a/src/Drupal/Commands/field/FieldBaseInfoCommands.php b/src/Drupal/Commands/field/FieldBaseInfoCommands.php new file mode 100644 index 0000000000..f2e51c29af --- /dev/null +++ b/src/Drupal/Commands/field/FieldBaseInfoCommands.php @@ -0,0 +1,82 @@ +entityTypeManager = $entityTypeManager; + $this->entityTypeBundleInfo = $entityTypeBundleInfo; + $this->entityFieldManager = $entityFieldManager; + } + + /** + * List all base fields of an entity type + * + * @command field:base-info + * @aliases field-base-info,fbi + * + * @param string $entityType + * The machine name of the entity type + * + * @option show-machine-names + * Show machine names instead of labels in option lists. + * + * @default-fields field_name,required,field_type,cardinality + * @field-labels + * label: Label + * description: Description + * field_name: Field name + * field_type: Field type + * required: Required + * translatable: Translatable + * cardinality: Cardinality + * default_value: Default value + * default_value_callback: Default value callback + * allowed_values: Allowed values + * allowed_values_function: Allowed values function + * handler: Selection handler + * target_bundles: Target bundles + * @filter-default-field field_name + * @table-style default + * + * @usage drush field:base-info taxonomy_term + * List all base fields. + * @usage drush field:base-info + * List all base fields and fill in the remaining information through prompts. + * + * @version 11.0 + */ + public function info(?string $entityType = null, array $options = [ + 'format' => 'table', + ]): RowsOfFields + { + $this->input->setArgument('entityType', $entityType = $entityType ?? $this->askEntityType()); + $this->validateEntityType($entityType); + + $fieldDefinitions = $this->entityFieldManager->getBaseFieldDefinitions($entityType); + + return $this->getRowsOfFieldsByFieldDefinitions($fieldDefinitions); + } +} diff --git a/src/Drupal/Commands/field/FieldBaseOverrideCreateCommands.php b/src/Drupal/Commands/field/FieldBaseOverrideCreateCommands.php new file mode 100644 index 0000000000..6a56a7b66d --- /dev/null +++ b/src/Drupal/Commands/field/FieldBaseOverrideCreateCommands.php @@ -0,0 +1,196 @@ +entityTypeManager = $entityTypeManager; + $this->entityTypeBundleInfo = $entityTypeBundleInfo; + $this->entityFieldManager = $entityFieldManager; + } + + /** + * Create a new base field override + * + * @command field:base-override-create + * @aliases bfoc + * + * @param string $entityType + * The machine name of the entity type + * @param string $bundle + * The machine name of the bundle + * + * @option field-name + * A unique machine-readable name containing letters, numbers, and underscores. + * @option field-label + * The field label + * @option field-description + * The field description + * @option is-required + * Whether the field is required + * + * @option show-machine-names + * Show machine names instead of labels in option lists. + * + * @usage drush field:base-field-override-create + * Create a base field override by answering the prompts. + * @usage drush field:base-field-override-create taxonomy_term tag + * Create a base field override and fill in the remaining information through prompts. + * @usage drush field:base-field-override-create taxonomy_term tag --field-name=name --field-label=Label --is-required=1 + * Create a base field override in a completely non-interactive way. + * + * @see \Drupal\field_ui\Form\FieldConfigEditForm + * @see \Drupal\field_ui\Form\FieldStorageConfigEditForm + * + * @version 11.0 + */ + public function create(?string $entityType = null, ?string $bundle = null, array $options = [ + 'field-name' => InputOption::VALUE_REQUIRED, + 'field-label' => InputOption::VALUE_REQUIRED, + 'field-description' => InputOption::VALUE_REQUIRED, + 'is-required' => InputOption::VALUE_REQUIRED, + 'show-machine-names' => InputOption::VALUE_OPTIONAL, + ]): void + { + $this->input->setArgument('entityType', $entityType = $entityType ?? $this->askEntityType()); + $this->validateEntityType($entityType); + + $this->input->setArgument('bundle', $bundle = $bundle ?? $this->askBundle()); + $this->validateBundle($entityType, $bundle); + + $fieldName = $this->input->getOption('field-name') ?? $this->askFieldName($entityType); + $this->input->setOption('field-name', $fieldName); + + if ($fieldName === '') { + throw new \InvalidArgumentException(dt('The %optionName option is required.', [ + '%optionName' => 'field-name', + ])); + } + + /** @var BaseFieldOverride|BaseFieldDefinition $definition */ + $definition = BaseFieldOverride::loadByName($entityType, $bundle, $fieldName) + ?? $this->getBaseFieldDefinition($entityType, $fieldName); + + if ($definition === null) { + throw new \InvalidArgumentException( + t("Base field with name ':fieldName' does not exist on bundle ':bundle'.", [ + ':fieldName' => $fieldName, + ':bundle' => $bundle, + ]) + ); + } + + $this->input->setOption( + 'field-label', + $this->input->getOption('field-label') ?? $this->askFieldLabel((string) $definition->getLabel()) + ); + $this->input->setOption( + 'field-description', + $this->input->getOption('field-description') ?? $this->askFieldDescription($definition->getDescription()) + ); + $this->input->setOption( + 'is-required', + (bool) ($this->input->getOption('is-required') ?? $this->askRequired($definition->isRequired())) + ); + + $fieldName = $this->input->getOption('field-name'); + $fieldLabel = $this->input->getOption('field-label'); + $fieldDescription = $this->input->getOption('field-description'); + $isRequired = $this->input->getOption('is-required'); + + $baseFieldOverride = $this->createBaseFieldOverride($entityType, $bundle, $fieldName, $fieldLabel, $fieldDescription, $isRequired); + + $this->logResult($baseFieldOverride); + } + + protected function askFieldName(string $entityType): ?string + { + /** @var BaseFieldDefinition[] $definitions */ + $definitions = $this->entityFieldManager->getBaseFieldDefinitions($entityType); + $choices = []; + + foreach ($definitions as $definition) { + $label = $this->input->getOption('show-machine-names') ? $definition->getName() : (string) $definition->getLabel(); + $choices[$definition->getName()] = $label; + } + + return $this->io()->choice('Field name', $choices); + } + + protected function askFieldLabel(string $default): string + { + return $this->io()->ask('Field label', $default); + } + + protected function askFieldDescription(?string $default): ?string + { + return $this->io()->ask('Field description', $default); + } + + protected function askRequired(bool $default): bool + { + return $this->io()->askQuestion(new ConfirmationQuestion('Required', $default)); + } + + protected function createBaseFieldOverride(string $entityType, string $bundle, string $fieldName, $fieldLabel, $fieldDescription, bool $isRequired): BaseFieldOverride + { + $definition = $this->getBaseFieldDefinition($entityType, $fieldName); + $override = BaseFieldOverride::loadByName($entityType, $bundle, $fieldName) + ?? BaseFieldOverride::createFromBaseFieldDefinition($definition, $bundle); + + $override + ->setLabel($fieldLabel) + ->setDescription($fieldDescription) + ->setRequired($isRequired) + ->save(); + + return $override; + } + + protected function logResult(BaseFieldOverride $baseFieldOverride): void + { + $this->logger()->success( + sprintf( + 'Successfully created base field override \'%s\' on %s with bundle \'%s\'', + $baseFieldOverride->getName(), + $baseFieldOverride->getTargetEntityTypeId(), + $baseFieldOverride->getTargetBundle() + ) + ); + } + + protected function getBaseFieldDefinition(string $entityType, string $fieldName): ?BaseFieldDefinition + { + /** @var BaseFieldDefinition[] $definitions */ + $definitions = $this->entityFieldManager->getBaseFieldDefinitions($entityType); + + return $definitions[$fieldName] ?? null; + } +} diff --git a/src/Drupal/Commands/core/FieldCreateCommands.php b/src/Drupal/Commands/field/FieldCreateCommands.php similarity index 87% rename from src/Drupal/Commands/core/FieldCreateCommands.php rename to src/Drupal/Commands/field/FieldCreateCommands.php index b9a441cd1c..412100a894 100644 --- a/src/Drupal/Commands/core/FieldCreateCommands.php +++ b/src/Drupal/Commands/field/FieldCreateCommands.php @@ -1,6 +1,6 @@ InputOption::VALUE_REQUIRED, 'field-label' => InputOption::VALUE_REQUIRED, 'field-description' => InputOption::VALUE_OPTIONAL, @@ -134,6 +139,7 @@ public function create(string $entityType, ?string $bundle = null, array $option 'existing' => false, ]): void { + $this->input->setArgument('entityType', $entityType = $entityType ?? $this->askEntityType()); $this->validateEntityType($entityType); $this->input->setArgument('bundle', $bundle = $bundle ?? $this->askBundle()); @@ -216,41 +222,6 @@ public function create(string $entityType, ?string $bundle = null, array $option $this->logResult($field); } - protected function validateEntityType(string $entityTypeId): void - { - if (!$this->entityTypeManager->hasDefinition($entityTypeId)) { - throw new \InvalidArgumentException( - t("Entity type with id ':entityType' does not exist.", [':entityType' => $entityTypeId]) - ); - } - } - - protected function validateBundle(string $entityTypeId, string $bundle): void - { - if (!$entityTypeDefinition = $this->entityTypeManager->getDefinition($entityTypeId)) { - return; - } - - $bundleEntityType = $entityTypeDefinition->getBundleEntityType(); - - if ($bundleEntityType === null && $bundle === $entityTypeId) { - return; - } - - $bundleDefinition = $this->entityTypeManager - ->getStorage($bundleEntityType) - ->load($bundle); - - if (!$bundleDefinition) { - throw new \InvalidArgumentException( - t("Bundle ':bundle' does not exist on entity type with id ':entityType'.", [ - ':bundle' => $bundle, - ':entityType' => $entityTypeId, - ]) - ); - } - } - protected function askExistingFieldName(): ?string { $entityType = $this->input->getArgument('entityType'); @@ -277,7 +248,7 @@ protected function askFieldName(): string } while (!$fieldName) { - $answer = $this->io()->ask('Field name', $machineName, [static::class, 'validateRequired']); + $answer = $this->io()->ask('Field name', $machineName); if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $answer)) { $this->logger()->error('Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character.'); @@ -302,7 +273,7 @@ protected function askFieldName(): string protected function askFieldLabel(): string { - return $this->io()->ask('Field label', null, [static::class, 'validateRequired']); + return $this->io()->askRequired('Field label'); } protected function askFieldDescription(): ?string @@ -344,7 +315,7 @@ protected function askFieldWidget(): string $choices[$name] = $label; } - return $this->io()->choice('Field widget', $choices); + return $this->io()->choice('Field widget', $choices, key($choices)); } protected function askRequired(): bool @@ -361,44 +332,6 @@ protected function askTranslatable(): bool return $this->io()->confirm('Translatable', false); } - protected function askBundle(): ?string - { - $entityTypeId = $this->input->getArgument('entityType'); - $entityTypeDefinition = $this->entityTypeManager->getDefinition($entityTypeId); - $bundleEntityType = $entityTypeDefinition->getBundleEntityType(); - $bundleInfo = $this->entityTypeBundleInfo->getBundleInfo($entityTypeId); - $choices = []; - - // If the entity type has one fixed bundle (eg. user), return it. - if ($bundleEntityType === null && count($bundleInfo) === 1) { - return key($bundleInfo); - } - - // If the entity type doesn't have bundles, return null - // TODO Find an example - if ($bundleEntityType === null && count($bundleInfo) === 0) { - return null; - } - - // If the entity type can have multiple bundles but it doesn't have any, throw an error - if ($bundleEntityType !== null && count($bundleInfo) === 0) { - throw new \InvalidArgumentException( - t("Entity type with id ':entityType' does not have any bundles.", [':entityType' => $entityTypeId]) - ); - } - - foreach ($bundleInfo as $bundle => $data) { - $label = $this->input->getOption('show-machine-names') ? $bundle : $data['label']; - $choices[$bundle] = $label; - } - - if (!$this->input->isInteractive() || !$answer = $this->io()->choice('Bundle', $choices)) { - throw new \InvalidArgumentException(t('The bundle argument is required.')); - } - - return $answer; - } - protected function askCardinality(): int { $fieldType = $this->input->getOption('field-type'); @@ -413,7 +346,7 @@ protected function askCardinality(): int $cardinality = $this->io()->choice( 'Allowed number of values', array_combine($choices, $choices), - 1 + 0 ); $limit = FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED; @@ -674,7 +607,8 @@ protected function getExistingFieldStorageOptions(string $entityType, string $bu ? $fieldTypes[$fieldType]['id'] : $fieldTypes[$fieldType]['label']; - if ($fieldStorage instanceof FieldStorageConfigInterface + if ( + $fieldStorage instanceof FieldStorageConfigInterface && !$fieldStorage->isLocked() && empty($fieldTypes[$fieldType]['no_ui']) && !in_array($bundle, $fieldStorage->getBundles(), true) @@ -713,15 +647,4 @@ protected function ensureOption(string $name, callable $asker, bool $required): $this->input->setOption($name, $value); } - - public static function validateRequired(?string $value): string - { - // FALSE is not considered as empty value because question helper use - // it as negative answer on confirmation questions. - if ($value === null || $value === '') { - throw new \UnexpectedValueException('This value is required.'); - } - - return $value; - } } diff --git a/src/Drupal/Commands/field/FieldDefinitionRowsOfFieldsTrait.php b/src/Drupal/Commands/field/FieldDefinitionRowsOfFieldsTrait.php new file mode 100644 index 0000000000..7d1398777d --- /dev/null +++ b/src/Drupal/Commands/field/FieldDefinitionRowsOfFieldsTrait.php @@ -0,0 +1,59 @@ +getFieldStorageDefinition(); + $handlerSettings = $field->getSetting('handler_settings'); + + $rows[$field->getName()] = [ + 'label' => $field->getLabel(), + 'description' => $field->getDescription(), + 'field_name' => $field->getName(), + 'field_type' => $field->getType(), + 'required' => $field->isRequired(), + 'translatable' => $field->isTranslatable(), + 'cardinality' => $storage->getCardinality(), + 'default_value' => empty($field->getDefaultValueLiteral()) ? null : $field->getDefaultValueLiteral(), + 'default_value_callback' => $field->getDefaultValueCallback(), + 'allowed_values' => $storage->getSetting('allowed_values'), + 'allowed_values_function' => $storage->getSetting('allowed_values_function'), + 'handler' => $field->getSetting('handler'), + 'target_bundles' => $handlerSettings['target_bundles'] ?? null, + ]; + } + + $result = new RowsOfFields($rows); + $result->addRendererFunction([$this, 'renderArray']); + $result->addRendererFunction([$this, 'renderBoolean']); + + return $result; + } +} diff --git a/src/Drupal/Commands/field/FieldDeleteCommands.php b/src/Drupal/Commands/field/FieldDeleteCommands.php new file mode 100644 index 0000000000..3e9845b3bd --- /dev/null +++ b/src/Drupal/Commands/field/FieldDeleteCommands.php @@ -0,0 +1,165 @@ +entityTypeManager = $entityTypeManager; + $this->entityTypeBundleInfo = $entityTypeBundleInfo; + } + + /** + * Delete a field + * + * @command field:delete + * @aliases field-delete,fd + * + * @param string $entityType + * The machine name of the entity type + * @param string $bundle + * The machine name of the bundle + * + * @option field-name + * The machine name of the field + * + * @option show-machine-names + * Show machine names instead of labels in option lists. + * + * @usage drush field:delete + * Delete a field by answering the prompts. + * @usage drush field-delete taxonomy_term tag + * Delete a field and fill in the remaining information through prompts. + * @usage drush field-delete taxonomy_term tag --field-name=field_tag_label + * Delete a field in a non-interactive way. + * + * @version 11.0 + * @see \Drupal\field_ui\Form\FieldConfigDeleteForm + */ + public function delete(?string $entityType = null, ?string $bundle = null, array $options = [ + 'field-name' => InputOption::VALUE_REQUIRED, + 'show-machine-names' => InputOption::VALUE_OPTIONAL, + ]): void + { + $this->input->setArgument('entityType', $entityType = $entityType ?? $this->askEntityType()); + $this->validateEntityType($entityType); + + $this->input->setArgument('bundle', $bundle = $bundle ?? $this->askBundle()); + $this->validateBundle($entityType, $bundle); + + $fieldName = $this->input->getOption('field-name') ?? $this->askExisting($entityType, $bundle); + $this->input->setOption('field-name', $fieldName); + + if ($fieldName === '') { + throw new \InvalidArgumentException(dt('The %optionName option is required.', [ + '%optionName' => 'field-name', + ])); + } + + /** @var FieldConfig[] $results */ + $results = $this->entityTypeManager + ->getStorage('field_config') + ->loadByProperties([ + 'field_name' => $fieldName, + 'entity_type' => $entityType, + 'bundle' => $bundle, + ]); + + if ($results === []) { + throw new \InvalidArgumentException( + t("Field with name ':fieldName' does not exist on bundle ':bundle'.", [ + ':fieldName' => $fieldName, + ':bundle' => $bundle, + ]) + ); + } + + $this->deleteFieldConfig(reset($results)); + + // Fields are purged on cron. However field module prevents disabling modules + // when field types they provided are used in a field until it is fully + // purged. In the case that a field has minimal or no content, a single call + // to field_purge_batch() will remove it from the system. Call this with a + // low batch limit to avoid administrators having to wait for cron runs when + // removing fields that meet this criteria. + field_purge_batch(10); + } + + protected function askExisting(string $entityType, string $bundle): string + { + $choices = []; + /** @var FieldConfigInterface[] $fieldConfigs */ + $fieldConfigs = $this->entityTypeManager + ->getStorage('field_config') + ->loadByProperties([ + 'entity_type' => $entityType, + 'bundle' => $bundle, + ]); + + foreach ($fieldConfigs as $fieldConfig) { + $label = $this->input->getOption('show-machine-names') + ? $fieldConfig->get('field_name') + : $fieldConfig->get('label'); + + $choices[$fieldConfig->get('field_name')] = $label; + } + + if ($choices === []) { + throw new \InvalidArgumentException( + t("Bundle ':bundle' has no fields.", [ + ':bundle' => $bundle, + ]) + ); + } + + return $this->io()->choice('Choose a field to delete', $choices); + } + + protected function deleteFieldConfig(FieldConfigInterface $fieldConfig): void + { + $fieldStorage = $fieldConfig->getFieldStorageDefinition(); + $bundles = $this->entityTypeBundleInfo->getBundleInfo($fieldConfig->getTargetEntityTypeId()); + $bundleLabel = $bundles[$fieldConfig->getTargetBundle()]['label']; + + if ($fieldStorage && !$fieldStorage->isLocked()) { + $fieldConfig->delete(); + + // If there is only one bundle left for this field storage, it will be + // deleted too, notify the user about dependencies. + if (count($fieldStorage->getBundles()) <= 1) { + $fieldStorage->delete(); + } + + $message = 'The field :field has been deleted from the :type bundle.'; + } else { + $message = 'There was a problem removing the :field from the :type content type.'; + } + + $this->logger()->success( + t($message, [':field' => $fieldConfig->label(), ':type' => $bundleLabel]) + ); + } +} diff --git a/src/Drupal/Commands/field/FieldInfoCommands.php b/src/Drupal/Commands/field/FieldInfoCommands.php new file mode 100644 index 0000000000..ef211293a5 --- /dev/null +++ b/src/Drupal/Commands/field/FieldInfoCommands.php @@ -0,0 +1,87 @@ +entityTypeManager = $entityTypeManager; + $this->entityTypeBundleInfo = $entityTypeBundleInfo; + } + + /** + * List all configurable fields of an entity bundle + * + * @command field:info + * @aliases field-info,fi + * + * @param string $entityType + * The machine name of the entity type + * @param string $bundle + * The machine name of the bundle + * + * @option show-machine-names + * Show machine names instead of labels in option lists. + * + * @default-fields field_name,required,field_type,cardinality + * @field-labels + * label: Label + * description: Description + * field_name: Field name + * field_type: Field type + * required: Required + * translatable: Translatable + * cardinality: Cardinality + * default_value: Default value + * default_value_callback: Default value callback + * allowed_values: Allowed values + * allowed_values_function: Allowed values function + * handler: Selection handler + * target_bundles: Target bundles + * @filter-default-field field_name + * @table-style default + * + * @usage drush field-info taxonomy_term tag + * List all fields. + * @usage drush field:info + * List all fields and fill in the remaining information through prompts. + * + * @version 11.0 + */ + public function info(?string $entityType = null, ?string $bundle = null, array $options = [ + 'format' => 'table', + ]): RowsOfFields + { + $this->input->setArgument('entityType', $entityType = $entityType ?? $this->askEntityType()); + $this->validateEntityType($entityType); + + $this->input->setArgument('bundle', $bundle = $bundle ?? $this->askBundle()); + $this->validateBundle($entityType, $bundle); + + $fieldDefinitions = $this->entityTypeManager + ->getStorage('field_config') + ->loadByProperties([ + 'entity_type' => $entityType, + 'bundle' => $bundle, + ]); + + return $this->getRowsOfFieldsByFieldDefinitions($fieldDefinitions); + } +} diff --git a/src/Drupal/Commands/field/drush.services.yml b/src/Drupal/Commands/field/drush.services.yml new file mode 100644 index 0000000000..8c302ca57c --- /dev/null +++ b/src/Drupal/Commands/field/drush.services.yml @@ -0,0 +1,45 @@ +services: + field.create.commands: + class: \Drush\Drupal\Commands\field\FieldCreateCommands + arguments: + - '@plugin.manager.field.field_type' + - '@plugin.manager.field.widget' + - '@plugin.manager.entity_reference_selection' + - '@entity_type.manager' + - '@entity_type.bundle.info' + - '@module_handler' + - '@entity_field.manager' + calls: + - [ setContentTranslationManager, [ '@?content_translation.manager' ] ] + tags: + - { name: drush.command } + field.info.commands: + class: \Drush\Drupal\Commands\field\FieldInfoCommands + arguments: + - '@entity_type.manager' + - '@entity_type.bundle.info' + tags: + - { name: drush.command } + field.delete.commands: + class: \Drush\Drupal\Commands\field\FieldDeleteCommands + arguments: + - '@entity_type.manager' + - '@entity_type.bundle.info' + tags: + - { name: drush.command } + field.base-override-create.commands: + class: \Drush\Drupal\Commands\field\FieldBaseOverrideCreateCommands + arguments: + - '@entity_type.manager' + - '@entity_type.bundle.info' + - '@entity_field.manager' + tags: + - { name: drush.command } + field.base-info.commands: + class: \Drush\Drupal\Commands\field\FieldBaseInfoCommands + arguments: + - '@entity_type.manager' + - '@entity_type.bundle.info' + - '@entity_field.manager' + tags: + - { name: drush.command } \ No newline at end of file diff --git a/src/Drupal/Commands/pm/PmCommands.php b/src/Drupal/Commands/pm/PmCommands.php index 23a0acb647..1e00ac2087 100644 --- a/src/Drupal/Commands/pm/PmCommands.php +++ b/src/Drupal/Commands/pm/PmCommands.php @@ -1,4 +1,5 @@ extensionListModule = $extensionListModule; } - /** - * @return \Drupal\Core\Config\ConfigFactoryInterface - */ - public function getConfigFactory() + public function getConfigFactory(): ConfigFactoryInterface { return $this->configFactory; } - /** - * @return \Drupal\Core\Extension\ModuleInstallerInterface - */ - public function getModuleInstaller() + public function getModuleInstaller(): ModuleInstallerInterface { return $this->moduleInstaller; } - /** - * @return \Drupal\Core\Extension\ModuleHandlerInterface - */ - public function getModuleHandler() + public function getModuleHandler(): ModuleHandlerInterface { return $this->moduleHandler; } - /** - * @return \Drupal\Core\Extension\ThemeHandlerInterface - */ - public function getThemeHandler() + public function getThemeHandler(): ThemeHandlerInterface { return $this->themeHandler; } - /** - * @return \Drupal\Core\Extension\ModuleExtensionList - */ - public function getExtensionListModule() + public function getExtensionListModule(): ModuleExtensionList { return $this->extensionListModule; } @@ -85,7 +70,7 @@ public function getExtensionListModule() * @aliases en,pm-enable * @bootstrap root */ - public function enable(array $modules) + public function enable(array $modules): void { $modules = StringUtils::csvToArray($modules); $todo = $this->addInstallDependencies($modules); @@ -114,12 +99,12 @@ public function enable(array $modules) * * @hook validate pm:enable * - * @throws \Drush\Exceptions\UserAbortException - * @throws \Drupal\Core\Extension\MissingDependencyException + * @throws UserAbortException + * @throws MissingDependencyException * * @see \drupal_check_module() */ - public function validateEnableModules(CommandData $commandData) + public function validateEnableModules(CommandData $commandData): void { $modules = $commandData->input()->getArgument('modules'); $modules = StringUtils::csvToArray($modules); @@ -164,7 +149,7 @@ public function validateEnableModules(CommandData $commandData) * @param $modules A comma delimited list of modules. * @aliases pmu,pm-uninstall */ - public function uninstall(array $modules) + public function uninstall(array $modules): void { $modules = StringUtils::csvToArray($modules); $list = $this->addUninstallDependencies($modules); @@ -183,7 +168,7 @@ public function uninstall(array $modules) /** * @hook validate pm-uninstall */ - public function validateUninstall(CommandData $commandData) + public function validateUninstall(CommandData $commandData): void { if ($modules = $commandData->input()->getArgument('modules')) { $modules = StringUtils::csvToArray($modules); @@ -219,9 +204,8 @@ public function validateUninstall(CommandData $commandData) * @default-fields package,display_name,status,version * @aliases pml,pm-list * @filter-default-field display_name - * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields */ - public function pmList($options = ['format' => 'table', 'type' => 'module,theme', 'status' => 'enabled,disabled', 'package' => self::REQ, 'core' => false, 'no-core' => false]) + public function pmList($options = ['format' => 'table', 'type' => 'module,theme', 'status' => 'enabled,disabled', 'package' => self::REQ, 'core' => false, 'no-core' => false]): RowsOfFields { $rows = []; @@ -279,7 +263,7 @@ public function pmList($options = ['format' => 'table', 'type' => 'module,theme' $row = [ 'package' => $extension->info['package'], 'project' => isset($extension->info['project']) ? $extension->info['project'] : '', - 'display_name' => $extension->info['name']. ' ('. $extension->getName(). ')', + 'display_name' => $extension->info['name'] . ' (' . $extension->getName() . ')', 'name' => $extension->getName(), 'type' => $extension->getType(), 'path' => $extension->getPath(), @@ -302,12 +286,12 @@ public function pmList($options = ['format' => 'table', 'type' => 'module,theme' * @return * String describing extension status. Values: enabled|disabled. */ - public function extensionStatus($extension) + public function extensionStatus($extension): string { return $extension->status == 1 ? 'enabled' : 'disabled'; } - public function addInstallDependencies($modules) + public function addInstallDependencies($modules): array { $module_data = $this->getExtensionListModule()->reset()->getList(); $module_list = array_combine($modules, $modules); diff --git a/src/Drupal/Commands/pm/ThemeCommands.php b/src/Drupal/Commands/pm/ThemeCommands.php index 039637dff4..55be734b9e 100644 --- a/src/Drupal/Commands/pm/ThemeCommands.php +++ b/src/Drupal/Commands/pm/ThemeCommands.php @@ -1,4 +1,5 @@ themeInstaller; } @@ -32,7 +32,7 @@ public function getThemeInstaller() * @param $themes A comma delimited list of themes. * @aliases then,theme-enable */ - public function enable(array $themes) + public function enable(array $themes): void { $themes = StringUtils::csvToArray($themes); if (!$this->getThemeInstaller()->install($themes, true)) { @@ -48,7 +48,7 @@ public function enable(array $themes) * @param $themes A comma delimited list of themes. * @aliases thun,theme-uninstall */ - public function uninstall(array $themes) + public function uninstall(array $themes): void { $themes = StringUtils::csvToArray($themes); // The uninstall() method has no return value. Assume it succeeded, and diff --git a/src/Drupal/Commands/sql/SanitizeCommands.php b/src/Drupal/Commands/sql/SanitizeCommands.php index 1b3ba775cc..ada181dde5 100644 --- a/src/Drupal/Commands/sql/SanitizeCommands.php +++ b/src/Drupal/Commands/sql/SanitizeCommands.php @@ -9,7 +9,6 @@ class SanitizeCommands extends DrushCommands implements CustomEventAwareInterface { - use CustomEventAwareTrait; /** @@ -30,7 +29,7 @@ class SanitizeCommands extends DrushCommands implements CustomEventAwareInterfac * Sanitizes database but exempts two user fields from modification. * @topics docs:hooks */ - public function sanitize() + public function sanitize(): void { /** * In order to present only one prompt, collect all confirmations from diff --git a/src/Drupal/Commands/sql/SanitizeCommentsCommands.php b/src/Drupal/Commands/sql/SanitizeCommentsCommands.php index 7d2a685c86..0c73b6a810 100644 --- a/src/Drupal/Commands/sql/SanitizeCommentsCommands.php +++ b/src/Drupal/Commands/sql/SanitizeCommentsCommands.php @@ -1,4 +1,5 @@ applies()) { //Update anon. @@ -61,7 +62,7 @@ public function sanitize($result, CommandData $commandData) * * @inheritdoc */ - public function messages(&$messages, InputInterface $input) + public function messages(&$messages, InputInterface $input): void { if ($this->applies()) { $messages[] = dt('Remove comment display names and emails.'); diff --git a/src/Drupal/Commands/sql/SanitizePluginInterface.php b/src/Drupal/Commands/sql/SanitizePluginInterface.php index ddc8a99e35..41c67fbd7a 100644 --- a/src/Drupal/Commands/sql/SanitizePluginInterface.php +++ b/src/Drupal/Commands/sql/SanitizePluginInterface.php @@ -29,5 +29,5 @@ public function sanitize($result, CommandData $commandData); * @return String[] * An array of messages. */ - public function messages(&$messages, InputInterface $input); + public function messages(array &$messages, InputInterface $input); } diff --git a/src/Drupal/Commands/sql/SanitizeSessionsCommands.php b/src/Drupal/Commands/sql/SanitizeSessionsCommands.php index 577e580856..5a04c230f7 100644 --- a/src/Drupal/Commands/sql/SanitizeSessionsCommands.php +++ b/src/Drupal/Commands/sql/SanitizeSessionsCommands.php @@ -1,4 +1,5 @@ getDatabase()->truncate('sessions')->execute(); $this->logger()->success(dt('Sessions table truncated.')); @@ -44,7 +45,7 @@ public function sanitize($result, CommandData $commandData) * * @inheritdoc */ - public function messages(&$messages, InputInterface $input) + public function messages(&$messages, InputInterface $input): void { $messages[] = dt('Truncate sessions table.'); } diff --git a/src/Drupal/Commands/sql/SanitizeUserFieldsCommands.php b/src/Drupal/Commands/sql/SanitizeUserFieldsCommands.php index 02f1e0ad69..b54b44bbb6 100644 --- a/src/Drupal/Commands/sql/SanitizeUserFieldsCommands.php +++ b/src/Drupal/Commands/sql/SanitizeUserFieldsCommands.php @@ -1,4 +1,5 @@ options(); $conn = $this->getDatabase(); @@ -128,7 +129,7 @@ public function sanitize($result, CommandData $commandData) * * @inheritdoc */ - public function messages(&$messages, InputInterface $input) + public function messages(&$messages, InputInterface $input): void { $messages[] = dt('Sanitize text fields associated with users.'); } @@ -138,7 +139,7 @@ public function messages(&$messages, InputInterface $input) * @option whitelist-fields Deprecated. Use allowlist-fields instead. * @option allowlist-fields A comma delimited list of fields exempt from sanitization. */ - public function options($options = ['whitelist-fields' => '', 'allowlist-fields' => '']) + public function options($options = ['whitelist-fields' => '', 'allowlist-fields' => '']): void { } } diff --git a/src/Drupal/Commands/sql/SanitizeUserTableCommands.php b/src/Drupal/Commands/sql/SanitizeUserTableCommands.php index c01ffef98c..95fa751318 100644 --- a/src/Drupal/Commands/sql/SanitizeUserTableCommands.php +++ b/src/Drupal/Commands/sql/SanitizeUserTableCommands.php @@ -1,4 +1,5 @@ options(); $query = $this->database->update('users_field_data')->condition('uid', 0, '>'); @@ -92,7 +93,7 @@ public function sanitize($result, CommandData $commandData) * By default, passwords are randomized. Specify no to disable that. Specify any other value to set all passwords * to that value. */ - public function options($options = ['sanitize-email' => 'user+%uid@localhost.localdomain', 'sanitize-password' => null]) + public function options($options = ['sanitize-email' => 'user+%uid@localhost.localdomain', 'sanitize-password' => null]): void { } @@ -101,7 +102,7 @@ public function options($options = ['sanitize-email' => 'user+%uid@localhost.loc * * @inheritdoc */ - public function messages(&$messages, InputInterface $input) + public function messages(&$messages, InputInterface $input): void { $options = $input->getOptions(); if ($this->isEnabled($options['sanitize-password'])) { @@ -115,9 +116,8 @@ public function messages(&$messages, InputInterface $input) /** * Test an option value to see if it is disabled. * @param $value - * @return bool */ - protected function isEnabled($value) + protected function isEnabled($value): bool { return $value != 'no' && $value != '0'; } diff --git a/src/Drupal/DrupalKernelTrait.php b/src/Drupal/DrupalKernelTrait.php index 9b543148d7..9718f039f6 100644 --- a/src/Drupal/DrupalKernelTrait.php +++ b/src/Drupal/DrupalKernelTrait.php @@ -2,6 +2,7 @@ namespace Drush\Drupal; +use Symfony\Component\DependencyInjection\ContainerInterface; use Composer\Semver\Semver; use Drupal\Core\DependencyInjection\ServiceModifierInterface; use Drupal\Core\Site\Settings; @@ -44,7 +45,7 @@ protected function getContainerBuilder() /** * Initializes the service container. * - * @return \Symfony\Component\DependencyInjection\ContainerInterface + * @return ContainerInterface */ protected function initializeContainer() { @@ -100,6 +101,7 @@ public function discoverServiceProviders() // - These commands are not available until Drupal is bootstrapped. $this->addDrushServiceProvider("_drush__config", DRUSH_BASE_PATH . '/src/Drupal/Commands/config/drush.services.yml'); $this->addDrushServiceProvider("_drush__core", DRUSH_BASE_PATH . '/src/Drupal/Commands/core/drush.services.yml'); + $this->addDrushServiceProvider("_drush__field", DRUSH_BASE_PATH . '/src/Drupal/Commands/field/drush.services.yml'); $this->addDrushServiceProvider("_drush__pm", DRUSH_BASE_PATH . '/src/Drupal/Commands/pm/drush.services.yml'); $this->addDrushServiceProvider("_drush__sql", DRUSH_BASE_PATH . '/src/Drupal/Commands/sql/drush.services.yml'); @@ -202,9 +204,9 @@ protected function findAppropriateServicesFile($module, $services, $dir) /** * Add a services.yml file if it exists. */ - protected function addDrushServiceProvider($serviceProviderName, $serviceYmlPath) + protected function addDrushServiceProvider($serviceProviderName, $serviceYmlPath = '') { - if (file_exists($serviceYmlPath)) { + if (($serviceYmlPath !== null) && file_exists($serviceYmlPath)) { $this->serviceYamls['app'][$serviceProviderName] = $serviceYmlPath; } } diff --git a/src/Drupal/DrupalUtil.php b/src/Drupal/DrupalUtil.php index 7346695d5a..5b2b049b1c 100644 --- a/src/Drupal/DrupalUtil.php +++ b/src/Drupal/DrupalUtil.php @@ -1,25 +1,26 @@ renderRoot($data); } - $data = \Drupal\Core\Mail\MailFormatHelper::htmlToText($data); + $data = MailFormatHelper::htmlToText($data); return $data; } } diff --git a/src/Drupal/DrushLoggerServiceProvider.php b/src/Drupal/DrushLoggerServiceProvider.php index a03c5d4fa4..15ba28e469 100644 --- a/src/Drupal/DrushLoggerServiceProvider.php +++ b/src/Drupal/DrushLoggerServiceProvider.php @@ -2,6 +2,7 @@ namespace Drush\Drupal; +use Drush\Log\DrushLog; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\ServiceProviderInterface; use Drush\Log\LoggerDrupalToDrush; @@ -12,9 +13,9 @@ class DrushLoggerServiceProvider implements ServiceProviderInterface /** * @inheritDoc */ - public function register(ContainerBuilder $container) + public function register(ContainerBuilder $container): void { - $container->register('logger.drupaltodrush', \Drush\Log\DrushLog::class) + $container->register('logger.drupaltodrush', DrushLog::class) ->addArgument(new Reference('logger.log_message_parser')) ->addTag('logger'); } diff --git a/src/Drupal/DrushServiceModifier.php b/src/Drupal/DrushServiceModifier.php index b45f49bc30..e18b0c6dba 100644 --- a/src/Drupal/DrushServiceModifier.php +++ b/src/Drupal/DrushServiceModifier.php @@ -2,6 +2,7 @@ namespace Drush\Drupal; +use DrupalCodeGenerator\Application; use Drush\Drush; use Drupal\Core\DependencyInjection\ServiceModifierInterface; use Drupal\Core\DependencyInjection\ContainerBuilder; @@ -20,7 +21,7 @@ class DrushServiceModifier implements ServiceModifierInterface /** * @inheritdoc */ - public function alter(ContainerBuilder $container) + public function alter(ContainerBuilder $container): void { Drush::logger()->debug(dt("Service modifier alter.")); // http://symfony.com/doc/2.7/components/dependency_injection/tags.html#register-the-pass-with-the-container @@ -31,7 +32,7 @@ public function alter(ContainerBuilder $container) $container->register(self::DRUSH_COMMAND_INFO_ALTERER_SERVICES, 'Drush\Command\ServiceCommandlist'); $container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_COMMAND_INFO_ALTERER_SERVICES, 'drush.command_info_alterer')); $container->register(self::DRUSH_GENERATOR_SERVICES, 'Drush\Command\ServiceCommandlist'); - $container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_GENERATOR_SERVICES, 'drush.generator.v' . \DrupalCodeGenerator\Application::API)); + $container->addCompilerPass(new FindCommandsCompilerPass(self::DRUSH_GENERATOR_SERVICES, 'drush.generator.v' . Application::API)); } /** @@ -39,9 +40,8 @@ public function alter(ContainerBuilder $container) * * @param $container_definition * Cached container definition - * @return bool */ - public function check($container_definition) + public function check($container_definition): bool { return isset($container_definition['services'][self::DRUSH_CONSOLE_SERVICES]) && diff --git a/src/Drupal/ExtensionDiscovery.php b/src/Drupal/ExtensionDiscovery.php index 3ceda8492c..722c5ce321 100644 --- a/src/Drupal/ExtensionDiscovery.php +++ b/src/Drupal/ExtensionDiscovery.php @@ -6,7 +6,7 @@ class ExtensionDiscovery extends DrupalExtensionDiscovery { - public static function reset() + public static function reset(): void { static::$files = []; } diff --git a/src/Drupal/FindCommandsCompilerPass.php b/src/Drupal/FindCommandsCompilerPass.php index b1b029a085..598642b709 100644 --- a/src/Drupal/FindCommandsCompilerPass.php +++ b/src/Drupal/FindCommandsCompilerPass.php @@ -1,9 +1,11 @@ tagId = $tagId; } - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { Drush::logger()->debug(dt("process !storage !tag", ['!storage' => $this->storageClassId, '!tag' => $this->tagId])); // We expect that our called registered the storage @@ -63,7 +65,7 @@ public function process(ContainerBuilder $container) Drush::logger()->debug(dt("Found tagged service !id", ['!id' => $id])); $definition->addMethodCall( 'addCommandReference', - [new Reference($id)] + [new Reference($id, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)] ); } } diff --git a/src/Drupal/InstallerKernel.php b/src/Drupal/InstallerKernel.php index f696bdd57c..e8b4e3310a 100644 --- a/src/Drupal/InstallerKernel.php +++ b/src/Drupal/InstallerKernel.php @@ -1,4 +1,5 @@ 0, MigrateIdMapInterface::STATUS_IGNORED => 0, MigrateIdMapInterface::STATUS_IMPORTED => 0, @@ -44,10 +40,8 @@ class MigrateExecutable extends MigrateExecutableBase /** * Counter of map deletions. - * - * @var int */ - protected $deleteCounter = 0; + protected int $deleteCounter = 0; /** * Maximum number of items to process in this migration. @@ -65,90 +59,70 @@ class MigrateExecutable extends MigrateExecutableBase /** * Show timestamp in progress message. - * - * @var bool */ - protected $showTimestamp; + protected bool $showTimestamp; /** * Show internal counter in progress message. - * - * @var bool */ - protected $showTotal; + protected bool $showTotal; /** * List of specific source IDs to import. - * - * @var array */ - protected $idlist; + protected array $idlist; /** * List of all source IDs that are found in source during this migration. - * - * @var array */ - protected $allSourceIdValues = []; + protected array $allSourceIdValues = []; /** * Count of number of items processed so far in this migration. - * - * @var int */ - protected $counter = 0; + protected int $counter = 0; /** * Whether the destination item exists before saving. - * - * @var bool */ - protected $preExistingItem = false; + protected bool $preExistingItem = false; /** * List of event listeners we have registered. * * @var callable[] */ - protected $listeners = []; + protected array $listeners = []; /** * Whether to delete rows missing from source after an import. - * - * @var bool */ - protected $deleteMissingSourceRows; + protected bool $deleteMissingSourceRows; /** * Static cached ID map. - * - * @var \Drush\Drupal\Migrate\MigrateIdMapFilter */ - protected $idMap; + protected ?MigrateIdMapFilter $idMap; /** - * If the execution exposes an progress bar. - * - * @var bool + * If the execution exposes a progress bar. */ - protected $exposeProgressBar; + protected bool $exposeProgressBar; /** * The Symfony progress bar. - * - * @var \Symfony\Component\Console\Helper\ProgressBar */ - protected $progressBar; + protected ?ProgressBar $progressBar; /** * Constructs a new migrate executable instance. * - * @param \Drupal\migrate\Plugin\MigrationInterface $migration - * @param \Drupal\migrate\MigrateMessageInterface $message - * @param \Symfony\Component\Console\Output\OutputInterface $output + * @param MigrationInterface $migration + * @param MigrateMessageInterface $message + * @param OutputInterface $output * @param array $options * - * @throws \Drupal\migrate\MigrateException + * @throws MigrateException */ public function __construct(MigrationInterface $migration, MigrateMessageInterface $message, OutputInterface $output, array $options = []) { @@ -184,7 +158,7 @@ public function __construct(MigrationInterface $migration, MigrateMessageInterfa // Cannot use the progress bar when: // - `--no-progress` option is used, // - `--feedback` option is used, - // - The migration source plugin is configured to skips count. + // - The migration source plugin is configured to skip count. $this->exposeProgressBar = $options['progress'] && !$this->feedback && empty($migration->getSourceConfiguration()['skip_count']); $this->listeners[MigrateEvents::MAP_SAVE] = [$this, 'onMapSave']; @@ -207,7 +181,7 @@ public function __construct(MigrationInterface $migration, MigrateMessageInterfa /** * Counts up any map save events. * - * @param \Drupal\migrate\Event\MigrateMapSaveEvent $event + * @param MigrateMapSaveEvent $event * The map event. */ public function onMapSave(MigrateMapSaveEvent $event): void @@ -227,7 +201,7 @@ public function onMapSave(MigrateMapSaveEvent $event): void /** * Counts up any rollback events. * - * @param \Drupal\migrate\Event\MigrateMapDeleteEvent $event + * @param MigrateMapDeleteEvent $event * The map event. */ public function onMapDelete(MigrateMapDeleteEvent $event): void @@ -239,7 +213,7 @@ public function onMapDelete(MigrateMapDeleteEvent $event): void /** * Reacts when the import is about to start. * - * @param \Drupal\migrate\Event\MigrateImportEvent $event + * @param MigrateImportEvent $event * The import event. */ public function onPreImport(MigrateImportEvent $event): void @@ -264,7 +238,7 @@ public function onPreImport(MigrateImportEvent $event): void * propagation, thus avoiding the destination object rollback, even when * the`--delete` option has been passed. * - * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * @param MigrationInterface $migration * * @see \Drush\Drupal\Migrate\MigrateExecutable::onMissingSourceRows() */ @@ -285,7 +259,7 @@ protected function handleMissingSourceRows(MigrationInterface $migration): void if ($destinationIds) { $missingSourceEvent = new MigrateMissingSourceRowsEvent($migration, $destinationIds); - $this->getEventDispatcher()->dispatch(MigrateMissingSourceRowsEvent::class, $missingSourceEvent); + $this->getEventDispatcher()->dispatch($missingSourceEvent); } } @@ -297,7 +271,7 @@ protected function handleMissingSourceRows(MigrationInterface $migration): void * destination entity and then stopping the event propagation, thus avoiding * the destination object deletion, even the `--delete` option was passed. * - * @param \Drush\Drupal\Migrate\MigrateMissingSourceRowsEvent $event + * @param MigrateMissingSourceRowsEvent $event * The event object. */ public function onMissingSourceRows(MigrateMissingSourceRowsEvent $event): void @@ -326,7 +300,7 @@ public function onMissingSourceRows(MigrateMissingSourceRowsEvent $event): void /** * Reacts to migration completion. * - * @param \Drupal\migrate\Event\MigrateImportEvent $event + * @param MigrateImportEvent $event * The map event. */ public function onPostImport(MigrateImportEvent $event): void @@ -341,10 +315,8 @@ public function onPostImport(MigrateImportEvent $event): void /** * Emits information on the import progress. - * - * @param bool $done */ - protected function importFeedbackMessage($done = true): void + protected function importFeedbackMessage(bool $done = true): void { $processed = $this->getProcessedCount(); $timer = Timer::read('migrate:' . $this->migration->getPluginId()); @@ -392,7 +364,7 @@ protected function importFeedbackMessage($done = true): void /** * Reacts when the rollback is about to starts. * - * @param \Drupal\migrate\Event\MigrateRollbackEvent $event + * @param MigrateRollbackEvent $event * The map event. */ public function onPreRollback(MigrateRollbackEvent $event): void @@ -403,7 +375,7 @@ public function onPreRollback(MigrateRollbackEvent $event): void /** * Reacts to rollback completion. * - * @param \Drupal\migrate\Event\MigrateRollbackEvent $event + * @param MigrateRollbackEvent $event * The map event. */ public function onPostRollback(MigrateRollbackEvent $event): void @@ -423,10 +395,8 @@ public function onPostRollback(MigrateRollbackEvent $event): void /** * Emits information on the rollback execution progress. - * - * @param bool $done */ - protected function rollbackFeedbackMessage($done = true): void + protected function rollbackFeedbackMessage(bool $done = true): void { $rolledBack = $this->getRollbackCount(); if ($done) { @@ -452,7 +422,7 @@ protected function rollbackFeedbackMessage($done = true): void /** * Reacts to an item about to be imported. * - * @param \Drupal\migrate\Event\MigratePreRowSaveEvent $event + * @param MigratePreRowSaveEvent $event * The pre-save event. */ public function onPreRowSave(MigratePreRowSaveEvent $event): void @@ -468,7 +438,7 @@ public function onPreRowSave(MigratePreRowSaveEvent $event): void /** * Reacts aftre a row has been deleted. * - * @param \Drupal\migrate\Event\MigratePostRowSaveEvent $event + * @param MigratePostRowSaveEvent $event * The event. */ public function onPostRowSave(MigratePostRowSaveEvent $event): void @@ -479,7 +449,7 @@ public function onPostRowSave(MigratePostRowSaveEvent $event): void /** * Reacts to item rollback. * - * @param \Drupal\migrate\Event\MigrateRowDeleteEvent $event + * @param MigrateRowDeleteEvent $event * The post-save event. */ public function onPostRowDelete(MigrateRowDeleteEvent $event): void @@ -493,10 +463,10 @@ public function onPostRowDelete(MigrateRowDeleteEvent $event): void /** * Reacts to a new row being prepared. * - * @param \Drush\Drupal\Migrate\MigratePrepareRowEvent $event + * @param MigratePrepareRowEvent $event * The prepare-row event. * - * @throws \Drupal\migrate\MigrateSkipRowException + * @throws MigrateSkipRowException */ public function onPrepareRow(MigratePrepareRowEvent $event): void { @@ -512,7 +482,7 @@ public function onPrepareRow(MigratePrepareRowEvent $event): void } } if ($skip) { - throw new MigrateSkipRowException(null, false); + throw new MigrateSkipRowException('', false); } } @@ -621,7 +591,7 @@ protected function resetCounters(): void /** * Initializes the command progress bar if possible. * - * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * @param MigrationInterface $migration * The migration. */ protected function initProgressBar(MigrationInterface $migration): void diff --git a/src/Drupal/Migrate/MigrateIdMapFilter.php b/src/Drupal/Migrate/MigrateIdMapFilter.php index b50dc00adb..ed45af4e0b 100644 --- a/src/Drupal/Migrate/MigrateIdMapFilter.php +++ b/src/Drupal/Migrate/MigrateIdMapFilter.php @@ -9,23 +9,18 @@ */ class MigrateIdMapFilter extends \FilterIterator { - /** * List of specific source IDs to filter on. - * - * @var array */ - protected $sourceIdList; + protected array $sourceIdList; /** * List of specific destination IDs to filter on. - * - * @var array */ - protected $destinationIdList; + protected array $destinationIdList; /** - * @param \Drupal\migrate\Plugin\MigrateIdMapInterface $idMap + * @param MigrateIdMapInterface $idMap * The ID map. * @param array|null $sourceIdList * The source ID list to filter on. @@ -49,7 +44,7 @@ public function accept(): bool return true; } - /** @var \Drupal\migrate\Plugin\MigrateIdMapInterface $idMap */ + /** @var MigrateIdMapInterface $idMap */ $idMap = $this->getInnerIterator(); $acceptedBySourceIdList = $this->sourceIdList && in_array(array_values($idMap->currentSource()), $this->sourceIdList); diff --git a/src/Drupal/Migrate/MigrateMessage.php b/src/Drupal/Migrate/MigrateMessage.php index 98d7fa06c2..a6e24da568 100644 --- a/src/Drupal/Migrate/MigrateMessage.php +++ b/src/Drupal/Migrate/MigrateMessage.php @@ -17,7 +17,7 @@ class MigrateMessage implements MigrateMessageInterface, LoggerAwareInterface /** * Constructs a migrate message class. * - * @param \Psr\Log\LoggerInterface $logger + * @param LoggerInterface $logger */ public function __construct(LoggerInterface $logger) { diff --git a/src/Drupal/Migrate/MigrateMissingSourceRowsEvent.php b/src/Drupal/Migrate/MigrateMissingSourceRowsEvent.php index 9e72f18918..58d4e452d0 100644 --- a/src/Drupal/Migrate/MigrateMissingSourceRowsEvent.php +++ b/src/Drupal/Migrate/MigrateMissingSourceRowsEvent.php @@ -3,7 +3,7 @@ namespace Drush\Drupal\Migrate; use Drupal\migrate\Plugin\MigrationInterface; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * Missing source rows event. @@ -12,22 +12,20 @@ class MigrateMissingSourceRowsEvent extends Event { /** * The migration plugin instance. - * - * @var \Drupal\migrate\Plugin\MigrationInterface */ - protected $migration; + protected MigrationInterface $migration; /** * Values representing the destination IDs. * * @var array[] */ - protected $destinationIds; + protected array $destinationIds; /** * Constructs a new event instance. * - * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * @param MigrationInterface $migration * The migration plugin instance. * @param array[] $destinationIds * Values representing the destination ID. @@ -41,7 +39,7 @@ public function __construct(MigrationInterface $migration, array $destinationIds /** * Gets the migration plugin instance. * - * @return \Drupal\migrate\Plugin\MigrationInterface + * @return MigrationInterface * The migration being rolled back. */ public function getMigration(): MigrationInterface diff --git a/src/Drupal/Migrate/MigratePrepareRowEvent.php b/src/Drupal/Migrate/MigratePrepareRowEvent.php index 07f134fe56..6ec7d57563 100644 --- a/src/Drupal/Migrate/MigratePrepareRowEvent.php +++ b/src/Drupal/Migrate/MigratePrepareRowEvent.php @@ -5,7 +5,7 @@ use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\MigrateSourceInterface; use Drupal\migrate\Row; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * Wraps a prepare-row event for event listeners. @@ -19,33 +19,27 @@ final class MigratePrepareRowEvent extends Event { /** * Row object. - * - * @var \Drupal\migrate\Row */ - protected $row; + protected Row $row; /** * Migration source plugin. - * - * @var \Drupal\migrate\Plugin\MigrateSourceInterface */ - protected $source; + protected MigrateSourceInterface $source; /** * Migration plugin. - * - * @var \Drupal\migrate\Plugin\MigrationInterface */ - protected $migration; + protected MigrationInterface $migration; /** * Constructs a prepare-row event object. * - * @param \Drupal\migrate\Row $row + * @param Row $row * Row of source data to be analyzed/manipulated. - * @param \Drupal\migrate\Plugin\MigrateSourceInterface $source + * @param MigrateSourceInterface $source * Source plugin that is the source of the event. - * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * @param MigrationInterface $migration * Migration entity. */ public function __construct(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) @@ -58,7 +52,7 @@ public function __construct(Row $row, MigrateSourceInterface $source, MigrationI /** * Gets the row object. * - * @return \Drupal\migrate\Row + * @return Row * The row object about to be imported. */ public function getRow(): Row @@ -67,11 +61,11 @@ public function getRow(): Row } /** - * Gets the source plugin. - * - * @return \Drupal\migrate\Plugin\MigrateSourceInterface $source - * The source plugin firing the event. - */ + * Gets the source plugin. + * + * @return MigrateSourceInterface $source + * The source plugin firing the event. + */ public function getSource(): MigrateSourceInterface { return $this->source; @@ -80,7 +74,7 @@ public function getSource(): MigrateSourceInterface /** * Gets the migration plugin. * - * @return \Drupal\migrate\Plugin\MigrationInterface + * @return MigrationInterface * The migration entity being imported. */ public function getMigration(): MigrationInterface diff --git a/src/Drupal/Migrate/MigrateUtils.php b/src/Drupal/Migrate/MigrateUtils.php index 093ab550fa..fa2259ecb4 100644 --- a/src/Drupal/Migrate/MigrateUtils.php +++ b/src/Drupal/Migrate/MigrateUtils.php @@ -7,12 +7,11 @@ */ class MigrateUtils { - /** * Parses as an array the list of IDs received from console. * * IDs are delimited by comma. Each ID consists in one are many ID columns, - * separated by a colon (:). + * separated by a colon (":"). * * @param string|null $idlist * @@ -20,7 +19,7 @@ class MigrateUtils */ public static function parseIdList(?string $idlist): array { - $idlist = array_filter(str_getcsv($idlist)); + $idlist = array_filter(str_getcsv((string) $idlist)); array_walk($idlist, function (string &$value) { $value = str_getcsv(trim($value), ':'); }); diff --git a/src/Drupal/UpdateKernel.php b/src/Drupal/UpdateKernel.php index 5632fd38f8..581672542a 100644 --- a/src/Drupal/UpdateKernel.php +++ b/src/Drupal/UpdateKernel.php @@ -1,4 +1,5 @@ config() instead. */ - public static function config(): Config\DrushConfig + public static function config(): DrushConfig { return self::service('config'); } @@ -356,7 +360,7 @@ public static function process($commandline, $cwd = null, $env = null, $input = * @return * A wrapper around Symfony Process. */ - public static function shell($command, $cwd = null, array $env = null, $input = null, $timeout = 60): Process + public static function shell(string $command, $cwd = null, array $env = null, $input = null, $timeout = 60): ProcessBase { return self::processManager()->shell($command, $cwd, $env, $input, $timeout); } @@ -418,7 +422,7 @@ public static function debug(): bool /** * Return the Bootstrap Manager. */ - public static function bootstrapManager(): Boot\BootstrapManager + public static function bootstrapManager(): BootstrapManager { return self::service('bootstrap.manager'); } @@ -426,7 +430,7 @@ public static function bootstrapManager(): Boot\BootstrapManager /** * Return the Bootstrap object. */ - public static function bootstrap(): Boot\Boot + public static function bootstrap(): Boot { return self::bootstrapManager()->bootstrap(); } @@ -454,6 +458,9 @@ public static function redispatchOptions($input = null) // Remove anything in $options that was not on the cli $options = array_intersect_key($options, array_flip($optionNamesFromCommandline)); + // Don't suppress output as it is usually needed in redispatches. See https://github.com/drush-ops/drush/issues/4805 and https://github.com/drush-ops/drush/issues/4933 + unset($options['quiet']); + // Add in the 'runtime.context' items, which includes --include, --alias-path et. al. return $options + array_filter(self::config()->get(PreflightArgs::DRUSH_RUNTIME_CONTEXT_NAMESPACE)); } diff --git a/src/Exec/ExecTrait.php b/src/Exec/ExecTrait.php index b7ec6c2d75..3ce7824bed 100644 --- a/src/Exec/ExecTrait.php +++ b/src/Exec/ExecTrait.php @@ -1,4 +1,5 @@ logger()->info(dt('No graphical display appears to be available, not starting browser.')); return false; } @@ -54,9 +55,9 @@ public function startBrowser($uri = null, $sleep = 0, $port = false, $browser = // See if we can find an OS helper to open URLs in default browser. if (self::programExists('xdg-open')) { $browser = 'xdg-open'; - } else if (self::programExists('open')) { + } elseif (self::programExists('open')) { $browser = 'open'; - } else if (self::programExists('start')) { + } elseif (self::programExists('start')) { $browser = 'start'; } else { // Can't find a valid browser. @@ -99,9 +100,9 @@ public static function programExists($program) return $process->isSuccessful(); } - public static function getEditor() + public static function getEditor(?string $editor) { // See http://drupal.org/node/1740294 - return '${VISUAL-${EDITOR-vi}} %s'; + return $editor ? "$editor %s" : '${VISUAL-${EDITOR-vi}} %s'; } } diff --git a/src/Formatters/DrushFormatterManager.php b/src/Formatters/DrushFormatterManager.php index 04e2ef7a75..eceffc9618 100644 --- a/src/Formatters/DrushFormatterManager.php +++ b/src/Formatters/DrushFormatterManager.php @@ -7,7 +7,7 @@ namespace Drush\Formatters; -use \Consolidation\OutputFormatters\FormatterManager; +use Consolidation\OutputFormatters\FormatterManager; /** * Our own output formatter diff --git a/src/Log/DrushLog.php b/src/Log/DrushLog.php index 7a3398dde1..12240d7e2f 100644 --- a/src/Log/DrushLog.php +++ b/src/Log/DrushLog.php @@ -16,6 +16,7 @@ namespace Drush\Log; +use Robo\Robo; use Drupal\Core\Logger\LogMessageParserInterface; use Drupal\Core\Logger\RfcLoggerTrait; use Drupal\Core\Logger\RfcLogLevel; @@ -41,14 +42,14 @@ class DrushLog implements LoggerInterface, LoggerAwareInterface /** * The message's placeholders parser. * - * @var \Drupal\Core\Logger\LogMessageParserInterface + * @var LogMessageParserInterface */ protected $parser; /** * Constructs a DrushLog object. * - * @param \Drupal\Core\Logger\LogMessageParserInterface $parser + * @param LogMessageParserInterface $parser * The parser to use when extracting message variables. */ public function __construct(LogMessageParserInterface $parser) @@ -59,10 +60,10 @@ public function __construct(LogMessageParserInterface $parser) /** * {@inheritdoc} */ - public function log($level, $message, array $context = []) + public function log($level, $message, array $context = []): void { // Only log during Drush requests, not web requests. - if (!\Robo\Robo::hasContainer()) { + if (!Robo::hasContainer()) { return; } diff --git a/src/Log/DrushLoggerManager.php b/src/Log/DrushLoggerManager.php new file mode 100644 index 0000000000..bb7a8422de --- /dev/null +++ b/src/Log/DrushLoggerManager.php @@ -0,0 +1,14 @@ +log(ConsoleLogLevel::SUCCESS, $message, $context); + } +} diff --git a/src/Log/Logger.php b/src/Log/Logger.php index c508bcfbd7..9078c7598b 100644 --- a/src/Log/Logger.php +++ b/src/Log/Logger.php @@ -9,11 +9,6 @@ * * This logger is designed such that it can be provided to * other libraries that log to a Psr\Log\LoggerInterface. - * As such, it takes responsibility for passing log messages - * to backend invoke, as necessary (c.f. drush_backend_packet()). - * - * Drush supports all of the required log levels from Psr\Log\LogLevel, - * and also defines its own. See Drush\Log\LogLevel. * * Those who may wish to change the way logging works in Drush * should therefore NOT attempt to replace this logger with their @@ -31,13 +26,12 @@ class Logger extends RoboLogger { - public function __construct(OutputInterface $output) { parent::__construct($output); } - public function log($level, $message, array $context = []) + public function log($level, $message, array $context = []): void { // Append timer and memory values. if (Drush::debug()) { @@ -57,7 +51,7 @@ public static function formatSize($size) // format_plural() not always available. return dt('@count bytes', ['@count' => $size]); } else { - $size = $size / DRUSH_KILOBYTE; // Convert bytes to kilobytes. + $size /= DRUSH_KILOBYTE; // Convert bytes to kilobytes. $units = [ dt('@size KB', []), dt('@size MB', []), @@ -70,7 +64,7 @@ public static function formatSize($size) ]; foreach ($units as $unit) { if (round($size, 2) >= DRUSH_KILOBYTE) { - $size = $size / DRUSH_KILOBYTE; + $size /= DRUSH_KILOBYTE; } else { break; } diff --git a/src/Log/SuccessInterface.php b/src/Log/SuccessInterface.php new file mode 100644 index 0000000000..ed3cab36e4 --- /dev/null +++ b/src/Log/SuccessInterface.php @@ -0,0 +1,11 @@ +specParser = new SiteSpecParser(); } - public function setArgsRemapper(ArgsRemapper $remapper) + public function setArgsRemapper(ArgsRemapper $remapper): void { $this->remapper = $remapper; } @@ -38,11 +39,10 @@ public function setArgsRemapper(ArgsRemapper $remapper) * @param string[] $argv * Commandline arguments. The first element is * the path to the application, which we will ignore. - * @param PreflightArgsInterface $storage * A storage object to hold the arguments we remove * from argv, plus the remaining argv arguments. */ - public function parse($argv, PreflightArgsInterface $storage) + public function parse(array $argv, PreflightArgsInterface $storage) { $sawArg = false; @@ -94,7 +94,7 @@ public function parse($argv, PreflightArgsInterface $storage) * nextCouldBeValue determines whether there is a next argument that * exists and does not begin with a `-`. */ - protected static function nextCouldBeValue($argv) + protected static function nextCouldBeValue($argv): bool { if (empty($argv)) { return false; @@ -108,9 +108,8 @@ protected static function nextCouldBeValue($argv) * * @param string $arg * Argument to test. - * @return bool */ - protected function isAliasOrSiteSpec($arg) + protected function isAliasOrSiteSpec(string $arg): bool { if (SiteAliasName::isAliasName($arg)) { return true; @@ -127,7 +126,7 @@ protected function isAliasOrSiteSpec($arg) * @param $opt The option string to check * @return [$methodName, $optionValue, $acceptsValueFromNextArg] */ - protected function findMethodForOptionWithValues($optionsTable, $opt) + protected function findMethodForOptionWithValues($optionsTable, $opt): array { // Skip $opt if it is empty, or if it is not an option. if (empty($opt) || ($opt[0] != '-')) { @@ -157,7 +156,7 @@ protected function findMethodForOptionWithValues($optionsTable, $opt) * to 'true'. * @return [$methodName, $optionValue, $acceptsValueFromNextArg] */ - protected function checkMatchingOption($opt, $keyParam, $methodName) + protected function checkMatchingOption($opt, $keyParam, $methodName): array { // Test to see if $key ends in '='; remove the character if present. // If the char is removed, it means the option accepts a value. @@ -166,7 +165,7 @@ protected function checkMatchingOption($opt, $keyParam, $methodName) $acceptsValueFromNextArg = $keyParam[strlen($keyParam) - 1] != '~'; // If $opt does not begin with $key, then it cannot be a match. - if ($key != substr($opt, 0, strlen($key))) { + if ($key !== substr($opt, 0, strlen($key))) { return [false, false, false]; } @@ -175,8 +174,8 @@ protected function checkMatchingOption($opt, $keyParam, $methodName) // a value; in this case, the value will be provided from the next // argument in the calling function. If this option does not take a // supplied value, then we set its value to 'true' - if (strlen($key) == strlen($opt)) { - return [$methodName, $acceptsValue ? null: true, $acceptsValueFromNextArg]; + if (strlen($key) === strlen($opt)) { + return [$methodName, $acceptsValue ? null : true, $acceptsValueFromNextArg]; } // If the option is not an exact match for the key, then the next diff --git a/src/Preflight/ArgsRemapper.php b/src/Preflight/ArgsRemapper.php index d5abca8452..a12d3d9502 100644 --- a/src/Preflight/ArgsRemapper.php +++ b/src/Preflight/ArgsRemapper.php @@ -1,4 +1,5 @@ logger = $preflightLog ?: new PreflightLog(); } - /** - * @return PreflightLog - */ - public function logger() + public function logger(): PreflightLog { return $this->logger; } @@ -76,7 +78,7 @@ public function logger() /** * @param PreflightLog $logger */ - public function setLogger(PreflightLog $logger) + public function setLogger(PreflightLog $logger): void { $this->logger = $logger; } @@ -85,7 +87,7 @@ public function setLogger(PreflightLog $logger) * Perform preliminary initialization. This mostly involves setting up * legacy systems. */ - public function init() + public function init(): void { // Define legacy constants, and include legacy files that Drush still needs LegacyPreflight::includeCode($this->environment->drushBasePath()); @@ -111,7 +113,7 @@ public function init() * Eventually, we might want to expose this table to some form of * 'help' output, so folks can see the available conversions. */ - protected function remapOptions() + protected function remapOptions(): array { return [ '--ssh-options' => '-Dssh.options', @@ -135,7 +137,7 @@ protected function remapOptions() * * This should be fixed in Symfony Console. */ - protected function remapCommandAliases() + protected function remapCommandAliases(): array { return [ 'si' => 'site:install', @@ -150,7 +152,7 @@ protected function remapCommandAliases() * Arguments and options not used during preflight will be processed * with an ArgvInput. */ - public function preflightArgs($argv) + public function preflightArgs($argv): PreflightArgs { $argProcessor = new ArgsPreprocessor(); $remapper = new ArgsRemapper($this->remapOptions(), $this->remapCommandAliases()); @@ -167,7 +169,7 @@ public function preflightArgs($argv) * Create the initial config locator object, and inject any needed * settings, paths and so on into it. */ - public function prepareConfig(Environment $environment) + public function prepareConfig(Environment $environment): void { // Make our environment settings available as configuration items $this->configLocator->addEnvironment($environment); @@ -176,12 +178,12 @@ public function prepareConfig(Environment $environment) $this->configLocator->addDrushConfig($environment->drushBasePath()); } - public function createInput() + public function createInput(): InputInterface { return $this->preflightArgs->createInput(); } - public function getCommandFilePaths() + public function getCommandFilePaths(): array { $commandlinePaths = $this->preflightArgs->commandPaths(); $configPaths = $this->config()->get('drush.include', []); @@ -192,22 +194,21 @@ public function getCommandFilePaths() return $this->configLocator->getCommandFilePaths(array_merge($commandlinePaths, $configPaths), $this->drupalFinder()->getDrupalRoot()); } - public function loadSiteAutoloader() + public function loadSiteAutoloader(): ClassLoader { return $this->environment()->loadSiteAutoloader($this->drupalFinder()->getDrupalRoot()); } - public function config() + public function config(): DrushConfig { return $this->configLocator->config(); } /** * @param $argv - * @return bool * True if the request was successfully redispatched remotely. False if the request should proceed. */ - public function preflight($argv) + public function preflight($argv): bool { // Fail fast if there is anything in our environment that does not check out $this->verify->verify($this->environment); @@ -217,7 +218,7 @@ public function preflight($argv) $this->prepareConfig($this->environment); // Now that we know the value, set debug flag. - $this->logger()->setDebug($this->preflightArgs->get(PreflightArgs::DEBUG)); + $this->logger()->setDebug($this->preflightArgs->get(PreflightArgs::DEBUG, false)); // Do legacy initialization (load static includes, define old constants, etc.) $this->init(); @@ -240,7 +241,7 @@ public function preflight($argv) $paths = $this->configLocator->getSiteAliasPaths($this->preflightArgs->aliasPaths(), $this->environment); // Configure alias manager. - $aliasFileLoader = new \Drush\SiteAlias\SiteAliasFileLoader(); + $aliasFileLoader = new SiteAliasFileLoader(); $this->aliasManager = (new SiteAliasManager($aliasFileLoader))->addSearchLocations($paths); $this->aliasManager->setReferenceData($config->export()); @@ -266,7 +267,7 @@ public function preflight($argv) // NOTE: termination handlers have not been set yet, so it is okay // to exit early without taking special action. $status = RedispatchToSiteLocal::redispatchIfSiteLocalDrush($argv, $root, $this->environment->vendorPath(), $this->logger()); - if ($status !== false) { + if ($status) { return $status; } @@ -324,7 +325,7 @@ protected function findSelectedSite() * @param string $selectedRoot The location to being searching for a site * @param string|bool $fallbackPath The secondary location to search (usualy the vendor director) */ - protected function setSelectedSite($selectedRoot, $fallbackPath = false, $originalSelection = null) + protected function setSelectedSite(string $selectedRoot, $fallbackPath = false, $originalSelection = null) { if ($selectedRoot || $fallbackPath) { $foundRoot = $this->drupalFinder->locateRoot($selectedRoot); @@ -347,30 +348,24 @@ protected function setSelectedSite($selectedRoot, $fallbackPath = false, $origin /** * Return the Drupal Finder - * - * @return DrupalFinder */ - public function drupalFinder() + public function drupalFinder(): DrupalFinder { return $this->drupalFinder; } /** * Return the alias manager - * - * @return SiteAliasManager */ - public function aliasManager() + public function aliasManager(): SiteAliasManager { return $this->aliasManager; } /** * Return the environment - * - * @return Environment */ - public function environment() + public function environment(): Environment { return $this->environment; } diff --git a/src/Preflight/PreflightArgs.php b/src/Preflight/PreflightArgs.php index 52aeeeec1d..2654295dfc 100644 --- a/src/Preflight/PreflightArgs.php +++ b/src/Preflight/PreflightArgs.php @@ -1,9 +1,10 @@ homeDir; } - /** - * @param string $homeDir - */ - public function setHomeDir($homeDir) + public function setHomeDir(string $homeDir): void { $this->homeDir = $homeDir; } const DRUSH_CONFIG_PATH_NAMESPACE = 'drush.paths'; + const DRUSH_RUNTIME_CONTEXT_NAMESPACE = 'runtime.contxt'; + const ALIAS = 'alias'; + const ALIAS_PATH = 'alias-path'; + const COMMAND_PATH = 'include'; + const CONFIG_PATH = 'config'; + const COVERAGE_FILE = 'coverage-file'; + const LOCAL = 'local'; + const ROOT = 'root'; + const URI = 'uri'; + const SIMULATE = 'simulate'; - const BACKEND = 'backend'; + const STRICT = 'strict'; + const DEBUG = 'preflight-debug'; /** @@ -75,14 +81,13 @@ public function __construct($data = []) /** * @inheritdoc */ - public function optionsWithValues() + public function optionsWithValues(): array { return [ '-r=' => 'setSelectedSite', '--root=' => 'setSelectedSite', '--debug' => 'setDebug', '-d' => 'setDebug', - '-vv' => 'setDebug', '-vvv' => 'setDebug', '-l=' => 'setUri', '--uri=' => 'setUri', @@ -93,7 +98,6 @@ public function optionsWithValues() '--local' => 'setLocal', '--simulate' => 'setSimulate', '-s' => 'setSimulate', - '--backend=' => 'setBackend', '--drush-coverage=' => 'setCoverageFile', '--strict=' => 'setStrict', '--help' => 'adjustHelpOption', @@ -106,7 +110,7 @@ public function optionsWithValues() * option away and add a 'help' command to the beginning * of the argument list. */ - public function adjustHelpOption() + public function adjustHelpOption(): void { $drushPath = array_shift($this->args); array_unshift($this->args, $drushPath, 'help'); @@ -117,12 +121,11 @@ public function adjustHelpOption() * preflight option in. The values of the config items in this map * must be BOOLEANS or STRINGS. */ - protected function optionConfigMap() + protected function optionConfigMap(): array { return [ - self::SIMULATE => \Robo\Config\Config::SIMULATE, - self::BACKEND => self::BACKEND, - self::LOCAL => self::DRUSH_RUNTIME_CONTEXT_NAMESPACE . '.' . self::LOCAL, + self::SIMULATE => \Robo\Config\Config::SIMULATE, + self::LOCAL => self::DRUSH_RUNTIME_CONTEXT_NAMESPACE . '.' . self::LOCAL, ]; } @@ -131,12 +134,12 @@ protected function optionConfigMap() * preflight option in. The values of the items in this map must be * STRINGS or ARRAYS OF STRINGS. */ - protected function optionConfigPathMap() + protected function optionConfigPathMap(): array { return [ - self::ALIAS_PATH => self::DRUSH_CONFIG_PATH_NAMESPACE . '.' . self::ALIAS_PATH, - self::CONFIG_PATH => self::DRUSH_CONFIG_PATH_NAMESPACE . '.' . self::CONFIG_PATH, - self::COMMAND_PATH => self::DRUSH_CONFIG_PATH_NAMESPACE . '.' . self::COMMAND_PATH, + self::ALIAS_PATH => self::DRUSH_CONFIG_PATH_NAMESPACE . '.' . self::ALIAS_PATH, + self::CONFIG_PATH => self::DRUSH_CONFIG_PATH_NAMESPACE . '.' . self::CONFIG_PATH, + self::COMMAND_PATH => self::DRUSH_CONFIG_PATH_NAMESPACE . '.' . self::COMMAND_PATH, ]; } @@ -145,7 +148,7 @@ protected function optionConfigPathMap() * * @see Environment::exportConfigData(), which also exports information to config. */ - public function applyToConfig(ConfigInterface $config) + public function applyToConfig(ConfigInterface $config): void { // Copy the relevant preflight options to the applicable configuration namespace foreach ($this->optionConfigMap() as $option_key => $config_key) { @@ -154,7 +157,7 @@ public function applyToConfig(ConfigInterface $config) // Merging as they are lists. foreach ($this->optionConfigPathMap() as $option_key => $config_key) { $cli_paths = $this->get($option_key, []); - $config_paths = (array) $config->get($config_key, []); + $config_paths = (array)$config->get($config_key, []); $merged_paths = array_unique(array_merge($cli_paths, $config_paths)); $config->set($config_key, $merged_paths); @@ -172,7 +175,7 @@ public function applyToConfig(ConfigInterface $config) /** * @inheritdoc */ - public function args() + public function args(): array { return $this->args; } @@ -196,14 +199,15 @@ public function commandName() /** * @inheritdoc */ - public function setCommandName($commandName) + public function setCommandName($commandName): void { $this->commandName = $commandName; } + /** * @inheritdoc */ - public function addArg($arg) + public function addArg($arg): self { $this->args[] = $arg; return $this; @@ -212,7 +216,7 @@ public function addArg($arg) /** * @inheritdoc */ - public function passArgs($args) + public function passArgs($args): self { $this->args = array_merge($this->args, $args); return $this; @@ -229,7 +233,7 @@ public function alias() /** * @inheritdoc */ - public function hasAlias() + public function hasAlias(): bool { return $this->has(self::ALIAS); } @@ -237,7 +241,7 @@ public function hasAlias() /** * @inheritdoc */ - public function setAlias($alias) + public function setAlias($alias): self { // Treat `drush @self ...` as if an alias had not been used at all. if ($alias == '@self') { @@ -254,7 +258,7 @@ public function selectedSite($default = false) return $this->get(self::ROOT, $default); } - public function setDebug($value) + public function setDebug($value): void { $this->set(self::DEBUG, $value); $this->addArg('-vvv'); @@ -263,7 +267,7 @@ public function setDebug($value) /** * Set the selected site. */ - public function setSelectedSite($root) + public function setSelectedSite($root): self { return $this->set(self::ROOT, StringUtils::replaceTilde($root, $this->homeDir())); } @@ -276,7 +280,7 @@ public function uri($default = false) return $this->get(self::URI, $default); } - public function hasUri() + public function hasUri(): bool { return $this->has(self::URI); } @@ -284,7 +288,7 @@ public function hasUri() /** * Set the uri option */ - public function setUri($uri) + public function setUri($uri): self { return $this->set(self::URI, $uri); } @@ -299,10 +303,8 @@ public function configPaths() /** * Add another location where drush.yml files may be found - * - * @param string $path */ - public function addConfigPath($path) + public function addConfigPath(string $path): self { $paths = $this->configPaths(); $paths[] = StringUtils::replaceTilde($path, $this->homeDir()); @@ -314,7 +316,7 @@ public function addConfigPath($path) * * @param string[] $configPaths */ - public function mergeConfigPaths($configPaths) + public function mergeConfigPaths(array $configPaths): self { $paths = $this->configPaths(); $merged_paths = array_merge($paths, $configPaths); @@ -331,10 +333,8 @@ public function aliasPaths() /** * Set one more path where aliases may be found. - * - * @param string $path */ - public function addAliasPath($path) + public function addAliasPath(string $path): self { $paths = $this->aliasPaths(); $paths[] = StringUtils::replaceTilde($path, $this->homeDir()); @@ -343,10 +343,8 @@ public function addAliasPath($path) /** * Add multiple additional locations for alias paths. - * - * @param string $aliasPaths */ - public function mergeAliasPaths($aliasPaths) + public function mergeAliasPaths(string $aliasPaths): self { $aliasPaths = array_map( function ($item) { @@ -369,10 +367,8 @@ public function commandPaths() /** * Add one more path where commandfiles might be found. - * - * @param string $path */ - public function addCommandPath($path) + public function addCommandPath(string $path): self { $paths = $this->commandPaths(); $paths[] = StringUtils::replaceTilde($path, $this->homeDir()); @@ -384,7 +380,7 @@ public function addCommandPath($path) * * @param $commanPaths */ - public function mergeCommandPaths($commandPaths) + public function mergeCommandPaths($commandPaths): self { $paths = $this->commandPaths(); $merged_paths = array_merge($paths, $commandPaths); @@ -396,15 +392,13 @@ public function mergeCommandPaths($commandPaths) */ public function isLocal() { - return $this->get(self::LOCAL); + return $this->get(self::LOCAL, false); } /** * Set local mode - * - * @param bool $isLocal */ - public function setLocal($isLocal) + public function setLocal(bool $isLocal): self { return $this->set(self::LOCAL, $isLocal); } @@ -422,34 +416,11 @@ public function isSimulated() * * @param bool $simulated */ - public function setSimulate($simulate) + public function setSimulate($simulate): self { return $this->set(self::SIMULATE, $simulate); } - /** - * Determine whether Drush was placed in simulated mode. - */ - public function isBackend() - { - return $this->get(self::BACKEND); - } - - /** - * Set backend mode - * - * @param bool $backend - */ - public function setBackend($backend) - { - if ($backend == 'json') { - // Remap to --format. See \Drush\Commands\sql\SqlSyncCommands::dump. - $this->addArg('--format=json'); - } else { - return $this->set(self::BACKEND, true); - } - } - /** * Get the path to the coverage file. */ @@ -463,7 +434,7 @@ public function coverageFile() * * @param string */ - public function setCoverageFile($coverageFile) + public function setCoverageFile($coverageFile): self { return $this->set(self::COVERAGE_FILE, StringUtils::replaceTilde($coverageFile, $this->homeDir())); } @@ -478,10 +449,8 @@ public function isStrict() /** * Set strict mode. - * - * @param bool $strict */ - public function setStrict($strict) + public function setStrict(bool $strict): self { return $this->set(self::STRICT, $strict); } @@ -491,9 +460,10 @@ public function setStrict($strict) * just the option name of any item that is an option. * * @param array $argv e.g. ['foo', '--bar=baz', 'boz'] + * * @return string[] e.g. ['bar'] */ - protected function getOptionNameList($argv) + protected function getOptionNameList(array $argv): array { return array_filter( array_map( @@ -517,85 +487,13 @@ function ($item) { /** * Create a Symfony Input object. */ - public function createInput() + public function createInput(): InputInterface { // In strict mode (the default), create an ArgvInput. When // strict mode is disabled, create a more forgiving input object. - if ($this->isStrict() && !$this->isBackend()) { + if ($this->isStrict()) { return new DrushArgvInput($this->args()); } - - // If in backend mode, read additional options from stdin. - // TODO: Maybe reading stdin options should be the responsibility of some - // backend manager class? Could be called from preflight and injected here. - $input = new LessStrictArgvInput($this->args()); - $input->injectAdditionalOptions($this->readStdinOptions()); - - return $input; - } - - /** - * Read options fron STDIN during POST requests. - * - * This function will read any text from the STDIN pipe, - * and attempts to generate an associative array if valid - * JSON was received. - * - * @return - * An associative array of options, if successfull. Otherwise an empty array. - */ - protected function readStdinOptions() - { - // If we move this method to a backend manager, then testing for - // backend mode will be the responsibility of the caller. - if (!$this->isBackend()) { - return []; - } - - $fp = fopen('php://stdin', 'r'); - // Windows workaround: we cannot count on stream_get_contents to - // return if STDIN is reading from the keyboard. We will therefore - // check to see if there are already characters waiting on the - // stream (as there always should be, if this is a backend call), - // and if there are not, then we will exit. - // This code prevents drush from hanging forever when called with - // --backend from the commandline; however, overall it is still - // a futile effort, as it does not seem that backend invoke can - // successfully write data to that this function can read, - // so the argument list and command always come out empty. :( - // Perhaps stream_get_contents is the problem, and we should use - // the technique described here: - // http://bugs.php.net/bug.php?id=30154 - // n.b. the code in that issue passes '0' for the timeout in stream_select - // in a loop, which is not recommended. - // Note that the following DOES work: - // drush ev 'print(json_encode(array("test" => "XYZZY")));' | drush status --backend - // So, redirecting input is okay, it is just the proc_open that is a problem. - if (drush_is_windows()) { - // Note that stream_select uses reference parameters, so we need variables (can't pass a constant NULL) - $read = [$fp]; - $write = null; - $except = null; - // Question: might we need to wait a bit for STDIN to be ready, - // even if the process that called us immediately writes our parameters? - // Passing '100' for the timeout here causes us to hang indefinitely - // when called from the shell. - $changed_streams = stream_select($read, $write, $except, 0); - // Return on error or no changed streams (0). - // Oh, according to http://php.net/manual/en/function.stream-select.php, - // stream_select will return FALSE for streams returned by proc_open. - // That is not applicable to us, is it? Our stream is connected to a stream - // created by proc_open, but is not a stream returned by proc_open. - if ($changed_streams < 1) { - return []; - } - } - stream_set_blocking($fp, false); - $string = stream_get_contents($fp); - fclose($fp); - if (trim($string)) { - return json_decode($string, true); - } - return []; + return new LessStrictArgvInput($this->args()); } } diff --git a/src/Preflight/PreflightArgsInterface.php b/src/Preflight/PreflightArgsInterface.php index 73e2415df7..ac56162d6b 100644 --- a/src/Preflight/PreflightArgsInterface.php +++ b/src/Preflight/PreflightArgsInterface.php @@ -1,4 +1,5 @@ output = $output ?: new StreamOutput(fopen('php://stderr', 'w')); } - /** - * @return bool - */ - public function getDebug() + public function getDebug(): ?bool { return $this->debug; } - /** - * @param bool $debug - */ - public function setDebug($debug) + public function setDebug(bool $debug): self { $this->debug = $debug; return $this; } - public function log($message) + public function log($message): void { if ($this->getDebug()) { $this->output->write(' [preflight] ' . $message . "\n"); diff --git a/src/Preflight/PreflightSiteLocator.php b/src/Preflight/PreflightSiteLocator.php index 5a2e1f2233..9d281a0401 100644 --- a/src/Preflight/PreflightSiteLocator.php +++ b/src/Preflight/PreflightSiteLocator.php @@ -29,12 +29,12 @@ public function __construct(SiteAliasManager $siteAliasManager) * provided on the commandline that is either missing or invalid. * * @param PreflightArgsInterface $preflightArgs An alias name or site specification - * @param \Drush\Config\Environment $environment + * @param Environment $environment * @param string $root The default Drupal root (from site:set, --root or cwd) * - * @return \Consolidation\SiteAlias\SiteAlias|false + * @return SiteAlias|false */ - public function findSite(PreflightArgsInterface $preflightArgs, Environment $environment, $root) + public function findSite(PreflightArgsInterface $preflightArgs, Environment $environment, string $root): SiteAlias { $self = $this->determineSelf($preflightArgs, $environment, $root); @@ -52,13 +52,11 @@ public function findSite(PreflightArgsInterface $preflightArgs, Environment $env * or, if those are invalid, then generate one from * the provided root and URI. * - * @param \Drush\Preflight\PreflightArgsInterface $preflightArgs - * @param \Drush\Config\Environment $environment + * @param PreflightArgsInterface $preflightArgs + * @param Environment $environment * @param $root - * - * @return \Consolidation\SiteAlias\SiteAlias */ - protected function determineSelf(PreflightArgsInterface $preflightArgs, Environment $environment, $root) + protected function determineSelf(PreflightArgsInterface $preflightArgs, Environment $environment, $root): SiteAlias { if ($preflightArgs->hasAlias()) { $aliasName = $preflightArgs->alias(); @@ -96,12 +94,10 @@ protected function determineSelf(PreflightArgsInterface $preflightArgs, Environm /** * Generate @self from the provided root and URI. * - * @param \Drush\Preflight\PreflightArgsInterface $preflightArgs + * @param PreflightArgsInterface $preflightArgs * @param $root - * - * @return \Consolidation\SiteAlias\SiteAlias */ - protected function buildSelf(PreflightArgsInterface $preflightArgs, $root) + protected function buildSelf(PreflightArgsInterface $preflightArgs, $root): SiteAlias { // If there is no root, then return '@none' if (!$root) { diff --git a/src/Preflight/PreflightVerify.php b/src/Preflight/PreflightVerify.php index 5948826081..60f05f310c 100644 --- a/src/Preflight/PreflightVerify.php +++ b/src/Preflight/PreflightVerify.php @@ -1,4 +1,5 @@ $minimumPhpVersion])); @@ -47,7 +48,7 @@ public function confirmPhpVersion($minimumPhpVersion) * * @param Environment $environment */ - protected function confirmUsingCLI(Environment $environment) + protected function confirmUsingCLI(Environment $environment): void { if (!$environment->verifyCLI()) { throw new \Exception(StringUtils::interpolate('Drush is designed to run via the command line.')); @@ -59,7 +60,7 @@ protected function confirmUsingCLI(Environment $environment) * begins. If the php environment is too restrictive, then * notify the user that a setting change is needed and abort. */ - protected function checkPhpIni() + protected function checkPhpIni(): void { $ini_checks = ['safe_mode' => '', 'open_basedir' => '']; @@ -84,9 +85,8 @@ protected function checkPhpIni() * @param string|string[] $disallowed_value * The value that the ini seting cannot be, or a list of disallowed * values that cannot appear in the setting. - * @return bool */ - protected function invalidIniValue($ini_value, $disallowed_value) + protected function invalidIniValue(string $ini_value, $disallowed_value): bool { if (empty($disallowed_value)) { return !empty($ini_value) && (strcasecmp($ini_value, 'off') != 0); diff --git a/src/Preflight/RedispatchToSiteLocal.php b/src/Preflight/RedispatchToSiteLocal.php index 1e63591942..7a15dd4e62 100644 --- a/src/Preflight/RedispatchToSiteLocal.php +++ b/src/Preflight/RedispatchToSiteLocal.php @@ -13,7 +13,6 @@ */ class RedispatchToSiteLocal { - /** * Determine if a local redispatch is needed, and do so if it is. * @@ -25,7 +24,7 @@ class RedispatchToSiteLocal * @return bool * True if redispatch occurred, and was returned successfully. */ - public static function redispatchIfSiteLocalDrush($argv, $root, $vendor, PreflightLog $preflightLog) + public static function redispatchIfSiteLocalDrush(array $argv, string $root, string $vendor, PreflightLog $preflightLog) { // Try to find the site-local Drush. If there is none, we are done. @@ -41,7 +40,7 @@ public static function redispatchIfSiteLocalDrush($argv, $root, $vendor, Preflig // Do another special check to detect symlinked Drush folder similar // to what the SUT sets up for Drush functional tests. - if (dirname($vendor) == dirname($siteLocalDrush)) { + if (dirname($vendor) === dirname($siteLocalDrush)) { return false; } @@ -64,9 +63,9 @@ function ($item) { * Find a site-local Drush, if there is one in the selected site's * vendor directory. * - * @param string $root The selected site root + * @param $root The selected site root */ - protected static function findSiteLocalDrush($root) + protected static function findSiteLocalDrush(string $root) { $candidates = [ "$root/vendor/drush/drush/drush", diff --git a/src/Psysh/Caster.php b/src/Psysh/Caster.php index 7e806b5aa5..f434b71e23 100644 --- a/src/Psysh/Caster.php +++ b/src/Psysh/Caster.php @@ -14,7 +14,6 @@ */ class Caster { - /** * Casts \Drupal\Core\Entity\ContentEntityInterface classes. */ diff --git a/src/Psysh/DrushCommand.php b/src/Psysh/DrushCommand.php index dbd3f63954..f0f36c35e7 100644 --- a/src/Psysh/DrushCommand.php +++ b/src/Psysh/DrushCommand.php @@ -1,4 +1,5 @@ getName()); return count($parts) >= 2 ? array_shift($parts) : 'global'; @@ -52,7 +52,7 @@ public function getNamespace() /** * {@inheritdoc} */ - protected function configure() + protected function configure(): void { $this ->setName($this->command->getName()) @@ -65,7 +65,7 @@ protected function configure() /** * {@inheritdoc} */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): void { $args = $input->getArguments(); $first = array_shift($args); @@ -100,10 +100,9 @@ protected function execute(InputInterface $input, OutputInterface $output) * * Currently it's a word-wrapped description, plus any examples provided. * - * @return string * The help string. */ - protected function buildHelpFromCommand() + protected function buildHelpFromCommand(): string { $help = wordwrap($this->command->getDescription()); diff --git a/src/Psysh/DrushHelpCommand.php b/src/Psysh/DrushHelpCommand.php index fb3f0a11c3..8510daff02 100644 --- a/src/Psysh/DrushHelpCommand.php +++ b/src/Psysh/DrushHelpCommand.php @@ -23,7 +23,6 @@ */ class DrushHelpCommand extends BaseCommand { - /** * Label for PsySH commands. */ @@ -39,7 +38,7 @@ class DrushHelpCommand extends BaseCommand /** * {@inheritdoc} */ - protected function configure() + protected function configure(): void { $this ->setName('help') @@ -55,7 +54,7 @@ protected function configure() * * @param \Symfony\Component\Console\Command\Command $command */ - public function setCommand(Command $command) + public function setCommand(Command $command): void { $this->command = $command; } @@ -63,7 +62,7 @@ public function setCommand(Command $command) /** * {@inheritdoc} */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): void { if ($this->command !== null) { // Help for an individual command. diff --git a/src/Psysh/Shell.php b/src/Psysh/Shell.php index c495b3d952..a40f280a82 100644 --- a/src/Psysh/Shell.php +++ b/src/Psysh/Shell.php @@ -12,7 +12,6 @@ class Shell extends BaseShell { - /** * Get a command (if one exists) for the current input string. * @@ -52,7 +51,7 @@ protected function hasCommand(string $input): bool * @return string|NULL * The current command. */ - protected function getCommandFromInput(string $input) + protected function getCommandFromInput(string $input): ?string { // Remove the alias from the start of the string before parsing and // returning the command. Essentially, when choosing a command, we're diff --git a/src/Runtime/DependencyInjection.php b/src/Runtime/DependencyInjection.php index b0b068c17f..7d77239f1e 100644 --- a/src/Runtime/DependencyInjection.php +++ b/src/Runtime/DependencyInjection.php @@ -1,6 +1,16 @@ handlers = $handlerList; } @@ -41,23 +51,23 @@ public function initContainer( ClassLoader $loader, DrupalFinder $drupalFinder, SiteAliasManager $aliasManager - ) { + ): Container { // Create default input and output objects if they were not provided if (!$input) { - $input = new \Symfony\Component\Console\Input\StringInput(''); + $input = new StringInput(''); } if (!$output) { - $output = new \Symfony\Component\Console\Output\ConsoleOutput(); + $output = new ConsoleOutput(); } // Set up our dependency injection container. - $container = new \League\Container\Container(); + $container = new Container(); // With league/container 3.x, first call wins, so add Drush services first. - $this->addDrushServices($container, $loader, $drupalFinder, $aliasManager, $config); + $this->addDrushServices($container, $loader, $drupalFinder, $aliasManager, $config, $output); // Robo has the same signature for configureContainer in 1.x, 2.x and 3.x. - \Robo\Robo::configureContainer($container, $application, $config, $input, $output); + Robo::configureContainer($container, $application, $config, $input, $output); $container->add('container', $container); // Store the container in the \Drush object @@ -75,7 +85,7 @@ public function initContainer( /** * Make sure we are notified on exit, and when bad things happen. */ - public function installHandlers($container) + public function installHandlers($container): void { foreach ($this->handlers as $handlerId) { $handler = $container->get($handlerId); @@ -84,12 +94,12 @@ public function installHandlers($container) } // Add Drush Services to league/container 3.x - protected function addDrushServices($container, ClassLoader $loader, DrupalFinder $drupalFinder, SiteAliasManager $aliasManager, DrushConfig $config) + protected function addDrushServices($container, ClassLoader $loader, DrupalFinder $drupalFinder, SiteAliasManager $aliasManager, DrushConfig $config, OutputInterface $output): void { - // Override Robo's logger with our own - $container->share('logger', 'Drush\Log\Logger') - ->addArgument('output') - ->addMethodCall('setLogOutputStyler', ['logStyler']); + // Override Robo's logger with a LoggerManager that delegates to the Drush logger. + $container->share('logger', '\Drush\Log\DrushLoggerManager') + ->addMethodCall('setLogOutputStyler', ['logStyler']) + ->addMethodCall('add', ['drush', new Logger($output)]); $container->share('loader', $loader); $container->share('site.alias.manager', $aliasManager); @@ -100,7 +110,7 @@ protected function addDrushServices($container, ClassLoader $loader, DrupalFinde // Override Robo's formatter manager with our own // @todo not sure that we'll use this. Maybe remove it. - $container->share('formatterManager', \Drush\Formatters\DrushFormatterManager::class) + $container->share('formatterManager', DrushFormatterManager::class) ->addMethodCall('addDefaultFormatters', []) ->addMethodCall('addDefaultSimplifiers', []); @@ -133,15 +143,15 @@ protected function addDrushServices($container, ClassLoader $loader, DrupalFinde $container->share('shutdownHandler', 'Drush\Runtime\ShutdownHandler'); // Add inflectors. @see \Drush\Boot\BaseBoot::inflect - $container->inflector(\Drush\Boot\AutoloaderAwareInterface::class) + $container->inflector(AutoloaderAwareInterface::class) ->invokeMethod('setAutoloader', ['loader']); - $container->inflector(\Consolidation\SiteAlias\SiteAliasManagerAwareInterface::class) + $container->inflector(SiteAliasManagerAwareInterface::class) ->invokeMethod('setSiteAliasManager', ['site.alias.manager']); - $container->inflector(\Consolidation\SiteProcess\ProcessManagerAwareInterface::class) + $container->inflector(ProcessManagerAwareInterface::class) ->invokeMethod('setProcessManager', ['process.manager']); } - protected function alterServicesForDrush($container, Application $application) + protected function alterServicesForDrush($container, Application $application): void { $paramInjection = $container->get('parameterInjection'); $paramInjection->register('Symfony\Component\Console\Style\SymfonyStyle', new DrushStyleInjector()); @@ -153,15 +163,9 @@ protected function alterServicesForDrush($container, Application $application) $hookManager->addInitializeHook($container->get('bootstrap.hook')); $hookManager->addPreValidator($container->get('tildeExpansion.hook')); - // Install our command cache into the command factory - // TODO: Create class-based implementation of our cache management functions. - $cacheBackend = _drush_cache_get_object('factory'); - $commandCacheDataStore = new CommandCache($cacheBackend); - $factory = $container->get('commandFactory'); $factory->setIncludeAllPublicMethods(false); $factory->setIgnoreCommandsInTraits(true); - $factory->setDataStore($commandCacheDataStore); $factory->addCommandInfoAlterer(new DrushCommandInfoAlterer()); $commandProcessor = $container->get('commandProcessor'); @@ -170,7 +174,7 @@ protected function alterServicesForDrush($container, Application $application) ProcessManager::addTransports($container->get('process.manager')); } - protected function injectApplicationServices($container, Application $application) + protected function injectApplicationServices($container, Application $application): void { $application->setLogger($container->get('logger')); $application->setBootstrapManager($container->get('bootstrap.manager')); diff --git a/src/Runtime/ErrorHandler.php b/src/Runtime/ErrorHandler.php index fb97eb0c33..9b371e9aca 100644 --- a/src/Runtime/ErrorHandler.php +++ b/src/Runtime/ErrorHandler.php @@ -1,10 +1,6 @@ get('runtime.php.notices', LogLevel::INFO); - $halt_on_error = Drush::config()->get('runtime.php.halt-on-error', (drush_drupal_major_version() != 6)); + $halt_on_error = Drush::config()->get('runtime.php.halt-on-error', true); // Bitmask value that constitutes an error needing to be logged. $error = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR; diff --git a/src/Runtime/HandlerInterface.php b/src/Runtime/HandlerInterface.php index 4da4c96ed6..d4d1696694 100644 --- a/src/Runtime/HandlerInterface.php +++ b/src/Runtime/HandlerInterface.php @@ -1,4 +1,5 @@ debug('Redispatch hook exit early'); diff --git a/src/Runtime/Runtime.php b/src/Runtime/Runtime.php index fede4b422e..438c8e065b 100644 --- a/src/Runtime/Runtime.php +++ b/src/Runtime/Runtime.php @@ -1,6 +1,9 @@ doRun($argv, $output); } catch (\Exception $e) { // Fallback to status 1 if the Exception has not indicated otherwise. @@ -66,7 +69,7 @@ protected function doRun($argv, $output) $status = $this->preflight->preflight($argv); // If preflight signals that we are done, then exit early. - if ($status !== false) { + if ($status) { return $status; } @@ -79,7 +82,7 @@ protected function doRun($argv, $output) // Create the Symfony Application et. al. $input = $this->preflight->createInput(); - $application = new \Drush\Application('Drush Commandline Tool', Drush::getVersion()); + $application = new Application('Drush Commandline Tool', Drush::getVersion()); // Set up the DI container. $container = $this->di->initContainer( @@ -127,7 +130,7 @@ protected function doRun($argv, $output) /** * Mark the current request as having completed successfully. */ - public static function setCompleted() + public static function setCompleted(): void { Drush::config()->set(self::DRUSH_RUNTIME_COMPLETED_NAMESPACE, true); } @@ -139,7 +142,7 @@ public static function setCompleted() * Mark the exit code for current request. * @param int $code */ - public static function setExitCode($code) + public static function setExitCode(int $code): void { Drush::config()->set(self::DRUSH_RUNTIME_EXIT_CODE_NAMESPACE, $code); } diff --git a/src/Runtime/ShutdownHandler.php b/src/Runtime/ShutdownHandler.php index 9b31fa67bc..ef77e9a584 100644 --- a/src/Runtime/ShutdownHandler.php +++ b/src/Runtime/ShutdownHandler.php @@ -1,10 +1,6 @@ input(); $args = $input->getArguments(); @@ -30,7 +30,7 @@ public function validate(CommandData $commandData) foreach ($options as $name => $value) { if (is_string($value)) { $replaced = StringUtils::replaceTilde($value, $this->getConfig()->home()); - if ($value != $replaced) { + if ($value !== $replaced) { $input->setOption($name, $replaced); } } @@ -38,7 +38,7 @@ public function validate(CommandData $commandData) foreach ($args as $name => $value) { if (is_string($value)) { $replaced = StringUtils::replaceTilde($value, $this->getConfig()->home()); - if ($value != $replaced) { + if ($value !== $replaced) { $input->setArgument($name, $replaced); } } diff --git a/src/SiteAlias/HostPath.php b/src/SiteAlias/HostPath.php index 4fc895446c..f8fd452912 100644 --- a/src/SiteAlias/HostPath.php +++ b/src/SiteAlias/HostPath.php @@ -1,4 +1,5 @@ target = ''; } - /** - * @return bool - */ - public function isSimulate() + public function isSimulate(): bool { return $this->simulate; } - /** - * @param bool $simulate - */ - public function setSimulate($simulate) + public function setSimulate(bool $simulate): void { $this->simulate = $simulate; } @@ -71,7 +66,7 @@ public function setSimulate($simulate) * @param string $target * A directory to write to. If not provided, writes go into same dir as the corresponding legacy file. */ - public function setTargetDir($target) + public function setTargetDir(string $target): void { $this->target = $target; } @@ -102,7 +97,7 @@ public function convert() return $convertedFiles; } - protected function checkAnyNeedsConversion($legacyFiles) + protected function checkAnyNeedsConversion($legacyFiles): bool { foreach ($legacyFiles as $legacyFile) { $convertedFile = $this->determineConvertedFilename($legacyFile); @@ -113,7 +108,7 @@ protected function checkAnyNeedsConversion($legacyFiles) return false; } - protected function convertAll($legacyFiles) + protected function convertAll($legacyFiles): array { $result = []; foreach ($legacyFiles as $legacyFile) { @@ -130,7 +125,7 @@ protected function convertAll($legacyFiles) return $result; } - protected function writeAll($convertedFiles) + protected function writeAll($convertedFiles): void { foreach ($convertedFiles as $path => $data) { $contents = $this->getContents($path, $data); @@ -144,26 +139,25 @@ protected function writeAll($convertedFiles) } } - protected function getContents($path, $data) + protected function getContents($path, $data): string { if (!empty($data)) { $indent = 2; - return Yaml::dump($data, PHP_INT_MAX, $indent, false, true); + return Yaml::dump($data, PHP_INT_MAX, $indent, false); } $recoverSource = $this->recoverLegacyFile($path); if (!$recoverSource) { $recoverSource = 'the source alias file'; } - $contents = <<checksumPath($path); if ($this->safeToWrite($path, $contents, $checksumPath)) { @@ -181,7 +175,7 @@ protected function writeOne($path, $contents) * alias file at the specified path. If the user has modified the target * file, then we will not overwrite it. */ - protected function safeToWrite($path, $contents, $checksumPath) + protected function safeToWrite($path, $contents, $checksumPath): bool { // Bail if simulate mode is enabled. if ($this->isSimulate()) { @@ -211,10 +205,10 @@ protected function safeToWrite($path, $contents, $checksumPath) // the checksum we cached in the checksum file, then there has // been no user modification of this file, and it is safe to // overwrite it. - return $previousChecksum == $previousWrittenChecksum; + return $previousChecksum === $previousWrittenChecksum; } - public function saveChecksum($checksumPath, $path, $contents) + public function saveChecksum($checksumPath, $path, $contents): void { $name = basename($path); $comment = <<convertedFileMap[basename($convertedFile)] = basename($legacyFile); } @@ -317,14 +311,14 @@ protected function convertLegacyFile($legacyFile) return $this->convertMultipleAliasesLegacyFile($legacyFile, $aliases, $options); } - protected function convertSingleAliasLegacyFile($legacyFile, $options) + protected function convertSingleAliasLegacyFile($legacyFile, $options): array { $aliasName = basename($legacyFile, '.alias.drushrc.php'); return $this->convertAlias($aliasName, $options, dirname($legacyFile)); } - protected function convertMultipleAliasesLegacyFile($legacyFile, $aliases, $options) + protected function convertMultipleAliasesLegacyFile($legacyFile, $aliases, $options): array { $result = []; foreach ($aliases as $aliasName => $data) { @@ -336,7 +330,7 @@ protected function convertMultipleAliasesLegacyFile($legacyFile, $aliases, $opti return $result; } - protected function convertAlias($aliasName, $data, $dir = '') + protected function convertAlias($aliasName, $data, $dir = ''): array { $env = 'dev'; // We allow $aliasname to be: @@ -358,7 +352,7 @@ protected function convertAlias($aliasName, $data, $dir = '') return $this->convertSingleFileAlias($aliasName, $env, $data, $dir); } - protected function fixSiteData($data) + protected function fixSiteData($data): array { $keyMap = $this->keyConversion(); @@ -391,7 +385,7 @@ protected function fixSiteData($data) return $this->remapData($data); } - protected function remapData($data) + protected function remapData($data): array { $converter = new Data($data); @@ -412,7 +406,7 @@ protected function remapData($data) * Anything NOT identified by the key in the returned array * is moved to the 'options' element. */ - protected function keyConversion() + protected function keyConversion(): array { return [ 'remote-host' => 'host', @@ -432,14 +426,14 @@ protected function keyConversion() * been moved into the 'options' element before this remapping * table is consulted. */ - protected function dataRemap() + protected function dataRemap(): array { return [ 'options.ssh-options' => 'ssh.options', ]; } - protected function removePercentFromKey($data) + protected function removePercentFromKey($data): array { return array_flip( @@ -452,7 +446,7 @@ function ($item) { ); } - protected function convertSingleFileAlias($aliasName, $env, $data, $dir = '') + protected function convertSingleFileAlias($aliasName, $env, $data, $dir = ''): array { $filename = $this->outputFilename($aliasName, '.site.yml', $dir); return [ @@ -479,7 +473,6 @@ protected function outputFilename($name, $extension, $dir = '') * @param array $array1 * @param array $array2 * - * @return array * * @see http://php.net/manual/en/function.array-merge-recursive.php#92195 * @see https://github.com/grasmash/bolt/blob/robo-rebase/src/Robo/Common/ArrayManipulator.php#L22 @@ -487,7 +480,7 @@ protected function outputFilename($name, $extension, $dir = '') protected static function arrayMergeRecursiveDistinct( array &$array1, array &$array2 - ) { + ): array { $merged = $array1; foreach ($array2 as $key => &$value) { $merged[$key] = self::mergeRecursiveValue($merged, $key, $value); diff --git a/src/SiteAlias/ProcessManager.php b/src/SiteAlias/ProcessManager.php index 8af0a2512a..0b90d09f2a 100644 --- a/src/SiteAlias/ProcessManager.php +++ b/src/SiteAlias/ProcessManager.php @@ -1,17 +1,14 @@ drushSiteProcess($siteAlias, $args, $options, $options_double_dash); @@ -39,14 +29,8 @@ public function drush(SiteAliasInterface $siteAlias, $command, $args = [], $opti * drushSiteProcess should be avoided in favor of the drush method above. * drushSiteProcess exists specifically for use by the RedispatchHook, * which does not have specific knowledge about which argument is the command. - * - * @param SiteAliasInterface $siteAlias - * @param array $args - * @param array $options - * @param array $options_double_dash - * @return ProcessBase */ - public function drushSiteProcess(SiteAliasInterface $siteAlias, $args = [], $options = [], $options_double_dash = []) + public function drushSiteProcess(SiteAliasInterface $siteAlias, array $args = [], array $options = [], array $options_double_dash = []): ProcessBase { // Fill in the root and URI from the site alias, if the caller // did not already provide them in $options. @@ -101,7 +85,7 @@ public function drushScript(SiteAliasInterface $siteAlias) * Use Drush::drush() or ProcessManager::drush() instead of this method * when calling Drush. */ - public function siteProcess(SiteAliasInterface $siteAlias, $args = [], $options = [], $optionsPassedAsArgs = []) + public function siteProcess(SiteAliasInterface $siteAlias, $args = [], $options = [], $optionsPassedAsArgs = []): ProcessBase { $process = parent::siteProcess($siteAlias, $args, $options, $optionsPassedAsArgs); return $this->configureProcess($process); @@ -119,10 +103,9 @@ public function siteProcess(SiteAliasInterface $siteAlias, $args = [], $options * @param mixed|null $input The input as stream resource, scalar or \Traversable, or null for no input * @param int|float|null $timeout The timeout in seconds or null to disable * - * @return ProcessBase * A wrapper around Symfony Process. */ - public function process($commandline, $cwd = null, array $env = null, $input = null, $timeout = 60) + public function process($commandline, $cwd = null, array $env = null, $input = null, $timeout = 60): ProcessBase { $process = parent::process($commandline, $cwd, $env, $input, $timeout); return $this->configureProcess($process); @@ -135,9 +118,8 @@ public function process($commandline, $cwd = null, array $env = null, $input = n * @param array|null $env The environment variables or null to use the same environment as the current PHP process * @param mixed|null $input The input as stream resource, scalar or \Traversable, or null for no input * @param int|float|null $timeout The timeout in seconds or null to disable - * @return Process */ - public function shell($command, $cwd = null, array $env = null, $input = null, $timeout = 60) + public function shell($command, $cwd = null, array $env = null, $input = null, $timeout = 60): ProcessBase { $process = parent::shell($command, $cwd, $env, $input, $timeout); return $this->configureProcess($process); @@ -146,18 +128,13 @@ public function shell($command, $cwd = null, array $env = null, $input = null, $ /** * configureProcess sets up a process object so that it is ready to use. */ - protected static function configureProcess(ProcessBase $process) + protected static function configureProcess(ProcessBase $process): ProcessBase { $process->setSimulated(Drush::simulate()); $process->setVerbose(Drush::verbose()); - // Handle BC method of making env variables inherited. The default in - // later versions is always inherit and this method disappears. - // @todo Remove this if() block once Symfony 3 support is dropped. - if (method_exists($process, 'inheritEnvironmentVariables')) { - set_error_handler(null); - $process->inheritEnvironmentVariables(); - restore_error_handler(); - } + // Don't let sub-process inherit the verbosity of its parent https://github.com/symfony/console/blob/3.4/Application.php#L970-L972 + putenv('SHELL_VERBOSITY'); + unset($_ENV['SHELL_VERBOSITY'], $_SERVER['SHELL_VERBOSITY']); $process->setLogger(Drush::logger()); $process->setRealtimeOutput(new DrushStyle(Drush::input(), Drush::output())); $process->setTimeout(Drush::getTimeout()); diff --git a/src/SiteAlias/SiteAliasFileLoader.php b/src/SiteAlias/SiteAliasFileLoader.php index dc6b617f11..fb6824aadc 100644 --- a/src/SiteAlias/SiteAliasFileLoader.php +++ b/src/SiteAlias/SiteAliasFileLoader.php @@ -1,4 +1,5 @@ siteAliasManager = $siteAliasManager; - } - - /** - * @return SiteAliasManager - */ - public function siteAliasManager() - { - return $this->siteAliasManager; - } - - /** - * @inheritdoc - */ - public function hasSiteAliasManager() - { - return isset($this->siteAliasManager); - } -} diff --git a/src/SiteAlias/SiteAliasName.php b/src/SiteAlias/SiteAliasName.php index f936c97a58..87fe56010a 100644 --- a/src/SiteAlias/SiteAliasName.php +++ b/src/SiteAlias/SiteAliasName.php @@ -1,4 +1,5 @@ process; } - /** - * @param Process $process - */ - public function setProcess($process) + public function setProcess(Process $process): void { $this->process = $process; } @@ -75,9 +70,8 @@ public function setProcess($process) * * @param $options * An options array as handed to a command callback. - * @return SqlBase */ - public static function create($options = []) + public static function create(array $options = []): ?SqlBase { // Set defaults in the unfortunate event that caller doesn't provide values. $options += [ @@ -107,42 +101,41 @@ public static function create($options = []) } } - public static function getInstance($db_spec, $options) + public static function getInstance($db_spec, $options): ?self { $driver = $db_spec['driver']; - $class_name = 'Drush\Sql\Sql'. ucfirst($driver); + $class_name = 'Drush\Sql\Sql' . ucfirst($driver); if (class_exists($class_name)) { $instance = new $class_name($db_spec, $options); // Inject config $instance->setConfig(Drush::config()); return $instance; } + return null; } /* * Get the current $db_spec. */ - public function getDbSpec() + public function getDbSpec(): array { return $this->dbSpec; } /** * Set the current db spec. - * - * @param array $dbSpec */ - public function setDbSpec($dbSpec) + public function setDbSpec(array $dbSpec): void { $this->dbSpec = $dbSpec; } /** * The unix command used to connect to the database. - * @return string */ - public function command() + public function command(): string { + return ''; } /** @@ -151,10 +144,8 @@ public function command() * @param bool $hide_password * If TRUE, DBMS should try to hide password from process list. * On mysql, that means using --defaults-file to supply the user+password. - * - * @return string */ - public function connect($hide_password = true) + public function connect(bool $hide_password = true): string { return trim($this->command() . ' ' . $this->creds($hide_password) . ' ' . $this->getOption('extra', $this->queryExtra)); } @@ -163,7 +154,7 @@ public function connect($hide_password = true) /* * Execute a SQL dump and return the path to the resulting dump file. * - * @return string|bool + * @return * Returns path to dump file, or false on failure. */ public function dump() @@ -211,7 +202,7 @@ public function dump() * make pipefail work right in this instance, we must wrap it * in 'bash -c', since pipefail is a bash feature. */ - protected function addPipeFail($cmd, $pipefail) + protected function addPipeFail(string $cmd, string $pipefail): string { if (empty($pipefail)) { return $cmd; @@ -231,24 +222,26 @@ protected function addPipeFail($cmd, $pipefail) * * @param array $table_selection * Supported keys: 'skip', 'structure', 'tables'. - * @return string + * @return * One or more mysqldump/pg_dump/sqlite3/etc statements that are ready for executing. * If multiple statements are needed, enclose in parenthesis. */ - public function dumpCmd($table_selection) + public function dumpCmd($table_selection): string { + return ''; } /* * Generate a path to an output file for a SQL dump when needed. * - * @param string|bool @file + * @param string|bool|null @file * If TRUE, generate a path based on usual backup directory and current date. * Otherwise, just return the path that was provided. */ - public function dumpFile($file) + public function dumpFile($file): ?string { - $database = $this->dbSpec['database']; + // basename() is needed for sqlite as $database is a path. Harmless otherwise. + $database = basename($this->dbSpec['database']); // $file is passed in to us usually via --result-file. If the user // has set $options['result-file'] = 'auto', then we @@ -272,17 +265,17 @@ public function dumpFile($file) * If you don't want to query results to print during --debug then * provide a $result_file whose value can be drush_bit_bucket(). * - * @param string $query + * @param $query * The SQL to be executed. Should be NULL if $input_file is provided. - * @param string $input_file + * @param $input_file * A path to a file containing the SQL to be executed. - * @param string $result_file + * @param $result_file * A path to save query results to. Can be drush_bit_bucket() if desired. * * @return boolean * TRUE on success, FALSE on failure */ - public function query($query, $input_file = null, $result_file = '') + public function query(string $query, $input_file = null, $result_file = ''): ?bool { if (!Drush::simulate()) { return $this->alwaysQuery($query, $input_file, $result_file); @@ -296,17 +289,17 @@ public function query($query, $input_file = null, $result_file = '') * If you don't want results to print during --debug then * provide a $result_file whose value can be drush_bit_bucket(). * - * @param string $query + * @param $query * The SQL to be executed. Should be null if $input_file is provided. - * @param string $input_file + * @param $input_file * A path to a file containing the SQL to be executed. - * @param string $result_file + * @param $result_file * A path to save query results to. Can be drush_bit_bucket() if desired. * - * @return boolean - * TRUE on success, FALSE on failure + * @return + * TRUE on success, FALSE on failure. */ - public function alwaysQuery($query, $input_file = null, $result_file = '') + public function alwaysQuery(string $query, $input_file = null, ?string $result_file = ''): bool { $input_file_original = $input_file; if ($input_file && FsUtils::isTarball($input_file)) { @@ -333,7 +326,7 @@ public function alwaysQuery($query, $input_file = null, $result_file = '') $exec = implode(' ', $parts); if ($result_file) { - $exec .= ' > '. Escape::shellArg($result_file); + $exec .= ' > ' . Escape::shellArg($result_file); } // In --verbose mode, Process will show the call to mysql/psql/sqlite, @@ -358,7 +351,7 @@ public function alwaysQuery($query, $input_file = null, $result_file = '') /** * Show the query in debug mode and simulate mode */ - protected function logQueryInDebugMode($query, $input_file_original) + protected function logQueryInDebugMode($query, $input_file_original): void { // In --verbose mode, Drush::process() will show the call to mysql/psql/sqlite, // but the sql query itself is stored in a temp file and not displayed. @@ -371,12 +364,13 @@ protected function logQueryInDebugMode($query, $input_file_original) /* * A string to add to the command when queries should not print their results. */ - public function silent() + public function silent(): ?string { + return null; } - public function queryPrefix($query) + public function queryPrefix($query): ?string { // Inject table prefixes as needed. if (Drush::bootstrapManager()->hasBootstrapped(DRUSH_BOOTSTRAP_DRUPAL_DATABASE)) { @@ -399,14 +393,13 @@ public function queryFormat($query) * * @param array $tables * An array of table names - * @return boolean * True if successful, FALSE if failed. */ - public function drop($tables) + public function drop(array $tables): ?bool { $return = true; if ($tables) { - $sql = 'DROP TABLE '. implode(', ', $tables); + $sql = 'DROP TABLE ' . implode(', ', $tables); $return = $this->query($sql); } return $return; @@ -420,10 +413,10 @@ public function drop($tables) * @param boolean $quoted * Quote the database name. Mysql uses backticks to quote which can cause problems * in a Windows shell. Set TRUE if the CREATE is not running on the bash command line. - * @return string */ - public function createdbSql($dbname, $quoted = false) + public function createdbSql($dbname, bool $quoted = false): string { + return ''; } /** @@ -432,10 +425,9 @@ public function createdbSql($dbname, $quoted = false) * @param boolean $quoted * Quote the database name. Mysql uses backticks to quote which can cause problems * in a Windows shell. Set TRUE if the CREATE is not running on the bash command line. - * @return boolean * True if successful, FALSE otherwise. */ - public function createdb($quoted = false) + public function createdb(bool $quoted = false): ?bool { $dbname = $this->getDbSpec()['database']; $sql = $this->createdbSql($dbname, $quoted); @@ -447,10 +439,10 @@ public function createdb($quoted = false) /** * Drop all tables (if DB exists) or CREATE target database. * - * return boolean + * return * TRUE or FALSE depending on success. */ - public function dropOrCreate() + public function dropOrCreate(): bool { if ($this->dbExists()) { return $this->drop($this->listTablesQuoted()); @@ -461,52 +453,48 @@ public function dropOrCreate() /* * Determine if the specified DB already exists. - * - * @return bool */ - public function dbExists() + public function dbExists(): bool { + return false; } /** - * Build a fragment connection parameters. + * Build a string containing connection credentials. * * @param bool $hide_password * If TRUE, DBMS should try to hide password from process list. * On mysql, that means using --defaults-file to supply the user+password. - * @return string */ - public function creds($hide_password = true) + public function creds(bool $hide_password = true): string { + return ''; } /** * The active database driver. - * @return string */ - public function scheme() + public function scheme(): string { return $this->dbSpec['driver']; } /** * Extract the name of all existing tables in the given database. - * - * @return array|null - * An array of table names which exist in the current database. */ - public function listTables() + public function listTables(): array { + return []; } /** * Extract the name of all existing tables in the given database. * - * @return array|null + * @return * An array of table names which exist in the current database, * appropriately quoted for the RDMS. */ - public function listTablesQuoted() + public function listTablesQuoted(): array { return $this->listTables(); } @@ -517,7 +505,7 @@ public function listTablesQuoted() * @return string * A bash fragment. */ - public function paramsToOptions($parameters) + public function paramsToOptions($parameters): string { // Turn each parameter into a valid parameter string. $parameter_strings = []; @@ -533,10 +521,8 @@ public function paramsToOptions($parameters) /** * Adjust DB connection with superuser credentials if provided. - * - * @return null */ - public function su() + public function su(): void { $create_db_target = $this->getDbSpec(); @@ -556,10 +542,7 @@ public function su() $this->setDbSpec($create_db_target); } - /** - * @return array - */ - public function getOptions() + public function getOptions(): array { return $this->options; } @@ -570,32 +553,19 @@ public function getOption($name, $default = null) return array_key_exists($name, $options) && !is_null($options[$name]) ? $options[$name] : $default; } - /** - * @deprecated. - */ - public function db_spec() // @codingStandardsIgnoreLine - { - return $this->getDbSpec(); - } - /** * Convert from an old-style database URL to an array of database settings. * * @param db_url * A Drupal 6 db url string to convert, or an array with a 'default' element. - * @return array * An array of database values containing only the 'default' element of * the db url. If the parse fails the array is empty. */ - public static function dbSpecFromDbUrl($db_url) + public static function dbSpecFromDbUrl($db_url): array { $db_spec = []; - if (is_array($db_url)) { - $db_url_default = $db_url['default']; - } else { - $db_url_default = $db_url; - } + $db_url_default = is_array($db_url) ? $db_url['default'] : $db_url; // If it's a sqlite database, pick the database path and we're done. if (strpos($db_url_default, 'sqlite://') === 0) { diff --git a/src/Sql/SqlMysql.php b/src/Sql/SqlMysql.php index 33f53464ab..1cb6964be1 100644 --- a/src/Sql/SqlMysql.php +++ b/src/Sql/SqlMysql.php @@ -6,20 +6,19 @@ class SqlMysql extends SqlBase { - public $queryExtra = '-A'; - public function command() + public function command(): string { return 'mysql'; } - public function creds($hide_password = true) + public function creds($hide_password = true): string { $dbSpec = $this->getDbSpec(); if ($hide_password) { // Default to unix socket if configured. - $unixSocket = !empty($dbSpec['unix_socket']) ? 'socket="' . $dbSpec['unix_socket'] . '"' : ''; + $unixSocket = empty($dbSpec['unix_socket']) ? '' : 'socket="' . $dbSpec['unix_socket'] . '"'; // EMPTY password is not the same as NO password, and is valid. $contents = <<paramsToOptions($parameters); } - public function silent() + public function silent(): string { return '--silent'; } - public function createdbSql($dbname, $quoted = false) + public function createdbSql($dbname, $quoted = false): string { $dbSpec = $this->getDbSpec(); if ($quoted) { @@ -120,13 +119,13 @@ public function createdbSql($dbname, $quoted = false) /** * @inheritdoc */ - public function dbExists() + public function dbExists(): bool { // Suppress output. We only care about return value. return $this->alwaysQuery("SELECT 1;"); } - public function listTables() + public function listTables(): array { $tables = []; $this->alwaysQuery('SHOW TABLES;'); @@ -136,7 +135,7 @@ public function listTables() return $tables; } - public function listTablesQuoted() + public function listTablesQuoted(): array { $tables = $this->listTables(); foreach ($tables as &$table) { @@ -145,7 +144,7 @@ public function listTablesQuoted() return $tables; } - public function dumpCmd($table_selection) + public function dumpCmd($table_selection): string { $dbSpec = $this->getDbSpec(); $parens = false; @@ -186,7 +185,7 @@ public function dumpCmd($table_selection) $ignores[] = '--ignore-table=' . $dbSpec['database'] . '.' . $table; $parens = true; } - $exec .= ' '. implode(' ', $ignores); + $exec .= ' ' . implode(' ', $ignores); // Run mysqldump again and append output if we need some structure only tables. if (!empty($structure_tables)) { diff --git a/src/Sql/SqlPgsql.php b/src/Sql/SqlPgsql.php index 2ee044572d..89d7c6059f 100644 --- a/src/Sql/SqlPgsql.php +++ b/src/Sql/SqlPgsql.php @@ -8,7 +8,6 @@ class SqlPgsql extends SqlBase { - public $queryExtra = "--no-align --field-separator=\"\t\" --pset tuples_only=on"; public $queryFile = "--file"; @@ -41,24 +40,26 @@ private function createPasswordFile() return $this->password_file; } - public function command() + public function command(): string { return 'psql -q'; } - public function getEnv() + public function getEnv(): array { + $return = []; $pw_file = $this->createPasswordFile(); if (isset($pw_file)) { - return ['PGPASSFILE' => $pw_file]; + $return = ['PGPASSFILE' => $pw_file]; } + return $return; } /* * @param $hide_password * Not used in postgres. We always hide via a .pgpass file. */ - public function creds($hide_password = true) + public function creds($hide_password = true): string { $dbSpec = $this->getDbSpec(); // Some drush commands (e.g. site-install) want to connect to the @@ -78,7 +79,7 @@ public function creds($hide_password = true) return $this->paramsToOptions($parameters); } - public function createdbSql($dbname, $quoted = false) + public function createdbSql($dbname, $quoted = false): string { if ($quoted) { $dbname = '"' . $dbname . '"'; @@ -88,7 +89,7 @@ public function createdbSql($dbname, $quoted = false) return implode(' ', $sql); } - public function dbExists() + public function dbExists(): bool { $dbSpec = $this->getDbSpec(); $database = $dbSpec['database']; @@ -111,14 +112,14 @@ public function queryFormat($query) return $query; } - public function listTables() + public function listTables(): array { $return = $this->alwaysQuery(PSQL_SHOW_TABLES); $tables = explode(PHP_EOL, trim($this->getProcess()->getOutput())); return array_filter($tables); } - public function dumpCmd($table_selection) + public function dumpCmd($table_selection): string { $parens = false; $skip_tables = $table_selection['skip']; @@ -156,7 +157,7 @@ public function dumpCmd($table_selection) foreach ($skip_tables as $table) { $ignores[] = "--exclude-table=$table"; } - $exec .= ' '. implode(' ', $ignores); + $exec .= ' ' . implode(' ', $ignores); // Run pg_dump again and append output if we need some structure only tables. if (!empty($structure_tables)) { $parens = true; @@ -171,10 +172,7 @@ public function dumpCmd($table_selection) return $parens ? "($exec)" : $exec; } - /** - * @return string|null - */ - public function getPasswordFile() + public function getPasswordFile(): ?string { return $this->password_file; } diff --git a/src/Sql/SqlSqlite.php b/src/Sql/SqlSqlite.php index c6597e9449..5c4011266e 100644 --- a/src/Sql/SqlSqlite.php +++ b/src/Sql/SqlSqlite.php @@ -3,16 +3,15 @@ namespace Drush\Sql; use Drush\Drush; -use mysql_xdevapi\Exception; class SqlSqlite extends SqlBase { - public function command() + public function command(): string { return 'sqlite3'; } - public function creds($hide_password = true) + public function creds($hide_password = true): string { // SQLite doesn't do user management, instead relying on the filesystem // for that. So the only info we really need is the path to the database @@ -20,7 +19,7 @@ public function creds($hide_password = true) return ' ' . $this->getDbSpec()['database']; } - public function createdbSql($dbname, $quoted = false) + public function createdbSql($dbname, $quoted = false): string { return ''; } @@ -32,7 +31,7 @@ public function createdbSql($dbname, $quoted = false) * Quote the database name. Mysql uses backticks to quote which can cause problems * in a Windows shell. Set TRUE if the CREATE is not running on the bash command line. */ - public function createdb($quoted = false) + public function createdb(bool $quoted = false): bool { $file = $this->getDbSpec()['database']; if (file_exists($file)) { @@ -44,17 +43,17 @@ public function createdb($quoted = false) $path = dirname($file); Drush::logger()->debug("SQLITE: creating '$path' for creating '$file'"); if (!drush_mkdir($path)) { - throw new Exception("SQLITE: Cannot create $path"); + throw new \Exception("SQLITE: Cannot create $path"); } return file_exists($path); } - public function dbExists() + public function dbExists(): bool { return file_exists($this->getDbSpec()['database']); } - public function listTables() + public function listTables(): array { $return = $this->alwaysQuery('.tables'); $tables_raw = explode(PHP_EOL, trim($this->getProcess()->getOutput())); @@ -76,7 +75,7 @@ public function listTables() return $tables; } - public function drop($tables) + public function drop($tables): bool { $return = true; $sql = ''; @@ -91,7 +90,7 @@ public function drop($tables) return $return; } - public function dumpCmd($table_selection) + public function dumpCmd($table_selection): string { // Dumping is usually not necessary in SQLite, since all database data // is stored in a single file which can be copied just diff --git a/src/Sql/SqlTableSelectionTrait.php b/src/Sql/SqlTableSelectionTrait.php index d998db1ff1..2354ecc889 100644 --- a/src/Sql/SqlTableSelectionTrait.php +++ b/src/Sql/SqlTableSelectionTrait.php @@ -1,4 +1,5 @@ getTableSelection($options); // Get the existing table names in the specified database. @@ -51,7 +51,7 @@ public function getExpandedTableSelection($options, $all_tables) * @return array * An array of tables with non-existant tables removed. */ - public function expandAndFilterTables($tables, $db_tables) + public function expandAndFilterTables(array $tables, array $db_tables) { $expanded_tables = $this->ExpandWildcardTables($tables, $db_tables); $tables = $this->filterTables(array_merge($tables, $expanded_tables), $db_tables); @@ -70,7 +70,7 @@ public function expandAndFilterTables($tables, $db_tables) * @return * $tables array with wildcards resolved to real table names. */ - public function expandWildcardTables($tables, $db_tables) + public function expandWildcardTables(array $tables, array $db_tables) { // Table name expansion based on `*` wildcard. $expanded_db_tables = []; @@ -97,7 +97,7 @@ public function expandWildcardTables($tables, $db_tables) * An array with only valid table names (i.e. all of which actually exist in * the database). */ - public function filterTables($tables, $db_tables) + public function filterTables(array $tables, array $db_tables) { // Ensure all the tables actually exist in the database. foreach ($tables as $k => $table) { @@ -145,7 +145,7 @@ public function getTableSelection($options) * Returns an array of tables based on the first option * found, or an empty array if there were no matches. */ - public function getRawTableList($option_name, $options) + public function getRawTableList($option_name, array $options) { $key_list = StringUtils::csvToArray($options[$option_name . '-key']); foreach ($key_list as $key) { diff --git a/src/Style/DrushStyle.php b/src/Style/DrushStyle.php index 89920a8f69..c09c92efbd 100644 --- a/src/Style/DrushStyle.php +++ b/src/Style/DrushStyle.php @@ -4,11 +4,12 @@ use Drush\Drush; use Drush\Exceptions\UserAbortException; +use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Style\SymfonyStyle; class DrushStyle extends SymfonyStyle { - public function confirm($question, $default = true) + public function confirm($question, $default = true): bool { // Automatically accept confirmations if the --yes argument was supplied. if (Drush::affirmative()) { @@ -19,42 +20,49 @@ public function confirm($question, $default = true) $this->warning($question . ': no.'); return false; } - - $return = parent::confirm($question, $default); - return $return; + return parent::confirm($question, $default); } - /** - * @param string $question - * @param array $choices - * If an associative array is passed, the chosen *key* is returned. - * @param null $default - * @return mixed - */ public function choice($question, array $choices, $default = null) { - $choices = array_merge(['cancel' => 'Cancel'], $choices) ; + // Display the choices without their keys. $choices_values = array_values($choices); $return = parent::choice($question, $choices_values, $default); - if ($return == 'Cancel') { - throw new UserAbortException(); - } else { - return array_search($return, $choices); - } + + return array_search($return, $choices); } - public function warning($message) + public function warning($message): void { $this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ! ', true); } - public function note($message) + public function note($message): void { $this->block($message, 'NOTE', 'fg=black;bg=yellow', ' ! '); } - public function caution($message) + public function caution($message): void { $this->block($message, 'CAUTION', 'fg=black;bg=yellow', ' ! ', true); } + + /** + * @return mixed + */ + public function askRequired($question) + { + $question = new Question($question); + $question->setValidator(function (?string $value) { + // FALSE is not considered as empty value because question helper use + // it as negative answer on confirmation questions. + if ($value === null || $value === '') { + throw new \UnexpectedValueException('This value is required.'); + } + + return $value; + }); + + return $this->askQuestion($question); + } } diff --git a/src/Symfony/BootstrapCompilerPass.php b/src/Symfony/BootstrapCompilerPass.php index 11f0b97173..20765649d4 100644 --- a/src/Symfony/BootstrapCompilerPass.php +++ b/src/Symfony/BootstrapCompilerPass.php @@ -8,7 +8,7 @@ class BootstrapCompilerPass implements CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->has('bootstrap.manager')) { return; diff --git a/src/Symfony/BufferedConsoleOutput.php b/src/Symfony/BufferedConsoleOutput.php index 7c2e4f7932..96b8af58ab 100644 --- a/src/Symfony/BufferedConsoleOutput.php +++ b/src/Symfony/BufferedConsoleOutput.php @@ -5,6 +5,7 @@ use Symfony\Component\Console\Formatter\OutputFormatterInterface; use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\OutputInterface; /** @@ -13,6 +14,7 @@ class BufferedConsoleOutput extends BufferedOutput implements ConsoleOutputInterface { protected $stderr; + private array $consoleSectionOutputs = []; /** * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) @@ -29,7 +31,7 @@ public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = fa /** * {@inheritdoc} */ - public function getErrorOutput() + public function getErrorOutput(): BufferedOutput { return $this->stderr; } @@ -37,8 +39,13 @@ public function getErrorOutput() /** * {@inheritdoc} */ - public function setErrorOutput(OutputInterface $error) + public function setErrorOutput(OutputInterface $error): void { $this->stderr = $error; } + + public function section(): ConsoleSectionOutput + { + // @todo + } } diff --git a/src/Symfony/DrushArgvInput.php b/src/Symfony/DrushArgvInput.php index 74e533155b..e13c01f56a 100644 --- a/src/Symfony/DrushArgvInput.php +++ b/src/Symfony/DrushArgvInput.php @@ -66,7 +66,7 @@ public function __construct(array $argv = null, InputDefinition $definition = nu parent::__construct($definition); } - protected function setTokens(array $tokens) + protected function setTokens(array $tokens): void { $this->tokens = $tokens; } @@ -74,7 +74,7 @@ protected function setTokens(array $tokens) /** * {@inheritdoc} */ - protected function parse() + protected function parse(): void { $parseOptions = true; $this->parsed = $this->tokens; @@ -98,7 +98,7 @@ protected function parse() * * @param string $token The current token */ - private function parseShortOption($token) + private function parseShortOption($token): void { $name = substr($token, 1); @@ -121,7 +121,7 @@ private function parseShortOption($token) * * @throws RuntimeException When option given doesn't exist */ - private function parseShortOptionSet($name) + private function parseShortOptionSet($name): void { $len = strlen($name); for ($i = 0; $i < $len; ++$i) { @@ -145,7 +145,7 @@ private function parseShortOptionSet($name) * * @param string $token The current token */ - private function parseLongOption($token) + private function parseLongOption($token): void { $name = substr($token, 2); @@ -171,7 +171,7 @@ private function parseLongOption($token) * * @throws RuntimeException When too many arguments are given */ - private function parseArgument($token) + private function parseArgument($token): void { $c = count($this->arguments); @@ -204,7 +204,7 @@ private function parseArgument($token) * * @throws RuntimeException When option given doesn't exist */ - private function addShortOption($shortcut, $value) + private function addShortOption($shortcut, $value): void { if (!$this->definition->hasShortcut($shortcut)) { throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); @@ -221,7 +221,7 @@ private function addShortOption($shortcut, $value) * * @throws RuntimeException When option given doesn't exist */ - private function addLongOption($name, $value) + private function addLongOption($name, $value): void { if (!$this->definition->hasOption($name)) { throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); @@ -278,7 +278,7 @@ public function getFirstArgument() /** * {@inheritdoc} */ - public function hasParameterOption($values, $onlyParams = false) + public function hasParameterOption($values, $onlyParams = false): bool { $values = (array) $values; @@ -287,7 +287,7 @@ public function hasParameterOption($values, $onlyParams = false) return false; } foreach ($values as $value) { - if ($token === $value || 0 === strpos($token, $value.'=')) { + if ($token === $value || 0 === strpos($token, $value . '=')) { return true; } @@ -324,7 +324,7 @@ public function getParameterOption($values, $default = false, $onlyParams = fals } foreach ($values as $value) { - if ($token === $value || 0 === strpos($token, $value.'=')) { + if ($token === $value || 0 === strpos($token, $value . '=')) { if (false !== $pos = strpos($token, '=')) { return substr($token, $pos + 1); } @@ -346,7 +346,7 @@ public function __toString() { $tokens = array_map(function ($token) { if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) { - return $match[1].$this->escapeToken($match[2]); + return $match[1] . $this->escapeToken($match[2]); } if ($token && '-' !== $token[0]) { diff --git a/src/Symfony/DrushStyleInjector.php b/src/Symfony/DrushStyleInjector.php index d446ea2b94..2f000eef8c 100644 --- a/src/Symfony/DrushStyleInjector.php +++ b/src/Symfony/DrushStyleInjector.php @@ -1,4 +1,5 @@ input(), $commandData->output()); } diff --git a/src/Symfony/IndiscriminateInputDefinition.php b/src/Symfony/IndiscriminateInputDefinition.php index 9d4713a703..0bfc534239 100644 --- a/src/Symfony/IndiscriminateInputDefinition.php +++ b/src/Symfony/IndiscriminateInputDefinition.php @@ -21,7 +21,7 @@ class IndiscriminateInputDefinition extends InputDefinition /** * @inheritdoc */ - public function hasShortcut($name) + public function hasShortcut($name): bool { return true; } @@ -29,7 +29,7 @@ public function hasShortcut($name) /** * @inheritdoc */ - public function hasOption($name) + public function hasOption($name): bool { return true; } @@ -37,7 +37,7 @@ public function hasOption($name) /** * @inheritdoc */ - public function getOption($name) + public function getOption($name): InputOption { if (parent::hasOption($name)) { return parent::getOption($name); diff --git a/src/Symfony/LessStrictArgvInput.php b/src/Symfony/LessStrictArgvInput.php index 35af060caf..0104fc916e 100644 --- a/src/Symfony/LessStrictArgvInput.php +++ b/src/Symfony/LessStrictArgvInput.php @@ -3,7 +3,6 @@ namespace Drush\Symfony; use Symfony\Component\Console\Input\ArgvInput; - use Symfony\Component\Console\Exception\RuntimeException; /** @@ -59,7 +58,7 @@ public function getOption($name) return false; } - protected function setTokens(array $tokens) + protected function setTokens(array $tokens): void { $this->tokens = $tokens; } @@ -67,7 +66,7 @@ protected function setTokens(array $tokens) /** * {@inheritdoc} */ - protected function parse() + protected function parse(): void { $parseOptions = true; $this->parsed = $this->tokens; @@ -93,7 +92,7 @@ protected function parse() * * @param string $token The current token */ - private function parseShortOption($token) + private function parseShortOption($token): void { $name = substr($token, 1); @@ -114,7 +113,7 @@ private function parseShortOption($token) * * @param string $name The current token */ - private function parseShortOptionSet($name) + private function parseShortOptionSet($name): void { $len = strlen($name); for ($i = 0; $i < $len; ++$i) { @@ -138,7 +137,7 @@ private function parseShortOptionSet($name) * * @param string $token The current token */ - private function parseLongOption($token) + private function parseLongOption($token): void { $name = substr($token, 2); @@ -164,7 +163,7 @@ private function parseLongOption($token) * * @throws RuntimeException When too many arguments are given */ - private function parseArgument($token) + private function parseArgument($token): void { $c = count($this->arguments); @@ -197,7 +196,7 @@ private function parseArgument($token) * * @throws RuntimeException When option given doesn't exist */ - private function addShortOption($shortcut, $value) + private function addShortOption($shortcut, $value): void { if (!$this->definition->hasShortcut($shortcut)) { // Hard to know what to do with unknown short options. Maybe @@ -210,7 +209,7 @@ private function addShortOption($shortcut, $value) $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); } - public function injectAdditionalOptions($additionalOptions) + public function injectAdditionalOptions($additionalOptions): void { $this->additionalOptions += $additionalOptions; $this->options += $additionalOptions; @@ -224,7 +223,7 @@ public function injectAdditionalOptions($additionalOptions) * * @throws RuntimeException When option given doesn't exist */ - private function addLongOption($name, $value) + private function addLongOption($name, $value): void { if (!$this->definition->hasOption($name)) { // If we don't know anything about this option, then we'll @@ -284,7 +283,7 @@ public function getFirstArgument() /** * {@inheritdoc} */ - public function hasParameterOption($values, $onlyParams = false) + public function hasParameterOption($values, $onlyParams = false): bool { $values = (array) $values; @@ -293,7 +292,7 @@ public function hasParameterOption($values, $onlyParams = false) return false; } foreach ($values as $value) { - if ($token === $value || 0 === strpos($token, $value.'=')) { + if ($token === $value || 0 === strpos($token, $value . '=')) { return true; } } @@ -317,7 +316,7 @@ public function getParameterOption($values, $default = false, $onlyParams = fals } foreach ($values as $value) { - if ($token === $value || 0 === strpos($token, $value.'=')) { + if ($token === $value || 0 === strpos($token, $value . '=')) { if (false !== $pos = strpos($token, '=')) { return substr($token, $pos + 1); } @@ -339,7 +338,7 @@ public function __toString() { $tokens = array_map(function ($token) { if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) { - return $match[1].$this->escapeToken($match[2]); + return $match[1] . $this->escapeToken($match[2]); } if ($token && $token[0] !== '-') { diff --git a/src/TestTraits/CliTestTrait.php b/src/TestTraits/CliTestTrait.php index ebe2063c23..194773d178 100644 --- a/src/TestTraits/CliTestTrait.php +++ b/src/TestTraits/CliTestTrait.php @@ -1,4 +1,5 @@ process = new Process($command, $cd, $env, $input, 0); - $this->process->inheritEnvironmentVariables(true); if ($this->timeout) { $this->process->setTimeout($this->timeout) ->setIdleTimeout($this->idleTimeout); @@ -107,7 +107,7 @@ public function startExecute($command, $cd = null, $env = null, $input = null) } else { $message = 'Command had no output for ' . $this->idleTimeout . " seconds:\n" . $command; } - throw new \Exception($message . $this->buildProcessMessage()); + throw new \Exception($message . $this->buildProcessMessage(), $e->getCode(), $e); } } @@ -125,7 +125,7 @@ public function startExecute($command, $cd = null, $env = null, $input = null) * @param string $input * A string representing the STDIN that is piped to the command. */ - public function execute($command, $expected_return = 0, $cd = null, $env = null, $input = null) + public function execute($command, int $expected_return = 0, $cd = null, $env = null, $input = null) { try { // Process uses a default timeout of 60 seconds, set it to 0 (none). @@ -166,7 +166,7 @@ public function execute($command, $expected_return = 0, $cd = null, $env = null, } else { $message = 'Command had no output for ' . $this->idleTimeout . " seconds:\n" . $command; } - throw new \Exception($message . $this->buildProcessMessage()); + throw new \Exception($message . $this->buildProcessMessage(), $e->getCode(), $e); } } @@ -243,7 +243,7 @@ public function buildProcessMessage() * Optional regular expression that should be ignored in the error output. */ - protected function assertOutputEquals($expected, $filter = '') + protected function assertOutputEquals(string $expected, string $filter = '') { $output = $this->getSimplifiedOutput(); if (!empty($filter)) { @@ -264,7 +264,7 @@ protected function assertOutputEquals($expected, $filter = '') * @param string $filter * Optional regular expression that should be ignored in the error output. */ - protected function assertErrorOutputEquals($expected, $filter = '') + protected function assertErrorOutputEquals(string $expected, string $filter = '') { $output = $this->getSimplifiedErrorOutput(); if (!empty($filter)) { diff --git a/src/TestTraits/DrushTestTrait.php b/src/TestTraits/DrushTestTrait.php index bfa2c000eb..32008f6a89 100644 --- a/src/TestTraits/DrushTestTrait.php +++ b/src/TestTraits/DrushTestTrait.php @@ -1,4 +1,5 @@ databasePrefix) && function_exists('drupal_generate_test_ua') && !isset($env['HTTP_USER_AGENT'])) { @@ -89,7 +90,7 @@ public function drush($command, array $args = [], array $options = [], $site_spe * @param string $value The option value (or empty) * @return string */ - protected function convertKeyValueToFlag($key, $value) + protected function convertKeyValueToFlag(string $key, string $value) { if (!isset($value)) { return "--$key"; diff --git a/src/TestTraits/OutputUtilsTrait.php b/src/TestTraits/OutputUtilsTrait.php index d8d1ab524f..1d7f51a6e9 100644 --- a/src/TestTraits/OutputUtilsTrait.php +++ b/src/TestTraits/OutputUtilsTrait.php @@ -1,4 +1,5 @@ - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Drush\Internal\Config\Yaml; - -/** - * Escaper encapsulates escaping rules for single and double-quoted - * YAML strings. - * - * @author Matthew Lewinski - * - * @internal - */ -class Escaper -{ - // Characters that would cause a dumped string to require double quoting. - const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9"; - - // Mapping arrays for escaping a double quoted string. The backslash is - // first to ensure proper escaping because str_replace operates iteratively - // on the input arrays. This ordering of the characters avoids the use of strtr, - // which performs more slowly. - private static $escapees = array('\\', '\\\\', '\\"', '"', - "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", - "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", - "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", - "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", - "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9", - ); - private static $escaped = array('\\\\', '\\"', '\\\\', '\\"', - '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', - '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', - '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', - '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', - '\\N', '\\_', '\\L', '\\P', - ); - - /** - * Determines if a PHP value would require double quoting in YAML. - * - * @param string $value A PHP value - * - * @return bool True if the value would require double quotes - */ - public static function requiresDoubleQuoting($value) - { - return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); - } - - /** - * Escapes and surrounds a PHP value with double quotes. - * - * @param string $value A PHP value - * - * @return string The quoted, escaped string - */ - public static function escapeWithDoubleQuotes($value) - { - return sprintf('"%s"', str_replace(self::$escapees, self::$escaped, $value)); - } - - /** - * Determines if a PHP value would require single quoting in YAML. - * - * @param string $value A PHP value - * - * @return bool True if the value would require single quotes - */ - public static function requiresSingleQuoting($value) - { - // Determines if a PHP value is entirely composed of a value that would - // require single quoting in YAML. - if (in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'))) { - return true; - } - - // Determines if the PHP value contains any single characters that would - // cause it to require single quoting in YAML. - return 0 < preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value); - } - - /** - * Escapes and surrounds a PHP value with single quotes. - * - * @param string $value A PHP value - * - * @return string The quoted, escaped string - */ - public static function escapeWithSingleQuotes($value) - { - return sprintf("'%s'", str_replace('\'', '\'\'', $value)); - } -} diff --git a/src/internal-forks/Config/Yaml/Exception/ExceptionInterface.php b/src/internal-forks/Config/Yaml/Exception/ExceptionInterface.php deleted file mode 100644 index 266a2f6f1d..0000000000 --- a/src/internal-forks/Config/Yaml/Exception/ExceptionInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Drush\Internal\Config\Yaml\Exception; - -/** - * Exception interface for all exceptions thrown by the component. - * - * @author Fabien Potencier - */ -interface ExceptionInterface -{ -} diff --git a/src/internal-forks/Config/Yaml/Exception/ParseException.php b/src/internal-forks/Config/Yaml/Exception/ParseException.php deleted file mode 100644 index eca5a3265c..0000000000 --- a/src/internal-forks/Config/Yaml/Exception/ParseException.php +++ /dev/null @@ -1,139 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Drush\Internal\Config\Yaml\Exception; - -/** - * Exception class thrown when an error occurs during parsing. - * - * @author Fabien Potencier - */ -class ParseException extends RuntimeException -{ - private $parsedFile; - private $parsedLine; - private $snippet; - private $rawMessage; - - /** - * @param string $message The error message - * @param int $parsedLine The line where the error occurred - * @param string|null $snippet The snippet of code near the problem - * @param string|null $parsedFile The file name where the error occurred - * @param \Exception|null $previous The previous exception - */ - public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null) - { - $this->parsedFile = $parsedFile; - $this->parsedLine = $parsedLine; - $this->snippet = $snippet; - $this->rawMessage = $message; - - $this->updateRepr(); - - parent::__construct($this->message, 0, $previous); - } - - /** - * Gets the snippet of code near the error. - * - * @return string The snippet of code - */ - public function getSnippet() - { - return $this->snippet; - } - - /** - * Sets the snippet of code near the error. - * - * @param string $snippet The code snippet - */ - public function setSnippet($snippet) - { - $this->snippet = $snippet; - - $this->updateRepr(); - } - - /** - * Gets the filename where the error occurred. - * - * This method returns null if a string is parsed. - * - * @return string The filename - */ - public function getParsedFile() - { - return $this->parsedFile; - } - - /** - * Sets the filename where the error occurred. - * - * @param string $parsedFile The filename - */ - public function setParsedFile($parsedFile) - { - $this->parsedFile = $parsedFile; - - $this->updateRepr(); - } - - /** - * Gets the line where the error occurred. - * - * @return int The file line - */ - public function getParsedLine() - { - return $this->parsedLine; - } - - /** - * Sets the line where the error occurred. - * - * @param int $parsedLine The file line - */ - public function setParsedLine($parsedLine) - { - $this->parsedLine = $parsedLine; - - $this->updateRepr(); - } - - private function updateRepr() - { - $this->message = $this->rawMessage; - - $dot = false; - if ('.' === substr($this->message, -1)) { - $this->message = substr($this->message, 0, -1); - $dot = true; - } - - if (null !== $this->parsedFile) { - $this->message .= sprintf(' in %s', json_encode($this->parsedFile, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); - } - - if ($this->parsedLine >= 0) { - $this->message .= sprintf(' at line %d', $this->parsedLine); - } - - if ($this->snippet) { - $this->message .= sprintf(' (near "%s")', $this->snippet); - } - - if ($dot) { - $this->message .= '.'; - } - } -} diff --git a/src/internal-forks/Config/Yaml/Exception/RuntimeException.php b/src/internal-forks/Config/Yaml/Exception/RuntimeException.php deleted file mode 100644 index 37f65205e5..0000000000 --- a/src/internal-forks/Config/Yaml/Exception/RuntimeException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Drush\Internal\Config\Yaml\Exception; - -/** - * Exception class thrown when an error occurs during parsing. - * - * @author Romain Neutron - */ -class RuntimeException extends \RuntimeException implements ExceptionInterface -{ -} diff --git a/src/internal-forks/Config/Yaml/Inline.php b/src/internal-forks/Config/Yaml/Inline.php deleted file mode 100644 index 4e537d9eb9..0000000000 --- a/src/internal-forks/Config/Yaml/Inline.php +++ /dev/null @@ -1,831 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Drush\Internal\Config\Yaml; - -use Drush\Internal\Config\Yaml\Exception\ParseException; -use Drush\Internal\Config\Yaml\Exception\DumpException; -use Drush\Internal\Config\Yaml\Tag\TaggedValue; - -/** - * Inline implements a YAML parser/dumper for the YAML inline syntax. - * - * @author Fabien Potencier - * - * @internal - */ -class Inline -{ - const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')'; - - public static $parsedLineNumber; - - private static $exceptionOnInvalidType = false; - private static $objectSupport = false; - private static $objectForMap = false; - private static $constantSupport = false; - - /** - * Converts a YAML string to a PHP value. - * - * @param string $value A YAML string - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior - * @param array $references Mapping of variable names to values - * - * @return mixed A PHP value - * - * @throws ParseException - */ - public static function parse($value, $flags = 0, $references = array()) - { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3 && !is_array($references)) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED); - - if ($references) { - $flags |= Yaml::PARSE_OBJECT; - } - - if (func_num_args() >= 4) { - @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(3)) { - $flags |= Yaml::PARSE_OBJECT_FOR_MAP; - } - } - - if (func_num_args() >= 5) { - $references = func_get_arg(4); - } else { - $references = array(); - } - } - - self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags); - self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags); - self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags); - self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags); - - if (is_null($value) || '' === trim($value)) { - return ''; - } - - if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { - $mbEncoding = mb_internal_encoding(); - mb_internal_encoding('ASCII'); - } - - $i = 0; - $tag = self::parseTag($value, $i, $flags); - switch ($value[$i]) { - case '[': - $result = self::parseSequence($value, $flags, $i, $references); - ++$i; - break; - case '{': - $result = self::parseMapping($value, $flags, $i, $references); - ++$i; - break; - default: - $result = self::parseScalar($value, $flags, null, $i, null === $tag, $references); - } - - if (null !== $tag) { - return new TaggedValue($tag, $result); - } - - // some comments are allowed at the end - if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) { - throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i))); - } - - if (isset($mbEncoding)) { - mb_internal_encoding($mbEncoding); - } - - return $result; - } - - /** - * Dumps a given PHP variable to a YAML string. - * - * @param mixed $value The PHP variable to convert - * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string - * - * @return string The YAML string representing the PHP value - * - * @throws DumpException When trying to dump PHP resource - */ - public static function dump($value, $flags = 0) - { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(2)) { - $flags |= Yaml::DUMP_OBJECT; - } - } - - switch (true) { - case is_resource($value): - if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { - throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value))); - } - - return 'null'; - case $value instanceof \DateTimeInterface: - return $value->format('c'); - case is_object($value): - if ($value instanceof TaggedValue) { - return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags); - } - - if (Yaml::DUMP_OBJECT & $flags) { - return '!php/object:'.serialize($value); - } - - if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) { - return self::dumpArray($value, $flags & ~Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); - } - - if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { - throw new DumpException('Object support when dumping a YAML file has been disabled.'); - } - - return 'null'; - case is_array($value): - return self::dumpArray($value, $flags); - case null === $value: - return 'null'; - case true === $value: - return 'true'; - case false === $value: - return 'false'; - case ctype_digit($value): - return is_string($value) ? "'$value'" : (int) $value; - case is_numeric($value): - $locale = setlocale(LC_NUMERIC, 0); - if (false !== $locale) { - setlocale(LC_NUMERIC, 'C'); - } - if (is_float($value)) { - $repr = (string) $value; - if (is_infinite($value)) { - $repr = str_ireplace('INF', '.Inf', $repr); - } elseif (floor($value) == $value && $repr == $value) { - // Preserve float data type since storing a whole number will result in integer value. - $repr = '!!float '.$repr; - } - } else { - $repr = is_string($value) ? "'$value'" : (string) $value; - } - if (false !== $locale) { - setlocale(LC_NUMERIC, $locale); - } - - return $repr; - case '' == $value: - return "''"; - case self::isBinaryString($value): - return '!!binary '.base64_encode($value); - case Escaper::requiresDoubleQuoting($value): - return Escaper::escapeWithDoubleQuotes($value); - case Escaper::requiresSingleQuoting($value): - case Parser::pregMatch('{^[0-9]+[_0-9]*$}', $value): - case Parser::pregMatch(self::getHexRegex(), $value): - case Parser::pregMatch(self::getTimestampRegex(), $value): - return Escaper::escapeWithSingleQuotes($value); - default: - return $value; - } - } - - /** - * Check if given array is hash or just normal indexed array. - * - * @internal - * - * @param array|\ArrayObject|\stdClass $value The PHP array or array-like object to check - * - * @return bool true if value is hash array, false otherwise - */ - public static function isHash($value) - { - if ($value instanceof \stdClass || $value instanceof \ArrayObject) { - return true; - } - - $expectedKey = 0; - - foreach ($value as $key => $val) { - if ($key !== $expectedKey++) { - return true; - } - } - - return false; - } - - /** - * Dumps a PHP array to a YAML string. - * - * @param array $value The PHP array to dump - * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string - * - * @return string The YAML string representing the PHP array - */ - private static function dumpArray($value, $flags) - { - // array - if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) { - $output = array(); - foreach ($value as $val) { - $output[] = self::dump($val, $flags); - } - - return sprintf('[%s]', implode(', ', $output)); - } - - // hash - $output = array(); - foreach ($value as $key => $val) { - $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); - } - - return sprintf('{ %s }', implode(', ', $output)); - } - - /** - * Parses a YAML scalar. - * - * @param string $scalar - * @param int $flags - * @param string[] $delimiters - * @param int &$i - * @param bool $evaluate - * @param array $references - * - * @return string - * - * @throws ParseException When malformed inline YAML string is parsed - * - * @internal - */ - public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i = 0, $evaluate = true, $references = array(), $legacyOmittedKeySupport = false) - { - if (in_array($scalar[$i], array('"', "'"))) { - // quoted scalar - $output = self::parseQuotedScalar($scalar, $i); - - if (null !== $delimiters) { - $tmp = ltrim(substr($scalar, $i), ' '); - if (!in_array($tmp[0], $delimiters)) { - throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i))); - } - } - } else { - // "normal" string - if (!$delimiters) { - $output = substr($scalar, $i); - $i += strlen($output); - - // remove comments - if (Parser::pregMatch('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) { - $output = substr($output, 0, $match[0][1]); - } - } elseif (Parser::pregMatch('/^(.'.($legacyOmittedKeySupport ? '+' : '*').'?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { - $output = $match[1]; - $i += strlen($output); - } else { - throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar)); - } - - // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >) - if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) { - throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0])); - } - - if ($output && '%' === $output[0]) { - @trigger_error(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0 on line %d.', $output, self::$parsedLineNumber + 1), E_USER_DEPRECATED); - } - - if ($evaluate) { - $output = self::evaluateScalar($output, $flags, $references); - } - } - - return $output; - } - - /** - * Parses a YAML quoted scalar. - * - * @param string $scalar - * @param int &$i - * - * @return string - * - * @throws ParseException When malformed inline YAML string is parsed - */ - private static function parseQuotedScalar($scalar, &$i) - { - if (!Parser::pregMatch('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { - throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i))); - } - - $output = substr($match[0], 1, strlen($match[0]) - 2); - - $unescaper = new Unescaper(); - if ('"' == $scalar[$i]) { - $output = $unescaper->unescapeDoubleQuotedString($output); - } else { - $output = $unescaper->unescapeSingleQuotedString($output); - } - - $i += strlen($match[0]); - - return $output; - } - - /** - * Parses a YAML sequence. - * - * @param string $sequence - * @param int $flags - * @param int &$i - * @param array $references - * - * @return array - * - * @throws ParseException When malformed inline YAML string is parsed - */ - private static function parseSequence($sequence, $flags, &$i = 0, $references = array()) - { - $output = array(); - $len = strlen($sequence); - ++$i; - - // [foo, bar, ...] - while ($i < $len) { - if (']' === $sequence[$i]) { - return $output; - } - if (',' === $sequence[$i] || ' ' === $sequence[$i]) { - ++$i; - - continue; - } - - $tag = self::parseTag($sequence, $i, $flags); - switch ($sequence[$i]) { - case '[': - // nested sequence - $value = self::parseSequence($sequence, $flags, $i, $references); - break; - case '{': - // nested mapping - $value = self::parseMapping($sequence, $flags, $i, $references); - break; - default: - $isQuoted = in_array($sequence[$i], array('"', "'")); - $value = self::parseScalar($sequence, $flags, array(',', ']'), $i, null === $tag, $references); - - // the value can be an array if a reference has been resolved to an array var - if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) { - // embedded mapping? - try { - $pos = 0; - $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references); - } catch (\InvalidArgumentException $e) { - // no, it's not - } - } - - --$i; - } - - if (null !== $tag) { - $value = new TaggedValue($tag, $value); - } - - $output[] = $value; - - ++$i; - } - - throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence)); - } - - /** - * Parses a YAML mapping. - * - * @param string $mapping - * @param int $flags - * @param int &$i - * @param array $references - * - * @return array|\stdClass - * - * @throws ParseException When malformed inline YAML string is parsed - */ - private static function parseMapping($mapping, $flags, &$i = 0, $references = array()) - { - $output = array(); - $len = strlen($mapping); - ++$i; - $allowOverwrite = false; - - // {foo: bar, bar:foo, ...} - while ($i < $len) { - switch ($mapping[$i]) { - case ' ': - case ',': - ++$i; - continue 2; - case '}': - if (self::$objectForMap) { - return (object) $output; - } - - return $output; - } - - // key - $isKeyQuoted = in_array($mapping[$i], array('"', "'"), true); - $key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false, array(), true); - - if (':' !== $key && false === $i = strpos($mapping, ':', $i)) { - break; - } - - if (':' === $key) { - @trigger_error(sprintf('Omitting the key of a mapping is deprecated and will throw a ParseException in 4.0 on line %d.', self::$parsedLineNumber + 1), E_USER_DEPRECATED); - } - - if (!(Yaml::PARSE_KEYS_AS_STRINGS & $flags)) { - $evaluatedKey = self::evaluateScalar($key, $flags, $references); - - if ('' !== $key && $evaluatedKey !== $key && !is_string($evaluatedKey) && !is_int($evaluatedKey)) { - @trigger_error(sprintf('Implicit casting of incompatible mapping keys to strings is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line %d.', self::$parsedLineNumber + 1), E_USER_DEPRECATED); - } - } - - if (':' !== $key && !$isKeyQuoted && (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) { - @trigger_error(sprintf('Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since version 3.2 and will throw a ParseException in 4.0 on line %d.', self::$parsedLineNumber + 1), E_USER_DEPRECATED); - } - - if ('<<' === $key) { - $allowOverwrite = true; - } - - while ($i < $len) { - if (':' === $mapping[$i] || ' ' === $mapping[$i]) { - ++$i; - - continue; - } - - $tag = self::parseTag($mapping, $i, $flags); - switch ($mapping[$i]) { - case '[': - // nested sequence - $value = self::parseSequence($mapping, $flags, $i, $references); - // Spec: Keys MUST be unique; first one wins. - // Parser cannot abort this mapping earlier, since lines - // are processed sequentially. - // But overwriting is allowed when a merge node is used in current block. - if ('<<' === $key) { - foreach ($value as $parsedValue) { - $output += $parsedValue; - } - } elseif ($allowOverwrite || !isset($output[$key])) { - if (null !== $tag) { - $output[$key] = new TaggedValue($tag, $value); - } else { - $output[$key] = $value; - } - } elseif (isset($output[$key])) { - @trigger_error(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED); - } - break; - case '{': - // nested mapping - $value = self::parseMapping($mapping, $flags, $i, $references); - // Spec: Keys MUST be unique; first one wins. - // Parser cannot abort this mapping earlier, since lines - // are processed sequentially. - // But overwriting is allowed when a merge node is used in current block. - if ('<<' === $key) { - $output += $value; - } elseif ($allowOverwrite || !isset($output[$key])) { - if (null !== $tag) { - $output[$key] = new TaggedValue($tag, $value); - } else { - $output[$key] = $value; - } - } elseif (isset($output[$key])) { - @trigger_error(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED); - } - break; - default: - $value = self::parseScalar($mapping, $flags, array(',', '}'), $i, null === $tag, $references); - // Spec: Keys MUST be unique; first one wins. - // Parser cannot abort this mapping earlier, since lines - // are processed sequentially. - // But overwriting is allowed when a merge node is used in current block. - if ('<<' === $key) { - $output += $value; - } elseif ($allowOverwrite || !isset($output[$key])) { - if (null !== $tag) { - $output[$key] = new TaggedValue($tag, $value); - } else { - $output[$key] = $value; - } - } elseif (isset($output[$key])) { - @trigger_error(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d.', $key, self::$parsedLineNumber + 1), E_USER_DEPRECATED); - } - --$i; - } - ++$i; - - continue 2; - } - } - - throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping)); - } - - /** - * Evaluates scalars and replaces magic values. - * - * @param string $scalar - * @param int $flags - * @param array $references - * - * @return mixed The evaluated YAML string - * - * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved - */ - private static function evaluateScalar($scalar, $flags, $references = array()) - { - $scalar = trim($scalar); - $scalarLower = strtolower($scalar); - - if (0 === strpos($scalar, '*')) { - if (false !== $pos = strpos($scalar, '#')) { - $value = substr($scalar, 1, $pos - 2); - } else { - $value = substr($scalar, 1); - } - - // an unquoted * - if (false === $value || '' === $value) { - throw new ParseException('A reference must contain at least one character.'); - } - - if (!array_key_exists($value, $references)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $value)); - } - - return $references[$value]; - } - - switch (true) { - case 'null' === $scalarLower: - case '' === $scalar: - case '~' === $scalar: - return; - case 'true' === $scalarLower: - return true; - case 'false' === $scalarLower: - return false; - case '!' === $scalar[0]: - switch (true) { - case 0 === strpos($scalar, '!str'): - return (string) substr($scalar, 5); - case 0 === strpos($scalar, '! '): - return (int) self::parseScalar(substr($scalar, 2), $flags); - case 0 === strpos($scalar, '!php/object:'): - if (self::$objectSupport) { - return unserialize(substr($scalar, 12)); - } - - if (self::$exceptionOnInvalidType) { - throw new ParseException('Object support when parsing a YAML file has been disabled.'); - } - - return; - case 0 === strpos($scalar, '!!php/object:'): - if (self::$objectSupport) { - @trigger_error(sprintf('The !!php/object tag to indicate dumped PHP objects is deprecated since version 3.1 and will be removed in 4.0. Use the !php/object tag instead on line %d.', self::$parsedLineNumber + 1), E_USER_DEPRECATED); - - return unserialize(substr($scalar, 13)); - } - - if (self::$exceptionOnInvalidType) { - throw new ParseException('Object support when parsing a YAML file has been disabled.'); - } - - return; - case 0 === strpos($scalar, '!php/const:'): - if (self::$constantSupport) { - if (defined($const = substr($scalar, 11))) { - return constant($const); - } - - throw new ParseException(sprintf('The constant "%s" is not defined.', $const)); - } - if (self::$exceptionOnInvalidType) { - throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar)); - } - - return; - case 0 === strpos($scalar, '!!float '): - return (float) substr($scalar, 8); - case 0 === strpos($scalar, '!!binary '): - return self::evaluateBinaryScalar(substr($scalar, 9)); - default: - @trigger_error(sprintf('Using the unquoted scalar value "%s" is deprecated since version 3.3 and will be considered as a tagged value in 4.0. You must quote it on line %d.', $scalar, self::$parsedLineNumber + 1), E_USER_DEPRECATED); - } - - // Optimize for returning strings. - // no break - case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || is_numeric($scalar[0]): - switch (true) { - case Parser::pregMatch('{^[+-]?[0-9][0-9_]*$}', $scalar): - $scalar = str_replace('_', '', (string) $scalar); - // omitting the break / return as integers are handled in the next case - // no break - case ctype_digit($scalar): - $raw = $scalar; - $cast = (int) $scalar; - - return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); - case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): - $raw = $scalar; - $cast = (int) $scalar; - - return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw); - case is_numeric($scalar): - case Parser::pregMatch(self::getHexRegex(), $scalar): - $scalar = str_replace('_', '', $scalar); - - return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar; - case '.inf' === $scalarLower: - case '.nan' === $scalarLower: - return -log(0); - case '-.inf' === $scalarLower: - return log(0); - case Parser::pregMatch('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar): - case Parser::pregMatch('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar): - if (false !== strpos($scalar, ',')) { - @trigger_error(sprintf('Using the comma as a group separator for floats is deprecated since version 3.2 and will be removed in 4.0 on line %d.', self::$parsedLineNumber + 1), E_USER_DEPRECATED); - } - - return (float) str_replace(array(',', '_'), '', $scalar); - case Parser::pregMatch(self::getTimestampRegex(), $scalar): - if (Yaml::PARSE_DATETIME & $flags) { - // When no timezone is provided in the parsed date, YAML spec says we must assume UTC. - return new \DateTime($scalar, new \DateTimeZone('UTC')); - } - - $timeZone = date_default_timezone_get(); - date_default_timezone_set('UTC'); - $time = strtotime($scalar); - date_default_timezone_set($timeZone); - - return $time; - } - } - - return (string) $scalar; - } - - /** - * @param string $value - * @param int &$i - * @param int $flags - * - * @return null|string - */ - private static function parseTag($value, &$i, $flags) - { - if ('!' !== $value[$i]) { - return; - } - - $tagLength = strcspn($value, " \t\n", $i + 1); - $tag = substr($value, $i + 1, $tagLength); - - $nextOffset = $i + $tagLength + 1; - $nextOffset += strspn($value, ' ', $nextOffset); - - // Is followed by a scalar - if (!isset($value[$nextOffset]) || !in_array($value[$nextOffset], array('[', '{'), true)) { - // Manage scalars in {@link self::evaluateScalar()} - return; - } - - // Built-in tags - if ($tag && '!' === $tag[0]) { - throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag)); - } - - if (Yaml::PARSE_CUSTOM_TAGS & $flags) { - $i = $nextOffset; - - return $tag; - } - - throw new ParseException(sprintf('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!%s".', $tag)); - } - - /** - * @param string $scalar - * - * @return string - * - * @internal - */ - public static function evaluateBinaryScalar($scalar) - { - $parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar)); - - if (0 !== (strlen($parsedBinaryData) % 4)) { - throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', strlen($parsedBinaryData))); - } - - if (!Parser::pregMatch('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) { - throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData)); - } - - return base64_decode($parsedBinaryData, true); - } - - private static function isBinaryString($value) - { - return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value); - } - - /** - * Gets a regex that matches a YAML date. - * - * @return string The regular expression - * - * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 - */ - private static function getTimestampRegex() - { - return <<[0-9][0-9][0-9][0-9]) - -(?P[0-9][0-9]?) - -(?P[0-9][0-9]?) - (?:(?:[Tt]|[ \t]+) - (?P[0-9][0-9]?) - :(?P[0-9][0-9]) - :(?P[0-9][0-9]) - (?:\.(?P[0-9]*))? - (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) - (?::(?P[0-9][0-9]))?))?)? - $~x -EOF; - } - - /** - * Gets a regex that matches a YAML number in hexadecimal notation. - * - * @return string - */ - private static function getHexRegex() - { - return '~^0x[0-9a-f_]++$~i'; - } -} diff --git a/src/internal-forks/Config/Yaml/LICENSE b/src/internal-forks/Config/Yaml/LICENSE deleted file mode 100644 index 17d16a1336..0000000000 --- a/src/internal-forks/Config/Yaml/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2017 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 -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. diff --git a/src/internal-forks/Config/Yaml/Parser.php b/src/internal-forks/Config/Yaml/Parser.php deleted file mode 100644 index a3376decee..0000000000 --- a/src/internal-forks/Config/Yaml/Parser.php +++ /dev/null @@ -1,1089 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Drush\Internal\Config\Yaml; - -use Drush\Internal\Config\Yaml\Exception\ParseException; -use Drush\Internal\Config\Yaml\Tag\TaggedValue; - -/** - * Parser parses YAML strings to convert them to PHP arrays. - * - * @author Fabien Potencier - */ -class Parser -{ - const TAG_PATTERN = '(?P![\w!.\/:-]+)'; - const BLOCK_SCALAR_HEADER_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?'; - - private $offset = 0; - private $totalNumberOfLines; - private $lines = array(); - private $currentLineNb = -1; - private $currentLine = ''; - private $refs = array(); - private $skippedLineNumbers = array(); - private $locallySkippedLineNumbers = array(); - - public function __construct() - { - if (func_num_args() > 0) { - @trigger_error(sprintf('The constructor arguments $offset, $totalNumberOfLines, $skippedLineNumbers of %s are deprecated and will be removed in 4.0', self::class), E_USER_DEPRECATED); - - $this->offset = func_get_arg(0); - if (func_num_args() > 1) { - $this->totalNumberOfLines = func_get_arg(1); - } - if (func_num_args() > 2) { - $this->skippedLineNumbers = func_get_arg(2); - } - } - } - - /** - * Parses a YAML string to a PHP value. - * - * @param string $value A YAML string - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior - * - * @return mixed A PHP value - * - * @throws ParseException If the YAML is not valid - */ - public function parse($value, $flags = 0) - { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(2)) { - $flags |= Yaml::PARSE_OBJECT; - } - } - - if (func_num_args() >= 4) { - @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(3)) { - $flags |= Yaml::PARSE_OBJECT_FOR_MAP; - } - } - - if (false === preg_match('//u', $value)) { - throw new ParseException('The YAML value does not appear to be valid UTF-8.'); - } - - $this->refs = array(); - - $mbEncoding = null; - $e = null; - $data = null; - - if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { - $mbEncoding = mb_internal_encoding(); - mb_internal_encoding('UTF-8'); - } - - try { - $data = $this->doParse($value, $flags); - } catch (\Exception $e) { - } catch (\Throwable $e) { - } - - if (null !== $mbEncoding) { - mb_internal_encoding($mbEncoding); - } - - $this->lines = array(); - $this->currentLine = ''; - $this->refs = array(); - $this->skippedLineNumbers = array(); - $this->locallySkippedLineNumbers = array(); - - if (null !== $e) { - throw $e; - } - - return $data; - } - - private function doParse($value, $flags) - { - $this->currentLineNb = -1; - $this->currentLine = ''; - $value = $this->cleanup($value); - $this->lines = explode("\n", $value); - $this->locallySkippedLineNumbers = array(); - - if (null === $this->totalNumberOfLines) { - $this->totalNumberOfLines = count($this->lines); - } - - if (!$this->moveToNextLine()) { - return null; - } - - $data = array(); - $context = null; - $allowOverwrite = false; - - while ($this->isCurrentLineEmpty()) { - if (!$this->moveToNextLine()) { - return null; - } - } - - // Resolves the tag and returns if end of the document - if (null !== ($tag = $this->getLineTag($this->currentLine, $flags, false)) && !$this->moveToNextLine()) { - return new TaggedValue($tag, ''); - } - - do { - if ($this->isCurrentLineEmpty()) { - continue; - } - - // tab? - if ("\t" === $this->currentLine[0]) { - throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - - $isRef = $mergeNode = false; - if (self::pregMatch('#^\-((?P\s+)(?P.+))?$#u', rtrim($this->currentLine), $values)) { - if ($context && 'mapping' == $context) { - throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - $context = 'sequence'; - - if (isset($values['value']) && self::pregMatch('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { - $isRef = $matches['ref']; - $values['value'] = $matches['value']; - } - - if (isset($values['value'][1]) && '?' === $values['value'][0] && ' ' === $values['value'][1]) { - @trigger_error(sprintf('Starting an unquoted string with a question mark followed by a space is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d.', $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); - } - - // array - if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { - $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags); - } elseif (null !== $subTag = $this->getLineTag(ltrim($values['value'], ' '), $flags)) { - $data[] = new TaggedValue( - $subTag, - $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags) - ); - } else { - if (isset($values['leadspaces']) - && self::pregMatch('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $this->trimTag($values['value']), $matches) - ) { - // this is a compact notation element, add to next block and parse - $block = $values['value']; - if ($this->isNextLineIndented()) { - $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1); - } - - $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $flags); - } else { - $data[] = $this->parseValue($values['value'], $flags, $context); - } - } - if ($isRef) { - $this->refs[$isRef] = end($data); - } - } elseif (self::pregMatch('#^(?P'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?(?:![^\s]++\s++)?[^ \'"\[\{!].*?) *\:(\s++(?P.+))?$#u', rtrim($this->currentLine), $values) - && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'"))) - ) { - if ($context && 'sequence' == $context) { - throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine); - } - $context = 'mapping'; - - // force correct settings - Inline::parse(null, $flags, $this->refs); - try { - Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); - $i = 0; - $evaluateKey = !(Yaml::PARSE_KEYS_AS_STRINGS & $flags); - - // constants in key will be evaluated anyway - if (isset($values['key'][0]) && '!' === $values['key'][0] && Yaml::PARSE_CONSTANT & $flags) { - $evaluateKey = true; - } - - $key = Inline::parseScalar($values['key'], 0, null, $i, $evaluateKey); - } catch (ParseException $e) { - $e->setParsedLine($this->getRealCurrentLineNb() + 1); - $e->setSnippet($this->currentLine); - - throw $e; - } - - if (!(Yaml::PARSE_KEYS_AS_STRINGS & $flags) && !is_string($key) && !is_int($key)) { - $keyType = is_numeric($key) ? 'numeric key' : 'non-string key'; - @trigger_error(sprintf('Implicit casting of %s to string is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead on line %d.', $keyType, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); - } - - // Convert float keys to strings, to avoid being converted to integers by PHP - if (is_float($key)) { - $key = (string) $key; - } - - if ('<<' === $key && (!isset($values['value']) || !self::pregMatch('#^&(?P[^ ]+)#u', $values['value'], $refMatches))) { - $mergeNode = true; - $allowOverwrite = true; - if (isset($values['value'][0]) && '*' === $values['value'][0]) { - $refName = substr(rtrim($values['value']), 1); - if (!array_key_exists($refName, $this->refs)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - - $refValue = $this->refs[$refName]; - - if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $refValue instanceof \stdClass) { - $refValue = (array) $refValue; - } - - if (!is_array($refValue)) { - throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - - $data += $refValue; // array union - } else { - if (isset($values['value']) && '' !== $values['value']) { - $value = $values['value']; - } else { - $value = $this->getNextEmbedBlock(); - } - $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags); - - if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsed instanceof \stdClass) { - $parsed = (array) $parsed; - } - - if (!is_array($parsed)) { - throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - - if (isset($parsed[0])) { - // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes - // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier - // in the sequence override keys specified in later mapping nodes. - foreach ($parsed as $parsedItem) { - if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsedItem instanceof \stdClass) { - $parsedItem = (array) $parsedItem; - } - - if (!is_array($parsedItem)) { - throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); - } - - $data += $parsedItem; // array union - } - } else { - // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the - // current mapping, unless the key already exists in it. - $data += $parsed; // array union - } - } - } elseif ('<<' !== $key && isset($values['value']) && self::pregMatch('#^&(?P[^ ]++) *+(?P.*)#u', $values['value'], $matches)) { - $isRef = $matches['ref']; - $values['value'] = $matches['value']; - } - - $subTag = null; - if ($mergeNode) { - // Merge keys - } elseif (!isset($values['value']) || '' === $values['value'] || 0 === strpos($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) { - // hash - // if next line is less indented or equal, then it means that the current value is null - if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { - // Spec: Keys MUST be unique; first one wins. - // But overwriting is allowed when a merge node is used in current block. - if ($allowOverwrite || !isset($data[$key])) { - if (null !== $subTag) { - $data[$key] = new TaggedValue($subTag, ''); - } else { - $data[$key] = null; - } - } else { - @trigger_error(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); - } - } else { - // remember the parsed line number here in case we need it to provide some contexts in error messages below - $realCurrentLineNbKey = $this->getRealCurrentLineNb(); - $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags); - if ('<<' === $key) { - $this->refs[$refMatches['ref']] = $value; - - if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $value instanceof \stdClass) { - $value = (array) $value; - } - - $data += $value; - } elseif ($allowOverwrite || !isset($data[$key])) { - // Spec: Keys MUST be unique; first one wins. - // But overwriting is allowed when a merge node is used in current block. - if (null !== $subTag) { - $data[$key] = new TaggedValue($subTag, $value); - } else { - $data[$key] = $value; - } - } else { - @trigger_error(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d.', $key, $realCurrentLineNbKey + 1), E_USER_DEPRECATED); - } - } - } else { - $value = $this->parseValue(rtrim($values['value']), $flags, $context); - // Spec: Keys MUST be unique; first one wins. - // But overwriting is allowed when a merge node is used in current block. - if ($allowOverwrite || !isset($data[$key])) { - $data[$key] = $value; - } else { - @trigger_error(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since version 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d.', $key, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); - } - } - if ($isRef) { - $this->refs[$isRef] = $data[$key]; - } - } else { - // multiple documents are not supported - if ('---' === $this->currentLine) { - throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine); - } - - if (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1]) { - @trigger_error(sprintf('Starting an unquoted string with a question mark followed by a space is deprecated since version 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0 on line %d.', $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); - } - - // 1-liner optionally followed by newline(s) - if (is_string($value) && $this->lines[0] === trim($value)) { - try { - Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); - $value = Inline::parse($this->lines[0], $flags, $this->refs); - } catch (ParseException $e) { - $e->setParsedLine($this->getRealCurrentLineNb() + 1); - $e->setSnippet($this->currentLine); - - throw $e; - } - - return $value; - } - - // try to parse the value as a multi-line string as a last resort - if (0 === $this->currentLineNb) { - $parseError = false; - $previousLineWasNewline = false; - $previousLineWasTerminatedWithBackslash = false; - $value = ''; - - foreach ($this->lines as $line) { - try { - if (isset($line[0]) && ('"' === $line[0] || "'" === $line[0])) { - $parsedLine = $line; - } else { - $parsedLine = Inline::parse($line, $flags, $this->refs); - } - - if (!is_string($parsedLine)) { - $parseError = true; - break; - } - - if ('' === trim($parsedLine)) { - $value .= "\n"; - } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { - $value .= ' '; - } - - if ('' !== trim($parsedLine) && '\\' === substr($parsedLine, -1)) { - $value .= ltrim(substr($parsedLine, 0, -1)); - } elseif ('' !== trim($parsedLine)) { - $value .= trim($parsedLine); - } - - if ('' === trim($parsedLine)) { - $previousLineWasNewline = true; - $previousLineWasTerminatedWithBackslash = false; - } elseif ('\\' === substr($parsedLine, -1)) { - $previousLineWasNewline = false; - $previousLineWasTerminatedWithBackslash = true; - } else { - $previousLineWasNewline = false; - $previousLineWasTerminatedWithBackslash = false; - } - } catch (ParseException $e) { - $parseError = true; - break; - } - } - - if (!$parseError) { - return Inline::parse(trim($value)); - } - } - - throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - } while ($this->moveToNextLine()); - - if (null !== $tag) { - $data = new TaggedValue($tag, $data); - } - - if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data) && 'mapping' === $context) { - $object = new \stdClass(); - - foreach ($data as $key => $value) { - $object->$key = $value; - } - - $data = $object; - } - - return empty($data) ? null : $data; - } - - private function parseBlock($offset, $yaml, $flags) - { - $skippedLineNumbers = $this->skippedLineNumbers; - - foreach ($this->locallySkippedLineNumbers as $lineNumber) { - if ($lineNumber < $offset) { - continue; - } - - $skippedLineNumbers[] = $lineNumber; - } - - $parser = new self(); - $parser->offset = $offset; - $parser->totalNumberOfLines = $this->totalNumberOfLines; - $parser->skippedLineNumbers = $skippedLineNumbers; - $parser->refs = &$this->refs; - - return $parser->doParse($yaml, $flags); - } - - /** - * Returns the current line number (takes the offset into account). - * - * @return int The current line number - */ - private function getRealCurrentLineNb() - { - $realCurrentLineNumber = $this->currentLineNb + $this->offset; - - foreach ($this->skippedLineNumbers as $skippedLineNumber) { - if ($skippedLineNumber > $realCurrentLineNumber) { - break; - } - - ++$realCurrentLineNumber; - } - - return $realCurrentLineNumber; - } - - /** - * Returns the current line indentation. - * - * @return int The current line indentation - */ - private function getCurrentLineIndentation() - { - return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' ')); - } - - /** - * Returns the next embed block of YAML. - * - * @param int $indentation The indent level at which the block is to be read, or null for default - * @param bool $inSequence True if the enclosing data structure is a sequence - * - * @return string A YAML string - * - * @throws ParseException When indentation problem are detected - */ - private function getNextEmbedBlock($indentation = null, $inSequence = false) - { - $oldLineIndentation = $this->getCurrentLineIndentation(); - $blockScalarIndentations = array(); - - if ($this->isBlockScalarHeader()) { - $blockScalarIndentations[] = $oldLineIndentation; - } - - if (!$this->moveToNextLine()) { - return; - } - - if (null === $indentation) { - $newIndent = $this->getCurrentLineIndentation(); - - $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem(); - - if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { - throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - } else { - $newIndent = $indentation; - } - - $data = array(); - if ($this->getCurrentLineIndentation() >= $newIndent) { - $data[] = substr($this->currentLine, $newIndent); - } else { - $this->moveToPreviousLine(); - - return; - } - - if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) { - // the previous line contained a dash but no item content, this line is a sequence item with the same indentation - // and therefore no nested list or mapping - $this->moveToPreviousLine(); - - return; - } - - $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); - - if (empty($blockScalarIndentations) && $this->isBlockScalarHeader()) { - $blockScalarIndentations[] = $this->getCurrentLineIndentation(); - } - - $previousLineIndentation = $this->getCurrentLineIndentation(); - - while ($this->moveToNextLine()) { - $indent = $this->getCurrentLineIndentation(); - - // terminate all block scalars that are more indented than the current line - if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && '' !== trim($this->currentLine)) { - foreach ($blockScalarIndentations as $key => $blockScalarIndentation) { - if ($blockScalarIndentation >= $indent) { - unset($blockScalarIndentations[$key]); - } - } - } - - if (empty($blockScalarIndentations) && !$this->isCurrentLineComment() && $this->isBlockScalarHeader()) { - $blockScalarIndentations[] = $indent; - } - - $previousLineIndentation = $indent; - - if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { - $this->moveToPreviousLine(); - break; - } - - if ($this->isCurrentLineBlank()) { - $data[] = substr($this->currentLine, $newIndent); - continue; - } - - if ($indent >= $newIndent) { - $data[] = substr($this->currentLine, $newIndent); - } elseif ($this->isCurrentLineComment()) { - $data[] = $this->currentLine; - } elseif (0 == $indent) { - $this->moveToPreviousLine(); - - break; - } else { - throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - } - - return implode("\n", $data); - } - - /** - * Moves the parser to the next line. - * - * @return bool - */ - private function moveToNextLine() - { - if ($this->currentLineNb >= count($this->lines) - 1) { - return false; - } - - $this->currentLine = $this->lines[++$this->currentLineNb]; - - return true; - } - - /** - * Moves the parser to the previous line. - * - * @return bool - */ - private function moveToPreviousLine() - { - if ($this->currentLineNb < 1) { - return false; - } - - $this->currentLine = $this->lines[--$this->currentLineNb]; - - return true; - } - - /** - * Parses a YAML value. - * - * @param string $value A YAML value - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior - * @param string $context The parser context (either sequence or mapping) - * - * @return mixed A PHP value - * - * @throws ParseException When reference does not exist - */ - private function parseValue($value, $flags, $context) - { - if (0 === strpos($value, '*')) { - if (false !== $pos = strpos($value, '#')) { - $value = substr($value, 1, $pos - 2); - } else { - $value = substr($value, 1); - } - - if (!array_key_exists($value, $this->refs)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine); - } - - return $this->refs[$value]; - } - - if (self::pregMatch('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { - $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; - - $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); - - if ('' !== $matches['tag']) { - if ('!!binary' === $matches['tag']) { - return Inline::evaluateBinaryScalar($data); - } elseif ('!' !== $matches['tag']) { - @trigger_error(sprintf('Using the custom tag "%s" for the value "%s" is deprecated since version 3.3. It will be replaced by an instance of %s in 4.0 on line %d.', $matches['tag'], $data, TaggedValue::class, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); - } - } - - return $data; - } - - try { - $quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null; - - // do not take following lines into account when the current line is a quoted single line value - if (null !== $quotation && self::pregMatch('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) { - return Inline::parse($value, $flags, $this->refs); - } - - $lines = array(); - - while ($this->moveToNextLine()) { - // unquoted strings end before the first unindented line - if (null === $quotation && 0 === $this->getCurrentLineIndentation()) { - $this->moveToPreviousLine(); - - break; - } - - $lines[] = trim($this->currentLine); - - // quoted string values end with a line that is terminated with the quotation character - if ('' !== $this->currentLine && substr($this->currentLine, -1) === $quotation) { - break; - } - } - - for ($i = 0, $linesCount = count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) { - if ('' === $lines[$i]) { - $value .= "\n"; - $previousLineBlank = true; - } elseif ($previousLineBlank) { - $value .= $lines[$i]; - $previousLineBlank = false; - } else { - $value .= ' '.$lines[$i]; - $previousLineBlank = false; - } - } - - Inline::$parsedLineNumber = $this->getRealCurrentLineNb(); - $parsedValue = Inline::parse($value, $flags, $this->refs); - - if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { - throw new ParseException('A colon cannot be used in an unquoted mapping value.'); - } - - return $parsedValue; - } catch (ParseException $e) { - $e->setParsedLine($this->getRealCurrentLineNb() + 1); - $e->setSnippet($this->currentLine); - - throw $e; - } - } - - /** - * Parses a block scalar. - * - * @param string $style The style indicator that was used to begin this block scalar (| or >) - * @param string $chomping The chomping indicator that was used to begin this block scalar (+ or -) - * @param int $indentation The indentation indicator that was used to begin this block scalar - * - * @return string The text value - */ - private function parseBlockScalar($style, $chomping = '', $indentation = 0) - { - $notEOF = $this->moveToNextLine(); - if (!$notEOF) { - return ''; - } - - $isCurrentLineBlank = $this->isCurrentLineBlank(); - $blockLines = array(); - - // leading blank lines are consumed before determining indentation - while ($notEOF && $isCurrentLineBlank) { - // newline only if not EOF - if ($notEOF = $this->moveToNextLine()) { - $blockLines[] = ''; - $isCurrentLineBlank = $this->isCurrentLineBlank(); - } - } - - // determine indentation if not specified - if (0 === $indentation) { - if (self::pregMatch('/^ +/', $this->currentLine, $matches)) { - $indentation = strlen($matches[0]); - } - } - - if ($indentation > 0) { - $pattern = sprintf('/^ {%d}(.*)$/', $indentation); - - while ($notEOF && ( - $isCurrentLineBlank || - self::pregMatch($pattern, $this->currentLine, $matches) - ) - ) { - if ($isCurrentLineBlank && strlen($this->currentLine) > $indentation) { - $blockLines[] = substr($this->currentLine, $indentation); - } elseif ($isCurrentLineBlank) { - $blockLines[] = ''; - } else { - $blockLines[] = $matches[1]; - } - - // newline only if not EOF - if ($notEOF = $this->moveToNextLine()) { - $isCurrentLineBlank = $this->isCurrentLineBlank(); - } - } - } elseif ($notEOF) { - $blockLines[] = ''; - } - - if ($notEOF) { - $blockLines[] = ''; - $this->moveToPreviousLine(); - } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) { - $blockLines[] = ''; - } - - // folded style - if ('>' === $style) { - $text = ''; - $previousLineIndented = false; - $previousLineBlank = false; - - for ($i = 0, $blockLinesCount = count($blockLines); $i < $blockLinesCount; ++$i) { - if ('' === $blockLines[$i]) { - $text .= "\n"; - $previousLineIndented = false; - $previousLineBlank = true; - } elseif (' ' === $blockLines[$i][0]) { - $text .= "\n".$blockLines[$i]; - $previousLineIndented = true; - $previousLineBlank = false; - } elseif ($previousLineIndented) { - $text .= "\n".$blockLines[$i]; - $previousLineIndented = false; - $previousLineBlank = false; - } elseif ($previousLineBlank || 0 === $i) { - $text .= $blockLines[$i]; - $previousLineIndented = false; - $previousLineBlank = false; - } else { - $text .= ' '.$blockLines[$i]; - $previousLineIndented = false; - $previousLineBlank = false; - } - } - } else { - $text = implode("\n", $blockLines); - } - - // deal with trailing newlines - if ('' === $chomping) { - $text = preg_replace('/\n+$/', "\n", $text); - } elseif ('-' === $chomping) { - $text = preg_replace('/\n+$/', '', $text); - } - - return $text; - } - - /** - * Returns true if the next line is indented. - * - * @return bool Returns true if the next line is indented, false otherwise - */ - private function isNextLineIndented() - { - $currentIndentation = $this->getCurrentLineIndentation(); - $EOF = !$this->moveToNextLine(); - - while (!$EOF && $this->isCurrentLineEmpty()) { - $EOF = !$this->moveToNextLine(); - } - - if ($EOF) { - return false; - } - - $ret = $this->getCurrentLineIndentation() > $currentIndentation; - - $this->moveToPreviousLine(); - - return $ret; - } - - /** - * Returns true if the current line is blank or if it is a comment line. - * - * @return bool Returns true if the current line is empty or if it is a comment line, false otherwise - */ - private function isCurrentLineEmpty() - { - return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); - } - - /** - * Returns true if the current line is blank. - * - * @return bool Returns true if the current line is blank, false otherwise - */ - private function isCurrentLineBlank() - { - return '' == trim($this->currentLine, ' '); - } - - /** - * Returns true if the current line is a comment line. - * - * @return bool Returns true if the current line is a comment line, false otherwise - */ - private function isCurrentLineComment() - { - //checking explicitly the first char of the trim is faster than loops or strpos - $ltrimmedLine = ltrim($this->currentLine, ' '); - - return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0]; - } - - private function isCurrentLineLastLineInDocument() - { - return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); - } - - /** - * Cleanups a YAML string to be parsed. - * - * @param string $value The input YAML string - * - * @return string A cleaned up YAML string - */ - private function cleanup($value) - { - $value = str_replace(array("\r\n", "\r"), "\n", $value); - - // strip YAML header - $count = 0; - $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); - $this->offset += $count; - - // remove leading comments - $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); - if (1 === $count) { - // items have been removed, update the offset - $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); - $value = $trimmedValue; - } - - // remove start of the document marker (---) - $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); - if (1 === $count) { - // items have been removed, update the offset - $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); - $value = $trimmedValue; - - // remove end of the document marker (...) - $value = preg_replace('#\.\.\.\s*$#', '', $value); - } - - return $value; - } - - /** - * Returns true if the next line starts unindented collection. - * - * @return bool Returns true if the next line starts unindented collection, false otherwise - */ - private function isNextLineUnIndentedCollection() - { - $currentIndentation = $this->getCurrentLineIndentation(); - $notEOF = $this->moveToNextLine(); - - while ($notEOF && $this->isCurrentLineEmpty()) { - $notEOF = $this->moveToNextLine(); - } - - if (false === $notEOF) { - return false; - } - - $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem(); - - $this->moveToPreviousLine(); - - return $ret; - } - - /** - * Returns true if the string is un-indented collection item. - * - * @return bool Returns true if the string is un-indented collection item, false otherwise - */ - private function isStringUnIndentedCollectionItem() - { - return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); - } - - /** - * Tests whether or not the current line is the header of a block scalar. - * - * @return bool - */ - private function isBlockScalarHeader() - { - return (bool) self::pregMatch('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine); - } - - /** - * A local wrapper for `preg_match` which will throw a ParseException if there - * is an internal error in the PCRE engine. - * - * This avoids us needing to check for "false" every time PCRE is used - * in the YAML engine - * - * DRUSH FORK: Renamed from preg_match to pregMatch. Not what I would have - * preferred, but easier / cleaner than customizing phpcs to not flag this - * one warning. - * - * @throws ParseException on a PCRE internal error - * - * @see preg_last_error() - * - * @internal - */ - public static function pregMatch($pattern, $subject, &$matches = null, $flags = 0, $offset = 0) - { - if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) { - switch (preg_last_error()) { - case PREG_INTERNAL_ERROR: - $error = 'Internal PCRE error.'; - break; - case PREG_BACKTRACK_LIMIT_ERROR: - $error = 'pcre.backtrack_limit reached.'; - break; - case PREG_RECURSION_LIMIT_ERROR: - $error = 'pcre.recursion_limit reached.'; - break; - case PREG_BAD_UTF8_ERROR: - $error = 'Malformed UTF-8 data.'; - break; - case PREG_BAD_UTF8_OFFSET_ERROR: - $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; - break; - default: - $error = 'Error.'; - } - - throw new ParseException($error); - } - - return $ret; - } - - /** - * Trim the tag on top of the value. - * - * Prevent values such as `!foo {quz: bar}` to be considered as - * a mapping block. - */ - private function trimTag($value) - { - if ('!' === $value[0]) { - return ltrim(substr($value, 1, strcspn($value, " \r\n", 1)), ' '); - } - - return $value; - } - - private function getLineTag($value, $flags, $nextLineCheck = true) - { - if ('' === $value || '!' !== $value[0] || 1 !== self::pregMatch('/^'.self::TAG_PATTERN.' *( +#.*)?$/', $value, $matches)) { - return; - } - - if ($nextLineCheck && !$this->isNextLineIndented()) { - return; - } - - $tag = substr($matches['tag'], 1); - - // Built-in tags - if ($tag && '!' === $tag[0]) { - throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag)); - } - - if (Yaml::PARSE_CUSTOM_TAGS & $flags) { - return $tag; - } - - throw new ParseException(sprintf('Tags support is not enabled. You must use the flag `Yaml::PARSE_CUSTOM_TAGS` to use "%s".', $matches['tag'])); - } -} diff --git a/src/internal-forks/Config/Yaml/README.md b/src/internal-forks/Config/Yaml/README.md deleted file mode 100644 index 730ce8aca4..0000000000 --- a/src/internal-forks/Config/Yaml/README.md +++ /dev/null @@ -1,20 +0,0 @@ -Yaml Component -============== - -The Yaml component loads and dumps YAML files. - -This is a COPY of symfony/yaml re-namespaced to Drush\Config\Yaml. This is -here so that Drush can parse its yaml config and aliasfiles prior to autoloading -any Symfony packages. This helps avoid dependency conflicts when the global -Drush includes the autoload file from Drupal. - -DO NOT USE THESE CLASSES OUTSIDE OF PREFLIGHT. Instead, use symfony/yaml. - -Resources ---------- - - * [Documentation](https://symfony.com/doc/current/components/yaml/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/src/internal-forks/Config/Yaml/Tag/TaggedValue.php b/src/internal-forks/Config/Yaml/Tag/TaggedValue.php deleted file mode 100644 index 5935bfa67d..0000000000 --- a/src/internal-forks/Config/Yaml/Tag/TaggedValue.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Drush\Internal\Config\Yaml\Tag; - -/** - * @author Nicolas Grekas - * @author Guilhem N. - */ -final class TaggedValue -{ - private $tag; - private $value; - - /** - * @param string $tag - * @param mixed $value - */ - public function __construct($tag, $value) - { - $this->tag = $tag; - $this->value = $value; - } - - /** - * @return string - */ - public function getTag() - { - return $this->tag; - } - - /** - * @return mixed - */ - public function getValue() - { - return $this->value; - } -} diff --git a/src/internal-forks/Config/Yaml/Unescaper.php b/src/internal-forks/Config/Yaml/Unescaper.php deleted file mode 100644 index 02e6edefac..0000000000 --- a/src/internal-forks/Config/Yaml/Unescaper.php +++ /dev/null @@ -1,142 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Drush\Internal\Config\Yaml; - -use Drush\Internal\Config\Yaml\Exception\ParseException; - -/** - * Unescaper encapsulates unescaping rules for single and double-quoted - * YAML strings. - * - * @author Matthew Lewinski - * - * @internal - */ -class Unescaper -{ - /** - * Regex fragment that matches an escaped character in a double quoted string. - */ - const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)'; - - /** - * Unescapes a single quoted string. - * - * @param string $value A single quoted string - * - * @return string The unescaped string - */ - public function unescapeSingleQuotedString($value) - { - return str_replace('\'\'', '\'', $value); - } - - /** - * Unescapes a double quoted string. - * - * @param string $value A double quoted string - * - * @return string The unescaped string - */ - public function unescapeDoubleQuotedString($value) - { - $callback = function ($match) { - return $this->unescapeCharacter($match[0]); - }; - - // evaluate the string - return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); - } - - /** - * Unescapes a character that was found in a double-quoted string. - * - * @param string $value An escaped character - * - * @return string The unescaped character - */ - private function unescapeCharacter($value) - { - switch ($value[1]) { - case '0': - return "\x0"; - case 'a': - return "\x7"; - case 'b': - return "\x8"; - case 't': - return "\t"; - case "\t": - return "\t"; - case 'n': - return "\n"; - case 'v': - return "\xB"; - case 'f': - return "\xC"; - case 'r': - return "\r"; - case 'e': - return "\x1B"; - case ' ': - return ' '; - case '"': - return '"'; - case '/': - return '/'; - case '\\': - return '\\'; - case 'N': - // U+0085 NEXT LINE - return "\xC2\x85"; - case '_': - // U+00A0 NO-BREAK SPACE - return "\xC2\xA0"; - case 'L': - // U+2028 LINE SEPARATOR - return "\xE2\x80\xA8"; - case 'P': - // U+2029 PARAGRAPH SEPARATOR - return "\xE2\x80\xA9"; - case 'x': - return self::utf8chr(hexdec(substr($value, 2, 2))); - case 'u': - return self::utf8chr(hexdec(substr($value, 2, 4))); - case 'U': - return self::utf8chr(hexdec(substr($value, 2, 8))); - default: - throw new ParseException(sprintf('Found unknown escape character "%s".', $value)); - } - } - - /** - * Get the UTF-8 character for the given code point. - * - * @param int $c The unicode code point - * - * @return string The corresponding UTF-8 character - */ - private static function utf8chr($c) - { - if (0x80 > $c %= 0x200000) { - return chr($c); - } - if (0x800 > $c) { - return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); - } - if (0x10000 > $c) { - return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); - } - - return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); - } -} diff --git a/src/internal-forks/Config/Yaml/Yaml.php b/src/internal-forks/Config/Yaml/Yaml.php deleted file mode 100644 index 693aa0f67a..0000000000 --- a/src/internal-forks/Config/Yaml/Yaml.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Drush\Internal\Config\Yaml; - -use Drush\Internal\Config\Yaml\Exception\ParseException; - -/** - * Yaml offers convenience methods to load and dump YAML. - * - * @author Fabien Potencier - */ -class Yaml -{ - const DUMP_OBJECT = 1; - const PARSE_EXCEPTION_ON_INVALID_TYPE = 2; - const PARSE_OBJECT = 4; - const PARSE_OBJECT_FOR_MAP = 8; - const DUMP_EXCEPTION_ON_INVALID_TYPE = 16; - const PARSE_DATETIME = 32; - const DUMP_OBJECT_AS_MAP = 64; - const DUMP_MULTI_LINE_LITERAL_BLOCK = 128; - const PARSE_CONSTANT = 256; - const PARSE_CUSTOM_TAGS = 512; - const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024; - const PARSE_KEYS_AS_STRINGS = 2048; - - /** - * Parses YAML into a PHP value. - * - * Usage: - * - * $array = Yaml::parse(file_get_contents('config.yml')); - * print_r($array); - * - * - * @param string $input A string containing YAML - * @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior - * - * @return mixed The YAML converted to a PHP value - * - * @throws ParseException If the YAML is not valid - */ - public static function parse($input, $flags = 0) - { - if (is_bool($flags)) { - @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED); - - if ($flags) { - $flags = self::PARSE_EXCEPTION_ON_INVALID_TYPE; - } else { - $flags = 0; - } - } - - if (func_num_args() >= 3) { - @trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the PARSE_OBJECT flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(2)) { - $flags |= self::PARSE_OBJECT; - } - } - - if (func_num_args() >= 4) { - @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED); - - if (func_get_arg(3)) { - $flags |= self::PARSE_OBJECT_FOR_MAP; - } - } - - $yaml = new Parser(); - - return $yaml->parse($input, $flags); - } -} diff --git a/sut/drush/Commands/SimpleSutCommands.php b/sut/drush/Commands/SimpleSutCommands.php index 06b6087236..ffccfcbc4a 100644 --- a/sut/drush/Commands/SimpleSutCommands.php +++ b/sut/drush/Commands/SimpleSutCommands.php @@ -5,8 +5,12 @@ use Consolidation\AnnotatedCommand\CommandData; use Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface; use Consolidation\AnnotatedCommand\Events\CustomEventAwareTrait; +use Consolidation\Log\ConsoleLogLevel; use Consolidation\OutputFormatters\StructuredData\RowsOfFields; +use Drush\Drush; +use Drush\Symfony\DrushArgvInput; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Drush\Commands\DrushCommands; @@ -21,7 +25,7 @@ class SimpleSutCommands extends DrushCommands { /** - * Show a fabulous picture. + * Show a message. * * @command sut:simple * @hidden @@ -30,4 +34,23 @@ public function example() { $this->logger()->notice(dt("This is an example site-wide command committed to the repository in the SUT inside of the 'drush/Commands' directory.")); } + + /** + * Replace Drush logger with a custom one. + * + * In a real-world implementation, you would likely use `@hook *` instead of `@hook sut:simple`. + * + * @hook init sut:simple + */ + public function customLogger(DrushArgvInput $argv, AnnotationData $annotationData): void + { + $verbosityLevelMap = [ConsoleLogLevel::SUCCESS => OutputInterface::VERBOSITY_NORMAL]; + $formatLevelMap = [ConsoleLogLevel::SUCCESS => \Psr\Log\LogLevel::INFO]; + // One could use Monolog if desired. + // Drush expects custom loggers to always write to stderr, so dont use ConsoleLogger directly, + $newLogger = new ConsoleLogger(Drush::output(), $verbosityLevelMap, $formatLevelMap); + $drushLoggerManager = $this->logger(); + $drushLoggerManager->reset()->add('foo', $newLogger); + } } + diff --git a/sut/modules/unish/drush_empty_module/drush_empty_module.info.yml b/sut/modules/unish/drush_empty_module/drush_empty_module.info.yml index ebac62f70d..c85994ec0b 100644 --- a/sut/modules/unish/drush_empty_module/drush_empty_module.info.yml +++ b/sut/modules/unish/drush_empty_module/drush_empty_module.info.yml @@ -1,7 +1,7 @@ name: Drush empty module description: 'Drush empty module' core: 8.x -core_version_requirement: ^8 || ^9 +core_version_requirement: ^8 || ^9 || ^10 type: module # Require for the sake of tests - simulate what packaging would do. project: drush_empty_module diff --git a/sut/themes/unish/drush_empty_theme/drush_empty_theme.info.yml b/sut/themes/unish/drush_empty_theme/drush_empty_theme.info.yml index b68f22c9b7..ac6a509e08 100644 --- a/sut/themes/unish/drush_empty_theme/drush_empty_theme.info.yml +++ b/sut/themes/unish/drush_empty_theme/drush_empty_theme.info.yml @@ -1,6 +1,6 @@ name: Drush empty theme description: 'Drush empty theme' core: 8.x -core_version_requirement: ^8 || ^9 +core_version_requirement: ^8 || ^9 || ^10 type: theme base theme: stable diff --git a/tests/fixtures/drush-extensions/DrushExtensionsCommands.php b/tests/fixtures/drush-extensions/DrushExtensionsCommands.php new file mode 100644 index 0000000000..2800829b32 --- /dev/null +++ b/tests/fixtures/drush-extensions/DrushExtensionsCommands.php @@ -0,0 +1,20 @@ +io()->text('Hello world!'); + } +} diff --git a/tests/fixtures/drush-extensions/drush.yml b/tests/fixtures/drush-extensions/drush.yml new file mode 100644 index 0000000000..17a3d4c09f --- /dev/null +++ b/tests/fixtures/drush-extensions/drush.yml @@ -0,0 +1,8 @@ +# +# Drush configuration file used to configure the System Unter Test (sut) +# +# Docs at https://github.com/drush-ops/drush/blob/master/examples/example.drush.yml +# +drush: + commands: + '\Drush\Commands\drush_extensions\DrushExtensionsCommands': '${env.FIXTURES_DIR}/drush-extensions/DrushExtensionsCommands.php' \ No newline at end of file diff --git a/tests/functional/resources/modules/d8/woot/composer.json b/tests/fixtures/modules/woot/composer.json similarity index 89% rename from tests/functional/resources/modules/d8/woot/composer.json rename to tests/fixtures/modules/woot/composer.json index e2b8325324..a2dceee453 100644 --- a/tests/functional/resources/modules/d8/woot/composer.json +++ b/tests/fixtures/modules/woot/composer.json @@ -14,7 +14,7 @@ "extra": { "drush": { "services": { - "drush.services.yml": "^9 || ^10" + "drush.services.yml": "^9 || ^10 || ^11" } } } diff --git a/tests/functional/resources/modules/d8/woot/drush.services.yml b/tests/fixtures/modules/woot/drush.services.yml similarity index 100% rename from tests/functional/resources/modules/d8/woot/drush.services.yml rename to tests/fixtures/modules/woot/drush.services.yml diff --git a/tests/functional/resources/modules/d8/woot/migrations/test_migration.yml b/tests/fixtures/modules/woot/migrations/test_migration.yml similarity index 80% rename from tests/functional/resources/modules/d8/woot/migrations/test_migration.yml rename to tests/fixtures/modules/woot/migrations/test_migration.yml index ab585436ad..7f08ef501c 100644 --- a/tests/functional/resources/modules/d8/woot/migrations/test_migration.yml +++ b/tests/fixtures/modules/woot/migrations/test_migration.yml @@ -4,7 +4,7 @@ migration_tags: - tag1 source: plugin: embedded_data - # Source data is set dynamically in woot_migrate_source_info_alter(). + # Source data is set dynamically in woot_migration_plugins_alter(). data_rows: [] ids: id: diff --git a/tests/functional/resources/modules/d8/woot/migrations/test_migration_source_issues.yml b/tests/fixtures/modules/woot/migrations/test_migration_source_issues.yml similarity index 100% rename from tests/functional/resources/modules/d8/woot/migrations/test_migration_source_issues.yml rename to tests/fixtures/modules/woot/migrations/test_migration_source_issues.yml diff --git a/tests/functional/resources/modules/d8/woot/migrations/test_migration_tagged.yml b/tests/fixtures/modules/woot/migrations/test_migration_tagged.yml similarity index 100% rename from tests/functional/resources/modules/d8/woot/migrations/test_migration_tagged.yml rename to tests/fixtures/modules/woot/migrations/test_migration_tagged.yml diff --git a/tests/functional/resources/modules/d8/woot/migrations/test_migration_untagged.yml b/tests/fixtures/modules/woot/migrations/test_migration_untagged.yml similarity index 100% rename from tests/functional/resources/modules/d8/woot/migrations/test_migration_untagged.yml rename to tests/fixtures/modules/woot/migrations/test_migration_untagged.yml diff --git a/tests/functional/resources/modules/d8/woot/src/Commands/AnnotatedGreetCommand.php b/tests/fixtures/modules/woot/src/Commands/AnnotatedGreetCommand.php similarity index 96% rename from tests/functional/resources/modules/d8/woot/src/Commands/AnnotatedGreetCommand.php rename to tests/fixtures/modules/woot/src/Commands/AnnotatedGreetCommand.php index 1a42b81341..2502ebe491 100644 --- a/tests/functional/resources/modules/d8/woot/src/Commands/AnnotatedGreetCommand.php +++ b/tests/fixtures/modules/woot/src/Commands/AnnotatedGreetCommand.php @@ -1,4 +1,5 @@ getArgument('name'); if ($name) { - $text = 'Hello '.$name; + $text = 'Hello ' . $name; } else { $text = 'Hello'; } diff --git a/tests/functional/resources/modules/d8/woot/src/Commands/GreetCommand.php b/tests/fixtures/modules/woot/src/Commands/GreetCommand.php similarity index 97% rename from tests/functional/resources/modules/d8/woot/src/Commands/GreetCommand.php rename to tests/fixtures/modules/woot/src/Commands/GreetCommand.php index cd52c0ce24..39fca6af0e 100644 --- a/tests/functional/resources/modules/d8/woot/src/Commands/GreetCommand.php +++ b/tests/fixtures/modules/woot/src/Commands/GreetCommand.php @@ -1,4 +1,5 @@ getArgument('name'); if ($name) { - $text = 'Hello '.$name; + $text = 'Hello ' . $name; } else { $text = 'Hello'; } diff --git a/tests/functional/resources/modules/d8/woot/src/Commands/WootCommands.php b/tests/fixtures/modules/woot/src/Commands/WootCommands.php similarity index 99% rename from tests/functional/resources/modules/d8/woot/src/Commands/WootCommands.php rename to tests/fixtures/modules/woot/src/Commands/WootCommands.php index 9f8596eb22..feed2432d8 100644 --- a/tests/functional/resources/modules/d8/woot/src/Commands/WootCommands.php +++ b/tests/fixtures/modules/woot/src/Commands/WootCommands.php @@ -1,4 +1,5 @@ get('woot.test_migrate_trigger_failures')) { + if (\Drupal::state()->get('woot.migrate_runner.trigger_failures')) { throw new \RuntimeException('Earthquake while rolling back'); } } diff --git a/tests/functional/resources/modules/d8/woot/src/EventSubscriber/ProcessRowTestSubscriber.php b/tests/fixtures/modules/woot/src/EventSubscriber/ProcessRowTestSubscriber.php similarity index 93% rename from tests/functional/resources/modules/d8/woot/src/EventSubscriber/ProcessRowTestSubscriber.php rename to tests/fixtures/modules/woot/src/EventSubscriber/ProcessRowTestSubscriber.php index 7ea64f8b25..fd55068d45 100644 --- a/tests/functional/resources/modules/d8/woot/src/EventSubscriber/ProcessRowTestSubscriber.php +++ b/tests/fixtures/modules/woot/src/EventSubscriber/ProcessRowTestSubscriber.php @@ -28,7 +28,7 @@ public function onPrepareRow(MigratePrepareRowEvent $event) { // Log only in MigrateRunnerTest::testMigrateImportAndRollback() test. // @see \Unish\MigrateRunnerTest::testMigrateImportAndRollback() - if (\Drupal::state()->get('woot.test_migrate_trigger_failures')) { + if (\Drupal::state()->get('woot.migrate_runner.trigger_failures')) { \Drush\Drush::logger() ->notice("MigrateEvents::DRUSH_MIGRATE_PREPARE_ROW fired for row with ID {$event->getRow()->getSourceProperty('id')}"); } diff --git a/tests/functional/resources/modules/d8/woot/src/Generators/ExampleGenerator.php b/tests/fixtures/modules/woot/src/Generators/ExampleGenerator.php similarity index 100% rename from tests/functional/resources/modules/d8/woot/src/Generators/ExampleGenerator.php rename to tests/fixtures/modules/woot/src/Generators/ExampleGenerator.php diff --git a/tests/functional/resources/modules/d8/woot/src/Generators/example-generator.twig b/tests/fixtures/modules/woot/src/Generators/example-generator.twig similarity index 100% rename from tests/functional/resources/modules/d8/woot/src/Generators/example-generator.twig rename to tests/fixtures/modules/woot/src/Generators/example-generator.twig diff --git a/tests/functional/resources/modules/d8/woot/src/Plugin/QueueWorker/CustomException.php b/tests/fixtures/modules/woot/src/Plugin/QueueWorker/CustomException.php similarity index 100% rename from tests/functional/resources/modules/d8/woot/src/Plugin/QueueWorker/CustomException.php rename to tests/fixtures/modules/woot/src/Plugin/QueueWorker/CustomException.php diff --git a/tests/functional/resources/modules/d8/woot/src/Plugin/QueueWorker/WootCustomException.php b/tests/fixtures/modules/woot/src/Plugin/QueueWorker/WootCustomException.php similarity index 99% rename from tests/functional/resources/modules/d8/woot/src/Plugin/QueueWorker/WootCustomException.php rename to tests/fixtures/modules/woot/src/Plugin/QueueWorker/WootCustomException.php index 122cc18ad4..fd890e9d13 100644 --- a/tests/functional/resources/modules/d8/woot/src/Plugin/QueueWorker/WootCustomException.php +++ b/tests/fixtures/modules/woot/src/Plugin/QueueWorker/WootCustomException.php @@ -15,7 +15,6 @@ */ class WootCustomException extends QueueWorkerBase { - /** * {@inheritdoc} */ diff --git a/tests/functional/resources/modules/d8/woot/src/Plugin/QueueWorker/WootRequeueException.php b/tests/fixtures/modules/woot/src/Plugin/QueueWorker/WootRequeueException.php similarity index 99% rename from tests/functional/resources/modules/d8/woot/src/Plugin/QueueWorker/WootRequeueException.php rename to tests/fixtures/modules/woot/src/Plugin/QueueWorker/WootRequeueException.php index 5ee43fe910..19da1b4dc7 100644 --- a/tests/functional/resources/modules/d8/woot/src/Plugin/QueueWorker/WootRequeueException.php +++ b/tests/fixtures/modules/woot/src/Plugin/QueueWorker/WootRequeueException.php @@ -16,7 +16,6 @@ */ class WootRequeueException extends QueueWorkerBase { - /** * {@inheritdoc} */ diff --git a/tests/functional/resources/modules/d8/woot/src/Plugin/migrate/process/TestFailProcess.php b/tests/fixtures/modules/woot/src/Plugin/migrate/process/TestFailProcess.php similarity index 94% rename from tests/functional/resources/modules/d8/woot/src/Plugin/migrate/process/TestFailProcess.php rename to tests/fixtures/modules/woot/src/Plugin/migrate/process/TestFailProcess.php index 91bde4f418..a876dc9d96 100644 --- a/tests/functional/resources/modules/d8/woot/src/Plugin/migrate/process/TestFailProcess.php +++ b/tests/fixtures/modules/woot/src/Plugin/migrate/process/TestFailProcess.php @@ -21,7 +21,7 @@ public function transform($value, MigrateExecutableInterface $migrate_executable { // Fail only in MigrateRunnerTest::testMigrateImportAndRollback() test. // @see \Unish\MigrateRunnerTest::testMigrateImportAndRollback() - if (in_array($value, [2, 9, 17]) && \Drupal::state()->get('woot.test_migrate_trigger_failures')) { + if (in_array($value, [2, 9, 17]) && \Drupal::state()->get('woot.migrate_runner.trigger_failures')) { throw new MigrateException("ID {$value} should fail"); } return $value; diff --git a/tests/functional/resources/modules/d8/woot/src/Plugin/migrate/source/TestSourceWithMissedRequirements.php b/tests/fixtures/modules/woot/src/Plugin/migrate/source/TestSourceWithMissedRequirements.php similarity index 100% rename from tests/functional/resources/modules/d8/woot/src/Plugin/migrate/source/TestSourceWithMissedRequirements.php rename to tests/fixtures/modules/woot/src/Plugin/migrate/source/TestSourceWithMissedRequirements.php diff --git a/tests/functional/resources/modules/d8/woot/src/WootCommandInfoAlterer.php b/tests/fixtures/modules/woot/src/WootCommandInfoAlterer.php similarity index 100% rename from tests/functional/resources/modules/d8/woot/src/WootCommandInfoAlterer.php rename to tests/fixtures/modules/woot/src/WootCommandInfoAlterer.php diff --git a/tests/functional/resources/modules/d8/woot/src/WootManager.php b/tests/fixtures/modules/woot/src/WootManager.php similarity index 100% rename from tests/functional/resources/modules/d8/woot/src/WootManager.php rename to tests/fixtures/modules/woot/src/WootManager.php diff --git a/tests/functional/resources/modules/d8/woot/woot.deploy.php b/tests/fixtures/modules/woot/woot.deploy.php similarity index 100% rename from tests/functional/resources/modules/d8/woot/woot.deploy.php rename to tests/fixtures/modules/woot/woot.deploy.php diff --git a/tests/functional/resources/modules/d8/woot/woot.info.yml b/tests/fixtures/modules/woot/woot.info.yml similarity index 59% rename from tests/functional/resources/modules/d8/woot/woot.info.yml rename to tests/fixtures/modules/woot/woot.info.yml index 67be3af9f2..7fe4f3a572 100644 --- a/tests/functional/resources/modules/d8/woot/woot.info.yml +++ b/tests/fixtures/modules/woot/woot.info.yml @@ -1,6 +1,5 @@ name: Woot type: module description: Woot Mightily. -core: 8.x -core_version_requirement: ^8 || ^9 +core_version_requirement: ^9 || ^10 package: Other diff --git a/tests/functional/resources/modules/d8/woot/woot.install b/tests/fixtures/modules/woot/woot.install similarity index 100% rename from tests/functional/resources/modules/d8/woot/woot.install rename to tests/fixtures/modules/woot/woot.install diff --git a/tests/functional/resources/modules/d8/woot/woot.module b/tests/fixtures/modules/woot/woot.module similarity index 89% rename from tests/functional/resources/modules/d8/woot/woot.module rename to tests/fixtures/modules/woot/woot.module index 37252e01ae..9ce7730131 100644 --- a/tests/functional/resources/modules/d8/woot/woot.module +++ b/tests/fixtures/modules/woot/woot.module @@ -49,7 +49,7 @@ function woot_get_taxonomy_term_entity_type_id() function woot_migration_plugins_alter(array &$migration_plugins): void { // Dynamically create the test_migration source data. - $size = \Drupal::state()->get('woot.test_migration_source_data_amount', 2); + $size = \Drupal::state()->get('woot.migrate_runner.source_data_amount', 2); foreach (range(1, $size) as $id) { $migration_plugins['test_migration']['source']['data_rows'][] = [ 'id' => $id, @@ -58,7 +58,7 @@ function woot_migration_plugins_alter(array &$migration_plugins): void } // Allow tests to remove rows from the source. - if ($removedRows = \Drupal::state()->get('woot.test_migration_source_removed_rows')) { + if ($removedRows = \Drupal::state()->get('woot.migrate_runner.removed_source_rows')) { $removedRows = explode(',', $removedRows); $migration_plugins['test_migration']['source']['data_rows'] = array_filter( $migration_plugins['test_migration']['source']['data_rows'], @@ -69,7 +69,7 @@ function woot_migration_plugins_alter(array &$migration_plugins): void $migration_plugins['test_migration']['source']['data_rows'] = array_values($migration_plugins['test_migration']['source']['data_rows']); } - if (\Drupal::state()->get('woot.test_command_progress_bar.skip_count')) { + if (\Drupal::state()->get('woot.migrate_runner.progress_bar_skip_count')) { // Test the command progress bar for sources that skip count. // @see \Unish\MigrateRunnerTest::testCommandProgressBar() $migration_plugins['test_migration']['source']['skip_count'] = true; diff --git a/tests/functional/resources/modules/d8/woot/woot.post_update.php b/tests/fixtures/modules/woot/woot.post_update.php similarity index 100% rename from tests/functional/resources/modules/d8/woot/woot.post_update.php rename to tests/fixtures/modules/woot/woot.post_update.php diff --git a/tests/functional/resources/modules/d8/woot/woot.routing.yml b/tests/fixtures/modules/woot/woot.routing.yml similarity index 100% rename from tests/functional/resources/modules/d8/woot/woot.routing.yml rename to tests/fixtures/modules/woot/woot.routing.yml diff --git a/tests/functional/resources/modules/d8/woot/woot.services.yml b/tests/fixtures/modules/woot/woot.services.yml similarity index 100% rename from tests/functional/resources/modules/d8/woot/woot.services.yml rename to tests/fixtures/modules/woot/woot.services.yml diff --git a/tests/fixtures/sitealiases/legacy/pantheon.aliases.drushrc.php b/tests/fixtures/sitealiases/legacy/pantheon.aliases.drushrc.php index 145ec405b0..52c2654687 100644 --- a/tests/fixtures/sitealiases/legacy/pantheon.aliases.drushrc.php +++ b/tests/fixtures/sitealiases/legacy/pantheon.aliases.drushrc.php @@ -1,4 +1,5 @@ setUpDrupal(1, true); diff --git a/tests/functional/CommandInfoAlterTest.php b/tests/functional/CommandInfoAlterTest.php index 1c5e1b23f9..d0a3b0d8c8 100644 --- a/tests/functional/CommandInfoAlterTest.php +++ b/tests/functional/CommandInfoAlterTest.php @@ -18,7 +18,7 @@ class CommandInfoAlterTest extends CommandUnishTestCase public function testCommandInfoAlter() { $this->setUpDrupal(1, true); - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm-enable', ['woot']); $this->drush('woot:altered', [], ['help' => true, 'debug' => true]); $this->assertStringNotContainsString('woot-initial-alias', $this->getOutput()); diff --git a/tests/functional/Commands/TestFixtureCommands.php b/tests/functional/Commands/TestFixtureCommands.php index e35819196f..50a9ec506b 100644 --- a/tests/functional/Commands/TestFixtureCommands.php +++ b/tests/functional/Commands/TestFixtureCommands.php @@ -4,6 +4,7 @@ * @file * Commands which are useful for unit tests. */ + namespace Drush\Commands; use Drupal\Core\DrupalKernel; @@ -14,7 +15,6 @@ class TestFixtureCommands extends DrushCommands implements AutoloaderAwareInterface { - use AutoloaderAwareTrait; // Obsolete: @@ -126,7 +126,7 @@ protected function loadDrupalAutoloader($drupal_root) { static $autoloader = false; - $autoloadFilePath = $drupal_root .'/autoload.php'; + $autoloadFilePath = $drupal_root . '/autoload.php'; if (!$autoloader && file_exists($autoloadFilePath)) { $autoloader = require $autoloadFilePath; } diff --git a/tests/functional/ConfigPullTest.php b/tests/functional/ConfigPullTest.php index 381ef15580..711e70c3e5 100644 --- a/tests/functional/ConfigPullTest.php +++ b/tests/functional/ConfigPullTest.php @@ -1,4 +1,5 @@ setUpDrupal(2, true); diff --git a/tests/functional/ConfigTest.php b/tests/functional/ConfigTest.php index e0381418ac..f10c3d9f7d 100644 --- a/tests/functional/ConfigTest.php +++ b/tests/functional/ConfigTest.php @@ -11,7 +11,7 @@ * @group commands * @group config */ -class ConfigCase extends CommandUnishTestCase +class ConfigTest extends CommandUnishTestCase { use TestModuleHelperTrait; @@ -25,11 +25,38 @@ public function setup(): void } } + /** + * @todo If this becomes an integration test, add test for stdin handling. + */ public function testConfigGetSet() { + // Simple value $this->drush('config:set', ['system.site', 'name', 'config_test']); $this->drush('config:get', ['system.site', 'name']); - $this->assertEquals("'system.site:name': config_test", $this->getOutput(), 'Config was successfully set and get.'); + $this->assertEquals("'system.site:name': config_test", $this->getOutput()); + + // Nested value + $this->drush('config:set', ['system.site', 'page.front', 'llama']); + $this->drush('config:get', ['system.site', 'page.front']); + $this->assertEquals("'system.site:page.front': llama", $this->getOutput()); + + // Simple sequence value + $this->drush('config:set', ['user.role.authenticated', 'permissions', '[foo,bar]'], ['input-format' => 'yaml']); + $this->drush('config:get', ['user.role.authenticated', 'permissions'], ['format' => 'json']); + $output = $this->getOutputFromJSON('user.role.authenticated:permissions'); + + // Mapping value + $this->drush('config:set', ['system.site', 'page', "{403: '403', front: home}"], ['input-format' => 'yaml']); + $this->drush('config:get', ['system.site', 'page'], ['format' => 'json']); + $output = $this->getOutputFromJSON('system.site:page'); + $this->assertSame(['403' => '403', 'front' => 'home'], $output); + + // Multiple top-level keys + $this->drush('config:set', ['user.role.authenticated', '?', "{label: 'Auth user', weight: 5}"], ['input-format' => 'yaml']); + $this->drush('config:get', ['user.role.authenticated'], ['format' => 'json']); + $output = $this->getOutputFromJSON(); + $this->assertSame('Auth user', $output['label']); + $this->assertSame(5, $output['weight']); } public function testConfigExportImportStatusExistingConfig() @@ -73,25 +100,20 @@ public function testConfigExportImportStatusExistingConfig() // Test the --existing-config option for site:install. $this->drush('core:status', [], ['field' => 'drupal-version']); $drupal_version = $this->getOutputRaw(); - if (Comparator::greaterThanOrEqualTo($drupal_version, '8.6')) { - $contents = file_get_contents($system_site_yml); - $contents = preg_replace('/front: .*/', 'front: unish existing', $contents); - file_put_contents($system_site_yml, $contents); - $this->installDrupal('dev', true, ['existing-config' => true], false); - $this->drush('config-get', ['system.site', 'page'], ['format' => 'json']); - $page = $this->getOutputFromJSON('system.site:page'); - $this->assertStringContainsString('unish existing', $page['front'], 'Existing config was successfully imported during site:install.'); - } + $contents = file_get_contents($system_site_yml); + $contents = preg_replace('/front: .*/', 'front: unish existing', $contents); + file_put_contents($system_site_yml, $contents); + $this->installDrupal('dev', true, ['existing-config' => true], false); + $this->drush('config-get', ['system.site', 'page'], ['format' => 'json']); + $page = $this->getOutputFromJSON('system.site:page'); + $this->assertStringContainsString('unish existing', $page['front'], 'Existing config was successfully imported during site:install.'); // Similar, but this time via --partial option. - if ($this->isDrupalGreaterThanOrEqualTo('8.8.0')) { - $this->markTestSkipped('Partial config import not yet working on 8.8.0'); - } $contents = file_get_contents($system_site_yml); $contents = preg_replace('/front: .*/', 'front: unish partial', $contents); $partial_path = self::getSandbox() . '/partial'; $this->mkdir($partial_path); - $contents = file_put_contents($partial_path. '/system.site.yml', $contents); + $contents = file_put_contents($partial_path . '/system.site.yml', $contents); $this->drush('config-import', [], ['partial' => null, 'source' => $partial_path]); $this->drush('config-get', ['system.site', 'page'], ['format' => 'json']); $page = $this->getOutputFromJSON('system.site:page'); @@ -103,7 +125,7 @@ public function testConfigImport() $options = [ 'include' => __DIR__, ]; - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm-enable', ['woot'], $options); // Export the configuration. @@ -159,6 +181,6 @@ class: Drupal\woot\DependingService protected function getConfigSyncDir() { $this->drush('core:status', [], ['format' => 'json', 'fields' => 'config-sync']); - return $this->webroot().'/'.$this->getOutputFromJSON('config-sync'); + return $this->webroot() . '/' . $this->getOutputFromJSON('config-sync'); } } diff --git a/tests/functional/ContainerTest.php b/tests/functional/ContainerTest.php index 91653ef5be..9b034e23ce 100644 --- a/tests/functional/ContainerTest.php +++ b/tests/functional/ContainerTest.php @@ -23,7 +23,7 @@ public function testContainer() $this->setUpDrupal(1, true); // Copy the 'woot' module over to the Drupal site we just set up. - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); // Enable our module. $this->drush('pm-enable', ['woot']); diff --git a/tests/functional/CoreTest.php b/tests/functional/CoreTest.php index 5c3275748b..80d5f034e8 100644 --- a/tests/functional/CoreTest.php +++ b/tests/functional/CoreTest.php @@ -10,7 +10,7 @@ * * @group commands */ -class CoreCase extends CommandUnishTestCase +class CoreTest extends CommandUnishTestCase { public function setup(): void { @@ -47,7 +47,8 @@ public function testOptionsUri() 'format' => 'json', 'uri' => 'OMIT', // A special value which causes --uri to not be specified. ]; - foreach ([ + foreach ( + [ 'test.uri' => ['http://test.uri', 'sites/dev'], 'test.uri/' => ['http://test.uri/', 'sites/dev'], 'test.uri/subpath' => ['http://test.uri/subpath', 'sites/stage'], @@ -60,7 +61,8 @@ public function testOptionsUri() 'https://test.uri/' => ['https://test.uri/', 'sites/dev'], 'https://test.uri/subpath' => ['https://test.uri/subpath', 'sites/stage'], 'https://test.uri/subpath/' => ['https://test.uri/subpath/', 'sites/stage'], - ] as $test_uri => $expected) { + ] as $test_uri => $expected + ) { // Put a yml file in the drush folder. $config_options = [ 'options' => [ @@ -84,7 +86,8 @@ public function testOptionsUriRequestUrl() $command_options = [ 'uri' => 'OMIT', // A special value which causes --uri to not be specified. ]; - foreach ([ + foreach ( + [ 'test.uri' => 'http://test.uri', 'test.uri/' => 'http://test.uri', 'test.uri/subpath' => 'http://test.uri/subpath', @@ -97,7 +100,8 @@ public function testOptionsUriRequestUrl() 'https://test.uri/' => 'https://test.uri', 'https://test.uri/subpath' => 'https://test.uri/subpath', 'https://test.uri/subpath/' => 'https://test.uri/subpath', - ] as $test_uri => $expected) { + ] as $test_uri => $expected + ) { // Put a yml file in the drush folder. $config_options = [ 'options' => [ @@ -109,7 +113,7 @@ public function testOptionsUriRequestUrl() unlink($drush_config_file); $output = $this->getOutputRaw(); // Include the test URI, for some context in errors. - $i=10; + $i = 10; $this->assertEquals([$test_uri => $expected], [$test_uri => trim($output)]); } } @@ -171,4 +175,33 @@ public function testRecursiveConfigLoading() $this->assertContains($b_drush_config_file, $output['drush-conf'], "Loaded drush config files are: " . $drush_conf_as_string); $this->assertEquals($test_uri, $output['uri']); } + + public function testSiteSpecificConfigLoading() + { + $sandbox = $this->getSandbox(); + + foreach (['dev', 'stage'] as $site) { + $site_tmp_dir = "{$sandbox}/tmp/{$site}"; + $drush_config_file = Path::join($this->webroot(), "/sites/{$site}/drush.yml"); + + $drush_config_yml = [ + 'drush' => [] + ]; + + file_put_contents($drush_config_file, Yaml::dump($drush_config_yml, PHP_INT_MAX, 2)); + + $options = [ + 'uri' => $site, + 'format' => 'json', + ]; + + // Test that the site-specific config file is loaded. + $this->drush('core-status', [], $options); + $output = $this->getOutputFromJSON(); + $loaded = array_flip($output['drush-conf']); + $this->assertArrayHasKey("sites/{$site}/drush.yml", $loaded); + + unlink($drush_config_file); + } + } } diff --git a/tests/functional/DeployHookTest.php b/tests/functional/DeployHookTest.php index 99336ce4a6..ac2efbe853 100644 --- a/tests/functional/DeployHookTest.php +++ b/tests/functional/DeployHookTest.php @@ -19,7 +19,7 @@ public function testDeployHooks() $options = [ 'yes' => null, ]; - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm-enable', ['woot'], $options); // Run deploy hooks. @@ -59,7 +59,7 @@ public function testDeployHooks() public function testSkipDeployHooks() { $this->setUpDrupal(1, true); - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm-enable', ['woot'], ['yes' => null]); $options = [ diff --git a/tests/functional/FieldCreateTest.php b/tests/functional/FieldCreateTest.php deleted file mode 100644 index dc93cc736f..0000000000 --- a/tests/functional/FieldCreateTest.php +++ /dev/null @@ -1,60 +0,0 @@ -getSites()) { - $this->setUpDrupal(1, true); - // Create a content entity with bundles. - CreateEntityType::createContentEntity($this); - $this->drush('pm-enable', ['text,field_ui,unish_article']); - $this->drush('php:script', ['create_unish_article_bundles'], ['script-path' => Path::join(__DIR__, 'resources')]); - } - } - - public function testFieldCreate() - { - // Arguments. - $this->drush('field:create', [], [], null, null, self::EXIT_ERROR); - $this->assertStringContainsString('Not enough arguments (missing: "entityType")', $this->getErrorOutputRaw()); - $this->drush('field:create', ['foo'], [], null, null, self::EXIT_ERROR); - $this->assertStringContainsString('Entity type with id \'foo\' does not exist.', $this->getErrorOutputRaw()); - $this->drush('field:create', ['user'], [], null, null, self::EXIT_ERROR); - $this->assertStringNotContainsString('The bundle argument is required.', $this->getErrorOutputRaw()); - $this->drush('field:create', ['user', 'user'], [], null, null, self::EXIT_ERROR); - $this->assertStringNotContainsString('bundle', $this->getErrorOutputRaw()); - - // New field storage - $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test2', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => '-1'], null, null, self::EXIT_ERROR); - $this->assertStringContainsString('The target-type option is required.', $this->getErrorOutputRaw()); - /// @todo --target-bundle not yet validated. - // $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test3', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => '-1', 'target-type' => 'unish_article', 'target-bundle' => 'NO-EXIST']); - // $this->assertStringContainsString('TODO', $this->getErrorOutputRaw()); - $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test3', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => '-1', 'target-type' => 'unish_article', 'target-bundle' => 'beta']); - $this->assertStringContainsString("Successfully created field 'field_test3' on unish_article type with bundle 'alpha'", $this->getErrorOutputRaw()); - $this->assertStringContainsString("http://dev/admin/structure/unish_article_types/manage/alpha/fields/unish_article.alpha.field_test3", $this->getSimplifiedErrorOutput()); - $php = "return Drupal::entityTypeManager()->getStorage('field_config')->load('unish_article.alpha.field_test3')->getSettings()"; - $this->drush('php:eval', [$php], ['format' => 'json']); - $settings = $this->getOutputFromJSON(); - $this->assertSame('unish_article', $settings['target_type']); - $this->assertEquals(['beta' => 'beta'], $settings['handler_settings']['target_bundles']); - $this->drush('field:create', ['unish_article', 'alpha'], ['field-name' => 'field_test3', 'field-label' => 'Body'], null, null, self::EXIT_ERROR); - $this->assertStringContainsString('--existing option', $this->getSimplifiedErrorOutput()); - - // Existing storage - $this->drush('field:create', ['unish_article', 'beta'], ['existing-field-name' => 'field_test3', 'field-label' => 'Body', 'field-widget' => 'text_textarea_with_summary']); - $this->assertStringContainsString('Success', $this->getErrorOutputRaw()); - $this->drush('field:create', ['unish_article', 'beta'], ['existing-field-name' => 'field_test3', 'field-label' => 'Body', 'field-widget' => 'text_textarea_with_summary'], null, null, self::EXIT_ERROR); - $this->assertStringContainsString('Field with name \'field_test3\' already exists on bundle \'beta\'', $this->getErrorOutputRaw()); - } -} diff --git a/tests/functional/FieldTest.php b/tests/functional/FieldTest.php new file mode 100644 index 0000000000..80bbc45df3 --- /dev/null +++ b/tests/functional/FieldTest.php @@ -0,0 +1,117 @@ +getSites()) { + $this->setUpDrupal(1, true); + // Create a content entity with bundles. + CreateEntityType::createContentEntity($this); + $this->drush('pm-enable', ['text,field_ui,unish_article']); + $this->drush('php:script', ['create_unish_article_bundles'], ['script-path' => Path::join(__DIR__, 'resources')]); + } + } + + public function testFieldCreate() + { + // Arguments. + $this->drush('field:create', [], [], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('The entityType argument is required', $this->getErrorOutputRaw()); + $this->drush('field:create', ['foo'], [], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('Entity type with id \'foo\' does not exist.', $this->getErrorOutputRaw()); + $this->drush('field:create', ['unish_article'], [], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('The bundle argument is required.', $this->getErrorOutputRaw()); + $this->drush('field:create', ['user', 'user'], [], null, null, self::EXIT_ERROR); + $this->assertStringNotContainsString('bundle', $this->getErrorOutputRaw()); + + // New field storage + $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test2', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('The target-type option is required.', $this->getErrorOutputRaw()); + /// @todo --target-bundle not yet validated. + // $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test3', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => '-1', 'target-type' => 'unish_article', 'target-bundle' => 'NO-EXIST']); + // $this->assertStringContainsString('TODO', $this->getErrorOutputRaw()); + $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test3', 'field-type' => 'entity_reference', 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, 'target-type' => 'unish_article', 'target-bundle' => 'beta']); + $this->assertStringContainsString("Successfully created field 'field_test3' on unish_article type with bundle 'alpha'", $this->getErrorOutputRaw()); + $this->assertStringContainsString("http://dev/admin/structure/unish_article_types/manage/alpha/fields/unish_article.alpha.field_test3", $this->getSimplifiedErrorOutput()); + $php = "return Drupal::entityTypeManager()->getStorage('field_config')->load('unish_article.alpha.field_test3')->getSettings()"; + $this->drush('php:eval', [$php], ['format' => 'json']); + $settings = $this->getOutputFromJSON(); + $this->assertSame('unish_article', $settings['target_type']); + $this->assertEquals(['beta' => 'beta'], $settings['handler_settings']['target_bundles']); + $this->drush('field:create', ['unish_article', 'alpha'], ['field-name' => 'field_test3', 'field-label' => 'Body'], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('--existing option', $this->getSimplifiedErrorOutput()); + + // Existing storage + $this->drush('field:create', ['unish_article', 'beta'], ['existing-field-name' => 'field_test3', 'field-label' => 'Body', 'field-widget' => 'text_textarea_with_summary']); + $this->assertStringContainsString('Success', $this->getErrorOutputRaw()); + $this->drush('field:create', ['unish_article', 'beta'], ['existing-field-name' => 'field_test3', 'field-label' => 'Body', 'field-widget' => 'text_textarea_with_summary'], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('Field with name \'field_test3\' already exists on bundle \'beta\'', $this->getErrorOutputRaw()); + } + + public function testFieldInfo() + { + $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test4', 'field-description' => 'baz', 'field-type' => 'entity_reference', 'is-required' => true, 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, 'target-type' => 'unish_article', 'target-bundle' => 'beta']); + $this->assertStringContainsString("Successfully created field 'field_test4' on unish_article type with bundle 'alpha'", $this->getSimplifiedErrorOutput()); + + $this->drush('field:info', ['unish_article'], [], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('The bundle argument is required.', $this->getSimplifiedErrorOutput()); + $this->drush('field:info', ['unish_article', 'alpha'], ['format' => 'json', 'fields' => '*']); + $json = $this->getOutputFromJSON('field_test4'); + $this->assertSame('field_test4', $json['field_name']); + $this->assertTrue($json['required']); + $this->assertSame('entity_reference', $json['field_type']); + $this->assertSame('baz', $json['description']); + $this->assertSame(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, $json['cardinality']); + $this->assertFalse($json['translatable']); + $this->assertArrayHasKey('beta', $json['target_bundles']); + } + + public function testFieldDelete() + { + $this->drush('field:create', ['unish_article', 'alpha'], ['field-label' => 'Test', 'field-name' => 'field_test5', 'field-description' => 'baz', 'field-type' => 'entity_reference', 'is-required' => true, 'field-widget' => 'entity_reference_autocomplete', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, 'target-type' => 'unish_article', 'target-bundle' => 'beta']); + $this->assertStringContainsString("Successfully created field 'field_test5' on unish_article type with bundle 'alpha'", $this->getSimplifiedErrorOutput()); + + $this->drush('field:delete', ['unish_article'], [], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('The bundle argument is required.', $this->getErrorOutputRaw()); + $this->drush('field:delete', ['unish_article', 'alpha'], [], null, null, self::EXIT_ERROR); + $this->assertStringContainsString('The field-name option is required.', $this->getErrorOutputRaw()); + + $this->drush('field:delete', ['unish_article', 'alpha'], ['field-name' => 'field_testZZZZZ'], null, null, self::EXIT_ERROR); + $this->assertStringContainsString("Field with name 'field_testZZZZZ' does not exist on bundle 'alpha'", $this->getErrorOutputRaw()); + $this->drush('field:delete', ['unish_article', 'alpha'], ['field-name' => 'field_test5']); + $this->assertStringContainsString(" The field Test has been deleted from the Alpha bundle.", $this->getErrorOutputRaw()); + } + + public function testFieldBaseInfo() + { + $this->drush('field:base-info', ['user'], ['format' => 'json', 'fields' => '*']); + $json = $this->getOutputFromJSON(); + $this->assertArrayHasKey('name', $json); + $this->assertSame('Name', $json['name']['label']); + } + + public function testFieldBaseCreateOverride() + { + $options = [ + 'field-name' => 'name', + 'field-label' => 'Handle', + 'field-description' => 'The way this person wishes to called', + 'is-required' => true, + ]; + $this->drush('field:base-override-create', ['user', 'user'], $options); + $this->drush('config:get', ['core.base_field_override.user.user.name'], ['format' => 'json']); + $json = $this->getOutputFromJSON(); + $this->assertSame('Handle', $json['label']); + $this->assertSame(true, $json['required']); + } +} diff --git a/tests/functional/LanguageAddTest.php b/tests/functional/LanguageAddTest.php index f886a97793..447ea670ba 100644 --- a/tests/functional/LanguageAddTest.php +++ b/tests/functional/LanguageAddTest.php @@ -10,7 +10,7 @@ * @group slow * @group pm */ -class LanguageAddCase extends CommandUnishTestCase +class LanguageAddTest extends CommandUnishTestCase { protected function setup(): void { diff --git a/tests/functional/PmEnDisUnListInfoTest.php b/tests/functional/PmEnDisUnListInfoTest.php index 4eff2dad93..1c3adddb51 100644 --- a/tests/functional/PmEnDisUnListInfoTest.php +++ b/tests/functional/PmEnDisUnListInfoTest.php @@ -13,9 +13,8 @@ * @group slow * @group pm */ -class EnDisUnListInfoCase extends CommandUnishTestCase +class EnDisUnListInfoTest extends CommandUnishTestCase { - public function testEnDisUnList() { $sites = $this->setUpDrupal(1, true); @@ -48,13 +47,7 @@ public function testEnDisUnList() $drupal_version = $this->getOutputRaw(); // Test the testing install profile theme is installed. - // Since Drupal 8.8, stark is the default testing theme. - // https://www.drupal.org/node/3083055. - // TODO: Replace once Drupal 8.7 is no longer supported. $active_theme = 'stark'; - if (Comparator::lessThan($drupal_version, '8.8')) { - $active_theme = 'classy'; - } $this->assertStringContainsString($active_theme, $out, 'Themes are in the pm-list'); // Test cache was cleared after enabling a module. diff --git a/tests/functional/PmEnLocaleImportTest.php b/tests/functional/PmEnLocaleImportTest.php index 547c74d0ef..64c9d38e53 100644 --- a/tests/functional/PmEnLocaleImportTest.php +++ b/tests/functional/PmEnLocaleImportTest.php @@ -11,9 +11,8 @@ * @group slow * @group pm */ -class PmEnLocaleImportCase extends CommandUnishTestCase +class PmEnLocaleImportTest extends CommandUnishTestCase { - public function testBatchImportTranslations() { $info_yml = Path::join($this->webroot(), 'modules/unish/drush_empty_module/drush_empty_module.info.yml'); diff --git a/tests/functional/QueueTest.php b/tests/functional/QueueTest.php index 682e73dc5a..4a32aee3b2 100644 --- a/tests/functional/QueueTest.php +++ b/tests/functional/QueueTest.php @@ -7,7 +7,7 @@ /** * @group commands */ -class QueueCase extends CommandUnishTestCase +class QueueTest extends CommandUnishTestCase { use TestModuleHelperTrait; @@ -69,7 +69,7 @@ public function testRequeueException() $sites = $this->setUpDrupal(1, true); // Copy the 'woot' module over to the Drupal site we just set up. - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); // Enable woot module, which contains a queue worker that throws a // RequeueException. @@ -110,7 +110,7 @@ public function testCustomException() $this->setUpDrupal(1, true); // Copy the 'woot' module over to the Drupal site we just set up. - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); // Enable woot module, which contains a queue worker that throws a // custom exception. diff --git a/tests/functional/RedispatchTest.php b/tests/functional/RedispatchTest.php index 6b2f22572a..e2ff0796ea 100644 --- a/tests/functional/RedispatchTest.php +++ b/tests/functional/RedispatchTest.php @@ -9,7 +9,6 @@ */ class RedispatchTest extends CommandUnishTestCase { - /** * Covers the following origin responsibilities. * - A remote host is recognized in site specification. diff --git a/tests/functional/RoleTest.php b/tests/functional/RoleTest.php index 50c98fb308..8103c573c0 100644 --- a/tests/functional/RoleTest.php +++ b/tests/functional/RoleTest.php @@ -8,7 +8,7 @@ * @group slow * @group commands */ -class RoleCase extends CommandUnishTestCase +class RoleTest extends CommandUnishTestCase { use TestModuleHelperTrait; diff --git a/tests/functional/RsyncTest.php b/tests/functional/RsyncTest.php index a3ebe2e0ba..abdce97945 100644 --- a/tests/functional/RsyncTest.php +++ b/tests/functional/RsyncTest.php @@ -11,7 +11,6 @@ */ class RsyncTest extends CommandUnishTestCase { - public function setup(): void { if ($this->isWindows()) { diff --git a/tests/functional/SiteInstallTest.php b/tests/functional/SiteInstallTest.php index 31e4838af8..16894fd727 100644 --- a/tests/functional/SiteInstallTest.php +++ b/tests/functional/SiteInstallTest.php @@ -6,9 +6,8 @@ * @group base * @group slow */ -class SiteInstallCommandCase extends CommandUnishTestCase +class SiteInstallTest extends CommandUnishTestCase { - /** * Test functionality of site set. */ diff --git a/tests/functional/SiteSetTest.php b/tests/functional/SiteSetTest.php index 89e8e4ac6e..6a44f1ee3a 100644 --- a/tests/functional/SiteSetTest.php +++ b/tests/functional/SiteSetTest.php @@ -6,9 +6,8 @@ * @group base * @group slow */ -class SiteSetCommandCase extends CommandUnishTestCase +class SiteSetTest extends CommandUnishTestCase { - /** * Test functionality of site set. */ diff --git a/tests/functional/SiteSshTest.php b/tests/functional/SiteSshTest.php index 85762b4855..89ef906bc6 100644 --- a/tests/functional/SiteSshTest.php +++ b/tests/functional/SiteSshTest.php @@ -8,9 +8,8 @@ * * @group commands */ -class SiteSshCase extends CommandUnishTestCase +class SiteSshTest extends CommandUnishTestCase { - /** * Test drush ssh --simulate. No additional bash passed. */ diff --git a/tests/functional/SqlConnectCreateTest.php b/tests/functional/SqlConnectTest.php similarity index 98% rename from tests/functional/SqlConnectCreateTest.php rename to tests/functional/SqlConnectTest.php index 6857b9e9df..1f23fc8111 100644 --- a/tests/functional/SqlConnectCreateTest.php +++ b/tests/functional/SqlConnectTest.php @@ -10,9 +10,8 @@ * @group commands * @group sql */ -class SqlConnectCase extends CommandUnishTestCase +class SqlConnectTest extends CommandUnishTestCase { - public function testSqlConnect() { $this->setUpDrupal(1, true); diff --git a/tests/functional/SqlDumpTest.php b/tests/functional/SqlDumpTest.php index 0cfe24b743..475caadda6 100644 --- a/tests/functional/SqlDumpTest.php +++ b/tests/functional/SqlDumpTest.php @@ -13,7 +13,6 @@ */ class SqlDumpTest extends CommandUnishTestCase { - /** * Test that a dump file is created successfully. */ diff --git a/tests/functional/SqlSyncTest.php b/tests/functional/SqlSyncTest.php index 3077d7fedf..6a0ca77aee 100644 --- a/tests/functional/SqlSyncTest.php +++ b/tests/functional/SqlSyncTest.php @@ -1,13 +1,5 @@ isWindows()) { @@ -43,9 +34,7 @@ public function testSimulatedSqlSync() 'uri' => 'OMIT', 'simulate' => null, 'alias-path' => __DIR__ . '/resources/alias-fixtures', - // Ensure that shortcuts are normalized to long option names https://github.com/drush-ops/drush/pull/4515. - 'q' => null, - // This option is needed in order to "override" -q and get error output from Drush. + // @todo Ensure that shortcuts are normalized to long option names https://github.com/drush-ops/drush/pull/4515. 'verbose' => null, ]; @@ -54,21 +43,21 @@ public function testSimulatedSqlSync() // Test simulated simple rsync remote-to-local $this->drush('sql:sync', ['@synctest.remote', '@synctest.local'], $options, '@synctest.local'); $output = $this->getSimplifiedErrorOutput(); - $this->assertStringContainsString("[notice] Simulating: ssh -o PasswordAuthentication=whatever www-admin@server.isp.simulated '/path/to/drush sql-dump --quiet --no-interaction --strict=0 --gzip --result-file=auto --format=json --uri=remote --root=__DIR__/sut", $output); + $this->assertStringContainsString("[notice] Simulating: ssh -o PasswordAuthentication=whatever www-admin@server.isp.simulated '/path/to/drush sql-dump --no-interaction --strict=0 --gzip --result-file=auto --format=json --uri=remote --root=__DIR__/sut", $output); $this->assertStringContainsString("[notice] Simulating: __DIR__/drush core-rsync @synctest.remote:/simulated/path/to/dump.tgz @synctest.local:__SANDBOX__/tmp/dump.tgz --yes --uri=local --root=__DIR__/sut -- --remove-source-files", $output); - $this->assertStringContainsString("[notice] Simulating: __DIR__/drush sql-query --quiet --no-interaction --strict=0 --file=__SANDBOX__/tmp/dump.tgz --file-delete --uri=local --root=__DIR__/sut", $output); + $this->assertStringContainsString("[notice] Simulating: __DIR__/drush sql-query --no-interaction --strict=0 --file=__SANDBOX__/tmp/dump.tgz --file-delete --uri=local --root=__DIR__/sut", $output); // Test simulated simple sql:sync local-to-remote $this->drush('sql:sync', ['@synctest.local', '@synctest.remote'], $options, '@synctest.local'); $output = $this->getSimplifiedErrorOutput(); - $this->assertStringContainsString("[notice] Simulating: __DIR__/drush sql-dump --quiet --no-interaction --strict=0 --gzip --result-file=auto --format=json --uri=local --root=__DIR__/sut", $output); + $this->assertStringContainsString("[notice] Simulating: __DIR__/drush sql-dump --no-interaction --strict=0 --gzip --result-file=auto --format=json --uri=local --root=__DIR__/sut", $output); $this->assertStringContainsString("[notice] Simulating: __DIR__/drush core-rsync @synctest.local:/simulated/path/to/dump.tgz @synctest.remote:/tmp/dump.tgz --yes --uri=local --root=__DIR__/sut -- --remove-source-files", $output); - $this->assertStringContainsString("[notice] Simulating: ssh -o PasswordAuthentication=whatever www-admin@server.isp.simulated '/path/to/drush sql-query --quiet --no-interaction --strict=0 --file=/tmp/dump.tgz --file-delete --uri=remote --root=__DIR__/sut'", $output); + $this->assertStringContainsString("[notice] Simulating: ssh -o PasswordAuthentication=whatever www-admin@server.isp.simulated '/path/to/drush sql-query --no-interaction --strict=0 --file=/tmp/dump.tgz --file-delete --uri=remote --root=__DIR__/sut'", $output); // Test simulated remote invoke with a remote runner. $this->drush('sql:sync', ['@synctest.remote', '@synctest.local'], $options, 'user@server/path/to/drupal#sitename'); $output = $this->getSimplifiedErrorOutput(); - $this->assertStringContainsString("[notice] Simulating: ssh -o PasswordAuthentication=no user@server 'drush --no-interaction sql:sync @synctest.remote @synctest.local -q --uri=sitename --root=/path/to/drupal'", $output); + $this->assertStringContainsString("[notice] Simulating: ssh -o PasswordAuthentication=no user@server 'drush --no-interaction sql:sync @synctest.remote @synctest.local --uri=sitename --root=/path/to/drupal'", $output); } /** diff --git a/tests/functional/UpdateDBTest.php b/tests/functional/UpdateDBTest.php index 7e52ebacae..230702543f 100644 --- a/tests/functional/UpdateDBTest.php +++ b/tests/functional/UpdateDBTest.php @@ -63,7 +63,7 @@ public function testFailedUpdate($last_successful_update, $expected_status_repor $options = [ 'yes' => null, ]; - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm-enable', ['woot'], $options); // Force a pending update. @@ -147,7 +147,7 @@ public function testFailedPostUpdate() $options = [ 'yes' => null, ]; - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm-enable', ['woot'], $options); // Force re-run of woot_update_8104(). @@ -197,7 +197,7 @@ public function testUpdateModuleWithServiceDependency() $options = [ 'include' => __DIR__, ]; - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm-enable', ['woot'], $options); // Force re-run of the post-update woot_post_update_install_drush_empty_module(). @@ -243,7 +243,7 @@ public function testSuccessfulUpdate() $options = [ 'yes' => null, ]; - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm-enable', ['woot'], $options); // Force re-run of woot_update_8104() which is expected to be completed successfully. @@ -274,7 +274,7 @@ public function testBatchUpdateLogMessages() 'yes' => null, ]; $this->setUpDrupal(1, true); - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm:enable', ['woot'], $options); // Force re-run of woot_update_8105(). @@ -317,7 +317,7 @@ public function testEnableModuleViaUpdate() 'yes' => null, ]; $this->setUpDrupal(1, true); - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm:enable', ['woot'], $options); // Force re-run of woot_update_8106(). @@ -343,7 +343,7 @@ public function testEnableModuleViaPostUpdate() 'yes' => null, ]; $this->setUpDrupal(1, true); - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm:enable', ['woot'], $options); // Force re-run of woot_post_update_install_taxonomy(). diff --git a/tests/functional/UserTest.php b/tests/functional/UserTest.php index 988c3b7176..8007394211 100644 --- a/tests/functional/UserTest.php +++ b/tests/functional/UserTest.php @@ -8,10 +8,10 @@ * @group slow * @group commands */ -class UserCase extends CommandUnishTestCase +class UserTest extends CommandUnishTestCase { - const NAME = 'example'; + const MAIL = 'example@example.com'; public function setup(): void { @@ -23,9 +23,10 @@ public function setup(): void public function testBlockUnblock() { + $uid = 2; + $this->drush('user-block', [self::NAME]); $this->drush('user-information', [self::NAME], ['format' => 'json']); - $uid = 2; $output = $this->getOutputFromJSON($uid); $this->assertEquals(0, $output['user_status'], 'User is blocked.'); @@ -34,15 +35,38 @@ public function testBlockUnblock() $this->drush('user-information', [self::NAME], ['format' => 'json']); $output = $this->getOutputFromJSON($uid); $this->assertEquals(1, $output['user_status'], 'User is unblocked.'); + + // user-block user by uid. + $this->drush('user-block', [], ['uid' => $uid]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $this->assertEquals(0, $output['user_status'], 'User (id) is blocked.'); + + $this->drush('user-unblock', [], ['uid' => $uid]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $this->assertEquals(1, $output['user_status'], 'User (id) is unblocked.'); + + + // user-block user by mail. + $this->drush('user-block', [], ['mail' => self::MAIL]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $this->assertEquals(0, $output['user_status'], 'User (mail) is blocked.'); + + $this->drush('user-unblock', [], ['uid' => $uid]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $this->assertEquals(1, $output['user_status'], 'User (mail) is unblocked.'); } public function testUserRole() { + $uid = 2; // First, create the role since we use testing install profile. $this->drush('role-create', ['test role']); $this->drush('user-add-role', ['test role', self::NAME]); $this->drush('user-information', [self::NAME], ['format' => 'json']); - $uid = 2; $output = $this->getOutputFromJSON($uid); $expected = ['authenticated', 'test role']; $this->assertEquals($expected, array_values($output['roles']), 'User has test role.'); @@ -53,6 +77,34 @@ public function testUserRole() $output = $this->getOutputFromJSON($uid); $expected = ['authenticated']; $this->assertEquals($expected, array_values($output['roles']), 'User removed test role.'); + + // user-add-role by uid. + $this->drush('user-add-role', ['test role'], ['uid' => $uid]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $expected = ['authenticated', 'test role']; + $this->assertEquals($expected, array_values($output['roles']), 'User (id) has test role.'); + + // user-remove-role by uid + $this->drush('user-remove-role', ['test role'], ['uid' => $uid]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $expected = ['authenticated']; + $this->assertEquals($expected, array_values($output['roles']), 'User (id) removed test role.'); + + // user-add-role by mail. + $this->drush('user-add-role', ['test role'], ['mail' => self::MAIL]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $expected = ['authenticated', 'test role']; + $this->assertEquals($expected, array_values($output['roles']), 'User (mail) has test role.'); + + // user-remove-role by mail. + $this->drush('user-remove-role', ['test role'], ['mail' => self::MAIL]); + $this->drush('user-information', [self::NAME], ['format' => 'json']); + $output = $this->getOutputFromJSON($uid); + $expected = ['authenticated']; + $this->assertEquals($expected, array_values($output['roles']), 'User (mail) removed test role.'); } public function testUserPassword() @@ -106,7 +158,7 @@ public function testUserLogin() $this->assertStringContainsString('/user/reset/' . $uid, $url['path'], 'Login with uid option returned a valid reset URL'); // Test specific user by mail. $uid = 2; - $mail = 'example@example.com'; + $mail = self::MAIL; $this->drush('user-login', [], $user_login_options + ['mail' => $mail]); $output = $this->getOutput(); $url = parse_url($output); @@ -138,11 +190,11 @@ public function testUserCancel() public function userCreate() { - $this->drush('user-create', [self::NAME], ['password' => 'password', 'mail' => "example@example.com"]); + $this->drush('user-create', [self::NAME], ['password' => 'password', 'mail' => self::MAIL]); $this->drush('user-information', [self::NAME], ['format' => 'json']); $uid = 2; $output = $this->getOutputFromJSON($uid); - $this->assertEquals('example@example.com', $output['mail']); + $this->assertEquals(self::MAIL, $output['mail']); $this->assertEquals(self::NAME, $output['name']); $this->assertEquals(1, $output['user_status'], 'Newly created user is Active.'); $expected = ['authenticated']; diff --git a/tests/functional/WatchdogTailTest.php b/tests/functional/WatchdogTailTest.php index 3b973f3039..520058c037 100644 --- a/tests/functional/WatchdogTailTest.php +++ b/tests/functional/WatchdogTailTest.php @@ -11,7 +11,6 @@ */ class WatchdogTailTest extends CommandUnishTestCase { - /** * Test that watchdog tail works. */ diff --git a/tests/integration/AttibutesTest.php b/tests/integration/AttributesTest.php similarity index 100% rename from tests/integration/AttibutesTest.php rename to tests/integration/AttributesTest.php diff --git a/tests/integration/CacheCommandTest.php b/tests/integration/CacheCommandTest.php index bc2ad9ae1a..51bf9c772d 100644 --- a/tests/integration/CacheCommandTest.php +++ b/tests/integration/CacheCommandTest.php @@ -7,7 +7,7 @@ * * @group commands */ -class CacheCommandCase extends UnishIntegrationTestCase +class CacheCommandTest extends UnishIntegrationTestCase { public function testCacheGet() { @@ -39,7 +39,7 @@ public function testCacheSet() // Test cache-set using all arguments and many options. $expected = ['key' => 'value']; - $stdin = json_encode(['data'=> $expected]); + $stdin = json_encode(['data' => $expected]); $bin = 'default'; $this->drush('cache-set', ['my_cache_id', '-', $bin, 'CACHE_PERMANENT'], ['input-format' => 'json', 'cache-get' => true], self::EXIT_SUCCESS, $stdin); diff --git a/tests/integration/CommandsFromConfigurationTest.php b/tests/integration/CommandsFromConfigurationTest.php new file mode 100644 index 0000000000..021c44f8c4 --- /dev/null +++ b/tests/integration/CommandsFromConfigurationTest.php @@ -0,0 +1,27 @@ +drush('drush-extensions-hello', [], ['help' => null], self::EXIT_ERROR); + $this->assertStringContainsString('Command "drush-extensions-hello" is not defined.', $this->getErrorOutput()); + $this->drush('drush-extensions-hello', [], [ + 'help' => null, + 'config' => getenv('FIXTURES_DIR') . '/drush-extensions/drush.yml', + ]); + $this->assertStringContainsString('Command to load from this file using drush config.', $this->getOutput()); + $this->drush('drush-extensions-hello', [], [ + 'config' => getenv('FIXTURES_DIR') . '/drush-extensions/drush.yml', + ]); + $this->assertStringContainsString('Hello world!', $this->getOutput()); + } +} diff --git a/tests/integration/CoreTest.php b/tests/integration/CoreTest.php index 9ac99f00d3..184cb81113 100644 --- a/tests/integration/CoreTest.php +++ b/tests/integration/CoreTest.php @@ -71,7 +71,7 @@ public function testRoute() $json = $this->getOutputFromJSON(); $this->assertArrayHasKey('user.login', $json); $this->assertSame('/user/login', $json['user.login']); - $this->drush('route', [], ['path' =>'/user/login', 'format' => 'json']); + $this->drush('route', [], ['path' => '/user/login', 'format' => 'json']); $json = $this->getOutputFromJSON(); $this->assertSame('/user/login', $json['path']); $this->assertSame('user.login', $json['name']); @@ -79,7 +79,7 @@ public function testRoute() $this->assertSame("FALSE", $json['requirements']['_user_is_logged_in']); $this->assertSame('access_check.user.login_status', $json['options']['_access_checks'][0]); - $this->drush('route', [], ['name' =>'user.login', 'format' => 'json']); + $this->drush('route', [], ['name' => 'user.login', 'format' => 'json']); $json = $this->getOutputFromJSON(); $this->assertSame('/user/login', $json['path']); } diff --git a/tests/integration/CustomLoggerTest.php b/tests/integration/CustomLoggerTest.php new file mode 100644 index 0000000000..98ca6fd0a6 --- /dev/null +++ b/tests/integration/CustomLoggerTest.php @@ -0,0 +1,17 @@ +drush('version', [], ['debug' => true]); + $this->assertStringContainsString('sec', $this->getErrorOutputRaw()); + + // sut:simple has been hooked so that a custom logger is use. It doesn't show timing information during --debug. + $this->drush('sut:simple', [], ['debug' => true, 'simulate' => true]); + $this->assertStringNotContainsString('sec', $this->getOutput()); + } +} diff --git a/tests/integration/DeployTest.php b/tests/integration/DeployTest.php index 6e4674a64f..467c6aaa47 100644 --- a/tests/integration/DeployTest.php +++ b/tests/integration/DeployTest.php @@ -7,7 +7,6 @@ */ class DeployTest extends UnishIntegrationTestCase { - /** * A simple test since all the sub-commands are tested elsewhere. */ @@ -15,7 +14,7 @@ public function testDeploy() { // Prep a config directory that will be imported later. $this->drush('config:export'); - + $this->drush('deploy'); $expecteds = ["Database updates start.", 'Config import start.', 'Deploy hook start.', 'Cache rebuild start.']; foreach ($expecteds as $expected) { diff --git a/tests/integration/IntegrationTestsTest.php b/tests/integration/IntegrationTestsTest.php new file mode 100644 index 0000000000..8779bed578 --- /dev/null +++ b/tests/integration/IntegrationTestsTest.php @@ -0,0 +1,26 @@ +isWindows()) { + $this->markTestSkipped('@todo Fails on Windows'); + } + + // Ensure that a verbose run does not affect subsequent runs. + $this->drush('version', [], ['debug' => null]); + $this->assertStringContainsString('[info] Starting bootstrap to none', $this->getErrorOutputRaw()); + $this->drush('version'); + $this->assertStringNotContainsString('[info] Starting bootstrap to none', $this->getErrorOutputRaw()); + } +} diff --git a/tests/functional/MigrateRunnerTest.php b/tests/integration/MigrateRunnerTest.php similarity index 90% rename from tests/functional/MigrateRunnerTest.php rename to tests/integration/MigrateRunnerTest.php index 5c5f0e8132..bf16206804 100644 --- a/tests/functional/MigrateRunnerTest.php +++ b/tests/integration/MigrateRunnerTest.php @@ -2,14 +2,14 @@ namespace Unish; +use Drupal\migrate\Plugin\MigrationInterface; use Webmozart\PathUtil\Path; /** - * @group slow * @group commands * @coversDefaultClass \Drush\Drupal\Commands\core\MigrateRunnerCommands */ -class MigrateRunnerTest extends CommandUnishTestCase +class MigrateRunnerTest extends UnishIntegrationTestCase { use TestModuleHelperTrait; @@ -19,8 +19,7 @@ class MigrateRunnerTest extends CommandUnishTestCase protected function setUp(): void { parent::setUp(); - $this->setUpDrupal(1, true); - $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm:enable', ['migrate', 'node', 'woot']); } @@ -96,7 +95,7 @@ public function testMigrateStatus(): void $this->drush('migrate:status', [], [ 'field' => 'status', 'names-only' => null, - ], null, null, self::EXIT_ERROR); + ], self::EXIT_ERROR); $this->assertStringContainsString('Cannot use --names-only with --field=status.', $this->getErrorOutput()); $actualIds = array_column($output, 'id'); @@ -106,7 +105,7 @@ public function testMigrateStatus(): void $this->assertContains('test_migration_untagged', $actualIds); // Check that invalid migration IDs are reported. - $this->drush('migrate:status', ['non_existing,test_migration,another_invalid'], [], null, null, self::EXIT_ERROR); + $this->drush('migrate:status', ['non_existing,test_migration,another_invalid'], [], self::EXIT_ERROR); $this->assertStringContainsString('Invalid migration IDs: non_existing, another_invalid', $this->getErrorOutput()); // Check --fields option. @@ -146,7 +145,7 @@ public function testMigrateImportAndRollback(): void // Trigger logging in ProcessRowTestSubscriber::onPrepareRow(). // @see \Drupal\woot\EventSubscriber\ProcessRowTestSubscriber::onPrepareRow() // @see \Drupal\woot\EventSubscriber\PreRowDeleteTestSubscriber::onPreRowDelete() - $this->drush('state:set', ['woot.test_migrate_trigger_failures', true]); + $this->drush('state:set', ['woot.migrate_runner.trigger_failures', true]); // Warm-up the 'migrate_prepare_row' hook implementations cache to test // that system_migrate_prepare_row() is picked-up during import. See @@ -157,7 +156,7 @@ public function testMigrateImportAndRollback(): void // Expect that this command will fail because the 2nd row fails. // @see \Drupal\woot\Plugin\migrate\process\TestFailProcess - $this->drush('migrate:import', ['test_migration'], [], null, null, self::EXIT_ERROR); + $this->drush('migrate:import', ['test_migration'], [], self::EXIT_ERROR); // Check for the expected command output. $output = $this->getErrorOutput(); @@ -175,12 +174,12 @@ public function testMigrateImportAndRollback(): void // Check that an appropriate error is logged when rollback fails. // @see \Drupal\woot\EventSubscriber\PreRowDeleteTestSubscriber::onPreRowDelete() - $this->drush('migrate:rollback', [], ['all' => null], null, null, self::EXIT_ERROR); + $this->drush('migrate:rollback', [], ['all' => null], self::EXIT_ERROR); $this->assertStringContainsString('Earthquake while rolling back', $this->getErrorOutputRaw()); $this->drush('migrate:reset', ['test_migration']); // Reset the flag, so we won't fail the rollback again. - $this->drush('state:delete', ['woot.test_migrate_trigger_failures']); + $this->drush('state:delete', ['woot.migrate_runner.trigger_failures']); $this->drush('migrate:rollback', ['test_migration']); // Note that item with source ID 2, which failed to import, was already @@ -214,14 +213,15 @@ public function testMigrateImportAndRollback(): void public function testMigrateImportAndRollbackWithIdList(): void { // Enlarge the source recordset to 50 rows. - $this->drush('state:set', ['woot.test_migration_source_data_amount', 50]); + $this->drush('state:set', ['woot.migrate_runner.source_data_amount', 50]); $this->drush('migrate:import', ['test_migration'], [ // Intentionally added 56, which is out of bounds. - 'idlist' => '4,12,29,34,56', + 'idlist' => '4,12,29,34,56', ]); + $this->drush('migrate:status', ['test_migration'], [ - 'format' => 'json', + 'format' => 'json', ]); // Check that only rows with ID 4, 12, 29, 34 were imported. @@ -233,11 +233,9 @@ public function testMigrateImportAndRollbackWithIdList(): void $this->drush('migrate:rollback', ['test_migration'], [ // Intentionally added 56, which is out of bounds. - 'idlist' => '4,34,56', - ]); - $this->drush('migrate:status', ['test_migration'], [ - 'format' => 'json', + 'idlist' => '4,34,56', ]); + $this->drush('migrate:status', ['test_migration'], ['format' => 'json']); // Check that only rows with ID 4 and 34 were rolled back. $this->assertSame(50, $this->getOutputFromJSON(0)['total']); @@ -253,15 +251,15 @@ public function testMigrateImportAndRollbackWithIdList(): void */ public function testMissingSourceRows(): void { - $this->drush('state:set', ['woot.test_migration_source_data_amount', 5]); + $this->drush('state:set', ['woot.migrate_runner.source_data_amount', 5]); $this->drush('migrate:import', ['test_migration']); $this->assertStringContainsString('[notice] Processed 5 items (5 created, 0 updated, 0 failed, 0 ignored)', $this->getErrorOutput()); $this->drush('sql:query', ['SELECT title FROM node_field_data']); $this->assertSame(['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'], $this->getOutputAsList()); - $this->drush('state:set', ['woot.test_migration_source_removed_rows', '2,4']); - // Rebuild cache to get the new source plugin definition. - $this->drush('cache:rebuild'); + $this->drush('state:set', ['woot.migrate_runner.removed_source_rows', '2,4']); + // Get fresh plugin definitions. + drupal_flush_all_caches(); $this->drush('migrate:import', ['test_migration'], ['delete' => null]); @@ -293,12 +291,12 @@ public function testMigrateStopAndResetStatus(): void */ public function testMigrateMessagesAndFieldSource(): void { - $this->drush('state:set', ['woot.test_migration_source_data_amount', 20]); - $this->drush('state:set', ['woot.test_migrate_trigger_failures', true]); + $this->drush('state:set', ['woot.migrate_runner.source_data_amount', 20]); + $this->drush('state:set', ['woot.migrate_runner.trigger_failures', true]); $this->drush('migrate:import', ['test_migration'], [ 'no-progress' => null, - ], null, null, self::EXIT_ERROR); + ], self::EXIT_ERROR); // All messages together. $this->drush('migrate:messages', ['test_migration'], ['format' => 'json']); @@ -363,8 +361,8 @@ public function testMigrateMessagesAndFieldSource(): void public function testImportingWithLimitAndFeedback(): void { // Set the test_migration source to 300 records. - // @see woot_migrate_source_info_alter() - $this->drush('state:set', ['woot.test_migration_source_data_amount', 300]); + // @see woot_migration_plugins_alter() + $this->drush('state:set', ['woot.migrate_runner.source_data_amount', 300]); $this->drush('migrate:import', ['test_migration'], [ 'feedback' => 20, 'limit' => 199, @@ -432,7 +430,7 @@ public function testImportingWithUpdateAndIdlist(): void */ public function testCommandProgressBar(): void { - $this->drush('state:set', ['woot.test_migration_source_data_amount', 50]); + $this->drush('state:set', ['woot.migrate_runner.source_data_amount', 50]); // Check an import and rollback with progress bar. $this->drush('migrate:import', ['test_migration']); @@ -453,9 +451,10 @@ public function testCommandProgressBar(): void $this->assertNoProgressBar(); // Set the 'test_migration' source plugin to skip count. - // @see woot_migrate_source_info_alter() - $this->drush('state:set', ['woot.test_command_progress_bar.skip_count', true]); - $this->drush('cache:rebuild'); + // @see woot_migration_plugins_alter() + $this->drush('state:set', ['woot.migrate_runner.progress_bar_skip_count', true]); + // Get fresh plugin definitions. + drupal_flush_all_caches(); // Check that progress bar won't show when the source skips count. $this->drush('migrate:import', ['test_migration']); @@ -519,4 +518,31 @@ protected function progressBarAssertionHelper(bool $assertHasProgressBar): void } } } + + /** + * {@inheritdoc} + */ + protected function tearDown(): void + { + // Cleanup any created content. + $this->drush('entity:delete', ['node']); + + // Uninstalling Migrate module doesn't automatically drop the tables. + // @see https://www.drupal.org/project/drupal/issues/2713327 + $migrations = \Drupal::service('plugin.manager.migration')->createInstances([]); + array_walk($migrations, function (MigrationInterface $migration): void { + $migration->getIdMap()->destroy(); + }); + + // Uninstall test modules. + $this->drush('pm:uninstall', ['migrate', 'node', 'woot']); + + // Cleanup any testing state vars. + $state = \Drupal::state(); + foreach (['trigger_failures', 'source_data_amount', 'removed_source_rows', 'progress_bar_skip_count'] as $var) { + $state->delete("woot.migrate_runner.$var"); + } + + parent::tearDown(); + } } diff --git a/tests/integration/ModuleGeneratorTest.php b/tests/integration/ModuleGeneratorTest.php index e67c67c417..5efc8d9911 100644 --- a/tests/integration/ModuleGeneratorTest.php +++ b/tests/integration/ModuleGeneratorTest.php @@ -16,11 +16,17 @@ class ModuleGeneratorTest extends UnishIntegrationTestCase */ public function testModuleGenerators(): void { - $this->setupModulesForTests(['woot'], Path::join(__DIR__, '../functional/resources/modules/d8')); + $this->setupModulesForTests(['woot'], Path::join(__DIR__, '/../fixtures/modules')); $this->drush('pm:enable', ['woot']); $this->drush('cc', ['drush']); $this->drush('generate', ['list']); $this->assertStringContainsString('woot:example', $this->getOutput()); $this->assertStringContainsString('Generates a woot.', $this->getOutput()); } + + public function tearDown(): void + { + $this->drush('pm:uninstall', ['woot']); + parent::tearDown(); + } } diff --git a/tests/integration/SecurityUpdatesTest.php b/tests/integration/SecurityUpdatesTest.php index 3cc1df6435..a38f8f6090 100644 --- a/tests/integration/SecurityUpdatesTest.php +++ b/tests/integration/SecurityUpdatesTest.php @@ -2,6 +2,8 @@ namespace Unish; +use Composer\Semver\Semver; + /** * Tests "pm:security" command. * @group commands @@ -9,13 +11,12 @@ */ class SecurityUpdatesTest extends UnishIntegrationTestCase { - /** * Test that insecure Drupal packages are correctly identified. */ public function testInsecureDrupalPackage() { - list($expected_package, $expected_version) = ['drupal/semver_example', '2.2.0']; + list($expected_package, $expected_version) = ['drupal/semver_example', '2.3.0']; $this->drush('pm:security', [], ['format' => 'json'], self::EXIT_ERROR_WITH_CLARITY); $this->assertStringContainsString('One or more of your dependencies has an outstanding security update.', $this->getErrorOutput()); $this->assertStringContainsString("$expected_package", $this->getErrorOutput()); @@ -24,14 +25,14 @@ public function testInsecureDrupalPackage() $this->assertEquals($expected_package, $security_advisories[$expected_package]['name']); $this->assertEquals($expected_version, $security_advisories[$expected_package]['version']); - // If our SUT is 9.0.0, then we should find a security update for Drupal core too. - if (\Drupal::VERSION != '9.0.0') { - $this->markTestSkipped("We only test for drupal/core security updates if the SUT is on Drupal 9.0.0"); + // If our SUT is 9.2.8, then we should find a security update for Drupal core too. + if (\Drupal::VERSION != '9.2.8') { + $this->markTestSkipped("We only test for drupal/core security updates if the SUT is on Drupal 9.2.8"); } $this->assertStringContainsString("Try running: composer require drupal/core", $this->getErrorOutput()); $this->arrayHasKey('drupal/core', $security_advisories); $this->assertEquals('drupal/core', $security_advisories['drupal/core']['name']); - $this->assertEquals('9.0.0', $security_advisories['drupal/core']['version']); + $this->assertEquals('9.2.8', $security_advisories['drupal/core']['version']); } /** diff --git a/tests/integration/SiteAliasConvertTest.php b/tests/integration/SiteAliasConvertTest.php index 7f989da093..3cb579a269 100644 --- a/tests/integration/SiteAliasConvertTest.php +++ b/tests/integration/SiteAliasConvertTest.php @@ -10,7 +10,6 @@ */ class SiteAliasConvertTest extends UnishIntegrationTestCase { - /** * Test functionality of site:alias-convert. */ diff --git a/tests/integration/SqlCliTest.php b/tests/integration/SqlCliTest.php index a3da378d08..8ac518eda9 100644 --- a/tests/integration/SqlCliTest.php +++ b/tests/integration/SqlCliTest.php @@ -16,7 +16,6 @@ */ class SqlCliTest extends UnishIntegrationTestCase { - public function testSqlCli() { if ($this->isWindows()) { diff --git a/tests/integration/WatchdogTest.php b/tests/integration/WatchdogTest.php index f9a90b3f53..d6751d07ed 100644 --- a/tests/integration/WatchdogTest.php +++ b/tests/integration/WatchdogTest.php @@ -7,7 +7,6 @@ */ class WatchdogTest extends UnishIntegrationTestCase { - public function testWatchdog() { $this->drush('pm-enable', ['dblog']); diff --git a/tests/integration/resources/Commands/ExampleAttributesCommands.php b/tests/integration/resources/Commands/ExampleAttributesCommands.php index 08823e7037..6bd7528fc6 100644 --- a/tests/integration/resources/Commands/ExampleAttributesCommands.php +++ b/tests/integration/resources/Commands/ExampleAttributesCommands.php @@ -1,4 +1,5 @@ false]) { if ($options['flip']) { diff --git a/tests/unish/CommandUnishTestCase.php b/tests/unish/CommandUnishTestCase.php index 7728367506..4333f464bb 100644 --- a/tests/unish/CommandUnishTestCase.php +++ b/tests/unish/CommandUnishTestCase.php @@ -71,7 +71,7 @@ public function getErrorOutputRaw() public function drushBackground($command, array $args = [], array $options = [], $site_specification = null, $cd = null, $suffix = null, $env = []) { list($cmd, ) = $this->prepareDrushCommand($command, $args, $options, $site_specification, $suffix); - return $this->startExecute($cmd, $cd, $env); + return $this->startExecute(explode(' ', $cmd), $cd, $env); } /** @@ -191,7 +191,8 @@ protected function prepareDrushCommand($command, array $args = [], array $option if ($hide_stderr) { $cmd[] = '2>' . $this->bitBucket(); } - $exec = array_filter($cmd, 'strlen'); // Remove NULLs + // Remove NULLs + $exec = @array_filter($cmd, 'strlen'); $cmd = implode(' ', $exec); return [$cmd, $coverage_file]; } diff --git a/tests/unish/CreateEntityType.php b/tests/unish/CreateEntityType.php index 701e5e229e..1be7265162 100644 --- a/tests/unish/CreateEntityType.php +++ b/tests/unish/CreateEntityType.php @@ -15,10 +15,11 @@ public static function createContentEntity($testCase): void 'name' => 'Unish Article', 'machine_name' => 'unish_article', 'description' => 'A test module', - 'package' => 'unish', + 'package' => 'Unish', 'dependencies' => 'drupal:text', ]; $testCase->drush('generate', ['module'], ['verbose' => null, 'answer' => $answers, 'destination' => Path::join($testCase->webroot(), 'modules/contrib')], null, null, $testCase::EXIT_SUCCESS, null, ['SHELL_INTERACTIVE' => 1]); + // Create a content entity type and enable its module. // Note that only the values below are used. The keys are for documentation. $answers = [ diff --git a/tests/unish/TestModuleHelperTrait.php b/tests/unish/TestModuleHelperTrait.php index edf7e7c4fe..90d8ea343f 100644 --- a/tests/unish/TestModuleHelperTrait.php +++ b/tests/unish/TestModuleHelperTrait.php @@ -33,7 +33,7 @@ public function setupModulesForTests(array $modules, $sourcePath) $info_path = $targetDir . "/$module.info.yml"; $module_info = file_get_contents($info_path); if (strpos($module_info, 'core_version_requirement') === false) { - $module_info = "core_version_requirement: ^8 || ^9\n$module_info"; + $module_info = "core_version_requirement: ^8 || ^9 || ^10\n$module_info"; file_put_contents($info_path, $module_info); } } diff --git a/tests/unish/UnishIntegrationTestCase.php b/tests/unish/UnishIntegrationTestCase.php index 3b203bf424..7d7970f6ca 100644 --- a/tests/unish/UnishIntegrationTestCase.php +++ b/tests/unish/UnishIntegrationTestCase.php @@ -1,4 +1,5 @@ run($input, $output); $this->stdout = $output->fetch(); $this->stderr = $output->getErrorOutput()->fetch(); - $this->assertEquals($expected_return, $return, "Command failed: \n\n" . $this->getErrorOutput()); + // Undo the env variable and verbosity property that Process unhelpfully persists. + // https://github.com/symfony/console/blob/3.4/Application.php#L970-L972 + putenv('SHELL_VERBOSITY'); + unset($_ENV['SHELL_VERBOSITY'], $_SERVER['SHELL_VERBOSITY']); + $output->setVerbosity(OutputInterface::VERBOSITY_NORMAL); + + $this->assertEquals($expected_return, $return, "Command failed: \n\n" . $this->getErrorOutput()); return $return; } diff --git a/tests/unish/UnishTestCase.php b/tests/unish/UnishTestCase.php index c8ca0568a7..48d7e2d6a0 100644 --- a/tests/unish/UnishTestCase.php +++ b/tests/unish/UnishTestCase.php @@ -71,6 +71,7 @@ public function __construct($name = null, array $data = [], $dataName = '') self::setEnv(['ETC_PREFIX' => $unish_sandbox]); self::setEnv(['SHARE_PREFIX' => $unish_sandbox]); self::setEnv(['TEMP' => Path::join($unish_sandbox, 'tmp')]); + self::setEnv(['FIXTURES_DIR' => Path::join(dirname(__DIR__), 'fixtures')]); } /** @@ -741,7 +742,7 @@ public static function setEnv(array $vars) foreach ($vars as $k => $v) { putenv($k . '=' . $v); // Value must be a string. See \Symfony\Component\Process\Process::getDefaultEnv. - $_SERVER[$k]= (string) $v; + $_SERVER[$k] = (string) $v; } } diff --git a/tests/unish/Utils/FSUtils.php b/tests/unish/Utils/FSUtils.php index 4cef991cdd..eac6df48d3 100644 --- a/tests/unish/Utils/FSUtils.php +++ b/tests/unish/Utils/FSUtils.php @@ -1,4 +1,5 @@ assertEquals($expectedRows, $actualRows); } - /** - * @return \Drupal\Core\Database\Driver\sqlite\Connection - */ - protected function getDatabaseConnection(): Connection + protected function getDatabaseConnection() { if (!extension_loaded('pdo_sqlite')) { $this->markTestSkipped('The pdo_sqlite extension is not available.'); } $options['database'] = ':memory:'; - $pdo = Connection::open($options); + $pdo = new \PDO("sqlite::memory:"); + if (Comparator::greaterThanOrEqualTo(\Drupal::VERSION, '9.4')) { + /** @var \Composer\Autoload\ClassLoader $loader */ + $loader = require PHPUNIT_COMPOSER_INSTALL; + $loader->addPsr4('Drupal\sqlite\\', Path::join([dirname(__DIR__, 2), 'sut/core/modules/sqlite/src'])); + } $connection = new Connection($pdo, $options); // Create the table and load it with data. $mapTableSchema = $this->getMapTableSchema(); - $connection->schema()->createTable('migrate_map_test_migration', $mapTableSchema); + $table = 'migrate_map_test_migration'; + if ($connection->schema()->tableExists($table)) { + $connection->schema()->dropTable($table); + } + $connection->schema()->createTable($table, $mapTableSchema); $fields = array_keys($mapTableSchema['fields']); - $insert = $connection->insert('migrate_map_test_migration')->fields($fields); + $insert = $connection->insert($table)->fields($fields); $mapTableData = $this->getMapTableData(); $mapTableData = array_map(function (array $row): array { diff --git a/tests/unit/SiteAliasFileDiscoveryTest.php b/tests/unit/SiteAliasFileDiscoveryTest.php index 8fdfe5836a..f64a3467bc 100644 --- a/tests/unit/SiteAliasFileDiscoveryTest.php +++ b/tests/unit/SiteAliasFileDiscoveryTest.php @@ -1,4 +1,5 @@ database = $database; diff --git a/tests/unit/XhProfTest.php b/tests/unit/XhProfTest.php index 1eee71ce4a..cd4edea2dd 100644 --- a/tests/unit/XhProfTest.php +++ b/tests/unit/XhProfTest.php @@ -13,7 +13,6 @@ */ class XhProfTest extends TestCase { - /** * Test various combinations of XHProf flag options. *