Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend id vs id rule and add LabID rule #42

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
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: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,27 @@ See [GitHub releases](https://github.com/mll-lab/php-utils/releases).

## Unreleased

## v6.0.0

### Changed

- Breaking Change: Rename class `MLL\Utils\PHPStan\Rules\VariableNameIdToIDRule` to `MLL\Utils\PHPStan\Rules\NameIdToIDRule`
- Make `MLL\Utils\PHPStan\Rules\NameIdToIDRule` handle variable names, parameter names, method names, and class names for incorrect capitalization of `Id`
- Add `Idt` and `Identical` to the list of incorrect capitalizations for `Id` of `MLL\Utils\PHPStan\Rules\NameIdToIDRule`
- Add RuleIdentifier `mll.capitalizationOfID` to `MLL\Utils\PHPStan\Rules\NameIdToIDRule`
- Add PHPStan-rule `MLL\Utils\PHPStan\Rules\CanonicalCapitalizations` to check spelling of LabID

## v5.8.0

### Added

- Add PHPStan-Rule `MLL\Utils\PHPStan\Rules\ThrowableClassNameRule`
- Add PHPStan-rule `MLL\Utils\PHPStan\Rules\ThrowableClassNameRule`

## v5.7.0

### Added

- Add PHPStan-Rule `MLL\Utils\PHPStan\Rules\VariableNameIdToIDRule`
- Add PHPStan-rule `MLL\Utils\PHPStan\Rules\VariableNameIdToIDRule`

## v5.6.0

Expand Down
18 changes: 18 additions & 0 deletions src/PHPStan/NodeNameExtractor/ClassMethodNameExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types=1);

namespace MLL\Utils\PHPStan\NodeNameExtractor;

use PhpParser\Node;
use PhpParser\Node\Stmt\ClassMethod;

class ClassMethodNameExtractor implements NodeNameExtractor
{
public function extract(Node $node): ?string
{
if ($node instanceof ClassMethod) {
return $node->name->toString();
}

return null;
}
}
22 changes: 22 additions & 0 deletions src/PHPStan/NodeNameExtractor/ClassNameExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php declare(strict_types=1);

namespace MLL\Utils\PHPStan\NodeNameExtractor;

use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\Class_;

class ClassNameExtractor implements NodeNameExtractor
{
public function extract(Node $node): ?string
{
if ($node instanceof Class_) {
$name = $node->name;
if ($name instanceof Identifier) {
return $name->toString();
}
}

return null;
}
}
10 changes: 10 additions & 0 deletions src/PHPStan/NodeNameExtractor/NodeNameExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php declare(strict_types=1);

namespace MLL\Utils\PHPStan\NodeNameExtractor;

use PhpParser\Node;

interface NodeNameExtractor
{
public function extract(Node $node): ?string;
}
23 changes: 23 additions & 0 deletions src/PHPStan/NodeNameExtractor/StringNameExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php declare(strict_types=1);

namespace MLL\Utils\PHPStan\NodeNameExtractor;

use PhpParser\Node;
use PhpParser\Node\Scalar\EncapsedStringPart;
use PhpParser\Node\Scalar\String_;

class StringNameExtractor implements NodeNameExtractor
{
public function extract(Node $node): ?string
{
if ($node instanceof String_) {
return $node->value;
}

if ($node instanceof EncapsedStringPart) {
return $node->value;
}

return null;
}
}
32 changes: 32 additions & 0 deletions src/PHPStan/NodeNameExtractor/VariableNameExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types=1);

namespace MLL\Utils\PHPStan\NodeNameExtractor;

use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Param;

class VariableNameExtractor implements NodeNameExtractor
{
public function extract(Node $node): ?string
{
if ($node instanceof Variable) {
$name = $node->name;
if (is_string($name)) {
return $name;
}
}

if ($node instanceof Param) {
$var = $node->var;
if ($var instanceof Variable) {
$name = $var->name;
if (is_string($name)) {
return $name;
}
}
}

return null;
}
}
100 changes: 100 additions & 0 deletions src/PHPStan/Rules/CanonicalCapitalization.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php declare(strict_types=1);

namespace MLL\Utils\PHPStan\Rules;

use MLL\Utils\PHPStan\NodeNameExtractor\ClassMethodNameExtractor;
use MLL\Utils\PHPStan\NodeNameExtractor\ClassNameExtractor;
use MLL\Utils\PHPStan\NodeNameExtractor\StringNameExtractor;
use MLL\Utils\PHPStan\NodeNameExtractor\VariableNameExtractor;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

