Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PHP 8.2] Add rule for AllowDynamicProperties attribute #1225

Merged
merged 47 commits into from
Nov 26, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
dbc8ce6
Add rule for AllowDynamicProperties attribute
mallardduck Nov 13, 2021
c2764a0
Add PHP_82 setlist and config
mallardduck Nov 13, 2021
0e14505
implement MinPhpVersionInterface
mallardduck Nov 13, 2021
d731d54
rename test and remove redundant info
mallardduck Nov 14, 2021
5d2c132
refactor to more direct returns
mallardduck Nov 14, 2021
e11a636
correct return types
mallardduck Nov 14, 2021
28a37ec
use more accurate variable name
mallardduck Nov 14, 2021
19df4bc
Split tests into single examples
mallardduck Nov 14, 2021
a4f2173
Move new rule out of PHP 8.2 specific rulesets
mallardduck Nov 14, 2021
e919ae1
finish correcting the name changes for the refactor
mallardduck Nov 14, 2021
45d96b8
remove the php 8.2 set that was added
mallardduck Nov 14, 2021
ade82cd
update @see test class
mallardduck Nov 14, 2021
0496a13
Use PhpAttributeAnalyzer to detect attribute
mallardduck Nov 14, 2021
8798ce1
Update rules/Transform/Rector/Class_/AddAllowDynamicPropertiesAttribu…
mallardduck Nov 14, 2021
c70b25f
Add back MinPhpVersionInterface
mallardduck Nov 17, 2021
ed67519
Merge branch 'main' into dynamic-properties-attribute
mallardduck Nov 17, 2021
e705f47
make rule final and constant private
mallardduck Nov 20, 2021
7efd4ba
Merge branch 'main' into dynamic-properties-attribute
mallardduck Nov 20, 2021
474ffc8
reorg methods
mallardduck Nov 20, 2021
72709ef
Add failing test for "extend stdClass" example
mallardduck Nov 23, 2021
b454478
flip the logic in refactor to return early
mallardduck Nov 23, 2021
8d958ed
ensure classes that extend stdClass aren't touched
mallardduck Nov 23, 2021
f5a3cc0
introduce shouldSkipAllowDynamicPropertiesAttribute method
mallardduck Nov 24, 2021
d9ec12e
Add new use-case for skip stdClass descendants
mallardduck Nov 24, 2021
f204729
correct detection for any descendants of stdClass
mallardduck Nov 24, 2021
6253c1c
make case specific checking methods
mallardduck Nov 24, 2021
5e75349
make the rule more succinct
mallardduck Nov 24, 2021
2fff324
Update test class names to fit use-case
mallardduck Nov 24, 2021
f18feba
Use 3 levels of children to ensure recursion check works
mallardduck Nov 24, 2021
c54bcdd
Add tests to check "hasAttribute" recursion
mallardduck Nov 24, 2021
3c62123
return early if class doesn't extend anything
mallardduck Nov 24, 2021
b6cdce8
refactor hasNeededAttributeAlready to work towards recursive checking
mallardduck Nov 24, 2021
56c0a12
add failing test for magic set
mallardduck Nov 24, 2021
79db25c
refactor for better recursive check
mallardduck Nov 24, 2021
062ebc6
Add method to check for magic _set
mallardduck Nov 24, 2021
9de8f40
re-order methods
mallardduck Nov 24, 2021
9a3bbf0
Update to use intentionally broken example
mallardduck Nov 24, 2021
c677277
refactor to use reflection provider
mallardduck Nov 24, 2021
1019cb6
clarify todo
mallardduck Nov 25, 2021
5b6d335
rename test fixture
mallardduck Nov 25, 2021
d4be6d1
Add a new hasInheritedPhpAttribute method to PhpAttributeAnalyzer
mallardduck Nov 25, 2021
a59a33b
Merge branch 'main' into dynamic-properties-attribute
mallardduck Nov 25, 2021
cbbeada
improve method param names and code style
mallardduck Nov 25, 2021
a3365a5
fix whitespace
mallardduck Nov 25, 2021
56b5ea6
cast to string instead of using `toString`
mallardduck Nov 26, 2021
0ab233a
ensure reflection class exists
mallardduck Nov 26, 2021
f5bc103
check reflectionProvider with hasClass
mallardduck Nov 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions config/set/php82.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

