Skip to content

Commit

Permalink
feat: JIRA-13731 add withoutTimestamps phpstan rule
Browse files Browse the repository at this point in the history
- Added rule to confirm that withoutTimestamps is used in migrations
  • Loading branch information
bpotmalnik committed Oct 11, 2024
1 parent 203b9af commit 7dfae2a
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 0 deletions.
55 changes: 55 additions & 0 deletions src/PHPStan/Laravel/Migrations/RequireWithoutTimestampsRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Worksome\CodingStyle\PHPStan\Laravel\Migrations;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use Illuminate\Support\Str;

/**
* Custom PHPStan rule to check if migration files using 'update' or 'insert'
* methods include 'withoutTimestamps' somewhere in the file.
*/
class RequireWithoutTimestampsRule implements Rule
{
public function getNodeType(): string
{
return Node\Stmt\Class_::class;
}

/**
* @param Node\Stmt\Class_ $node
* @param Scope $scope
*/
public function processNode(Node $node, Scope $scope): array
{
$filePath = $scope->getFile();

if (! Str::contains($filePath, 'database/migrations')) {
return [];
}

$fileContent = file_get_contents($filePath);

if (preg_match('/\b(update|insert|save|saveQuietly|create)\s*\(/', $fileContent)) {
if (!str_contains($fileContent, 'withoutTimestamps')) {
$filenameWithExtension = basename($filePath);
$filename = Str::before($filenameWithExtension, '.');

return [
RuleErrorBuilder::message(
"$filename file uses 'update' or 'create' action without 'withoutTimestamps()' protection."
)
->identifier('worksome.requireWithoutTimestamps')
->build(),
];
}
}

return [];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('test', 50)
->nullable();
});

User::withTrashed()->update([
'test' => 'test',
]);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('test', 50)
->nullable();
});

User::withoutTimestamps(function(){
User::withTrashed() ->update([
'test' => 'test',
]);
});
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Worksome\CodingStyle\Tests\PHPStan\Laravel\EnforceKebabCaseArtisanCommandsRule;

use Worksome\CodingStyle\PHPStan\Laravel\Migrations\RequireWithoutTimestampsRule;

it('checks withoutTimestamps in migration rule', function (string $path, array ...$errors) {
$this->rule = new RequireWithoutTimestampsRule();

expect($path)->toHaveRuleErrors($errors);
})->with([
'valid migration' => [
__DIR__ . '/Fixture/database/migrations/valid_migration.php.inc',
],
'invalid migration' => [
__DIR__ . '/Fixture/database/migrations/invalid_migration.php.inc',
[
"invalid_migration file uses 'update' or 'create' action without 'withoutTimestamps()' protection.",
7,
],
],
]);

0 comments on commit 7dfae2a

Please sign in to comment.