mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-17 13:28:18 +01:00
add Standalone
This commit is contained in:
parent
4fd551bdad
commit
0a5efeaf84
@ -8,17 +8,14 @@ function includeProjectsAutoload(string $composerJsonPath, string $cwd): void
|
||||
|
||||
$composerSettings = json_decode($contents, true);
|
||||
if (! is_array($composerSettings)) {
|
||||
fwrite(STDERR, "Failed to load '${composerJsonPath}'\n");
|
||||
exit(1);
|
||||
die(sprintf('Failed to load "%s"', $composerJsonPath));
|
||||
}
|
||||
|
||||
$vendorPath = $composerSettings['config']['vendor-dir'] ?? $cwd . '/vendor';
|
||||
if (! is_dir($vendorPath)) {
|
||||
fwrite(STDERR, "Please check if 'composer.phar install' was run already (expected to find '${vendorPath}')\n");
|
||||
exit(1);
|
||||
die(sprintf('Please check if "composer install" was run already (expected to find "%s")', $vendorPath));
|
||||
}
|
||||
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require $vendorPath . '/autoload.php';
|
||||
}
|
||||
|
||||
@ -29,6 +26,7 @@ if (is_file($projectAutoload)) {
|
||||
require $projectAutoload;
|
||||
}
|
||||
|
||||
// is autolaod successful?
|
||||
if (class_exists('Rector\HttpKernel\RectorKernel')) {
|
||||
return;
|
||||
}
|
||||
@ -53,11 +51,9 @@ if (file_exists($composerJsonPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(
|
||||
STDERR,
|
||||
die(
|
||||
sprintf(
|
||||
'Composer autoload.php was not found in paths "%s". Have you ran "composer update"?',
|
||||
implode('", "', $possibleAutoloadPaths)
|
||||
)
|
||||
);
|
||||
exit(1);
|
||||
|
@ -11,6 +11,7 @@
|
||||
"bin": ["bin/rector"],
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"ext-json": "*",
|
||||
"composer/xdebug-handler": "^1.3",
|
||||
"doctrine/annotations": "^1.6",
|
||||
"doctrine/inflector": "^1.3",
|
||||
|
@ -40,6 +40,11 @@ final class SetOptionResolver
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->detectFromNameAndDirectory($setName, $configDirectory);
|
||||
}
|
||||
|
||||
public function detectFromNameAndDirectory(string $setName, string $configDirectory): ?string
|
||||
{
|
||||
$nearestMatches = $this->findNearestMatchingFiles($configDirectory, $setName);
|
||||
if (count($nearestMatches) === 0) {
|
||||
$this->reportSetNotFound($configDirectory, $setName);
|
||||
@ -57,18 +62,16 @@ final class SetOptionResolver
|
||||
|
||||
$suggestedSet = ObjectHelpers::getSuggestion($allSets, $setName);
|
||||
|
||||
$hasSetVersion = (bool) Strings::match($setName, '#[\d]#');
|
||||
|
||||
[$versionedSets, $unversionedSets] = $this->separateVersionedAndUnversionedSets($allSets);
|
||||
|
||||
$setsListInString = $this->createSetListInString($hasSetVersion, $unversionedSets, $versionedSets);
|
||||
$setsListInString = $this->createSetListInString($unversionedSets, $versionedSets);
|
||||
|
||||
$setNotFoundMessage = sprintf(
|
||||
'%s "%s" was not found.%s%s',
|
||||
ucfirst($this->keyName),
|
||||
$setName,
|
||||
PHP_EOL,
|
||||
$suggestedSet ? sprintf('Did you mean "%s"?', $suggestedSet) . PHP_EOL : 'Pick one of above.'
|
||||
$suggestedSet ? sprintf('Did you mean "%s"?', $suggestedSet) . PHP_EOL : ''
|
||||
);
|
||||
|
||||
$pickOneOfMessage = sprintf('Pick "--%s" of:%s%s', $this->keyName, PHP_EOL . PHP_EOL, $setsListInString);
|
||||
@ -152,24 +155,18 @@ final class SetOptionResolver
|
||||
* @param string[] $unversionedSets
|
||||
* @param string[] $versionedSets
|
||||
*/
|
||||
private function createSetListInString(
|
||||
bool $hasSetVersion,
|
||||
array $unversionedSets,
|
||||
array $versionedSets
|
||||
): string {
|
||||
private function createSetListInString(array $unversionedSets, array $versionedSets): string
|
||||
{
|
||||
$setsListInString = '';
|
||||
|
||||
if ($hasSetVersion === false) {
|
||||
foreach ($unversionedSets as $unversionedSet) {
|
||||
$setsListInString .= ' * ' . $unversionedSet . PHP_EOL;
|
||||
}
|
||||
foreach ($unversionedSets as $unversionedSet) {
|
||||
$setsListInString .= ' * ' . $unversionedSet . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($hasSetVersion) {
|
||||
foreach ($versionedSets as $groupName => $configName) {
|
||||
$setsListInString .= ' * ' . $groupName . ': ' . implode(', ', $configName) . PHP_EOL;
|
||||
}
|
||||
foreach ($versionedSets as $groupName => $configName) {
|
||||
$setsListInString .= ' * ' . $groupName . ': ' . implode(', ', $configName) . PHP_EOL;
|
||||
}
|
||||
|
||||
return $setsListInString;
|
||||
}
|
||||
|
||||
@ -183,12 +180,16 @@ final class SetOptionResolver
|
||||
$unversionedSets = [];
|
||||
|
||||
foreach ($allSets as $set) {
|
||||
$match = Strings::match($set, '#^[A-Za-z\-]+#');
|
||||
if ($match === null) {
|
||||
$hasVersion = (bool) Strings::match($set, '#\d#');
|
||||
|
||||
if ($hasVersion === false) {
|
||||
$unversionedSets[] = $set;
|
||||
continue;
|
||||
}
|
||||
|
||||
$setWithoutVersion = rtrim($match[0], '-');
|
||||
$match = Strings::match($set, '#^(?<set>[A-Za-z\-]+)#');
|
||||
$setWithoutVersion = $match['set'];
|
||||
|
||||
if ($setWithoutVersion !== $set) {
|
||||
$versionedSets[$setWithoutVersion][] = $set;
|
||||
}
|
||||
|
71
src/DependencyInjection/RectorContainerFactory.php
Normal file
71
src/DependencyInjection/RectorContainerFactory.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\DependencyInjection;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Rector\Console\Option\SetOptionResolver;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\HttpKernel\RectorKernel;
|
||||
use Rector\Set\Set;
|
||||
use Symplify\PackageBuilder\Configuration\ConfigFileFinder;
|
||||
|
||||
final class RectorContainerFactory
|
||||
{
|
||||
/**
|
||||
* @var SetOptionResolver
|
||||
*/
|
||||
private $setOptionResolver;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setOptionResolver = new SetOptionResolver();
|
||||
}
|
||||
|
||||
public function createFromSet(string $set): ContainerInterface
|
||||
{
|
||||
$configFiles = $this->resolveConfigs($set);
|
||||
|
||||
return $this->createFromConfigs($configFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveConfigs(string $set): array
|
||||
{
|
||||
$config = $this->setOptionResolver->detectFromNameAndDirectory($set, Set::SET_DIRECTORY);
|
||||
if ($config === null) {
|
||||
throw new ShouldNotHappenException(sprintf('Config file for "%s" set was not found', $set));
|
||||
}
|
||||
|
||||
// copied mostly from https://github.com/rectorphp/rector/blob/master/bin/container.php
|
||||
$configFiles = [];
|
||||
$configFiles[] = $config;
|
||||
// local config has priority
|
||||
$configFiles[] = ConfigFileFinder::provide('rector', ['rector.yml', 'rector.yaml']);
|
||||
|
||||
// remove empty values
|
||||
return array_filter($configFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $configFiles
|
||||
*/
|
||||
private function createFromConfigs(array $configFiles): ContainerInterface
|
||||
{
|
||||
// to override the configs without clearing cache
|
||||
$environment = 'prod' . random_int(1, 10000000);
|
||||
$isDebug = true;
|
||||
|
||||
$rectorKernel = new RectorKernel($environment, $isDebug);
|
||||
if ($configFiles) {
|
||||
$rectorKernel->setConfigs($configFiles);
|
||||
}
|
||||
|
||||
$rectorKernel->boot();
|
||||
|
||||
return $rectorKernel->getContainer();
|
||||
}
|
||||
}
|
13
src/Set/Set.php
Normal file
13
src/Set/Set.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Set;
|
||||
|
||||
final class Set
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const SET_DIRECTORY = __DIR__ . '/../../config/set';
|
||||
}
|
@ -14,7 +14,7 @@ final class SetProvider
|
||||
public function provide(): array
|
||||
{
|
||||
$finder = Finder::create()->files()
|
||||
->in(__DIR__ . '/../../config/set');
|
||||
->in(Set::SET_DIRECTORY);
|
||||
|
||||
$sets = [];
|
||||
foreach ($finder->getIterator() as $fileInfo) {
|
||||
|
165
src/Standalone/RectorStandaloneRunner.php
Normal file
165
src/Standalone/RectorStandaloneRunner.php
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Standalone;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Rector\Application\ErrorAndDiffCollector;
|
||||
use Rector\Application\RectorApplication;
|
||||
use Rector\Autoloading\AdditionalAutoloader;
|
||||
use Rector\Configuration\Configuration;
|
||||
use Rector\Configuration\Option;
|
||||
use Rector\Console\Command\ProcessCommand;
|
||||
use Rector\Console\Output\ConsoleOutputFormatter;
|
||||
use Rector\DependencyInjection\RectorContainerFactory;
|
||||
use Rector\Exception\FileSystem\FileNotFoundException;
|
||||
use Rector\Extension\FinishingExtensionRunner;
|
||||
use Rector\Extension\ReportingExtensionRunner;
|
||||
use Rector\FileSystem\FilesFinder;
|
||||
use Rector\Guard\RectorGuard;
|
||||
use Rector\PhpParser\NodeTraverser\RectorNodeTraverser;
|
||||
use Rector\Stubs\StubLoader;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* This class is needed over process/cli run to get console output in sane way;
|
||||
* without it, it's not possible to get inside output closed stream.
|
||||
*/
|
||||
final class RectorStandaloneRunner
|
||||
{
|
||||
/**
|
||||
* @var RectorContainerFactory
|
||||
*/
|
||||
private $rectorContainerFactory;
|
||||
|
||||
/**
|
||||
* @var SymfonyStyle
|
||||
*/
|
||||
private $nativeSymfonyStyle;
|
||||
|
||||
public function __construct(RectorContainerFactory $rectorContainerFactory, SymfonyStyle $symfonyStyle)
|
||||
{
|
||||
$this->rectorContainerFactory = $rectorContainerFactory;
|
||||
$this->nativeSymfonyStyle = $symfonyStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $source
|
||||
*/
|
||||
public function processSourceWithSet(array $source, string $set, bool $isDryRun): void
|
||||
{
|
||||
$source = $this->absolutizeSource($source);
|
||||
|
||||
$container = $this->rectorContainerFactory->createFromSet($set);
|
||||
$this->prepare($container, $source, $isDryRun);
|
||||
|
||||
/** @var FilesFinder $filesFinder */
|
||||
$filesFinder = $container->get(FilesFinder::class);
|
||||
$phpFileInfos = $filesFinder->findInDirectoriesAndFiles($source, ['php']);
|
||||
|
||||
/** @var RectorApplication $rectorApplication */
|
||||
$rectorApplication = $container->get(RectorApplication::class);
|
||||
$rectorApplication->runOnFileInfos($phpFileInfos);
|
||||
|
||||
$this->reportErrors($container);
|
||||
|
||||
$this->finish($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mostly copied from: https://github.com/rectorphp/rector/blob/master/src/Console/Command/ProcessCommand.php.
|
||||
* @param string[] $source
|
||||
*/
|
||||
private function prepare(ContainerInterface $container, array $source, bool $isDryRun): void
|
||||
{
|
||||
ini_set('memory_limit', '4096M');
|
||||
|
||||
/** @var RectorNodeTraverser $rectorNodeTraverser */
|
||||
$rectorNodeTraverser = $container->get(RectorNodeTraverser::class);
|
||||
$this->prepareConfiguration($container, $rectorNodeTraverser, $isDryRun);
|
||||
|
||||
/** @var RectorGuard $rectorGuard */
|
||||
$rectorGuard = $container->get(RectorGuard::class);
|
||||
$rectorGuard->ensureSomeRectorsAreRegistered();
|
||||
|
||||
// setup verbosity from the current run
|
||||
/** @var SymfonyStyle $symfonyStyle */
|
||||
$symfonyStyle = $container->get(SymfonyStyle::class);
|
||||
$symfonyStyle->setVerbosity($this->nativeSymfonyStyle->getVerbosity());
|
||||
|
||||
/** @var AdditionalAutoloader $additionalAutoloader */
|
||||
$additionalAutoloader = $container->get(AdditionalAutoloader::class);
|
||||
$additionalAutoloader->autoloadWithInputAndSource(new ArrayInput([]), $source);
|
||||
|
||||
/** @var StubLoader $stubLoader */
|
||||
$stubLoader = $container->get(StubLoader::class);
|
||||
$stubLoader->loadStubs();
|
||||
}
|
||||
|
||||
private function reportErrors(ContainerInterface $container): void
|
||||
{
|
||||
/** @var ErrorAndDiffCollector $errorAndDiffCollector */
|
||||
$errorAndDiffCollector = $container->get(ErrorAndDiffCollector::class);
|
||||
|
||||
/** @var ConsoleOutputFormatter $consoleOutputFormatter */
|
||||
$consoleOutputFormatter = $container->get(ConsoleOutputFormatter::class);
|
||||
$consoleOutputFormatter->report($errorAndDiffCollector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $source
|
||||
* @return string[]
|
||||
*/
|
||||
private function absolutizeSource(array $source): array
|
||||
{
|
||||
foreach ($source as $key => $singleSource) {
|
||||
/** @var string $singleSource */
|
||||
if (! file_exists($singleSource)) {
|
||||
throw new FileNotFoundException($singleSource);
|
||||
}
|
||||
|
||||
/** @var string $realpath */
|
||||
$realpath = realpath($singleSource);
|
||||
$source[$key] = $realpath;
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
private function finish(ContainerInterface $container): void
|
||||
{
|
||||
/** @var FinishingExtensionRunner $finishingExtensionRunner */
|
||||
$finishingExtensionRunner = $container->get(FinishingExtensionRunner::class);
|
||||
$finishingExtensionRunner->run();
|
||||
|
||||
/** @var ReportingExtensionRunner $reportingExtensionRunner */
|
||||
$reportingExtensionRunner = $container->get(ReportingExtensionRunner::class);
|
||||
$reportingExtensionRunner->run();
|
||||
}
|
||||
|
||||
private function prepareConfiguration(
|
||||
ContainerInterface $container,
|
||||
RectorNodeTraverser $rectorNodeTraverser,
|
||||
bool $isDryRun
|
||||
): void {
|
||||
/** @var Configuration $configuration */
|
||||
$configuration = $container->get(Configuration::class);
|
||||
|
||||
$configuration->setAreAnyPhpRectorsLoaded((bool) $rectorNodeTraverser->getPhpRectorCount());
|
||||
|
||||
// definition mimics @see ProcessCommand definition
|
||||
/** @var ProcessCommand $processCommand */
|
||||
$processCommand = $container->get(ProcessCommand::class);
|
||||
$definition = clone $processCommand->getDefinition();
|
||||
|
||||
// reset arguments to prevent "source is missing"
|
||||
$definition->setArguments([]);
|
||||
|
||||
$configuration->resolveFromInput(new ArrayInput([
|
||||
'--' . Option::OPTION_DRY_RUN => $isDryRun,
|
||||
'--' . Option::OPTION_OUTPUT_FORMAT => 'console',
|
||||
], $definition));
|
||||
}
|
||||
}
|
29
tests/DependencyInjection/RectorContainerFactoryTest.php
Normal file
29
tests/DependencyInjection/RectorContainerFactoryTest.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\DependencyInjection;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Rector\DependencyInjection\RectorContainerFactory;
|
||||
use Rector\Exception\Configuration\SetNotFoundException;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
final class RectorContainerFactoryTest extends TestCase
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$rectorContainerFactory = new RectorContainerFactory();
|
||||
|
||||
$rectorContainer = $rectorContainerFactory->createFromSet('doctrine');
|
||||
$this->assertInstanceOf(ContainerInterface::class, $rectorContainer);
|
||||
}
|
||||
|
||||
public function testMissingSet(): void
|
||||
{
|
||||
$rectorContainerFactory = new RectorContainerFactory();
|
||||
|
||||
$this->expectException(SetNotFoundException::class);
|
||||
$rectorContainerFactory->createFromSet('bla');
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user