Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions build/target-repository/docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -5510,6 +5510,8 @@ Add override attribute to overridden methods

Add type to constants

:wrench: **configure it!**

- class: [`Rector\Php83\Rector\ClassConst\AddTypeToConstRector`](../rules/Php83/Rector/ClassConst/AddTypeToConstRector.php)

```diff
Expand All @@ -5522,6 +5524,22 @@ Add type to constants

<br>

```diff
class ParentClass extends ChildClass
{
- public const TYPE = 'some_type';
+ public const string TYPE = 'some_type';
public const OTHER_TYPE = 'other_type';
}

class ChildClass
{
public const OTHER_TYPE = 'other_type';
}
```

<br>

### CombineHostPortLdapUriRector

Combine separated host and port on `ldap_connect()` args
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Php83\Rector\ClassConst\AddTypeToConstRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class AllowHasChildRectorTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/FixtureAllowHasChild');
}

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

namespace Rector\Tests\Php83\Rector\ClassConst\AddTypeToConstRector\Fixture;

class ApplyTypeWhenNoChildren
{
private const A = 1000;
public const B = 'foo';
}
?>
-----
<?php

namespace Rector\Tests\Php83\Rector\ClassConst\AddTypeToConstRector\Fixture;

class ApplyTypeWhenNoChildren
{
private const int A = 1000;
public const string B = 'foo';
}
?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Rector\Tests\Php83\Rector\ClassConst\AddTypeToConstRector\Fixture;

class ApplyTypeWhenNotOverridden
{
public const STRING = 'some_type';
public const INT = 1;
protected const bool = true;
protected const array = [];
}

class Child extends ApplyTypeWhenNotOverridden
{
public const STRING = 'some_type';
protected const bool = true;
}

?>
-----
<?php

namespace Rector\Tests\Php83\Rector\ClassConst\AddTypeToConstRector\Fixture;

class ApplyTypeWhenNotOverridden
{
public const STRING = 'some_type';
public const int INT = 1;
protected const bool = true;
protected const array array = [];
}

class Child extends ApplyTypeWhenNotOverridden
{
public const STRING = 'some_type';
protected const bool = true;
}

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

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Php83\Rector\ClassConst\AddTypeToConstRector;
use Rector\ValueObject\PhpVersion;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(AddTypeToConstRector::class, [
AddTypeToConstRector::ALLOW_HAS_CHILD => true,
]);

$rectorConfig->phpVersion(PhpVersion::PHP_83);
};
7 changes: 1 addition & 6 deletions rules/Php74/Rector/FuncCall/HebrevcToNl2brHebrevRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ public function refactor(Node $node): ?FuncCall
}

$node->name = new Name('hebrev');
return new FuncCall(
new Name('nl2br'),
[
new Arg($node)
]
);
return new FuncCall(new Name('nl2br'), [new Arg($node)]);
Comment thread
TomasVotruba marked this conversation as resolved.
Resolved
}
}
110 changes: 105 additions & 5 deletions rules/Php83/Rector/ClassConst/AddTypeToConstRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,43 @@
use PhpParser\Node\Stmt\ClassConst;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
use Rector\PhpParser\AstResolver;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\Rector\AbstractRector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Webmozart\Assert\Assert;

/**
* @see \Rector\Tests\Php83\Rector\ClassConst\AddTypeToConstRector\AddTypeToConstRectorTest
*/
final class AddTypeToConstRector extends AbstractRector implements MinPhpVersionInterface
final class AddTypeToConstRector extends AbstractRector implements ConfigurableRectorInterface, MinPhpVersionInterface
{
/**
* @api
* @var string
*/
final public const ALLOW_HAS_CHILD = 'allow_has_child';

private bool $allowHasChild = false;

public function __construct(
private readonly ReflectionProvider $reflectionProvider,
private readonly StaticTypeMapper $staticTypeMapper
private readonly StaticTypeMapper $staticTypeMapper,
private readonly FamilyRelationsAnalyzer $familyRelationsAnalyzer,
private readonly AstResolver $astResolver,
) {
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Add type to constants', [
new CodeSample(
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
final class SomeClass
{
Expand All @@ -57,6 +71,40 @@ final class SomeClass
}
CODE_SAMPLE
,
[
AddTypeToConstRector::ALLOW_HAS_CHILD => false,
]
),
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
class ParentClass extends ChildClass
{
public const TYPE = 'some_type';
public const OTHER_TYPE = 'other_type';
}

class ChildClass
{
public const OTHER_TYPE = 'other_type';
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class ParentClass extends ChildClass
{
public const string TYPE = 'some_type';
public const OTHER_TYPE = 'other_type';
}

class ChildClass
{
public const OTHER_TYPE = 'other_type';
}
CODE_SAMPLE
,
[
AddTypeToConstRector::ALLOW_HAS_CHILD => true,
]
),
]);
}
Expand Down Expand Up @@ -86,6 +134,7 @@ public function refactor(Node $node): ?Class_
}

$parentClassReflections = $this->getParentReflections($className);
$childClassReflections = $this->getChildClassReflections($className);

$hasChanged = false;

Expand All @@ -103,7 +152,11 @@ public function refactor(Node $node): ?Class_
continue;
}

if ($this->canBeInherited($classConst, $node)) {
if (! $this->allowHasChild && $this->canBeInherited($classConst, $node)) {
continue;
}

if ($this->allowHasChild && $this->isConstGuardedByChildren($constNode, $childClassReflections)) {
continue;
}

Expand Down Expand Up @@ -139,6 +192,16 @@ public function refactor(Node $node): ?Class_
return $node;
}

/**
* @param mixed[] $configuration
*/
public function configure(array $configuration): void
{
$ignoreInheritance = $configuration[self::ALLOW_HAS_CHILD] ?? (bool) current($configuration);
Assert::boolean($ignoreInheritance);
$this->allowHasChild = $ignoreInheritance;
}

public function provideMinPhpVersion(): int
{
return PhpVersionFeature::TYPED_CLASS_CONSTANTS;
Expand All @@ -160,6 +223,29 @@ public function isConstGuardedByParents(Const_ $const, array $parentClassReflect
return false;
}

/**
* @param ClassReflection[] $childClassReflections
*/
private function isConstGuardedByChildren(Const_ $const, array $childClassReflections): bool
{
foreach ($childClassReflections as $childClassReflection) {
$classLike = $this->astResolver->resolveClassFromClassReflection($childClassReflection);
if (! $classLike instanceof Class_) {
continue;
}

foreach ($classLike->getConstants() as $childClassConstants) {
foreach ($childClassConstants->consts as $childConst) {
if ($this->nodeComparator->areNodesEqual($childConst, $const)) {
return true;
}
}
}
}

return false;
}

private function findValueType(Expr $expr): ?Identifier
{
if ($expr instanceof UnaryPlus || $expr instanceof UnaryMinus) {
Expand Down Expand Up @@ -220,6 +306,20 @@ private function getParentReflections(string $className): array
$currentClassReflection !== $classReflection);
}

/**
* @return ClassReflection[]
*/
private function getChildClassReflections(string $className): array
{
if (! $this->reflectionProvider->hasClass($className)) {
return [];
}

$currentClassReflection = $this->reflectionProvider->getClass($className);

return $this->familyRelationsAnalyzer->getChildrenOfClassReflection($currentClassReflection);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the method removed at PR:

so this change no longer valid,

Please rebase. You may can revert to original ignore_inheritance config ;)

}

private function canBeInherited(ClassConst $classConst, Class_ $class): bool
{
return ! $class->isFinal() && ! $classConst->isPrivate() && ! $classConst->isFinal();
Expand Down
2 changes: 1 addition & 1 deletion src/StaticTypeMapper/PhpParser/NameNodeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private function expandedNamespacedName(Node $node): ?FullyQualified
return null;
}

if (!$node->hasAttribute(AttributeKey::NAMESPACED_NAME)) {
if (! $node->hasAttribute(AttributeKey::NAMESPACED_NAME)) {
Comment thread
TomasVotruba marked this conversation as resolved.
Resolved
return null;
}

Expand Down