Skip to content

Commit 08816b3

Browse files
committed
[FEATURE] Add MigratePluginContentElementAndPluginSubtypesFractor
Fixes: sabbelasichon/typo3-rector#4323
1 parent 6837fda commit 08816b3

10 files changed

+236
-1
lines changed

packages/typo3-fractor/config/typo3-13.php

+2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
declare(strict_types=1);
44

5+
use a9f\Typo3Fractor\TYPO3v13\TypoScript\MigratePluginContentElementAndPluginSubtypesFractor;
56
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
67

78
return static function (ContainerConfigurator $containerConfigurator): void {
89
$services = $containerConfigurator->services();
910
$services->defaults()
1011
->autoconfigure()
1112
->autowire();
13+
$services->set(MigratePluginContentElementAndPluginSubtypesFractor::class);
1214
};

packages/typo3-fractor/docs/typo3-fractor-rules.md

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 27 Rules Overview
1+
# 28 Rules Overview
22

33
## AbstractMessageGetSeverityFluidFractor
44

@@ -256,6 +256,26 @@ Migrate password and salted password to password type
256256

257257
<br>
258258

259+
## MigratePluginContentElementAndPluginSubtypesFractor
260+
261+
Migrate plugin content element and plugin subtypes (list_type)
262+
263+
- class: [`a9f\Typo3Fractor\TYPO3v13\TypoScript\MigratePluginContentElementAndPluginSubtypesFractor`](../rules/TYPO3v13/TypoScript/MigratePluginContentElementAndPluginSubtypesFractor.php)
264+
265+
```diff
266+
-tt_content.list.20.examples_pi1 = USER
267+
-tt_content.list.20.examples_pi1 {
268+
+tt_content.examples_pi1 = USER
269+
+tt_content.examples_pi1 {
270+
userFunc = MyVendor\Examples\Controller\ExampleController->example
271+
}
272+
273+
-tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1
274+
+tt_content.examples_pi1 < plugin.tx_examples_pi1
275+
```
276+
277+
<br>
278+
259279
## MigrateRenderTypeColorpickerToTypeColorFlexFormFractor
260280

