class AllowedPackages

Same name in other branches
  1. 8.9.x composer/Plugin/Scaffold/AllowedPackages.php \Drupal\Composer\Plugin\Scaffold\AllowedPackages
  2. 10 composer/Plugin/Scaffold/AllowedPackages.php \Drupal\Composer\Plugin\Scaffold\AllowedPackages
  3. 11.x composer/Plugin/Scaffold/AllowedPackages.php \Drupal\Composer\Plugin\Scaffold\AllowedPackages

Determine recursively which packages have been allowed to scaffold files.

If the root-level composer.json allows drupal/core, and drupal/core allows drupal/assets, then the later package will also implicitly be allowed.

@internal

Hierarchy

Expanded class hierarchy of AllowedPackages

File

composer/Plugin/Scaffold/AllowedPackages.php, line 19

Namespace

Drupal\Composer\Plugin\Scaffold
View source
class AllowedPackages implements PostPackageEventListenerInterface {
    
    /**
     * The Composer service.
     *
     * @var \Composer\Composer
     */
    protected $composer;
    
    /**
     * Composer's I/O service.
     *
     * @var \Composer\IO\IOInterface
     */
    protected $io;
    
    /**
     * Manager of the options in the top-level composer.json's 'extra' section.
     *
     * @var \Drupal\Composer\Plugin\Scaffold\ManageOptions
     */
    protected $manageOptions;
    
    /**
     * The list of new packages added by this Composer command.
     *
     * @var array
     */
    protected $newPackages = [];
    
    /**
     * AllowedPackages constructor.
     *
     * @param \Composer\Composer $composer
     *   The composer object.
     * @param \Composer\IO\IOInterface $io
     *   IOInterface to write to.
     * @param \Drupal\Composer\Plugin\Scaffold\ManageOptions $manage_options
     *   Manager of the options in the top-level composer.json's 'extra' section.
     */
    public function __construct(Composer $composer, IOInterface $io, ManageOptions $manage_options) {
        $this->composer = $composer;
        $this->io = $io;
        $this->manageOptions = $manage_options;
    }
    
    /**
     * Gets a list of all packages that are allowed to copy scaffold files.
     *
     * We will implicitly allow the projects 'drupal/legacy-scaffold-assets'
     * and 'drupal/core' to scaffold files, if they are present. Any other
     * project must be explicitly whitelisted in the top-level composer.json
     * file in order to be allowed to override scaffold files.
     * Configuration for packages specified later will override configuration
     * specified by packages listed earlier. In other words, the last listed
     * package has the highest priority. The root package will always be returned
     * at the end of the list.
     *
     * @return \Composer\Package\PackageInterface[]
     *   An array of allowed Composer packages.
     */
    public function getAllowedPackages() {
        $top_level_packages = $this->getTopLevelAllowedPackages();
        $allowed_packages = $this->recursiveGetAllowedPackages($top_level_packages);
        // If the root package defines any file mappings, then implicitly add it
        // to the list of allowed packages. Add it at the end so that it overrides
        // all the preceding packages.
        if ($this->manageOptions
            ->getOptions()
            ->hasFileMapping()) {
            $root_package = $this->composer
                ->getPackage();
            unset($allowed_packages[$root_package->getName()]);
            $allowed_packages[$root_package->getName()] = $root_package;
        }
        // Handle any newly-added packages that are not already allowed.
        return $this->evaluateNewPackages($allowed_packages);
    }
    
    /**
     * {@inheritdoc}
     */
    public function event(PackageEvent $event) {
        $operation = $event->getOperation();
        // Determine the package. Later, in evaluateNewPackages(), we will report
        // which of the newly-installed packages have scaffold operations, and
        // whether or not they are allowed to scaffold by the allowed-packages
        // option in the root-level composer.json file.
        $operationType = $this->getOperationType($operation);
        $package = $operationType === 'update' ? $operation->getTargetPackage() : $operation->getPackage();
        if (ScaffoldOptions::hasOptions($package->getExtra())) {
            $this->newPackages[$package->getName()] = $package;
        }
    }
    
    /**
     * Gets all packages that are allowed in the top-level composer.json.
     *
     * We will implicitly allow the projects 'drupal/legacy-scaffold-assets'
     * and 'drupal/core' to scaffold files, if they are present. Any other
     * project must be explicitly whitelisted in the top-level composer.json
     * file in order to be allowed to override scaffold files.
     *
     * @return array
     *   An array of allowed Composer package names.
     */
    protected function getTopLevelAllowedPackages() {
        $implicit_packages = [
            'drupal/legacy-scaffold-assets',
            'drupal/core',
        ];
        $top_level_packages = $this->manageOptions
            ->getOptions()
            ->allowedPackages();
        return array_merge($implicit_packages, $top_level_packages);
    }
    
