eliashaeussler/composer-update-check

View on GitHub
src/Package/UpdateCheckResult.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

declare(strict_types=1);

namespace EliasHaeussler\ComposerUpdateCheck\Package;

/*
 * This file is part of the Composer package "eliashaeussler/composer-update-check".
 *
 * Copyright (C) 2020 Elias Häußler <elias@haeussler.dev>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

/**
 * UpdateCheckResult.
 *
 * @author Elias Häußler <elias@haeussler.dev>
 * @license GPL-3.0-or-later
 */
class UpdateCheckResult
{
    protected const COMMAND_OUTPUT_PATTERN =
        '#^'.
            '\\s*- Upgrading '.
            '(?P<name>\\S+) \\('.
                '(?P<outdated>dev-\\S+ \\S+|(?!dev-)\\S+)'.
                ' => '.
                '(?P<new>dev-\S+ \S+|(?!dev-)\S+)'.
            '\\)'.
        '$#';
    protected const LEGACY_COMMAND_OUTPUT_PATTERN =
        '#^'.
            '\\s*- Updating '.
            '(?P<name>dev-\\S+ \\S+|(?!dev-)\\S+) \\('.
                '(?P<outdated>dev-\S+ \S+|(?!dev-)\S+)'.
            '\\) to \\S+ \\('.
                '(?P<new>dev-\S+ \S+|(?!dev-)\S+)'.
            '\\)'.
        '$#';

    /**
     * @var OutdatedPackage[]
     */
    private $outdatedPackages;

    /**
     * @param OutdatedPackage[] $outdatedPackages
     */
    public function __construct(array $outdatedPackages)
    {
        $this->outdatedPackages = $outdatedPackages;
        $this->validateOutdatedPackages();
        $this->sortPackages($this->outdatedPackages);
    }

    /**
     * @return OutdatedPackage[]
     */
    public function getOutdatedPackages(): array
    {
        return $this->outdatedPackages;
    }

    /**
     * @param string[] $allowedPackages
     */
    public static function fromCommandOutput(string $output, array $allowedPackages): self
    {
        $outputParts = explode(PHP_EOL, $output);
        $packages = array_unique(
            array_filter(
                array_map([static::class, 'parseCommandOutput'], $outputParts),
                function (?OutdatedPackage $outdatedPackage) use ($allowedPackages) {
                    if (null === $outdatedPackage) {
                        return false;
                    }

                    return in_array($outdatedPackage->getName(), $allowedPackages, true);
                }
            ),
            SORT_REGULAR
        );

        return new self($packages);
    }

    public static function parseCommandOutput(string $output): ?OutdatedPackage
    {
        if (
            !preg_match(static::COMMAND_OUTPUT_PATTERN, $output, $matches) &&
            !preg_match(static::LEGACY_COMMAND_OUTPUT_PATTERN, $output, $matches)
        ) {
            return null;
        }
        $packageName = $matches['name'];
        $outdatedVersion = $matches['outdated'];
        $newVersion = $matches['new'];

        return new OutdatedPackage($packageName, $outdatedVersion, $newVersion);
    }

    /**
     * @param OutdatedPackage[] $outdatedPackages
     */
    private function sortPackages(array &$outdatedPackages): void
    {
        usort($outdatedPackages, function (OutdatedPackage $a, OutdatedPackage $b) {
            return strcmp($a->getName(), $b->getName());
        });
    }

    private function validateOutdatedPackages(): void
    {
        foreach ($this->outdatedPackages as $key => $outdatedPackage) {
            if (!($outdatedPackage instanceof OutdatedPackage)) {
                throw new \InvalidArgumentException(sprintf('Outdated package #%s must be an instance of "%s".', $key, OutdatedPackage::class), 1600276584);
            }
        }
    }
}