Skip to content

Commit

Permalink
Added lots of rectors for deprecated methods
Browse files Browse the repository at this point in the history
  • Loading branch information
aschempp committed Dec 14, 2023
1 parent 4355c36 commit e21f82c
Show file tree
Hide file tree
Showing 37 changed files with 1,230 additions and 3 deletions.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
/vendor
# Composer
/vendor/
/composer.lock

# PhpUnit
/.phpunit.cache/
/.phpunit.result.cache
15 changes: 13 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,27 @@
],
"require": {
"php": "^8.1",
"rector/rector": "^0.14.1 || ^0.15"
"rector/rector": "^0.18",
"phpstan/phpstan": "^1.0",
"webmozart/assert": "^1.2"
},
"require-dev": {
"contao/core-bundle": "^4.4 || ^5.0"
"contao/core-bundle": "^4.4 || ^5.0",
"phpunit/phpunit": "^10.0"
},
"autoload": {
"psr-4": {
"Contao\\Rector\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Contao\\Rector\\Tests\\": "tests"
}
},
"scripts": {
"test": "vendor/bin/phpunit"
},
"minimum-stability": "dev",
"prefer-stable": true
}
15 changes: 15 additions & 0 deletions config/sets/contao/contao-413.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@

use Contao\ArrayUtil;
use Contao\BackendUser;
use Contao\Controller;
use Contao\CoreBundle\Security\ContaoCorePermissions;
use Contao\Folder;
use Contao\Rector\Rector\InsertTagsServiceRector;
use Contao\Rector\Rector\LegacyFrameworkCallToServiceCallRector;
use Contao\Rector\ValueObject\LegacyFrameworkCallToServiceCall;
use Contao\StringUtil;
use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector;
Expand Down Expand Up @@ -46,4 +50,15 @@
new RenameClassAndConstFetch(BackendUser::class, 'CAN_EDIT_ARTICLE_HIERARCHY', ContaoCorePermissions::class, 'USER_CAN_EDIT_ARTICLE_HIERARCHY'),
new RenameClassAndConstFetch(BackendUser::class, 'CAN_DELETE_ARTICLES', ContaoCorePermissions::class, 'USER_CAN_DELETE_ARTICLES'),
]);

$rectorConfig->ruleWithConfiguration(LegacyFrameworkCallToServiceCallRector::class, [
// Contao 4.10
new LegacyFrameworkCallToServiceCall(Controller::class, 'parseSimpleTokens', 'contao.string.simple_token_parser', 'parse'),
]);

// Contao 4.13
$rectorConfig->rule(InsertTagsServiceRector::class);

// Contao 4.12
//'Contao\FrontendUser::isMemberOf($ids)' => 'Contao\System::getContainer()->get(\'security.helper\')->isGranted(ContaoCorePermissions::MEMBER_IN_GROUPS, $ids)',
};
80 changes: 80 additions & 0 deletions config/sets/contao/contao-49.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,38 @@

declare(strict_types=1);

use Contao\Automator;
use Contao\Backend;
use Contao\ContentElement;
use Contao\Controller;
use Contao\CoreBundle\Framework\ContaoFramework;
use Contao\Database;
use Contao\Environment;
use Contao\FileUpload;
use Contao\Folder;
use Contao\Image;
use Contao\Input;
use Contao\Module;
use Contao\Rector\Rector\ConstantToServiceParameterRector;
use Contao\Rector\Rector\ControllerMethodToVersionsClassRector;
use Contao\Rector\Rector\LegacyFrameworkCallToInstanceCallRector;
use Contao\Rector\Rector\LegacyFrameworkCallToStaticCallRector;
use Contao\Rector\Rector\LoginConstantsToSymfonySecurityRector;
use Contao\Rector\ValueObject\ConstantToServiceParameter;
use Contao\Rector\ValueObject\LegacyFrameworkCallToInstanceCall;
use Contao\Rector\ValueObject\LegacyFrameworkCallToStaticCall;
use Contao\StringUtil;
use Contao\Widget;
use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\Rector\Name\RenameClassRector;
use Rector\Renaming\ValueObject\MethodCallRename;
use Rector\Transform\Rector\FuncCall\FuncCallToStaticCallRector;
use Rector\Transform\Rector\MethodCall\MethodCallToFuncCallRector;
use Rector\Transform\Rector\MethodCall\MethodCallToStaticCallRector;
use Rector\Transform\ValueObject\FuncCallToStaticCall;
use Rector\Transform\ValueObject\MethodCallToFuncCall;
use Rector\Transform\ValueObject\MethodCallToStaticCall;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(RenameClassRector::class, [
Expand All @@ -29,4 +55,58 @@
new FuncCallToStaticCall('standardize', StringUtil::class, 'standardize'),
new FuncCallToStaticCall('strip_insert_tags', StringUtil::class, 'stripInsertTags'),
]);