    /**
     * Builds a name-to-package mapping from a list of package names.
     *
     * @param string[] $packages_to_allow
     *   List of package names to allow.
     * @param array $allowed_packages
     *   Mapping of package names to PackageInterface of packages already
     *   accumulated.
     *
     * @return \Composer\Package\PackageInterface[]
     *   Mapping of package names to PackageInterface in priority order.
     */
    protected function recursiveGetAllowedPackages(array $packages_to_allow, array $allowed_packages = []) {
        foreach ($packages_to_allow as $name) {
            $package = $this->getPackage($name);
            if ($package instanceof PackageInterface && !isset($allowed_packages[$name])) {
                $allowed_packages[$name] = $package;
                $package_options = $this->manageOptions
                    ->packageOptions($package);
                $allowed_packages = $this->recursiveGetAllowedPackages($package_options->allowedPackages(), $allowed_packages);
            }
        }
        return $allowed_packages;
    }
    
    /**
     * Evaluates newly-added packages and see if they are already allowed.
     *
     * For now we will only emit warnings if they are not.
     *
     * @param array $allowed_packages
     *   Mapping of package names to PackageInterface of packages already
     *   accumulated.
     *
     * @return \Composer\Package\PackageInterface[]
     *   Mapping of package names to PackageInterface in priority order.
     */
    protected function evaluateNewPackages(array $allowed_packages) {
        foreach ($this->newPackages as $name => $newPackage) {
            if (!array_key_exists($name, $allowed_packages)) {
                $this->io
                    ->write("Not scaffolding files for <comment>{$name}</comment>, because it is not listed in the element 'extra.drupal-scaffold.allowed-packages' in the root-level composer.json file.");
            }
            else {
                $this->io
                    ->write("Package <comment>{$name}</comment> has scaffold operations, and is already allowed in the root-level composer.json file.");
            }
        }
        // @todo We could prompt the user and ask if they wish to allow a
        // newly-added package. This might be useful if, for example, the user
        // might wish to require an installation profile that contains scaffolded
        // assets. For more information, see:
        // https://www.drupal.org/project/drupal/issues/3064990
        return $allowed_packages;
    }
    
    /**
     * Determine the type of the provided operation.
     *
     * Adjusts API used for Composer 1 or Composer 2.
     *
     * @param \Composer\DependencyResolver\Operation\OperationInterface $operation
     *   The operation object.
     *
     * @return string
     *   The operation type.
     */
    protected function getOperationType(OperationInterface $operation) {
        // Use Composer 2 method.
        if (method_exists($operation, 'getOperationType')) {
            return $operation->getOperationType();
        }
        // Fallback to Composer 1 method.
        return $operation->getJobType();
    }
    
    /**
     * Retrieves a package from the current composer process.
     *
     * @param string $name
     *   Name of the package to get from the current composer installation.
     *
     * @return \Composer\Package\PackageInterface|null
     *   The Composer package.
     */
    protected function getPackage($name) {
        return $this->composer
            ->getRepositoryManager()
            ->getLocalRepository()
            ->findPackage($name, '*');
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
AllowedPackages::$composer protected property The Composer service.
AllowedPackages::$io protected property Composer&#039;s I/O service.
AllowedPackages::$manageOptions protected property Manager of the options in the top-level composer.json&#039;s &#039;extra&#039; section.
AllowedPackages::$newPackages protected property The list of new packages added by this Composer command.
AllowedPackages::evaluateNewPackages protected function Evaluates newly-added packages and see if they are already allowed.
AllowedPackages::event public function Handles package events during a &#039;composer require&#039; operation. Overrides PostPackageEventListenerInterface::event
AllowedPackages::getAllowedPackages public function Gets a list of all packages that are allowed to copy scaffold files.
AllowedPackages::getOperationType protected function Determine the type of the provided operation.
AllowedPackages::getPackage protected function Retrieves a package from the current composer process.
AllowedPackages::getTopLevelAllowedPackages protected function Gets all packages that are allowed in the top-level composer.json.
AllowedPackages::recursiveGetAllowedPackages protected function Builds a name-to-package mapping from a list of package names.
AllowedPackages::__construct public function AllowedPackages constructor.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.