Skip to content

Commit

Permalink
Fix external packages bleeding edge includes (#3389)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Feb 17, 2023
1 parent d8aa22b commit 9c6f41e
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\NodeTypeResolver\DependencyInjection\BleedingEdgeIncludePurifier;

use Nette\Utils\FileSystem;
use Rector\NodeTypeResolver\DependencyInjection\BleedingEdgeIncludePurifier;
use Rector\Testing\PHPUnit\AbstractTestCase;

final class BleedingEdgeIncludePurifierTest extends AbstractTestCase
{
private BleedingEdgeIncludePurifier $bleedingEdgeIncludePurifier;

protected function setUp(): void
{
$this->boot();

$this->bleedingEdgeIncludePurifier = $this->getService(BleedingEdgeIncludePurifier::class);
}

public function testNothing(): void
{
$purifiedConfigFilePath = $this->bleedingEdgeIncludePurifier->purifyConfigFile(
__DIR__ . '/Fixture/no_bleeding_edge.neon'
);
$this->assertNull($purifiedConfigFilePath);
}

public function test(): void
{
$purifiedConfigFilePath = $this->bleedingEdgeIncludePurifier->purifyConfigFile(
__DIR__ . '/Fixture/some_file_including.neon'
);

$this->assertNotNull($purifiedConfigFilePath);

$this->assertFileEquals(__DIR__ . '/Expected/some_file_including.neon', $purifiedConfigFilePath);

// cleanup after yourself :)
FileSystem::delete($purifiedConfigFilePath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
includes:

parameters:
one: two
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
parameters:
one: two
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- %rootDir%/conf/bleedingEdge.neon

parameters:
one: two
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\NodeTypeResolver\DependencyInjection;

use PHPStan\Parser\Parser;
use Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory;
use Rector\Testing\PHPUnit\AbstractTestCase;

final class PHPStanServiceFactoryTest extends AbstractTestCase
{
private PHPStanServicesFactory $phpStanServicesFactory;

protected function setUp(): void
{
$this->boot();

$this->phpStanServicesFactory = $this->getService(PHPStanServicesFactory::class);
}

public function test(): void
{
$phpstanParser = $this->phpStanServicesFactory->createPHPStanParser();
$this->assertInstanceOf(Parser::class, $phpstanParser);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Rector\NodeTypeResolver\DependencyInjection;

use Nette\Utils\FileSystem;
use Nette\Utils\Strings;

/**
* Prevents failing include of bleeding edge in of phpstan extensions.
* @see https://github.com/rectorphp/rector/issues/2431
*
* Do not delete this. It's not tested here, but is needed to avoid this rare case happen. It's not possible to solve otherwise.
* @see https://github.com/rectorphp/rector-src/commit/70fb9af2fdfa55db63c68c6dac6723fe55cec1a0
* @eee https://github.com/rectorphp/rector/pull/2550
*
* @see \Rector\Tests\NodeTypeResolver\DependencyInjection\BleedingEdgeIncludePurifier\BleedingEdgeIncludePurifierTest
*/
final class BleedingEdgeIncludePurifier
{
/**
* @see https://regex101.com/r/CWADBe/2
* @var string
*/
private const BLEEDING_EDGE_REGEX = '#\n\s+-(.*?)bleedingEdge\.neon[\'|"]?#';

public function purifyConfigFile(string $filePath): ?string
{
// must be neon file
if (! str_ends_with($filePath, '.neon')) {
return null;
}

$fileContents = FileSystem::read($filePath);

// bleeding edge clean out, see https://github.com/rectorphp/rector/issues/2431
$matches = Strings::match($fileContents, self::BLEEDING_EDGE_REGEX);
if ($matches === null) {
return null;
}

$temporaryFilePath = $this->createTemporaryFilePath($filePath);

$clearedFileContents = Strings::replace($fileContents, self::BLEEDING_EDGE_REGEX);
FileSystem::write($temporaryFilePath, $clearedFileContents);

return $temporaryFilePath;
}

private function createTemporaryFilePath(string $filePath): string
{
$fileDirectory = dirname($filePath);
$baseFileName = pathinfo($filePath, PATHINFO_BASENAME);

return $fileDirectory . '/temp_' . $baseFileName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,44 @@
use Rector\Core\Configuration\Option;
use Rector\Core\Configuration\Parameter\ParameterProvider;
use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider;
use Symfony\Component\Filesystem\Filesystem;

/**
* Factory so Symfony app can use services from PHPStan container
*
* @see \Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory
*/
final class PHPStanServicesFactory
{
private readonly Container $container;

public function __construct(
ParameterProvider $parameterProvider,
PHPStanExtensionsConfigResolver $phpStanExtensionsConfigResolver,
private readonly ParameterProvider $parameterProvider,
private readonly PHPStanExtensionsConfigResolver $phpStanExtensionsConfigResolver,
BleedingEdgeIncludePurifier $bleedingEdgeIncludePurifier,
) {
$containerFactory = new ContainerFactory(getcwd());
$additionalConfigFiles = $this->resolveAdditionalConfigFiles();

$additionalConfigFiles = [];
$purifiedConfigFiles = [];

if ($parameterProvider->hasParameter(Option::PHPSTAN_FOR_RECTOR_PATH)) {
$additionalConfigFiles[] = $parameterProvider->provideStringParameter(Option::PHPSTAN_FOR_RECTOR_PATH);
}
foreach ($additionalConfigFiles as $key => $additionalConfigFile) {
$purifiedConfigFile = $bleedingEdgeIncludePurifier->purifyConfigFile($additionalConfigFile);

$additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/static-reflection.neon';
$additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/better-infer.neon';
$additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/parser.neon';
// nothing was changed
if ($purifiedConfigFile === null) {
continue;
}

$extensionConfigFiles = $phpStanExtensionsConfigResolver->resolve();
$additionalConfigFiles = array_merge($additionalConfigFiles, $extensionConfigFiles);
$additionalConfigFiles[$key] = $purifiedConfigFile;
$purifiedConfigFiles[] = $purifiedConfigFile;
}

$existingAdditionalConfigFiles = array_filter($additionalConfigFiles, 'file_exists');
$containerFactory = new ContainerFactory(getcwd());
$this->container = $containerFactory->create(sys_get_temp_dir(), $additionalConfigFiles, []);

$this->container = $containerFactory->create(sys_get_temp_dir(), $existingAdditionalConfigFiles, []);
// clear temporary files, after container is created
$filesystem = new Filesystem();
$filesystem->remove($purifiedConfigFiles);
}

/**
Expand Down Expand Up @@ -120,4 +128,27 @@ public function createDynamicSourceLocatorProvider(): DynamicSourceLocatorProvid
{
return $this->container->getByType(DynamicSourceLocatorProvider::class);
}

/**
* @return string[]
*/
private function resolveAdditionalConfigFiles(): array
{
$additionalConfigFiles = [];

if ($this->parameterProvider->hasParameter(Option::PHPSTAN_FOR_RECTOR_PATH)) {
$additionalConfigFiles[] = $this->parameterProvider->provideStringParameter(
Option::PHPSTAN_FOR_RECTOR_PATH
);
}

$additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/static-reflection.neon';
$additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/better-infer.neon';
$additionalConfigFiles[] = __DIR__ . '/../../../config/phpstan/parser.neon';

$extensionConfigFiles = $this->phpStanExtensionsConfigResolver->resolve();
$additionalConfigFiles = array_merge($additionalConfigFiles, $extensionConfigFiles);

return array_filter($additionalConfigFiles, 'file_exists');
}
}

0 comments on commit 9c6f41e

Please sign in to comment.