$rectorConfig->ruleWithConfiguration(LegacyFrameworkCallToStaticCallRector::class, [
// Contao 4.0
new LegacyFrameworkCallToStaticCall(Controller::class, 'getTheme', Backend::class, 'getTheme'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'getBackendThemes', Backend::class, 'getThemes'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'removeOldFeeds', Automator::class, 'purgeXmlFiles'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'restoreBasicEntities', StringUtil::class, 'restoreBasicEntities'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'resizeImage', Image::class, 'resize'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'getImage', Image::class, 'get'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'generateImage', Image::class, 'getHtml'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'prepareForWidget', Widget::class, 'getAttributesFromDca'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'optionSelected', Widget::class, 'optionSelected'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'optionChecked', Widget::class, 'optionChecked'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'findContentElement', ContentElement::class, 'findClass'),
new LegacyFrameworkCallToStaticCall(Controller::class, 'findFrontendModule', Module::class, 'findClass'),
]);

$rectorConfig->ruleWithConfiguration(MethodCallToFuncCallRector::class, [
// Contao 4.0
new MethodCallToFuncCall(Controller::class, 'classFileExists', 'class_exists')
]);

$rectorConfig->ruleWithConfiguration(MethodCallToStaticCallRector::class, [
// Contao 4.0
new MethodCallToStaticCall(Environment::class, 'get', Environment::class, 'get'),
new MethodCallToStaticCall(Environment::class, 'set', Environment::class, 'set'),
new MethodCallToStaticCall(Input::class, 'get', Input::class, 'get'),
new MethodCallToStaticCall(Input::class, 'post', Input::class, 'post'),
new MethodCallToStaticCall(Input::class, 'postHtml', Input::class, 'postHtml'),
new MethodCallToStaticCall(Input::class, 'postRaw', Input::class, 'postRaw'),

// Contao 4.6
new MethodCallToStaticCall(FileUpload::class, 'getMaximumUploadSize', FileUpload::class, 'getMaxUploadSize'),
]);

$rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [
// Contao 4.0
new MethodCallRename(Folder::class, 'clear', 'purge'),
new MethodCallRename(Database::class, 'executeCached', 'execute'),
]);

$rectorConfig->ruleWithConfiguration(ConstantToServiceParameterRector::class, [
new ConstantToServiceParameter('TL_ROOT', 'kernel.project_dir'),
]);

$rectorConfig->ruleWithConfiguration(LegacyFrameworkCallToInstanceCallRector::class, [
new LegacyFrameworkCallToInstanceCall(Controller::class, 'getChildRecords', Database::class, 'getChildRecords'),
new LegacyFrameworkCallToInstanceCall(Controller::class, 'getParentRecords', Database::class, 'getParentRecords'),
]);

$rectorConfig->rule(LoginConstantsToSymfonySecurityRector::class);
$rectorConfig->rule(ControllerMethodToVersionsClassRector::class);

//'Contao\Controller::getBackendLanguages()' => 'Contao\System::getContainer()->get(\'contao.intl.locales\')->getEnabledLocales(null, true)',
};
14 changes: 14 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.3/phpunit.xsd" bootstrap="vendor/autoload.php" executionOrder="depends,defects" beStrictAboutOutputDuringTests="true" colors="true" failOnRisky="true" failOnWarning="true" cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="default">
<directory suffix="Test.php">tests</directory>
</testsuite>
</testsuites>
<coverage/>
<source>
<include>
<directory suffix=".php">src</directory>
</include>
</source>
</phpunit>
69 changes: 69 additions & 0 deletions src/Rector/AbstractLegacyFrameworkCallRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

namespace Contao\Rector\Rector;

use PhpParser\Node;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Reflection\ReflectionResolver;

