Skip to content

Commit

Permalink
[TASK] Add extension.neon automatically (#1611)
Browse files Browse the repository at this point in the history
Resolves: #1608
  • Loading branch information
sabbelasichon authored Nov 17, 2020
1 parent 591ba88 commit 50ecd34
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 25 deletions.
2 changes: 2 additions & 0 deletions build/box.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"vendor/rector/rector/packages",
"config",
"src",
"utils/phpstan/config",
"utils/phpstan/src",
"Migrations"
],
"exclude-composer-files": false,
Expand Down
19 changes: 18 additions & 1 deletion config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

declare(strict_types=1);

use PHPStan\Analyser\NodeScopeResolver;
use PHPStan\Analyser\ScopeFactory;
use PHPStan\PhpDoc\TypeNodeResolver;
use PHPStan\Reflection\ReflectionProvider;
use Ssch\TYPO3Rector\Console\Application;
use Ssch\TYPO3Rector\DependencyInjection\PHPStanServicesFactory;
use Ssch\TYPO3Rector\Helper\Database\Refactorings\DatabaseConnectionToDbalRefactoring;
use Symfony\Component\Console\Application as SymfonyApplication;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;

return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
Expand All @@ -20,13 +26,24 @@

$services->alias(SymfonyApplication::class, Application::class);

$services->set(ReflectionProvider::class)
->factory([service(PHPStanServicesFactory::class), 'createReflectionProvider']);

$services->set(NodeScopeResolver::class)
->factory([service(PHPStanServicesFactory::class), 'createNodeScopeResolver']);

$services->set(ScopeFactory::class)
->factory([service(PHPStanServicesFactory::class), 'createScopeFactory']);

$services->set(TypeNodeResolver::class)
->factory([service(PHPStanServicesFactory::class), 'createTypeNodeResolver']);

