Skip to content
Snippets Groups Projects
Unverified Commit 29756947 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3346707 by tedbow, phenaproxima, alexpott, catch, wim leers, dww,...

Issue #3346707 by tedbow, phenaproxima, alexpott, catch, wim leers, dww, effulgentsia, gábor hojtsy, drumm, grasmash, chrisfromredfin, fizcs3, cola, capysara, diegors, daisyleroy, abhishek_gupta1, bnjmnm, quietone, lauriii, poker10, xjm, anish.a, ajits, traviscarden, heddn, Idoni, srishtiiee, siramsay, shabbir, rocketeerbkw, Schnitzel, s_leu, Theresa.Grannum, yash.rode, wiifm, wendyZ, tim.plunkett, Webbeh, rkoller, Ranjit1032002, kunal.sachdev, kjankowski, jayesh.d, immaculatexavier, Ishani Patel, leksat, lhridley, percoction, rahul_, p.ayekumi, omkar.podey, narendra.rajwar27, narendrar: Add Alpha level Experimental Package Manager module
parent c836e0a0
No related branches found
No related tags found
13 merge requests!11131[10.4.x-only-DO-NOT-MERGE]: Issue ##2842525 Ajax attached to Views exposed filter form does not trigger callbacks,!3878Removed unused condition head title for views,!3818Issue #2140179: $entity->original gets stale between updates,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3133core/modules/system/css/components/hidden.module.css,!2964Issue #2865710 : Dependencies from only one instance of a widget are used in display modes,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2062Issue #3246454: Add weekly granularity to views date sort,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493
Pipeline #320505 passed with warnings
Pipeline: drupal

