diff --git a/.gitignore b/.gitignore
index 2a76e0ea..b453ada9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
vendor/
+.phpunit.result.cache
phpcs.xml
composer.lock
tests/input2
diff --git a/.travis.yml b/.travis.yml
index 9d68b004..d5626ddd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,6 +21,7 @@ install: travis_retry composer update --prefer-dist
script:
- vendor/bin/phpcs
- vendor/bin/phpcs $(find tests/input/* | sort) --report=summary --report-file=phpcs.log; diff tests/expected_report.txt phpcs.log
+ - vendor/bin/phpunit
stages:
- Validate against schema
diff --git a/composer.json b/composer.json
index f195a9aa..04283f5c 100644
--- a/composer.json
+++ b/composer.json
@@ -25,12 +25,20 @@
"Doctrine\\Sniffs\\": "lib/Doctrine/Sniffs"
}
},
+ "autoload-dev": {
+ "psr-4": {
+ "Doctrine\\Tests\\": "tests/"
+ }
+ },
"require": {
"php": "^7.2",
"dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
"slevomat/coding-standard": "dev-master#63a8186b129ee96d1277e68c80cf87c8cdb356d1",
"squizlabs/php_codesniffer": "^3.5.0"
},
+ "require-dev": {
+ "phpunit/phpunit": "^8.1"
+ },
"config": {
"sort-packages": true
},
diff --git a/lib/Doctrine/Sniffs/Operators/NegationOperatorSpacingSniff.php b/lib/Doctrine/Sniffs/Operators/NegationOperatorSpacingSniff.php
new file mode 100644
index 00000000..c6c1c227
--- /dev/null
+++ b/lib/Doctrine/Sniffs/Operators/NegationOperatorSpacingSniff.php
@@ -0,0 +1,114 @@
+getTokens();
+
+ $previousEffective = TokenHelper::findPreviousEffective($file, $pointer - 1);
+
+ $possibleOperandTypes = [
+ T_CONSTANT_ENCAPSED_STRING,
+ T_CLOSE_PARENTHESIS,
+ T_CLOSE_SHORT_ARRAY,
+ T_CLOSE_SQUARE_BRACKET,
+ T_DNUMBER,
+ T_ENCAPSED_AND_WHITESPACE,
+ T_LNUMBER,
+ T_NUM_STRING,
+ T_STRING,
+ T_VARIABLE,
+ ];
+
+ if (in_array($tokens[$previousEffective]['code'], $possibleOperandTypes, true)) {
+ return;
+ }
+
+ $whitespacePointer = $pointer + 1;
+ if (! isset($tokens[$whitespacePointer])) {
+ return;
+ }
+
+ $numberOfSpaces = $this->numberOfSpaces($tokens[$whitespacePointer]);
+ $expectedSpaces = $this->requireSpace ? 1 : 0;
+
+ if ($numberOfSpaces === $expectedSpaces
+ || ! $this->recordError($file, $pointer, $tokens[$pointer], $expectedSpaces, $numberOfSpaces)) {
+ return;
+ }
+
+ if ($expectedSpaces > $numberOfSpaces) {
+ $file->fixer->addContent($pointer, ' ');
+
+ return;
+ }
+
+ $file->fixer->replaceToken($whitespacePointer, '');
+ }
+
+ /**
+ * @param mixed[] $token
+ */
+ private function numberOfSpaces(array $token) : int
+ {
+ if ($token['code'] !== T_WHITESPACE) {
+ return 0;
+ }
+
+ return strlen($token['content']);
+ }
+
+ /**
+ * @param mixed[] $token
+ */
+ private function recordError(File $file, int $pointer, array $token, int $expected, int $found) : bool
+ {
+ return $file->addFixableError(
+ 'Expected exactly %d space after "%s"; %d found',
+ $pointer,
+ self::CODE_INVALID_SPACE_AFTER_MINUS,
+ [$expected, $token['content'], $found]
+ );
+ }
+}
diff --git a/lib/Doctrine/Sniffs/Operators/OperatorSpacingSniff.php b/lib/Doctrine/Sniffs/Operators/OperatorSpacingSniff.php
new file mode 100644
index 00000000..3ab35deb
--- /dev/null
+++ b/lib/Doctrine/Sniffs/Operators/OperatorSpacingSniff.php
@@ -0,0 +1,78 @@
+isOperator($file, $pointer)) {
+ return;
+ }
+
+ parent::process($file, $pointer);
+ }
+
+ /**
+ * @param int $pointer
+ *
+ * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
+ */
+ protected function isOperator(File $file, $pointer) : bool
+ {
+ if (! parent::isOperator($file, $pointer)) {
+ return false;
+ }
+
+ $tokens = $file->getTokens();
+ if ($tokens[$pointer]['code'] === T_MINUS || $tokens[$pointer]['code'] === T_PLUS) {
+ $prev = $file->findPrevious(Tokens::$emptyTokens, $pointer - 1, null, true);
+ if (in_array($tokens[$prev]['code'], self::NON_OPERAND_TOKENS, true)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/lib/Doctrine/ruleset.xml b/lib/Doctrine/ruleset.xml
index cc7b04a8..7823be15 100644
--- a/lib/Doctrine/ruleset.xml
+++ b/lib/Doctrine/ruleset.xml
@@ -16,6 +16,12 @@
+
+
+
+
+
+
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 00000000..75c01700
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,20 @@
+
+
+
+ tests
+
+
+
+ src/
+
+
+
diff --git a/tests/Sniffs/Operators/NegationOperatorSpacingSniffTest.php b/tests/Sniffs/Operators/NegationOperatorSpacingSniffTest.php
new file mode 100644
index 00000000..9bc7f09f
--- /dev/null
+++ b/tests/Sniffs/Operators/NegationOperatorSpacingSniffTest.php
@@ -0,0 +1,27 @@
+ true]
+ );
+
+ self::assertSame(97, $file->getErrorCount());
+
+ self::assertAllFixedInFile($file);
+ }
+}
diff --git a/tests/Sniffs/Operators/OperatorSpacingSniffTest.php b/tests/Sniffs/Operators/OperatorSpacingSniffTest.php
new file mode 100644
index 00000000..07812b87
--- /dev/null
+++ b/tests/Sniffs/Operators/OperatorSpacingSniffTest.php
@@ -0,0 +1,32 @@
+ false]
+ )
+ );
+ }
+
+ public function testErrors() : void
+ {
+ $file = self::checkFile(
+ __DIR__ . '/data/OperatorSpacingSniffErrors.php',
+ ['ignoreSpacingBeforeAssignments' => false]
+ );
+
+ self::assertSame(67, $file->getErrorCount());
+
+ self::assertAllFixedInFile($file);
+ }
+}
diff --git a/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffNoErrors.php b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffNoErrors.php
new file mode 100644
index 00000000..0dd6a23f
--- /dev/null
+++ b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffNoErrors.php
@@ -0,0 +1,108 @@
+ -16 || $a > -$b || $a > -self::CONSTANT) {
+
+}
+
+
+yield -1;
+echo -1;
+$a = -1;
+func(-1);
+$a = [-1];
+return -1;
+print -1;
+$a &= -1;
+switch ($a) {
+ case -1:
+}
+$a = $a ?? -1;
+$a .= -1;
+$a /= -1;
+$a = [1 => -1];
+$a = $a == -1;
+$a = $a >= -1;
+$a = $a === -1;
+$a = $a != -1;
+$a = $a !== -1;
+$a = $a <= -1;
+$a = $a <=> -1;
+$a = $a and -1;
+$a = $a or -1;
+$a = $a xor -1;
+$a -= -1;
+$a %= -1;
+$a *= -1;
+$a |= -1;
+$a += -1;
+$a = $a ** -1;
+$a **= -1;
+$a = $a << -1;
+$a <<= -1;
+$a = $a >> -1;
+$a >>= -1;
+$a = (string) -1;
+$a = (array) -1;
+$a = (bool) -1;
+$a = (object) -1;
+$a = (unset) -1;
+$a = (float) -1;
+$a = (int) -1;
+$a ^= -1;
+$a = [$a, -1];
+$a = $a[-1];
+$a = $a ? -1 : -1;
+
+
+yield -$b;
+echo -$b;
+$a = -$b;
+func(-$b);
+$a = [-$b];
+return -$b;
+print -$b;
+$a &= -$b;
+switch ($a) {
+ case -$b:
+}
+$a = $a ?? -$b;
+$a .= -$b;
+$a /= -$b;
+$a = [1 => -$b];
+$a = $a == -$b;
+$a = $a >= -$b;
+$a = $a === -$b;
+$a = $a != -$b;
+$a = $a !== -$b;
+$a = $a <= -$b;
+$a = $a <=> -$b;
+$a = $a and -$b;
+$a = $a or -$b;
+$a = $a xor -$b;
+$a -= -$b;
+$a %= -$b;
+$a *= -$b;
+$a |= -$b;
+$a += -$b;
+$a = $a ** -$b;
+$a **= -$b;
+$a = $a << -$b;
+$a <<= -$b;
+$a = $a >> -$b;
+$a >>= -$b;
+$a = (string) -$b;
+$a = (array) -$b;
+$a = (bool) -$b;
+$a = (object) -$b;
+$a = (unset) -$b;
+$a = (float) -$b;
+$a = (int) -$b;
+$a ^= -$b;
+$a = [$a, -$b];
+$a = $a[-$b];
+$a = $a ? -$b : -$b;
diff --git a/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.fixed.php b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.fixed.php
new file mode 100644
index 00000000..991f175f
--- /dev/null
+++ b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.fixed.php
@@ -0,0 +1,108 @@
+ - 16 || $a > - $b || $a > - self::CONSTANT) {
+
+}
+
+
+yield - 1;
+echo - 1;
+$a = - 1;
+func(- 1);
+$a = [- 1];
+return - 1;
+print - 1;
+$a &= - 1;
+switch ($a) {
+ case - 1:
+}
+$a = $a ?? - 1;
+$a .= - 1;
+$a /= - 1;
+$a = [1 => - 1];
+$a = $a == - 1;
+$a = $a >= - 1;
+$a = $a === - 1;
+$a = $a != - 1;
+$a = $a !== - 1;
+$a = $a <= - 1;
+$a = $a <=> - 1;
+$a = $a and - 1;
+$a = $a or - 1;
+$a = $a xor - 1;
+$a -= - 1;
+$a %= - 1;
+$a *= - 1;
+$a |= - 1;
+$a += - 1;
+$a = $a ** - 1;
+$a **= - 1;
+$a = $a << - 1;
+$a <<= - 1;
+$a = $a >> - 1;
+$a >>= - 1;
+$a = (string) - 1;
+$a = (array) - 1;
+$a = (bool) - 1;
+$a = (object) - 1;
+$a = (unset) - 1;
+$a = (float) - 1;
+$a = (int) - 1;
+$a ^= - 1;
+$a = [$a, - 1];
+$a = $a[- 1];
+$a = $a ? - 1 : - 1;
+
+
+yield - $b;
+echo - $b;
+$a = - $b;
+func(- $b);
+$a = [- $b];
+return - $b;
+print - $b;
+$a &= - $b;
+switch ($a) {
+ case - $b:
+}
+$a = $a ?? - $b;
+$a .= - $b;
+$a /= - $b;
+$a = [1 => - $b];
+$a = $a == - $b;
+$a = $a >= - $b;
+$a = $a === - $b;
+$a = $a != - $b;
+$a = $a !== - $b;
+$a = $a <= - $b;
+$a = $a <=> - $b;
+$a = $a and - $b;
+$a = $a or - $b;
+$a = $a xor - $b;
+$a -= - $b;
+$a %= - $b;
+$a *= - $b;
+$a |= - $b;
+$a += - $b;
+$a = $a ** - $b;
+$a **= - $b;
+$a = $a << - $b;
+$a <<= - $b;
+$a = $a >> - $b;
+$a >>= - $b;
+$a = (string) - $b;
+$a = (array) - $b;
+$a = (bool) - $b;
+$a = (object) - $b;
+$a = (unset) - $b;
+$a = (float) - $b;
+$a = (int) - $b;
+$a ^= - $b;
+$a = [$a, - $b];
+$a = $a[- $b];
+$a = $a ? - $b : - $b;
diff --git a/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.php b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.php
new file mode 100644
index 00000000..0dd6a23f
--- /dev/null
+++ b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.php
@@ -0,0 +1,108 @@
+ -16 || $a > -$b || $a > -self::CONSTANT) {
+
+}
+
+
+yield -1;
+echo -1;
+$a = -1;
+func(-1);
+$a = [-1];
+return -1;
+print -1;
+$a &= -1;
+switch ($a) {
+ case -1:
+}
+$a = $a ?? -1;
+$a .= -1;
+$a /= -1;
+$a = [1 => -1];
+$a = $a == -1;
+$a = $a >= -1;
+$a = $a === -1;
+$a = $a != -1;
+$a = $a !== -1;
+$a = $a <= -1;
+$a = $a <=> -1;
+$a = $a and -1;
+$a = $a or -1;
+$a = $a xor -1;
+$a -= -1;
+$a %= -1;
+$a *= -1;
+$a |= -1;
+$a += -1;
+$a = $a ** -1;
+$a **= -1;
+$a = $a << -1;
+$a <<= -1;
+$a = $a >> -1;
+$a >>= -1;
+$a = (string) -1;
+$a = (array) -1;
+$a = (bool) -1;
+$a = (object) -1;
+$a = (unset) -1;
+$a = (float) -1;
+$a = (int) -1;
+$a ^= -1;
+$a = [$a, -1];
+$a = $a[-1];
+$a = $a ? -1 : -1;
+
+
+yield -$b;
+echo -$b;
+$a = -$b;
+func(-$b);
+$a = [-$b];
+return -$b;
+print -$b;
+$a &= -$b;
+switch ($a) {
+ case -$b:
+}
+$a = $a ?? -$b;
+$a .= -$b;
+$a /= -$b;
+$a = [1 => -$b];
+$a = $a == -$b;
+$a = $a >= -$b;
+$a = $a === -$b;
+$a = $a != -$b;
+$a = $a !== -$b;
+$a = $a <= -$b;
+$a = $a <=> -$b;
+$a = $a and -$b;
+$a = $a or -$b;
+$a = $a xor -$b;
+$a -= -$b;
+$a %= -$b;
+$a *= -$b;
+$a |= -$b;
+$a += -$b;
+$a = $a ** -$b;
+$a **= -$b;
+$a = $a << -$b;
+$a <<= -$b;
+$a = $a >> -$b;
+$a >>= -$b;
+$a = (string) -$b;
+$a = (array) -$b;
+$a = (bool) -$b;
+$a = (object) -$b;
+$a = (unset) -$b;
+$a = (float) -$b;
+$a = (int) -$b;
+$a ^= -$b;
+$a = [$a, -$b];
+$a = $a[-$b];
+$a = $a ? -$b : -$b;
diff --git a/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.fixed.php b/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.fixed.php
new file mode 100644
index 00000000..fe71eb67
--- /dev/null
+++ b/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.fixed.php
@@ -0,0 +1,42 @@
+ $b;
+$a >= $b;
+$a < $b;
+$a <= $b;
+$a != $b;
+
+$a = $b;
+$a === $b;
+$a > $b;
+$a >= $b;
+$a < $b;
+$a <= $b;
+$a != $b;
+
+$a = $a + $b;
+$a = $a - $b;
+$a = $a / $b;
+$a = $a * $b;
+$a = $a + $b;
+$a = $a ^ $b;
+
+$a = $a + $b;
+$a = $a - $b;
+$a = $a / $b;
+$a = $a * $b;
+$a = $a + $b;
+$a = $a ^ $b;
+
+$a = self::CONST - $a;
+
+$a += $b;
+$a -= $b;
+
+$a += $b;
+$a -= $b;
+
+$a = ($a) instanceof $b;
+$a = $a instanceof $b;
diff --git a/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.php b/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.php
new file mode 100644
index 00000000..774a056c
--- /dev/null
+++ b/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.php
@@ -0,0 +1,42 @@
+$b;
+$a>=$b;
+$a<$b;
+$a<=$b;
+$a!=$b;
+
+$a = $b;
+$a === $b;
+$a > $b;
+$a >= $b;
+$a < $b;
+$a <= $b;
+$a != $b;
+
+$a = $a+$b;
+$a = $a-$b;
+$a = $a/$b;
+$a = $a*$b;
+$a = $a+$b;
+$a = $a^$b;
+
+$a = $a + $b;
+$a = $a - $b;
+$a = $a / $b;
+$a = $a * $b;
+$a = $a + $b;
+$a = $a ^ $b;
+
+$a = self::CONST- $a;
+
+$a+=$b;
+$a-=$b;
+
+$a += $b;
+$a -= $b;
+
+$a = ($a)instanceof$b;
+$a = $a instanceof $b;
diff --git a/tests/Sniffs/Operators/data/OperatorSpacingSniffNoErrors.php b/tests/Sniffs/Operators/data/OperatorSpacingSniffNoErrors.php
new file mode 100644
index 00000000..61a110d0
--- /dev/null
+++ b/tests/Sniffs/Operators/data/OperatorSpacingSniffNoErrors.php
@@ -0,0 +1,194 @@
+ -1];
+$a = $a == -1;
+$a = $a >= -1;
+$a = $a === -1;
+$a = $a != -1;
+$a = $a !== -1;
+$a = $a <= -1;
+$a = $a <=> -1;
+$a = $a and -1;
+$a = $a or -1;
+$a = $a xor -1;
+$a -= -1;
+$a %= -1;
+$a *= -1;
+$a |= -1;
+$a += -1;
+$a = $a ** -1;
+$a **= -1;
+$a = $a << -1;
+$a <<= -1;
+$a = $a >> -1;
+$a >>= -1;
+$a = (string) -1;
+$a = (array) -1;
+$a = (bool) -1;
+$a = (object) -1;
+$a = (unset) -1;
+$a = (float) -1;
+$a = (int) -1;
+$a ^= -1;
+$a = [$a, -1];
+$a = $a[-1];
+$a = $a ? -1 : -1;
+
+yield - 1;
+echo - 1;
+$a = - 1;
+func(- 1);
+$a = [- 1];
+return - 1;
+print - 1;
+$a &= - 1;
+switch ($a) {
+ case - 1:
+}
+$a = $a ?? - 1;
+$a .= - 1;
+$a /= - 1;
+$a = [1 => - 1];
+$a = $a == - 1;
+$a = $a >= - 1;
+$a = $a === - 1;
+$a = $a != - 1;
+$a = $a !== - 1;
+$a = $a <= - 1;
+$a = $a <=> - 1;
+$a = $a and - 1;
+$a = $a or - 1;
+$a = $a xor - 1;
+$a -= - 1;
+$a %= - 1;
+$a *= - 1;
+$a |= - 1;
+$a += - 1;
+$a = $a ** - 1;
+$a **= - 1;
+$a = $a << - 1;
+$a <<= - 1;
+$a = $a >> - 1;
+$a >>= - 1;
+$a = (string) - 1;
+$a = (array) - 1;
+$a = (bool) - 1;
+$a = (object) - 1;
+$a = (unset) - 1;
+$a = (float) - 1;
+$a = (int) - 1;
+$a ^= - 1;
+$a = [$a, - 1];
+$a = $a[- 1];
+$a = $a ? - 1 : - 1;
+
+
+yield -$b;
+echo -$b;
+$a = -$b;
+func(-$b);
+$a = [-$b];
+return -$b;
+print -$b;
+$a &= -$b;
+switch ($a) {
+ case -$b:
+}
+$a = $a ?? -$b;
+$a .= -$b;
+$a /= -$b;
+$a = [1 => -$b];
+$a = $a == -$b;
+$a = $a >= -$b;
+$a = $a === -$b;
+$a = $a != -$b;
+$a = $a !== -$b;
+$a = $a <= -$b;
+$a = $a <=> -$b;
+$a = $a and -$b;
+$a = $a or -$b;
+$a = $a xor -$b;
+$a -= -$b;
+$a %= -$b;
+$a *= -$b;
+$a |= -$b;
+$a += -$b;
+$a = $a ** -$b;
+$a **= -$b;
+$a = $a << -$b;
+$a <<= -$b;
+$a = $a >> -$b;
+$a >>= -$b;
+$a = (string) -$b;
+$a = (array) -$b;
+$a = (bool) -$b;
+$a = (object) -$b;
+$a = (unset) -$b;
+$a = (float) -$b;
+$a = (int) -$b;
+$a ^= -$b;
+$a = [$a, -$b];
+$a = $a[-$b];
+$a = $a ? -$b : -$b;
+
+yield - $b;
+echo - $b;
+$a = - $b;
+func(- $b);
+$a = [- $b];
+return - $b;
+print - $b;
+$a &= - $b;
+switch ($a) {
+ case - $b:
+}
+$a = $a ?? - $b;
+$a .= - $b;
+$a /= - $b;
+$a = [1 => - $b];
+$a = $a == - $b;
+$a = $a >= - $b;
+$a = $a === - $b;
+$a = $a != - $b;
+$a = $a !== - $b;
+$a = $a <= - $b;
+$a = $a <=> - $b;
+$a = $a and - $b;
+$a = $a or - $b;
+$a = $a xor - $b;
+$a -= - $b;
+$a %= - $b;
+$a *= - $b;
+$a |= - $b;
+$a += - $b;
+$a = $a ** - $b;
+$a **= - $b;
+$a = $a << - $b;
+$a <<= - $b;
+$a = $a >> - $b;
+$a >>= - $b;
+$a = (string) - $b;
+$a = (array) - $b;
+$a = (bool) - $b;
+$a = (object) - $b;
+$a = (unset) - $b;
+$a = (float) - $b;
+$a = (int) - $b;
+$a ^= - $b;
+$a = [$a, - $b];
+$a = $a[- $b];
+$a = $a ? - $b : - $b;
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 00000000..adb2dc28
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,18 @@
+ 'value',
'key2' => 'value',
];
diff --git a/tests/input/trailing_comma_on_array.php b/tests/input/trailing_comma_on_array.php
index aeb1a729..3b39e357 100644
--- a/tests/input/trailing_comma_on_array.php
+++ b/tests/input/trailing_comma_on_array.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-$array = [
+$array = [
'key1' => 'value',
'key2' => 'value'
];