Skip to content

Commit ca5d80c

Browse files
authored
chore: codebase cleanup (#3)
1 parent 6b0e397 commit ca5d80c

21 files changed

Lines changed: 310 additions & 238 deletions

.github/workflows/check-pr.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ jobs:
5757
${{ runner.os }}-pint-
5858
5959
- name: 🔧️ Run Rector
60-
run: composer rector:dry
60+
run: composer rector
6161

6262
- name: 🍺 Run Laravel Pint
63-
run: composer format:check
63+
run: composer format:php
6464

6565
- name: 📷 PHPStan Result Cache
6666
uses: actions/cache@v5
@@ -235,7 +235,7 @@ jobs:
235235

236236
- name: 💬 Coverage PR Comment
237237
if: github.event_name == 'pull_request' && steps.coverage.outputs.file != ''
238-
uses: marocchino/sticky-pull-request-comment@v2
238+
uses: marocchino/sticky-pull-request-comment@v3
239239
with:
240240
recreate: true
241241
path: code-coverage-results.md

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"lab404/laravel-impersonate": "^1.7",
2828
"larastan/larastan": "^3.0",
2929
"laravel/pint": "^1.21",
30-
"orchestra/testbench": "^10.0",
30+
"orchestra/testbench": "^10.0||^11.0",
3131
"pestphp/pest": "^4.1",
3232
"phpunit/phpunit": "^12.0",
3333
"rector/rector": "^2.0",

phpstan.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@ parameters:
1717
-
1818
message: '#Class App\\Console\\Commands\\#'
1919
path: src/ChassisServiceProvider.php
20+
# Scope is class-level generic in Laravel 13 but not in 12. We annotate
21+
# for 13; silence the "not generic" complaint for 12.
22+
-
23+
identifier: generics.notGeneric
24+
path: src/Models/Scopes/AutomaticallyOrderedScope.php

rector.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
SetList::DEAD_CODE,
2626
SetList::EARLY_RETURN,
2727
SetList::TYPE_DECLARATION,
28+
SetList::INSTANCEOF,
2829
])
2930
->withDowngradeSets(php83: true)
3031
->withPreparedSets(
@@ -43,6 +44,47 @@
4344
AddOverrideAttributeToOverriddenMethodsRector::class,
4445
])
4546
->withRules([
47+
// Laravel: classes / migrations / model structure
48+
RectorLaravel\Rector\Class_\AnonymousMigrationsRector::class,
49+
RectorLaravel\Rector\Class_\AddExtendsAnnotationToModelFactoriesRector::class,
50+
RectorLaravel\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector::class,
51+
RectorLaravel\Rector\ClassMethod\MakeModelAttributesAndScopesProtectedRector::class,
52+
53+
// Laravel: query builder / Eloquent typing and relation helpers
54+
RectorLaravel\Rector\MethodCall\EloquentWhereRelationTypeHintingParameterRector::class,
55+
RectorLaravel\Rector\MethodCall\EloquentWhereTypeHintClosureParameterRector::class,
56+
RectorLaravel\Rector\MethodCall\EloquentOrderByToLatestOrOldestRector::class,
57+
58+
// Laravel: collections / fluent chain cleanup
59+
RectorLaravel\Rector\BooleanNot\AvoidNegatedCollectionContainsOrDoesntContainRector::class,
60+
RectorLaravel\Rector\MethodCall\AvoidNegatedCollectionFilterOrRejectRector::class,
61+
RectorLaravel\Rector\MethodCall\ConvertEnumerableToArrayToAllRector::class,
62+
RectorLaravel\Rector\MethodCall\UnaliasCollectionMethodsRector::class,
63+
RectorLaravel\Rector\MethodCall\ReverseConditionableMethodCallRector::class,
64+
65+
// Laravel: control flow / defaults / app helpers
66+
RectorLaravel\Rector\If_\AbortIfRector::class,
67+
RectorLaravel\Rector\If_\ReportIfRector::class,
68+
RectorLaravel\Rector\Expr\AppEnvironmentComparisonToParameterRector::class,
69+
RectorLaravel\Rector\FuncCall\AppToResolveRector::class,
70+
71+
// Laravel: framework helper normalizations + misc refactors
72+
RectorLaravel\Rector\FuncCall\NotFilledBlankFuncCallToBlankFilledFuncCallRector::class,
73+
RectorLaravel\Rector\FuncCall\RemoveDumpDataDeadCodeRector::class,
74+
RectorLaravel\Rector\FuncCall\SleepFuncToSleepStaticCallRector::class,
75+
RectorLaravel\Rector\FuncCall\ThrowIfAndThrowUnlessExceptionsToUseClassStringRector::class,
76+
RectorLaravel\Rector\Expr\SubStrToStartsWithOrEndsWithStaticMethodCallRector\SubStrToStartsWithOrEndsWithStaticMethodCallRector::class,
77+
RectorLaravel\Rector\Cast\DatabaseExpressionCastsToMethodCallRector::class,
78+
RectorLaravel\Rector\PropertyFetch\OptionalToNullsafeOperatorRector::class,
79+
RectorLaravel\Rector\PropertyFetch\ReplaceFakerInstanceWithHelperRector::class,
80+
RectorLaravel\Rector\StaticCall\RouteActionCallableRector::class,
81+
82+
// Laravel :testing helpers
83+
RectorLaravel\Rector\MethodCall\AssertStatusToAssertMethodRector::class,
84+
RectorLaravel\Rector\MethodCall\JsonCallToExplicitJsonCallRector::class,
85+
RectorLaravel\Rector\StaticCall\AssertWithClassStringToTypeHintedClosureRector::class,
86+
RectorLaravel\Rector\StaticCall\CarbonSetTestNowToTravelToRector::class,
87+
4688
// PHPUnit: annotations to attributes + provider conventions
4789
PHPUnit\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector::class,
4890
PHPUnit\AnnotationsToAttributes\Rector\ClassMethod\DataProviderAnnotationToAttributeRector::class,
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Northwestern\SysDev\Chassis\Console\Commands\Migrate\Concerns;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Name;
9+
use PhpParser\Node\Stmt\Class_;
10+
use PhpParser\Node\Stmt\Namespace_;
11+
use PhpParser\Node\Stmt\Use_;
12+
use PhpParser\Node\Stmt\UseUse;
13+
use PhpParser\NodeTraverser;
14+
use PhpParser\NodeVisitor\CloningVisitor;
15+
use PhpParser\Parser;
16+
use PhpParser\PrettyPrinter\Standard;
17+
18+
trait InteractsWithAst
19+
{
20+
/**
21+
* @param array<Node\Stmt> $stmts
22+
* @return list<Node\Stmt>
23+
*/
24+
protected function cloneStatements(array $stmts): array
25+
{
26+
$cloneTraverser = new NodeTraverser();
27+
$cloneTraverser->addVisitor(new CloningVisitor());
28+
29+
/** @var list<Node\Stmt> $cloned */
30+
$cloned = $cloneTraverser->traverse($stmts);
31+
32+
return $cloned;
33+
}
34+
35+
/**
36+
* @param array<Node\Stmt> $stmts
37+
*/
38+
protected function findClassNode(array $stmts): ?Class_
39+
{
40+
foreach ($stmts as $stmt) {
41+
if ($stmt instanceof Class_) {
42+
return $stmt;
43+
}
44+
45+
if ($stmt instanceof Namespace_) {
46+
foreach ($stmt->stmts as $nsStmt) {
47+
if ($nsStmt instanceof Class_) {
48+
return $nsStmt;
49+
}
50+
}
51+
}
52+
}
53+
54+
return null;
55+
}
56+
57+
/**
58+
* @param array<Node\Stmt> $stmts
59+
*/
60+
protected function ensureUseStatement(array &$stmts, string $fqcn): void
61+
{
62+
foreach ($stmts as $stmt) {
63+
if ($stmt instanceof Namespace_) {
64+
$this->ensureUseStatementInList($stmt->stmts, $fqcn);
65+
66+
return;
67+
}
68+
}
69+
70+
$this->ensureUseStatementInList($stmts, $fqcn);
71+
}
72+
73+
/**
74+
* @param array<Node\Stmt> $newStmts
75+
* @param array<Node\Stmt> $oldStmts
76+
*/
77+
protected function formatPreservingPrint(Standard $printer, Parser $parser, array $newStmts, array $oldStmts): string
78+
{
79+
return $printer->printFormatPreserving($newStmts, $oldStmts, $parser->getTokens());
80+
}
81+
82+
/**
83+
* @param array<Node\Stmt> $stmts
84+
*/
85+
private function ensureUseStatementInList(array &$stmts, string $fqcn): void
86+
{
87+
foreach ($stmts as $stmt) {
88+
if ($stmt instanceof Use_) {
89+
foreach ($stmt->uses as $use) {
90+
if ($use->name->toString() === $fqcn) {
91+
return;
92+
}
93+
}
94+
}
95+
}
96+
97+
$lastUseIndex = -1;
98+
foreach ($stmts as $i => $stmt) {
99+
if ($stmt instanceof Use_) {
100+
$lastUseIndex = $i;
101+
}
102+
}
103+
104+
$newUse = new Use_([new UseUse(new Name($fqcn))]);
105+
106+
if ($lastUseIndex >= 0) {
107+
array_splice($stmts, $lastUseIndex + 1, 0, [$newUse]);
108+
} else {
109+
array_splice($stmts, 1, 0, [$newUse]);
110+
}
111+
}
112+
}

src/Console/Commands/Migrate/Concerns/TracksChanges.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ protected function recordChange(MigrationContext $context, string $file, int $li
2323
/**
2424
* Increment a named counter on the context.
2525
*
26-
* @param 'namespacesRewritten'|'filesDeleted'|'filesScaffolded' $counter
26+
* @param 'namespacesRewritten'|'filesDeleted'|'filesCreated'|'filesModified' $counter
2727
*/
2828
protected function incrementCounter(MigrationContext $context, string $counter, int $amount = 1): void
2929
{

src/Console/Commands/Migrate/MigrateToChassisCommand.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function handle(): int
5454
$this->newLine();
5555
$this->displayReport($context);
5656

57-
if ($isDryRun && ($context->namespacesRewritten > 0 || $context->filesDeleted > 0 || $context->filesScaffolded > 0)) {
57+
if ($isDryRun && ($context->namespacesRewritten > 0 || $context->filesDeleted > 0 || $context->filesCreated > 0 || $context->filesModified > 0)) {
5858
$this->newLine();
5959
note('Run without --dry-run to apply these changes.');
6060
}
@@ -109,8 +109,9 @@ private function displayReport(MigrationContext $context): void
109109

110110
$rows = [
111111
['Namespace references rewritten', (string) $context->namespacesRewritten],
112+
['Files modified', (string) $context->filesModified],
113+
['Files created', (string) $context->filesCreated],
112114
['Files deleted', (string) $context->filesDeleted],
113-
['Files scaffolded', (string) $context->filesScaffolded],
114115
];
115116

116117
if ($context->conflicts !== []) {

src/Console/Commands/Migrate/MigrationContext.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ class MigrationContext
1818

1919
public int $filesDeleted = 0;
2020

21-
public int $filesScaffolded = 0;
21+
public int $filesCreated = 0;
22+
23+
public int $filesModified = 0;
2224

2325
/** @var list<array{string, int, string}> */
2426
public array $changeLog = [];
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Northwestern\SysDev\Chassis\Console\Commands\Migrate\Steps;
6+
7+
use Northwestern\SysDev\Chassis\Console\Commands\Migrate\Concerns\TracksChanges;
8+
use Northwestern\SysDev\Chassis\Console\Commands\Migrate\Contracts\MigrationStep;
9+
use Northwestern\SysDev\Chassis\Console\Commands\Migrate\MigrationContext;
10+
11+
abstract class AbstractMigrationStep implements MigrationStep
12+
{
13+
use TracksChanges;
14+
15+
protected function writeHeading(MigrationContext $context, string $message): void
16+
{
17+
$context->command->newLine();
18+
$context->command->getOutput()->writeln("<info>{$message}</info>");
19+
}
20+
21+
protected function success(MigrationContext $context, string $message): void
22+
{
23+
$context->command->line(" <fg=green>✓</> {$message}");
24+
}
25+
26+
protected function skip(MigrationContext $context, string $message): void
27+
{
28+
$context->command->line(" <fg=yellow>⊘</> {$message}");
29+
}
30+
31+
protected function note(MigrationContext $context, string $message): void
32+
{
33+
$context->command->line(" <fg=gray>{$message}</>");
34+
}
35+
36+
protected function relativePath(string $fullPath): string
37+
{
38+
return str_replace(base_path() . '/', '', $fullPath);
39+
}
40+
41+
protected function markFileCreated(MigrationContext $context, int $amount = 1): void
42+
{
43+
$this->incrementCounter($context, 'filesCreated', $amount);
44+
}
45+
46+
protected function markFileModified(MigrationContext $context, int $amount = 1): void
47+
{
48+
$this->incrementCounter($context, 'filesModified', $amount);
49+
}
50+
51+
protected function addConflict(MigrationContext $context, string $message): void
52+
{
53+
$context->conflicts[] = $message;
54+
}
55+
}

src/Console/Commands/Migrate/Steps/CleanPhpunitExclusionsStep.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
use DOMElement;
99
use DOMXPath;
1010
use Illuminate\Support\Facades\File;
11-
use Northwestern\SysDev\Chassis\Console\Commands\Migrate\Concerns\TracksChanges;
12-
use Northwestern\SysDev\Chassis\Console\Commands\Migrate\Contracts\MigrationStep;
1311
use Northwestern\SysDev\Chassis\Console\Commands\Migrate\MigrationContext;
1412

1513
/**
@@ -24,10 +22,8 @@
2422
* pointing to a file or directory still present on disk is left untouched, so
2523
* app-specific exclusions are preserved.
2624
*/
27-
class CleanPhpunitExclusionsStep implements MigrationStep
25+
class CleanPhpunitExclusionsStep extends AbstractMigrationStep
2826
{
29-
use TracksChanges;
30-
3127
/**
3228
* Candidate phpunit config files, in preference order.
3329
*
@@ -78,8 +74,10 @@ public function run(MigrationContext $context): void
7874
File::put($absolutePath, $output);
7975
}
8076

77+
$this->markFileModified($context);
78+
8179
foreach ($removed as $path) {
82-
$context->command->line(" <fg=green>✓</> {$relativePath} removed exclusion: {$path}");
80+
$this->success($context, "{$relativePath} removed exclusion: {$path}");
8381
}
8482
}
8583

0 commit comments

Comments
 (0)