diff --git a/resources/phpcs.xml b/resources/phpcs.xml
new file mode 100644
index 00000000..5179500b
--- /dev/null
+++ b/resources/phpcs.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ src/
+
+ */src/Generated/*
+ */src/Orm/*/Base/
+ */src/Orm/*/Map/
+ */src/Orm/Propel/
+
+
+
+
diff --git a/src/Builder/FileNormalizer/CodeSnifferCompositeNormalizer.php b/src/Builder/FileNormalizer/CodeSnifferCompositeNormalizer.php
index 22618871..f45f9b86 100644
--- a/src/Builder/FileNormalizer/CodeSnifferCompositeNormalizer.php
+++ b/src/Builder/FileNormalizer/CodeSnifferCompositeNormalizer.php
@@ -9,6 +9,10 @@
namespace SprykerSdk\Integrator\Builder\FileNormalizer;
+use SprykerSdk\Integrator\Composer\ComposerLockReaderInterface;
+use SprykerSdk\Integrator\IntegratorConfig;
+use Symfony\Component\Filesystem\Filesystem;
+
class CodeSnifferCompositeNormalizer implements FileNormalizerInterface
{
/**
@@ -16,17 +20,62 @@ class CodeSnifferCompositeNormalizer implements FileNormalizerInterface
*/
public const ERROR_MESSAGE = 'Unable to execute code style fixer. Please manually execute it to adjust project code styles.';
+ /**
+ * @var string
+ */
+ public const SPRYKER_CS_PACKAGE = 'spryker/code-sniffer';
+
+ /**
+ * @var string
+ */
+ public const PHP_CS_FIXER_PATH = 'vendor/bin/phpcbf';
+
+ /**
+ * @var string
+ */
+ public const PHP_CS_FIXER_CONFIG_PATH = 'phpcs.xml';
+
+ /**
+ * @var string
+ */
+ protected const INTERNAL_PHP_CS_FIXER_CONFIG_PATH = 'resources/phpcs.xml';
+
/**
* @var array<\SprykerSdk\Integrator\Builder\FileNormalizer\FileNormalizerInterface>
*/
protected array $codeSniffNormalizers;
/**
- * @param array<\SprykerSdk\Integrator\Builder\FileNormalizer\FileNormalizerInterface> $codeSniffNormalizers
+ * @var \SprykerSdk\Integrator\Composer\ComposerLockReaderInterface
*/
- public function __construct(array $codeSniffNormalizers)
- {
+ protected ComposerLockReaderInterface $composerLockReader;
+
+ /**
+ * @var \SprykerSdk\Integrator\IntegratorConfig
+ */
+ protected IntegratorConfig $config;
+
+ /**
+ * @var \Symfony\Component\Filesystem\Filesystem
+ */
+ protected Filesystem $filesystem;
+
+ /**
+ * @param array $codeSniffNormalizers
+ * @param \SprykerSdk\Integrator\Composer\ComposerLockReaderInterface $composerLockReader
+ * @param \SprykerSdk\Integrator\IntegratorConfig $config
+ * @param \Symfony\Component\Filesystem\Filesystem $filesystem
+ */
+ public function __construct(
+ array $codeSniffNormalizers,
+ ComposerLockReaderInterface $composerLockReader,
+ IntegratorConfig $config,
+ Filesystem $filesystem
+ ) {
$this->codeSniffNormalizers = $codeSniffNormalizers;
+ $this->composerLockReader = $composerLockReader;
+ $this->config = $config;
+ $this->filesystem = $filesystem;
}
/**
@@ -34,7 +83,7 @@ public function __construct(array $codeSniffNormalizers)
*/
public function isApplicable(): bool
{
- return count(array_filter($this->codeSniffNormalizers, static fn (FileNormalizerInterface $normalizer): bool => $normalizer->isApplicable())) > 0;
+ return true;
}
/**
@@ -51,6 +100,28 @@ public function getErrorMessage(): ?string
* @return void
*/
public function normalize(array $filePaths): void
+ {
+ if (!$this->isPhpCsConfigMissed()) {
+ $this->executeNormalizers($filePaths);
+
+ return;
+ }
+
+ $this->filesystem->copy(static::getInitialCsFixerConfig(), $this->getProjectCSFixerConfigPath());
+
+ try {
+ $this->executeNormalizers($filePaths);
+ } finally {
+ $this->filesystem->remove($this->getProjectCSFixerConfigPath());
+ }
+ }
+
+ /**
+ * @param array $filePaths
+ *
+ * @return void
+ */
+ protected function executeNormalizers(array $filePaths): void
{
foreach ($this->codeSniffNormalizers as $codeSniffNormalizer) {
if (!$codeSniffNormalizer->isApplicable()) {
@@ -60,4 +131,38 @@ public function normalize(array $filePaths): void
$codeSniffNormalizer->normalize($filePaths);
}
}
+
+ /**
+ * @return bool
+ */
+ protected function isPhpCsConfigMissed(): bool
+ {
+ return $this->composerLockReader->getPackageData(static::SPRYKER_CS_PACKAGE) !== null
+ && $this->filesystem->exists($this->getProjectCSFixerPath())
+ && !$this->filesystem->exists($this->getProjectCSFixerConfigPath());
+ }
+
+ /**
+ * @return string
+ */
+ protected function getProjectCSFixerPath(): string
+ {
+ return $this->config->getProjectRootDirectory() . static::PHP_CS_FIXER_PATH;
+ }
+
+ /**
+ * @return string
+ */
+ protected function getProjectCSFixerConfigPath(): string
+ {
+ return $this->config->getProjectRootDirectory() . static::PHP_CS_FIXER_CONFIG_PATH;
+ }
+
+ /**
+ * @return string
+ */
+ public static function getInitialCsFixerConfig(): string
+ {
+ return dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . static::INTERNAL_PHP_CS_FIXER_CONFIG_PATH;
+ }
}
diff --git a/src/Composer/ComposerLockReader.php b/src/Composer/ComposerLockReader.php
index aef8837b..ae13abf4 100644
--- a/src/Composer/ComposerLockReader.php
+++ b/src/Composer/ComposerLockReader.php
@@ -78,6 +78,30 @@ public function getModuleVersions(): array
return $packages;
}
+ /**
+ * @param string $packageName
+ *
+ * @return array|null
+ */
+ public function getPackageData(string $packageName): ?array
+ {
+ $composerLockData = $this->getProjectComposerLockData();
+
+ foreach (static::GROUP_PACKAGES as $packagesKey) {
+ if (!isset($composerLockData[$packagesKey])) {
+ continue;
+ }
+
+ foreach ($composerLockData[$packagesKey] as $packageData) {
+ if ($packageData['name'] === $packageName) {
+ return $packageData;
+ }
+ }
+ }
+
+ return null;
+ }
+
/**
* @param array $packageData
*
diff --git a/src/Composer/ComposerLockReaderInterface.php b/src/Composer/ComposerLockReaderInterface.php
index 675ad1a1..88dcaf2a 100644
--- a/src/Composer/ComposerLockReaderInterface.php
+++ b/src/Composer/ComposerLockReaderInterface.php
@@ -15,4 +15,11 @@ interface ComposerLockReaderInterface
* @return array
*/
public function getModuleVersions(): array;
+
+ /**
+ * @param string $packageName
+ *
+ * @return array|null
+ */
+ public function getPackageData(string $packageName): ?array;
}
diff --git a/src/IntegratorFactory.php b/src/IntegratorFactory.php
index 5c0a556b..ee79e34e 100644
--- a/src/IntegratorFactory.php
+++ b/src/IntegratorFactory.php
@@ -509,7 +509,7 @@ public function createWireTransferManifestStrategy(): ManifestStrategyInterface
{
return new WireTransferManifestStrategy(
$this->getConfig(),
- new Filesystem(),
+ $this->createFilesystem(),
);
}
@@ -520,7 +520,7 @@ public function createWireSchemaManifestStrategy(): ManifestStrategyInterface
{
return new WireSchemaManifestStrategy(
$this->getConfig(),
- new Filesystem(),
+ $this->createFilesystem(),
);
}
@@ -563,6 +563,9 @@ public function createCodeSnifferCompositeNormalizer(): FileNormalizerInterface
$this->createPhpCSFixerNormalizer(),
$this->createCodeSniffStyleFileNormalizer(),
],
+ $this->createComposerLockReader(),
+ $this->getConfig(),
+ $this->createFilesystem(),
);
}
@@ -1165,6 +1168,14 @@ protected function createManifestToModulesRatingRequestMapper(): ManifestToModul
return new ManifestToModulesRatingRequestMapper();
}
+ /**
+ * @return \Symfony\Component\Filesystem\Filesystem
+ */
+ protected function createFilesystem(): Filesystem
+ {
+ return new Filesystem();
+ }
+
/**
* @return \SprykerSdk\Integrator\Builder\Finder\ClassConstantFinderInterface
*/
diff --git a/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSnifferCompositeNormalizerTest.php b/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSnifferCompositeNormalizerTest.php
index 17f17a51..edd6b039 100644
--- a/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSnifferCompositeNormalizerTest.php
+++ b/tests/SprykerSdkTest/Integrator/Builder/FileNormalizer/CodeSnifferCompositeNormalizerTest.php
@@ -9,22 +9,31 @@
namespace SprykerSdkTest\Integrator\Builder\FileNormalizer;
+use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use SprykerSdk\Integrator\Builder\FileNormalizer\CodeSnifferCompositeNormalizer;
use SprykerSdk\Integrator\Builder\FileNormalizer\FileNormalizerInterface;
+use SprykerSdk\Integrator\Composer\ComposerLockReaderInterface;
+use SprykerSdk\Integrator\IntegratorConfig;
+use Symfony\Component\Filesystem\Filesystem;
class CodeSnifferCompositeNormalizerTest extends TestCase
{
/**
* @return void
*/
- public function testIsApplicableShouldReturnTrueWhenOneOfNormalizerIsApplicable(): void
+ public function testIsApplicableShouldReturnAlwaysTrue(): void
{
// Arrange
$normalizerOne = $this->createNormalizerMock(false, false);
$normalizerTwo = $this->createNormalizerMock(true, false);
- $codeSnifferCompositeNormalizer = new CodeSnifferCompositeNormalizer([$normalizerOne, $normalizerTwo]);
+ $codeSnifferCompositeNormalizer = new CodeSnifferCompositeNormalizer(
+ [$normalizerOne, $normalizerTwo],
+ $this->createComposerLockReaderMock(),
+ $this->createIntegratorConfigMock(),
+ $this->createFilesystemMock(),
+ );
// Act
$result = $codeSnifferCompositeNormalizer->isApplicable();
@@ -36,31 +45,94 @@ public function testIsApplicableShouldReturnTrueWhenOneOfNormalizerIsApplicable(
/**
* @return void
*/
- public function testIsApplicableShouldReturnFalseWhenAllNormalizerNotApplicable(): void
+ public function testNormalizeShouldCallApplicableNormalizer(): void
{
- // Arrange
+ // Arrange & Assert
$normalizerOne = $this->createNormalizerMock(false, false);
- $normalizerTwo = $this->createNormalizerMock(false, false);
+ $normalizerTwo = $this->createNormalizerMock(true, true);
- $codeSnifferCompositeNormalizer = new CodeSnifferCompositeNormalizer([$normalizerOne, $normalizerTwo]);
+ $codeSnifferCompositeNormalizer = new CodeSnifferCompositeNormalizer(
+ [$normalizerOne, $normalizerTwo],
+ $this->createComposerLockReaderMock(),
+ $this->createIntegratorConfigMock(),
+ $this->createFilesystemMock(),
+ );
// Act
- $result = $codeSnifferCompositeNormalizer->isApplicable();
+ $codeSnifferCompositeNormalizer->normalize([]);
+ }
+
+ /**
+ * @return void
+ */
+ public function testGetErrorMessageShouldReturnErrorMessage(): void
+ {
+ // Arrange
+ $codeSnifferCompositeNormalizer = new CodeSnifferCompositeNormalizer(
+ [$this->createMock(FileNormalizerInterface::class)],
+ $this->createComposerLockReaderMock(),
+ $this->createIntegratorConfigMock(),
+ $this->createFilesystemMock(),
+ );
+
+ // Act
+ $errorMessage = $codeSnifferCompositeNormalizer->getErrorMessage();
// Assert
- $this->assertFalse($result);
+ $this->assertSame(CodeSnifferCompositeNormalizer::ERROR_MESSAGE, $errorMessage);
+ }
+
+ /**
+ * @dataProvider missedPhpConfigConditionsDataProvider
+ *
+ * @param bool $isSprykerPackageInstalled
+ * @param bool $isCsFixerExecutableFound
+ * @param bool $isCsFixerConfigFound
+ *
+ * @return void
+ */
+ public function testNormalizeShouldSkipConfigCoppingWhenConditionFalse(
+ bool $isSprykerPackageInstalled,
+ bool $isCsFixerExecutableFound,
+ bool $isCsFixerConfigFound
+ ): void {
+ // Arrange & Assert
+ $codeSnifferCompositeNormalizer = new CodeSnifferCompositeNormalizer(
+ [$this->createNormalizerMock(true, true)],
+ $this->createComposerLockReaderMock($isSprykerPackageInstalled),
+ $this->createIntegratorConfigMock(),
+ $this->createFilesystemMock($isCsFixerExecutableFound, $isCsFixerConfigFound),
+ );
+
+ // Act
+ $codeSnifferCompositeNormalizer->normalize([]);
+ }
+
+ /**
+ * @return array>
+ */
+ public function missedPhpConfigConditionsDataProvider(): array
+ {
+ return [
+ [false, true, false],
+ [true, false, false],
+ [true, true, true],
+ ];
}
/**
* @return void
*/
- public function testIsApplicableShouldNormalizeApplicableNormalizer(): void
+ public function testNormalizeShouldAddAndRemoveMissedPhpcsConfigAndCallApplicableNormalizer(): void
{
// Arrange & Assert
- $normalizerOne = $this->createNormalizerMock(false, false);
- $normalizerTwo = $this->createNormalizerMock(true, true);
- $codeSnifferCompositeNormalizer = new CodeSnifferCompositeNormalizer([$normalizerOne, $normalizerTwo]);
+ $codeSnifferCompositeNormalizer = new CodeSnifferCompositeNormalizer(
+ [$this->createNormalizerMock(true, true)],
+ $this->createComposerLockReaderMock(true),
+ $this->createIntegratorConfigMock(),
+ $this->createFilesystemMock(true, false, true),
+ );
// Act
$codeSnifferCompositeNormalizer->normalize([]);
@@ -69,16 +141,24 @@ public function testIsApplicableShouldNormalizeApplicableNormalizer(): void
/**
* @return void
*/
- public function testGetErrorMessageShouldReturnErrorMessage(): void
+ public function testNormalizeShouldRemoveMissedPhpcsConfigWhenExceptionThrown(): void
{
- // Arrange
- $codeSnifferCompositeNormalizer = new CodeSnifferCompositeNormalizer([$this->createMock(FileNormalizerInterface::class)]);
+ // Arrange & Assert
+ $this->expectException(InvalidArgumentException::class);
- // Act
- $errorMessage = $codeSnifferCompositeNormalizer->getErrorMessage();
+ $fileNormalizersMock = $this->createMock(FileNormalizerInterface::class);
+ $fileNormalizersMock->method('isApplicable')->willReturn(true);
+ $fileNormalizersMock->method('normalize')->willThrowException(new InvalidArgumentException(''));
- // Assert
- $this->assertSame(CodeSnifferCompositeNormalizer::ERROR_MESSAGE, $errorMessage);
+ $codeSnifferCompositeNormalizer = new CodeSnifferCompositeNormalizer(
+ [$fileNormalizersMock],
+ $this->createComposerLockReaderMock(true),
+ $this->createIntegratorConfigMock(),
+ $this->createFilesystemMock(true, false, true),
+ );
+
+ // Act
+ $codeSnifferCompositeNormalizer->normalize([]);
}
/**
@@ -99,4 +179,63 @@ protected function createNormalizerMock(bool $isApplicable, bool $shouldCall, ?s
return $fileNormalizersMock;
}
+
+ /**
+ * @param bool $isSprykerPackageInstalled
+ *
+ * @return \SprykerSdk\Integrator\Composer\ComposerLockReaderInterface
+ */
+ protected function createComposerLockReaderMock(bool $isSprykerPackageInstalled = false): ComposerLockReaderInterface
+ {
+ $composerLockReader = $this->createMock(ComposerLockReaderInterface::class);
+ $composerLockReader->method('getPackageData')->willReturn(
+ $isSprykerPackageInstalled ? ['name' => CodeSnifferCompositeNormalizer::SPRYKER_CS_PACKAGE] : null,
+ );
+
+ return $composerLockReader;
+ }
+
+ /**
+ * @param bool $isCsFixerExecutableFound
+ * @param bool $isCsFixerConfigFound
+ * @param bool $shouldInvokeConfigCopping
+ *
+ * @return \Symfony\Component\Filesystem\Filesystem
+ */
+ protected function createFilesystemMock(
+ bool $isCsFixerExecutableFound = false,
+ bool $isCsFixerConfigFound = true,
+ bool $shouldInvokeConfigCopping = false
+ ): Filesystem {
+ $filesystem = $this->createMock(Filesystem::class);
+
+ $filesystem->method('exists')
+ ->willReturnMap([
+ [CodeSnifferCompositeNormalizer::PHP_CS_FIXER_PATH, $isCsFixerExecutableFound],
+ [CodeSnifferCompositeNormalizer::PHP_CS_FIXER_CONFIG_PATH, $isCsFixerConfigFound],
+ ]);
+
+ $filesystem
+ ->expects($shouldInvokeConfigCopping ? $this->once() : $this->never())
+ ->method('copy')
+ ->with(CodeSnifferCompositeNormalizer::getInitialCsFixerConfig());
+
+ $filesystem
+ ->expects($shouldInvokeConfigCopping ? $this->once() : $this->never())
+ ->method('remove')
+ ->with(CodeSnifferCompositeNormalizer::PHP_CS_FIXER_CONFIG_PATH);
+
+ return $filesystem;
+ }
+
+ /**
+ * @return \SprykerSdk\Integrator\IntegratorConfig
+ */
+ protected function createIntegratorConfigMock(): IntegratorConfig
+ {
+ $integratorConfig = $this->createMock(IntegratorConfig::class);
+ $integratorConfig->method('getProjectRootDirectory')->willReturn('');
+
+ return $integratorConfig;
+ }
}