use Rector\Php82\Rector\Class_\AllowDynamicPropertiesRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(AllowDynamicPropertiesRector::class);
};
5 changes: 5 additions & 0 deletions packages/Set/ValueObject/SetList.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ final class SetList implements SetListInterface
*/
public const PHP_81 = __DIR__ . '/../../../config/set/php81.php';

/**
* @var string
*/
public const PHP_82 = __DIR__ . '/../../../config/set/php82.php';

/**
* @var string
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Php82\Rector\Class_\AllowDynamicPropertiesRector;

use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;

final class AllowDynamicPropertiesRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}

/**
* @return Iterator<SmartFileInfo>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Rector\Tests\Php82\Rector\Class_\AllowDynamicPropertiesRector\Fixture;

#[\Immutable]
class SomeObject
{
public string $someProperty = 'hello world';
}

class AnotherObject
{
public string $someProperty = 'hello world';
}

?>
-----
<?php

namespace Rector\Tests\Php82\Rector\Class_\AllowDynamicPropertiesRector\Fixture;

#[\Immutable]
#[\AllowDynamicProperties]
class SomeObject
{
public string $someProperty = 'hello world';
}

#[\AllowDynamicProperties]
class AnotherObject
{
public string $someProperty = 'hello world';
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Rector\Tests\Php82\Rector\Class_\AllowDynamicPropertiesRector\Fixture;

#[\AllowDynamicProperties]
class AnotherObject
{
public string $someProperty = 'hello world';
}

?>
-----
<?php

namespace Rector\Tests\Php82\Rector\Class_\AllowDynamicPropertiesRector\Fixture;

#[\AllowDynamicProperties]
class AnotherObject
{
public string $someProperty = 'hello world';
}

?>
mallardduck marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

use Rector\Php82\Rector\Class_\AllowDynamicPropertiesRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(AllowDynamicPropertiesRector::class);
};
94 changes: 94 additions & 0 deletions rules/Php82/Rector/Class_/AllowDynamicPropertiesRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace Rector\Php82\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Type\ObjectType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersion;
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @changelog https://github.com/php/php-src/pull/7571
*
* @see \Rector\Tests\Php82\Rector\Class_\AllowDynamicPropertiesRector\AllowDynamicPropertiesRectorTest
*/
class AllowDynamicPropertiesRector extends AbstractRector implements MinPhpVersionInterface
{
/**
* @var string
*/
private const ATTRIBUTE = 'AllowDynamicProperties';

public function __construct(
private PhpAttributeGroupFactory $phpAttributeGroupFactory,
) {
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Add the `AllowDynamicProperties` attribute to all classes', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeObject {
public string $someProperty = 'hello world';
}
CODE_SAMPLE

,
<<<'CODE_SAMPLE'
#[AllowDynamicProperties]
class SomeObject {
public string $someProperty = 'hello world';
}
CODE_SAMPLE
),
]);
}


public function getNodeTypes(): array
{
return [Class_::class];
}

public function refactor(Node $node)
mallardduck marked this conversation as resolved.
Show resolved Hide resolved
{
$skipAttribute = false;
mallardduck marked this conversation as resolved.
Show resolved Hide resolved
assert($node instanceof Class_);
mallardduck marked this conversation as resolved.
Show resolved Hide resolved

foreach ($node->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $key => $attribute) {
if ($this->isName($attribute->name, 'AllowDynamicProperties')) {
$skipAttribute = true;
break 2;
}
}
}

if (!$skipAttribute) {
$node = $this->addAllowDynamicPropertiesAttribute($node);
mallardduck marked this conversation as resolved.
Show resolved Hide resolved
}

return $node;
mallardduck marked this conversation as resolved.
Show resolved Hide resolved
}

private function addAllowDynamicPropertiesAttribute(Class_ $node): Node
{
$attributeGroup = $this->phpAttributeGroupFactory->createFromClass(self::ATTRIBUTE);
$node->attrGroups[] = $attributeGroup;

return $node;
}

public function provideMinPhpVersion(): int
{
// TODO: Consider what actual version to target...
return PhpVersion::PHP_81;
}
}