feat: Add a psalm plugin to forbid static properties and variables

Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
This commit is contained in:
Côme Chilliet 2026-03-13 10:20:10 +01:00
parent 0d90f4272a
commit 8b5bc09cbe
No known key found for this signature in database
GPG key ID: A3E2F658B28C760A
3 changed files with 78 additions and 0 deletions

View file

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
use PhpParser\Node\Stmt\Property;
use Psalm\CodeLocation;
use Psalm\IssueBuffer;
use Psalm\Plugin\EventHandler\AfterClassLikeVisitInterface;
use Psalm\Plugin\EventHandler\AfterStatementAnalysisInterface;
use Psalm\Plugin\EventHandler\Event\AfterClassLikeVisitEvent;
use Psalm\Plugin\EventHandler\Event\AfterStatementAnalysisEvent;
/**
* Complains about static property in classes and static vars in methods
*/
class StaticVarsChecker implements AfterClassLikeVisitInterface, AfterStatementAnalysisInterface {
public static function afterClassLikeVisit(AfterClassLikeVisitEvent $event): void {
$classLike = $event->getStmt();
$statementsSource = $event->getStatementsSource();
foreach ($classLike->stmts as $stmt) {
if ($stmt instanceof Property) {
if ($stmt->isStatic()) {
IssueBuffer::maybeAdd(
// ImpureStaticProperty is close enough, all static properties are impure to my eyes
new \Psalm\Issue\ImpureStaticProperty(
'Static property should not be used as they do not follow requests lifecycle',
new CodeLocation($statementsSource, $stmt),
)
);
}
}
}
}
public static function afterStatementAnalysis(AfterStatementAnalysisEvent $event): ?bool {
$stmt = $event->getStmt();
if ($stmt instanceof PhpParser\Node\Stmt\Static_) {
IssueBuffer::maybeAdd(
// Same logic
new \Psalm\Issue\ImpureStaticVariable(
'Static var should not be used as they do not follow requests lifecycle and are hard to reset',
new CodeLocation($event->getStatementsSource(), $stmt),
)
);
}
return null;
}
}

View file

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
class StaticVarsTest {
public static $forbiddenStaticProperty;
protected static $forbiddenProtectedStaticProperty;
private static $forbiddenPrivateStaticProperty;
private static $forbiddenPrivateStaticPropertyWithValue = [];
public function normalFunction(): void {
static $forbiddenStaticVar = false;
}
public static function staticFunction(): void {
static $forbiddenStaticVar = false;
}
}

View file

@ -21,6 +21,7 @@
<plugin filename="build/psalm/AttributeNamedParameters.php" />
<plugin filename="build/psalm/LogicalOperatorChecker.php" />
<pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
<plugin filename="build/psalm/StaticVarsChecker.php" />
</plugins>
<projectFiles>
<directory name="apps/admin_audit"/>