261281
Migrate renderType colorpicker to type color
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
tt_content.list.20.examples_pi1 = USER
2+
tt_content.list.20.examples_pi1 {
3+
userFunc = MyVendor\Examples\Controller\ExampleController->example
4+
}
5+
-----
6+
tt_content.examples_pi1 = USER
7+
tt_content.examples_pi1 {
8+
userFunc = MyVendor\Examples\Controller\ExampleController->example
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
tt_content {
2+
list.20 {
3+
examples_pi1 {
4+
userFunc = MyVendor\Examples\Controller\ExampleController->example
5+
}
6+
}
7+
}
8+
-----
9+
tt_content {
10+
examples_pi1 {
11+
userFunc = MyVendor\Examples\Controller\ExampleController->example
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
tt_content {
2+
list {
3+
20 {
4+
examples_pi1 {
5+
userFunc = MyVendor\Examples\Controller\ExampleController->example
6+
}
7+
}
8+
}
9+
}
10+
-----
11+
tt_content {
12+
examples_pi1 {
13+
userFunc = MyVendor\Examples\Controller\ExampleController->example
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1
2+
-----
3+
tt_content.examples_pi1 < plugin.tx_examples_pi1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
tt_content.list.20.examples_pi1 =< plugin.tx_examples_pi1
2+
-----
3+
tt_content.examples_pi1 =< plugin.tx_examples_pi1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace a9f\Typo3Fractor\Tests\TYPO3v13\TypoScript\MigratePluginContentElementAndPluginSubtypesFractor;
6+
7+
use a9f\Fractor\Testing\PHPUnit\AbstractFractorTestCase;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
10+
final class MigratePluginContentElementAndPluginSubtypesFractorTest extends AbstractFractorTestCase
11+
{
12+
#[DataProvider('provideData')]
13+
public function test(string $filePath): void
14+
{
15+
$this->doTestFile($filePath);
16+
}
17+
18+
public static function provideData(): \Iterator
19+
{
20+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixtures', '*.typoscript.fixture');
21+
}
22+
23+
public function provideConfigFilePath(): ?string
24+
{
25+
return __DIR__ . '/config/fractor.php';
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use a9f\Fractor\Configuration\FractorConfiguration;
6+
use a9f\Fractor\ValueObject\Indent;
7+
use a9f\FractorXml\Configuration\XmlProcessorOption;
8+
use a9f\Typo3Fractor\TYPO3v13\TypoScript\MigratePluginContentElementAndPluginSubtypesFractor;
9+
10+
return FractorConfiguration::configure()
11+
->withOptions([
12+
XmlProcessorOption::INDENT_CHARACTER => Indent::STYLE_TAB,
13+
XmlProcessorOption::INDENT_SIZE => 1,
14+
])
15+
->withRules([MigratePluginContentElementAndPluginSubtypesFractor::class]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace a9f\Typo3Fractor\TYPO3v13\TypoScript;
6+
7+
use a9f\FractorTypoScript\AbstractTypoScriptFractor;
8+
use Helmich\TypoScriptParser\Parser\AST\NestedAssignment;
9+
use Helmich\TypoScriptParser\Parser\AST\Operator\Assignment;
10+
use Helmich\TypoScriptParser\Parser\AST\Operator\Copy;
11+
use Helmich\TypoScriptParser\Parser\AST\Operator\Reference;
12+
use Helmich\TypoScriptParser\Parser\AST\Statement;
13+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
14+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
15+
16+
/**
17+
* @changelog https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/13.4/Deprecation-105076-PluginContentElementAndPluginSubTypes.html
18+
* @see \a9f\Typo3Fractor\Tests\TYPO3v13\TypoScript\MigratePluginContentElementAndPluginSubtypesFractor\MigratePluginContentElementAndPluginSubtypesFractorTest
19+
*/
20+
final class MigratePluginContentElementAndPluginSubtypesFractor extends AbstractTypoScriptFractor
21+
{
22+
public function getRuleDefinition(): RuleDefinition
23+
{
24+
return new RuleDefinition('Migrate plugin content element and plugin subtypes (list_type)', [new CodeSample(
25+
<<<'CODE_SAMPLE'
26+
tt_content.list.20.examples_pi1 = USER
27+
tt_content.list.20.examples_pi1 {
28+
userFunc = MyVendor\Examples\Controller\ExampleController->example
29+
}
30+
31+
tt_content.list.20.examples_pi1 < plugin.tx_examples_pi1
32+
CODE_SAMPLE
33+
,
34+
<<<'CODE_SAMPLE'
35+
tt_content.examples_pi1 = USER
36+
tt_content.examples_pi1 {
37+
userFunc = MyVendor\Examples\Controller\ExampleController->example
38+
}
39+
40+
tt_content.examples_pi1 < plugin.tx_examples_pi1
41+
CODE_SAMPLE
42+
)]);
43+
}
44+
45+
public function refactor(Statement $statement): null|Statement|int
46+
{
47+
if ($this->shouldSkip($statement)) {
48+
return null;
49+
}
50+
51+
if ($statement instanceof NestedAssignment) {
52+
$rootLine = ['tt_content', 'list', '20', '#'];
53+
/** @var NestedAssignment|null $pluginStatement */
54+
$pluginStatement = $this->findPluginStatement($statement, $rootLine);
55+
if ($pluginStatement === null) {
56+
return null;
57+
}
58+
59+
$pluginStatement->object->absoluteName = str_replace(
60+
'list.20.',
61+
'',
62+
$pluginStatement->object->absoluteName
63+
);
64+
$pluginStatement->object->relativeName = str_replace(
65+
'list.20.',
66+
'',
67+
$pluginStatement->object->relativeName
68+
);
69+
70+
if ($pluginStatement->object->absoluteName === $pluginStatement->object->relativeName) {
71+
return $pluginStatement;
72+
}
73+
$statement->statements[0] = $pluginStatement;
74+
} elseif ($statement instanceof Assignment || $statement instanceof Copy || $statement instanceof Reference) {
75+
$statement->object->absoluteName = str_replace('list.20.', '', $statement->object->absoluteName);
76+
$statement->object->relativeName = str_replace('list.20.', '', $statement->object->relativeName);
77+
}
78+
79+
return $statement;
80+
}
81+
82+
private function shouldSkip(Statement $statement): bool
83+
{
84+
if (! $statement instanceof NestedAssignment
85+
&& ! $statement instanceof Assignment
86+
&& ! $statement instanceof Copy
87+
&& ! $statement instanceof Reference
88+
) {
89+
return true;
90+
}
91+
92+
if (! str_starts_with($statement->object->absoluteName, 'tt_content')) {
93+
return true;
94+
}
95+
96+
return false;
97+
}
98+
99+
/**
100+
* @param array<int, string> $rootLine
101+
*/
102+
private function findPluginStatement(Statement $statement, array $rootLine): ?Statement
103+
{
104+
if (! $statement instanceof NestedAssignment) {
105+
return null;
106+
}
107+
108+
if (! isset($rootLine[0])) {
109+
return $statement;
110+
}
111+
112+
$objectPath = $statement->object->relativeName;
113+
$parts = explode('.', $objectPath);
114+
foreach ($parts as $part) {
115+
$firstRootLineItem = $rootLine[0];
116+
if ($firstRootLineItem === $part || $firstRootLineItem === '#') {
117+
array_shift($rootLine);
118+
}
119+
}
120+
121+
if ($rootLine === []) {
122+
// we found it!
123+
return $statement;
124+
}
125+
126+
return $this->findPluginStatement($statement->statements[0], $rootLine);
127+
}
128+
}

0 commit comments

Comments
 (0)