$services->load('Ssch\\TYPO3Rector\\', __DIR__ . '/../src')
->exclude(
[
__DIR__ . '/../src/Rector',
__DIR__ . '/../src/Set',
__DIR__ . '/../src/Bootstrap',
__DIR__ . '/../src/DependencyInjection',
__DIR__ . '/../src/HttpKernel',
__DIR__ . '/../src/Compiler',
]
Expand Down
170 changes: 170 additions & 0 deletions src/DependencyInjection/PHPStanServicesFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<?php

declare(strict_types=1);

namespace Ssch\TYPO3Rector\DependencyInjection;

use Nette\Utils\Strings;
use PHPStan\Analyser\NodeScopeResolver;
use PHPStan\Analyser\ScopeFactory;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Dependency\DependencyResolver;
use PHPStan\DependencyInjection\Container;
use PHPStan\DependencyInjection\ContainerFactory;
use PHPStan\DependencyInjection\Type\DynamicReturnTypeExtensionRegistryProvider;
use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider;
use PHPStan\File\FileHelper;
use PHPStan\PhpDoc\TypeNodeResolver;
use PHPStan\Reflection\ReflectionProvider;
use PHPUnit\Framework\TestCase;
use Symplify\SmartFileSystem\SmartFileSystem;

/**
* Factory so Symfony app can use services from PHPStan container
* @see packages/NodeTypeResolver/config/config.yaml:17
*/
final class PHPStanServicesFactory
{
/**
* @see https://regex101.com/r/CWADBe/2
* @var string
*/
private const BLEEDING_EDGE_REGEX = '#\n\s+-(.*?)bleedingEdge\.neon[\'|"]?#';

/**
* @var Container
*/
private $container;

public function __construct()
{
$currentWorkingDirectory = getcwd();
$smartFileSystem = new SmartFileSystem();

$containerFactory = new ContainerFactory($currentWorkingDirectory);
$additionalConfigFiles = [];

// possible path collision for Docker
$additionalConfigFiles = $this->appendPhpstanPHPUnitExtensionIfExists(
$currentWorkingDirectory,
$additionalConfigFiles
);

$temporaryPHPStanNeon = null;

$currentProjectConfigFile = $currentWorkingDirectory . '/phpstan.neon';

if (file_exists($currentProjectConfigFile)) {
$phpstanNeonContent = $smartFileSystem->readFile($currentProjectConfigFile);

// bleeding edge clean out, see https://github.com/rectorphp/rector/issues/2431
if (Strings::match($phpstanNeonContent, self::BLEEDING_EDGE_REGEX)) {
// Note: We need a unique file per process if rector runs in parallel
$pid = getmypid();
$temporaryPHPStanNeon = $currentWorkingDirectory . '/rector-temp-phpstan' . $pid . '.neon';
$clearedPhpstanNeonContent = Strings::replace($phpstanNeonContent, self::BLEEDING_EDGE_REGEX);
$smartFileSystem->dumpFile($temporaryPHPStanNeon, $clearedPhpstanNeonContent);

$additionalConfigFiles[] = $temporaryPHPStanNeon;
} else {
$additionalConfigFiles[] = $currentProjectConfigFile;
}
}

$additionalConfigFiles[] = __DIR__ . '/../../vendor/rector/rector/packages/node-type-resolver/config/phpstan/type-extensions.neon';
$additionalConfigFiles[] = __DIR__ . '/../../utils/phpstan/config/extension.neon';

// enable type inferring from constructor
$additionalConfigFiles[] = __DIR__ . '/../../vendor/rector/rector/packages/node-type-resolver/config/phpstan/better-infer.neon';

$this->container = $containerFactory->create(sys_get_temp_dir(), $additionalConfigFiles, []);

// clear bleeding edge fallback
if (null !== $temporaryPHPStanNeon) {
$smartFileSystem->remove($temporaryPHPStanNeon);
}
}

/**
* @api
*/
public function createReflectionProvider(): ReflectionProvider
{
return $this->container->getByType(ReflectionProvider::class);
}

/**
* @api
*/
public function createNodeScopeResolver(): NodeScopeResolver
{
return $this->container->getByType(NodeScopeResolver::class);
}

/**
* @api
*/
public function createTypeSpecifier(): TypeSpecifier
{
return $this->container->getByType(TypeSpecifier::class);
}

/**
* @api
*/
public function createScopeFactory(): ScopeFactory
{
return $this->container->getByType(ScopeFactory::class);
}

/**
* @api
*/
public function createDynamicReturnTypeExtensionRegistryProvider(): DynamicReturnTypeExtensionRegistryProvider
{
return $this->container->getByType(DynamicReturnTypeExtensionRegistryProvider::class);
}

/**
* @api
*/
public function createDependencyResolver(): DependencyResolver
{
return $this->container->getByType(DependencyResolver::class);
}

/**
* @api
*/
public function createFileHelper(): FileHelper
{
return $this->container->getByType(FileHelper::class);
}

/**
* @api
*/
public function createOperatorTypeSpecifyingExtensionRegistryProvider(): OperatorTypeSpecifyingExtensionRegistryProvider
{
return $this->container->getByType(OperatorTypeSpecifyingExtensionRegistryProvider::class);
}

/**
* @api
*/
public function createTypeNodeResolver(): TypeNodeResolver
{
return $this->container->getByType(TypeNodeResolver::class);
}

private function appendPhpstanPHPUnitExtensionIfExists(
string $currentWorkingDirectory,
array $additionalConfigFiles
): array {
$phpstanPhpunitExtensionConfig = $currentWorkingDirectory . '/vendor/phpstan/phpstan-phpunit/extension.neon';
if (file_exists($phpstanPhpunitExtensionConfig) && class_exists(TestCase::class)) {
$additionalConfigFiles[] = $phpstanPhpunitExtensionConfig;
}
return $additionalConfigFiles;
}
}
13 changes: 13 additions & 0 deletions utils/phpstan/config/extension.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
services:
-
class: Ssch\TYPO3Rector\PHPStan\Type\GeneralUtilityDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
-
class: Ssch\TYPO3Rector\PHPStan\Type\ObjectManagerDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: Ssch\TYPO3Rector\PHPStan\Type\ContextGetAspectDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
10 changes: 10 additions & 0 deletions utils/phpstan/config/rules.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
-
class: Ssch\TYPO3Rector\PHPStan\Rules\AddSeeDocBlockForRectorClass
tags:
- phpstan.rules.rule

-
class: Ssch\TYPO3Rector\PHPStan\Rules\AddCodeCoverageIgnoreForRectorDefinition
tags:
- phpstan.rules.rule
27 changes: 3 additions & 24 deletions utils/phpstan/config/typo3-rector.neon
Original file line number Diff line number Diff line change
@@ -1,24 +1,3 @@
services:
-
class: Ssch\TYPO3Rector\PHPStan\Rules\AddSeeDocBlockForRectorClass
tags:
- phpstan.rules.rule

-
class: Ssch\TYPO3Rector\PHPStan\Rules\AddCodeCoverageIgnoreForRectorDefinition
tags:
- phpstan.rules.rule

-
class: Ssch\TYPO3Rector\PHPStan\Type\GeneralUtilityDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension

-
class: Ssch\TYPO3Rector\PHPStan\Type\ObjectManagerDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: Ssch\TYPO3Rector\PHPStan\Type\ContextGetAspectDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
includes:
- rules.neon
- extension.neon

0 comments on commit 50ecd34

Please sign in to comment.