move check_services_in_yaml_configs to ValidateServicesInSetsCommand

This commit is contained in:
TomasVotruba 2020-03-29 16:09:49 +02:00
parent be4b34302f
commit a957eba2eb
4 changed files with 204 additions and 139 deletions

View File

@ -2,143 +2,4 @@
declare(strict_types=1);
use Nette\Utils\Strings;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\Yaml\Yaml;
require __DIR__ . '/../vendor/autoload.php';
$serviceExistenceConfigChecker = new ServiceExistenceConfigChecker();
$serviceExistenceConfigChecker->check();
final class ServiceExistenceConfigChecker
{
/**
* @var YamlConfigFileProvider
*/
private $yamlConfigFileProvider;
/**
* @var ServiceConfigurationValidator
*/
private $serviceConfigurationValidator;
public function __construct()
{
$this->yamlConfigFileProvider = new YamlConfigFileProvider();
$this->serviceConfigurationValidator = new ServiceConfigurationValidator();
}
public function check(): void
{
foreach ($this->yamlConfigFileProvider->provider() as $configFileInfo) {
$yamlContent = Yaml::parseFile($configFileInfo->getRealPath());
if (! isset($yamlContent['services'])) {
continue;
}
foreach ($yamlContent['services'] as $service => $serviceConfiguration) {
// configuration → skip
if (Strings::startsWith($service, '_')) {
continue;
}
// autodiscovery → skip
if (Strings::endsWith($service, '\\')) {
continue;
}
if (! ClassExistenceStaticHelper::doesClassLikeExist($service)) {
throw new ShouldNotHappenException(sprintf(
'Service "%s" from config "%s" was not found. Check if it really exists or is even autoload, please',
$service,
$configFileInfo->getRealPath()
));
}
$this->serviceConfigurationValidator->validate($service, $serviceConfiguration, $configFileInfo);
}
}
echo 'All configs have existing services - good job!' . PHP_EOL;
}
}
final class YamlConfigFileProvider
{
/**
* @return SplFileInfo[]
*/
public function provider(): array
{
$finder = (new Finder())->name('*.yaml')
->in(__DIR__ . '/../config')
->files();
return iterator_to_array($finder->getIterator());
}
}
final class ServiceConfigurationValidator
{
public function validate(string $serviceClass, $configuration, SplFileInfo $configFileInfo): void
{
if (! is_array($configuration)) {
return;
}
foreach (array_keys($configuration) as $key) {
if (! $this->isArgumentName($key)) {
continue;
}
$constructorParameterNames = $this->resolveClassConstructorArgumentNames($serviceClass);
if (in_array($key, $constructorParameterNames, true)) {
continue;
}
throw new ShouldNotHappenException(sprintf(
'Service "%s" has unused argument "%s" in config "%s".%sCorrect it to one of existing arguments "%s".',
$serviceClass,
$key,
$configFileInfo->getRealPath(),
PHP_EOL,
implode('", "', $constructorParameterNames)
));
}
}
private function isArgumentName($key): bool
{
if (! is_string($key)) {
return false;
}
return Strings::startsWith($key, '$');
}
/**
* @return string[]
*/
private function resolveClassConstructorArgumentNames(string $class): array
{
$reflectionClass = new ReflectionClass($class);
$constructorReflection = $reflectionClass->getConstructor();
if ($constructorReflection === null) {
return [];
}
$constructorParameterNames = [];
foreach ($constructorReflection->getParameters() as $parameterReflection) {
$constructorParameterNames[] = '$' . $parameterReflection->getName();
}
sort($constructorParameterNames);
return $constructorParameterNames;
}
}

