Skip to content

Commit 4666578

Browse files
committed
Add support to check for function, class/interface/trait methods signature:
- Parameter added/removed - Parameter typing added/removed - Parameter default added/removed - Parameter default value changed Major refactoring of the operations so that they only contain their code and reason, moving everything else to the *OperatorUnary/*OperatorDelta classes. Major refactoring of the function analyzer and class method analyzer tests to test all visibilities and type (class/interface/trait). [#83] Removing method default parameter value should generate MAJOR level entry
1 parent 5294dcb commit 4666578

File tree

59 files changed

+1706
-1091
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1706
-1091
lines changed

composer.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,18 @@
1919
"support": {
2020
"issues": "https://github.com/tomzx/php-semver-checker/issues"
2121
},
22+
"repositories": [
23+
{
24+
"type": "vcs",
25+
"url": "https://github.com/tomzx/php-parser"
26+
}
27+
],
2228
"require": {
2329
"php": ">=5.4.0",
2430

2531
"hassankhan/config": "~0.10",
2632
"herrera-io/phar-update": "~2.0",
27-
"nikic/php-parser": "~2.0",
33+
"nikic/php-parser": "dev-features/php-semver-checker",
2834
"symfony/console": "~2.7|~3.0",
2935
"symfony/yaml": "~2.7|~3.0",
3036
"tomzx/finder": "~0.1"

docs/Ruleset.md

Lines changed: 87 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@
55
Code | Level | Rule
66
-----|-------|-------
77
V001 | MAJOR | Function removed
8-
V002 | MAJOR | Function parameter changed
8+
V002 | MAJOR | Function parameter added
99
V003 | MINOR | Function added
1010
V004 | PATCH | Function implementation changed
11-
V067 | PATCH | Function parameter name changed
11+
V067 | PATCH | Function parameter name changed
12+
V068 | MAJOR | Function parameter removed
13+
V069 | MAJOR | Function parameter typing added
14+
V070 | MINOR | Function parameter typing removed
15+
V071 | MINOR | Function parameter default added
16+
V072 | MAJOR | Function parameter default removed
17+
V073 | MINOR | Function parameter default value changed
1218

1319
# Classes
1420

@@ -19,8 +25,8 @@ V006 | MAJOR | Class public method removed
1925
V007 | MAJOR | Class protected method removed
2026
V008 | MAJOR | Class public property removed
2127
V009 | MAJOR | Class protected property removed
22-
V010 | MAJOR | Class public method parameter changed
23-
V011 | MAJOR | Class protected method parameter changed
28+
V010 | MAJOR | Class public method parameter added
29+
V011 | MAJOR | Class protected method parameter added
2430
V012 | MAJOR | *New public constructor (does not match supertype)*
2531
V013 | MAJOR | *New protected constructor (does not match supertype)*
2632
V015 | MAJOR | Class public method added
@@ -30,7 +36,7 @@ V020 | MAJOR | Class protected property added
3036
V014 | MINOR | Class added
3137
V017 | MINOR | *Final class public method added*
3238
V018 | MINOR | *Final class protected method added*
33-
V021 | MINOR | *Final class protected method parameter changed*
39+
V021 | MINOR | *Final class protected method parameter added*
3440
V022 | PATCH | *Final class protected method removed*
3541
V023 | PATCH | [Final] Class public class method implementation changed
3642
V024 | PATCH | [Final] Class protected class method implementation changed
@@ -40,10 +46,52 @@ V027 | PATCH | Class private property removed
4046
V028 | PATCH | Class private method added
4147
V029 | PATCH | Class private method removed
4248
V030 | PATCH | *Final class protected method added*
43-
V031 | PATCH | Class private method parameter changed
49+
V031 | PATCH | Class private method parameter added
4450
V060 | PATCH | Class public method parameter name changed
45-
V061 | PATCH | Class protected method parameter name changed
46-
V062 | PATCH | Class private method parameter name changed
51+
V061 | PATCH | Class protected method parameter name changed
52+
V062 | PATCH | Class private method parameter name changed
53+
V080 | ----- | *Final class public method parameter added*
54+
V081 | ----- | *Final class private method parameter added*
55+
V082 | MAJOR | Class public method parameter removed
56+
V083 | MAJOR | Class protected method parameter removed
57+
V084 | PATCH | Class private method parameter removed
58+
V085 | MAJOR | Class public method parameter typing added
59+
V086 | MAJOR | Class protected method parameter typing added
60+
V087 | PATCH | Class private method parameter typing added
61+
V088 | MAJOR | Class public method parameter typing removed
62+
V089 | MAJOR | Class protected method parameter typing removed
63+
V090 | PATCH | Class private method parameter typing removed
64+
V091 | MINOR | Class public method parameter default added
65+
V092 | MINOR | Class protected method parameter default added
66+
V093 | PATCH | Class private method parameter default added
67+
V094 | MAJOR | Class public method parameter default removed
68+
V095 | MAJOR | Class protected method parameter default removed
69+
V096 | PATCH | Class private method parameter default removed
70+
V097 | MAJOR | Class public method parameter default value changed
71+
V098 | MAJOR | Class protected method parameter default value changed
72+
V099 | PATCH | Class private method parameter default value changed
73+
VXXX | MAJOR | *Final class public method parameter added*
74+
VXXX | MAJOR | *Final class protected method parameter added*
75+
VXXX | PATCH | *Final class private method parameter added*
76+
VXXX | MAJOR | *Final class public method parameter removed*
77+
VXXX | MAJOR | *Final class protected method parameter removed*
78+
VXXX | PATCH | *Final class private method parameter removed*
79+
VXXX | MAJOR | *Final class public method parameter typing added*
80+
VXXX | MAJOR | *Final class protected method parameter typing added*
81+
VXXX | PATCH | *Final class private method parameter typing added*
82+
VXXX | ????? | *Final class public method parameter typing removed*
83+
VXXX | ????? | *Final class protected method parameter typing removed*
84+
VXXX | PATCH | *Final class private method parameter typing removed*
85+
VXXX | ????? | *Final class public method parameter default added*
86+
VXXX | ????? | *Final class protected method parameter default added*
87+
VXXX | PATCH | *Final class private method parameter default added*
88+
VXXX | ????? | *Final class public method parameter default removed*
89+
VXXX | ????? | *Final class protected method parameter default removed*
90+
VXXX | PATCH | *Final class private method parameter default removed*
91+
VXXX | ????? | *Final class public method parameter default value changed*
92+
VXXX | ????? | *Final class protected method parameter default value changed*
93+
VXXX | PATCH | *Final class private method parameter default value changed*
94+
4795

4896
# Interface
4997

@@ -53,8 +101,14 @@ V032 | MINOR | Interface added
53101
V033 | MAJOR | Interface removed
54102
V034 | MAJOR | Interface method added
55103
V035 | MAJOR | Interface method removed
56-
V036 | MAJOR | Interface method parameter changed
104+
V036 | MAJOR | Interface method parameter added
57105
V063 | PATCH | Interface method parameter name changed
106+
V074 | MAJOR | Interface method parameter removed
107+
V075 | MAJOR | Interface method parameter typing added
108+
V076 | MAJOR | Interface method parameter typing removed
109+
V077 | MINOR | Interface method parameter default added
110+
V078 | MAJOR | Interface method parameter default removed
111+
V079 | MAJOR | Interface method parameter default value changed
58112

59113
# Trait
60114

@@ -65,8 +119,8 @@ V038 | MAJOR | Trait public method removed
65119
V039 | MAJOR | Trait protected method removed
66120
V040 | MAJOR | Trait public property removed
67121
V041 | MAJOR | Trait protected property removed
68-
V042 | MAJOR | Trait public method parameter changed
69-
V043 | MAJOR | Trait protected method parameter changed
122+
V042 | MAJOR | Trait public method parameter added
123+
V043 | MAJOR | Trait protected method parameter added
70124
V044 | MAJOR | *New public constructor (does not match supertype)*
71125
V045 | MAJOR | *New protected constructor (does not match supertype)*
72126
V047 | MAJOR | Trait public method added
@@ -78,14 +132,32 @@ V056 | MAJOR | Trait private property removed
78132
V057 | MAJOR | Trait private method added
79133
V058 | MAJOR | Trait private method removed
80134
V046 | MINOR | Trait added
81-
V051 | MINOR | *REMOVED*
135+
V051 | ----- | *REMOVED*
82136
V052 | PATCH | Trait public method implementation changed
83137
V053 | PATCH | Trait protected method implementation changed
84138
V054 | PATCH | Trait private method implementation changed
85-
V059 | PATCH | Trait private method parameter changed
139+
V059 | PATCH | Trait private method parameter added
86140
V064 | PATCH | Trait public method parameter name changed
87-
V065 | PATCH | Trait protected method parameter name changed
88-
V066 | PATCH | Trait private method parameter name changed
141+
V065 | PATCH | Trait protected method parameter name changed
142+
V066 | PATCH | Trait private method parameter name changed
143+
V100 | MAJOR | Trait public method parameter removed
144+
V101 | MAJOR | Trait protected method parameter removed
145+
V102 | MAJOR | Trait private method parameter removed
146+
V103 | MAJOR | Trait public method parameter typing added
147+
V104 | MAJOR | Trait protected method parameter typing added
148+
V105 | MAJOR | Trait private method parameter typing added
149+
V106 | MAJOR | Trait public method parameter typing removed
150+
V107 | MAJOR | Trait protected method parameter typing removed
151+
V108 | MAJOR | Trait private method parameter typing removed
152+
V109 | MINOR | Trait public method parameter default added
153+
V110 | MINOR | Trait protected method parameter default added
154+
V111 | MINOR | Trait private method parameter default added
155+
V112 | MAJOR | Trait public method parameter default removed
156+
V113 | MAJOR | Trait protected method parameter default removed
157+
V114 | MAJOR | Trait private method parameter default removed
158+
V115 | MAJOR | Trait public method parameter default value changed
159+
V116 | MAJOR | Trait protected method parameter default value changed
160+
V117 | MAJOR | Trait private method parameter default value changed
89161

90162
# To classify
91163

scripts/extract-rules-from-ruleset.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
$ruleset = file_get_contents(__DIR__.'/../docs/Ruleset.md');
44

5-
$result = preg_match_all('/(?<code>V\d+) \| (?<level>[^ ]+)/', $ruleset, $matches);
5+
$result = preg_match_all('/(?<code>V\d+) \| (?<level>(PATCH|MINOR|MAJOR))/', $ruleset, $matches);
66

77
if ($result) {
88
array_shift($matches);

src/PHPSemVerChecker/Analyzer/ClassMethodAnalyzer.php

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,19 @@
33
namespace PHPSemVerChecker\Analyzer;
44

55
use PhpParser\Node\Stmt;
6+
use PHPSemVerChecker\Comparator\Implementation;
67
use PHPSemVerChecker\Comparator\Signature;
78
use PHPSemVerChecker\Operation\ClassMethodAdded;
89
use PHPSemVerChecker\Operation\ClassMethodImplementationChanged;
9-
use PHPSemVerChecker\Operation\ClassMethodParameterChanged;
10+
use PHPSemVerChecker\Operation\ClassMethodOperationUnary;
11+
use PHPSemVerChecker\Operation\ClassMethodParameterAdded;
12+
use PHPSemVerChecker\Operation\ClassMethodParameterDefaultAdded;
13+
use PHPSemVerChecker\Operation\ClassMethodParameterDefaultRemoved;
14+
use PHPSemVerChecker\Operation\ClassMethodParameterDefaultValueChanged;
1015
use PHPSemVerChecker\Operation\ClassMethodParameterNameChanged;
16+
use PHPSemVerChecker\Operation\ClassMethodParameterRemoved;
17+
use PHPSemVerChecker\Operation\ClassMethodParameterTypingAdded;
18+
use PHPSemVerChecker\Operation\ClassMethodParameterTypingRemoved;
1119
use PHPSemVerChecker\Operation\ClassMethodRemoved;
1220
use PHPSemVerChecker\Report\Report;
1321

@@ -71,23 +79,34 @@ public function analyze(Stmt $contextBefore, Stmt $contextAfter)
7179
$paramsBefore = $methodBefore->params;
7280
$paramsAfter = $methodAfter->params;
7381

74-
// Signature
75-
$signatureChanged = false;
76-
if ( ! Signature::isSameTypehints($paramsBefore, $paramsAfter)) {
77-
$data = new ClassMethodParameterChanged($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter);
82+
$signatureResult = Signature::analyze($paramsBefore, $paramsAfter);
83+
84+
$changes = [
85+
'parameter_added' => ClassMethodParameterAdded::class,
86+
'parameter_removed' => ClassMethodParameterRemoved::class,
87+
'parameter_renamed' => ClassMethodParameterNameChanged::class,
88+
'parameter_typing_added' => ClassMethodParameterTypingAdded::class,
89+
'parameter_typing_removed' => ClassMethodParameterTypingRemoved::class,
90+
'parameter_default_added' => ClassMethodParameterDefaultAdded::class,
91+
'parameter_default_removed' => ClassMethodParameterDefaultRemoved::class,
92+
'parameter_default_value_changed' => ClassMethodParameterDefaultValueChanged::class,
93+
];
94+
95+
foreach ($changes as $changeType => $class) {
96+
if ( ! $signatureResult[$changeType]) {
97+
continue;
98+
}
99+
if (is_a($class, ClassMethodOperationUnary::class, true)) {
100+
$data = new $class($this->context, $this->fileAfter, $contextAfter, $methodAfter);
101+
} else {
102+
$data = new $class($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter);
103+
}
78104
$report->add($this->context, $data);
79-
$signatureChanged = true;
80105
}
81106

82-
if ( ! $signatureChanged && ! Signature::isSameVariables($paramsBefore, $paramsAfter)) {
83-
$data = new ClassMethodParameterNameChanged($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter);
84-
$report->add($this->context, $data);
85-
}
86-
87-
// Different length (considering params with defaults)
88-
89107
// Difference in source code
90-
if ($methodBefore->stmts != $methodAfter->stmts) {
108+
// Cast to array because interfaces do not have stmts (= null)
109+
if ( ! Implementation::isSame((array)$methodBefore->stmts, (array)$methodAfter->stmts)) {
91110
$data = new ClassMethodImplementationChanged($this->context, $this->fileBefore, $contextBefore, $methodBefore, $this->fileAfter, $contextAfter, $methodAfter);
92111
$report->add($this->context, $data);
93112
}

src/PHPSemVerChecker/Analyzer/FunctionAnalyzer.php

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,20 @@
22

33
namespace PHPSemVerChecker\Analyzer;
44

5+
use PHPSemVerChecker\Comparator\Implementation;
56
use PHPSemVerChecker\Comparator\Signature;
67
use PHPSemVerChecker\Operation\FunctionAdded;
78
use PHPSemVerChecker\Operation\FunctionImplementationChanged;
9+
use PHPSemVerChecker\Operation\FunctionOperationUnary;
10+
use PHPSemVerChecker\Operation\FunctionParameterAdded;
811
use PHPSemVerChecker\Operation\FunctionParameterChanged;
12+
use PHPSemVerChecker\Operation\FunctionParameterDefaultAdded;
13+
use PHPSemVerChecker\Operation\FunctionParameterDefaultRemoved;
14+
use PHPSemVerChecker\Operation\FunctionParameterDefaultValueChanged;
915
use PHPSemVerChecker\Operation\FunctionParameterNameChanged;
16+
use PHPSemVerChecker\Operation\FunctionParameterRemoved;
17+
use PHPSemVerChecker\Operation\FunctionParameterTypingAdded;
18+
use PHPSemVerChecker\Operation\FunctionParameterTypingRemoved;
1019
use PHPSemVerChecker\Operation\FunctionRemoved;
1120
use PHPSemVerChecker\Operation\Unknown;
1221
use PHPSemVerChecker\Registry\Registry;
@@ -48,27 +57,36 @@ public function analyze(Registry $registryBefore, Registry $registryAfter)
4857
if ($functionBefore != $functionAfter) {
4958
$paramsBefore = $functionBefore->params;
5059
$paramsAfter = $functionAfter->params;
51-
// Signature
5260

53-
if ( ! Signature::isSameTypehints($paramsBefore, $paramsAfter)) {
54-
$data = new FunctionParameterChanged($fileBefore, $functionBefore, $fileAfter, $functionAfter);
61+
$signatureResult = Signature::analyze($paramsBefore, $paramsAfter);
62+
63+
$changes = [
64+
'parameter_added' => FunctionParameterAdded::class,
65+
'parameter_removed' => FunctionParameterRemoved::class,
66+
'parameter_renamed' => FunctionParameterNameChanged::class,
67+
'parameter_typing_added' => FunctionParameterTypingAdded::class,
68+
'parameter_typing_removed' => FunctionParameterTypingRemoved::class,
69+
'parameter_default_added' => FunctionParameterDefaultAdded::class,
70+
'parameter_default_removed' => FunctionParameterDefaultRemoved::class,
71+
'parameter_default_value_changed' => FunctionParameterDefaultValueChanged::class,
72+
];
73+
74+
foreach ($changes as $changeType => $class) {
75+
if ( ! $signatureResult[$changeType]) {
76+
continue;
77+
}
78+
if (is_a($class, FunctionOperationUnary::class, true)) {
79+
$data = new $class($fileAfter, $functionAfter);
80+
} else {
81+
$data = new $class($fileBefore, $functionBefore, $fileAfter, $functionAfter);
82+
}
5583
$report->addFunction($data);
56-
continue;
5784
}
5885

59-
if ( ! Signature::isSameVariables($paramsBefore, $paramsAfter)) {
60-
$data = new FunctionParameterNameChanged($fileBefore, $functionBefore, $fileAfter, $functionAfter);
61-
$report->addFunction($data);
62-
continue;
63-
}
64-
65-
// Different length (considering params with defaults)
66-
6786
// Difference in source code
68-
if ($functionBefore->stmts != $functionAfter->stmts) {
87+
if ( ! Implementation::isSame($functionBefore->stmts, $functionAfter->stmts)) {
6988
$data = new FunctionImplementationChanged($fileBefore, $functionBefore, $fileAfter, $functionAfter);
7089
$report->addFunction($data);
71-
continue;
7290
}
7391
}
7492
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace PHPSemVerChecker\Comparator;
4+
5+
use PhpParser\NodeDumper;
6+
7+
class Implementation
8+
{
9+
/**
10+
* @param array $statementsA
11+
* @param array $statementsB
12+
* @return bool
13+
*/
14+
public static function isSame(array $statementsA, array $statementsB)
15+
{
16+
// Naive way to check if two implementation are the same
17+
$nodeDumper = new NodeDumper();
18+
19+
$dumpA = $nodeDumper->dump($statementsA);
20+
$dumpB = $nodeDumper->dump($statementsB);
21+
22+
// var_dump($dumpA === $dumpB);
23+
// var_dump($statementsA == $statementsB);
24+
// echo '-----'.PHP_EOL;
25+
26+
return $dumpA === $dumpB;
27+
}
28+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace PHPSemVerChecker\Comparator;
4+
5+
class Node
6+
{
7+
public static function isEqual(\PhpParser\Node $nodeA, \PhpParser\Node $nodeB)
8+
{
9+
if ($nodeA->getType() !== $nodeB->getType()) {
10+
return false;
11+
}
12+
13+
$subNodesA = $nodeA->getSubNodeNames();
14+
$subNodesB = $nodeB->getSubNodeNames();
15+
if ($subNodesA !== $subNodesB) {
16+
return false;
17+
}
18+
19+
foreach ($subNodesA as $key) {
20+
$valueA = $nodeA->$key;
21+
$valueB = $nodeB->$key;
22+
$result = true;
23+
if ($valueA instanceof \PhpParser\Node && $valueB instanceof \PhpParser\Node) {
24+
$result = self::isEqual($valueA, $valueB);
25+
} else {
26+
$result = $valueA === $valueB;
27+
}
28+
29+
if ( ! $result) {
30+
return false;
31+
}
32+
}
33+
34+
return true;
35+
}
36+
}

0 commit comments

Comments
 (0)