Skip to content
Merged
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
2 changes: 2 additions & 0 deletions config/rector/sets/cakephp53.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
declare(strict_types=1);

use Cake\Upgrade\Rector\Rector\ClassMethod\FormExecuteToProcessRector;
use Cake\Upgrade\Rector\Rector\MethodCall\BreadcrumbsHelperAddManyRector;
use Cake\Upgrade\Rector\Rector\MethodCall\EntityIsEmptyRector;
use Cake\Upgrade\Rector\Rector\MethodCall\EntityPatchRector;
use Cake\Upgrade\Rector\Rector\MethodCall\NewExprToFuncRector;
Expand All @@ -25,6 +26,7 @@
'Cake\TestSuite\Fixture\TransactionFixtureStrategy' => 'Cake\TestSuite\Fixture\TransactionStrategy',
'Cake\TestSuite\Fixture\TruncateFixtureStrategy' => 'Cake\TestSuite\Fixture\TruncateStrategy',
]);
$rectorConfig->rule(BreadcrumbsHelperAddManyRector::class);
$rectorConfig->rule(EntityIsEmptyRector::class);
$rectorConfig->rule(EntityPatchRector::class);
$rectorConfig->rule(FormExecuteToProcessRector::class);
Expand Down
142 changes: 142 additions & 0 deletions src/Rector/Rector/MethodCall/BreadcrumbsHelperAddManyRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php
declare(strict_types=1);

namespace Cake\Upgrade\Rector\Rector\MethodCall;

use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Scalar\String_;
use PHPStan\Type\ObjectType;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* Transforms BreadcrumbsHelper::add(array) to addMany() and prepend(array) to prependMany()
*
* In CakePHP 5.3, passing an array of crumbs to add() or prepend() is deprecated.
* Use addMany() or prependMany() instead.
*
* @see https://book.cakephp.org/5/en/appendices/5-3-migration-guide.html
*/
final class BreadcrumbsHelperAddManyRector extends AbstractRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Change BreadcrumbsHelper::add(array) to addMany() and prepend(array) to prependMany()',
[
new CodeSample(
<<<'CODE_SAMPLE'
$this->Breadcrumbs->add([
['title' => 'Home', 'url' => '/'],
['title' => 'Articles', 'url' => '/articles'],
]);
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
$this->Breadcrumbs->addMany([
['title' => 'Home', 'url' => '/'],
['title' => 'Articles', 'url' => '/articles'],
]);
CODE_SAMPLE,
),
],
);
}

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

public function refactor(Node $node): ?Node
{
if (!$node instanceof MethodCall) {
return null;
}

// Must be add or prepend method
if (!$node->name instanceof Identifier) {
return null;
}

$methodName = $node->name->toString();
if ($methodName !== 'add' && $methodName !== 'prepend') {
return null;
}

// Must have at least one argument
if (count($node->args) < 1) {
return null;
}

// First argument must be an array
$firstArg = $node->args[0]->value;
if (!$firstArg instanceof Array_) {
return null;
}

// Check if the array looks like an array of crumbs (array of arrays)
// rather than a single crumb with title/url keys
if (!$this->isArrayOfCrumbs($firstArg)) {
return null;
}

// Check if this is called on BreadcrumbsHelper
$callerType = $this->getType($node->var);
if (!$callerType instanceof ObjectType) {
return null;
}

if (!$callerType->isInstanceOf('Cake\View\Helper\BreadcrumbsHelper')->yes()) {
return null;
}

// Rename to addMany or prependMany
$newMethodName = $methodName === 'add' ? 'addMany' : 'prependMany';
$node->name = new Identifier($newMethodName);

return $node;
}

