InstalledPackage.php

Namespace

Drupal\package_manager

File

core/modules/package_manager/src/InstalledPackage.php

View source
<?php

declare (strict_types=1);
namespace Drupal\package_manager;

use Drupal\Component\Serialization\Yaml;

/**
 * A value object that represents an installed Composer package.
 */
final class InstalledPackage {
    
    /**
     * Constructs an InstalledPackage object.
     *
     * @param string $name
     *   The package name.
     * @param string $version
     *   The package version.
     * @param string|null $path
     *   The package path, or NULL if the package type is `metapackage`.
     * @param string $type
     *   The package type.
     */
    private function __construct(string $name, string $version, ?string $path, string $type) {
    }
    
    /**
     * Create an installed package object from an array.
     *
     * @param array $data
     *   The package data.
     *
     * @return static
     */
    public static function createFromArray(array $data) : static {
        $path = isset($data['path']) ? realpath($data['path']) : NULL;
        // Fall back to `library`.
        // @see https://getcomposer.org/doc/04-schema.md#type
        $type = $data['type'] ?? 'library';
        assert(($type === 'metapackage') === is_null($path), 'Metapackage install path must be NULL.');
        return new static($data['name'], $data['version'], $path, $type);
    }
    
    /**
     * Returns the Drupal project name for this package.
     *
     * This assumes that drupal.org adds a `project` key to every `.info.yml` file
     * in the package, regardless of where they are in the package's directory
     * structure. The package name is irrelevant except for checking that the
     * vendor is `drupal`. For example, if the project key in the info file were
     * `my_module`, and the package name were `drupal/whatever`, and this method
     * would return `my_module`.
     *
     * @return string|null
     *   The name of the Drupal project installed by this package, or NULL if:
     *   - The package type is not one of `drupal-module`, `drupal-theme`, or
     *     `drupal-profile`.
     *   - The package's vendor is not `drupal`.
     *   - The project name could not otherwise be determined.
     *
     * @throws \UnexpectedValueException
     *   Thrown if the same project name exists in more than one package.
     */
    public function getProjectName() : ?string {
        // Only consider packages which are packaged by drupal.org and will be
        // known to the core Update module.
        $drupal_package_types = [
            'drupal-module',
            'drupal-theme',
            'drupal-profile',
        ];
        if ($this->path && str_starts_with($this->name, 'drupal/') && in_array($this->type, $drupal_package_types, TRUE)) {
            return $this->scanForProjectName();
        }
        return NULL;
    }
    
    /**
     * Scans a given path to determine the Drupal project name.
     *
     * The path will be scanned recursively for `.info.yml` files containing a
     * `project` key.
     *
     * @return string|null
     *   The name of the project, as declared in the first found `.info.yml` which
     *   contains a `project` key, or NULL if none was found.
     */
    private function scanForProjectName() : ?string {
        $iterator = new \RecursiveDirectoryIterator($this->path);
        $iterator = new \RecursiveIteratorIterator($iterator);
        $iterator = new \RegexIterator($iterator, '/.+\\.info\\.yml$/', \RegexIterator::GET_MATCH);
        foreach ($iterator as $match) {
            $info = file_get_contents($match[0]);
            $info = Yaml::decode($info);
            if (!empty($info['project'])) {
                return $info['project'];
            }
        }
        return NULL;
    }

}

Classes

Title Deprecated Summary
InstalledPackage A value object that represents an installed Composer package.

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