Skip to content

Commit c3ba0a8

Browse files
authored
Merge pull request #395 from cakephp/6.x-merge
6.x merge
2 parents 9b77640 + ca61a8c commit c3ba0a8

File tree

16 files changed

+475
-3
lines changed

16 files changed

+475
-3
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ jobs:
4747
run: |
4848
if ${{ matrix.prefer-lowest == 'prefer-lowest' }}; then
4949
make install-dev-lowest
50-
elif ${{ matrix.php-version == '8.2' }}; then
51-
make install-dev-ignore-reqs
5250
else
5351
make install-dev
5452
fi

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ DEV_DEPENDENCIES = cakephp/cakephp:5.x-dev \
44
cakephp/cakephp-codesniffer:^5.0 \
55
mikey179/vfsstream:^1.6.8 \
66
phpunit/phpunit:^10.5.38 \
7-
cakephp/migrations:^4.5.0
7+
cakephp/migrations:^5.0.0
88

99
install-dev:
1010
composer require --dev $(DEV_DEPENDENCIES)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# CakePHP Upgrade tool
22

33
[![CI](https://github.com/cakephp/upgrade/actions/workflows/ci.yml/badge.svg)](https://github.com/cakephp/upgrade/actions/workflows/ci.yml)
4+
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%208.1-8892BF.svg)](https://php.net/)
5+
[![License](https://poser.pugx.org/cakephp/upgrade/license.svg)](LICENSE)
46

57
Upgrade tools for CakePHP meant to facilitate migrating between CakePHP 4.x
68
versions, from CakePHP 4.x to CakePHP 5.x, and between CakePHP 5.x versions.

config/rector/sets/cakephp53.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
<?php
22
declare(strict_types=1);
33

4+
use Cake\Upgrade\Rector\Cake5\BreadcrumbsHelperAddManyRector;
45
use Cake\Upgrade\Rector\Cake5\EntityIsEmptyRector;
56
use Cake\Upgrade\Rector\Cake5\EntityPatchRector;
67
use Cake\Upgrade\Rector\Cake5\FormExecuteToProcessRector;
78
use Cake\Upgrade\Rector\Cake5\NewExprToFuncRector;
89
use Cake\Upgrade\Rector\Cake5\QueryParamAccessRector;
10+
use Cake\Upgrade\Rector\Cake5\TypeFactoryGetMappedRector;
911
use Rector\Config\RectorConfig;
1012
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
1113
use Rector\Renaming\Rector\Name\RenameClassRector;
@@ -24,8 +26,10 @@
2426
'Cake\TestSuite\Fixture\TransactionFixtureStrategy' => 'Cake\TestSuite\Fixture\TransactionStrategy',
2527
'Cake\TestSuite\Fixture\TruncateFixtureStrategy' => 'Cake\TestSuite\Fixture\TruncateStrategy',
2628
]);
29+
$rectorConfig->rule(BreadcrumbsHelperAddManyRector::class);
2730
$rectorConfig->rule(EntityIsEmptyRector::class);
2831
$rectorConfig->rule(EntityPatchRector::class);
2932
$rectorConfig->rule(FormExecuteToProcessRector::class);
3033
$rectorConfig->rule(QueryParamAccessRector::class);
34+
$rectorConfig->rule(TypeFactoryGetMappedRector::class);
3135
};
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Cake\Upgrade\Rector\Cake5;
5+
6+
use PhpParser\Node;
7+
use PhpParser\Node\ArrayItem;
8+
use PhpParser\Node\Expr\Array_;
9+
use PhpParser\Node\Expr\MethodCall;
10+
use PhpParser\Node\Identifier;
11+
use PhpParser\Node\Scalar\String_;
12+
use PHPStan\Type\ObjectType;
13+
use Rector\Rector\AbstractRector;
14+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
15+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
16+
17+
/**
18+
* Transforms BreadcrumbsHelper::add(array) to addMany() and prepend(array) to prependMany()
19+
*
20+
* In CakePHP 5.3, passing an array of crumbs to add() or prepend() is deprecated.
21+
* Use addMany() or prependMany() instead.
22+
*
23+
* @see https://book.cakephp.org/5/en/appendices/5-3-migration-guide.html
24+
*/
25+
final class BreadcrumbsHelperAddManyRector extends AbstractRector
26+
{
27+
public function getRuleDefinition(): RuleDefinition
28+
{
29+
return new RuleDefinition(
30+
'Change BreadcrumbsHelper::add(array) to addMany() and prepend(array) to prependMany()',
31+
[
32+
new CodeSample(
33+
<<<'CODE_SAMPLE'
34+
$this->Breadcrumbs->add([
35+
['title' => 'Home', 'url' => '/'],
36+
['title' => 'Articles', 'url' => '/articles'],
37+
]);
38+
CODE_SAMPLE
39+
,
40+
<<<'CODE_SAMPLE'
41+
$this->Breadcrumbs->addMany([
42+
['title' => 'Home', 'url' => '/'],
43+
['title' => 'Articles', 'url' => '/articles'],
44+
]);
45+
CODE_SAMPLE,
46+
),
47+
],
48+
);
49+
}
50+
51+
public function getNodeTypes(): array
52+
{
53+
return [MethodCall::class];
54+
}
55+
56+
public function refactor(Node $node): ?Node
57+
{
58+
if (!$node instanceof MethodCall) {
59+
return null;
60+
}
61+
62+
// Must be add or prepend method
63+
if (!$node->name instanceof Identifier) {
64+
return null;
65+
}
66+
67+
$methodName = $node->name->toString();
68+
if ($methodName !== 'add' && $methodName !== 'prepend') {
69+
return null;
70+
}
71+
72+
// Must have at least one argument
73+
if (count($node->args) < 1) {
74+
return null;
75+
}
76+
77+
// First argument must be an array
78+
$firstArg = $node->args[0]->value;
79+
if (!$firstArg instanceof Array_) {
80+
return null;
81+
}
82+
83+
// Check if the array looks like an array of crumbs (array of arrays)
84+
// rather than a single crumb with title/url keys
85+
if (!$this->isArrayOfCrumbs($firstArg)) {
86+
return null;
87+
}
88+
89+
// Check if this is called on BreadcrumbsHelper
90+
$callerType = $this->getType($node->var);
91+
if (!$callerType instanceof ObjectType) {
92+
return null;
93+
}
94+
95+
if (!$callerType->isInstanceOf('Cake\View\Helper\BreadcrumbsHelper')->yes()) {
96+
return null;
97+
}
98+
99+
// Rename to addMany or prependMany
100+
$newMethodName = $methodName === 'add' ? 'addMany' : 'prependMany';
101+
$node->name = new Identifier($newMethodName);
102+
103+
return $node;
104+
}
105+
106+
/**
107+
* Determine if an array is an array of crumbs (array of arrays)
108+
* vs a single crumb (array with title/url keys)
109+
*/
110+
private function isArrayOfCrumbs(Array_ $array): bool
111+
{
112+
// An array of crumbs is typically a numerically indexed array of arrays
113+
// e.g. [['title' => 'Home'], ['title' => 'Articles']]
114+
// A single crumb would have string keys like 'title', 'url'
115+
// e.g. ['title' => 'Home', 'url' => '/']
116+
117+
if (count($array->items) === 0) {
118+
return false;
119+
}
120+
121+
foreach ($array->items as $item) {
122+
if (!$item instanceof ArrayItem) {
123+
continue;
124+
}
125+
126+
// If item has a string key like 'title' or 'url', it's a single crumb
127+
if ($item->key instanceof String_) {
128+
$keyValue = $item->key->value;
129+
if ($keyValue === 'title' || $keyValue === 'url' || $keyValue === 'options') {
130+
return false;
131+
}
132+
}
133+
134+
// If the item value is an array, it's likely an array of crumbs
135+
if ($item->value instanceof Array_) {
136+
return true;
137+
}
138+
}
139+
140+
return false;
141+
}
142+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Cake\Upgrade\Rector\Cake5;
5+
6+
use PhpParser\Node;
7+
use PhpParser\Node\Expr\StaticCall;
8+
use PhpParser\Node\Identifier;
9+
use PhpParser\Node\Name;
10+
use Rector\Rector\AbstractRector;
11+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
12+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
13+
14+
/**
15+
* Transforms TypeFactory::getMap($type) to TypeFactory::getMapped($type)
16+
*
17+
* In CakePHP 5.3, calling getMap() with a type argument is deprecated.
18+
* Use getMapped() instead for single-type lookups.
19+
*
20+
* @see https://book.cakephp.org/5/en/appendices/5-3-migration-guide.html
21+
*/
22+
final class TypeFactoryGetMappedRector extends AbstractRector
23+
{
24+
public function getRuleDefinition(): RuleDefinition
25+
{
26+
return new RuleDefinition(
27+
'Change TypeFactory::getMap($type) to TypeFactory::getMapped($type)',
28+
[
29+
new CodeSample(
30+
<<<'CODE_SAMPLE'
31+
use Cake\Database\TypeFactory;
32+
33+
$class = TypeFactory::getMap('datetime');
34+
CODE_SAMPLE
35+
,
36+
<<<'CODE_SAMPLE'
37+
use Cake\Database\TypeFactory;
38+
39+
$class = TypeFactory::getMapped('datetime');
40+
CODE_SAMPLE,
41+
),
42+
],
43+
);
44+
}
45+
46+
public function getNodeTypes(): array
47+
{
48+
return [StaticCall::class];
49+
}
50+
51+
public function refactor(Node $node): ?Node
52+
{
53+
if (!$node instanceof StaticCall) {
54+
return null;
55+
}
56+
57+
// Must be getMap method
58+
if (!$node->name instanceof Identifier || $node->name->toString() !== 'getMap') {
59+
return null;
60+
}
61+
62+
// Must have at least one argument (the type)
63+
if (count($node->args) < 1) {
64+
return null;
65+
}
66+
67+
// Check if this is called on TypeFactory
68+
if (!$node->class instanceof Name) {
69+
return null;
70+
}
71+
72+
$className = $node->class->toString();
73+
if ($className !== 'Cake\Database\TypeFactory' && $className !== 'TypeFactory') {
74+
return null;
75+
}
76+
77+
// Rename to getMapped
78+
$node->name = new Identifier('getMapped');
79+
80+
return $node;
81+
}
82+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\BreadcrumbsHelperAddManyRector;
5+
6+
use Iterator;
7+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
8+
9+
final class BreadcrumbsHelperAddManyRectorTest extends AbstractRectorTestCase
10+
{
11+
/**
12+
* @dataProvider provideData()
13+
*/
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\BreadcrumbsHelperAddManyRector\Fixture;
4+
5+
use Cake\View\Helper\BreadcrumbsHelper;
6+
7+
class SomeView
8+
{
9+
private BreadcrumbsHelper $Breadcrumbs;
10+
11+
public function someMethod()
12+
{
13+
// Should transform: add with array of crumbs
14+
$this->Breadcrumbs->add([
15+
['title' => 'Home', 'url' => '/'],
16+
['title' => 'Articles', 'url' => '/articles'],
17+
]);
18+
19+
// Should transform: prepend with array of crumbs
20+
$this->Breadcrumbs->prepend([
21+
['title' => 'Dashboard'],
22+
]);
23+
}
24+
}
25+
26+
?>
27+
-----
28+
<?php
29+
30+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\BreadcrumbsHelperAddManyRector\Fixture;
31+
32+
use Cake\View\Helper\BreadcrumbsHelper;
33+
34+
class SomeView
35+
{
36+
private BreadcrumbsHelper $Breadcrumbs;
37+
38+
public function someMethod()
39+
{
40+
// Should transform: add with array of crumbs
41+
$this->Breadcrumbs->addMany([
42+
['title' => 'Home', 'url' => '/'],
43+
['title' => 'Articles', 'url' => '/articles'],
44+
]);
45+
46+
// Should transform: prepend with array of crumbs
47+
$this->Breadcrumbs->prependMany([
48+
['title' => 'Dashboard'],
49+
]);
50+
}
51+
}
52+
53+
?>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Cake\Upgrade\Test\TestCase\Rector\MethodCall\BreadcrumbsHelperAddManyRector\Fixture;
4+
5+
use Cake\View\Helper\BreadcrumbsHelper;
6+
7+
class SomeView
8+
{
9+
private BreadcrumbsHelper $Breadcrumbs;
10+
11+
public function someMethod()
12+
{
13+
// Should NOT transform: single crumb with title/url
14+
$this->Breadcrumbs->add('Home', '/');
15+
16+
// Should NOT transform: single crumb as array with title key
17+
$this->Breadcrumbs->add(['title' => 'Home', 'url' => '/']);
18+
19+
// Should NOT transform: prepend single crumb
20+
$this->Breadcrumbs->prepend('Dashboard');
21+
}
22+
}
23+
24+
?>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
use Cake\Upgrade\Rector\Cake5\BreadcrumbsHelperAddManyRector;
5+
use Rector\Config\RectorConfig;
6+
7+
return static function (RectorConfig $rectorConfig): void {
8+
$rectorConfig->rule(BreadcrumbsHelperAddManyRector::class);
9+
};

0 commit comments

Comments
 (0)