#320531

    Pipeline: drupal

    #320519

      Pipeline: drupal

      #320510

        Showing
        with 1858 additions and 2 deletions
        ......@@ -496,7 +496,7 @@
        "dist": {
        "type": "path",
        "url": "core",
        "reference": "278aa6e72e52943d0bfa149af42fd1dcc0c9a31f"
        "reference": "c5a5d4ac5312bd6c16d1e8505b20ba640837b7a0"
        },
        "require": {
        "asm89/stack-cors": "^2.1",
        ......@@ -525,6 +525,7 @@
        "mck89/peast": "^1.14",
        "pear/archive_tar": "^1.4.14",
        "php": ">=8.3.0",
        "php-tuf/composer-stager": "^2-rc5",
        "psr/log": "^3.0",
        "revolt/event-loop": "^1.0",
        "sebastian/diff": "^4|^5",
        ......@@ -1457,6 +1458,83 @@
        },
        "time": "2021-03-21T15:43:46+00:00"
        },
        {
        "name": "php-tuf/composer-stager",
        "version": "v2.0.0-rc5",
        "source": {
        "type": "git",
        "url": "https://github.com/php-tuf/composer-stager.git",
        "reference": "9c5aef2ab98db381c30fe4837e9007e1ac2b89e6"
        },
        "dist": {
        "type": "zip",
        "url": "https://api.github.com/repos/php-tuf/composer-stager/zipball/9c5aef2ab98db381c30fe4837e9007e1ac2b89e6",
        "reference": "9c5aef2ab98db381c30fe4837e9007e1ac2b89e6",
        "shasum": ""
        },
        "require": {
        "ext-json": "*",
        "php": ">=8.1.0",
        "symfony/filesystem": "^6.2 || ^7.0",
        "symfony/process": "^6.2 || ^7.0",
        "symfony/translation-contracts": "^3.1"
        },
        "require-dev": {
        "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
        "infection/infection": "^0.27.0 || ^0.28.0 || ^0.29.0",
        "phpbench/phpbench": "^1.2",
        "phpro/grumphp-shim": "^2.0",
        "phpspec/prophecy": "^1.17",
        "phpspec/prophecy-phpunit": "^2.0",
        "phpstan/extension-installer": "^1.3",
        "phpstan/phpstan": "^1.10",
        "phpstan/phpstan-strict-rules": "^1.5",
        "phpunit/phpunit": "^9.6",
        "rector/rector": "^0.17.5 || ^0.18.0 || ^0.19.0 || ^1.0.0",
        "slevomat/coding-standard": "^8.13",
        "squizlabs/php_codesniffer": "^3.7",
        "symfony/config": "^6.3",
        "symfony/dependency-injection": "^6.3",
        "symfony/yaml": "^6.3",
        "thecodingmachine/phpstan-strict-rules": "^1.0"
        },
        "suggest": {
        "symfony/dependency-injection": "For dependency injection",
        "symfony/translation": "For internationalization tools"
        },
        "type": "library",
        "extra": {
        "branch-alias": {
        "dev-develop": "2.x-dev"
        },
        "grumphp": {
        "disable-plugin": true
        }
        },
        "autoload": {
        "psr-4": {
        "PhpTuf\\ComposerStager\\": "src/"
        }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
        "MIT"
        ],
        "authors": [
        {
        "name": "Travis Carden",
        "email": "travis.carden@gmail.com",
        "role": "Developer"
        }
        ],
        "description": "Stages Composer commands so they can be safely run on a production codebase.",
        "homepage": "https://github.com/php-tuf/composer-stager",
        "support": {
        "issues": "https://github.com/php-tuf/composer-stager/issues",
        "source": "https://github.com/php-tuf/composer-stager"
        },
        "time": "2024-07-17T18:52:20+00:00"
        },
        {
        "name": "psr/cache",
        "version": "3.0.0",
        ......
        ......@@ -23,6 +23,7 @@
        "pear/console_getopt": "~v1.4.3",
        "pear/pear-core-minimal": "~v1.10.15",
        "pear/pear_exception": "~v1.0.2",
        "php-tuf/composer-stager": "~v2.0.0-rc5",
        "psr/cache": "~3.0.0",
        "psr/container": "~2.0.2",
        "psr/event-dispatcher": "~1.0.0",
        ......
        ......@@ -36324,6 +36324,342 @@
        'count' => 1,
        'path' => __DIR__ . '/modules/options/tests/src/Kernel/Views/OptionsTestBase.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\ComposerInspector\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/ComposerInspector.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\ComposerInspector\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/ComposerInspector.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\EventSubscriber\\\\ChangeLogger\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/EventSubscriber/ChangeLogger.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\EventSubscriber\\\\ChangeLogger\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/EventSubscriber/ChangeLogger.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\PackageManagerUninstallValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/PackageManagerUninstallValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\PackageManagerUninstallValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/PackageManagerUninstallValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\PathExcluder\\\\UnknownPathExcluder\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/PathExcluder/UnknownPathExcluder.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\PathExcluder\\\\UnknownPathExcluder\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/PathExcluder/UnknownPathExcluder.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\StageBase\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/StageBase.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\StageBase\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/StageBase.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\AllowedScaffoldPackagesValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/AllowedScaffoldPackagesValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\AllowedScaffoldPackagesValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/AllowedScaffoldPackagesValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\ComposerMinimumStabilityValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/ComposerMinimumStabilityValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\ComposerMinimumStabilityValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/ComposerMinimumStabilityValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\ComposerPatchesValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/ComposerPatchesValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\ComposerPatchesValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/ComposerPatchesValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\ComposerPluginsValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/ComposerPluginsValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\ComposerPluginsValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/ComposerPluginsValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\ComposerValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/ComposerValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\ComposerValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/ComposerValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\DiskSpaceValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/DiskSpaceValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\DiskSpaceValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/DiskSpaceValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\DuplicateInfoFileValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/DuplicateInfoFileValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\DuplicateInfoFileValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/DuplicateInfoFileValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\EnabledExtensionsValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/EnabledExtensionsValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\EnabledExtensionsValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/EnabledExtensionsValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\EnvironmentSupportValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/EnvironmentSupportValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\EnvironmentSupportValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/EnvironmentSupportValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\LockFileValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/LockFileValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\LockFileValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/LockFileValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\MultisiteValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/MultisiteValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\MultisiteValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/MultisiteValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\OverwriteExistingPackagesValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/OverwriteExistingPackagesValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\OverwriteExistingPackagesValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/OverwriteExistingPackagesValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\PendingUpdatesValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/PendingUpdatesValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\PendingUpdatesValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/PendingUpdatesValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\PhpExtensionsValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/PhpExtensionsValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\PhpExtensionsValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/PhpExtensionsValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\PhpTufValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/PhpTufValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\PhpTufValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/PhpTufValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\RsyncValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/RsyncValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\RsyncValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/RsyncValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\SettingsValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/SettingsValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\SettingsValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/SettingsValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\StageNotInActiveValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/StageNotInActiveValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\StageNotInActiveValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/StageNotInActiveValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\StagedDBUpdateValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/StagedDBUpdateValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\StagedDBUpdateValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/StagedDBUpdateValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\SupportedReleaseValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/SupportedReleaseValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\SupportedReleaseValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/SupportedReleaseValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\WritableFileSystemValidator\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/WritableFileSystemValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\package_manager\\\\Validator\\\\WritableFileSystemValidator\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/src/Validator/WritableFileSystemValidator.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\Tests\\\\package_manager\\\\Functional\\\\FailureMarkerRequirementTest\\:\\:formatPlural\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/tests/src/Functional/FailureMarkerRequirementTest.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\Tests\\\\package_manager\\\\Functional\\\\FailureMarkerRequirementTest\\:\\:getNumberOfPlurals\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/tests/src/Functional/FailureMarkerRequirementTest.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\Tests\\\\package_manager\\\\Kernel\\\\StageOwnershipTest\\:\\:grantPermissions\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/tests/src/Kernel/StageOwnershipTest.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Method Drupal\\\\Tests\\\\package_manager\\\\Kernel\\\\StageOwnershipTest\\:\\:setCurrentUser\\(\\) has no return type specified\\.$#',
        'count' => 1,
        'path' => __DIR__ . '/modules/package_manager/tests/src/Kernel/StageOwnershipTest.php',
        ];
        $ignoreErrors[] = [
        // identifier: missingType.return
        'message' => '#^Function page_cache_help\\(\\) has no return type specified\\.$#',
        ......@@ -49,7 +49,8 @@
        "pear/archive_tar": "^1.4.14",
        "psr/log": "^3.0",
        "mck89/peast": "^1.14",
        "sebastian/diff": "^4|^5"
        "sebastian/diff": "^4|^5",
        "php-tuf/composer-stager": "^2-rc5"
        },
        "conflict": {
        "drush/drush": "<12.4.3"
        ......
        ......@@ -256,6 +256,7 @@ guzzlehttp
        hande
        hateoas
        hexcode
        hhvm
        hilited
        hmac
        hookname
        ......@@ -568,6 +569,7 @@ subvalues
        subview
        supercede
        svgz
        syncer
        synchronizable
        syrop
        tabbingmanager
        ......
        executables:
        composer: ~
        rsync: ~
        additional_trusted_composer_plugins: []
        include_unknown_files_in_project_root: false
        package_name:
        type: string
        label: 'Package name'
        constraints:
        Regex:
        # @see https://getcomposer.org/schema.json
        pattern: '/^[a-z0-9]([_.-]?[a-z0-9]+)*\/[a-z0-9](([_.]|-{1,2})?[a-z0-9]+)*$/'
        package_manager.settings:
        type: config_object
        label: 'Package Manager settings'
        mapping:
        executables:
        type: sequence
        label: 'Absolute paths to required executables, or NULL to rely on PATH'
        sequence:
        type: string
        label: 'Absolute path to executable, or NULL'
        additional_trusted_composer_plugins:
        type: sequence
        label: 'Additional trusted composer plugins'
        sequence:
        type: package_name
        label: 'Trusted composer plugin'
        include_unknown_files_in_project_root:
        type: boolean
        label: 'Include unrecognized files and directories in the project root in staging operations'
        log:
        type: string
        label: 'Path of a file to which Composer Stager process output should be logged'
        constraints:
        NotBlank: []
        requiredKey: false
        <?php
        /**
        * @file
        * Documentation related to Package Manager.
        */
        /**
        * @defgroup package_manager_architecture Package Manager architecture
        * @{
        *
        * @section sec_overview Overview
        * Package Manager is an API-only module which provides the scaffolding and
        * functionality needed for Drupal to make changes to its own running code base
        * via Composer. It doesn't have a user interface.
        *
        * @see https://getcomposer.org/
        * @see https://github.com/php-tuf/composer-stager
        *
        * @section sec_concepts Concepts
        * At the center of Package Manager is the concept of a stage directory. A
        * stage directory is a complete copy of the active Drupal code base, created
        * in a temporary directory that isn't accessible over the web. The stage
        * directory doesn't have any site-specific assets like settings.php, uploaded
        * files, or SQLite databases.
        *
        * Only one stage directory can exist at any given time, and it is "owned" by
        * the user or session that originally created it. Only the owner can perform
        * operations on the stage directory, and only using the same class (i.e.,
        * \Drupal\package_manager\StageBase or a subclass) they used to create it.
        *
        * Package Manager can run Composer commands in the stage directory to require
        * or update packages in it, and then copy those changes back into the live,
        * running code base (which is referred to as the "active directory"). The
        * stage directory can then be safely deleted. Four distinct operations:
        * create, require, apply, and destroy. They comprise the "stage life cycle."
        *
        * Package Manager's \Drupal\package_manager\StageBase controls the stage life
        * cycle and is an abstract class that must be subclassed. Most of the time,
        * there should be little need to heavily customize a StageBase subclass;
        * custom code should generally use the event system to interact with the stage.
        *
        * @see sec_stage_events Stage API: Events
        * Events are dispatched before and after each operation in the stage life
        * cycle. There are two types of events: pre-operation and post-operation.
        * Pre-operation event subscribers can analyze the state of the stage directory,
        * or the system at large, and flag errors if they detect any problems. If
        * errors are flagged, the operation is prevented. Therefore, pre-operation
        * events are helpful to ensure that the stage directory is in a valid state.
        * Post-operation events are simple triggers allowing custom code to react when
        * an operation is successfully completed. They cannot flag errors to block
        * stage operations (although they can use the core messenger and logging
        * systems as needed).
        *
        * All stage events extend \Drupal\package_manager\Event\StageEvent, and all
        * pre-operation events extend
        * \Drupal\package_manager\Event\PreOperationStageEvent. All events have a
        * $stage property which allows access to the stage object itself.
        *
        * The stage dispatches the following events during its life cycle:
        *
        * - \Drupal\package_manager\Event\PreCreateEvent
        * Dispatched before the stage directory is created. At this point, the
        * stage will have recorded which user or session owns it, so another stage
        * directory cannot be created until the current one is destroyed. If
        * subscribers flag errors during this event, the stage will release its
        * ownership. This is the earliest possible time to detect problems that might
        * prevent the stage from completing its life cycle successfully. This event
        * is dispatched only once during the stage life cycle.
        * @see sec_stage_exceptions
        *
        * - \Drupal\package_manager\Event\PostCreateEvent
        * Dispatched after the stage directory has been created, which means that the
        * running Drupal code base has been copied into a separate, temporary
        * location. This event is dispatched only once during the stage life cycle.
        *
        * - \Drupal\package_manager\Event\PreRequireEvent
        * Dispatched before one or more Composer packages are required into the
        * stage directory. This event may be dispatched multiple times during the
        * stage life cycle, and receives a list of the packages which are about to
        * be required into the stage directory. The list of packages CANNOT be
        * altered by subscribers.
        *
        * - \Drupal\package_manager\Event\PostRequireEvent
        * Dispatched after one or more Composer packages have been added to the
        * stage directory. This event may be dispatched multiple times during the
        * stage life cycle, and receives a list of the packages which were required
        * into the stage directory. (Note that this is a list of packages which
        * were specifically *asked for*, not the full list of packages and
        * dependencies that was actually installed.)
        *
        * - \Drupal\package_manager\Event\PreApplyEvent
        * Dispatched before changes in the stage directory (i.e., new and/or updated
        * packages) are copied to the active directory. This is the final opportunity
        * for event subscribers to flag errors before the active directory is
        * modified, because once that has happened, the changes cannot be undone.
        * This event may be dispatched multiple times during the stage life cycle.
        *
        * - \Drupal\package_manager\Event\PostApplyEvent
        * Dispatched after changes in the stage directory have been copied to the
        * active directory. It should only be used for cleaning up after other
        * operations that happened during the stage life cycle. For example, a
        * PostCreateEvent subscriber might have set a state value which is no longer
        * needed once the stage has been applied to the active directory -- in such a
        * case, a PostApplyEvent subscriber should delete that value.
        * `drupal_flush_all_caches()` is called just before this event is dispatched,
        * so subscribers shouldn't need to flush any caches or rebuild the service
        * container. This event may be dispatched multiple times during the stage
        * life cycle, and should *never* be used for schema changes (i.e., operations
        * that should happen in `hook_update_N()` or a post-update function).
        *
        * @section sec_stage_api Stage API: Public methods
        * The public API of any stage consists of the following methods:
        *
        * - \Drupal\package_manager\StageBase::create()
        * Creates the stage directory, records ownership, and dispatches pre- and
        * post-create events. Returns a unique token which calling code must use to
        * verify stage ownership before performing operations on the stage
        * directory in subsequent requests (when the stage directory is created,
        * its ownership is automatically verified for the duration of the current
        * request). See \Drupal\package_manager\StageBase::claim() for more
        * information.
        *
        * - \Drupal\package_manager\StageBase::require()
        * Adds and/or updates packages in the stage directory and dispatches pre-
        * and post-require events. The stage must be claimed by its owner to call
        * this method.
        *
        * - \Drupal\package_manager\StageBase::apply()
        * Copies changes from the stage directory into the active directory, and
        * dispatches the pre-apply event. The stage must be claimed by its owner to
        * call this method.
        *
        * - \Drupal\package_manager\StageBase::postApply()
        * Performs post-apply tasks after changes have been copied from the stage
        * directory. This method should be called as soon as possible in a new
        * request because the code on disk may no longer match what has been loaded
        * into PHP's runtime memory. This method clears all Drupal caches, rebuilds
        * the service container, and dispatches the post-apply event. The stage must
        * be claimed by its owner to call this method.
        *
        * - \Drupal\package_manager\StageBase::destroy()
        * Destroys the stage directory, and releases ownership. It is possible to
        * destroy the stage without having claimed it first, but this shouldn't be
        * done unless absolutely necessary.
        *
        * - \Drupal\package_manager\StageBase::stageDirectoryExists()
        * Determines if the stage directory exists and returns a boolean accordingly.
        * This allows validators to directly know if the stage directory exists
        * without using \Drupal\package_manager\StageBase::getStageDirectory(), which
        * throws an exception if the stage directory does not exist.
        *
        * - \Drupal\package_manager\StageBase::getStageDirectory()
        * Returns the absolute path of the directory where changes should be staged.
        * It throws an exception if the stage hasn't been created or claimed yet.
        *
        * - \Drupal\package_manager\StageBase::isApplying()
        * Determines if the staged changes are being applied to the active directory.
        * It will return FALSE if more than an hour has passed since the apply
        * operation began.
        *
        * - \Drupal\package_manager\StageBase::isAvailable()
        * Determines if a stage directory can be created.
        *
        * @section sec_stage_exceptions Stage life cycle exceptions
        * If problems occur during any point of the stage life cycle, a
        * \Drupal\package_manager\Exception\StageException is thrown. If problems are
        * detected during one of the "pre" operations, a subclass of that is thrown:
        * \Drupal\package_manager\Exception\StageEventException. This will contain
        * \Drupal\package_manager\ValidationResult objects.
        *
        * Package Manager does not catch or handle these exceptions: they provide a
        * framework for other modules to build user experiences for installing,
        * updating, and removing packages.
        *
        * @section sec_validators_status_checks API: Validators and status checks
        * Package Manager requires certain conditions in order to function properly.
        * Event subscribers which check such conditions should ensure that they run
        * before \Drupal\package_manager\Validator\BaseRequirementsFulfilledValidator,
        * by using a priority higher than BaseRequirementsFulfilledValidator::PRIORITY.
        * BaseRequirementsFulfilledValidator will stop event propagation if any errors
        * have been flagged by the subscribers that ran before it.
        *
        * The following base requirements are checked by Package Manager:
        *
        * - Package Manager has not been explicitly disabled in the current
        * environment.
        * - The Composer executable is available.
        * - The detected version of Composer is supported.
        * - composer.json and composer.lock exist in the project root, and are valid
        * according to the @code composer validate @endcode command.
        * - The stage directory is not a subdirectory of the active directory.
        * - There is enough free disk space to do stage operations.
        * - The Drupal site root and vendor directory are writable.
        * - The current site is not part of a multisite.
        * - The project root and stage directory don't contain any unsupported links.
        * See https://github.com/php-tuf/composer-stager/tree/develop/src/Domain/Service/Precondition#symlinks
        * for information about which types of symlinks are supported.
        *
        * Apart from base requirements, Package Manager also enforces certain
        * constraints at various points of the stage life cycle (typically
        * \Drupal\package_manager\Event\PreCreateEvent and/or
        * \Drupal\package_manager\Event\PreApplyEvent), to ensure that both the active
        * directory and stage directory are kept in a safe, consistent state:
        *
        * - If the composer.lock file is changed (e.g., by installing or updating a
        * package) in the active directory after a stage directory has been created,
        * Package Manager will refuse to make any further changes to the stage
        * directory or apply the staged changes to the active directory.
        * - Composer plugins are able to perform arbitrary file system operations, and
        * hence could perform actions that make it impossible for Package Manager to
        * guarantee the Drupal site will continue to work correctly. For that reason,
        * Package Manager will refuse to make any further changes if untrusted
        * Composer plugins are installed or staged. If you know what you are doing,
        * it is possible to trust additional Composer plugins by modifying
        * package_manager.settings's "additional_trusted_composer_plugins" setting.
        * - The Drupal site must not have any pending database updates (i.e.,
        * update.php needs to be run).
        * - Composer must use HTTPS to download packages and metadata (i.e., Composer's
        * secure-http configuration option must be enabled). This is the default
        * behavior.
        *
        * Package Manager also assumes certain things that it does not explicitly
        * enforce or check:
        *
        * - Only Composer operations should be performed on the stage directory. If
        * other file operations were performed, any newly created files might not
        * be copied back to the active site because of
        * \Drupal\package_manager\PathExcluder\UnknownPathExcluder.
        *
        * Event subscribers which enforce these and other constraints are referred to
        * as validators.
        *
        * \Drupal\package_manager\Event\StatusCheckEvent may be dispatched at any time
        * to check the status of the Drupal site and whether Package Manager can
        * function properly. Package Manager does NOT dispatch this event on its own
        * because it doesn't have a UI; it is meant for modules that build on top of
        * Package Manager to ensure they will work correctly before they try to do any
        * stage operations, and present errors however they want in their own UIs.
        * Status checks can be dispatched irrespective of whether a stage directory has
        * actually been created.
        *
        * In general, validators should always listen to
        * \Drupal\package_manager\Event\StatusCheckEvent,
        * \Drupal\package_manager\Event\PreCreateEvent, and
        * \Drupal\package_manager\Event\PreApplyEvent. If they detect any errors,
        * they should call the event's ::addError() method to prevent the stage life
        * cycle from proceeding any further. If a validator encounters an exception,
        * it can use ::addErrorFromThrowable() instead of ::addError(). During status
        * checks, validators can call ::addWarning() for less severe problems --
        * warnings will NOT stop the stage life cycle. All three are convenience
        * methods for equivalent \Drupal\package_manager\ValidationResult constructors,
        * which can then be added to the event using ::addResult().
        *
        * @see \Drupal\package_manager\ValidationResult
        * @see \Drupal\package_manager\Event\PreOperationStageEvent::addError()
        * @see \Drupal\package_manager\Event\PreOperationStageEvent::addErrorFromThrowable()
        * @see \Drupal\package_manager\Event\StatusCheckEvent::addWarning()
        * @see \Drupal\package_manager\Event\PreOperationStageEvent::addResult()
        *
        * @section sec_excluded_paths Excluding files from stage operations
        * Certain files are never copied into the stage directory because they are
        * irrelevant to Composer or Package Manager. Examples include settings.php
        * and related files, public and private files, SQLite databases, and git
        * repositories. Custom code can subscribe to
        * Drupal\package_manager\Event\CollectPathsToExcludeEvent to flag paths which
        * should never be copied into the stage directory from the active directory or
        * vice versa.
        *
        * @see \Drupal\package_manager\Event\CollectPathsToExcludeEvent
        *
        * @section sec_services Useful services
        * The following services are especially useful to validators:
        * - \Drupal\package_manager\PathLocator looks up certain important paths in the
        * active directory, such as the vendor directory, the project root and the
        * web root.
        * - \Drupal\package_manager\ComposerInspector is a wrapper to interact with
        * Composer at the command line and get information from it about the
        * project's `composer.json`, which packages are installed, etc.
        *
        * @section sec_package_manager_failure_marker Package Manager failure marker
        * A file PACKAGE_MANAGER_FAILURE.yml is placed in the active directory while
        * staged code is copied back into it, and then removed after the copying is
        * finished. If this file exists, it means that the staged changes failed to be
        * applied to the active directory (for example: a file system error, or the
        * copying process was interrupted), and the site is therefore in an
        * indeterminate state. The only thing you can do is to restore the code and
        * database from a backup.
        * @see \Drupal\package_manager\FailureMarker
        *
        * @}
        */
        name: 'Package Manager'
        type: module
        description: 'API module providing functionality to stage package installs and updates with Composer.'
        package: Core
        version: VERSION
        lifecycle: experimental
        dependencies:
        - drupal:update
        <?php
        /**
        * @file
        * Contains install and update functions for Package Manager.
        */
        declare(strict_types=1);
        use Drupal\package_manager\ComposerInspector;
        use Drupal\package_manager\Exception\StageFailureMarkerException;
        use Drupal\package_manager\FailureMarker;
        use PhpTuf\ComposerStager\API\Exception\ExceptionInterface;
        use PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface;
        /**
        * Implements hook_requirements().
        */
        function package_manager_requirements(string $phase): array {
        $requirements = [];
        // If we're able to check for the presence of the failure marker at all, do it
        // irrespective of the current run phase. If the failure marker is there, the
        // site is in an indeterminate state and should be restored from backup ASAP.
        $service_id = FailureMarker::class;
        if (\Drupal::hasService($service_id)) {
        try {
        \Drupal::service($service_id)->assertNotExists(NULL);
        }
        catch (StageFailureMarkerException $exception) {
        $requirements['package_manager_failure_marker'] = [
        'title' => t('Failed Package Manager update detected'),
        'description' => $exception->getMessage(),
        'severity' => REQUIREMENT_ERROR,
        ];
        }
        }
        if ($phase !== 'runtime') {
        return $requirements;
        }
        /** @var \PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface $executable_finder */
        $executable_finder = \Drupal::service(ExecutableFinderInterface::class);
        // Report the Composer version in use, as well as its path.
        $title = t('Composer version');
        try {
        $requirements['package_manager_composer'] = [
        'title' => $title,
        'description' => t('@version (<code>@path</code>)', [
        '@version' => \Drupal::service(ComposerInspector::class)->getVersion(),
        '@path' => $executable_finder->find('composer'),
        ]),
        'severity' => REQUIREMENT_INFO,
        ];
        }
        catch (\Throwable $e) {
        // All Composer Stager exceptions are translatable.
        $message = $e instanceof ExceptionInterface
        ? $e->getTranslatableMessage()
        : $e->getMessage();
        $requirements['package_manager_composer'] = [
        'title' => $title,
        'description' => t('Composer was not found. The error message was: @message', [
        '@message' => $message,
        ]),
        'severity' => REQUIREMENT_ERROR,
        ];
        }
        return $requirements;
        }
        <?php
        /**
        * @file
        * Contains hook implementations for Package Manager.
        */
        declare(strict_types=1);
        use Drupal\package_manager\ComposerInspector;
        /**
        * Implements hook_help().
        */
        function package_manager_help($route_name): ?string {
        switch ($route_name) {
        case 'help.page.package_manager':
        $output = '<h3 id="package-manager-about">' . t('About') . '</h3>';
        $output .= '<p>' . t('Package Manager is a framework for updating Drupal core and installing contributed modules and themes via Composer. It has no user interface, but it provides an API for creating a temporary copy of the current site, making changes to the copy, and then syncing those changes back into the live site.') . '</p>';
        $output .= '<p>' . t('Package Manager dispatches events before and after various operations, and external code can integrate with it by subscribing to those events. For more information, see <code>package_manager.api.php</code>.') . '</p>';
        $output .= '<h3 id="package-manager-requirements">' . t('Requirements') . '</h3>';
        $output .= '<ul>';
        $output .= ' <li>' . t("The Drupal application's codebase must be writable in order to use Automatic Updates. This includes Drupal core, modules, themes and the Composer dependencies in the <code>vendor</code> directory. This makes Automatic Updates incompatible with some hosting platforms.") . '</li>';
        $output .= ' <li>' . t('Package Manager requires a Composer executable whose version satisfies <code>@version</code>, and PHP must have permission to run it.', ['@version' => ComposerInspector::SUPPORTED_VERSION]) . '</li>';
        $output .= ' <li>' . t("Your Drupal site's <code>composer.json</code> file must be valid according to <code>composer validate</code>. See <a href=\":url\">Composer's documentation</a> for more information.", [':url' => 'https://getcomposer.org/doc/03-cli.md#validate']) . '</li>';
        $output .= ' <li>' . t('Composer must be configured for secure downloads. This means that <a href=":disable-tls">the <code>disable-tls</code> option</a> must be <code>false</code>, and <a href=":secure-http">the <code>secure-http</code> option</a> must be <code>true</code> in the <code>config</code> section of your <code>composer.json</code> file. If these options are not set in your <code>composer.json</code>, Composer will behave securely by default. To set these values at the command line, run the following commands:', [
        ':disable-tls' => 'https://getcomposer.org/doc/06-config.md#disable-tls',
        ':secure-http' => 'https://getcomposer.org/doc/06-config.md#secure-http',
        ]);
        $output .= '<pre><code>';
        $output .= "composer config --unset disable-tls\n";
        $output .= "composer config --unset secure-http\n";
        $output .= '</code></pre></li></ul>';
        $output .= '<h3 id="package-manager-limitations">' . t('Limitations') . '</h3>';
        $output .= '<p>' . t("Because Package Manager modifies the current site's code base, it is intentionally limited in certain ways to prevent unexpected changes to the live site:") . '</p>';
        $output .= '<ul>';
        $output .= ' <li>' . t('It does not support Drupal multi-site installations.') . '</li>';
        $output .= ' <li>' . t('It only allows supported Composer plugins. If you have any, see <a href="#package-manager-faq-unsupported-composer-plugin">What if it says I have unsupported Composer plugins in my codebase?</a>.') . '</li>';
        $output .= ' <li>' . t('It does not automatically perform version control operations, e.g., with Git. Site administrators are responsible for committing updates.') . '</li>';
        $output .= ' <li>' . t('It can only maintain one copy of the site at any given time. If a copy of the site already exists, another one cannot be created until the existing copy is destroyed.') . '</li>';
        $output .= ' <li>' . t('It associates the temporary copy of the site with the user or session that originally created it, and only that user or session can make changes to it.') . '</li>';
        $output .= ' <li>' . t('It does not allow modules to be uninstalled while syncing changes into live site.') . '</li>';
        $output .= '</ul>';
        $output .= '<p>' . t('For more information, see the <a href=":url">online documentation for the Package Manager module</a>.', [':url' => 'https://www.drupal.org/docs/8/core/modules/package-manager']) . '</p>';
        $output .= '<h3 id="package-manager-faq">' . t('FAQ') . '</h3>';
        $output .= '<h4 id="package-manager-composer-related-faq">' . t('FAQs related to Composer') . '</h4>';
        $output .= '<ul>';
        $output .= ' <li>' . t('What if it says the <code>proc_open()</code> function is disabled on your PHP installation?');
        $output .= ' <p>' . t('Ask your system administrator to remove <code>proc_open()</code> from the <a href=":url">disable_functions</a> setting in <code>php.ini</code>.', [':url' => 'https://www.php.net/manual/en/ini.core.php#ini.disable-functions']) . '</p>';
        $output .= ' </li>';
        $output .= ' <li>' . t('What if it says the <code>composer</code> executable cannot be found?');
        $output .= ' <p>' . t("If the <code>composer</code> executable's path cannot be automatically determined, it can be explicitly set by adding the following line to <code>settings.php</code>:") . '</p>';
        $output .= " <pre><code>\$config['package_manager.settings']['executables']['composer'] = '/full/path/to/composer.phar';</code></pre>";
        $output .= ' </li>';
        $output .= ' <li>' . t('What if it says the detected version of Composer is not supported?');
        $output .= ' <p>' . t('The version of the <code>composer</code> executable must satisfy <code>@version</code>. See the <a href=":url">the Composer documentation</a> for more information, or use this command to update Composer:', ['@version' => ComposerInspector::SUPPORTED_VERSION, ':url' => 'https://getcomposer.org/doc/03-cli.md#self-update-selfupdate']) . '</p>';
        $output .= ' <pre><code>composer self-update</code></pre>';
        $output .= ' </li>';
        $output .= ' <li>' . t('What if it says the <code>composer validate</code> command failed?');
        $output .= ' <p>' . t('Composer detected problems with your <code>composer.json</code> and/or <code>composer.lock</code> files, and the project is not in a completely valid state. See <a href=":url">the Composer documentation</a> for more information.', [':url' => 'https://getcomposer.org/doc/04-schema.md']) . '</p>';
        $output .= ' </li>';
        $output .= '</ul>';
        $output .= '<h4 id="package-manager-faq-rsync">' . t('Using rsync') . '</h4>';
        $output .= '<p>' . t('Package Manager must be able to run <code>rsync</code> to copy files between the live site and the stage directory. Package Manager will try to detect the path to <code>rsync</code>, but if it cannot be detected, you can set it explicitly by adding the following line to <code>settings.php</code>:') . '</p>';
        $output .= "<pre><code>\$config['package_manager.settings']['executables']['rsync'] = '/full/path/to/rsync';</code></pre>";
        $output .= '<h4 id="package-manager-tuf-info">' . t('Enabling PHP-TUF protection') . '</h4>';
        $output .= '<p>' . t('Package Manager requires <a href=":php-tuf">PHP-TUF</a>, which implements <a href=":tuf">The Update Framework</a> as a way to help secure Composer package downloads via the <a href=":php-tuf-plugin">PHP-TUF Composer integration plugin</a>. This plugin must be installed and configured properly in order to use Package Manager.', [
        ':php-tuf' => 'https://github.com/php-tuf/php-tuf',
        ':tuf' => 'https://theupdateframework.io/',
        ':php-tuf-plugin' => 'https://github.com/php-tuf/composer-integration',
        ]) . '</p>';
        $output .= '<p>' . t('To install and configure the plugin as needed, you can run the following commands:') . '</p>';
        $output .= '<pre><code>';
        $output .= "composer config allow-plugins.php-tuf/composer-integration true\n";
        $output .= "composer require php-tuf/composer-integration";
        $output .= '</code></pre>';
        $output .= '<p>' . t('Package Manager currently requires the <code>https://packages.drupal.org/8</code> Composer repository to be protected by TUF. To set this up, run the following command:') . '</p>';
        $output .= '<pre><code>';
        $output .= "composer tuf:protect https://packages.drupal.org/8\n";
        $output .= '</code></pre>';
        $output .= '<h4 id="package-manager-faq-unsupported-composer-plugin">' . t('What if it says I have unsupported Composer plugins in my codebase?') . '</h4>';
        $output .= '<p>' . t('A fresh Drupal installation only uses supported Composer plugins, but some modules or themes may depend on additional Composer plugins. <a href=":new-issue">Create a new issue</a> when you encounter this.', [
        ':new-issue' => 'https://www.drupal.org/node/add/project-issue/auto_updates',
        ]) . '</p>';
        $output .= '<p>' . t('It is possible to <em>trust</em> additional Composer plugins, but this requires significant expertise: understanding the code of that Composer plugin, what the effects on the file system are and how it affects the Package Manager module. Some Composer plugins could result in a broken site!') . '</p>';
        $output .= '<h4 id="package-manager-faq-composer-patches-installed-or-removed">' . t('What if it says <code>cweagans/composer-patches</code> cannot be installed/removed?') . '</h4>';
        $output .= '<p>' . t('Installation or removal of <code>cweagans/composer-patches</code> via Package Manager is not supported. You can install or remove it manually by running Composer commands in your site root.') . '</p>';
        $output .= '<p>' . t('To install it:') . '</p>';
        $output .= '<pre><code>composer require cweagans/composer-patches</code></pre>';
        $output .= '<p>' . t('To remove it:') . '</p>';
        $output .= '<pre><code>composer remove cweagans/composer-patches</code></pre>';
        $output .= '<h4 id="package-manager-faq-composer-patches-not-a-root-dependency">' . t('What if it says <code>cweagans/composer-patches</code> must be a root dependency?') . '</h4>';
        $output .= '<p>' . t('If <code>cweagans/composer-patches</code> is installed, it must be defined as a dependency of the main project (i.e., it must be listed in the <code>require</code> or <code>require-dev</code> section of <code>composer.json</code>). You can run the following command in your site root to add it as a dependency of the main project:') . '</p>';
        $output .= "<pre><code>composer require cweagans/composer-patches</code></pre>";
        return $output;
        }
        return NULL;
        }
        services:
        _defaults:
        autoconfigure: true
        autowire: true
        # Underlying Symfony utilities for Composer Stager.
        Symfony\Component\Filesystem\Filesystem:
        public: false
        Symfony\Component\Process\ExecutableFinder:
        public: false
        # Basic infrastructure services for Composer Stager, overridden by us to
        # provide additional functionality.
        Drupal\package_manager\ExecutableFinder:
        public: false
        decorates: 'PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface'
        Drupal\package_manager\ProcessFactory:
        public: false
        decorates: 'PhpTuf\ComposerStager\API\Process\Factory\ProcessFactoryInterface'
        Drupal\package_manager\TranslatableStringFactory:
        public: false
        decorates: 'PhpTuf\ComposerStager\API\Translation\Factory\TranslatableFactoryInterface'
        PhpTuf\ComposerStager\API\FileSyncer\Service\FileSyncerInterface:
        factory: ['@PhpTuf\ComposerStager\API\FileSyncer\Factory\FileSyncerFactoryInterface', 'create']
        Drupal\package_manager\LoggingBeginner:
        public: false
        decorates: 'PhpTuf\ComposerStager\API\Core\BeginnerInterface'
        Drupal\package_manager\LoggingStager:
        public: false
        decorates: 'PhpTuf\ComposerStager\API\Core\StagerInterface'
        Drupal\package_manager\LoggingCommitter:
        public: false
        decorates: 'PhpTuf\ComposerStager\API\Core\CommitterInterface'
        logger.channel.package_manager:
        parent: logger.channel_base
        arguments:
        - 'package_manager'
        logger.channel.package_manager_change_log:
        parent: logger.channel_base
        arguments:
        - 'package_manager_change_log'
        # Services provided to Drupal by Package Manager.
        Drupal\package_manager\PathLocator:
        arguments:
        $appRoot: '%app.root%'
        Drupal\package_manager\FailureMarker: {}
        Drupal\package_manager\EventSubscriber\UpdateDataSubscriber: {}
        Drupal\package_manager\EventSubscriber\ChangeLogger:
        calls:
        - [setLogger, ['@logger.channel.package_manager_change_log']]
        Drupal\package_manager\ComposerInspector: {}
        # Validators.
        Drupal\package_manager\Validator\EnvironmentSupportValidator: {}
        Drupal\package_manager\Validator\ComposerValidator: {}
        Drupal\package_manager\Validator\DiskSpaceValidator: {}
        Drupal\package_manager\Validator\PendingUpdatesValidator:
        arguments:
        - '%app.root%'
        - '@update.post_update_registry'
        autowire: false
        Drupal\package_manager\Validator\LockFileValidator:
        arguments:
        $keyValueFactory: '@keyvalue'
        Drupal\package_manager\Validator\WritableFileSystemValidator: {}
        Drupal\package_manager\Validator\ComposerMinimumStabilityValidator: {}
        Drupal\package_manager\Validator\MultisiteValidator: {}
        Drupal\package_manager\Validator\SymlinkValidator: {}
        Drupal\package_manager\Validator\DuplicateInfoFileValidator: {}
        Drupal\package_manager\Validator\EnabledExtensionsValidator: {}
        Drupal\package_manager\Validator\OverwriteExistingPackagesValidator: {}
        Drupal\package_manager\Validator\AllowedScaffoldPackagesValidator: {}
        Drupal\package_manager\Validator\StagedDBUpdateValidator: {}
        Drupal\package_manager\PathExcluder\TestSiteExcluder: {}
        Drupal\package_manager\PathExcluder\VendorHardeningExcluder: {}
        Drupal\package_manager\PathExcluder\SiteFilesExcluder:
        arguments:
        $wrappers: [public, private, assets]
        Drupal\package_manager\PathExcluder\SqliteDatabaseExcluder: {}
        Drupal\package_manager\PathExcluder\GitExcluder: {}
        Drupal\package_manager\PathExcluder\UnknownPathExcluder: {}
        Drupal\package_manager\PathExcluder\SiteConfigurationExcluder:
        arguments:
        $sitePath: '%site.path%'
        Drupal\package_manager\PathExcluder\NodeModulesExcluder: {}
        Drupal\package_manager\PackageManagerUninstallValidator:
        arguments:
        $eventDispatcher: '@event_dispatcher'
        Drupal\package_manager\Validator\SettingsValidator: {}
        Drupal\package_manager\Validator\RsyncValidator: {}
        Drupal\package_manager\Validator\ComposerPluginsValidator: {}
        Drupal\package_manager\Validator\ComposerPatchesValidator: {}
        Drupal\package_manager\Validator\BaseRequirementsFulfilledValidator: {}
        Drupal\package_manager\Validator\SupportedReleaseValidator: {}
        Drupal\package_manager\Validator\StageNotInActiveValidator: {}
        Drupal\package_manager\Validator\PhpExtensionsValidator: {}
        Drupal\package_manager\Validator\PhpTufValidator:
        arguments:
        $repositories:
        - 'https://packages.drupal.org/8'
        Drupal\package_manager\PackageManagerUpdateProcessor:
        arguments:
        # @todo Autowire $update_fetcher when https://drupal.org/i/3325557 lands.
        $update_fetcher: '@update.fetcher'
        $key_value_factory: '@keyvalue'
        $key_value_expirable_factory: '@keyvalue.expirable'
        <?php
        declare(strict_types=1);
        namespace Drupal\package_manager;
        use Composer\Semver\Semver;
        use Drupal\Core\StringTranslation\StringTranslationTrait;
        use Drupal\package_manager\Exception\ComposerNotReadyException;
        use PhpTuf\ComposerStager\API\Exception\PreconditionException;
        use PhpTuf\ComposerStager\API\Exception\RuntimeException;
        use PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface;
        use PhpTuf\ComposerStager\API\Precondition\Service\ComposerIsAvailableInterface;
        use PhpTuf\ComposerStager\API\Process\Service\ComposerProcessRunnerInterface;
        use Psr\Log\LoggerAwareInterface;
        use Psr\Log\LoggerAwareTrait;
        use Psr\Log\LoggerInterface;
        use Psr\Log\NullLogger;
        /**
        * Defines a class to get information from Composer.
        *
        * This is a PHP wrapper to facilitate interacting with composer and:
        * - list installed packages: getInstalledPackagesList() (`composer show`)
        * - validate composer state & project: validate() (`composer validate`)
        * - read project & package configuration: getConfig() (`composer config`)
        * - read root package info: getRootPackageInfo() (`composer show --self`)
        */
        class ComposerInspector implements LoggerAwareInterface {
        use LoggerAwareTrait {
        setLogger as traitSetLogger;
        }
        use StringTranslationTrait;
        /**
        * The process output callback.
        *
        * @var \Drupal\package_manager\ProcessOutputCallback
        */
        private ProcessOutputCallback $processCallback;
        /**
        * Statically cached installed package lists, keyed by directory.
        *
        * @var \Drupal\package_manager\InstalledPackagesList[]
        */
        private array $packageLists = [];
        /**
        * A semantic version constraint for the supported version(s) of Composer.
        *
        * @see https://endoflife.date/composer
        *
        * @var string
        */
        final public const SUPPORTED_VERSION = '^2.6';
        public function __construct(
        private readonly ComposerProcessRunnerInterface $runner,
        private readonly ComposerIsAvailableInterface $composerIsAvailable,
        private readonly PathFactoryInterface $pathFactory,
        ) {
        $this->processCallback = new ProcessOutputCallback();
        $this->setLogger(new NullLogger());
        }
        /**
        * {@inheritdoc}
        */
        public function setLogger(LoggerInterface $logger): void {
        $this->traitSetLogger($logger);
        $this->processCallback->setLogger($logger);
        }
        /**
        * Checks that Composer commands can be run.
        *
        * @param string $working_dir
        * The directory in which Composer will be run.
        *
        * @see ::validateExecutable()
        * @see ::validateProject()
        */
        public function validate(string $working_dir): void {
        $this->validateExecutable();
        $this->validateProject($working_dir);
        }
        /**
        * Checks that `composer.json` is valid and `composer.lock` exists.
        *
        * @param string $working_dir
        * The directory to check.
        *
        * @throws \Drupal\package_manager\Exception\ComposerNotReadyException
        * Thrown if:
        * - `composer.json` doesn't exist in the given directory or is invalid
        * according to `composer validate`.
        * - `composer.lock` doesn't exist in the given directory.
        */
        private function validateProject(string $working_dir): void {
        $messages = [];
        $previous_exception = NULL;
        // If either composer.json or composer.lock have changed, ensure the
        // directory is in a completely valid state, according to Composer.
        if ($this->invalidateCacheIfNeeded($working_dir)) {
        try {
        $this->runner->run([
        'validate',
        '--check-lock',
        '--no-check-publish',
        '--with-dependencies',
        '--no-ansi',
        "--working-dir=$working_dir",
        ]);
        }
        catch (RuntimeException $e) {
        $messages[] = $e->getMessage();
        $previous_exception = $e;
        }
        }
        // Check for the presence of composer.lock, because `composer validate`
        // doesn't expect it to exist, but we do (see ::getInstalledPackagesList()).
        if (!file_exists($working_dir . DIRECTORY_SEPARATOR . 'composer.lock')) {
        $messages[] = $this->t('composer.lock not found in @dir.', [
        '@dir' => $working_dir,
        ]);
        }
        if ($messages) {
        throw new ComposerNotReadyException($working_dir, $messages, 0, $previous_exception);
        }
        }
        /**
        * Validates that the Composer executable exists in a supported version.
        *
        * @throws \Exception
        * Thrown if the Composer executable is not available or the detected
        * version of Composer is not supported.
        */
        private function validateExecutable(): void {
        $messages = [];
        // Ensure the Composer executable is available. For performance reasons,
        // statically cache the result, since it's unlikely to change during the
        // current request. If $unavailable_message is NULL, it means we haven't
        // done this check yet. If it's FALSE, it means we did the check and there
        // were no errors; and, if it's a string, it's the error message we received
        // the last time we did this check.
        static $unavailable_message;
        if ($unavailable_message === NULL) {
        try {
        // The "Composer is available" precondition requires active and stage
        // directories, but they don't actually matter to it, nor do path
        // exclusions, so dummies can be passed for simplicity.
        $active_dir = $this->pathFactory->create(__DIR__);
        $stage_dir = $active_dir;
        $this->composerIsAvailable->assertIsFulfilled($active_dir, $stage_dir);
        $unavailable_message = FALSE;
        }
        catch (PreconditionException $e) {
        $unavailable_message = $e->getMessage();
        }
        }
        if ($unavailable_message) {
        $messages[] = $unavailable_message;
        }
        // The detected version of Composer is unlikely to change during the
        // current request, so statically cache it. If $unsupported_message is NULL,
        // it means we haven't done this check yet. If it's FALSE, it means we did
        // the check and there were no errors; and, if it's a string, it's the error
        // message we received the last time we did this check.
        static $unsupported_message;
        if ($unsupported_message === NULL) {
        try {
        $detected_version = $this->getVersion();
        if (Semver::satisfies($detected_version, static::SUPPORTED_VERSION)) {
        // We did the version check, and it did not produce an error message.
        $unsupported_message = FALSE;
        }
        else {
        $unsupported_message = $this->t('The detected Composer version, @version, does not satisfy <code>@constraint</code>.', [
        '@version' => $detected_version,
        '@constraint' => static::SUPPORTED_VERSION,
        ]);
        }
        }
        catch (\UnexpectedValueException $e) {
        $unsupported_message = $e->getMessage();
        }
        }
        if ($unsupported_message) {
        $messages[] = $unsupported_message;
        }
        if ($messages) {
        throw new ComposerNotReadyException(NULL, $messages);
        }
        }
        /**
        * Returns a config value from Composer.
        *
        * @param string $key
        * The config key to get.
        * @param string $context
        * The path of either the directory in which to run Composer, or a specific
        * configuration file (such as a particular package's `composer.json`) from
        * which to read specific values.
        *
        * @return string|null
        * The output data. Note that the caller must know the shape of the
        * requested key's value: if it's a string, no further processing is needed,
        * but if it is a boolean, an array or a map, JSON decoding should be
        * applied.
        *
        * @see ::getAllowPluginsConfig()
        * @see \Composer\Command\ConfigCommand::execute()
        */
        public function getConfig(string $key, string $context): ?string {
        $this->validateExecutable();
        $command = ['config', $key];
        // If we're consulting a specific file for the config value, we don't need
        // to validate the project as a whole.
        if (is_file($context)) {
        $command[] = "--file={$context}";
        }
        else {
        $this->validateProject($context);
        $command[] = "--working-dir={$context}";
        }
        try {
        $this->runner->run($command, callback: $this->processCallback->reset());
        }
        catch (RuntimeException $e) {
        // Assume any error from `composer config` is about an undefined key-value
        // pair which may have a known default value.
        return match ($key) {
        'extra' => '{}',
        default => throw $e,
        };
        }
        $output = $this->processCallback->getOutput();
        return $output ? trim(implode('', $output)) : NULL;
        }
        /**
        * Returns the current Composer version.
        *
        * @return string
        * The Composer version.
        *
        * @throws \UnexpectedValueException
        * Thrown if the Composer version cannot be determined.
        */
        public function getVersion(): string {
        $this->runner->run(['--format=json'], callback: $this->processCallback->reset());
        $data = $this->processCallback->parseJsonOutput();
        if (isset($data['application']['name'])
        && isset($data['application']['version'])
        && $data['application']['name'] === 'Composer'
        && is_string($data['application']['version'])) {
        return $data['application']['version'];
        }
        throw new \UnexpectedValueException('Unable to determine Composer version');
        }
        /**
        * Returns the installed packages list.
        *
        * @param string $working_dir
        * The working directory in which to run Composer. Should contain a
        * `composer.lock` file.
        *
        * @return \Drupal\package_manager\InstalledPackagesList
        * The installed packages list for the directory.
        *
        * @throws \UnexpectedValueException
        * Thrown if a package reports that its install path is the same as the
        * working directory, and it is not of the `metapackage` type.
        */
        public function getInstalledPackagesList(string $working_dir): InstalledPackagesList {
        $working_dir = realpath($working_dir);
        $this->validate($working_dir);
        if (array_key_exists($working_dir, $this->packageLists)) {
        return $this->packageLists[$working_dir];
        }
        $packages_data = $this->show($working_dir);
        $packages_data = $this->getPackageTypes($packages_data, $working_dir);
        foreach ($packages_data as $name => $package) {
        $path = $package['path'];
        // For packages installed as dev snapshots from certain version control
        // systems, `composer show` displays the version like `1.0.x-dev 0a1b2c`,
        // which will cause an exception if we try to parse it as a legitimate
        // semantic version. Since we don't need the abbreviated commit hash, just
        // remove it.
        if (str_contains($package['version'], '-dev ')) {
        $packages_data[$name]['version'] = explode(' ', $package['version'], 2)[0];
        }
        // We expect Composer to report that metapackages' install paths are the
        // same as the working directory, in which case InstalledPackage::$path
        // should be NULL. For all other package types, we consider it invalid
        // if the install path is the same as the working directory.
        if (isset($package['type']) && $package['type'] === 'metapackage') {
        if ($path !== NULL) {
        throw new \UnexpectedValueException("Metapackage '$name' is installed at unexpected path: '$path', expected NULL");
        }
        $packages_data[$name]['path'] = $path;
        }
        elseif ($path === $working_dir) {
        throw new \UnexpectedValueException("Package '$name' cannot be installed at path: '$path'");
        }
        else {
        $packages_data[$name]['path'] = realpath($path);
        }
        }
        $packages_data = array_map(InstalledPackage::createFromArray(...), $packages_data);
        $list = new InstalledPackagesList($packages_data);
        $this->packageLists[$working_dir] = $list;
        return $list;
        }
        /**
        * Loads package types from the lock file.
        *
        * The package type is not available using `composer show` for listing
        * packages. To avoiding making many calls to `composer show package-name`,
        * load the lock file data to get the `type` key.
        *
        * @param array $packages_data
        * The packages data returned from ::show().
        * @param string $working_dir
        * The directory where Composer was run.
        *
        * @return array
        * The packages data, with a `type` key added to each package.
        */
        private function getPackageTypes(array $packages_data, string $working_dir): array {
        $lock_content = file_get_contents($working_dir . DIRECTORY_SEPARATOR . 'composer.lock');
        $lock_data = json_decode($lock_content, TRUE, flags: JSON_THROW_ON_ERROR);
        $lock_packages = array_merge($lock_data['packages'] ?? [], $lock_data['packages-dev'] ?? []);
        foreach ($lock_packages as $lock_package) {
        $name = $lock_package['name'];
        if (isset($packages_data[$name]) && isset($lock_package['type'])) {
        $packages_data[$name]['type'] = $lock_package['type'];
        }
        }
        return $packages_data;
        }
        /**
        * Returns the output of `composer show --self` in a directory.
        *
        * @param string $working_dir
        * The directory in which to run Composer.
        *
        * @return array
        * The parsed output of `composer show --self`.
        */
        public function getRootPackageInfo(string $working_dir): array {
        $this->validate($working_dir);
        $this->runner->run(['show', '--self', '--format=json', "--working-dir={$working_dir}"], callback: $this->processCallback->reset());
        return $this->processCallback->parseJsonOutput();
        }
        /**
        * Gets the installed packages data from running `composer show`.
        *
        * @param string $working_dir
        * The directory in which to run `composer show`.
        *
        * @return array[]
        * The installed packages data, keyed by package name.
        */
        protected function show(string $working_dir): array {
        $data = [];
        $options = ['show', '--format=json', "--working-dir={$working_dir}"];
        // We don't get package installation paths back from `composer show` unless
        // we explicitly pass the --path option to it. However, for some
        // inexplicable reason, that option hides *other* relevant information
        // about the installed packages. So, to work around this maddening quirk, we
        // call `composer show` once without the --path option, and once with it,
        // then merge the results together. Composer, for its part, will not support
        // returning the install path from `composer show`: see
        // https://github.com/composer/composer/pull/11340.
        $this->runner->run($options, callback: $this->processCallback->reset());
        $output = $this->processCallback->parseJsonOutput();
        // $output['installed'] will not be set if no packages are installed.
        if (isset($output['installed'])) {
        foreach ($output['installed'] as $installed_package) {
        $data[$installed_package['name']] = $installed_package;
        }
        $options[] = '--path';
        $this->runner->run($options, callback: $this->processCallback->reset());
        $output = $this->processCallback->parseJsonOutput();
        foreach ($output['installed'] as $installed_package) {
        $data[$installed_package['name']]['path'] = $installed_package['path'];
        }
        }
        return $data;
        }
        /**
        * Invalidates cached data if composer.json or composer.lock have changed.
        *
        * The following cached data may be invalidated:
        * - Installed package lists (see ::getInstalledPackageList()).
        *
        * @param string $working_dir
        * A directory that contains a `composer.json` file, and optionally a
        * `composer.lock`. If either file has changed since the last time this
        * method was called, any cached data for the directory will be invalidated.
        *
        * @return bool
        * TRUE if the cached data was invalidated, otherwise FALSE.
        */
        private function invalidateCacheIfNeeded(string $working_dir): bool {
        static $known_hashes = [];
        $invalidate = FALSE;
        foreach (['composer.json', 'composer.lock'] as $filename) {
        $known_hash = $known_hashes[$working_dir][$filename] ?? '';
        // If the file doesn't exist, hash_file() will return FALSE.
        $current_hash = @hash_file('xxh64', $working_dir . DIRECTORY_SEPARATOR . $filename);
        if ($known_hash && $current_hash && hash_equals($known_hash, $current_hash)) {
        continue;
        }
        $known_hashes[$working_dir][$filename] = $current_hash;
        $invalidate = TRUE;
        }
        if ($invalidate) {
        unset($this->packageLists[$working_dir]);
        }
        return $invalidate;
        }
        /**
        * Returns the value of `allow-plugins` config setting.
        *
        * @param string $dir
        * The directory in which to run Composer.
        *
        * @return bool[]|bool
        * An array of boolean flags to allow or disallow certain plugins, or TRUE
        * if all plugins are allowed.
        *
        * @see https://getcomposer.org/doc/06-config.md#allow-plugins
        */
        public function getAllowPluginsConfig(string $dir): array|bool {
        $value = $this->getConfig('allow-plugins', $dir);
        // Try to convert the value we got back to a boolean. If it's not a boolean,
        // it should be an array of plugin-specific flags.
        $value = json_decode($value, TRUE, flags: JSON_THROW_ON_ERROR);
        // An empty array indicates that no plugins are allowed.
        return $value ?: [];
        }
        }
        <?php
        declare(strict_types=1);
        namespace Drupal\package_manager\Event;
        use Drupal\package_manager\StageBase;
        use Drupal\package_manager\PathLocator;
        use PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface;
        use PhpTuf\ComposerStager\API\Path\Factory\PathListFactoryInterface;
        use PhpTuf\ComposerStager\API\Path\Value\PathListInterface;
        /**
        * Defines an event that collects paths to exclude.
        *
        * These paths are excluded by Composer Stager and are never copied into the
        * stage directory from the active directory, or vice versa.
        */
        final class CollectPathsToExcludeEvent extends StageEvent implements PathListInterface {
        /**
        * Constructs a CollectPathsToExcludeEvent object.
        *
        * @param \Drupal\package_manager\StageBase $stage
        * The stage which fired this event.
        * @param \Drupal\package_manager\PathLocator $pathLocator
        * The path locator service.
        * @param \PhpTuf\ComposerStager\API\Path\Factory\PathFactoryInterface $pathFactory
        * The path factory service.
        * @param \PhpTuf\ComposerStager\API\Path\Value\PathListInterface|null $pathList
        * (optional) The list of paths to exclude.
        */
        public function __construct(
        StageBase $stage,
        private readonly PathLocator $pathLocator,
        private readonly PathFactoryInterface $pathFactory,
        private ?PathListInterface $pathList = NULL,
        ) {
        parent::__construct($stage);
        $this->pathList ??= \Drupal::service(PathListFactoryInterface::class)
        ->create();
        }
        /**
        * {@inheritdoc}
        */
        public function add(string ...$paths): void {
        $this->pathList->add(...$paths);
        }
        /**
        * {@inheritdoc}
        */
        public function getAll(): array {
        return array_unique($this->pathList->getAll());
        }
        /**
        * Flags paths to be ignored, relative to the web root.
        *
        * This should only be used for paths that, if they exist at all, are
        * *guaranteed* to exist within the web root.
        *
        * @param string[] $paths
        * The paths to ignore. These should be relative to the web root. They will
        * be made relative to the project root.
        */
        public function addPathsRelativeToWebRoot(array $paths): void {
        $web_root = $this->pathLocator->getWebRoot();
        if ($web_root) {
        $web_root .= '/';
        }
        foreach ($paths as $path) {
        // Make the path relative to the project root by prefixing the web root.
        $this->add($web_root . $path);
        }
        }
        /**
        * Flags paths to be ignored, relative to the project root.
        *
        * @param string[] $paths
        * The paths to ignore. Absolute paths will be made relative to the project
        * root; relative paths are assumed to be relative to the project root.
        *
        * @throws \LogicException
        * If any of the given paths are absolute, but not inside the project root.
        */
        public function addPathsRelativeToProjectRoot(array $paths): void {
        $project_root = $this->pathLocator->getProjectRoot();
        foreach ($paths as $path) {
        if ($this->pathFactory->create($path)->isAbsolute()) {
        if (!str_starts_with($path, $project_root)) {
        throw new \LogicException("$path is not inside the project root: $project_root.");
        }
        }
        // Make absolute paths relative to the project root.
        $path = str_replace($project_root, '', $path);
        $path = ltrim($path, '/');
        $this->add($path);
        }
        }
        /**
        * Finds all directories in the project root matching the given name.
        *
        * @param string $directory_name
        * A directory name.
        *
        * @return string[]
        * All discovered absolute paths matching the given directory name.
        */
        public function scanForDirectoriesByName(string $directory_name): array {
        $flags = \FilesystemIterator::UNIX_PATHS;
        $flags |= \FilesystemIterator::CURRENT_AS_SELF;
        $directories_tree = new \RecursiveDirectoryIterator($this->pathLocator->getProjectRoot(), $flags);
        $filtered_directories = new \RecursiveIteratorIterator($directories_tree, \RecursiveIteratorIterator::SELF_FIRST);
        $matched_directories = new \CallbackFilterIterator($filtered_directories,
        fn (\RecursiveDirectoryIterator $current) => $current->isDir() && $current->getFilename() === $directory_name
        );
        return array_keys(iterator_to_array($matched_directories));
        }
        }
        <?php
        declare(strict_types=1);
        namespace Drupal\package_manager\Event;
        /**
        * Event fired after staged changes are synced to the active directory.
        */
        final class PostApplyEvent extends StageEvent {
        }
        <?php
        declare(strict_types=1);
        namespace Drupal\package_manager\Event;
        /**
        * Event fired after a stage directory has been created.
        */
        final class PostCreateEvent extends StageEvent {
        }
        <?php
        declare(strict_types=1);
        namespace Drupal\package_manager\Event;
        /**
        * Event fired after packages are updated to the stage directory.
        */
        final class PostRequireEvent extends StageEvent {
        use RequireEventTrait;
        }
        <?php
        declare(strict_types=1);
        namespace Drupal\package_manager\Event;
        use Drupal\package_manager\ImmutablePathList;
        use Drupal\package_manager\StageBase;
        use PhpTuf\ComposerStager\API\Path\Value\PathListInterface;
        /**
        * Event fired before staged changes are synced to the active directory.
        */
        final class PreApplyEvent extends PreOperationStageEvent {
        /**
        * The list of paths to ignore in the active and stage directories.
        *
        * @var \Drupal\package_manager\ImmutablePathList
        */
        public readonly ImmutablePathList $excludedPaths;
        /**
        * Constructs a PreApplyEvent object.
        *
        * @param \Drupal\package_manager\StageBase $stage
        * The stage which fired this event.
        * @param \PhpTuf\ComposerStager\API\Path\Value\PathListInterface $excluded_paths
        * The list of paths to exclude. These will not be copied from the stage
        * directory to the active directory, nor be deleted from the active
        * directory if they exist, when the stage directory is copied back into
        * the active directory.
        */
        public function __construct(StageBase $stage, PathListInterface $excluded_paths) {
        parent::__construct($stage);
        $this->excludedPaths = new ImmutablePathList($excluded_paths);
        }
        }
        <?php
        declare(strict_types=1);
        namespace Drupal\package_manager\Event;
        use Drupal\package_manager\ImmutablePathList;
        use Drupal\package_manager\StageBase;
        use PhpTuf\ComposerStager\API\Path\Value\PathListInterface;
        /**
        * Event fired before a stage directory is created.
        */
        final class PreCreateEvent extends PreOperationStageEvent {
        /**
        * The list of paths to exclude from the stage directory.
        *
        * @var \Drupal\package_manager\ImmutablePathList
        */
        public readonly ImmutablePathList $excludedPaths;
        /**
        * Constructs a PreCreateEvent object.
        *
        * @param \Drupal\package_manager\StageBase $stage
        * The stage which fired this event.
        * @param \PhpTuf\ComposerStager\API\Path\Value\PathListInterface $excluded_paths
        * The list of paths to exclude. These will not be copied into the stage
        * directory when it is created.
        */
        public function __construct(StageBase $stage, PathListInterface $excluded_paths) {
        parent::__construct($stage);
        $this->excludedPaths = new ImmutablePathList($excluded_paths);
        }
        }
        <?php
        declare(strict_types=1);
        namespace Drupal\package_manager\Event;
        use Drupal\Core\StringTranslation\TranslatableMarkup;
        use Drupal\package_manager\ValidationResult;
        use Drupal\system\SystemManager;
        /**
        * Base class for events dispatched before a stage life cycle operation.
        */
        abstract class PreOperationStageEvent extends StageEvent {
        /**
        * The validation results.
        *
        * @var \Drupal\package_manager\ValidationResult[]
        */
        protected $results = [];
        /**
        * Gets the validation results.
        *
        * @param int|null $severity
        * (optional) The severity for the results to return. Should be one of the
        * SystemManager::REQUIREMENT_* constants.
        *
        * @return \Drupal\package_manager\ValidationResult[]
        * The validation results.
        */
        public function getResults(?int $severity = NULL): array {
        if ($severity !== NULL) {
        return array_filter($this->results, function ($result) use ($severity) {
        return $result->severity === $severity;
        });
        }
        return $this->results;
        }
        /**
        * Convenience method to flag a validation error.
        *
        * @param \Drupal\Core\StringTranslation\TranslatableMarkup[] $messages
        * The error messages.
        * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $summary
        * The summary of error messages. Must be passed if there is more than one
        * message.
        */
        public function addError(array $messages, ?TranslatableMarkup $summary = NULL): void {
        $this->addResult(ValidationResult::createError(array_values($messages), $summary));
        }
        /**
        * Convenience method, adds an error validation result from a throwable.
        *
        * @param \Throwable $throwable
        * The throwable.
        * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $summary
        * (optional) The summary of error messages.
        */
        public function addErrorFromThrowable(\Throwable $throwable, ?TranslatableMarkup $summary = NULL): void {
        $this->addResult(ValidationResult::createErrorFromThrowable($throwable, $summary));
        }
        /**
        * Adds a validation result to the event.
        *
        * @param \Drupal\package_manager\ValidationResult $result
        * The validation result to add.
        *
        * @throws \InvalidArgumentException
        * Thrown if the validation result is not an error.
        */
        public function addResult(ValidationResult $result): void {
        // Only errors are allowed for this event.
        if ($result->severity !== SystemManager::REQUIREMENT_ERROR) {
        throw new \InvalidArgumentException('Only errors are allowed.');
        }
        $this->results[] = $result;
        }
        /**
        * {@inheritdoc}
        */
        public function stopPropagation(): void {
        if (empty($this->getResults(SystemManager::REQUIREMENT_ERROR))) {
        $this->addErrorFromThrowable(new \LogicException('Event propagation stopped without any errors added to the event. This bypasses the package_manager validation system.'));
        }
        parent::stopPropagation();
        }
        }
        0% Loading or .
        You are about to add 0 people to the discussion. Proceed with caution.
        Finish editing this message first!
        Please register or to comment