From f8f7c66f96f86c3c48b2bd653fd206c678b0c952 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Fri, 15 Dec 2023 23:30:32 +0100 Subject: [PATCH] misc --- .github/workflows/code_analysis.yaml | 4 - .gitignore | 3 + bin/config-transformer.php | 9 +- composer.json | 6 +- config/config.php | 22 ++-- src/Collector/XmlImportCollector.php | 31 ----- src/Command/SwitchFormatCommand.php | 6 +- src/ConfigLoader.php | 9 +- src/Console/ColorConsoleDiffFormatter.php | 102 +++++++++++++++ src/Console/ConsoleDiffer.php | 22 ++++ src/Console/DifferFactory.php | 23 ++++ src/Console/Style/SymfonyStyleFactory.php | 20 +++ src/Converter/ConfigFormatConverter.php | 90 ++------------ .../ContainerBuilderCleaner.php | 106 ---------------- src/FileSystem/ConfigFileDumper.php | 2 +- src/FileSystem/RelativeFilePathHelper.php | 32 +++++ src/Finder/ConfigFileFinder.php | 2 +- src/Kernel/ConfigTransformerKernel.php | 3 + src/Naming/UniqueNaming.php | 26 ---- src/Reflection/PrivatesAccessor.php | 28 +++++ src/ValueObject/ConvertedContent.php | 11 +- tests/AbstractTestCase.php | 14 ++- .../AbstractConfigFormatConverterTestCase.php | 66 ---------- .../YamlToPhp/YamlToPhpTest.php | 63 +++++++--- .../YamlToPhpConverterTest.php | 2 +- .../ConfigFileFinder/ConfigFileFinderTest.php | 24 +--- tests/Helper/FixtureFinder.php | 2 +- tests/Helper/FixtureSplitter.php | 116 ++++++++++++++++++ tests/Helper/FixtureUpdater.php | 52 ++++++++ tests/Helper/ValueObject/InputAndExpected.php | 24 ++++ .../InputFileInfoAndExpectedFileInfo.php | 36 ++++++ 31 files changed, 570 insertions(+), 386 deletions(-) delete mode 100644 src/Collector/XmlImportCollector.php create mode 100644 src/Console/ColorConsoleDiffFormatter.php create mode 100644 src/Console/ConsoleDiffer.php create mode 100644 src/Console/DifferFactory.php create mode 100644 src/Console/Style/SymfonyStyleFactory.php delete mode 100644 src/DependencyInjection/ContainerBuilderCleaner.php create mode 100644 src/FileSystem/RelativeFilePathHelper.php delete mode 100644 src/Naming/UniqueNaming.php create mode 100644 src/Reflection/PrivatesAccessor.php delete mode 100644 tests/Converter/ConfigFormatConverter/AbstractConfigFormatConverterTestCase.php create mode 100644 tests/Helper/FixtureUpdater.php create mode 100644 tests/Helper/ValueObject/InputAndExpected.php create mode 100644 tests/Helper/ValueObject/InputFileInfoAndExpectedFileInfo.php diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index 7b6523a6cdf..47141dd701c 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -36,10 +36,6 @@ jobs: name: 'PHP Linter' run: vendor/bin/parallel-lint src tests - - - name: 'Check Commented Code' - run: vendor/bin/easy-ci check-commented-code src tests --ansi - - name: 'Check Active Classes' run: vendor/bin/class-leak check src --ansi --skip-type="\Symplify\PhpConfigPrinter\Contract\NodeVisitor\PrePrintNodeVisitorInterface" diff --git a/.gitignore b/.gitignore index d026490bac9..652511177c3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ composer.lock /vendor .phpunit.cache + +# kernel cache +/var diff --git a/bin/config-transformer.php b/bin/config-transformer.php index 1cedf79f0a3..688ccefb673 100755 --- a/bin/config-transformer.php +++ b/bin/config-transformer.php @@ -3,7 +3,6 @@ declare(strict_types=1); use Symplify\ConfigTransformer\Kernel\ConfigTransformerKernel; -use Symplify\SymplifyKernel\ValueObject\KernelBootAndApplicationRun; $possibleAutoloadPaths = [ // dependency @@ -26,6 +25,10 @@ require_once $scoperAutoloadFilepath; } +$configTransformerKernel = new ConfigTransformerKernel(); +$configTransformerKernel->boot(); -$kernelBootAndApplicationRun = new KernelBootAndApplicationRun(ConfigTransformerKernel::class); -$kernelBootAndApplicationRun->run(); +$container = $configTransformerKernel->getContainer(); + +$configTransformerApplication = $container->get(\Symplify\ConfigTransformer\Console\ConfigTransformerApplication::class); +$configTransformerApplication->run(); diff --git a/composer.json b/composer.json index 16807bd5157..20ec396f350 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ "symfony/expression-language": "^6.4", "symfony/finder": "^6.4", "symfony/yaml": "^6.4", - "symplify/php-config-printer": "^11.2" + "symplify/php-config-printer": "^11.3", + "webmozart/assert": "^1.11" }, "require-dev": { "cweagans/composer-patches": "^1.7", @@ -61,7 +62,6 @@ "check-cs": "vendor/bin/ecs check --ansi", "fix-cs": "vendor/bin/ecs check --fix --ansi", "phpstan": "vendor/bin/phpstan analyse --ansi --error-format symplify", - "rector": "vendor/bin/rector process --dry-run --ansi", - "release": "vendor/bin/monorepo-builder release patch --ansi" + "rector": "vendor/bin/rector process --dry-run --ansi" } } diff --git a/config/config.php b/config/config.php index e876a7ba277..8b4da9784a5 100644 --- a/config/config.php +++ b/config/config.php @@ -6,14 +6,14 @@ use PhpParser\NodeFinder; use SebastianBergmann\Diff\Differ; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\Yaml\Parser; +use Symplify\ConfigTransformer\Console\ColorConsoleDiffFormatter; use Symplify\ConfigTransformer\Console\ConfigTransformerApplication; -use Symplify\PackageBuilder\Console\Formatter\ColorConsoleDiffFormatter; -use Symplify\PackageBuilder\Console\Output\ConsoleDiffer; -use Symplify\PackageBuilder\Diff\DifferFactory; -use Symplify\PackageBuilder\Reflection\ClassLikeExistenceChecker; -use Symplify\PackageBuilder\Yaml\ParametersMerger; +use Symplify\ConfigTransformer\Console\ConsoleDiffer; +use Symplify\ConfigTransformer\Console\DifferFactory; +use Symplify\ConfigTransformer\Console\Style\SymfonyStyleFactory; use function Symfony\Component\DependencyInjection\Loader\Configurator\service; return static function (ContainerConfigurator $containerConfigurator): void { @@ -21,7 +21,8 @@ $services->defaults() ->public() - ->autowire(); + ->autowire() + ->autoconfigure(); $services->load('Symplify\ConfigTransformer\\', __DIR__ . '/../src') ->exclude([ @@ -35,18 +36,15 @@ $services->alias(Application::class, ConfigTransformerApplication::class); // color diff - $services->set(DifferFactory::class); $services->set(Differ::class) ->factory([service(DifferFactory::class), 'create']); - $services->set(ConsoleDiffer::class); - $services->set(ColorConsoleDiffFormatter::class); + $services->set(SymfonyStyle::class) + ->factory([service(SymfonyStyleFactory::class), 'create']); + $services->set(BuilderFactory::class); $services->set(NodeFinder::class); $services->set(Parser::class); - - $services->set(ClassLikeExistenceChecker::class); - $services->set(ParametersMerger::class); }; diff --git a/src/Collector/XmlImportCollector.php b/src/Collector/XmlImportCollector.php deleted file mode 100644 index 20272134e32..00000000000 --- a/src/Collector/XmlImportCollector.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ - private array $imports = []; - - public function addImport(mixed $resource, bool|string $ignoreErrors): void - { - $this->imports[] = [ - YamlKey::RESOURCE => $resource, - YamlKey::IGNORE_ERRORS => $ignoreErrors, - ]; - } - - /** - * @return array - */ - public function provide(): array - { - return $this->imports; - } -} diff --git a/src/Command/SwitchFormatCommand.php b/src/Command/SwitchFormatCommand.php index d1482a3c0f7..5c5ff159e5b 100644 --- a/src/Command/SwitchFormatCommand.php +++ b/src/Command/SwitchFormatCommand.php @@ -12,6 +12,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; use Symplify\ConfigTransformer\Configuration\ConfigurationFactory; use Symplify\ConfigTransformer\Converter\ConfigFormatConverter; use Symplify\ConfigTransformer\FileSystem\ConfigFileDumper; @@ -19,7 +20,6 @@ use Symplify\ConfigTransformer\ValueObject\Configuration; use Symplify\ConfigTransformer\ValueObject\ConvertedContent; use Symplify\ConfigTransformer\ValueObject\Option; -use Symplify\SmartFileSystem\SmartFileInfo; final class SwitchFormatCommand extends Command { @@ -45,7 +45,7 @@ protected function configure(): void InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Path to directory/file with configs', // 99 % of symfony project has this directory - [getcwd() . '/config'] + [getcwd() . '/config', getcwd() . '/app/config'] ); $this->addOption(Option::DRY_RUN, 'n', InputOption::VALUE_NONE, 'Dry run - no removal or config change'); @@ -89,7 +89,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return self::SUCCESS; } - private function removeFileInfo(Configuration $configuration, SmartFileInfo $fileInfo): void + private function removeFileInfo(Configuration $configuration, SplFileInfo $fileInfo): void { // only dry run, nothing to remove if ($configuration->isDryRun()) { diff --git a/src/ConfigLoader.php b/src/ConfigLoader.php index 25a4a4078c3..ef25f25f43d 100644 --- a/src/ConfigLoader.php +++ b/src/ConfigLoader.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; use Symfony\Component\DependencyInjection\Loader\GlobFileLoader; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\Finder\SplFileInfo; use Symplify\ConfigTransformer\DependencyInjection\ExtensionFaker; use Symplify\ConfigTransformer\DependencyInjection\Loader\MissingAutodiscoveryDirectoryTolerantYamlFileLoader; use Symplify\ConfigTransformer\DependencyInjection\Loader\SkippingPhpFileLoader; @@ -28,7 +29,7 @@ final class ConfigLoader * @see https://regex101.com/r/4Uanps/4 * @var string */ - private const PHP_CONST_REGEX = '#!php/const:?\s*([a-zA-Z0-9_\\\\]+(::[a-zA-Z0-9_]+)?)+(:\s*(.*))?#'; + private const PHP_CONST_REGEX = '#!php/const:?\s*([a-zA-Z0-9_\\\]+(::\w+)?)+(:\s*(.*))?#'; /** * @see https://regex101.com/r/spi4ir/1 @@ -42,11 +43,11 @@ public function __construct( } public function createAndLoadContainerBuilderFromFileInfo( - \SplFileInfo $smartFileInfo, + SplFileInfo $smartFileInfo, ): ContainerBuilderAndFileContent { $containerBuilder = new ContainerBuilder(); - $delegatingLoader = $this->createLoaderBySuffix($containerBuilder, $smartFileInfo->getSuffix()); + $delegatingLoader = $this->createLoaderBySuffix($containerBuilder, $smartFileInfo->getExtension()); $fileRealPath = $smartFileInfo->getRealPath(); // correct old syntax of tags so we can parse it @@ -59,7 +60,7 @@ public function createAndLoadContainerBuilderFromFileInfo( static fn (array $match): string => $match[1] . '"' . $match[2] . ($match[4] ?? '') . '"' ); - if (in_array($smartFileInfo->getSuffix(), [Format::YML, Format::YAML], true)) { + if (in_array($smartFileInfo->getExtension(), [Format::YML, Format::YAML], true)) { $content = Strings::replace( $content, self::PHP_CONST_REGEX, diff --git a/src/Console/ColorConsoleDiffFormatter.php b/src/Console/ColorConsoleDiffFormatter.php new file mode 100644 index 00000000000..280bcd84f7e --- /dev/null +++ b/src/Console/ColorConsoleDiffFormatter.php @@ -0,0 +1,102 @@ + + */ +final class ColorConsoleDiffFormatter +{ + /** + * @var string + * @see https://regex101.com/r/ovLMDF/1 + */ + private const PLUS_START_REGEX = '#^(\+.*)#'; + + /** + * @var string + * @see https://regex101.com/r/xwywpa/1 + */ + private const MINUT_START_REGEX = '#^(\-.*)#'; + + /** + * @var string + * @see https://regex101.com/r/CMlwa8/1 + */ + private const AT_START_REGEX = '#^(@.*)#'; + + /** + * @var string + * @see https://regex101.com/r/qduj2O/1 + */ + private const NEWLINES_REGEX = "#\n\r|\n#"; + + private readonly string $template; + + public function __construct() + { + $this->template = sprintf( + ' ---------- begin diff ----------%s%%s%s ----------- end diff -----------' . PHP_EOL, + PHP_EOL, + PHP_EOL + ); + } + + public function format(string $diff): string + { + return $this->formatWithTemplate($diff, $this->template); + } + + private function formatWithTemplate(string $diff, string $template): string + { + $escapedDiff = OutputFormatter::escape(rtrim($diff)); + + $escapedDiffLines = Strings::split($escapedDiff, self::NEWLINES_REGEX); + + // remove description of added + remove; obvious on diffs + foreach ($escapedDiffLines as $key => $escapedDiffLine) { + if ($escapedDiffLine === '--- Original') { + unset($escapedDiffLines[$key]); + } + + if ($escapedDiffLine === '+++ New') { + unset($escapedDiffLines[$key]); + } + } + + $coloredLines = array_map(function (string $string): string { + $string = $this->makePlusLinesGreen($string); + $string = $this->makeMinusLinesRed($string); + $string = $this->makeAtNoteCyan($string); + + if ($string === ' ') { + return ''; + } + + return $string; + }, $escapedDiffLines); + + return sprintf($template, implode(PHP_EOL, $coloredLines)); + } + + private function makePlusLinesGreen(string $string): string + { + return Strings::replace($string, self::PLUS_START_REGEX, '$1'); + } + + private function makeMinusLinesRed(string $string): string + { + return Strings::replace($string, self::MINUT_START_REGEX, '$1'); + } + + private function makeAtNoteCyan(string $string): string + { + return Strings::replace($string, self::AT_START_REGEX, '$1'); + } +} diff --git a/src/Console/ConsoleDiffer.php b/src/Console/ConsoleDiffer.php new file mode 100644 index 00000000000..4725d5a8d7d --- /dev/null +++ b/src/Console/ConsoleDiffer.php @@ -0,0 +1,22 @@ +differ->diff($old, $new); + return $this->colorConsoleDiffFormatter->format($diff); + } +} diff --git a/src/Console/DifferFactory.php b/src/Console/DifferFactory.php new file mode 100644 index 00000000000..e479f88592c --- /dev/null +++ b/src/Console/DifferFactory.php @@ -0,0 +1,23 @@ +currentFilePathProvider->setFilePath($smartFileInfo->getRealPath()); + $this->currentFilePathProvider->setFilePath($fileInfo->getRealPath()); $containerBuilderAndFileContent = $this->configLoader->createAndLoadContainerBuilderFromFileInfo( - $smartFileInfo + $fileInfo ); - $containerBuilder = $containerBuilderAndFileContent->getContainerBuilder(); - - if (in_array($smartFileInfo->getSuffix(), [Format::YAML, Format::YML], true)) { + if (in_array($fileInfo->getExtension(), [Format::YAML, Format::YML], true)) { $dumpedYaml = $containerBuilderAndFileContent->getFileContent(); - $dumpedYaml = $this->decorateWithCollectedXmlImports($dumpedYaml); - - return $this->yamlToPhpConverter->convert($dumpedYaml, $smartFileInfo->getRealPath()); + return $this->yamlToPhpConverter->convert($dumpedYaml, $fileInfo->getRealPath()); } - if ($smartFileInfo->getSuffix() === Format::XML) { - $dumpedYaml = $this->dumpContainerBuilderToYaml($containerBuilder); - $dumpedYaml = $this->decorateWithCollectedXmlImports($dumpedYaml); - - return $this->yamlToPhpConverter->convert($dumpedYaml, $smartFileInfo->getRealPath()); - } - - $message = sprintf('Suffix "%s" is not support yet', $smartFileInfo->getSuffix()); + $message = sprintf('Suffix "%s" is not support yet', $fileInfo->getExtension()); throw new NotImplementedYetException($message); } - - private function dumpContainerBuilderToYaml(ContainerBuilder $containerBuilder): string - { - $yamlDumper = new YamlDumper($containerBuilder); - $this->containerBuilderCleaner->cleanContainerBuilder($containerBuilder); - - // 1. services and parameters - $content = $yamlDumper->dump(); - if (! is_string($content)) { - throw new ShouldNotHappenException(); - } - - // 2. append extension yaml too - /** @var array $extensionsConfigs */ - $extensionsConfigs = $this->privatesAccessor->getPrivateProperty($containerBuilder, 'extensionConfigs'); - foreach ($extensionsConfigs as $namespace => $configs) { - $mergedConfig = []; - foreach ($configs as $config) { - $mergedConfig = $this->parametersMerger->merge($mergedConfig, $config); - } - - $extensionsConfigs[$namespace] = $mergedConfig; - } - - $extensionsContent = $this->dumpYaml($extensionsConfigs); - - return $content . PHP_EOL . $extensionsContent; - } - - private function decorateWithCollectedXmlImports(string $dumpedYaml): string - { - $collectedXmlImports = $this->xmlImportCollector->provide(); - if ($collectedXmlImports === []) { - return $dumpedYaml; - } - - /** @var array $yamlArray */ - $yamlArray = Yaml::parse($dumpedYaml, Yaml::PARSE_CUSTOM_TAGS); - $yamlArray['imports'] = array_merge($yamlArray['imports'] ?? [], $collectedXmlImports); - - return $this->dumpYaml($yamlArray); - } - - /** - * @param array $yamlArray - */ - private function dumpYaml(array $yamlArray): string - { - if ($yamlArray === []) { - return ''; - } - - return Yaml::dump($yamlArray, 10, 4, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK); - } } diff --git a/src/DependencyInjection/ContainerBuilderCleaner.php b/src/DependencyInjection/ContainerBuilderCleaner.php deleted file mode 100644 index 8d494aa71cb..00000000000 --- a/src/DependencyInjection/ContainerBuilderCleaner.php +++ /dev/null @@ -1,106 +0,0 @@ -removeSymfonyInternalServices($containerBuilder); - $this->removeTemporaryAnonymousIds($containerBuilder); - - foreach ($containerBuilder->getDefinitions() as $definition) { - $this->resolvePolyfillForNameTag($definition); - } - } - - private function removeSymfonyInternalServices(ContainerBuilder $containerBuilder): void - { - $containerBuilder->removeDefinition('service_container'); - $containerBuilder->removeAlias(PsrContainerInterface::class); - $containerBuilder->removeAlias(ContainerInterface::class); - } - - private function removeTemporaryAnonymousIds(ContainerBuilder $containerBuilder): void - { - $definitions = $this->privatesAccessor->getPrivateProperty($containerBuilder, 'definitions'); - - foreach ($definitions as $name => $definition) { - if (! is_string($name)) { - continue; - } - - if (! $this->isGeneratedKeyForAnonymousClass($name)) { - continue; - } - - unset($definitions[$name]); - $definitions[] = $definition; - } - - $this->privatesAccessor->setPrivateProperty($containerBuilder, 'definitions', $definitions); - } - - private function isGeneratedKeyForAnonymousClass(string $name): bool - { - return (bool) Strings::match($name, self::ANONYMOUS_CLASS_REGEX); - } - - private function resolvePolyfillForNameTag(Definition $definition): void - { - if ($definition->getTags() === []) { - return; - } - - $tags = $definition->getTags(); - foreach ($definition->getTags() as $name => $value) { - /** @var mixed[] $tagValues */ - $tagValues = $value[0]; - if ($this->shouldSkipNameTagInlining($tagValues)) { - continue; - } - - unset($tags[$name]); - - $tagValues = []; - foreach ($value as $singleValue) { - $singleTag = array_merge([ - 'name' => $name, - ], $singleValue); - $tagValues[] = $singleTag; - } - - $tags[] = $tagValues; - } - - $definition->setTags($tags); - } - - /** - * @param array $tagValues - */ - private function shouldSkipNameTagInlining(array $tagValues): bool - { - return $tagValues === []; - } -} diff --git a/src/FileSystem/ConfigFileDumper.php b/src/FileSystem/ConfigFileDumper.php index 1bdb01b7f38..e44b11856ec 100644 --- a/src/FileSystem/ConfigFileDumper.php +++ b/src/FileSystem/ConfigFileDumper.php @@ -6,9 +6,9 @@ use Nette\Utils\FileSystem; use Symfony\Component\Console\Style\SymfonyStyle; +use Symplify\ConfigTransformer\Console\ConsoleDiffer; use Symplify\ConfigTransformer\ValueObject\Configuration; use Symplify\ConfigTransformer\ValueObject\ConvertedContent; -use Symplify\PackageBuilder\Console\Output\ConsoleDiffer; final class ConfigFileDumper { diff --git a/src/FileSystem/RelativeFilePathHelper.php b/src/FileSystem/RelativeFilePathHelper.php new file mode 100644 index 00000000000..f9a4169abd8 --- /dev/null +++ b/src/FileSystem/RelativeFilePathHelper.php @@ -0,0 +1,32 @@ +makePathRelative( + $normalizedFilePath, + (string) realpath($directory) + ); + + return rtrim($relativeFilePath, '/'); + } + + private static function normalizePath(string $path): string + { + return str_replace('\\', '/', $path); + } +} diff --git a/src/Finder/ConfigFileFinder.php b/src/Finder/ConfigFileFinder.php index 3402330b75e..c7b12d263f1 100644 --- a/src/Finder/ConfigFileFinder.php +++ b/src/Finder/ConfigFileFinder.php @@ -4,8 +4,8 @@ namespace Symplify\ConfigTransformer\Finder; -use SplFileInfo; use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; final class ConfigFileFinder { diff --git a/src/Kernel/ConfigTransformerKernel.php b/src/Kernel/ConfigTransformerKernel.php index a6b09a9fb3f..86db0ed3d0f 100644 --- a/src/Kernel/ConfigTransformerKernel.php +++ b/src/Kernel/ConfigTransformerKernel.php @@ -8,6 +8,9 @@ use Symfony\Component\HttpKernel\Kernel; use Symplify\PhpConfigPrinter\ValueObject\PhpConfigPrinterConfig; +/** + * @api used in tests and bin + */ final class ConfigTransformerKernel extends Kernel { public function __construct() diff --git a/src/Naming/UniqueNaming.php b/src/Naming/UniqueNaming.php deleted file mode 100644 index 8027793cbd1..00000000000 --- a/src/Naming/UniqueNaming.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ - private array $existingNames = []; - - public function uniquateName(string $name): string - { - if (isset($this->existingNames[$name])) { - $serviceNameCounter = $this->existingNames[$name]; - $this->existingNames[$name] = ++$serviceNameCounter; - return $name . '.' . $serviceNameCounter; - } - - $this->existingNames[$name] = 1; - - return $name; - } -} diff --git a/src/Reflection/PrivatesAccessor.php b/src/Reflection/PrivatesAccessor.php new file mode 100644 index 00000000000..49a9287d904 --- /dev/null +++ b/src/Reflection/PrivatesAccessor.php @@ -0,0 +1,28 @@ +setAccessible(true); + + return $reflectionProperty->getValue($object); + } + + // write private property + public static function writePrivateProperty(object $object, string $propertyName, mixed $value): void + { + $reflectionProperty = new ReflectionProperty($object, $propertyName); + $reflectionProperty->setAccessible(true); + + $reflectionProperty->setValue($object, $value); + } +} diff --git a/src/ValueObject/ConvertedContent.php b/src/ValueObject/ConvertedContent.php index b3e3abba055..64562754212 100644 --- a/src/ValueObject/ConvertedContent.php +++ b/src/ValueObject/ConvertedContent.php @@ -6,9 +6,16 @@ use Nette\Utils\Strings; use Symfony\Component\Finder\SplFileInfo; +use Symplify\ConfigTransformer\FileSystem\RelativeFilePathHelper; final class ConvertedContent { + /** + * @var string + * @see https://regex101.com/r/SYP00O/1 + */ + private const LAST_SUFFIX_REGEX = '#\.[^.]+$#'; + public function __construct( private readonly string $convertedContent, private readonly SplFileInfo $originalFileInfo @@ -30,12 +37,12 @@ public function getNewRelativeFilePath(): string public function getOriginalFilePathWithoutSuffix(): string { - return $this->originalFileInfo->getRealPathWithoutSuffix(); + return Strings::replace($this->originalFileInfo->getRealPath(), self::LAST_SUFFIX_REGEX, ''); } public function getOriginalRelativeFilePath(): string { - return $this->originalFileInfo->getRelativeFilePathFromCwd(); + return RelativeFilePathHelper::resolveFromDirectory($this->originalFileInfo->getRealPath(), getcwd()); } public function getOriginalContent(): string diff --git a/tests/AbstractTestCase.php b/tests/AbstractTestCase.php index fc3496c7602..5f1fff7407c 100644 --- a/tests/AbstractTestCase.php +++ b/tests/AbstractTestCase.php @@ -5,11 +5,12 @@ namespace Symplify\ConfigTransformer\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symplify\ConfigTransformer\Kernel\ConfigTransformerKernel; abstract class AbstractTestCase extends TestCase { - protected \Symfony\Component\DependencyInjection\ContainerInterface $container; + private ContainerInterface $container; protected function setUp(): void { @@ -18,4 +19,15 @@ protected function setUp(): void $this->container = $configTransformerKernel->getContainer(); } + + /** + * @template T as object + * + * @param class-string $type + * @return T + */ + protected function getService(string $type): object + { + return $this->container->get($type); + } } diff --git a/tests/Converter/ConfigFormatConverter/AbstractConfigFormatConverterTestCase.php b/tests/Converter/ConfigFormatConverter/AbstractConfigFormatConverterTestCase.php deleted file mode 100644 index b3bcdd941f6..00000000000 --- a/tests/Converter/ConfigFormatConverter/AbstractConfigFormatConverterTestCase.php +++ /dev/null @@ -1,66 +0,0 @@ ->>>>>> af077e6 (fixup! bump) -======= -use Symplify\ConfigTransformer\Tests\AbstractTestCase; ->>>>>>> cd67f40 (bump) -use Symplify\EasyTesting\DataProvider\StaticFixtureUpdater; -use Symplify\EasyTesting\StaticFixtureSplitter; -use Symplify\SmartFileSystem\SmartFileInfo; -use Symplify\SmartFileSystem\SmartFileSystem; -======= ->>>>>>> 19a2662 (fixup! fixup! bump) -======= -use Symplify\ConfigTransformer\Tests\Helper\FixtureSplitter; ->>>>>>> 257b8ab (fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! bump) - -abstract class AbstractConfigFormatConverterTestCase extends AbstractTestCase -{ - protected ConfigFormatConverter $configFormatConverter; - - protected FileSystem $smartFileSystem; - - protected function setUp(): void - { -<<<<<<< HEAD -<<<<<<< HEAD - $this->bootKernel(ConfigTransformerKernel::class); - $this->configFormatConverter = $this->getService(ConfigFormatConverter::class); - $this->smartFileSystem = $this->getService(SmartFileSystem::class); -======= - $this->configFormatConverter = $this->container->get(ConfigFormatConverter::class); - $this->smartFileSystem = $this->container->get(FileSystem::class); ->>>>>>> 19a2662 (fixup! fixup! bump) -======= - $this->configFormatConverter = $this->container->get(ConfigFormatConverter::class); - $this->smartFileSystem = $this->container->get(SmartFileSystem::class); ->>>>>>> cd67f40 (bump) - } - - protected function doTestOutput(\SplFileInfo $fixtureFileInfo, bool $preserveDirStructure = false): void - { - $inputAndExpected = FixtureSplitter::splitFileInfoToLocalInputAndExpectedFileInfos($fixtureFileInfo, false, $preserveDirStructure); - $this->doTestFileInfo($inputAndExpected->getInputFileInfo(), $inputAndExpected->getExpectedFileContent(), $fixtureFileInfo); - } - - protected function doTestFileInfo(\SplFileInfo $inputFileInfo, string $expectedContent, \SplFileInfo $fixtureFileInfo): void - { - $convertedContent = $this->configFormatConverter->convert($inputFileInfo); - StaticFixtureUpdater::updateFixtureContent($inputFileInfo, $convertedContent, $fixtureFileInfo); - - $this->assertSame($expectedContent, $convertedContent, $fixtureFileInfo->getRelativeFilePathFromCwd()); - } -} diff --git a/tests/Converter/ConfigFormatConverter/YamlToPhp/YamlToPhpTest.php b/tests/Converter/ConfigFormatConverter/YamlToPhp/YamlToPhpTest.php index 971f7c9c860..ddb3ba6577d 100644 --- a/tests/Converter/ConfigFormatConverter/YamlToPhp/YamlToPhpTest.php +++ b/tests/Converter/ConfigFormatConverter/YamlToPhp/YamlToPhpTest.php @@ -7,13 +7,27 @@ use Iterator; use Nette\Utils\FileSystem; use PHPUnit\Framework\Attributes\DataProvider; -use Symplify\ConfigTransformer\Tests\Converter\ConfigFormatConverter\AbstractConfigFormatConverterTestCase; +use Symfony\Component\Finder\SplFileInfo; +use Symplify\ConfigTransformer\Converter\ConfigFormatConverter; +use Symplify\ConfigTransformer\FileSystem\RelativeFilePathHelper; +use Symplify\ConfigTransformer\Tests\AbstractTestCase; use Symplify\ConfigTransformer\Tests\Helper\FixtureFinder; +use Symplify\ConfigTransformer\Tests\Helper\FixtureSplitter; +use Symplify\ConfigTransformer\Tests\Helper\FixtureUpdater; -final class YamlToPhpTest extends AbstractConfigFormatConverterTestCase +final class YamlToPhpTest extends AbstractTestCase { + private ConfigFormatConverter $configFormatConverter; + + protected function setUp(): void + { + parent::setUp(); + + $this->configFormatConverter = $this->getService(ConfigFormatConverter::class); + } + #[DataProvider('provideDataForRouting')] - public function testRouting(\SplFileInfo $fileInfo): void + public function testRouting(SplFileInfo $fileInfo): void { $this->doTestOutput($fileInfo, true); } @@ -28,10 +42,10 @@ public static function provideDataForRouting(): Iterator #[DataProvider('provideData')] #[DataProvider('provideDataWithPhpImported')] - public function testNormal(\SplFileInfo $fixtureFileInfo): void + public function testNormal(SplFileInfo $fixtureFileInfo): void { // for imports - $temporaryPath = StaticFixtureSplitter::getTemporaryPath(); + $temporaryPath = FixtureSplitter::getTemporaryPath(); $filesystem = new \Symfony\Component\Filesystem\Filesystem(); $filesystem->mirror(__DIR__ . '/Fixture/normal', $temporaryPath); @@ -57,13 +71,13 @@ public function testNormal(\SplFileInfo $fixtureFileInfo): void } #[DataProvider('provideDataWithDirectory')] - public function testSpecialCaseWithDirectory(\SplFileInfo $fileInfo): void + public function testSpecialCaseWithDirectory(SplFileInfo $fileInfo): void { $this->doTestOutputWithExtraDirectory($fileInfo, __DIR__ . '/Fixture/nested'); } #[DataProvider('provideDataExtension')] - public function testEcs(\SplFileInfo $fileInfo): void + public function testEcs(SplFileInfo $fileInfo): void { $this->doTestOutputWithExtraDirectory($fileInfo, $fileInfo->getPath()); } @@ -72,10 +86,10 @@ public function testEcs(\SplFileInfo $fileInfo): void * @source https://github.com/symfony/maker-bundle/pull/604 */ #[DataProvider('provideDataMakerBundle')] - public function testMakerBundle(\SplFileInfo $fileInfo): void + public function testMakerBundle(SplFileInfo $fileInfo): void { // needed for all the included - $temporaryPath = StaticFixtureSplitter::getTemporaryPath(); + $temporaryPath = FixtureSplitter::getTemporaryPath(); FileSystem::write( $temporaryPath . '/../src/SomeClass.php', ' + * @return Iterator */ public static function provideDataMakerBundle(): Iterator { return FixtureFinder::yieldDirectory(__DIR__ . '/Fixture/maker-bundle', '*.yaml'); } - private function doTestOutputWithExtraDirectory(\SplFileInfo $fixtureFileInfo, string $extraDirectory): void + private function doTestOutput(SplFileInfo $fixtureFileInfo, bool $preserveDirStructure = false): void { - $inputAndExpected = StaticFixtureSplitter::splitFileInfoToInputAndExpected($fixtureFileInfo); - $temporaryPath = StaticFixtureSplitter::getTemporaryPath(); + $inputAndExpected = FixtureSplitter::splitFileInfoToLocalInputAndExpectedFileInfos($fixtureFileInfo, false, $preserveDirStructure); + + $this->doTestFileInfo($inputAndExpected->getInputFileInfo(), $inputAndExpected->getExpectedFileContent(), $fixtureFileInfo); + } + + private function doTestFileInfo(SplFileInfo $inputFileInfo, string $expectedContent, SplFileInfo $fixtureFileInfo): void + { + $convertedContent = $this->configFormatConverter->convert($inputFileInfo); + FixtureUpdater::updateFixtureContent($inputFileInfo, $convertedContent, $fixtureFileInfo); + + $filePath = RelativeFilePathHelper::resolveFromDirectory($fixtureFileInfo->getRealPath(), getcwd()); + + $this->assertSame($expectedContent, $convertedContent, $filePath); + } + + private function doTestOutputWithExtraDirectory(SplFileInfo $fixtureFileInfo, string $extraDirectory): void + { + $inputAndExpected = FixtureSplitter::splitFileInfoToInputAndExpected($fixtureFileInfo); + $temporaryPath = FixtureSplitter::getTemporaryPath(); // copy /src to temp directory, so Symfony FileLocator knows about it $fileSystem = new \Symfony\Component\Filesystem\Filesystem(); @@ -139,7 +170,8 @@ private function doTestOutputWithExtraDirectory(\SplFileInfo $fixtureFileInfo, s 'override' => true, ]); - $fileTemporaryPath = $temporaryPath . '/' . $fixtureFileInfo->getRelativeFilePathFromDirectory($extraDirectory); + $fileTemporaryPath = $temporaryPath . '/' . RelativeFilePathHelper::resolveFromDirectory($fixtureFileInfo->getRealPath(), $extraDirectory); + FileSystem::write($fileTemporaryPath, $inputAndExpected->getInput()); // require class to autoload it @@ -148,8 +180,7 @@ private function doTestOutputWithExtraDirectory(\SplFileInfo $fixtureFileInfo, s require_once $expectedFilePath; - $inputFileInfo = new \SplFileInfo($fileTemporaryPath); - + $inputFileInfo = new SplFileInfo($fileTemporaryPath, '', ''); $this->doTestFileInfo($inputFileInfo, $inputAndExpected->getExpected(), $fixtureFileInfo); } } diff --git a/tests/Converter/YamlToPhpConverter/YamlToPhpConverterTest.php b/tests/Converter/YamlToPhpConverter/YamlToPhpConverterTest.php index b55c4283946..bf6857e553c 100644 --- a/tests/Converter/YamlToPhpConverter/YamlToPhpConverterTest.php +++ b/tests/Converter/YamlToPhpConverter/YamlToPhpConverterTest.php @@ -15,7 +15,7 @@ protected function setUp(): void { parent::setUp(); - $this->yamlToPhpConverter = $this->container->get(YamlToPhpConverter::class); + $this->yamlToPhpConverter = $this->getService(YamlToPhpConverter::class); } public function test(): void diff --git a/tests/Finder/ConfigFileFinder/ConfigFileFinderTest.php b/tests/Finder/ConfigFileFinder/ConfigFileFinderTest.php index ace0785d3cc..18874bf2e62 100644 --- a/tests/Finder/ConfigFileFinder/ConfigFileFinderTest.php +++ b/tests/Finder/ConfigFileFinder/ConfigFileFinderTest.php @@ -4,37 +4,17 @@ namespace Symplify\ConfigTransformer\Tests\Finder\ConfigFileFinder; +use PHPUnit\Framework\TestCase; use Symplify\ConfigTransformer\Finder\ConfigFileFinder; -<<<<<<< HEAD -<<<<<<< HEAD -use Symplify\ConfigTransformer\Kernel\ConfigTransformerKernel; -======= -use Symplify\ConfigTransformer\Tests\AbstractTestCase; ->>>>>>> cd67f40 (bump) -use Symplify\ConfigTransformer\ValueObject\Configuration; - -final class ConfigFileFinderTest extends AbstractTestCase -{ - private ConfigFileFinder $configFileFinder; - - protected function setUp(): void - { - $this->configFileFinder = $this->container->get(ConfigFileFinder::class); - } - -======= -use Symplify\ConfigTransformer\Tests\AbstractTestCase; /** * @see \Symplify\ConfigTransformer\Finder\ConfigFileFinder */ -final class ConfigFileFinderTest extends AbstractTestCase +final class ConfigFileFinderTest extends TestCase { ->>>>>>> 8c52b6b (fixup! fixup! fixup! fixup! fixup! fixup! bump) public function test(): void { $configFileFinder = new ConfigFileFinder(); - ; $fileInfos = $configFileFinder->findFileInfos([__DIR__ . '/Fixture']); $this->assertCount(1, $fileInfos); diff --git a/tests/Helper/FixtureFinder.php b/tests/Helper/FixtureFinder.php index 20ac024016d..172080a3f96 100644 --- a/tests/Helper/FixtureFinder.php +++ b/tests/Helper/FixtureFinder.php @@ -5,8 +5,8 @@ namespace Symplify\ConfigTransformer\Tests\Helper; use Iterator; -use SplFileInfo; use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; final class FixtureFinder { diff --git a/tests/Helper/FixtureSplitter.php b/tests/Helper/FixtureSplitter.php index 2cf853d03c4..c94a1d611ef 100644 --- a/tests/Helper/FixtureSplitter.php +++ b/tests/Helper/FixtureSplitter.php @@ -4,6 +4,122 @@ namespace Symplify\ConfigTransformer\Tests\Helper; +use Nette\Utils\FileSystem; +use Nette\Utils\Strings; +use Symfony\Component\Finder\SplFileInfo; +use Symplify\ConfigTransformer\Tests\Helper\ValueObject\InputAndExpected; +use Symplify\ConfigTransformer\Tests\Helper\ValueObject\InputFileInfoAndExpectedFileInfo; + final class FixtureSplitter { + /** + * @see https://regex101.com/r/8fuULy/1 + * @var string + */ + private const SPLIT_LINE_REGEX = "#\-\-\-\-\-\r?\n#"; + + public static ?string $customTemporaryPath = null; + + public static function splitFileInfoToInputAndExpected(SplFileInfo $smartFileInfo): InputAndExpected + { + $splitLineCount = count(Strings::matchAll($smartFileInfo->getContents(), self::SPLIT_LINE_REGEX)); + + // if more or less, it could be a test cases for monorepo line in it + if ($splitLineCount === 1) { + // input → expected + [$input, $expected] = Strings::split($smartFileInfo->getContents(), self::SPLIT_LINE_REGEX); + + $expected = self::retypeExpected($expected); + + return new InputAndExpected($input, $expected); + } + + // no changes + return new InputAndExpected($smartFileInfo->getContents(), $smartFileInfo->getContents()); + } + + public static function splitFileInfoToLocalInputAndExpectedFileInfos( + SplFileInfo $smartFileInfo, + bool $autoloadTestFixture = false, + bool $preserveDirStructure = false + ): InputFileInfoAndExpectedFileInfo { + $inputAndExpected = self::splitFileInfoToInputAndExpected($smartFileInfo); + + $prefix = ''; + if ($preserveDirStructure) { + $dir = explode('Fixture', $smartFileInfo->getRealPath(), 2); + $prefix = isset($dir[1]) ? dirname($dir[1]) . '/' : ''; + $prefix = ltrim($prefix, '/\\'); + } + + $inputFileInfo = self::createTemporaryFileInfo( + $smartFileInfo, + $prefix . 'input', + $inputAndExpected->getInput() + ); + + // some files needs to be autoload to enable reflection + if ($autoloadTestFixture) { + require_once $inputFileInfo->getRealPath(); + } + + $expectedFileInfo = self::createTemporaryFileInfo( + $smartFileInfo, + $prefix . 'expected', + $inputAndExpected->getExpected() + ); + + return new InputFileInfoAndExpectedFileInfo($inputFileInfo, $expectedFileInfo); + } + + public static function getTemporaryPath(): string + { + if (self::$customTemporaryPath !== null) { + return self::$customTemporaryPath; + } + + return sys_get_temp_dir() . '/_temp_fixture_easy_testing'; + } + + public static function createTemporaryFileInfo( + SplFileInfo $fixtureSmartFileInfo, + string $prefix, + string $fileContent + ): SplFileInfo { + $temporaryFilePath = self::createTemporaryPathWithPrefix($fixtureSmartFileInfo, $prefix); + $dir = dirname($temporaryFilePath); + if (! is_dir($dir)) { + mkdir($dir, 0777, true); + } + + FileSystem::write($temporaryFilePath, $fileContent); + return new SplFileInfo($temporaryFilePath, '', ''); + } + + private static function createTemporaryPathWithPrefix(SplFileInfo $smartFileInfo, string $prefix): string + { + $hash = Strings::substring(md5($smartFileInfo->getRealPath()), -20); + + $fileBasename = $smartFileInfo->getBasename('.inc'); + + return self::getTemporaryPath() . sprintf('/%s_%s_%s', $prefix, $hash, $fileBasename); + } + + private static function retypeExpected(mixed $expected): mixed + { + if (! is_numeric(trim((string) $expected))) { + return $expected; + } + + // value re-type + if (strlen((string) (int) $expected) === strlen(trim((string) $expected))) { + return (int) $expected; + } + + if (strlen((string) (float) $expected) === strlen(trim((string) $expected))) { + return (float) $expected; + } + + return $expected; + } } diff --git a/tests/Helper/FixtureUpdater.php b/tests/Helper/FixtureUpdater.php new file mode 100644 index 00000000000..c47a2c7580d --- /dev/null +++ b/tests/Helper/FixtureUpdater.php @@ -0,0 +1,52 @@ +getRealPath(), $newOriginalContent); + } + + public static function updateExpectedFixtureContent( + string $newOriginalContent, + SplFileInfo $expectedFixtureFileInfo + ): void { + if (! getenv('UPDATE_TESTS') && ! getenv('UT')) { + return; + } + + FileSystem::write($expectedFixtureFileInfo->getRealPath(), $newOriginalContent); + } + + private static function resolveNewFixtureContent( + SplFileInfo|string $originalFileInfo, + string $changedContent + ): string { + if ($originalFileInfo instanceof SplFileInfo) { + $originalContent = $originalFileInfo->getContents(); + } else { + $originalContent = $originalFileInfo; + } + + if ($originalContent === $changedContent) { + return $originalContent; + } + + return $originalContent . '-----' . PHP_EOL . $changedContent; + } +} diff --git a/tests/Helper/ValueObject/InputAndExpected.php b/tests/Helper/ValueObject/InputAndExpected.php new file mode 100644 index 00000000000..c6a95b73061 --- /dev/null +++ b/tests/Helper/ValueObject/InputAndExpected.php @@ -0,0 +1,24 @@ +input; + } + + public function getExpected(): string + { + return $this->expected; + } +} diff --git a/tests/Helper/ValueObject/InputFileInfoAndExpectedFileInfo.php b/tests/Helper/ValueObject/InputFileInfoAndExpectedFileInfo.php new file mode 100644 index 00000000000..43d2e8ae007 --- /dev/null +++ b/tests/Helper/ValueObject/InputFileInfoAndExpectedFileInfo.php @@ -0,0 +1,36 @@ +inputFileInfo; + } + + public function getExpectedFileInfo(): SplFileInfo + { + return $this->expectedFileInfo; + } + + public function getExpectedFileContent(): string + { + return $this->expectedFileInfo->getContents(); + } + + public function getExpectedFileInfoRealPath(): string + { + return $this->expectedFileInfo->getRealPath(); + } +}