use function Safe\preg_match;

/** @implements Rule<Node> */
class CanonicalCapitalization implements Rule
{
private const CANONICAL_CAPITALIZATIONS = [
'Lab ID' => [
'lab id',
'Lab-ID',
'LabID ',
' LabID',
' labID',
'labID ',
'LABID',
'Labid',
'lab-Id',
'Lab Id',
],
];

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

public function processNode(Node $node, Scope $scope): array
{
$extractors = [
new ClassMethodNameExtractor(),
new ClassNameExtractor(),
new VariableNameExtractor(),
new StringNameExtractor(),
];

$nodeName = null;
foreach ($extractors as $extractor) {
$extractedName = $extractor->extract($node);
if ($extractedName !== null) {
$nodeName = $extractedName;
break;
}
}

if ($nodeName === null) {
return [];
}

$wrongCapitalization = CanonicalCapitalization::findWrongCapitalization($nodeName);
if ($wrongCapitalization === null) {
return [];
}

[$correct, $incorrect] = $wrongCapitalization;

$expectedName = self::fixIDCapitalization($nodeName, $correct, $incorrect);

return [
RuleErrorBuilder::message(<<<TXT
Name of {$node->getType()} "{$nodeName}" should use "{$correct}" instead of "{$incorrect}", rename it to "{$expectedName}".
TXT)
->identifier('mll.canonicalCapitalization')
->build(),
];
}

/** @return array{0: string, 1: string}|null */
public static function findWrongCapitalization(string $nodeName): ?array
{
foreach (self::CANONICAL_CAPITALIZATIONS as $correct => $incorrectVariants) {
foreach ($incorrectVariants as $incorrect) {
if (preg_match("/{$incorrect}/", $nodeName) === 1) {
return [$correct, $incorrect];
}
}
}

return null;
}

public static function fixIDCapitalization(string $nodeName, string $correct, string $incorrect): string
{
$trimmedIncorrect = trim($incorrect);
$trimmedCorrect = trim($correct);

return str_replace($trimmedIncorrect, $trimmedCorrect, $nodeName);
}
}
78 changes: 78 additions & 0 deletions src/PHPStan/Rules/CapitalizationOfIDRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php declare(strict_types=1);

namespace MLL\Utils\PHPStan\Rules;

use Illuminate\Support\Str;
use MLL\Utils\PHPStan\NodeNameExtractor\ClassMethodNameExtractor;
use MLL\Utils\PHPStan\NodeNameExtractor\ClassNameExtractor;
use MLL\Utils\PHPStan\NodeNameExtractor\VariableNameExtractor;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

/** @implements Rule<Node> */
class CapitalizationOfIDRule implements Rule
{
/** Lists words or phrases that contain "Id" but are fine. */
protected const FALSE_POSITIVES = [
'Identifier',
'Identical',
'Idt', // IDT is an abbreviation for the brand "Integrated DNA Technologies, Inc."
];

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

public function processNode(Node $node, Scope $scope): array
{
$extractors = [
new ClassMethodNameExtractor(),
new ClassNameExtractor(),
new VariableNameExtractor(),
];
$nodeName = null;
foreach ($extractors as $extractor) {
$extractedName = $extractor->extract($node);
if ($extractedName !== null) {
$nodeName = $extractedName;
break;
}
}

if ($nodeName === null) {
return [];
}

if (! self::containsWrongIDCapitalization($nodeName)) {
return [];
}

$expectedName = self::fixIDCapitalization($nodeName);

return [
RuleErrorBuilder::message(<<<TXT
Name of {$node->getType()} "{$nodeName}" should use "ID" instead of "Id", rename it to "{$expectedName}".
TXT)
->identifier('mll.capitalizationOfID')
->build(),
];
}

public static function containsWrongIDCapitalization(string $nodeName): bool
{
return \Safe\preg_match('/Id/', $nodeName) === 1
&& ! Str::contains($nodeName, self::FALSE_POSITIVES);
}

public static function fixIDCapitalization(string $nodeName): string
{
if ($nodeName === 'Id') {
return 'id';
}

return str_replace('Id', 'ID', $nodeName);
}
}
56 changes: 0 additions & 56 deletions src/PHPStan/Rules/VariableNameIdToIDRule.php

This file was deleted.

Loading
Loading