Skip to content

Commit

Permalink
IBX-8534: Added rule for replacing magic properties with getters (#11)
Browse files Browse the repository at this point in the history
For more details see https://issues.ibexa.co/browse/IBX-8534 and #11

Key changes:

* Implemented the rule for replacing magic properties with getters

* Configured the magic getters replacement rule for `Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest`
  • Loading branch information
ViniTou authored Sep 16, 2024
1 parent 2e48a1a commit f13f869
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 3 deletions.
16 changes: 16 additions & 0 deletions src/contracts/Sets/ibexa-50.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

namespace Ibexa\Contracts\Rector\Sets;

use Ibexa\Rector\Rule\PropertyToGetterRector;
use Ibexa\Rector\Rule\RemoveArgumentFromMethodCallRector;
use Rector\Config\RectorConfig;
use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector;
Expand Down Expand Up @@ -98,4 +99,19 @@
),
]
);

$rectorConfig->ruleWithConfiguration(
PropertyToGetterRector::class,
[
'Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest' => [
'scheme' => 'getScheme',
'host' => 'getHost',
'port' => 'getPort',
'pathinfo' => 'getPathInfo',
'queryParams' => 'getQueryParams',
'languages' => 'getLanguages',
'headers' => 'getHeaders',
],
]
);
};
92 changes: 92 additions & 0 deletions src/lib/Rule/PropertyToGetterRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Rector\Rule;

use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PHPStan\Type\ObjectType;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

final class PropertyToGetterRector extends AbstractRector implements ConfigurableRectorInterface
{
/** @var array<string, array<string, string>> */
private array $classPropertyToGetterMap = [];

/**
* @throws \Exception
*/
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Change direct property access to getter method for specified classes and properties', [new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
private $property;
public function getProperty() {}
}
$instance = new SomeClass();
$instance->property;
CODE_SAMPLE,
<<<'CODE_SAMPLE'
$instance = new SomeClass();
$instance->getProperty();
CODE_SAMPLE,
['var_dump']
)]);
}

/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [PropertyFetch::class];
}

/**
* @param \PhpParser\Node\Expr\PropertyFetch $node
*/
public function refactor(Node $node): ?Node
{
$className = $this->resolveClassName($node);
if ($className === null || !isset($this->classPropertyToGetterMap[$className])) {
return null;
}

$propertyName = $this->getName($node->name);

if (!isset($this->classPropertyToGetterMap[$className][$propertyName])) {
return null;
}

$getterMethodName = $this->classPropertyToGetterMap[$className][$propertyName];

return $this->nodeFactory->createMethodCall($node->var, $getterMethodName);
}

private function resolveClassName(PropertyFetch $propertyFetch): ?string
{
$type = $this->nodeTypeResolver->getType($propertyFetch->var);

if ($type instanceof ObjectType) {
return $type->getClassName();
}

return null;
}

public function configure(array $configuration): void
{
$this->classPropertyToGetterMap = $configuration;
}
}
37 changes: 37 additions & 0 deletions tests/lib/Rule/PropertyToGetterRector/Fixture/some_class.php.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php /** @noinspection ALL */

namespace Ibexa\Rector\Tests\Rule\PropertyToGetterRector\Fixture;

class SomeClass
{
public ?int $foo;

public function getFoo(): ?int
{
return $this->foo;
}
}

$some = new SomeClass();
$some->foo;

?>
-----
<?php /** @noinspection ALL */

namespace Ibexa\Rector\Tests\Rule\PropertyToGetterRector\Fixture;

class SomeClass
{
public ?int $foo;

public function getFoo(): ?int
{
return $this->foo;
}
}

$some = new SomeClass();
$some->getFoo();

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

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Rector\Tests\Rule\PropertyToGetterRector;

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

final class PropertyToGetterRectorTest extends AbstractRectorTestCase
{
/**
* @throws \Rector\Exception\ShouldNotHappenException
*/
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

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

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
21 changes: 21 additions & 0 deletions tests/lib/Rule/PropertyToGetterRector/config/configured_rule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

use Ibexa\Rector\Rule\PropertyToGetterRector;
use Rector\Config\RectorConfig;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(
PropertyToGetterRector::class,
[
'Ibexa\Rector\Tests\Rule\PropertyToGetterRector\Fixture\SomeClass' => [
'foo' => 'getFoo',
],
]
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

/**
* @covers \Ibexa\Rector\Rule\Internal\RemoveLegacyClassAliasRector
*/
final class RemoveArgumentFromMethodCallRectorTest extends AbstractRectorTestCase
{
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Ibexa\Rector\Tests\Sets\Ibexa50\Fixture;

use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest;

$request = new SimplifiedRequest();
$pathInfo = $request->pathinfo;

?>
-----
<?php

namespace Ibexa\Rector\Tests\Sets\Ibexa50\Fixture;

use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest;

$request = new SimplifiedRequest();
$pathInfo = $request->getPathInfo();

?>

0 comments on commit f13f869

Please sign in to comment.