mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-26 12:52:36 +00:00
Merge pull request #2718 from rectorphp/cakephp-templates
[CakePHPToSymfony] Add h function templates
This commit is contained in:
commit
8f8d44b527
|
@ -11,3 +11,4 @@ services:
|
|||
# .tpl templates to twig
|
||||
Rector\CakePHPToSymfony\Rector\Echo_\CakePHPTemplateLinkToTwigRector: null
|
||||
Rector\CakePHPToSymfony\Rector\Echo_\CakePHPTemplateTranslateToTwigRector: null
|
||||
Rector\CakePHPToSymfony\Rector\Echo_\CakePHPTemplateHToTwigRector: null
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# All 440 Rectors Overview
|
||||
# All 441 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
- [General](#general)
|
||||
|
@ -422,6 +422,19 @@ Migrate CakePHP 2.4 Controller to Symfony 5
|
|||
|
||||
<br>
|
||||
|
||||
### `CakePHPTemplateHToTwigRector`
|
||||
|
||||
- class: `Rector\CakePHPToSymfony\Rector\Echo_\CakePHPTemplateHToTwigRector`
|
||||
|
||||
Migrate CakePHP 2.4 h() function calls to Twig
|
||||
|
||||
```diff
|
||||
-<h3><?php echo h($value); ?></h3>
|
||||
+<h3>{{ value|escape }}</h3>
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### `CakePHPTemplateLinkToTwigRector`
|
||||
|
||||
- class: `Rector\CakePHPToSymfony\Rector\Echo_\CakePHPTemplateLinkToTwigRector`
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CakePHPToSymfony\Rector\Echo_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\Echo_;
|
||||
use PhpParser\Node\Stmt\InlineHTML;
|
||||
use Rector\CakePHPToSymfony\Rector\AbstractCakePHPRector;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
||||
/**
|
||||
* @see \Rector\CakePHPToSymfony\Tests\Rector\Echo_\CakePHPTemplateHToTwigRector\CakePHPTemplateHToTwigRectorTest
|
||||
*/
|
||||
final class CakePHPTemplateHToTwigRector extends AbstractCakePHPRector
|
||||
{
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Migrate CakePHP 2.4 h() function calls to Twig', [
|
||||
new CodeSample('<h3><?php echo h($value); ?></h3>', '<h3>{{ value|escape }}</h3>'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Echo_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Echo_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! isset($node->exprs[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$singleEchoedExpr = $node->exprs[0];
|
||||
if (! $singleEchoedExpr instanceof FuncCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isName($singleEchoedExpr, 'h')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$funcArg = $singleEchoedExpr->args[0]->value;
|
||||
if ($funcArg instanceof Variable) {
|
||||
$templateVariable = $this->getName($funcArg);
|
||||
} else {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
$htmlContent = sprintf('{{ %s|escape }}', $templateVariable);
|
||||
|
||||
return new InlineHTML($htmlContent);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CakePHPToSymfony\Tests\Rector\Echo_\CakePHPTemplateHToTwigRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\CakePHPToSymfony\Rector\Echo_\CakePHPTemplateHToTwigRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class CakePHPTemplateHToTwigRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideDataForTest()
|
||||
*/
|
||||
public function test(string $file): void
|
||||
{
|
||||
$this->doTestFileWithoutAutoload($file);
|
||||
}
|
||||
|
||||
public function provideDataForTest(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return CakePHPTemplateHToTwigRector::class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<h3><?php echo h($value); ?></h3>
|
||||
-----
|
||||
<h3>{{ value|escape }}</h3>
|
|
@ -8,26 +8,20 @@ use Nette\Utils\FileSystem;
|
|||
use Nette\Utils\Strings;
|
||||
use Rector\Utils\RectorGenerator\Composer\ComposerPackageAutoloadUpdater;
|
||||
use Rector\Utils\RectorGenerator\Configuration\ConfigurationFactory;
|
||||
use Rector\Utils\RectorGenerator\FileSystem\TemplateFileSystem;
|
||||
use Rector\Utils\RectorGenerator\Finder\TemplateFinder;
|
||||
use Rector\Utils\RectorGenerator\TemplateVariablesFactory;
|
||||
use Rector\Utils\RectorGenerator\ValueObject\Configuration;
|
||||
use Rector\Utils\RectorGenerator\ValueObject\Package;
|
||||
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\Finder\Finder;
|
||||
use Symplify\PackageBuilder\Console\Command\CommandNaming;
|
||||
use Symplify\PackageBuilder\Console\ShellCode;
|
||||
use Symplify\SmartFileSystem\Finder\FinderSanitizer;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class CreateRectorCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const TEMPLATES_DIRECTORY = __DIR__ . '/../../templates';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
@ -53,11 +47,6 @@ final class CreateRectorCommand extends Command
|
|||
*/
|
||||
private $configurationFactory;
|
||||
|
||||
/**
|
||||
* @var FinderSanitizer
|
||||
*/
|
||||
private $finderSanitizer;
|
||||
|
||||
/**
|
||||
* @var TemplateVariablesFactory
|
||||
*/
|
||||
|
@ -73,29 +62,42 @@ final class CreateRectorCommand extends Command
|
|||
*/
|
||||
private $composerPackageAutoloadUpdater;
|
||||
|
||||
/**
|
||||
* @var TemplateFinder
|
||||
*/
|
||||
private $templateFinder;
|
||||
|
||||
/**
|
||||
* @var TemplateFileSystem
|
||||
*/
|
||||
private $templateFileSystem;
|
||||
|
||||
/**
|
||||
* @param mixed[] $rectorRecipe
|
||||
*/
|
||||
public function __construct(
|
||||
SymfonyStyle $symfonyStyle,
|
||||
ConfigurationFactory $configurationFactory,
|
||||
FinderSanitizer $finderSanitizer,
|
||||
TemplateVariablesFactory $templateVariablesFactory,
|
||||
ComposerPackageAutoloadUpdater $composerPackageAutoloadUpdater,
|
||||
TemplateFinder $templateFinder,
|
||||
TemplateFileSystem $templateFileSystem,
|
||||
array $rectorRecipe
|
||||
) {
|
||||
parent::__construct();
|
||||
$this->symfonyStyle = $symfonyStyle;
|
||||
$this->configurationFactory = $configurationFactory;
|
||||
$this->finderSanitizer = $finderSanitizer;
|
||||
$this->templateVariablesFactory = $templateVariablesFactory;
|
||||
$this->rectorRecipe = $rectorRecipe;
|
||||
$this->composerPackageAutoloadUpdater = $composerPackageAutoloadUpdater;
|
||||
$this->templateFinder = $templateFinder;
|
||||
$this->templateFileSystem = $templateFileSystem;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setName(CommandNaming::classToName(self::class));
|
||||
$this->setAliases(['c']);
|
||||
$this->setDescription('[Dev] Create a new Rector, in a proper location, with new tests');
|
||||
}
|
||||
|
||||
|
@ -107,7 +109,7 @@ final class CreateRectorCommand extends Command
|
|||
// setup psr-4 autoload, if not already in
|
||||
$this->composerPackageAutoloadUpdater->processComposerAutoload($configuration);
|
||||
|
||||
$templateFileInfos = $this->findTemplateFileInfos();
|
||||
$templateFileInfos = $this->templateFinder->find($configuration->isPhpSnippet());
|
||||
$isUnwantedOverride = $this->isUnwantedOverride($templateFileInfos, $templateVariables, $configuration);
|
||||
|
||||
if ($isUnwantedOverride) {
|
||||
|
@ -117,8 +119,12 @@ final class CreateRectorCommand extends Command
|
|||
return ShellCode::SUCCESS;
|
||||
}
|
||||
|
||||
foreach ($this->findTemplateFileInfos() as $smartFileInfo) {
|
||||
$destination = $this->resolveDestination($smartFileInfo, $templateVariables, $configuration);
|
||||
foreach ($templateFileInfos as $smartFileInfo) {
|
||||
$destination = $this->templateFileSystem->resolveDestination(
|
||||
$smartFileInfo,
|
||||
$templateVariables,
|
||||
$configuration
|
||||
);
|
||||
|
||||
$content = $this->resolveContent($smartFileInfo, $templateVariables);
|
||||
|
||||
|
@ -153,7 +159,11 @@ final class CreateRectorCommand extends Command
|
|||
Configuration $configuration
|
||||
) {
|
||||
foreach ($templateFileInfos as $templateFileInfo) {
|
||||
$destination = $this->resolveDestination($templateFileInfo, $templateVariables, $configuration);
|
||||
$destination = $this->templateFileSystem->resolveDestination(
|
||||
$templateFileInfo,
|
||||
$templateVariables,
|
||||
$configuration
|
||||
);
|
||||
|
||||
if (! file_exists($destination)) {
|
||||
continue;
|
||||
|
@ -165,45 +175,6 @@ final class CreateRectorCommand extends Command
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SmartFileInfo[]
|
||||
*/
|
||||
private function findTemplateFileInfos(): array
|
||||
{
|
||||
$finder = Finder::create()->files()
|
||||
->in(self::TEMPLATES_DIRECTORY);
|
||||
|
||||
return $this->finderSanitizer->sanitize($finder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $templateVariables
|
||||
*/
|
||||
private function resolveDestination(
|
||||
SmartFileInfo $smartFileInfo,
|
||||
array $templateVariables,
|
||||
Configuration $configuration
|
||||
): string {
|
||||
$destination = $smartFileInfo->getRelativeFilePathFromDirectory(self::TEMPLATES_DIRECTORY);
|
||||
|
||||
// normalize core package
|
||||
if ($configuration->getPackage() === 'Rector') {
|
||||
$destination = Strings::replace($destination, '#packages\/_Package_/tests/Rector#', 'tests/Rector');
|
||||
$destination = Strings::replace($destination, '#packages\/_Package_/src/Rector#', 'src/Rector');
|
||||
}
|
||||
|
||||
// special keyword for 3rd party Rectors, not for core Github contribution
|
||||
if ($configuration->getPackage() === Package::UTILS) {
|
||||
$destination = Strings::replace($destination, '#packages\/_Package_#', 'utils/rector');
|
||||
}
|
||||
|
||||
if (! Strings::match($destination, '#fixture[\d+]*\.php\.inc#')) {
|
||||
$destination = rtrim($destination, '.inc');
|
||||
}
|
||||
|
||||
return $this->applyVariables($destination, $templateVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $templateVariables
|
||||
*/
|
||||
|
|
|
@ -48,10 +48,11 @@ final class ConfigurationFactory
|
|||
$category,
|
||||
$this->resolveFullyQualifiedNodeTypes($rectorRecipe['node_types']),
|
||||
$rectorRecipe['description'],
|
||||
trim(ltrim($rectorRecipe['code_before'], '<?php')),
|
||||
trim(ltrim($rectorRecipe['code_after'], '<?php')),
|
||||
$this->normalizeCode($rectorRecipe['code_before']),
|
||||
$this->normalizeCode($rectorRecipe['code_after']),
|
||||
array_filter((array) $rectorRecipe['source']),
|
||||
$this->resolveSetConfig($rectorRecipe['set'])
|
||||
$this->resolveSetConfig($rectorRecipe['set']),
|
||||
$this->detectPhpSnippet($rectorRecipe['code_before'])
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -134,4 +135,18 @@ final class ConfigurationFactory
|
|||
// in case of forgotten _
|
||||
return Strings::endsWith($nodeClass, '\\' . $nodeType . '_');
|
||||
}
|
||||
|
||||
private function normalizeCode(string $code): string
|
||||
{
|
||||
if (Strings::startsWith($code, '<?php')) {
|
||||
$code = ltrim($code, '<?php');
|
||||
}
|
||||
|
||||
return trim($code);
|
||||
}
|
||||
|
||||
private function detectPhpSnippet(string $code): bool
|
||||
{
|
||||
return Strings::startsWith($code, '<?php');
|
||||
}
|
||||
}
|
||||
|
|
50
utils/RectorGenerator/src/FileSystem/TemplateFileSystem.php
Normal file
50
utils/RectorGenerator/src/FileSystem/TemplateFileSystem.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils\RectorGenerator\FileSystem;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Utils\RectorGenerator\Finder\TemplateFinder;
|
||||
use Rector\Utils\RectorGenerator\ValueObject\Configuration;
|
||||
use Rector\Utils\RectorGenerator\ValueObject\Package;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class TemplateFileSystem
|
||||
{
|
||||
/**
|
||||
* @param string[] $templateVariables
|
||||
*/
|
||||
public function resolveDestination(
|
||||
SmartFileInfo $smartFileInfo,
|
||||
array $templateVariables,
|
||||
Configuration $configuration
|
||||
): string {
|
||||
$destination = $smartFileInfo->getRelativeFilePathFromDirectory(TemplateFinder::TEMPLATES_DIRECTORY);
|
||||
|
||||
// normalize core package
|
||||
if ($configuration->getPackage() === 'Rector') {
|
||||
$destination = Strings::replace($destination, '#packages\/_Package_/tests/Rector#', 'tests/Rector');
|
||||
$destination = Strings::replace($destination, '#packages\/_Package_/src/Rector#', 'src/Rector');
|
||||
}
|
||||
|
||||
// special keyword for 3rd party Rectors, not for core Github contribution
|
||||
if ($configuration->getPackage() === Package::UTILS) {
|
||||
$destination = Strings::replace($destination, '#packages\/_Package_#', 'utils/rector');
|
||||
}
|
||||
|
||||
if (! Strings::match($destination, '#fixture[\d+]*\.php\.inc#')) {
|
||||
$destination = rtrim($destination, '.inc');
|
||||
}
|
||||
|
||||
return $this->applyVariables($destination, $templateVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $variables
|
||||
*/
|
||||
private function applyVariables(string $content, array $variables): string
|
||||
{
|
||||
return str_replace(array_keys($variables), array_values($variables), $content);
|
||||
}
|
||||
}
|
57
utils/RectorGenerator/src/Finder/TemplateFinder.php
Normal file
57
utils/RectorGenerator/src/Finder/TemplateFinder.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils\RectorGenerator\Finder;
|
||||
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symplify\SmartFileSystem\Finder\FinderSanitizer;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class TemplateFinder
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const TEMPLATES_DIRECTORY = __DIR__ . '/../../templates';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const TEMPLATES_FIXTURE_DIRECTORY = __DIR__ . '/../../templates/packages/_Package_/tests/Rector/_Category_/_Name_/Fixture';
|
||||
|
||||
/**
|
||||
* @var FinderSanitizer
|
||||
*/
|
||||
private $finderSanitizer;
|
||||
|
||||
public function __construct(FinderSanitizer $finderSanitizer)
|
||||
{
|
||||
$this->finderSanitizer = $finderSanitizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SmartFileInfo[]
|
||||
*/
|
||||
public function find(bool $isPhpSnippet): array
|
||||
{
|
||||
$finder = Finder::create()->files()
|
||||
->in(self::TEMPLATES_DIRECTORY)
|
||||
->exclude(self::TEMPLATES_FIXTURE_DIRECTORY);
|
||||
|
||||
$smartFileInfos = $this->finderSanitizer->sanitize($finder);
|
||||
$smartFileInfos[] = $this->createFixtureSmartFileInfo($isPhpSnippet);
|
||||
|
||||
return $smartFileInfos;
|
||||
}
|
||||
|
||||
private function createFixtureSmartFileInfo(bool $isPhpSnippet): SmartFileInfo
|
||||
{
|
||||
if ($isPhpSnippet) {
|
||||
return new SmartFileInfo(self::TEMPLATES_FIXTURE_DIRECTORY . '/fixture.php.inc');
|
||||
}
|
||||
|
||||
// is html snippet
|
||||
return new SmartFileInfo(self::TEMPLATES_FIXTURE_DIRECTORY . '/html_fixture.php.inc');
|
||||
}
|
||||
}
|
|
@ -53,6 +53,11 @@ final class Configuration
|
|||
*/
|
||||
private $source = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isPhpSnippet = false;
|
||||
|
||||
/**
|
||||
* @param string[] $nodeTypes
|
||||
* @param string[] $source
|
||||
|
@ -66,7 +71,8 @@ final class Configuration
|
|||
string $codeBefore,
|
||||
string $codeAfter,
|
||||
array $source,
|
||||
?string $setConfig
|
||||
?string $setConfig,
|
||||
bool $isPhpSnippet
|
||||
) {
|
||||
$this->package = $package;
|
||||
$this->setName($name);
|
||||
|
@ -77,6 +83,7 @@ final class Configuration
|
|||
$this->description = $description;
|
||||
$this->source = $source;
|
||||
$this->setConfig = $setConfig;
|
||||
$this->isPhpSnippet = $isPhpSnippet;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
|
@ -130,6 +137,11 @@ final class Configuration
|
|||
return $this->setConfig;
|
||||
}
|
||||
|
||||
public function isPhpSnippet(): bool
|
||||
{
|
||||
return $this->isPhpSnippet;
|
||||
}
|
||||
|
||||
private function setName(string $name): void
|
||||
{
|
||||
if (! Strings::endsWith($name, 'Rector')) {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
_CodeBefore_
|
||||
-----
|
||||
_CodeAfter_
|
Loading…
Reference in New Issue
Block a user