Skip to content
Open
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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,17 @@ Ensures each migration creates at most one table.
|---|---|
| [Phinx](./src/Rules/Phinx/ForbidMultipleTableCreationsRule.php) | Multiple calls to `create()` on table instances |
| [Laravel](./src/Rules/Laravel/ForbidMultipleTableCreationsRule.php) | Multiple `Schema::create()` calls in the same migration |

---

### Rule: `NoDownMethodRule`
Forbids the usage of the `down` method in migrations.
> Useful for teams that prefer forward-only migrations or rely solely on the `change` method for extensive rollback support where possible.

#### Support

| Framework | Forbidden usage |
|---|---|
| [Phinx](./src/Rules/Phinx/NoDownMethodRule.php) | `public function down(): void` |
| [Laravel](./src/Rules/Laravel/NoDownMethodRule.php) | `public function down(): void` |

10 changes: 10 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,15 @@ services:

-
class: PhpStanMigrationRules\Rules\Laravel\ForbidMultipleTableCreationsRule
tags:
- phpstan.rules.rule

-
class: PhpStanMigrationRules\Rules\Laravel\NoDownMethodRule
tags:
- phpstan.rules.rule

-
class: PhpStanMigrationRules\Rules\Phinx\NoDownMethodRule
tags:
- phpstan.rules.rule
47 changes: 47 additions & 0 deletions src/Rules/Laravel/NoDownMethodRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace PhpStanMigrationRules\Rules\Laravel;

use PhpParser\Node;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\RuleErrorBuilder;

/**
* @extends LaravelRule<ClassMethod>
*/
final class NoDownMethodRule extends LaravelRule
{
private const string RULE_IDENTIFIER = 'laravel.schema.noDownMethod';

private const string MESSAGE = 'Forbidden: "down" method. Use "change" method for reversible migrations, or forward-only migrations.';

public function getNodeType(): string
{
return ClassMethod::class;
}

public function processNode(Node $node, Scope $scope): array
{
if (!$this->isLaravelMigration($scope)) {
return [];
}

if ($node->name->toString() !== 'down') {
return [];
}

if (!$node->isPublic()) {
return [];
}

return [
RuleErrorBuilder::message(self::MESSAGE)
->identifier(self::RULE_IDENTIFIER)
->tip('If you must rollback, consider creating a new migration that reverses these changes.')
->build(),
];
}
}
47 changes: 47 additions & 0 deletions src/Rules/Phinx/NoDownMethodRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace PhpStanMigrationRules\Rules\Phinx;

use PhpParser\Node;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\RuleErrorBuilder;

/**
* @extends PhinxRule<ClassMethod>
*/
final class NoDownMethodRule extends PhinxRule
{
private const string RULE_IDENTIFIER = 'phinx.schema.noDownMethod';

private const string MESSAGE = 'Forbidden: "down" method. Use "change" method for reversible migrations, or forward-only migrations.';

public function getNodeType(): string
{
return ClassMethod::class;
}

public function processNode(Node $node, Scope $scope): array
{
if (!$this->isPhinxMigration($scope)) {
return [];
}

if ($node->name->toString() !== 'down') {
return [];
}

if (!$node->isPublic()) {
return [];
}

return [
RuleErrorBuilder::message(self::MESSAGE)
->identifier(self::RULE_IDENTIFIER)
->tip('If you must rollback, consider creating a new migration that reverses these changes.')
->build(),
];
}
}
56 changes: 56 additions & 0 deletions tests/Rules/Laravel/NoDownMethodRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace PhpStanMigrationRules\Tests\Rules\Laravel;

use PhpStanMigrationRules\Rules\Laravel\NoDownMethodRule;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<NoDownMethodRule>
*/
final class NoDownMethodRuleTest extends RuleTestCase
{
protected function getRule(): Rule
{
return new NoDownMethodRule();
}

public function testReportsDownMethod(): void
{
$this->analyse(
[__DIR__ . '/fixtures/NoDownMethod.php'],
[
[
'Forbidden: "down" method. Use "change" method for reversible migrations, or forward-only migrations.',
11,
'If you must rollback, consider creating a new migration that reverses these changes.',
],
]
);
}

public function testReportsDownMethodInAnonymousClass(): void
{
$this->analyse(
[__DIR__ . '/fixtures/NoDownMethodAnonymous.php'],
[
[
'Forbidden: "down" method. Use "change" method for reversible migrations, or forward-only migrations.',
11,
'If you must rollback, consider creating a new migration that reverses these changes.',
],
]
);
}

public function testDoesNotReportChangeMethod(): void
{
$this->analyse(
[__DIR__ . '/fixtures/WithChangeMethod.php'],
[]
);
}
}
15 changes: 15 additions & 0 deletions tests/Rules/Laravel/fixtures/NoDownMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PhpStanMigrationRules\Tests\Rules\Laravel\fixtures;

use Illuminate\Database\Migrations\Migration;

class NoDownMethod extends Migration
{
public function down(): void
{
// ...
}
}
15 changes: 15 additions & 0 deletions tests/Rules/Laravel/fixtures/NoDownMethodAnonymous.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PhpStanMigrationRules\Tests\Rules\Laravel\fixtures;

use Illuminate\Database\Migrations\Migration;

return new class extends Migration
{
public function down(): void
{
// ...
}
};
15 changes: 15 additions & 0 deletions tests/Rules/Laravel/fixtures/WithChangeMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PhpStanMigrationRules\Tests\Rules\Laravel\fixtures;

use Illuminate\Database\Migrations\Migration;

class WithChangeMethod extends Migration
{
public function change(): void
{
// ...
}
}
42 changes: 42 additions & 0 deletions tests/Rules/Phinx/NoDownMethodRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace PhpStanMigrationRules\Tests\Rules\Phinx;

use PhpStanMigrationRules\Rules\Phinx\NoDownMethodRule;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<NoDownMethodRule>
*/
final class NoDownMethodRuleTest extends RuleTestCase
{
protected function getRule(): Rule
{
return new NoDownMethodRule();
}

public function testReportsDownMethod(): void
{
$this->analyse(
[__DIR__ . '/fixtures/NoDownMethod.php'],
[
[
'Forbidden: "down" method. Use "change" method for reversible migrations, or forward-only migrations.',
11,
'If you must rollback, consider creating a new migration that reverses these changes.',
],
]
);
}

public function testDoesNotReportChangeMethod(): void
{
$this->analyse(
[__DIR__ . '/fixtures/WithChangeMethod.php'],
[]
);
}
}
15 changes: 15 additions & 0 deletions tests/Rules/Phinx/fixtures/NoDownMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PhpStanMigrationRules\Tests\Rules\Phinx\fixtures;

use Phinx\Migration\AbstractMigration;

class NoDownMethod extends AbstractMigration
{
public function down(): void
{
// ...
}
}
15 changes: 15 additions & 0 deletions tests/Rules/Phinx/fixtures/WithChangeMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace PhpStanMigrationRules\Tests\Rules\Phinx\fixtures;

use Phinx\Migration\AbstractMigration;

class WithChangeMethod extends AbstractMigration
{
public function change(): void
{
// ...
}
}