Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: Implement PackageHashNormalizer #6

Merged
merged 1 commit into from
Jan 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ This package makes use of the following normalizers provided by [`localheinz/jso
Additionally, it provides and makes use of the following normalizers:

* [`Localheinz\Composer\Normalize\Normalizer\ConfigHashNormalizer`](#confighashnormalizer)
* [`Localheinz\Composer\Normalize\Normalizer\PackageHashNormalizer`](#packagehashnormalizer)

### `ConfigHashNormalizer`

Expand All @@ -51,6 +52,24 @@ the `ConfigHashNormalizer` will sort the `config` section by key in ascending or

:bulb: Find out more about the `config` section at https://getcomposer.org/doc/06-config.md.

### `PackageHashNormalizer`

If `composer.json` contains any configuration in the

* `conflict`
* `provide`
* `replaces`
* `require`
* `require-dev`
* `suggest`

sections, the `PackageHashNormalizer` will sort the content of these sections.

:bulb: This transfers the behaviour from using the `--sort-packages` or
`sort-packages` configuration flag to other sections. Find out more about
the `--sort-packages` flag and configuration at https://getcomposer.org/doc/06-config.md#sort-packages
and https://getcomposer.org/doc/03-cli.md#require.

## Contributing

Please have a look at [`CONTRIBUTING.md`](.github/CONTRIBUTING.md).
Expand All @@ -62,3 +81,11 @@ Please have a look at [`CODE_OF_CONDUCT.md`](.github/CODE_OF_CONDUCT.md).
## License

This package is licensed using the MIT License.

## Credits

The algorithm for sorting packages in the [`PackageHashNormalizer`](src/Normalizer/PackageHashNormalizer.php) has
been adopted from [`Composer\Json\JsonManipulator::sortPackages()`](https://github.com/composer/composer/blob/1.6.2/src/Composer/Json/JsonManipulator.php#L110-L146)
(originally licensed under MIT by [Nils Adermann](https://github.com/naderman) and [Jordi Boggiano](https://github.com/seldaek)),
which I initially contributed to `composer/composer` with [`composer/composer#3549`](https://github.com/composer/composer/pull/3549)
and [`composer/composer#3872`](https://github.com/composer/composer/pull/3872).
3 changes: 2 additions & 1 deletion src/NormalizePlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ public function getCommands(): array
{
return [
new Command\NormalizeCommand(new AutoFormatNormalizer(new ChainNormalizer(
new Normalizer\ConfigHashNormalizer()
new Normalizer\ConfigHashNormalizer(),
new Normalizer\PackageHashNormalizer()
))),
];
}
Expand Down
102 changes: 102 additions & 0 deletions src/Normalizer/PackageHashNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

declare(strict_types=1);

/**
* Copyright (c) 2018 Andreas Möller.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://github.com/localheinz/composer-normalize
*/

namespace Localheinz\Composer\Normalize\Normalizer;

use Composer\Repository;
use Localheinz\Json\Normalizer\NormalizerInterface;

final class PackageHashNormalizer implements NormalizerInterface
{
/**
* @var string[]
*/
private static $properties = [
'conflict',
'provide',
'replaces',
'require',
'require-dev',
'suggest',
];

public function normalize(string $json): string
{
$decoded = \json_decode($json);

if (null === $decoded && JSON_ERROR_NONE !== \json_last_error()) {
throw new \InvalidArgumentException(\sprintf(
'"%s" is not valid JSON.',
$json
));
}

$objectProperties = \array_intersect_key(
\get_object_vars($decoded),
\array_flip(self::$properties)
);

if (!\count($objectProperties)) {
return $json;
}

foreach ($objectProperties as $name => $value) {
$decoded->{$name} = $this->sortPackages((array) $decoded->{$name});
}

return \json_encode($decoded);
}

/**
* This code is adopted from composer/composer (originally licensed under MIT by Nils Adermann <naderman@naderman.de>
* and Jordi Boggiano <j.boggiano@seld.be>).
*
* @see https://github.com/composer/composer/blob/1.6.2/src/Composer/Json/JsonManipulator.php#L110-L146
*
* @param string[] $packages
*
* @return string[]
*/
private function sortPackages(array $packages): array
{
$prefix = function ($requirement) {
if (\preg_match(Repository\PlatformRepository::PLATFORM_PACKAGE_REGEX, $requirement)) {
return \preg_replace(
[
'/^php/',
'/^hhvm/',
'/^ext/',
'/^lib/',
'/^\D/',
],
[
'0-$0',
'1-$0',
'2-$0',
'3-$0',
'4-$0',
],
$requirement
);
}

return '5-' . $requirement;
};

\uksort($packages, function ($a, $b) use ($prefix) {
return \strnatcmp($prefix($a), $prefix($b));
});

return $packages;
}
}
99 changes: 99 additions & 0 deletions test/Unit/Normalizer/PackageHashNormalizerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

declare(strict_types=1);

/**
* Copyright (c) 2018 Andreas Möller.
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*
* @see https://github.com/localheinz/composer-normalize
*/

namespace Localheinz\Composer\Normalize\Test\Unit\Normalizer;

use Localheinz\Composer\Normalize\Normalizer\PackageHashNormalizer;

final class PackageHashNormalizerTest extends AbstractNormalizerTestCase
{
public function testNormalizeDoesNotModifyOtherProperty()
{
$json = <<<'JSON'
{
"foo": {
"qux": "quux",
"bar": "baz"
}
}
JSON;

$normalizer = new PackageHashNormalizer();

$this->assertSame($json, $normalizer->normalize($json));
}

/**
* @dataProvider providerProperty
*
* @param string $property
*/
public function testNormalizeSortsPackageHashIfPropertyExists(string $property)
{
$json = <<<JSON
{
"${property}": {
"localheinz/test-util": "Provides utilities for tests.",
"hhvm": "Okay",
"lib-baz": "Maybe it helps.",
"localheinz/php-cs-fixer-config": "Provides a configuration factory and multiple rule sets for friendsofphp/php-cs-fixer.",
"ext-foo": "Could be useful",
"php": "Because why not, it's great."
},
"foo": {
"qux": "quux",
"bar": "baz"
}
}
JSON;

$normalized = <<<JSON
{
"${property}": {
"php": "Because why not, it's great.",
"hhvm": "Okay",
"ext-foo": "Could be useful",
"lib-baz": "Maybe it helps.",
"localheinz/php-cs-fixer-config": "Provides a configuration factory and multiple rule sets for friendsofphp/php-cs-fixer.",
"localheinz/test-util": "Provides utilities for tests."
},
"foo": {
"qux": "quux",
"bar": "baz"
}
}
JSON;

$normalizer = new PackageHashNormalizer();

$this->assertSame(\json_encode(\json_decode($normalized)), $normalizer->normalize($json));
}

public function providerProperty(): \Generator
{
$values = [
'conflict',
'provide',
'replaces',
'require',
'require-dev',
'suggest',
];

foreach ($values as $value) {
yield $value => [
$value,
];
}
}
}