Skip to content

feat: When determining the local variable severity, the coefficients … #5

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

Merged
merged 1 commit into from
Mar 19, 2025
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

### Features

* When determining the local variable severity, the coefficients are now taken into account when the variable is an assignment.

### Bug fix

* Refactor remove interface Scope
Expand Down
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ composer require --dev smeghead/php-variable-hard-usage
```

## Usage

66 + 32 + 66
If you specify the path of the file for which you want to measure the local variable abuse and run the program, a report will be displayed in JSON format.

```bash
Expand Down Expand Up @@ -84,4 +84,40 @@ $ vendor/bin/php-variable-hard-usage somewhere/your-php-file.php
}
```

## How to calculate VariableHardUsage

VariableHardUsage is an index used to evaluate the frequency of use and scope of a local variable within a function. This indicator is calculated based on the variance of the line number at which the variable is used and the frequency with which the variable is assigned.

### Calculation Procedure

1. Obtain the line numbers of the variables:.

* Obtains the line numbers of all variables used in the function.

2. Calculate the average of the line numbers.

* Calculates the average of the retrieved line numbers. This is obtained by dividing the sum of the line numbers by the number of variables.

3. Calculate VariableHardUsage.

* For each variable, the absolute difference between the line number and the average line number is calculated.
* If a variable is assigned, the difference is multiplied by a factor (2 by default).
* Sum all these values to obtain VariableHardUsage.

### EXAMPLE.

For example, suppose there are three variables in a function, each with row numbers 10, 20, and 30, and that some assignments are made and some are not made. In this case, the average row number is 20.

* Variable A: Row 10, with assignment
* Variable B: Row 20, no assignment
* Variable C: Row 30, with assignment

In this case, VariableHardUsage is calculated as follows

* Variable A: |10 - 20| * 2 = 20
* Variable B: |20 - 20| * 1 = 0
* Variable C: |30 - 20| * 2 = 20

Summing these, VariableHardUsage is 20 + 0 + 20 = 40.

VariableHardUsage is thus calculated as a measure of the frequency of use and scope of a variable. This metric can be used to quantitatively evaluate the usage of local variables within a function and help improve code readability and maintainability.
4 changes: 3 additions & 1 deletion src/Analyze/VariableAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

final class VariableAnalyzer
{
private const ASSIGNED_VARIABLE_COEFFICIENT = 2;

/**
* @var list<Func>
*/
Expand Down Expand Up @@ -50,7 +52,7 @@ private function calcVariableHardUsage(array $vars): int
{
$lineNumbers = array_map(fn($var) => $var->lineNumber, $vars);
$avarageLinuNumber = intval(array_sum($lineNumbers) / count($lineNumbers));
$variableHardUsage = array_sum(array_map(fn($lineNumber) => abs($lineNumber - $avarageLinuNumber), $lineNumbers));
$variableHardUsage = array_sum(array_map(fn(VarReference $var) => abs($var->lineNumber - $avarageLinuNumber) * ($var->assigned ? self::ASSIGNED_VARIABLE_COEFFICIENT : 1), $vars));
return $variableHardUsage;
}
}
2 changes: 1 addition & 1 deletion src/Parse/VarReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ final class VarReference
public function __construct(
public readonly string $name,
public readonly int $lineNumber,
public readonly bool $updated = false
public readonly bool $assigned = false
)
{
}
Expand Down
3 changes: 2 additions & 1 deletion src/Parse/VariableParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ private function collectParseResultPerFunctionLike(array $functionLikes): array

$variables = $this->nodeFinder->findInstanceOf($function, Variable::class);
foreach ($variables as $variable) {
$func->addVariable(new VarReference($variable->name, $variable->getLine())); // @phpstan-ignore-line
$assigned = $variable->getAttribute('assigned');
$func->addVariable(new VarReference($variable->name, $variable->getLine(), $assigned === true)); // @phpstan-ignore-line
}
return $func;
}, $functionLikes);
Expand Down
4 changes: 4 additions & 0 deletions src/Parse/Visitor/FunctionLikeFindingVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Smeghead\PhpVariableHardUsage\Parse\Visitor;

use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\FunctionLike;
use PhpParser\NodeVisitor\FindingVisitor;
use PhpParser\Node\Stmt\Class_;
Expand All @@ -30,6 +31,9 @@ public function enterNode(Node $node) {
$this->currentNamespace = $node->name ? $node->name->name : null;
}

if ($node instanceof Assign) {
$node->var->setAttribute('assigned', true); // Mark as assigned
}
if ($node instanceof FunctionLike) {
$node->setAttribute('namespace', $this->currentNamespace);
}
Expand Down
18 changes: 18 additions & 0 deletions test/VariableAnalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,22 @@ public function testAnalyzeFunctionLong(): void
// abs(34 - 1) + abs(34 - 2) + abs(34 - 100) = 33 + 32 + 66 = 131
$this->assertSame(131, $scopes[0]->getAnalyzedVariables()[0]->variableHardUsage);
}

public function testAnalyzeFunctionLongAssignedVariable(): void
{
$func = new Func(null, 'testFunction');
$func->addVariable(new VarReference('a', 1, true));
$func->addVariable(new VarReference('a', 2));
$func->addVariable(new VarReference('a', 100));

$sut = new VariableAnalyzer([$func]);
$result = $sut->analyze();
$scopes = $result->scopes;

$this->assertCount(1, $scopes);
$this->assertSame('testFunction', $scopes[0]->name);
// (1 + 2 + 100) / 3 = 34
// abs(34 - 1) * 2 + abs(34 - 2) + abs(34 - 100) = 66 + 32 + 66 = 164
$this->assertSame(164, $scopes[0]->getAnalyzedVariables()[0]->variableHardUsage);
}
}
2 changes: 2 additions & 0 deletions test/VariableParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ public function testParseFunction(): void
$vars = $functions[0]->getVariables();
$this->assertSame('num', $vars[0]->name);
$this->assertSame(5, $vars[0]->lineNumber, 'first $num (5)');
$this->assertSame(true, $vars[0]->assigned, 'first $num (5) asign');
$this->assertSame('num', $vars[1]->name);
$this->assertSame(10, $vars[1]->lineNumber, 'second $num (10)');
$this->assertSame(false, $vars[1]->assigned, 'second $num (10) not reference');
}

public function testParseClass(): void
Expand Down
47 changes: 47 additions & 0 deletions test/fixtures/sample_functions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

// 酷使する関数
function hardWordCount(string $text): int {
$wordCount = 0;
$currentWord = '';
for ($i = 0; $i < strlen($text); $i++) {
if ($text[$i] === ' ') {
if ($currentWord !== '') {
$wordCount++;
$currentWord = '';
}
} else {
$currentWord .= $text[$i];
}
}
if ($currentWord !== '') {
$wordCount++;
}
return $wordCount;
}

// あまり酷使しない関数
function lightWordCount(string $text): int {
$words = explode(' ', $text);
$wordCount = 0;
foreach ($words as $word) {
if ($word !== '') {
$wordCount++;
}
}
return $wordCount;
}

// 全然酷使しない関数
function niceWordCount(string $text): int {
return count(array_filter(explode(' ', $text), fn($word) => $word !== ''));
}




$text = 'This is a pen.';

echo hardWordCount($text) . PHP_EOL;
echo lightWordCount($text) . PHP_EOL;
echo niceWordCount($text) . PHP_EOL;