abstract class AbstractLegacyFrameworkCallRector extends AbstractRector
{
public function __construct(private readonly ReflectionResolver $reflectionResolver)
{
}

public function getNodeTypes(): array
{
return [
Node\Expr\StaticCall::class,
Node\Expr\MethodCall::class,
];
}

protected function isParentStaticOrMethodClassCall(Node $node, string $oldClassName, string $oldMethodName, string|null $newClassName = null, string|null $newMethodName = null): bool
{
if ($this->isMethodCall($node, $oldClassName, $oldMethodName)) {
return true;
}

if (!$node instanceof Node\Expr\StaticCall || !$this->isName($node->name, $oldMethodName)) {
return false;
}

$classReflection = $this->reflectionResolver->resolveClassReflectionSourceObject($node);

if (!$classReflection instanceof ClassReflection) {
if (!$this->isNames($node->class, ['static', 'self'])) {
if ($oldMethodName === $newMethodName && $this->getName($node->class) === $newClassName) {
return false;
}

return is_a($this->getName($node->class), $oldClassName, true);
}

$classReflection = $this->reflectionResolver->resolveClassReflection($node);
}

if (!$classReflection instanceof ClassReflection) {
return false;
}

if ($oldMethodName === $newMethodName && $classReflection->getName() === $newClassName) {
return false;
}

return $classReflection->is($oldClassName);
}

protected function isMethodCall(Node $node, string $className, string $methodName): bool
{
return $node instanceof Node\Expr\MethodCall
&& $this->isName($node->name, $methodName)
&& $this->isObjectType($node->var, new ObjectType($className))
;
}
}
65 changes: 65 additions & 0 deletions src/Rector/ConstantToServiceParameterRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Contao\Rector\Rector;

use Contao\Rector\ValueObject\ConstantToServiceParameter;
use PhpParser\Node;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Webmozart\Assert\Assert;

final class ConstantToServiceParameterRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var array<ConstantToServiceParameter>
*/
private array $configuration;

public function configure(array $configuration): void
{
Assert::allIsAOf($configuration, ConstantToServiceParameter::class);
$this->configuration = $configuration;
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Fixes deprecated constants to service parameters', [
new CodeSample(
<<<'CODE_BEFORE'
$projectDir = TL_ROOT;
CODE_BEFORE
,
<<<'CODE_AFTER'
$projectDir = \Contao\System::getContainer()->getParameter('kernel.project_dir');
CODE_AFTER
),
]);
}

public function getNodeTypes(): array
{
return [
Node\Expr\ConstFetch::class,
];
}

public function refactor(Node $node): ?Node
{
assert($node instanceof Node\Expr\ConstFetch);

foreach ($this->configuration as $config) {
if ($this->isName($node->name, $config->getConstant())) {
$container = new Node\Expr\StaticCall(new Node\Name\FullyQualified('Contao\System'), 'getContainer');
$node = new Node\Expr\MethodCall($container, 'getParameter', [new Node\Arg(new Node\Scalar\String_($config->getParameter()))]);

return $node;
}
}

return null;
}
}
49 changes: 49 additions & 0 deletions src/Rector/ControllerMethodToVersionsClassRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Contao\Rector\Rector;

use Contao\Controller;
use PhpParser\Node;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

final class ControllerMethodToVersionsClassRector extends AbstractLegacyFrameworkCallRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Fixes deprecated Controller::createInitialVersion() and Controller::createNewVersion() to Versions class calls', [
new CodeSample(
<<<'CODE_BEFORE'
\Contao\Controller::createInitialVersion('tl_page', 17);
\Contao\Controller::createNewVersion('tl_page', 17);
CODE_BEFORE
,
<<<'CODE_AFTER'
(new \Contao\Versions('tl_page', 17))->initialize();
(new \Contao\Versions('tl_page', 17))->create();
CODE_AFTER
),
]);
}

public function refactor(Node $node): ?Node
{
if ($this->isMethodCall($node, Controller::class, 'createInitialVersion')) {
$versions = new Node\Expr\New_(new Node\Name\FullyQualified('Contao\Versions'), $node->args);
$node = new Node\Expr\MethodCall($versions, 'initialize');

return $node;
}

if ($this->isMethodCall($node, Controller::class, 'createNewVersion')) {
$versions = new Node\Expr\New_(new Node\Name\FullyQualified('Contao\Versions'), $node->args);
$node = new Node\Expr\MethodCall($versions, 'create');

return $node;
}

return $node;
}
}
Loading

0 comments on commit e21f82c

Please sign in to comment.