/**
* Determine if an array is an array of crumbs (array of arrays)
* vs a single crumb (array with title/url keys)
*/
private function isArrayOfCrumbs(Array_ $array): bool
{
// An array of crumbs is typically a numerically indexed array of arrays
// e.g. [['title' => 'Home'], ['title' => 'Articles']]
// A single crumb would have string keys like 'title', 'url'
// e.g. ['title' => 'Home', 'url' => '/']

if (count($array->items) === 0) {
return false;
}

foreach ($array->items as $item) {
if (!$item instanceof ArrayItem) {
continue;
}

// If item has a string key like 'title' or 'url', it's a single crumb
if ($item->key instanceof String_) {
$keyValue = $item->key->value;
if ($keyValue === 'title' || $keyValue === 'url' || $keyValue === 'options') {
return false;
}
}

// If the item value is an array, it's likely an array of crumbs
if ($item->value instanceof Array_) {
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);

namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\BreadcrumbsHelperAddManyRector;

use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class BreadcrumbsHelperAddManyRectorTest extends AbstractRectorTestCase
{
/**
* @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';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\BreadcrumbsHelperAddManyRector\Fixture;

use Cake\View\Helper\BreadcrumbsHelper;

class SomeView
{
private BreadcrumbsHelper $Breadcrumbs;

public function someMethod()
{
// Should transform: add with array of crumbs
$this->Breadcrumbs->add([
['title' => 'Home', 'url' => '/'],
['title' => 'Articles', 'url' => '/articles'],
]);

// Should transform: prepend with array of crumbs
$this->Breadcrumbs->prepend([
['title' => 'Dashboard'],
]);
}
}

?>
-----
<?php

namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\BreadcrumbsHelperAddManyRector\Fixture;

use Cake\View\Helper\BreadcrumbsHelper;

class SomeView
{
private BreadcrumbsHelper $Breadcrumbs;

public function someMethod()
{
// Should transform: add with array of crumbs
$this->Breadcrumbs->addMany([
['title' => 'Home', 'url' => '/'],
['title' => 'Articles', 'url' => '/articles'],
]);

// Should transform: prepend with array of crumbs
$this->Breadcrumbs->prependMany([
['title' => 'Dashboard'],
]);
}
}

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

namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\BreadcrumbsHelperAddManyRector\Fixture;

use Cake\View\Helper\BreadcrumbsHelper;

class SomeView
{
private BreadcrumbsHelper $Breadcrumbs;

public function someMethod()
{
// Should NOT transform: single crumb with title/url
$this->Breadcrumbs->add('Home', '/');

// Should NOT transform: single crumb as array with title key
$this->Breadcrumbs->add(['title' => 'Home', 'url' => '/']);

// Should NOT transform: prepend single crumb
$this->Breadcrumbs->prepend('Dashboard');
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);

use Cake\Upgrade\Rector\Rector\MethodCall\BreadcrumbsHelperAddManyRector;
use Rector\Config\RectorConfig;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(BreadcrumbsHelperAddManyRector::class);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
use Cake\ORM\Entity;
use Cake\ORM\Locator\LocatorAwareTrait;
use Cake\ORM\Query;
use Cake\View\Helper\BreadcrumbsHelper;

class SomeTest
{
use LocatorAwareTrait;

private BreadcrumbsHelper $Breadcrumbs;

public function testRenames(): void
{
$entity = new Entity();
Expand All @@ -24,6 +27,13 @@ public function testRenames(): void
$table = $this->fetchTable('Articles');
$expr = $table->find()->newExpr();

// BreadcrumbsHelper::add(array) should be changed to addMany()
$this->Breadcrumbs->add([
['title' => 'Home', 'url' => '/'],
]);
// Single crumb should stay as is
$this->Breadcrumbs->add('Articles', '/articles');

// TypeFactory::getMap($type) should be changed to getMapped($type)
$class = TypeFactory::getMap('datetime');
$allTypes = TypeFactory::getMap(); // This should stay as is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
use Cake\ORM\Entity;
use Cake\ORM\Locator\LocatorAwareTrait;
use Cake\ORM\Query;
use Cake\View\Helper\BreadcrumbsHelper;

class SomeTest
{
use LocatorAwareTrait;

private BreadcrumbsHelper $Breadcrumbs;

public function testRenames(): void
{
$entity = new Entity();
Expand All @@ -24,6 +27,13 @@ public function testRenames(): void
$table = $this->fetchTable('Articles');
$expr = $table->find()->expr();

// BreadcrumbsHelper::add(array) should be changed to addMany()
$this->Breadcrumbs->addMany([
['title' => 'Home', 'url' => '/'],
]);
// Single crumb should stay as is
$this->Breadcrumbs->add('Articles', '/articles');

// TypeFactory::getMap($type) should be changed to getMapped($type)
$class = TypeFactory::getMapped('datetime');
$allTypes = TypeFactory::getMap(); // This should stay as is
Expand Down