View File

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\SetRunner\Command;
use Nette\Utils\Strings;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
use Rector\Utils\SetRunner\Validation\ServiceConfigurationValidator;
use Rector\Utils\SetRunner\Yaml\YamlConfigFileProvider;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Yaml\Yaml;
use Symplify\PackageBuilder\Console\Command\CommandNaming;
use Symplify\PackageBuilder\Console\ShellCode;
use Symplify\SmartFileSystem\SmartFileInfo;
final class ValidateServicesInSetsCommand extends Command
{
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
/**
* @var ServiceConfigurationValidator
*/
private $serviceConfigurationValidator;
/**
* @var YamlConfigFileProvider
*/
private $yamlConfigFileProvider;
public function __construct(
SymfonyStyle $symfonyStyle,
ServiceConfigurationValidator $serviceConfigurationValidator,
YamlConfigFileProvider $yamlConfigFileProvider
) {
$this->symfonyStyle = $symfonyStyle;
$this->serviceConfigurationValidator = $serviceConfigurationValidator;
$this->yamlConfigFileProvider = $yamlConfigFileProvider;
parent::__construct();
}
protected function configure(): void
{
$this->setName(CommandNaming::classToName(self::class));
$this->setDescription('[CI] Validate services in sets');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
foreach ($this->yamlConfigFileProvider->provider() as $configFileInfo) {
$this->symfonyStyle->note(sprintf('Validating config "%s"', $configFileInfo->getRelativeFilePathFromCwd()));
$yamlContent = Yaml::parseFile($configFileInfo->getRealPath());
if (! isset($yamlContent['services'])) {
continue;
}
foreach ($yamlContent['services'] as $service => $serviceConfiguration) {
$this->validateService($service, $serviceConfiguration, $configFileInfo);
}
}
$this->symfonyStyle->success('All configs have existing services');
return ShellCode::SUCCESS;
}
private function validateService($service, $serviceConfiguration, SmartFileInfo $configFileInfo): void
{
// configuration → skip
if (Strings::startsWith($service, '_')) {
return;
}
// autodiscovery → skip
if (Strings::endsWith($service, '\\')) {
return;
}
if (! ClassExistenceStaticHelper::doesClassLikeExist($service)) {
throw new ShouldNotHappenException(sprintf(
'Service "%s" from config "%s" was not found. Check if it really exists or is even autoload, please',
$service,
$configFileInfo->getRealPath()
));
}
$this->serviceConfigurationValidator->validate($service, $serviceConfiguration, $configFileInfo);
}
}

View File

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\SetRunner\Validation;
use Nette\Utils\Strings;
use Rector\Core\Exception\ShouldNotHappenException;
use ReflectionClass;
use Symplify\SmartFileSystem\SmartFileInfo;
final class ServiceConfigurationValidator
{
public function validate(string $serviceClass, $configuration, SmartFileInfo $configFileInfo): void
{
if (! is_array($configuration)) {
return;
}
foreach (array_keys($configuration) as $key) {
if (! $this->isArgumentName($key)) {
continue;
}
$constructorParameterNames = $this->resolveClassConstructorArgumentNames($serviceClass);
if (in_array($key, $constructorParameterNames, true)) {
continue;
}
throw new ShouldNotHappenException(sprintf(
'Service "%s" has unused argument "%s" in config "%s".%sCorrect it to one of existing arguments "%s".',
$serviceClass,
$key,
$configFileInfo->getRealPath(),
PHP_EOL,
implode('", "', $constructorParameterNames)
));
}
}
private function isArgumentName($key): bool
{
if (! is_string($key)) {
return false;
}
return Strings::startsWith($key, '$');
}
/**
* @return string[]
*/
private function resolveClassConstructorArgumentNames(string $class): array
{
$reflectionClass = new ReflectionClass($class);
$constructorReflection = $reflectionClass->getConstructor();
if ($constructorReflection === null) {
return [];
}
$constructorParameterNames = [];
foreach ($constructorReflection->getParameters() as $parameterReflection) {
$constructorParameterNames[] = '$' . $parameterReflection->getName();
}
sort($constructorParameterNames);
return $constructorParameterNames;
}
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\SetRunner\Yaml;
use Symfony\Component\Finder\Finder;
use Symplify\SmartFileSystem\Finder\FinderSanitizer;
use Symplify\SmartFileSystem\SmartFileInfo;
final class YamlConfigFileProvider
{
/**
* @var FinderSanitizer
*/
private $finderSanitizer;
public function __construct(FinderSanitizer $finderSanitizer)
{
$this->finderSanitizer = $finderSanitizer;
}
/**
* @return SmartFileInfo[]
*/
public function provider(): array
{
$finder = (new Finder())->name('*.yaml')
->in(__DIR__ . '/../../../../config')
->sortByName()
->files();
return $this->finderSanitizer->sanitize($finder);
}
}