diff --git a/package.xml b/package.xml index 73aa918436..f040c25063 100644 --- a/package.xml +++ b/package.xml @@ -65,22 +65,126 @@ http://pear.php.net/dtd/package-2.0.xsd">
- * class ClassScopeTest extends PHP_CodeSniffer_Standards_AbstractScopeSniff
+ * use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
+ * use PHP_CodeSniffer\Files\File;
+ * use PHP_CodeSniffer\Utils\Sniffs\ConstructNames;
+ *
+ * class ClassScopeTest extends AbstractScopeSniff
* {
* public function __construct()
* {
* parent::__construct(array(T_CLASS), array(T_FUNCTION));
* }
*
- * protected function processTokenWithinScope(\PHP_CodeSniffer\Files\File $phpcsFile, $stackPtr, $currScope)
+ * protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
* {
- * $className = $phpcsFile->getDeclarationName($currScope);
+ * $className = ConstructNames::getDeclarationName($phpcsFile, $currScope);
+ * if (empty($className) === true) {
+ * // Live coding or parse error.
+ * return;
+ * }
+ *
* echo 'encountered a method within class '.$className;
* }
* }
@@ -66,7 +75,8 @@ abstract class AbstractScopeSniff implements Sniff
* processTokenOutsideScope method.
*
* @see PHP_CodeSniffer.getValidScopeTokeners()
- * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified tokens array is empty.
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified tokens arrays are empty
+ * or invalid.
*/
public function __construct(
array $scopeTokens,
diff --git a/src/Sniffs/AbstractVariableSniff.php b/src/Sniffs/AbstractVariableSniff.php
index e03f412d29..3dc6bd40ab 100644
--- a/src/Sniffs/AbstractVariableSniff.php
+++ b/src/Sniffs/AbstractVariableSniff.php
@@ -16,6 +16,8 @@
namespace PHP_CodeSniffer\Sniffs;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\Variables;
use PHP_CodeSniffer\Util\Tokens;
abstract class AbstractVariableSniff extends AbstractScopeSniff
@@ -27,22 +29,13 @@ abstract class AbstractVariableSniff extends AbstractScopeSniff
*
* Used by various naming convention sniffs.
*
+ * Set from within the constructor.
+ *
* @var array
+ *
+ * @deprecated 3.5.0 Use PHP_CodeSniffer\Util\Sniffs\Variables::$phpReservedVars instead.
*/
- protected $phpReservedVars = [
- '_SERVER' => true,
- '_GET' => true,
- '_POST' => true,
- '_REQUEST' => true,
- '_SESSION' => true,
- '_ENV' => true,
- '_COOKIE' => true,
- '_FILES' => true,
- 'GLOBALS' => true,
- 'http_response_header' => true,
- 'HTTP_RAW_POST_DATA' => true,
- 'php_errormsg' => true,
- ];
+ protected $phpReservedVars = [];
/**
@@ -50,6 +43,12 @@ abstract class AbstractVariableSniff extends AbstractScopeSniff
*/
public function __construct()
{
+ // Preserve BC without code duplication.
+ $this->phpReservedVars = array_combine(
+ array_keys(Variables::$phpReservedVars),
+ array_fill(0, count(Variables::$phpReservedVars), true)
+ );
+
$scopes = Tokens::$ooScopeTokens;
$listen = [
@@ -92,54 +91,12 @@ final protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $cu
return;
}
- // If this token is nested inside a function at a deeper
- // level than the current OO scope that was found, it's a normal
- // variable and not a member var.
- $conditions = array_reverse($tokens[$stackPtr]['conditions'], true);
- $inFunction = false;
- foreach ($conditions as $scope => $code) {
- if (isset(Tokens::$ooScopeTokens[$code]) === true) {
- break;
- }
-
- if ($code === T_FUNCTION || $code === T_CLOSURE) {
- $inFunction = true;
- }
- }
-
- if ($scope !== $currScope) {
- // We found a closer scope to this token, so ignore
- // this particular time through the sniff. We will process
- // this token when this closer scope is found to avoid
- // duplicate checks.
+ $deepestScope = Conditions::getLastCondition($phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
+ if ($deepestScope !== $currScope) {
return;
}
- // Just make sure this isn't a variable in a function declaration.
- if ($inFunction === false && isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- foreach ($tokens[$stackPtr]['nested_parenthesis'] as $opener => $closer) {
- if (isset($tokens[$opener]['parenthesis_owner']) === false) {
- // Check if this is a USE statement for a closure.
- $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), null, true);
- if ($tokens[$prev]['code'] === T_USE) {
- $inFunction = true;
- break;
- }
-
- continue;
- }
-
- $owner = $tokens[$opener]['parenthesis_owner'];
- if ($tokens[$owner]['code'] === T_FUNCTION
- || $tokens[$owner]['code'] === T_CLOSURE
- ) {
- $inFunction = true;
- break;
- }
- }
- }//end if
-
- if ($inFunction === true) {
+ if (Conditions::isOOProperty($phpcsFile, $stackPtr) === false) {
return $this->processVariable($phpcsFile, $stackPtr);
} else {
return $this->processMemberVar($phpcsFile, $stackPtr);
diff --git a/src/Standards/Generic/Docs/Lists/DisallowLongListSyntaxStandard.xml b/src/Standards/Generic/Docs/Lists/DisallowLongListSyntaxStandard.xml
new file mode 100644
index 0000000000..d1b0ea6109
--- /dev/null
+++ b/src/Standards/Generic/Docs/Lists/DisallowLongListSyntaxStandard.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ [$a, $b] = $array;
+ ]]>
+
+
+ list($a, $b) = $array;
+ ]]>
+
+
+
diff --git a/src/Standards/Generic/Docs/Lists/DisallowShortListSyntaxStandard.xml b/src/Standards/Generic/Docs/Lists/DisallowShortListSyntaxStandard.xml
new file mode 100644
index 0000000000..db6e66e630
--- /dev/null
+++ b/src/Standards/Generic/Docs/Lists/DisallowShortListSyntaxStandard.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ list($a, $b) = $array;
+ ]]>
+
+
+ [$a, $b] = $array;
+ ]]>
+
+
+
diff --git a/src/Standards/Generic/Sniffs/Arrays/DisallowShortArraySyntaxSniff.php b/src/Standards/Generic/Sniffs/Arrays/DisallowShortArraySyntaxSniff.php
index 8bd01ac9d9..98b1cccebe 100644
--- a/src/Standards/Generic/Sniffs/Arrays/DisallowShortArraySyntaxSniff.php
+++ b/src/Standards/Generic/Sniffs/Arrays/DisallowShortArraySyntaxSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\TokenIs;
class DisallowShortArraySyntaxSniff implements Sniff
{
@@ -39,13 +40,19 @@ public function register()
*/
public function process(File $phpcsFile, $stackPtr)
{
+ $tokens = $phpcsFile->getTokens();
+
+ if (TokenIs::isShortList($phpcsFile, $stackPtr) === true) {
+ // No need to examine nested subs of this short list.
+ return $tokens[$stackPtr]['bracket_closer'];
+ }
+
$phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes');
$error = 'Short array syntax is not allowed';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
if ($fix === true) {
- $tokens = $phpcsFile->getTokens();
$opener = $tokens[$stackPtr]['bracket_opener'];
$closer = $tokens[$stackPtr]['bracket_closer'];
diff --git a/src/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php b/src/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php
index 0288063af1..c4292107b2 100644
--- a/src/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php
+++ b/src/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Namespaces;
class DuplicateClassNameSniff implements Sniff
{
@@ -22,6 +23,20 @@ class DuplicateClassNameSniff implements Sniff
*/
protected $foundClasses = [];
+ /**
+ * The name of the last file seen.
+ *
+ * @var string
+ */
+ private $currentFile = '';
+
+ /**
+ * The name of the current namespace.
+ *
+ * @var string
+ */
+ private $currentNamespace = '';
+
/**
* Registers the tokens that this sniff wants to listen for.
@@ -46,9 +61,14 @@ public function register()
*/
public function process(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
+ $tokens = $phpcsFile->getTokens();
+ $fileName = $phpcsFile->getFilename();
+
+ if ($fileName !== $this->currentFile) {
+ $this->currentNamespace = '';
+ $this->currentFile = $fileName;
+ }
- $namespace = '';
$findTokens = [
T_CLASS,
T_INTERFACE,
@@ -67,24 +87,21 @@ public function process(File $phpcsFile, $stackPtr)
// Keep track of what namespace we are in.
if ($tokens[$stackPtr]['code'] === T_NAMESPACE) {
- $nsEnd = $phpcsFile->findNext(
- [
- T_NS_SEPARATOR,
- T_STRING,
- T_WHITESPACE,
- ],
- ($stackPtr + 1),
- null,
- true
- );
-
- $namespace = trim($phpcsFile->getTokensAsString(($stackPtr + 1), ($nsEnd - $stackPtr - 1)));
- $stackPtr = $nsEnd;
+ $newNamespace = Namespaces::getDeclaredName($phpcsFile, $stackPtr);
+ if ($newNamespace !== false) {
+ $this->currentNamespace = $newNamespace;
+ $stackPtr = $phpcsFile->findNext(Namespaces::$statementClosers, ($stackPtr + 1));
+
+ if ($tokens[$stackPtr]['code'] === T_CLOSE_TAG) {
+ // Namespace declaration ended on a close tag.
+ return;
+ }
+ }
} else {
$nameToken = $phpcsFile->findNext(T_STRING, $stackPtr);
$name = $tokens[$nameToken]['content'];
- if ($namespace !== '') {
- $name = $namespace.'\\'.$name;
+ if ($this->currentNamespace !== '') {
+ $name = $this->currentNamespace.'\\'.$name;
}
$compareName = strtolower($name);
diff --git a/src/Standards/Generic/Sniffs/CodeAnalysis/EmptyPHPStatementSniff.php b/src/Standards/Generic/Sniffs/CodeAnalysis/EmptyPHPStatementSniff.php
index 03a6f09985..c9707ee9a1 100644
--- a/src/Standards/Generic/Sniffs/CodeAnalysis/EmptyPHPStatementSniff.php
+++ b/src/Standards/Generic/Sniffs/CodeAnalysis/EmptyPHPStatementSniff.php
@@ -14,6 +14,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
class EmptyPHPStatementSniff implements Sniff
@@ -61,15 +62,9 @@ public function process(File $phpcsFile, $stackPtr)
return;
}
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- $nested = $tokens[$stackPtr]['nested_parenthesis'];
- $lastCloser = array_pop($nested);
- if (isset($tokens[$lastCloser]['parenthesis_owner']) === true
- && $tokens[$tokens[$lastCloser]['parenthesis_owner']]['code'] === T_FOR
- ) {
- // Empty for() condition.
- return;
- }
+ if (Parentheses::lastOwnerIn($phpcsFile, $stackPtr, T_FOR) !== false) {
+ // Empty for() condition.
+ return;
}
$fix = $phpcsFile->addFixableWarning(
diff --git a/src/Standards/Generic/Sniffs/CodeAnalysis/UnusedFunctionParameterSniff.php b/src/Standards/Generic/Sniffs/CodeAnalysis/UnusedFunctionParameterSniff.php
index 07a9b03ef0..e0ac052eef 100644
--- a/src/Standards/Generic/Sniffs/CodeAnalysis/UnusedFunctionParameterSniff.php
+++ b/src/Standards/Generic/Sniffs/CodeAnalysis/UnusedFunctionParameterSniff.php
@@ -18,6 +18,9 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
+use PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations;
use PHP_CodeSniffer\Util\Tokens;
class UnusedFunctionParameterSniff implements Sniff
@@ -61,10 +64,10 @@ public function process(File $phpcsFile, $stackPtr)
$errorCode = 'Found';
$implements = false;
$extends = false;
- $classPtr = $phpcsFile->getCondition($stackPtr, T_CLASS);
+ $classPtr = Conditions::getLastCondition($phpcsFile, $stackPtr, [T_CLASS, T_ANON_CLASS]);
if ($classPtr !== false) {
- $implements = $phpcsFile->findImplementedInterfaceNames($classPtr);
- $extends = $phpcsFile->findExtendedClassName($classPtr);
+ $implements = ObjectDeclarations::findImplementedInterfaceNames($phpcsFile, $classPtr);
+ $extends = ObjectDeclarations::findExtendedClassName($phpcsFile, $classPtr);
if ($extends !== false) {
$errorCode .= 'InExtendedClass';
} else if ($implements !== false) {
@@ -73,7 +76,7 @@ public function process(File $phpcsFile, $stackPtr)
}
$params = [];
- $methodParams = $phpcsFile->getMethodParameters($stackPtr);
+ $methodParams = FunctionDeclarations::getParameters($phpcsFile, $stackPtr);
// Skip when no parameters found.
$methodParamsCount = count($methodParams);
diff --git a/src/Standards/Generic/Sniffs/CodeAnalysis/UselessOverridingMethodSniff.php b/src/Standards/Generic/Sniffs/CodeAnalysis/UselessOverridingMethodSniff.php
index 39dcc1152a..9eb0b6ffe2 100644
--- a/src/Standards/Generic/Sniffs/CodeAnalysis/UselessOverridingMethodSniff.php
+++ b/src/Standards/Generic/Sniffs/CodeAnalysis/UselessOverridingMethodSniff.php
@@ -23,6 +23,8 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
use PHP_CodeSniffer\Util\Tokens;
class UselessOverridingMethodSniff implements Sniff
@@ -61,11 +63,16 @@ public function process(File $phpcsFile, $stackPtr)
}
// Get function name.
- $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ $methodName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($methodName) === true) {
+ // Live coding or parse error.
+ return;
+ }
// Get all parameters from method signature.
- $signature = [];
- foreach ($phpcsFile->getMethodParameters($stackPtr) as $param) {
+ $signature = [];
+ $parameters = FunctionDeclarations::getParameters($phpcsFile, $stackPtr);
+ foreach ($parameters as $param) {
$signature[] = $param['name'];
}
diff --git a/src/Standards/Generic/Sniffs/Commenting/DocCommentSniff.php b/src/Standards/Generic/Sniffs/Commenting/DocCommentSniff.php
index 14cc192301..c2a60a5219 100644
--- a/src/Standards/Generic/Sniffs/Commenting/DocCommentSniff.php
+++ b/src/Standards/Generic/Sniffs/Commenting/DocCommentSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Orthography;
class DocCommentSniff implements Sniff
{
@@ -155,7 +156,7 @@ public function process(File $phpcsFile, $stackPtr)
}
}
- if (preg_match('/^\p{Ll}/u', $shortContent) === 1) {
+ if (Orthography::isFirstCharLowercase($shortContent) === true) {
$error = 'Doc comment short description must start with a capital letter';
$phpcsFile->addError($error, $short, 'ShortNotCapital');
}
@@ -181,7 +182,9 @@ public function process(File $phpcsFile, $stackPtr)
}
}
- if (preg_match('/^\p{Ll}/u', $tokens[$long]['content']) === 1) {
+ // Allow for long comments in list format.
+ $longComment = ltrim($tokens[$long]['content'], '-');
+ if (Orthography::isFirstCharLowercase($longComment) === true) {
$error = 'Doc comment long description must start with a capital letter';
$phpcsFile->addError($error, $long, 'LongNotCapital');
}
diff --git a/src/Standards/Generic/Sniffs/Formatting/DisallowMultipleStatementsSniff.php b/src/Standards/Generic/Sniffs/Formatting/DisallowMultipleStatementsSniff.php
index a91af474fc..262f70ab7c 100644
--- a/src/Standards/Generic/Sniffs/Formatting/DisallowMultipleStatementsSniff.php
+++ b/src/Standards/Generic/Sniffs/Formatting/DisallowMultipleStatementsSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
class DisallowMultipleStatementsSniff implements Sniff
{
@@ -59,18 +60,8 @@ public function process(File $phpcsFile, $stackPtr)
} while ($tokens[$prev]['code'] === T_PHPCS_IGNORE);
// Ignore multiple statements in a FOR condition.
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- foreach ($tokens[$stackPtr]['nested_parenthesis'] as $bracket) {
- if (isset($tokens[$bracket]['parenthesis_owner']) === false) {
- // Probably a closure sitting inside a function call.
- continue;
- }
-
- $owner = $tokens[$bracket]['parenthesis_owner'];
- if ($tokens[$owner]['code'] === T_FOR) {
- return;
- }
- }
+ if (Parentheses::hasOwner($phpcsFile, $stackPtr, T_FOR) === true) {
+ return;
}
if ($tokens[$prev]['line'] === $tokens[$stackPtr]['line']) {
diff --git a/src/Standards/Generic/Sniffs/Formatting/MultipleStatementAlignmentSniff.php b/src/Standards/Generic/Sniffs/Formatting/MultipleStatementAlignmentSniff.php
index 4a68683235..4d9edf49c6 100644
--- a/src/Standards/Generic/Sniffs/Formatting/MultipleStatementAlignmentSniff.php
+++ b/src/Standards/Generic/Sniffs/Formatting/MultipleStatementAlignmentSniff.php
@@ -14,6 +14,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
class MultipleStatementAlignmentSniff implements Sniff
@@ -76,12 +77,8 @@ public function process(File $phpcsFile, $stackPtr)
$tokens = $phpcsFile->getTokens();
// Ignore assignments used in a condition, like an IF or FOR.
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- foreach ($tokens[$stackPtr]['nested_parenthesis'] as $start => $end) {
- if (isset($tokens[$start]['parenthesis_owner']) === true) {
- return;
- }
- }
+ if (Parentheses::hasOwner($phpcsFile, $stackPtr, Tokens::$parenthesisOpeners) === true) {
+ return;
}
$lastAssign = $this->checkAlignment($phpcsFile, $stackPtr);
@@ -216,12 +213,8 @@ public function checkAlignment($phpcsFile, $stackPtr, $end=null)
}
// Make sure it is not assigned inside a condition (eg. IF, FOR).
- if (isset($tokens[$assign]['nested_parenthesis']) === true) {
- foreach ($tokens[$assign]['nested_parenthesis'] as $start => $end) {
- if (isset($tokens[$start]['parenthesis_owner']) === true) {
- break(2);
- }
- }
+ if (Parentheses::hasOwner($phpcsFile, $assign, Tokens::$parenthesisOpeners) === true) {
+ break;
}
}//end if
diff --git a/src/Standards/Generic/Sniffs/Functions/CallTimePassByReferenceSniff.php b/src/Standards/Generic/Sniffs/Functions/CallTimePassByReferenceSniff.php
index 294346271c..b4c25b2756 100644
--- a/src/Standards/Generic/Sniffs/Functions/CallTimePassByReferenceSniff.php
+++ b/src/Standards/Generic/Sniffs/Functions/CallTimePassByReferenceSniff.php
@@ -11,6 +11,8 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
+use PHP_CodeSniffer\Util\Sniffs\TokenIs;
use PHP_CodeSniffer\Util\Tokens;
class CallTimePassByReferenceSniff implements Sniff
@@ -97,8 +99,7 @@ public function process(File $phpcsFile, $stackPtr)
// Make sure the variable belongs directly to this function call
// and is not inside a nested function call or array.
- $brackets = $tokens[$nextSeparator]['nested_parenthesis'];
- $lastBracket = array_pop($brackets);
+ $lastBracket = Parentheses::getLastCloser($phpcsFile, $nextSeparator);
if ($lastBracket !== $closeBracket) {
continue;
}
@@ -111,7 +112,7 @@ public function process(File $phpcsFile, $stackPtr)
);
if ($tokens[$tokenBefore]['code'] === T_BITWISE_AND) {
- if ($phpcsFile->isReference($tokenBefore) === false) {
+ if (TokenIs::isReference($phpcsFile, $tokenBefore) === false) {
continue;
}
diff --git a/src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php b/src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php
index 495151ea38..47dbdfedb1 100644
--- a/src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php
+++ b/src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
class FunctionCallArgumentSpacingSniff implements Sniff
@@ -108,8 +109,7 @@ public function process(File $phpcsFile, $stackPtr)
// Make sure the comma or variable belongs directly to this function call,
// and is not inside a nested function call or array.
- $brackets = $tokens[$nextSeparator]['nested_parenthesis'];
- $lastBracket = array_pop($brackets);
+ $lastBracket = Parentheses::getLastCloser($phpcsFile, $nextSeparator);
if ($lastBracket !== $closeBracket) {
continue;
}
diff --git a/src/Standards/Generic/Sniffs/Lists/DisallowLongListSyntaxSniff.php b/src/Standards/Generic/Sniffs/Lists/DisallowLongListSyntaxSniff.php
new file mode 100644
index 0000000000..f05a52f4e3
--- /dev/null
+++ b/src/Standards/Generic/Sniffs/Lists/DisallowLongListSyntaxSniff.php
@@ -0,0 +1,78 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Lists;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class DisallowLongListSyntaxSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return [T_LIST];
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+ if ($next === false || $tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
+ // Live coding or parse error.
+ return;
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Short list syntax used', 'no');
+
+ $error = 'Long list syntax is not allowed';
+ if (isset($tokens[$next]['parenthesis_closer']) === false) {
+ // Live coding/parse error, just show the error, don't try and fix it.
+ $phpcsFile->addError($error, $stackPtr, 'Found');
+ return;
+ }
+
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
+
+ if ($fix === true) {
+ $opener = $next;
+ $closer = $tokens[$next]['parenthesis_closer'];
+
+ $phpcsFile->fixer->beginChangeset();
+
+ $phpcsFile->fixer->replaceToken($stackPtr, '');
+ $phpcsFile->fixer->replaceToken($opener, '[');
+ $phpcsFile->fixer->replaceToken($closer, ']');
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ }//end process()
+
+
+}//end class
diff --git a/src/Standards/Generic/Sniffs/Lists/DisallowShortListSyntaxSniff.php b/src/Standards/Generic/Sniffs/Lists/DisallowShortListSyntaxSniff.php
new file mode 100644
index 0000000000..a9050d8a1c
--- /dev/null
+++ b/src/Standards/Generic/Sniffs/Lists/DisallowShortListSyntaxSniff.php
@@ -0,0 +1,68 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Lists;
+
+use PHP_CodeSniffer\Sniffs\Sniff;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\TokenIs;
+
+class DisallowShortListSyntaxSniff implements Sniff
+{
+
+
+ /**
+ * Registers the tokens that this sniff wants to listen for.
+ *
+ * @return int[]
+ */
+ public function register()
+ {
+ return [T_OPEN_SHORT_ARRAY];
+
+ }//end register()
+
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the current token
+ * in the stack passed in $tokens.
+ *
+ * @return void
+ */
+ public function process(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if (TokenIs::isShortList($phpcsFile, $stackPtr) === false) {
+ // No need to examine nested subs of this short array.
+ return $tokens[$stackPtr]['bracket_closer'];
+ }
+
+ $phpcsFile->recordMetric($stackPtr, 'Short list syntax used', 'yes');
+
+ $error = 'Short list syntax is not allowed';
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
+
+ if ($fix === true) {
+ $opener = $tokens[$stackPtr]['bracket_opener'];
+ $closer = $tokens[$stackPtr]['bracket_closer'];
+
+ $phpcsFile->fixer->beginChangeset();
+ $phpcsFile->fixer->replaceToken($opener, 'list(');
+ $phpcsFile->fixer->replaceToken($closer, ')');
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ }//end process()
+
+
+}//end class
diff --git a/src/Standards/Generic/Sniffs/NamingConventions/CamelCapsFunctionNameSniff.php b/src/Standards/Generic/Sniffs/NamingConventions/CamelCapsFunctionNameSniff.php
index 6b4331be71..dceb1523e1 100644
--- a/src/Standards/Generic/Sniffs/NamingConventions/CamelCapsFunctionNameSniff.php
+++ b/src/Standards/Generic/Sniffs/NamingConventions/CamelCapsFunctionNameSniff.php
@@ -10,9 +10,11 @@
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
-use PHP_CodeSniffer\Util\Common;
-use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
+use PHP_CodeSniffer\Util\Tokens;
class CamelCapsFunctionNameSniff extends AbstractScopeSniff
{
@@ -20,54 +22,37 @@ class CamelCapsFunctionNameSniff extends AbstractScopeSniff
/**
* A list of all PHP magic methods.
*
+ * Set from within the constructor.
+ *
* @var array
+ *
+ * @deprecated 3.5.0 Use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::$magicMethods instead.
*/
- protected $magicMethods = [
- 'construct' => true,
- 'destruct' => true,
- 'call' => true,
- 'callstatic' => true,
- 'get' => true,
- 'set' => true,
- 'isset' => true,
- 'unset' => true,
- 'sleep' => true,
- 'wakeup' => true,
- 'tostring' => true,
- 'set_state' => true,
- 'clone' => true,
- 'invoke' => true,
- 'debuginfo' => true,
- ];
+ protected $magicMethods = [];
/**
* A list of all PHP non-magic methods starting with a double underscore.
*
* These come from PHP modules such as SOAPClient.
*
+ * Set from within the constructor.
+ *
* @var array
+ *
+ * @deprecated 3.5.0 Use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::$methodsDoubleUnderscore instead.
*/
- protected $methodsDoubleUnderscore = [
- 'dorequest' => true,
- 'getcookies' => true,
- 'getfunctions' => true,
- 'getlastrequest' => true,
- 'getlastrequestheaders' => true,
- 'getlastresponse' => true,
- 'getlastresponseheaders' => true,
- 'gettypes' => true,
- 'setcookie' => true,
- 'setlocation' => true,
- 'setsoapheaders' => true,
- 'soapcall' => true,
- ];
+ protected $methodsDoubleUnderscore = [];
/**
* A list of all PHP magic functions.
*
+ * Set from within the constructor.
+ *
* @var array
+ *
+ * @deprecated 3.5.0 Use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::$magicFunctions instead.
*/
- protected $magicFunctions = ['autoload' => true];
+ protected $magicFunctions = [];
/**
* If TRUE, the string must not have two capital letters next to each other.
@@ -82,6 +67,22 @@ class CamelCapsFunctionNameSniff extends AbstractScopeSniff
*/
public function __construct()
{
+ // Preserve BC without code duplication.
+ $this->magicMethods = array_combine(
+ FunctionDeclarations::$magicMethods,
+ array_fill(0, count(FunctionDeclarations::$magicMethods), true)
+ );
+ $this->magicFunctions = array_combine(
+ FunctionDeclarations::$magicFunctions,
+ array_fill(0, count(FunctionDeclarations::$magicFunctions), true)
+ );
+
+ $methodsDoubleUnderscore = array_keys(FunctionDeclarations::$methodsDoubleUnderscore);
+ foreach ($methodsDoubleUnderscore as $method) {
+ $method = ltrim($method, '_');
+ $this->methodsDoubleUnderscore[$method] = true;
+ }
+
parent::__construct(Tokens::$ooScopeTokens, [T_FUNCTION], true);
}//end __construct()
@@ -102,35 +103,27 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$tokens = $phpcsFile->getTokens();
// Determine if this is a function which needs to be examined.
- $conditions = $tokens[$stackPtr]['conditions'];
- end($conditions);
- $deepestScope = key($conditions);
+ $deepestScope = Conditions::getLastCondition($phpcsFile, $stackPtr);
if ($deepestScope !== $currScope) {
return;
}
- $methodName = $phpcsFile->getDeclarationName($stackPtr);
- if ($methodName === null) {
- // Ignore closures.
+ $methodName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($methodName) === true) {
+ // Live coding or parse error.
return;
}
- $className = $phpcsFile->getDeclarationName($currScope);
+ $className = ConstructNames::getDeclarationName($phpcsFile, $currScope);
if (isset($className) === false) {
$className = '[Anonymous Class]';
}
$errorData = [$className.'::'.$methodName];
- $methodNameLc = strtolower($methodName);
- $classNameLc = strtolower($className);
-
- // Is this a magic method. i.e., is prefixed with "__" ?
+ // Check is this method is prefixed with "__" and not magic.
if (preg_match('|^__[^_]|', $methodName) !== 0) {
- $magicPart = substr($methodNameLc, 2);
- if (isset($this->magicMethods[$magicPart]) === true
- || isset($this->methodsDoubleUnderscore[$magicPart]) === true
- ) {
+ if (FunctionDeclarations::isSpecialMethodName($methodName) === true) {
return;
}
@@ -138,6 +131,9 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$phpcsFile->addError($error, $stackPtr, 'MethodDoubleUnderscore', $errorData);
}
+ $methodNameLc = strtolower($methodName);
+ $classNameLc = strtolower($className);
+
// PHP4 constructors are allowed to break our rules.
if ($methodNameLc === $classNameLc) {
return;
@@ -151,8 +147,8 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
// Ignore first underscore in methods prefixed with "_".
$methodName = ltrim($methodName, '_');
- $methodProps = $phpcsFile->getMethodProperties($stackPtr);
- if (Common::isCamelCaps($methodName, false, true, $this->strict) === false) {
+ $methodProps = FunctionDeclarations::getProperties($phpcsFile, $stackPtr);
+ if (ConstructNames::isCamelCaps($methodName, false, true, $this->strict) === false) {
if ($methodProps['scope_specified'] === true) {
$error = '%s method name "%s" is not in camel caps format';
$data = [
@@ -185,18 +181,17 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
*/
protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
{
- $functionName = $phpcsFile->getDeclarationName($stackPtr);
- if ($functionName === null) {
- // Ignore closures.
+ $functionName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($functionName) === true) {
+ // Live coding or parse error.
return;
}
$errorData = [$functionName];
- // Is this a magic function. i.e., it is prefixed with "__".
+ // Check is this function is prefixed with "__" and not magic.
if (preg_match('|^__[^_]|', $functionName) !== 0) {
- $magicPart = strtolower(substr($functionName, 2));
- if (isset($this->magicFunctions[$magicPart]) === true) {
+ if (FunctionDeclarations::isMagicFunctionName($functionName) === true) {
return;
}
@@ -207,7 +202,7 @@ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
// Ignore first underscore in functions prefixed with "_".
$functionName = ltrim($functionName, '_');
- if (Common::isCamelCaps($functionName, false, true, $this->strict) === false) {
+ if (ConstructNames::isCamelCaps($functionName, false, true, $this->strict) === false) {
$error = 'Function name "%s" is not in camel caps format';
$phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
$phpcsFile->recordMetric($stackPtr, 'CamelCase function name', 'no');
diff --git a/src/Standards/Generic/Sniffs/NamingConventions/ConstructorNameSniff.php b/src/Standards/Generic/Sniffs/NamingConventions/ConstructorNameSniff.php
index 2b233c9d5d..628ae466c7 100644
--- a/src/Standards/Generic/Sniffs/NamingConventions/ConstructorNameSniff.php
+++ b/src/Standards/Generic/Sniffs/NamingConventions/ConstructorNameSniff.php
@@ -15,6 +15,9 @@
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations;
class ConstructorNameSniff extends AbstractScopeSniff
{
@@ -59,20 +62,18 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$tokens = $phpcsFile->getTokens();
// Determine if this is a function which needs to be examined.
- $conditions = $tokens[$stackPtr]['conditions'];
- end($conditions);
- $deepestScope = key($conditions);
+ $deepestScope = Conditions::getLastCondition($phpcsFile, $stackPtr);
if ($deepestScope !== $currScope) {
return;
}
- $className = strtolower($phpcsFile->getDeclarationName($currScope));
+ $className = strtolower(ConstructNames::getDeclarationName($phpcsFile, $currScope));
if ($className !== $this->currentClass) {
$this->loadFunctionNamesInScope($phpcsFile, $currScope);
$this->currentClass = $className;
}
- $methodName = strtolower($phpcsFile->getDeclarationName($stackPtr));
+ $methodName = strtolower(ConstructNames::getDeclarationName($phpcsFile, $stackPtr));
if ($methodName === $className) {
if (in_array('__construct', $this->functionList, true) === false) {
@@ -89,7 +90,7 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
return;
}
- $parentClassName = strtolower($phpcsFile->findExtendedClassName($currScope));
+ $parentClassName = strtolower(ObjectDeclarations::findExtendedClassName($phpcsFile, $currScope));
if ($parentClassName === false) {
return;
}
@@ -144,7 +145,7 @@ protected function loadFunctionNamesInScope(File $phpcsFile, $currScope)
continue;
}
- $this->functionList[] = trim(strtolower($phpcsFile->getDeclarationName($i)));
+ $this->functionList[] = trim(strtolower(ConstructNames::getDeclarationName($phpcsFile, $i)));
if (isset($tokens[$i]['scope_closer']) !== false) {
// Skip past nested functions and such.
@@ -152,6 +153,9 @@ protected function loadFunctionNamesInScope(File $phpcsFile, $currScope)
}
}
+ // Remove any empties.
+ $this->functionList = array_filter($this->functionList);
+
}//end loadFunctionNamesInScope()
diff --git a/src/Standards/Generic/Sniffs/PHP/LowerCaseTypeSniff.php b/src/Standards/Generic/Sniffs/PHP/LowerCaseTypeSniff.php
index a490d1a74d..dedf4668b7 100644
--- a/src/Standards/Generic/Sniffs/PHP/LowerCaseTypeSniff.php
+++ b/src/Standards/Generic/Sniffs/PHP/LowerCaseTypeSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
use PHP_CodeSniffer\Util\Tokens;
class LowerCaseTypeSniff implements Sniff
@@ -85,7 +86,7 @@ public function process(File $phpcsFile, $stackPtr)
'object' => true,
];
- $props = $phpcsFile->getMethodProperties($stackPtr);
+ $props = FunctionDeclarations::getProperties($phpcsFile, $stackPtr);
// Strip off potential nullable indication.
$returnType = ltrim($props['return_type'], '?');
@@ -118,7 +119,7 @@ public function process(File $phpcsFile, $stackPtr)
}//end if
}//end if
- $params = $phpcsFile->getMethodParameters($stackPtr);
+ $params = FunctionDeclarations::getParameters($phpcsFile, $stackPtr);
if (empty($params) === true) {
return;
}
diff --git a/src/Standards/Generic/Sniffs/WhiteSpace/LanguageConstructSpacingSniff.php b/src/Standards/Generic/Sniffs/WhiteSpace/LanguageConstructSpacingSniff.php
index 82bf536292..4265b3a0e4 100644
--- a/src/Standards/Generic/Sniffs/WhiteSpace/LanguageConstructSpacingSniff.php
+++ b/src/Standards/Generic/Sniffs/WhiteSpace/LanguageConstructSpacingSniff.php
@@ -12,7 +12,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Common;
-use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Util\Sniffs\Namespaces;
class LanguageConstructSpacingSniff implements Sniff
{
@@ -69,12 +69,11 @@ public function process(File $phpcsFile, $stackPtr)
}
$content = $tokens[$stackPtr]['content'];
- if ($tokens[$stackPtr]['code'] === T_NAMESPACE) {
- $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
- if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === T_NS_SEPARATOR) {
- // Namespace keyword used as operator, not as the language construct.
- return;
- }
+ if ($tokens[$stackPtr]['code'] === T_NAMESPACE
+ && Namespaces::isDeclaration($phpcsFile, $stackPtr) === false
+ ) {
+ // Namespace keyword used as operator, not as the language construct; or live coding.
+ return;
}
if ($tokens[$stackPtr]['code'] === T_YIELD_FROM
diff --git a/src/Standards/Generic/Sniffs/WhiteSpace/ScopeIndentSniff.php b/src/Standards/Generic/Sniffs/WhiteSpace/ScopeIndentSniff.php
index 14ecb27c37..0b5c9b61aa 100644
--- a/src/Standards/Generic/Sniffs/WhiteSpace/ScopeIndentSniff.php
+++ b/src/Standards/Generic/Sniffs/WhiteSpace/ScopeIndentSniff.php
@@ -11,6 +11,8 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Config;
@@ -70,7 +72,7 @@ class ScopeIndentSniff implements Sniff
* or PHP open/close tags can escape from here and have their own
* rules elsewhere.
*
- * @var int[]
+ * @var string[]
*/
public $ignoreIndentationTokens = [];
@@ -80,14 +82,14 @@ class ScopeIndentSniff implements Sniff
* This is a cached copy of the public version of this var, which
* can be set in a ruleset file, and some core ignored tokens.
*
- * @var int[]
+ * @var (integer|string)[]
*/
private $ignoreIndentation = [];
/**
* Any scope openers that should not cause an indent.
*
- * @var int[]
+ * @var (integer|string)[]
*/
protected $nonIndentingScopes = [];
@@ -280,28 +282,25 @@ public function process(File $phpcsFile, $stackPtr)
$parenOpener = $tokens[$parenCloser]['parenthesis_opener'];
if ($tokens[$parenCloser]['line'] !== $tokens[$parenOpener]['line']) {
- $parens = 0;
- if (isset($tokens[$parenCloser]['nested_parenthesis']) === true
- && empty($tokens[$parenCloser]['nested_parenthesis']) === false
- ) {
- $parens = $tokens[$parenCloser]['nested_parenthesis'];
- end($parens);
- $parens = key($parens);
+ $parens = 0;
+ $lastParens = Parentheses::getLastOpener($phpcsFile, $parenCloser);
+ if ($lastParens !== false) {
+ $parens = $lastParens;
if ($this->debug === true) {
$line = $tokens[$parens]['line'];
echo "\t* token has nested parenthesis $parens on line $line *".PHP_EOL;
}
}
- $condition = 0;
- if (isset($tokens[$parenCloser]['conditions']) === true
- && empty($tokens[$parenCloser]['conditions']) === false
+ unset($lastParens);
+
+ $condition = 0;
+ $endCondition = Conditions::getLastCondition($phpcsFile, $parenCloser);
+ if ($endCondition !== false
&& (isset($tokens[$parenCloser]['parenthesis_owner']) === false
|| $parens > 0)
) {
- $condition = $tokens[$parenCloser]['conditions'];
- end($condition);
- $condition = key($condition);
+ $condition = $endCondition;
if ($this->debug === true) {
$line = $tokens[$condition]['line'];
$type = $tokens[$condition]['type'];
@@ -327,8 +326,7 @@ public function process(File $phpcsFile, $stackPtr)
$exact = false;
- $lastOpenTagConditions = array_keys($tokens[$lastOpenTag]['conditions']);
- $lastOpenTagCondition = array_pop($lastOpenTagConditions);
+ $lastOpenTagCondition = Conditions::getLastCondition($phpcsFile, $lastOpenTag);
if ($condition > 0 && $lastOpenTagCondition === $condition) {
if ($this->debug === true) {
@@ -566,9 +564,7 @@ public function process(File $phpcsFile, $stackPtr)
&& $tokens[$checkToken]['scope_opener'] === $checkToken))
) {
if (empty($tokens[$checkToken]['conditions']) === false) {
- $condition = $tokens[$checkToken]['conditions'];
- end($condition);
- $condition = key($condition);
+ $condition = Conditions::getLastCondition($phpcsFile, $checkToken);
} else {
$condition = $tokens[$checkToken]['scope_condition'];
}
@@ -715,26 +711,21 @@ public function process(File $phpcsFile, $stackPtr)
}
}
- $parens = 0;
- if (isset($tokens[$scopeCloser]['nested_parenthesis']) === true
- && empty($tokens[$scopeCloser]['nested_parenthesis']) === false
- ) {
- $parens = $tokens[$scopeCloser]['nested_parenthesis'];
- end($parens);
- $parens = key($parens);
+ $parens = 0;
+ $lastParens = Parentheses::getLastOpener($phpcsFile, $scopeCloser);
+ if ($lastParens !== false) {
+ $parens = $lastParens;
if ($this->debug === true) {
$line = $tokens[$parens]['line'];
echo "\t* token has nested parenthesis $parens on line $line *".PHP_EOL;
}
}
+ unset($lastParens);
+
$condition = 0;
- if (isset($tokens[$scopeCloser]['conditions']) === true
- && empty($tokens[$scopeCloser]['conditions']) === false
- ) {
- $condition = $tokens[$scopeCloser]['conditions'];
- end($condition);
- $condition = key($condition);
+ if (empty($tokens[$scopeCloser]['conditions']) === false) {
+ $condition = Conditions::getLastCondition($phpcsFile, $scopeCloser);
if ($this->debug === true) {
$line = $tokens[$condition]['line'];
$type = $tokens[$condition]['type'];
@@ -886,8 +877,7 @@ public function process(File $phpcsFile, $stackPtr)
if ($close !== false
&& $tokens[$checkToken]['conditions'] === $tokens[$close]['conditions']
) {
- $conditions = array_keys($tokens[$checkToken]['conditions']);
- $lastCondition = array_pop($conditions);
+ $lastCondition = Conditions::getLastCondition($phpcsFile, $checkToken);
$lastOpener = $tokens[$lastCondition]['scope_opener'];
$lastCloser = $tokens[$lastCondition]['scope_closer'];
if ($tokens[$lastCloser]['line'] !== $tokens[$checkToken]['line']
@@ -1248,13 +1238,9 @@ public function process(File $phpcsFile, $stackPtr)
$object = 0;
if ($phpcsFile->tokenizerType === 'JS') {
- $conditions = $tokens[$i]['conditions'];
- krsort($conditions, SORT_NUMERIC);
- foreach ($conditions as $token => $condition) {
- if ($condition === T_OBJECT) {
- $object = $token;
- break;
- }
+ $objectCondition = Conditions::getLastCondition($phpcsFile, $i, T_OBJECT);
+ if ($objectCondition !== false) {
+ $object = $objectCondition;
}
if ($this->debug === true && $object !== 0) {
@@ -1263,26 +1249,21 @@ public function process(File $phpcsFile, $stackPtr)
}
}
- $parens = 0;
- if (isset($tokens[$i]['nested_parenthesis']) === true
- && empty($tokens[$i]['nested_parenthesis']) === false
- ) {
- $parens = $tokens[$i]['nested_parenthesis'];
- end($parens);
- $parens = key($parens);
+ $parens = 0;
+ $lastParens = Parentheses::getLastOpener($phpcsFile, $i);
+ if ($lastParens !== false) {
+ $parens = $lastParens;
if ($this->debug === true) {
$line = $tokens[$parens]['line'];
echo "\t* token has nested parenthesis $parens on line $line *".PHP_EOL;
}
}
+ unset($lastParens);
+
$condition = 0;
- if (isset($tokens[$i]['conditions']) === true
- && empty($tokens[$i]['conditions']) === false
- ) {
- $condition = $tokens[$i]['conditions'];
- end($condition);
- $condition = key($condition);
+ if (empty($tokens[$i]['conditions']) === false) {
+ $condition = Conditions::getLastCondition($phpcsFile, $i);
if ($this->debug === true) {
$line = $tokens[$condition]['line'];
$type = $tokens[$condition]['type'];
diff --git a/src/Standards/Generic/Tests/Arrays/ArrayIndentUnitTest.inc b/src/Standards/Generic/Tests/Arrays/ArrayIndentUnitTest.inc
index 2a0ef0425d..35e8e77a36 100644
--- a/src/Standards/Generic/Tests/Arrays/ArrayIndentUnitTest.inc
+++ b/src/Standards/Generic/Tests/Arrays/ArrayIndentUnitTest.inc
@@ -56,3 +56,15 @@ $var = [
2 => 'two',
/* three */ 3 => 'three',
];
+
+// phpcs:set Generic.Arrays.ArrayIndent indent 4
+
+// Short lists should not be touched.
+[
+ $a,
+ [
+ $b,
+ $c,
+ $d,
+ ],
+] = $array;
diff --git a/src/Standards/Generic/Tests/Arrays/ArrayIndentUnitTest.inc.fixed b/src/Standards/Generic/Tests/Arrays/ArrayIndentUnitTest.inc.fixed
index 138565a7c7..652153d8cc 100644
--- a/src/Standards/Generic/Tests/Arrays/ArrayIndentUnitTest.inc.fixed
+++ b/src/Standards/Generic/Tests/Arrays/ArrayIndentUnitTest.inc.fixed
@@ -57,3 +57,15 @@ $var = [
2 => 'two',
/* three */ 3 => 'three',
];
+
+// phpcs:set Generic.Arrays.ArrayIndent indent 4
+
+// Short lists should not be touched.
+[
+ $a,
+ [
+ $b,
+ $c,
+ $d,
+ ],
+] = $array;
diff --git a/src/Standards/Generic/Tests/Arrays/DisallowShortArraySyntaxUnitTest.inc b/src/Standards/Generic/Tests/Arrays/DisallowShortArraySyntaxUnitTest.inc
index 74f27d8d47..0b86eecf33 100644
--- a/src/Standards/Generic/Tests/Arrays/DisallowShortArraySyntaxUnitTest.inc
+++ b/src/Standards/Generic/Tests/Arrays/DisallowShortArraySyntaxUnitTest.inc
@@ -10,3 +10,6 @@ $foo = [
2,
3
];
+
+// Short list, not short array.
+[$a, $b] = $array;
diff --git a/src/Standards/Generic/Tests/Arrays/DisallowShortArraySyntaxUnitTest.inc.fixed b/src/Standards/Generic/Tests/Arrays/DisallowShortArraySyntaxUnitTest.inc.fixed
index 7f68d4dc56..a585cef63c 100644
--- a/src/Standards/Generic/Tests/Arrays/DisallowShortArraySyntaxUnitTest.inc.fixed
+++ b/src/Standards/Generic/Tests/Arrays/DisallowShortArraySyntaxUnitTest.inc.fixed
@@ -10,3 +10,6 @@ $foo = array(
2,
3
);
+
+// Short list, not short array.
+[$a, $b] = $array;
diff --git a/src/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.10.inc b/src/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.10.inc
new file mode 100644
index 0000000000..d5ab20e3ed
--- /dev/null
+++ b/src/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.10.inc
@@ -0,0 +1,6 @@
+
+
+
+
+
+ 1,
10 => 1,
];
- break;
+
case 'DuplicateClassNameUnitTest.2.inc':
return [
2 => 1,
3 => 1,
4 => 1,
];
- break;
+
case 'DuplicateClassNameUnitTest.5.inc':
return [
3 => 1,
7 => 1,
];
- break;
+
case 'DuplicateClassNameUnitTest.6.inc':
return [10 => 1];
- break;
+
+ case 'DuplicateClassNameUnitTest.8.inc':
+ return [
+ 7 => 1,
+ 8 => 1,
+ ];
+
+ case 'DuplicateClassNameUnitTest.9.inc':
+ return [
+ 3 => 1,
+ 4 => 1,
+ ];
+
+ case 'DuplicateClassNameUnitTest.11.inc':
+ return [13 => 1];
+
default:
return [];
- break;
}//end switch
}//end getWarningList()
diff --git a/src/Standards/Generic/Tests/CodeAnalysis/UnusedFunctionParameterUnitTest.inc b/src/Standards/Generic/Tests/CodeAnalysis/UnusedFunctionParameterUnitTest.inc
index 3073e6fcdb..b7b6147a95 100644
--- a/src/Standards/Generic/Tests/CodeAnalysis/UnusedFunctionParameterUnitTest.inc
+++ b/src/Standards/Generic/Tests/CodeAnalysis/UnusedFunctionParameterUnitTest.inc
@@ -121,3 +121,29 @@ function something($a) {
function myCallback($a, $b, $c, $d) {
return $a * $c;
}
+
+$anonClass = new class() extends SomeClass {
+ public function something($a, $b) {
+ return $a * 2;
+ }
+};
+
+class ClassWithNestedAnonClass extends SomeClass {
+ public function getAnonClass() {
+ return new class() {
+ public function something($a, $b) {
+ return $a * 2;
+ }
+ };
+ }
+}
+
+class ClassWithNestedExtendedAnonClass {
+ public function getAnonClass() {
+ return new class() extends SomeClass {
+ public function something($a, $b) {
+ return $a * 2;
+ }
+ };
+ }
+}
diff --git a/src/Standards/Generic/Tests/CodeAnalysis/UnusedFunctionParameterUnitTest.php b/src/Standards/Generic/Tests/CodeAnalysis/UnusedFunctionParameterUnitTest.php
index 583e049864..a2f4743140 100644
--- a/src/Standards/Generic/Tests/CodeAnalysis/UnusedFunctionParameterUnitTest.php
+++ b/src/Standards/Generic/Tests/CodeAnalysis/UnusedFunctionParameterUnitTest.php
@@ -49,6 +49,9 @@ public function getWarningList()
106 => 1,
117 => 1,
121 => 2,
+ 126 => 1,
+ 134 => 1,
+ 144 => 1,
];
}//end getWarningList()
diff --git a/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.inc b/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.inc
index 81366272c9..e978b28a14 100644
--- a/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.inc
+++ b/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.inc
@@ -249,4 +249,18 @@
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
+/**
+ * Short description.
+ *
+ * - Long description in list format, correctly capitalized.
+ * - Second item long description in list format.
+ */
+
+/**
+ * Short description.
+ *
+ * - long description in list format, not capitalized
+ * - second item long description in list format
+ */
+
/** No docblock close tag. Must be last test without new line.
\ No newline at end of file
diff --git a/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.inc.fixed b/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.inc.fixed
index 43ce064a5d..723ac73221 100644
--- a/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.inc.fixed
+++ b/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.inc.fixed
@@ -254,4 +254,18 @@
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
+/**
+ * Short description.
+ *
+ * - Long description in list format, correctly capitalized.
+ * - Second item long description in list format.
+ */
+
+/**
+ * Short description.
+ *
+ * - long description in list format, not capitalized
+ * - second item long description in list format
+ */
+
/** No docblock close tag. Must be last test without new line.
\ No newline at end of file
diff --git a/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.js b/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.js
index b3283f7853..e59bdebbe0 100644
--- a/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.js
+++ b/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.js
@@ -248,3 +248,17 @@
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
+
+/**
+ * Short description.
+ *
+ * - Long description in list format, correctly capitalized.
+ * - Second item long description in list format.
+ */
+
+/**
+ * Short description.
+ *
+ * - long description in list format, not capitalized
+ * - second item long description in list format
+ */
diff --git a/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.js.fixed b/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.js.fixed
index 0df0687a2f..d32ab6cb75 100644
--- a/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.js.fixed
+++ b/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.js.fixed
@@ -253,3 +253,17 @@
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
+
+/**
+ * Short description.
+ *
+ * - Long description in list format, correctly capitalized.
+ * - Second item long description in list format.
+ */
+
+/**
+ * Short description.
+ *
+ * - long description in list format, not capitalized
+ * - second item long description in list format
+ */
diff --git a/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.php b/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.php
index 57937581ba..ddc774e6e7 100644
--- a/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.php
+++ b/src/Standards/Generic/Tests/Commenting/DocCommentUnitTest.php
@@ -88,6 +88,7 @@ public function getErrorList()
246 => 1,
248 => 1,
249 => 1,
+ 262 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.inc b/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.inc
index 7d0f9fb2de..4bb6eb5c7b 100644
--- a/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.inc
+++ b/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.inc
@@ -403,3 +403,11 @@ $foofoo = new Foo([
$b = new Bar(),
$c = new Bar(),
]);
+
+// Handling of short list syntax.
+foreach ($files as $file) {
+ $saves[$file] = array();
+ $contents = stripslashes(file_get_contents($file));
+ [$assetid, $time, $content] = explode("\n", $contents);
+ $saves[$file]['assetid'] = $assetid;
+}
\ No newline at end of file
diff --git a/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.inc.fixed b/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.inc.fixed
index 191c83e3f4..9e22d489d0 100644
--- a/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.inc.fixed
+++ b/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.inc.fixed
@@ -403,3 +403,11 @@ $foofoo = new Foo([
$b = new Bar(),
$c = new Bar(),
]);
+
+// Handling of short list syntax.
+foreach ($files as $file) {
+ $saves[$file] = array();
+ $contents = stripslashes(file_get_contents($file));
+ [$assetid, $time, $content] = explode("\n", $contents);
+ $saves[$file]['assetid'] = $assetid;
+}
\ No newline at end of file
diff --git a/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.php b/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.php
index 4076515161..fed8b6b896 100644
--- a/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.php
+++ b/src/Standards/Generic/Tests/Formatting/MultipleStatementAlignmentUnitTest.php
@@ -111,6 +111,9 @@ public function getWarningList($testFile='MultipleStatementAlignmentUnitTest.inc
398 => 1,
399 => 1,
401 => 1,
+ 409 => 1,
+ 410 => 1,
+ 412 => 1,
];
break;
case 'MultipleStatementAlignmentUnitTest.js':
diff --git a/src/Standards/Generic/Tests/Lists/DisallowLongListSyntaxUnitTest.inc b/src/Standards/Generic/Tests/Lists/DisallowLongListSyntaxUnitTest.inc
new file mode 100644
index 0000000000..9c450caf07
--- /dev/null
+++ b/src/Standards/Generic/Tests/Lists/DisallowLongListSyntaxUnitTest.inc
@@ -0,0 +1,21 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\Lists;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowLongListSyntaxUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array
+ */
+ public function getErrorList()
+ {
+ return [
+ 2 => 1,
+ 4 => 2,
+ 6 => 2,
+ 7 => 1,
+ 9 => 1,
+ 16 => 1,
+ 21 => 1,
+ ];
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array
+ */
+ public function getWarningList()
+ {
+ return [];
+
+ }//end getWarningList()
+
+
+}//end class
diff --git a/src/Standards/Generic/Tests/Lists/DisallowShortListSyntaxUnitTest.inc b/src/Standards/Generic/Tests/Lists/DisallowShortListSyntaxUnitTest.inc
new file mode 100644
index 0000000000..bec29ffa98
--- /dev/null
+++ b/src/Standards/Generic/Tests/Lists/DisallowShortListSyntaxUnitTest.inc
@@ -0,0 +1,17 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Standards\Generic\Tests\Lists;
+
+use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
+
+class DisallowShortListSyntaxUnitTest extends AbstractSniffUnitTest
+{
+
+
+ /**
+ * Returns the lines where errors should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of errors that should occur on that line.
+ *
+ * @return array
+ */
+ public function getErrorList()
+ {
+ return [
+ 6 => 1,
+ 8 => 2,
+ 9 => 2,
+ 10 => 1,
+ 12 => 1,
+ ];
+
+ }//end getErrorList()
+
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * The key of the array should represent the line number and the value
+ * should represent the number of warnings that should occur on that line.
+ *
+ * @return array
+ */
+ public function getWarningList()
+ {
+ return [];
+
+ }//end getWarningList()
+
+
+}//end class
diff --git a/src/Standards/Generic/Tests/NamingConventions/CamelCapsFunctionNameUnitTest.inc b/src/Standards/Generic/Tests/NamingConventions/CamelCapsFunctionNameUnitTest.inc
index 9bda11472e..76939c267f 100644
--- a/src/Standards/Generic/Tests/NamingConventions/CamelCapsFunctionNameUnitTest.inc
+++ b/src/Standards/Generic/Tests/NamingConventions/CamelCapsFunctionNameUnitTest.inc
@@ -33,42 +33,20 @@ function get_some_value() {}
/* Test for magic functions */
-class Magic_Test {
+class Magic_Test extends \SoapClient {
function __construct() {}
- function __destruct() {}
- function __call($name, $args) {}
- static function __callStatic($name, $args) {}
- function __get($name) {}
function __set($name, $value) {}
- function __isset($name) {}
- function __unset($name) {}
- function __sleep() {}
- function __wakeup() {}
- function __toString() {}
- function __set_state() {}
- function __clone() {}
+ function __getCookies() {}
function __autoload() {}
- function __invoke() {}
function __myFunction() {}
function __my_function() {}
}
-function __construct() {}
function __destruct() {}
-function __call() {}
function __callStatic() {}
-function __get() {}
-function __set() {}
-function __isset() {}
-function __unset() {}
-function __sleep() {}
-function __wakeup() {}
-function __toString() {}
function __set_state() {}
-function __clone() {}
function __autoload($class) {}
-function __invoke() {}
function __myFunction() {}
function __my_function() {}
@@ -90,68 +68,12 @@ class MyClass
public function __construct() {}
}
-trait Foo
-{
- function __call($name, $args) {}
-}
-
-class Magic_Case_Test {
- function __Construct() {}
- function __isSet($name) {}
- function __tostring() {}
-}
-function __autoLoad($class) {}
-
-class Foo extends \SoapClient
-{
- public function __soapCall(
- $functionName,
- $arguments,
- $options = array(),
- $inputHeaders = null,
- &$outputHeaders = array()
- ) {
- // body
- }
-}
-
-function __debugInfo() {}
-class Foo {
- function __debugInfo() {}
-}
-
function ___tripleUnderscore() {} // Ok.
class triple {
public function ___tripleUnderscore() {} // Ok.
}
-/* Magic methods in anonymous classes. */
-$a = new class {
- function __construct() {}
- function __destruct() {}
- function __call($name, $args) {}
- static function __callStatic($name, $args) {}
- function __get($name) {}
- function __set($name, $value) {}
- function __isset($name) {}
- function __unset($name) {}
- function __sleep() {}
- function __wakeup() {}
- function __toString() {}
- function __set_state() {}
- function __clone() {}
- function __autoload() {}
- function __invoke() {}
- function __myFunction() {}
- function __my_function() {}
-
-};
-
-class FooBar extends \SoapClient {
- public function __getCookies() {}
-}
-
class Nested {
public function getAnonymousClass() {
return new class() {
diff --git a/src/Standards/Generic/Tests/NamingConventions/CamelCapsFunctionNameUnitTest.php b/src/Standards/Generic/Tests/NamingConventions/CamelCapsFunctionNameUnitTest.php
index 502498d8bc..2ac640441c 100644
--- a/src/Standards/Generic/Tests/NamingConventions/CamelCapsFunctionNameUnitTest.php
+++ b/src/Standards/Generic/Tests/NamingConventions/CamelCapsFunctionNameUnitTest.php
@@ -26,43 +26,28 @@ class CamelCapsFunctionNameUnitTest extends AbstractSniffUnitTest
public function getErrorList()
{
$errors = [
- 10 => 1,
- 11 => 1,
- 12 => 1,
- 13 => 1,
- 16 => 1,
- 17 => 1,
- 20 => 1,
- 21 => 1,
- 24 => 1,
- 25 => 1,
- 30 => 1,
- 31 => 1,
- 50 => 1,
- 52 => 1,
- 53 => 2,
- 57 => 1,
- 58 => 1,
- 59 => 1,
- 60 => 1,
- 61 => 1,
- 62 => 1,
- 63 => 1,
- 64 => 1,
- 65 => 1,
- 66 => 1,
- 67 => 1,
- 68 => 2,
- 69 => 1,
- 71 => 1,
- 72 => 1,
- 73 => 2,
- 118 => 1,
- 144 => 1,
- 146 => 1,
- 147 => 2,
- 158 => 1,
- 159 => 1,
+ 10 => 1,
+ 11 => 1,
+ 12 => 1,
+ 13 => 1,
+ 16 => 1,
+ 17 => 1,
+ 20 => 1,
+ 21 => 1,
+ 24 => 1,
+ 25 => 1,
+ 30 => 1,
+ 31 => 1,
+ 40 => 1,
+ 41 => 1,
+ 42 => 2,
+ 46 => 1,
+ 47 => 1,
+ 48 => 2,
+ 50 => 1,
+ 51 => 2,
+ 80 => 1,
+ 81 => 1,
];
return $errors;
diff --git a/src/Standards/MySource/Sniffs/Channels/IncludeSystemSniff.php b/src/Standards/MySource/Sniffs/Channels/IncludeSystemSniff.php
index 599be1b9c4..8ea4554bcc 100644
--- a/src/Standards/MySource/Sniffs/Channels/IncludeSystemSniff.php
+++ b/src/Standards/MySource/Sniffs/Channels/IncludeSystemSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
use PHP_CodeSniffer\Util\Tokens;
class IncludeSystemSniff extends AbstractScopeSniff
@@ -94,7 +95,7 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$includedClasses[$matches[2]] = true;
// Or a system it implements.
- $class = $phpcsFile->getCondition($stackPtr, T_CLASS);
+ $class = Conditions::getCondition($phpcsFile, $stackPtr, T_CLASS);
$implements = $phpcsFile->findNext(T_IMPLEMENTS, $class, ($class + 10));
if ($implements !== false) {
$implementsClass = $phpcsFile->findNext(T_STRING, $implements);
@@ -123,13 +124,10 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
// Now go searching for includeSystem, includeAsset or require/include
// calls outside our scope. If we are in a class, look outside the
// class. If we are not, look outside the function.
- $condPtr = $currScope;
- if ($phpcsFile->hasCondition($stackPtr, T_CLASS) === true) {
- foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condType) {
- if ($condType === T_CLASS) {
- break;
- }
- }
+ $condPtr = $currScope;
+ $classCondPtr = Conditions::getCondition($phpcsFile, $stackPtr, T_CLASS);
+ if ($classCondPtr !== false) {
+ $condPtr = $classCondPtr;
}
for ($i = 0; $i < $condPtr; $i++) {
@@ -148,7 +146,7 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
// If we are in a testing class, we might have also included
// some systems and classes in our setUp() method.
$setupFunction = null;
- if ($phpcsFile->hasCondition($stackPtr, T_CLASS) === true) {
+ if (Conditions::hasCondition($phpcsFile, $stackPtr, T_CLASS) === true) {
foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condType) {
if ($condType === T_CLASS) {
// Is this is a testing class?
diff --git a/src/Standards/MySource/Sniffs/Channels/UnusedSystemSniff.php b/src/Standards/MySource/Sniffs/Channels/UnusedSystemSniff.php
index 6b10cd3ede..7782158172 100644
--- a/src/Standards/MySource/Sniffs/Channels/UnusedSystemSniff.php
+++ b/src/Standards/MySource/Sniffs/Channels/UnusedSystemSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
class UnusedSystemSniff implements Sniff
{
@@ -78,14 +79,11 @@ public function process(File $phpcsFile, $stackPtr)
if ($tokens[$stackPtr]['level'] === $level) {
// We are still in the base level, so this is the first
// time we have got here.
- $conditions = array_keys($tokens[$stackPtr]['conditions']);
- if (empty($conditions) === false) {
- $cond = array_pop($conditions);
- if ($tokens[$cond]['code'] === T_IF) {
- $i = $tokens[$cond]['scope_closer'];
- $level--;
- continue;
- }
+ $directScope = Conditions::validDirectScope($phpcsFile, $stackPtr, T_IF);
+ if ($directScope !== false) {
+ $i = $tokens[$directScope]['scope_closer'];
+ $level--;
+ continue;
}
}
diff --git a/src/Standards/MySource/Sniffs/Commenting/FunctionCommentSniff.php b/src/Standards/MySource/Sniffs/Commenting/FunctionCommentSniff.php
index fd75bcb22e..c5f44d0d6a 100644
--- a/src/Standards/MySource/Sniffs/Commenting/FunctionCommentSniff.php
+++ b/src/Standards/MySource/Sniffs/Commenting/FunctionCommentSniff.php
@@ -12,8 +12,8 @@
namespace PHP_CodeSniffer\Standards\MySource\Sniffs\Commenting;
use PHP_CodeSniffer\Standards\Squiz\Sniffs\Commenting\FunctionCommentSniff as SquizFunctionCommentSniff;
-use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
class FunctionCommentSniff extends SquizFunctionCommentSniff
{
@@ -32,12 +32,9 @@ public function process(File $phpcsFile, $stackPtr)
{
parent::process($phpcsFile, $stackPtr);
- $tokens = $phpcsFile->getTokens();
- $find = Tokens::$methodPrefixes;
- $find[] = T_WHITESPACE;
-
- $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
- if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG) {
+ $tokens = $phpcsFile->getTokens();
+ $commentEnd = Comments::findFunctionComment($phpcsFile, $stackPtr);
+ if ($commentEnd === false || $tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG) {
return;
}
diff --git a/src/Standards/MySource/Sniffs/Objects/CreateWidgetTypeCallbackSniff.php b/src/Standards/MySource/Sniffs/Objects/CreateWidgetTypeCallbackSniff.php
index 6224956b56..2fda8882cd 100644
--- a/src/Standards/MySource/Sniffs/Objects/CreateWidgetTypeCallbackSniff.php
+++ b/src/Standards/MySource/Sniffs/Objects/CreateWidgetTypeCallbackSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
class CreateWidgetTypeCallbackSniff implements Sniff
@@ -133,17 +134,14 @@ public function process(File $phpcsFile, $stackPtr)
// Just make sure those brackets dont belong to anyone,
// like an IF or FOR statement.
- foreach ($tokens[$i]['nested_parenthesis'] as $bracket) {
- if (isset($tokens[$bracket]['parenthesis_owner']) === true) {
- continue(2);
- }
+ if (Parentheses::hasOwner($phpcsFile, $i, Tokens::$parenthesisOpeners) === true) {
+ continue;
}
// Note that we use this endBracket down further when checking
// for a RETURN statement.
- $nestedParens = $tokens[$i]['nested_parenthesis'];
- $endBracket = end($nestedParens);
- $bracket = key($nestedParens);
+ $endBracket = Parentheses::getLastOpener($phpcsFile, $i);
+ $bracket = Parentheses::getLastCloser($phpcsFile, $i);
$prev = $phpcsFile->findPrevious(
Tokens::$emptyTokens,
diff --git a/src/Standards/MySource/Sniffs/PHP/AjaxNullComparisonSniff.php b/src/Standards/MySource/Sniffs/PHP/AjaxNullComparisonSniff.php
index f86fa9aef2..f3ceb083e3 100644
--- a/src/Standards/MySource/Sniffs/PHP/AjaxNullComparisonSniff.php
+++ b/src/Standards/MySource/Sniffs/PHP/AjaxNullComparisonSniff.php
@@ -14,6 +14,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
class AjaxNullComparisonSniff implements Sniff
{
@@ -42,11 +43,15 @@ public function register()
*/
public function process(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
+ $tokens = $phpcsFile->getTokens();
+ $commentEnd = Comments::findFunctionComment($phpcsFile, $stackPtr);
+ if ($commentEnd === false || $tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG) {
+ // Function doesn't have a doc comment or is using the wrong type of comment.
+ return;
+ }
// Make sure it is an API function. We know this by the doc comment.
- $commentEnd = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr);
- $commentStart = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, ($commentEnd - 1));
+ $commentStart = $tokens[$commentEnd]['comment_opener'];
$comment = $phpcsFile->getTokensAsString($commentStart, ($commentEnd - $commentStart));
if (strpos($comment, '* @api') === false) {
return;
diff --git a/src/Standards/MySource/Tests/Commenting/FunctionCommentUnitTest.inc b/src/Standards/MySource/Tests/Commenting/FunctionCommentUnitTest.inc
index 19d5c5d4cc..2128a70b49 100644
--- a/src/Standards/MySource/Tests/Commenting/FunctionCommentUnitTest.inc
+++ b/src/Standards/MySource/Tests/Commenting/FunctionCommentUnitTest.inc
@@ -98,4 +98,12 @@ private function functionCall() {}
* @api read
*/
private function functionCall() {}
-?>
+
+/**
+ * Short content.
+ *
+ * @api read
+ *
+ * @return void
+ */
+private /* comment */ function CorrectlyIdentifyDocblock() {}
diff --git a/src/Standards/MySource/Tests/Commenting/FunctionCommentUnitTest.php b/src/Standards/MySource/Tests/Commenting/FunctionCommentUnitTest.php
index 5cc43b6447..e4ffcb1c5b 100644
--- a/src/Standards/MySource/Tests/Commenting/FunctionCommentUnitTest.php
+++ b/src/Standards/MySource/Tests/Commenting/FunctionCommentUnitTest.php
@@ -26,11 +26,12 @@ class FunctionCommentUnitTest extends AbstractSniffUnitTest
public function getErrorList()
{
return [
- 28 => 1,
- 36 => 1,
- 37 => 2,
- 49 => 1,
- 58 => 1,
+ 28 => 1,
+ 36 => 1,
+ 37 => 2,
+ 49 => 1,
+ 58 => 1,
+ 108 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/MySource/Tests/PHP/AjaxNullComparisonUnitTest.inc b/src/Standards/MySource/Tests/PHP/AjaxNullComparisonUnitTest.inc
index aa95b03c70..faa9c74328 100644
--- a/src/Standards/MySource/Tests/PHP/AjaxNullComparisonUnitTest.inc
+++ b/src/Standards/MySource/Tests/PHP/AjaxNullComparisonUnitTest.inc
@@ -176,4 +176,21 @@ public static function addIssue(
}
}//end addIssue()
-?>
+
+
+/**
+ * Docblock.
+ *
+ * @param string $title Title of the new issue.
+ *
+ * @api write
+ * @api-permission public
+ */
+function apiFunction( $title = NULL ) {
+ if (empty($title)) {}
+}//end apiFunction()
+
+function functionWithoutDocblock( $title ) {
+ if ($title === NULL) {}
+
+}//end functionWithoutDocblock()
diff --git a/src/Standards/PEAR/Sniffs/Commenting/ClassCommentSniff.php b/src/Standards/PEAR/Sniffs/Commenting/ClassCommentSniff.php
index 90e9eb8cab..483123ca2e 100644
--- a/src/Standards/PEAR/Sniffs/Commenting/ClassCommentSniff.php
+++ b/src/Standards/PEAR/Sniffs/Commenting/ClassCommentSniff.php
@@ -10,7 +10,8 @@
namespace PHP_CodeSniffer\Standards\PEAR\Sniffs\Commenting;
use PHP_CodeSniffer\Files\File;
-use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
class ClassCommentSniff extends FileCommentSniff
{
@@ -43,18 +44,13 @@ public function register()
*/
public function process(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
- $type = strtolower($tokens[$stackPtr]['content']);
- $errorData = [$type];
-
- $find = Tokens::$methodPrefixes;
- $find[] = T_WHITESPACE;
-
- $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
- if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
- && $tokens[$commentEnd]['code'] !== T_COMMENT
- ) {
- $errorData[] = $phpcsFile->getDeclarationName($stackPtr);
+ $tokens = $phpcsFile->getTokens();
+ $type = strtolower($tokens[$stackPtr]['content']);
+ $errorData = [$type];
+ $commentEnd = Comments::findOOStructureComment($phpcsFile, $stackPtr);
+
+ if ($commentEnd === false) {
+ $errorData[] = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
$phpcsFile->addError('Missing doc comment for %s %s', $stackPtr, 'Missing', $errorData);
$phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'no');
return;
diff --git a/src/Standards/PEAR/Sniffs/Commenting/FileCommentSniff.php b/src/Standards/PEAR/Sniffs/Commenting/FileCommentSniff.php
index 25896153d2..06d8495be5 100644
--- a/src/Standards/PEAR/Sniffs/Commenting/FileCommentSniff.php
+++ b/src/Standards/PEAR/Sniffs/Commenting/FileCommentSniff.php
@@ -11,7 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
-use PHP_CodeSniffer\Util\Common;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
class FileCommentSniff implements Sniff
{
@@ -313,7 +313,7 @@ protected function processCategory($phpcsFile, array $tags)
}
$content = $tokens[($tag + 2)]['content'];
- if (Common::isUnderscoreName($content) !== true) {
+ if (ConstructNames::isUnderscoreName($content) !== true) {
$newContent = str_replace(' ', '_', $content);
$nameBits = explode('_', $newContent);
$firstBit = array_shift($nameBits);
@@ -355,7 +355,7 @@ protected function processPackage($phpcsFile, array $tags)
}
$content = $tokens[($tag + 2)]['content'];
- if (Common::isUnderscoreName($content) === true) {
+ if (ConstructNames::isUnderscoreName($content) === true) {
continue;
}
@@ -408,7 +408,7 @@ protected function processSubpackage($phpcsFile, array $tags)
}
$content = $tokens[($tag + 2)]['content'];
- if (Common::isUnderscoreName($content) === true) {
+ if (ConstructNames::isUnderscoreName($content) === true) {
continue;
}
diff --git a/src/Standards/PEAR/Sniffs/Commenting/FunctionCommentSniff.php b/src/Standards/PEAR/Sniffs/Commenting/FunctionCommentSniff.php
index 2f93218847..9d73135c40 100644
--- a/src/Standards/PEAR/Sniffs/Commenting/FunctionCommentSniff.php
+++ b/src/Standards/PEAR/Sniffs/Commenting/FunctionCommentSniff.php
@@ -11,7 +11,9 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
-use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
class FunctionCommentSniff implements Sniff
{
@@ -40,26 +42,9 @@ public function register()
*/
public function process(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
- $find = Tokens::$methodPrefixes;
- $find[] = T_WHITESPACE;
-
- $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
- if ($tokens[$commentEnd]['code'] === T_COMMENT) {
- // Inline comments might just be closing comments for
- // control structures or functions instead of function comments
- // using the wrong comment type. If there is other code on the line,
- // assume they relate to that code.
- $prev = $phpcsFile->findPrevious($find, ($commentEnd - 1), null, true);
- if ($prev !== false && $tokens[$prev]['line'] === $tokens[$commentEnd]['line']) {
- $commentEnd = $prev;
- }
- }
-
- if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
- && $tokens[$commentEnd]['code'] !== T_COMMENT
- ) {
- $function = $phpcsFile->getDeclarationName($stackPtr);
+ $commentEnd = Comments::findFunctionComment($phpcsFile, $stackPtr);
+ if ($commentEnd === false) {
+ $function = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
$phpcsFile->addError(
'Missing doc comment for function %s()',
$stackPtr,
@@ -72,6 +57,7 @@ public function process(File $phpcsFile, $stackPtr)
$phpcsFile->recordMetric($stackPtr, 'Function has doc comment', 'yes');
}
+ $tokens = $phpcsFile->getTokens();
if ($tokens[$commentEnd]['code'] === T_COMMENT) {
$phpcsFile->addError('You must use "/**" style comments for a function comment', $stackPtr, 'WrongStyle');
return;
@@ -116,7 +102,7 @@ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart)
$tokens = $phpcsFile->getTokens();
// Skip constructor and destructor.
- $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ $methodName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
$isSpecialMethod = ($methodName === '__construct' || $methodName === '__destruct');
$return = null;
@@ -279,7 +265,7 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
];
}//end foreach
- $realParams = $phpcsFile->getMethodParameters($stackPtr);
+ $realParams = FunctionDeclarations::getParameters($phpcsFile, $stackPtr);
$foundParams = [];
// We want to use ... for all variable length arguments, so add
diff --git a/src/Standards/PEAR/Sniffs/Files/IncludingFileSniff.php b/src/Standards/PEAR/Sniffs/Files/IncludingFileSniff.php
index cda11e4ae6..f503b11e7d 100644
--- a/src/Standards/PEAR/Sniffs/Files/IncludingFileSniff.php
+++ b/src/Standards/PEAR/Sniffs/Files/IncludingFileSniff.php
@@ -13,6 +13,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
class IncludingFileSniff implements Sniff
@@ -76,12 +77,8 @@ public function process(File $phpcsFile, $stackPtr)
// Check to see if this including statement is within the parenthesis
// of a condition. If that's the case then we need to process it as being
// within a condition, as they are checking the return value.
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- foreach ($tokens[$stackPtr]['nested_parenthesis'] as $left => $right) {
- if (isset($tokens[$left]['parenthesis_owner']) === true) {
- $inCondition = true;
- }
- }
+ if (Parentheses::hasOwner($phpcsFile, $stackPtr, Tokens::$parenthesisOpeners) === true) {
+ $inCondition = true;
}
// Check to see if they are assigning the return value of this
diff --git a/src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php b/src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php
index 0fa9cf4c30..9f2fad862b 100644
--- a/src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php
+++ b/src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Standards\Generic\Sniffs\Functions\OpeningFunctionBraceKernighanRitchieSniff;
use PHP_CodeSniffer\Standards\Generic\Sniffs\Functions\OpeningFunctionBraceBsdAllmanSniff;
@@ -105,7 +106,8 @@ public function process(File $phpcsFile, $stackPtr)
// Unfinished closures are tokenized as T_FUNCTION however, and can be excluded
// by checking for the scope_opener.
if ($tokens[$stackPtr]['code'] === T_FUNCTION
- && (isset($tokens[$stackPtr]['scope_opener']) === true || $phpcsFile->getMethodProperties($stackPtr)['has_body'] === false)
+ && (isset($tokens[$stackPtr]['scope_opener']) === true
+ || FunctionDeclarations::getProperties($phpcsFile, $stackPtr)['has_body'] === false)
) {
if ($tokens[($openBracket - 1)]['content'] === $phpcsFile->eolChar) {
$spaces = 'newline';
@@ -125,7 +127,7 @@ public function process(File $phpcsFile, $stackPtr)
}
// Must be no space before semicolon in abstract/interface methods.
- if ($phpcsFile->getMethodProperties($stackPtr)['has_body'] === false) {
+ if (FunctionDeclarations::getProperties($phpcsFile, $stackPtr)['has_body'] === false) {
$end = $phpcsFile->findNext(T_SEMICOLON, $closeBracket);
if ($tokens[($end - 1)]['content'] === $phpcsFile->eolChar) {
$spaces = 'newline';
diff --git a/src/Standards/PEAR/Sniffs/Functions/ValidDefaultValueSniff.php b/src/Standards/PEAR/Sniffs/Functions/ValidDefaultValueSniff.php
index 20bd848878..c09dd9cf0e 100644
--- a/src/Standards/PEAR/Sniffs/Functions/ValidDefaultValueSniff.php
+++ b/src/Standards/PEAR/Sniffs/Functions/ValidDefaultValueSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
class ValidDefaultValueSniff implements Sniff
{
@@ -46,7 +47,7 @@ public function process(File $phpcsFile, $stackPtr)
// If there is a value without a default after this, it is an error.
$defaultFound = false;
- $params = $phpcsFile->getMethodParameters($stackPtr);
+ $params = FunctionDeclarations::getParameters($phpcsFile, $stackPtr);
foreach ($params as $param) {
if ($param['variable_length'] === true) {
continue;
diff --git a/src/Standards/PEAR/Sniffs/NamingConventions/ValidFunctionNameSniff.php b/src/Standards/PEAR/Sniffs/NamingConventions/ValidFunctionNameSniff.php
index d2629f57e2..0169a43729 100644
--- a/src/Standards/PEAR/Sniffs/NamingConventions/ValidFunctionNameSniff.php
+++ b/src/Standards/PEAR/Sniffs/NamingConventions/ValidFunctionNameSniff.php
@@ -10,9 +10,11 @@
namespace PHP_CodeSniffer\Standards\PEAR\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
-use PHP_CodeSniffer\Util\Common;
-use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
+use PHP_CodeSniffer\Util\Tokens;
class ValidFunctionNameSniff extends AbstractScopeSniff
{
@@ -20,32 +22,24 @@ class ValidFunctionNameSniff extends AbstractScopeSniff
/**
* A list of all PHP magic methods.
*
+ * Set from within the constructor.
+ *
* @var array
+ *
+ * @deprecated 3.5.0 Use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::$magicMethods instead.
*/
- protected $magicMethods = [
- 'construct' => true,
- 'destruct' => true,
- 'call' => true,
- 'callstatic' => true,
- 'get' => true,
- 'set' => true,
- 'isset' => true,
- 'unset' => true,
- 'sleep' => true,
- 'wakeup' => true,
- 'tostring' => true,
- 'set_state' => true,
- 'clone' => true,
- 'invoke' => true,
- 'debuginfo' => true,
- ];
+ protected $magicMethods = [];
/**
* A list of all PHP magic functions.
*
+ * Set from within the constructor.
+ *
* @var array
+ *
+ * @deprecated 3.5.0 Use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::$magicFunctions instead.
*/
- protected $magicFunctions = ['autoload' => true];
+ protected $magicFunctions = [];
/**
@@ -53,6 +47,16 @@ class ValidFunctionNameSniff extends AbstractScopeSniff
*/
public function __construct()
{
+ // Preserve BC without code duplication.
+ $this->magicMethods = array_combine(
+ FunctionDeclarations::$magicMethods,
+ array_fill(0, count(FunctionDeclarations::$magicMethods), true)
+ );
+ $this->magicFunctions = array_combine(
+ FunctionDeclarations::$magicFunctions,
+ array_fill(0, count(FunctionDeclarations::$magicFunctions), true)
+ );
+
parent::__construct(Tokens::$ooScopeTokens, [T_FUNCTION], true);
}//end __construct()
@@ -73,33 +77,27 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$tokens = $phpcsFile->getTokens();
// Determine if this is a function which needs to be examined.
- $conditions = $tokens[$stackPtr]['conditions'];
- end($conditions);
- $deepestScope = key($conditions);
+ $deepestScope = Conditions::getLastCondition($phpcsFile, $stackPtr);
if ($deepestScope !== $currScope) {
return;
}
- $methodName = $phpcsFile->getDeclarationName($stackPtr);
- if ($methodName === null) {
- // Ignore closures.
+ $methodName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($methodName) === true) {
+ // Live coding or parse error.
return;
}
- $className = $phpcsFile->getDeclarationName($currScope);
+ $className = ConstructNames::getDeclarationName($phpcsFile, $currScope);
if (isset($className) === false) {
$className = '[Anonymous Class]';
}
$errorData = [$className.'::'.$methodName];
- $methodNameLc = strtolower($methodName);
- $classNameLc = strtolower($className);
-
- // Is this a magic method. i.e., is prefixed with "__" ?
+ // Check is this method is prefixed with "__" and not magic.
if (preg_match('|^__[^_]|', $methodName) !== 0) {
- $magicPart = substr($methodNameLc, 2);
- if (isset($this->magicMethods[$magicPart]) === true) {
+ if (FunctionDeclarations::isSpecialMethodName($methodName) === true) {
return;
}
@@ -107,6 +105,9 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$phpcsFile->addError($error, $stackPtr, 'MethodDoubleUnderscore', $errorData);
}
+ $methodNameLc = strtolower($methodName);
+ $classNameLc = strtolower($className);
+
// PHP4 constructors are allowed to break our rules.
if ($methodNameLc === $classNameLc) {
return;
@@ -117,7 +118,7 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
return;
}
- $methodProps = $phpcsFile->getMethodProperties($stackPtr);
+ $methodProps = FunctionDeclarations::getProperties($phpcsFile, $stackPtr);
$scope = $methodProps['scope'];
$scopeSpecified = $methodProps['scope_specified'];
@@ -150,7 +151,7 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$testMethodName = ltrim($methodName, '_');
- if (Common::isCamelCaps($testMethodName, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($testMethodName, false, true, false) === false) {
if ($scopeSpecified === true) {
$error = '%s method name "%s" is not in camel caps format';
$data = [
@@ -178,9 +179,9 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
*/
protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
{
- $functionName = $phpcsFile->getDeclarationName($stackPtr);
- if ($functionName === null) {
- // Ignore closures.
+ $functionName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($functionName) === true) {
+ // Live coding or parse error.
return;
}
@@ -191,10 +192,9 @@ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
$errorData = [$functionName];
- // Is this a magic function. i.e., it is prefixed with "__".
+ // Check is this function is prefixed with "__" and not magic.
if (preg_match('|^__[^_]|', $functionName) !== 0) {
- $magicPart = strtolower(substr($functionName, 2));
- if (isset($this->magicFunctions[$magicPart]) === true) {
+ if (FunctionDeclarations::isMagicFunctionName($functionName) === true) {
return;
}
@@ -241,7 +241,7 @@ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
$newCamelCapsPart = $camelCapsPart;
// Every function must have a camel caps part, so check that first.
- if (Common::isCamelCaps($camelCapsPart, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($camelCapsPart, false, true, false) === false) {
$validName = false;
$newCamelCapsPart = strtolower($camelCapsPart{0}).substr($camelCapsPart, 1);
}
diff --git a/src/Standards/PEAR/Sniffs/NamingConventions/ValidVariableNameSniff.php b/src/Standards/PEAR/Sniffs/NamingConventions/ValidVariableNameSniff.php
index 26d25ae9ce..d7eaf91e31 100644
--- a/src/Standards/PEAR/Sniffs/NamingConventions/ValidVariableNameSniff.php
+++ b/src/Standards/PEAR/Sniffs/NamingConventions/ValidVariableNameSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Variables;
class ValidVariableNameSniff extends AbstractVariableSniff
{
@@ -27,13 +28,12 @@ class ValidVariableNameSniff extends AbstractVariableSniff
*/
protected function processMemberVar(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
-
- $memberProps = $phpcsFile->getMemberProperties($stackPtr);
+ $memberProps = Variables::getMemberProperties($phpcsFile, $stackPtr);
if (empty($memberProps) === true) {
return;
}
+ $tokens = $phpcsFile->getTokens();
$memberName = ltrim($tokens[$stackPtr]['content'], '$');
$scope = $memberProps['scope'];
$scopeSpecified = $memberProps['scope_specified'];
diff --git a/src/Standards/PEAR/Sniffs/WhiteSpace/ScopeIndentSniff.php b/src/Standards/PEAR/Sniffs/WhiteSpace/ScopeIndentSniff.php
index 2620d20f2e..e846f3b6ab 100644
--- a/src/Standards/PEAR/Sniffs/WhiteSpace/ScopeIndentSniff.php
+++ b/src/Standards/PEAR/Sniffs/WhiteSpace/ScopeIndentSniff.php
@@ -17,7 +17,7 @@ class ScopeIndentSniff extends GenericScopeIndentSniff
/**
* Any scope openers that should not cause an indent.
*
- * @var int[]
+ * @var (integer|string)[]
*/
protected $nonIndentingScopes = [T_SWITCH];
diff --git a/src/Standards/PEAR/Tests/Commenting/ClassCommentUnitTest.inc b/src/Standards/PEAR/Tests/Commenting/ClassCommentUnitTest.inc
index 0ca87512f8..5fa8017097 100644
--- a/src/Standards/PEAR/Tests/Commenting/ClassCommentUnitTest.inc
+++ b/src/Standards/PEAR/Tests/Commenting/ClassCommentUnitTest.inc
@@ -117,4 +117,6 @@ interface Empty_Interface_Doc
trait Empty_Trait_Doc
{
-}//end trait
+}/** Unrelated end comment for previous structure using wrong comment style. */
+
+class NoCommentConfusedWithTrailingEndComment {}
diff --git a/src/Standards/PEAR/Tests/Commenting/ClassCommentUnitTest.php b/src/Standards/PEAR/Tests/Commenting/ClassCommentUnitTest.php
index 9a4bcf7dd6..664ba65755 100644
--- a/src/Standards/PEAR/Tests/Commenting/ClassCommentUnitTest.php
+++ b/src/Standards/PEAR/Tests/Commenting/ClassCommentUnitTest.php
@@ -44,6 +44,7 @@ public function getErrorList()
96 => 5,
106 => 5,
116 => 5,
+ 122 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.inc b/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.inc
index 977004ebed..ebddf44fe5 100644
--- a/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.inc
+++ b/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.inc
@@ -370,3 +370,10 @@ function process(File $phpcsFile, $stackPtr)
{
}//end process()
+
+class FooBar {
+ /**
+ * Docblock
+ */
+ public /* comment */ static function PickUpDocblockNotComment() {}
+}
diff --git a/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.inc.fixed b/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.inc.fixed
index ded6799a7a..2b6c156e8e 100644
--- a/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.inc.fixed
+++ b/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.inc.fixed
@@ -370,3 +370,10 @@ function process(File $phpcsFile, $stackPtr)
{
}//end process()
+
+class FooBar {
+ /**
+ * Docblock
+ */
+ public /* comment */ static function PickUpDocblockNotComment() {}
+}
diff --git a/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.php b/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.php
index cf755c8196..e8f0939584 100644
--- a/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.php
+++ b/src/Standards/PEAR/Tests/Commenting/FunctionCommentUnitTest.php
@@ -69,6 +69,7 @@ public function getErrorList()
361 => 1,
363 => 1,
364 => 1,
+ 377 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/PEAR/Tests/NamingConventions/ValidFunctionNameUnitTest.inc b/src/Standards/PEAR/Tests/NamingConventions/ValidFunctionNameUnitTest.inc
index 78280cdd90..27922723a1 100644
--- a/src/Standards/PEAR/Tests/NamingConventions/ValidFunctionNameUnitTest.inc
+++ b/src/Standards/PEAR/Tests/NamingConventions/ValidFunctionNameUnitTest.inc
@@ -106,41 +106,19 @@ function _My_Package() {}
/* Test for magic functions */
-class Magic_Test {
- function __construct() {}
- function __destruct() {}
- function __call($name, $args) {}
+class Magic_Test extends \SoapClient {
static function __callStatic($name, $args) {}
- function __get($name) {}
- function __set($name, $value) {}
- function __isset($name) {}
- function __unset($name) {}
- function __sleep() {}
function __wakeup() {}
- function __toString() {}
- function __set_state() {}
- function __clone() {}
+ function __getTypes() {}
function __autoload() {}
- function __invoke() {}
function __myFunction() {}
function __my_function() {}
}
-function __construct() {}
-function __destruct() {}
function __call() {}
-function __callStatic() {}
-function __get() {}
function __set() {}
-function __isset() {}
-function __unset() {}
-function __sleep() {}
-function __wakeup() {}
-function __toString() {}
function __set_state() {}
-function __clone() {}
function __autoload($class) {}
-function __invoke() {}
function __myFunction() {}
function __my_function() {}
@@ -165,45 +143,8 @@ class MyClass
public function __construct() {}
}
-trait Foo
-{
- function __call($name, $args) {}
-}
-
-class Magic_Case_Test {
- function __Construct() {}
- function __isSet($name) {}
- function __tostring() {}
-}
-function __autoLoad($class) {}
function _() {}
-function __debugInfo() {}
-class Foo {
- function __debugInfo() {}
-}
-
-/* Magic methods in anonymous classes. */
-$a = new class {
- function __construct() {}
- function __destruct() {}
- function __call($name, $args) {}
- static function __callStatic($name, $args) {}
- function __get($name) {}
- function __set($name, $value) {}
- function __isset($name) {}
- function __unset($name) {}
- function __sleep() {}
- function __wakeup() {}
- function __toString() {}
- function __set_state() {}
- function __clone() {}
- function __autoload() {}
- function __invoke() {}
- function __myFunction() {}
- function __my_function() {}
-};
-
function send_system_email__to_user($body, $recipient){}
class Nested {
diff --git a/src/Standards/PEAR/Tests/NamingConventions/ValidFunctionNameUnitTest.php b/src/Standards/PEAR/Tests/NamingConventions/ValidFunctionNameUnitTest.php
index 9bb6de0d84..28ff6011a8 100644
--- a/src/Standards/PEAR/Tests/NamingConventions/ValidFunctionNameUnitTest.php
+++ b/src/Standards/PEAR/Tests/NamingConventions/ValidFunctionNameUnitTest.php
@@ -92,36 +92,21 @@ public function getErrorList()
102 => 2,
103 => 2,
104 => 2,
- 123 => 1,
- 125 => 1,
- 126 => 2,
- 129 => 1,
- 130 => 1,
- 131 => 1,
- 132 => 1,
- 133 => 1,
- 134 => 1,
- 135 => 1,
- 136 => 1,
- 137 => 1,
- 138 => 1,
- 139 => 1,
- 140 => 3,
- 141 => 1,
- 143 => 1,
- 144 => 1,
- 145 => 3,
- 147 => 2,
- 148 => 1,
- 149 => 1,
- 181 => 1,
- 201 => 1,
- 203 => 1,
- 204 => 2,
- 207 => 2,
- 212 => 1,
- 213 => 1,
- 214 => 1,
+ 113 => 1,
+ 114 => 1,
+ 115 => 2,
+ 118 => 1,
+ 119 => 1,
+ 120 => 3,
+ 122 => 1,
+ 123 => 3,
+ 125 => 2,
+ 126 => 1,
+ 127 => 1,
+ 148 => 2,
+ 153 => 1,
+ 154 => 1,
+ 155 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/PSR1/Sniffs/Classes/ClassDeclarationSniff.php b/src/Standards/PSR1/Sniffs/Classes/ClassDeclarationSniff.php
index 858a626853..8fb0cea482 100644
--- a/src/Standards/PSR1/Sniffs/Classes/ClassDeclarationSniff.php
+++ b/src/Standards/PSR1/Sniffs/Classes/ClassDeclarationSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Namespaces;
class ClassDeclarationSniff implements Sniff
{
@@ -59,8 +60,8 @@ public function process(File $phpcsFile, $stackPtr)
$phpcsFile->recordMetric($stackPtr, 'One class per file', 'yes');
}
- $namespace = $phpcsFile->findNext([T_NAMESPACE, T_CLASS, T_INTERFACE, T_TRAIT], 0);
- if ($tokens[$namespace]['code'] !== T_NAMESPACE) {
+ $namespace = Namespaces::determineNamespace($phpcsFile, $stackPtr);
+ if ($namespace === '') {
$error = 'Each %s must be in a namespace of at least one level (a top-level vendor name)';
$phpcsFile->addError($error, $stackPtr, 'MissingNamespace', $errorData);
$phpcsFile->recordMetric($stackPtr, 'Class defined in namespace', 'no');
diff --git a/src/Standards/PSR1/Sniffs/Files/SideEffectsSniff.php b/src/Standards/PSR1/Sniffs/Files/SideEffectsSniff.php
index 66dcbb4b24..3e7392c2f4 100644
--- a/src/Standards/PSR1/Sniffs/Files/SideEffectsSniff.php
+++ b/src/Standards/PSR1/Sniffs/Files/SideEffectsSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Namespaces;
use PHP_CodeSniffer\Util\Tokens;
class SideEffectsSniff implements Sniff
@@ -143,9 +144,8 @@ private function searchForConflict($phpcsFile, $start, $end, $tokens)
continue;
}
- // Ignore entire namespace, declare, const and use statements.
- if ($tokens[$i]['code'] === T_NAMESPACE
- || $tokens[$i]['code'] === T_USE
+ // Ignore entire declare, const and use statements.
+ if ($tokens[$i]['code'] === T_USE
|| $tokens[$i]['code'] === T_DECLARE
|| $tokens[$i]['code'] === T_CONST
) {
@@ -161,11 +161,41 @@ private function searchForConflict($phpcsFile, $start, $end, $tokens)
continue;
}
+ // Ignore namespace declarations.
+ if ($tokens[$i]['code'] === T_NAMESPACE
+ && Namespaces::isDeclaration($phpcsFile, $i) === true
+ ) {
+ if (isset($tokens[$i]['scope_opener']) === true) {
+ $i = $tokens[$i]['scope_opener'];
+ } else {
+ $declarationCloser = $phpcsFile->findNext(Namespaces::$statementClosers, ($i + 1));
+ if ($declarationCloser !== false) {
+ $i = $declarationCloser;
+ }
+ }
+
+ continue;
+ }
+
+ // Ignore the close brace of a scoped namespace declaration.
+ if ($tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
+ && isset($tokens[$i]['scope_condition']) === true
+ && $tokens[$tokens[$i]['scope_condition']]['code'] === T_NAMESPACE
+ ) {
+ continue;
+ }
+
// Ignore function/class prefixes.
if (isset(Tokens::$methodPrefixes[$tokens[$i]['code']]) === true) {
continue;
}
+ // Ignore closures.
+ if ($tokens[$i]['code'] === T_CLOSURE) {
+ $i = $tokens[$i]['scope_closer'];
+ continue;
+ }
+
// Ignore anon classes.
if ($tokens[$i]['code'] === T_ANON_CLASS) {
$i = $tokens[$i]['scope_closer'];
diff --git a/src/Standards/PSR1/Sniffs/Methods/CamelCapsMethodNameSniff.php b/src/Standards/PSR1/Sniffs/Methods/CamelCapsMethodNameSniff.php
index 1670fd5762..0e4a3eeda1 100644
--- a/src/Standards/PSR1/Sniffs/Methods/CamelCapsMethodNameSniff.php
+++ b/src/Standards/PSR1/Sniffs/Methods/CamelCapsMethodNameSniff.php
@@ -10,8 +10,10 @@
namespace PHP_CodeSniffer\Standards\PSR1\Sniffs\Methods;
use PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\CamelCapsFunctionNameSniff as GenericCamelCapsFunctionNameSniff;
-use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
class CamelCapsMethodNameSniff extends GenericCamelCapsFunctionNameSniff
{
@@ -32,33 +34,26 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$tokens = $phpcsFile->getTokens();
// Determine if this is a function which needs to be examined.
- $conditions = $tokens[$stackPtr]['conditions'];
- end($conditions);
- $deepestScope = key($conditions);
+ $deepestScope = Conditions::getLastCondition($phpcsFile, $stackPtr);
if ($deepestScope !== $currScope) {
return;
}
- $methodName = $phpcsFile->getDeclarationName($stackPtr);
- if ($methodName === null) {
- // Ignore closures.
+ $methodName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($methodName) === true) {
+ // Live coding or parse error.
return;
}
// Ignore magic methods.
- if (preg_match('|^__[^_]|', $methodName) !== 0) {
- $magicPart = strtolower(substr($methodName, 2));
- if (isset($this->magicMethods[$magicPart]) === true
- || isset($this->methodsDoubleUnderscore[$magicPart]) === true
- ) {
- return;
- }
+ if (FunctionDeclarations::isSpecialMethodName($methodName) === true) {
+ return;
}
$testName = ltrim($methodName, '_');
- if ($testName !== '' && Common::isCamelCaps($testName, false, true, false) === false) {
+ if ($testName !== '' && ConstructNames::isCamelCaps($testName, false, true, false) === false) {
$error = 'Method name "%s" is not in camel caps format';
- $className = $phpcsFile->getDeclarationName($currScope);
+ $className = ConstructNames::getDeclarationName($phpcsFile, $currScope);
if (isset($className) === false) {
$className = '[Anonymous Class]';
}
diff --git a/src/Standards/PSR1/Tests/Classes/ClassDeclarationUnitTest.3.inc b/src/Standards/PSR1/Tests/Classes/ClassDeclarationUnitTest.3.inc
new file mode 100644
index 0000000000..b83445a338
--- /dev/null
+++ b/src/Standards/PSR1/Tests/Classes/ClassDeclarationUnitTest.3.inc
@@ -0,0 +1,4 @@
+ 1,
+ 3 => 2,
+ ];
+
+ case 'ClassDeclarationUnitTest.2.inc':
return [];
- }
- return [
- 2 => 1,
- 3 => 2,
- ];
+ case 'ClassDeclarationUnitTest.3.inc':
+ return [4 => 1];
+
+ case 'ClassDeclarationUnitTest.4.inc':
+ return [3 => 1];
+
+ case 'ClassDeclarationUnitTest.5.inc':
+ return [4 => 1];
+
+ case 'ClassDeclarationUnitTest.6.inc':
+ return [8 => 2];
+ }//end switch
}//end getErrorList()
diff --git a/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.13.inc b/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.13.inc
new file mode 100644
index 0000000000..4bfe476d9a
--- /dev/null
+++ b/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.13.inc
@@ -0,0 +1,5 @@
+ 1];
default:
return [];
diff --git a/src/Standards/PSR2/Sniffs/Classes/PropertyDeclarationSniff.php b/src/Standards/PSR2/Sniffs/Classes/PropertyDeclarationSniff.php
index 0b9f43058f..e0ed1f4d75 100644
--- a/src/Standards/PSR2/Sniffs/Classes/PropertyDeclarationSniff.php
+++ b/src/Standards/PSR2/Sniffs/Classes/PropertyDeclarationSniff.php
@@ -10,8 +10,9 @@
namespace PHP_CodeSniffer\Standards\PSR2\Sniffs\Classes;
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
-use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Variables;
+use PHP_CodeSniffer\Util\Tokens;
class PropertyDeclarationSniff extends AbstractVariableSniff
{
@@ -60,13 +61,8 @@ protected function processMemberVar(File $phpcsFile, $stackPtr)
$phpcsFile->addError($error, $stackPtr, 'Multiple');
}
- try {
- $propertyInfo = $phpcsFile->getMemberProperties($stackPtr);
- if (empty($propertyInfo) === true) {
- return;
- }
- } catch (\Exception $e) {
- // Turns out not to be a property after all.
+ $propertyInfo = Variables::getMemberProperties($phpcsFile, $stackPtr);
+ if (empty($propertyInfo) === true) {
return;
}
diff --git a/src/Standards/PSR2/Sniffs/Methods/MethodDeclarationSniff.php b/src/Standards/PSR2/Sniffs/Methods/MethodDeclarationSniff.php
index abd1cd3550..c91432c981 100644
--- a/src/Standards/PSR2/Sniffs/Methods/MethodDeclarationSniff.php
+++ b/src/Standards/PSR2/Sniffs/Methods/MethodDeclarationSniff.php
@@ -10,8 +10,10 @@
namespace PHP_CodeSniffer\Standards\PSR2\Sniffs\Methods;
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
-use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Tokens;
class MethodDeclarationSniff extends AbstractScopeSniff
{
@@ -41,16 +43,14 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$tokens = $phpcsFile->getTokens();
// Determine if this is a function which needs to be examined.
- $conditions = $tokens[$stackPtr]['conditions'];
- end($conditions);
- $deepestScope = key($conditions);
+ $deepestScope = Conditions::getLastCondition($phpcsFile, $stackPtr);
if ($deepestScope !== $currScope) {
return;
}
- $methodName = $phpcsFile->getDeclarationName($stackPtr);
- if ($methodName === null) {
- // Ignore closures.
+ $methodName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($methodName) === true) {
+ // Live coding or parse error.
return;
}
diff --git a/src/Standards/PSR2/Sniffs/Namespaces/NamespaceDeclarationSniff.php b/src/Standards/PSR2/Sniffs/Namespaces/NamespaceDeclarationSniff.php
index 42c55d1d4a..3fb494df27 100644
--- a/src/Standards/PSR2/Sniffs/Namespaces/NamespaceDeclarationSniff.php
+++ b/src/Standards/PSR2/Sniffs/Namespaces/NamespaceDeclarationSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Namespaces;
class NamespaceDeclarationSniff implements Sniff
{
@@ -39,9 +40,18 @@ public function register()
*/
public function process(File $phpcsFile, $stackPtr)
{
+ if (Namespaces::isDeclaration($phpcsFile, $stackPtr) === false) {
+ // Namespace operator or live coding/parse error.
+ return;
+ }
+
$tokens = $phpcsFile->getTokens();
+ $end = $phpcsFile->findNext(Namespaces::$statementClosers, ($stackPtr + 1));
+ if ($tokens[$end]['code'] === T_CLOSE_TAG) {
+ // PHP close tags have their own rules.
+ return;
+ }
- $end = $phpcsFile->findEndOfStatement($stackPtr);
for ($i = ($end + 1); $i < ($phpcsFile->numTokens - 1); $i++) {
if ($tokens[$i]['line'] === $tokens[$end]['line']) {
continue;
diff --git a/src/Standards/PSR2/Sniffs/Namespaces/UseDeclarationSniff.php b/src/Standards/PSR2/Sniffs/Namespaces/UseDeclarationSniff.php
index b0f0b747c5..acd7c764e9 100644
--- a/src/Standards/PSR2/Sniffs/Namespaces/UseDeclarationSniff.php
+++ b/src/Standards/PSR2/Sniffs/Namespaces/UseDeclarationSniff.php
@@ -11,6 +11,8 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Namespaces;
+use PHP_CodeSniffer\Util\Sniffs\UseStatements;
use PHP_CodeSniffer\Util\Tokens;
class UseDeclarationSniff implements Sniff
@@ -40,7 +42,7 @@ public function register()
*/
public function process(File $phpcsFile, $stackPtr)
{
- if ($this->shouldIgnoreUse($phpcsFile, $stackPtr) === true) {
+ if (UseStatements::isImportUse($phpcsFile, $stackPtr) === false) {
return;
}
@@ -166,28 +168,33 @@ public function process(File $phpcsFile, $stackPtr)
}//end if
// Make sure this USE comes after the first namespace declaration.
- $prev = $phpcsFile->findPrevious(T_NAMESPACE, ($stackPtr - 1));
- if ($prev !== false) {
+ $namespacePtr = Namespaces::findNamespacePtr($phpcsFile, $stackPtr);
+ if ($namespacePtr !== false) {
$first = $phpcsFile->findNext(T_NAMESPACE, 1);
- if ($prev !== $first) {
+ while ($first !== false && Namespaces::isDeclaration($phpcsFile, $first) === false) {
+ $first = $phpcsFile->findNext(T_NAMESPACE, ($first + 1));
+ }
+
+ if ($namespacePtr !== $first) {
$error = 'USE declarations must go after the first namespace declaration';
$phpcsFile->addError($error, $stackPtr, 'UseAfterNamespace');
}
} else {
$next = $phpcsFile->findNext(T_NAMESPACE, ($stackPtr + 1));
+ while ($next !== false && Namespaces::isDeclaration($phpcsFile, $next) === false) {
+ $next = $phpcsFile->findNext(T_NAMESPACE, ($next + 1));
+ }
+
if ($next !== false) {
$error = 'USE declarations must go after the namespace declaration';
$phpcsFile->addError($error, $stackPtr, 'UseBeforeNamespace');
}
- }
+ }//end if
// Only interested in the last USE statement from here onwards.
$nextUse = $phpcsFile->findNext(T_USE, ($stackPtr + 1));
- while ($this->shouldIgnoreUse($phpcsFile, $nextUse) === true) {
+ while ($nextUse !== false && UseStatements::isImportUse($phpcsFile, $nextUse) === false) {
$nextUse = $phpcsFile->findNext(T_USE, ($nextUse + 1));
- if ($nextUse === false) {
- break;
- }
}
if ($nextUse !== false) {
@@ -267,33 +274,4 @@ public function process(File $phpcsFile, $stackPtr)
}//end process()
- /**
- * Check if this use statement is part of the namespace block.
- *
- * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
- * @param int $stackPtr The position of the current token in
- * the stack passed in $tokens.
- *
- * @return bool
- */
- private function shouldIgnoreUse($phpcsFile, $stackPtr)
- {
- $tokens = $phpcsFile->getTokens();
-
- // Ignore USE keywords inside closures and during live coding.
- $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
- if ($next === false || $tokens[$next]['code'] === T_OPEN_PARENTHESIS) {
- return true;
- }
-
- // Ignore USE keywords for traits.
- if ($phpcsFile->hasCondition($stackPtr, [T_CLASS, T_TRAIT]) === true) {
- return true;
- }
-
- return false;
-
- }//end shouldIgnoreUse()
-
-
}//end class
diff --git a/src/Standards/PSR2/Tests/Namespaces/NamespaceDeclarationUnitTest.inc b/src/Standards/PSR2/Tests/Namespaces/NamespaceDeclarationUnitTest.inc
index 2500b6448e..5c0f707053 100644
--- a/src/Standards/PSR2/Tests/Namespaces/NamespaceDeclarationUnitTest.inc
+++ b/src/Standards/PSR2/Tests/Namespaces/NamespaceDeclarationUnitTest.inc
@@ -20,3 +20,14 @@ namespace Vendor\
Package;
namespace Vendor\Package;
+
+$a = namespace\functionCall();
+$b = 'test';
+
+namespace; /*comment*/
+namespace Vendor\Package {
+ class TestIt {}
+}
+
+namespace ?>
+
+ 1,
17 => 1,
19 => 1,
+ 27 => 1,
+ 28 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/PSR2/Tests/Namespaces/UseDeclarationUnitTest.1.inc b/src/Standards/PSR2/Tests/Namespaces/UseDeclarationUnitTest.1.inc
index c4e83da440..4890794a8f 100644
--- a/src/Standards/PSR2/Tests/Namespaces/UseDeclarationUnitTest.1.inc
+++ b/src/Standards/PSR2/Tests/Namespaces/UseDeclarationUnitTest.1.inc
@@ -34,5 +34,9 @@ $x = $foo ? function ($foo) use /* comment */ ($bar): int {
return 1;
} : $bar;
+$anonClass = new class() {
+ use TContainer;
+};
+
// Testcase must be on last line in the file.
use
\ No newline at end of file
diff --git a/src/Standards/PSR2/Tests/Namespaces/UseDeclarationUnitTest.18.inc b/src/Standards/PSR2/Tests/Namespaces/UseDeclarationUnitTest.18.inc
new file mode 100644
index 0000000000..aa6468aded
--- /dev/null
+++ b/src/Standards/PSR2/Tests/Namespaces/UseDeclarationUnitTest.18.inc
@@ -0,0 +1,7 @@
+ 1,
12 => 2,
];
+
case 'UseDeclarationUnitTest.3.inc':
return [
4 => 1,
6 => 1,
];
+
case 'UseDeclarationUnitTest.5.inc':
return [
5 => 1,
@@ -55,6 +57,7 @@ public function getErrorList($testFile='')
30 => 1,
35 => 1,
];
+
case 'UseDeclarationUnitTest.10.inc':
case 'UseDeclarationUnitTest.11.inc':
case 'UseDeclarationUnitTest.12.inc':
@@ -63,12 +66,17 @@ public function getErrorList($testFile='')
case 'UseDeclarationUnitTest.16.inc':
case 'UseDeclarationUnitTest.17.inc':
return [2 => 1];
+
case 'UseDeclarationUnitTest.15.inc':
return [
3 => 1,
4 => 1,
5 => 1,
];
+
+ case 'UseDeclarationUnitTest.19.inc':
+ return [4 => 1];
+
default:
return [];
}//end switch
diff --git a/src/Standards/Squiz/Sniffs/Arrays/ArrayDeclarationSniff.php b/src/Standards/Squiz/Sniffs/Arrays/ArrayDeclarationSniff.php
index 56521702c1..767d2a4217 100644
--- a/src/Standards/Squiz/Sniffs/Arrays/ArrayDeclarationSniff.php
+++ b/src/Standards/Squiz/Sniffs/Arrays/ArrayDeclarationSniff.php
@@ -11,6 +11,8 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
+use PHP_CodeSniffer\Util\Sniffs\TokenIs;
use PHP_CodeSniffer\Util\Tokens;
class ArrayDeclarationSniff implements Sniff
@@ -45,6 +47,13 @@ public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
+ if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY
+ && TokenIs::isShortList($phpcsFile, $stackPtr) === true
+ ) {
+ // No need to examine nested subs of this short list.
+ return $tokens[$stackPtr]['bracket_closer'];
+ }
+
if ($tokens[$stackPtr]['code'] === T_ARRAY) {
$phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'no');
@@ -229,12 +238,7 @@ public function processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arra
}//end while
if ($valueCount > 0) {
- $nestedParenthesis = false;
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- $nested = $tokens[$stackPtr]['nested_parenthesis'];
- $nestedParenthesis = array_pop($nested);
- }
-
+ $nestedParenthesis = Parentheses::getLastCloser($phpcsFile, $stackPtr);
if ($nestedParenthesis === false
|| $tokens[$nestedParenthesis]['line'] !== $tokens[$stackPtr]['line']
) {
diff --git a/src/Standards/Squiz/Sniffs/Classes/SelfMemberReferenceSniff.php b/src/Standards/Squiz/Sniffs/Classes/SelfMemberReferenceSniff.php
index 3faca91db6..706809a0bf 100644
--- a/src/Standards/Squiz/Sniffs/Classes/SelfMemberReferenceSniff.php
+++ b/src/Standards/Squiz/Sniffs/Classes/SelfMemberReferenceSniff.php
@@ -16,6 +16,9 @@
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\Namespaces;
use PHP_CodeSniffer\Util\Tokens;
class SelfMemberReferenceSniff extends AbstractScopeSniff
@@ -46,14 +49,7 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$tokens = $phpcsFile->getTokens();
// Determine if this is a double colon which needs to be examined.
- $conditions = $tokens[$stackPtr]['conditions'];
- $conditions = array_reverse($conditions, true);
- foreach ($conditions as $conditionToken => $tokenCode) {
- if ($tokenCode === T_CLASS || $tokenCode === T_ANON_CLASS || $tokenCode === T_CLOSURE) {
- break;
- }
- }
-
+ $conditionToken = Conditions::getLastCondition($phpcsFile, $stackPtr, [T_CLASS, T_ANON_CLASS, T_CLOSURE]);
if ($conditionToken !== $currScope) {
return;
}
@@ -81,18 +77,20 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($calledClassName - 1), null, true);
if ($prevNonEmpty !== false && $tokens[$prevNonEmpty]['code'] === T_NS_SEPARATOR) {
$declarationName = $this->getDeclarationNameWithNamespace($tokens, $calledClassName);
- $declarationName = ltrim($declarationName, '\\');
- $fullQualifiedClassName = $this->getNamespaceOfScope($phpcsFile, $currScope);
- if ($fullQualifiedClassName === '\\') {
- $fullQualifiedClassName = '';
- } else {
- $fullQualifiedClassName .= '\\';
- }
+ $fullQualifiedClassName = '';
- $fullQualifiedClassName .= $phpcsFile->getDeclarationName($currScope);
+ if (strpos($declarationName, '\\') === 0) {
+ // Only bother with building the FQCN when this is an absolute reference.
+ $fullQualifiedClassName = $this->getNamespaceOfScope($phpcsFile, $currScope);
+ if ($fullQualifiedClassName !== '\\') {
+ $fullQualifiedClassName .= '\\';
+ }
+
+ $fullQualifiedClassName .= ConstructNames::getDeclarationName($phpcsFile, $currScope);
+ }
} else {
- $declarationName = $phpcsFile->getDeclarationName($currScope);
- $fullQualifiedClassName = $tokens[$calledClassName]['content'];
+ $declarationName = $tokens[$calledClassName]['content'];
+ $fullQualifiedClassName = ConstructNames::getDeclarationName($phpcsFile, $currScope);
}
if ($declarationName === $fullQualifiedClassName) {
@@ -221,18 +219,7 @@ protected function getDeclarationNameWithNamespace(array $tokens, $stackPtr)
*/
protected function getNamespaceOfScope(File $phpcsFile, $stackPtr)
{
- $namespace = '\\';
- $namespaceDeclaration = $phpcsFile->findPrevious(T_NAMESPACE, $stackPtr);
-
- if ($namespaceDeclaration !== false) {
- $endOfNamespaceDeclaration = $phpcsFile->findNext([T_SEMICOLON, T_OPEN_CURLY_BRACKET], $namespaceDeclaration);
- $namespace = $this->getDeclarationNameWithNamespace(
- $phpcsFile->getTokens(),
- ($endOfNamespaceDeclaration - 1)
- );
- }
-
- return $namespace;
+ return '\\'.Namespaces::determineNamespace($phpcsFile, $stackPtr);
}//end getNamespaceOfScope()
diff --git a/src/Standards/Squiz/Sniffs/Classes/ValidClassNameSniff.php b/src/Standards/Squiz/Sniffs/Classes/ValidClassNameSniff.php
index 5e4515e876..fc8b849e72 100644
--- a/src/Standards/Squiz/Sniffs/Classes/ValidClassNameSniff.php
+++ b/src/Standards/Squiz/Sniffs/Classes/ValidClassNameSniff.php
@@ -11,7 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
-use PHP_CodeSniffer\Util\Common;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
class ValidClassNameSniff implements Sniff
{
@@ -53,20 +53,14 @@ public function process(File $phpcsFile, $stackPtr)
return;
}
- // Determine the name of the class or interface. Note that we cannot
- // simply look for the first T_STRING because a class name
- // starting with the number will be multiple tokens.
- $opener = $tokens[$stackPtr]['scope_opener'];
- $nameStart = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), $opener, true);
- $nameEnd = $phpcsFile->findNext(T_WHITESPACE, $nameStart, $opener);
- if ($nameEnd === false) {
- $name = $tokens[$nameStart]['content'];
- } else {
- $name = trim($phpcsFile->getTokensAsString($nameStart, ($nameEnd - $nameStart)));
+ $name = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($name) === true) {
+ // Live coding or parse error.
+ return;
}
// Check for PascalCase format.
- $valid = Common::isCamelCaps($name, true, true, false);
+ $valid = ConstructNames::isCamelCaps($name, true, true, false);
if ($valid === false) {
$type = ucfirst($tokens[$stackPtr]['content']);
$error = '%s name "%s" is not in PascalCase format';
diff --git a/src/Standards/Squiz/Sniffs/Commenting/BlockCommentSniff.php b/src/Standards/Squiz/Sniffs/Commenting/BlockCommentSniff.php
index dd735f6992..9464fc79df 100644
--- a/src/Standards/Squiz/Sniffs/Commenting/BlockCommentSniff.php
+++ b/src/Standards/Squiz/Sniffs/Commenting/BlockCommentSniff.php
@@ -11,6 +11,8 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+use PHP_CodeSniffer\Util\Sniffs\Orthography;
use PHP_CodeSniffer\Util\Tokens;
class BlockCommentSniff implements Sniff
@@ -111,34 +113,8 @@ public function process(File $phpcsFile, $stackPtr)
return;
}//end if
- $commentLines = [$stackPtr];
- $nextComment = $stackPtr;
- $lastLine = $tokens[$stackPtr]['line'];
- $commentString = $tokens[$stackPtr]['content'];
-
- // Construct the comment into an array.
- while (($nextComment = $phpcsFile->findNext(T_WHITESPACE, ($nextComment + 1), null, true)) !== false) {
- if ($tokens[$nextComment]['code'] !== $tokens[$stackPtr]['code']
- && isset(Tokens::$phpcsCommentTokens[$tokens[$nextComment]['code']]) === false
- ) {
- // Found the next bit of code.
- break;
- }
-
- if (($tokens[$nextComment]['line'] - 1) !== $lastLine) {
- // Not part of the block.
- break;
- }
-
- $lastLine = $tokens[$nextComment]['line'];
- $commentLines[] = $nextComment;
- $commentString .= $tokens[$nextComment]['content'];
- if ($tokens[$nextComment]['code'] === T_DOC_COMMENT_CLOSE_TAG
- || substr($tokens[$nextComment]['content'], -2) === '*/'
- ) {
- break;
- }
- }//end while
+ $lastCommentToken = Comments::findEndOfComment($phpcsFile, $stackPtr);
+ $commentString = $phpcsFile->getTokensAsString($stackPtr, ($lastCommentToken - $stackPtr + 1));
$commentText = str_replace($phpcsFile->eolChar, '', $commentString);
$commentText = trim($commentText, "/* \t");
@@ -147,9 +123,7 @@ public function process(File $phpcsFile, $stackPtr)
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'Empty');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
- $phpcsFile->fixer->replaceToken($stackPtr, '');
- $lastToken = array_pop($commentLines);
- for ($i = ($stackPtr + 1); $i <= $lastToken; $i++) {
+ for ($i = $stackPtr; $i <= $lastCommentToken; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
@@ -159,7 +133,7 @@ public function process(File $phpcsFile, $stackPtr)
return;
}
- if (count($commentLines) === 1) {
+ if ($stackPtr === $lastCommentToken) {
$error = 'Single line block comment not allowed; use inline ("// text") comment instead';
// Only fix comments when they are the last token on a line.
@@ -207,15 +181,16 @@ public function process(File $phpcsFile, $stackPtr)
$hasStars = false;
// Make sure first line isn't blank.
- if (trim($tokens[$commentLines[1]]['content']) === '') {
+ $firstLine = ($stackPtr + 1);
+ if (trim($tokens[$firstLine]['content']) === '') {
$error = 'Empty line not allowed at start of comment';
- $fix = $phpcsFile->addFixableError($error, $commentLines[1], 'HasEmptyLine');
+ $fix = $phpcsFile->addFixableError($error, $firstLine, 'HasEmptyLine');
if ($fix === true) {
- $phpcsFile->fixer->replaceToken($commentLines[1], '');
+ $phpcsFile->fixer->replaceToken($firstLine, '');
}
} else {
// Check indentation of first line.
- $content = $tokens[$commentLines[1]]['content'];
+ $content = $tokens[$firstLine]['content'];
$commentText = ltrim($content);
$leadingSpace = (strlen($content) - strlen($commentText));
@@ -237,10 +212,10 @@ public function process(File $phpcsFile, $stackPtr)
];
$error = 'First line of comment not aligned correctly; expected %s but found %s';
- $fix = $phpcsFile->addFixableError($error, $commentLines[1], 'FirstLineIndent', $data);
+ $fix = $phpcsFile->addFixableError($error, $firstLine, 'FirstLineIndent', $data);
if ($fix === true) {
- if (isset($tokens[$commentLines[1]]['orig_content']) === true
- && $tokens[$commentLines[1]]['orig_content'][0] === "\t"
+ if (isset($tokens[$firstLine]['orig_content']) === true
+ && $tokens[$firstLine]['orig_content'][0] === "\t"
) {
// Line is indented using tabs.
$padding = str_repeat("\t", floor($expected / $this->tabWidth));
@@ -249,28 +224,22 @@ public function process(File $phpcsFile, $stackPtr)
$padding = str_repeat(' ', $expected);
}
- $phpcsFile->fixer->replaceToken($commentLines[1], $padding.$commentText);
+ $phpcsFile->fixer->replaceToken($firstLine, $padding.$commentText);
}
}//end if
- if (preg_match('/^\p{Ll}/u', $commentText) === 1) {
+ // Make sure this check also works with leading asterixes and list comments.
+ $commentTextNoLeadingMarks = ltrim($commentText, '*- ');
+ if (Orthography::isFirstCharLowercase($commentTextNoLeadingMarks) === true) {
$error = 'Block comments must start with a capital letter';
- $phpcsFile->addError($error, $commentLines[1], 'NoCapital');
+ $phpcsFile->addError($error, $firstLine, 'NoCapital');
}
}//end if
// Check that each line of the comment is indented past the star.
- foreach ($commentLines as $line) {
- // First and last lines (comment opener and closer) are handled separately.
- if ($line === $commentLines[(count($commentLines) - 1)] || $line === $commentLines[0]) {
- continue;
- }
-
- // First comment line was handled above.
- if ($line === $commentLines[1]) {
- continue;
- }
-
+ // First and last lines (comment opener and closer) are handled separately.
+ // And the first actual comment line was handled above.
+ for ($line = ($stackPtr + 2); $line < $lastCommentToken; $line++) {
// If it's empty, continue.
if (trim($tokens[$line]['content']) === '') {
continue;
@@ -312,15 +281,14 @@ public function process(File $phpcsFile, $stackPtr)
$phpcsFile->fixer->replaceToken($line, $padding.$commentText);
}
}//end if
- }//end foreach
+ }//end for
// Finally, test the last line is correct.
- $lastIndex = (count($commentLines) - 1);
- $content = $tokens[$commentLines[$lastIndex]]['content'];
+ $content = $tokens[$lastCommentToken]['content'];
$commentText = ltrim($content);
if ($commentText !== '*/' && $commentText !== '**/') {
$error = 'Comment closer must be on a new line';
- $phpcsFile->addError($error, $commentLines[$lastIndex], 'CloserSameLine');
+ $phpcsFile->addError($error, $lastCommentToken, 'CloserSameLine');
} else {
$leadingSpace = (strlen($content) - strlen($commentText));
@@ -341,7 +309,7 @@ public function process(File $phpcsFile, $stackPtr)
];
$error = 'Last line of comment aligned incorrectly; expected %s but found %s';
- $fix = $phpcsFile->addFixableError($error, $commentLines[$lastIndex], 'LastLineIndent', $data);
+ $fix = $phpcsFile->addFixableError($error, $lastCommentToken, 'LastLineIndent', $data);
if ($fix === true) {
if (isset($tokens[$line]['orig_content']) === true
&& $tokens[$line]['orig_content'][0] === "\t"
@@ -353,7 +321,7 @@ public function process(File $phpcsFile, $stackPtr)
$padding = str_repeat(' ', $expected);
}
- $phpcsFile->fixer->replaceToken($commentLines[$lastIndex], $padding.$commentText);
+ $phpcsFile->fixer->replaceToken($lastCommentToken, $padding.$commentText);
}
}//end if
}//end if
@@ -374,11 +342,10 @@ public function process(File $phpcsFile, $stackPtr)
}
}
- $commentCloser = $commentLines[$lastIndex];
- $contentAfter = $phpcsFile->findNext(T_WHITESPACE, ($commentCloser + 1), null, true);
- if ($contentAfter !== false && ($tokens[$contentAfter]['line'] - $tokens[$commentCloser]['line']) < 2) {
+ $contentAfter = $phpcsFile->findNext(T_WHITESPACE, ($lastCommentToken + 1), null, true);
+ if ($contentAfter !== false && ($tokens[$contentAfter]['line'] - $tokens[$lastCommentToken]['line']) < 2) {
$error = 'Empty line required after block comment';
- $phpcsFile->addError($error, $commentCloser, 'NoEmptyLineAfter');
+ $phpcsFile->addError($error, $lastCommentToken, 'NoEmptyLineAfter');
}
}//end process()
diff --git a/src/Standards/Squiz/Sniffs/Commenting/ClassCommentSniff.php b/src/Standards/Squiz/Sniffs/Commenting/ClassCommentSniff.php
index 818c6d772d..d19bf9c6bc 100644
--- a/src/Standards/Squiz/Sniffs/Commenting/ClassCommentSniff.php
+++ b/src/Standards/Squiz/Sniffs/Commenting/ClassCommentSniff.php
@@ -19,7 +19,8 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
-use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
class ClassCommentSniff implements Sniff
{
@@ -48,15 +49,10 @@ public function register()
*/
public function process(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
- $find = Tokens::$methodPrefixes;
- $find[] = T_WHITESPACE;
-
- $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
- if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
- && $tokens[$commentEnd]['code'] !== T_COMMENT
- ) {
- $class = $phpcsFile->getDeclarationName($stackPtr);
+ $commentEnd = Comments::findOOStructureComment($phpcsFile, $stackPtr);
+
+ if ($commentEnd === false) {
+ $class = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
$phpcsFile->addError('Missing doc comment for class %s', $stackPtr, 'Missing', [$class]);
$phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'no');
return;
@@ -64,6 +60,7 @@ public function process(File $phpcsFile, $stackPtr)
$phpcsFile->recordMetric($stackPtr, 'Class has doc comment', 'yes');
+ $tokens = $phpcsFile->getTokens();
if ($tokens[$commentEnd]['code'] === T_COMMENT) {
$phpcsFile->addError('You must use "/**" style comments for a class comment', $stackPtr, 'WrongStyle');
return;
diff --git a/src/Standards/Squiz/Sniffs/Commenting/ClosingDeclarationCommentSniff.php b/src/Standards/Squiz/Sniffs/Commenting/ClosingDeclarationCommentSniff.php
index 63975b6077..f54b584485 100644
--- a/src/Standards/Squiz/Sniffs/Commenting/ClosingDeclarationCommentSniff.php
+++ b/src/Standards/Squiz/Sniffs/Commenting/ClosingDeclarationCommentSniff.php
@@ -11,6 +11,9 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
class ClosingDeclarationCommentSniff implements Sniff
{
@@ -46,7 +49,7 @@ public function process(File $phpcsFile, $stackPtr)
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['code'] === T_FUNCTION) {
- $methodProps = $phpcsFile->getMethodProperties($stackPtr);
+ $methodProps = FunctionDeclarations::getProperties($phpcsFile, $stackPtr);
// Abstract methods do not require a closing comment.
if ($methodProps['is_abstract'] === true) {
@@ -55,7 +58,7 @@ public function process(File $phpcsFile, $stackPtr)
// If this function is in an interface then we don't require
// a closing comment.
- if ($phpcsFile->hasCondition($stackPtr, T_INTERFACE) === true) {
+ if (Conditions::hasCondition($phpcsFile, $stackPtr, T_INTERFACE) === true) {
return;
}
@@ -65,7 +68,7 @@ public function process(File $phpcsFile, $stackPtr)
return;
}
- $decName = $phpcsFile->getDeclarationName($stackPtr);
+ $decName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
$comment = '//end '.$decName.'()';
} else if ($tokens[$stackPtr]['code'] === T_CLASS) {
$comment = '//end class';
diff --git a/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentSniff.php b/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentSniff.php
index c2f90f4a13..13d46d765c 100644
--- a/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentSniff.php
+++ b/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentSniff.php
@@ -12,11 +12,25 @@
use PHP_CodeSniffer\Standards\PEAR\Sniffs\Commenting\FunctionCommentSniff as PEARFunctionCommentSniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Config;
-use PHP_CodeSniffer\Util\Common;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
+use PHP_CodeSniffer\Util\Sniffs\Orthography;
class FunctionCommentSniff extends PEARFunctionCommentSniff
{
+ /**
+ * Whether short or long form parameter/return types are preferred.
+ *
+ * Valid values: 'short' or 'long'. Defaults to 'long'.
+ *
+ * This applies to just two types: `int` vs `integer` and `bool` vs `boolean`.
+ *
+ * @var string
+ */
+ public $typeFormat = 'long';
+
/**
* The current PHP version.
*
@@ -53,7 +67,7 @@ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart)
}
// Skip constructor and destructor.
- $methodName = $phpcsFile->getDeclarationName($stackPtr);
+ $methodName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
$isSpecialMethod = ($methodName === '__construct' || $methodName === '__destruct');
if ($isSpecialMethod === true) {
return;
@@ -66,7 +80,7 @@ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart)
$phpcsFile->addError($error, $return, 'MissingReturnType');
} else {
// Support both a return type and a description.
- preg_match('`^((?:\|?(?:array\([^\)]*\)|[\\\\a-z0-9\[\]]+))*)( .*)?`i', $content, $returnParts);
+ preg_match('`^((?:\|?(?:array(?:\([^\)]*\)|<[^>]*>+)|\(?[\\\\a-z0-9\[\]]+\)?))*)( .*)?`i', $content, $returnParts);
if (isset($returnParts[1]) === false) {
return;
}
@@ -74,16 +88,7 @@ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart)
$returnType = $returnParts[1];
// Check return type (can be multiple, separated by '|').
- $typeNames = explode('|', $returnType);
- $suggestedNames = [];
- foreach ($typeNames as $i => $typeName) {
- $suggestedName = Common::suggestType($typeName);
- if (in_array($suggestedName, $suggestedNames, true) === false) {
- $suggestedNames[] = $suggestedName;
- }
- }
-
- $suggestedType = implode('|', $suggestedNames);
+ $suggestedType = Comments::suggestTypeString($returnType, $this->typeFormat);
if ($returnType !== $suggestedType) {
$error = 'Expected "%s" but found "%s" for function return type';
$data = [
@@ -133,7 +138,7 @@ protected function processReturn(File $phpcsFile, $stackPtr, $commentStart)
}
}
}//end if
- } else if ($returnType !== 'mixed' && in_array('void', $typeNames, true) === false) {
+ } else if ($returnType !== 'mixed' && strpos($suggestedType, 'void') === false) {
// If return type is not void, there needs to be a return statement
// somewhere in the function that returns something.
if (isset($tokens[$stackPtr]['scope_closer']) === true) {
@@ -226,14 +231,12 @@ protected function processThrows(File $phpcsFile, $stackPtr, $commentStart)
}
// Starts with a capital letter and ends with a fullstop.
- $firstChar = $comment{0};
- if (strtoupper($firstChar) !== $firstChar) {
+ if (Orthography::isFirstCharCapitalized($comment) === false) {
$error = '@throws tag comment must start with a capital letter';
$phpcsFile->addError($error, ($tag + 2), 'ThrowsNotCapital');
}
- $lastChar = substr($comment, -1);
- if ($lastChar !== '.') {
+ if (Orthography::isLastCharPunctuation($comment, '.') === false) {
$error = '@throws tag comment must end with a full stop';
$phpcsFile->addError($error, ($tag + 2), 'ThrowsNoFullStop');
}
@@ -355,7 +358,7 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
];
}//end foreach
- $realParams = $phpcsFile->getMethodParameters($stackPtr);
+ $realParams = FunctionDeclarations::getParameters($phpcsFile, $stackPtr);
$foundParams = [];
// We want to use ... for all variable length arguments, so added
@@ -382,7 +385,7 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
$typeName = substr($typeName, 1);
}
- $suggestedName = Common::suggestType($typeName);
+ $suggestedName = Comments::suggestType($typeName, $this->typeFormat);
$suggestedTypeNames[] = $suggestedName;
if (count($typeNames) > 1) {
@@ -397,7 +400,9 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
$suggestedTypeHint = 'callable';
} else if (strpos($suggestedName, 'callback') !== false) {
$suggestedTypeHint = 'callable';
- } else if (in_array($suggestedName, Common::$allowedTypes, true) === false) {
+ } else if (isset(Comments::$allowedTypes[$suggestedName]) === false
+ && $suggestedName !== 'boolean' && $suggestedName !== 'integer'
+ ) {
$suggestedTypeHint = $suggestedName;
}
@@ -408,7 +413,9 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
$suggestedTypeHint = 'int';
} else if ($suggestedName === 'float') {
$suggestedTypeHint = 'float';
- } else if ($suggestedName === 'bool' || $suggestedName === 'boolean') {
+ } else if ($suggestedName === 'bool' || $suggestedName === 'boolean'
+ || $suggestedName === 'true' || $suggestedName === 'false'
+ ) {
$suggestedTypeHint = 'bool';
}
}
@@ -464,7 +471,9 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
}//end if
}//end foreach
- $suggestedType = implode($suggestedTypeNames, '|');
+ $suggestedType = Comments::suggestTypeString($param['type'], $this->typeFormat);
+ // Don't allow prefixing with nullable indicator.
+ $suggestedType = str_replace('?', '', $suggestedType);
if ($param['type'] !== $suggestedType) {
$error = 'Expected "%s" but found "%s" for parameter type';
$data = [
@@ -549,13 +558,12 @@ protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
$this->checkSpacingAfterParamName($phpcsFile, $param, $maxVar);
// Param comments must start with a capital letter and end with a full stop.
- if (preg_match('/^(\p{Ll}|\P{L})/u', $param['comment']) === 1) {
+ if (Orthography::isFirstCharCapitalized($param['comment']) === false) {
$error = 'Parameter comment must start with a capital letter';
$phpcsFile->addError($error, $param['tag'], 'ParamCommentNotCapital');
}
- $lastChar = substr($param['comment'], -1);
- if ($lastChar !== '.') {
+ if (Orthography::isLastCharPunctuation($param['comment'], '.') === false) {
$error = 'Parameter comment must end with a full stop';
$phpcsFile->addError($error, $param['tag'], 'ParamCommentFullStop');
}
diff --git a/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentThrowTagSniff.php b/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentThrowTagSniff.php
index cc68634d59..3e45a27d6b 100644
--- a/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentThrowTagSniff.php
+++ b/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentThrowTagSniff.php
@@ -11,7 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
-use PHP_CodeSniffer\Util\Tokens;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
class FunctionCommentThrowTagSniff implements Sniff
{
@@ -47,11 +47,8 @@ public function process(File $phpcsFile, $stackPtr)
return;
}
- $find = Tokens::$methodPrefixes;
- $find[] = T_WHITESPACE;
-
- $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
- if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG) {
+ $commentEnd = Comments::findFunctionComment($phpcsFile, $stackPtr);
+ if ($commentEnd === false || $tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG) {
// Function doesn't have a doc comment or is using the wrong type of comment.
return;
}
diff --git a/src/Standards/Squiz/Sniffs/Commenting/InlineCommentSniff.php b/src/Standards/Squiz/Sniffs/Commenting/InlineCommentSniff.php
index 6863a08021..e8a9947c9f 100644
--- a/src/Standards/Squiz/Sniffs/Commenting/InlineCommentSniff.php
+++ b/src/Standards/Squiz/Sniffs/Commenting/InlineCommentSniff.php
@@ -11,6 +11,9 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\Orthography;
use PHP_CodeSniffer\Util\Tokens;
class InlineCommentSniff implements Sniff
@@ -159,33 +162,15 @@ public function process(File $phpcsFile, $stackPtr)
$commentTokens = [$stackPtr];
- $nextComment = $stackPtr;
- $lastComment = $stackPtr;
- while (($nextComment = $phpcsFile->findNext(T_COMMENT, ($nextComment + 1), null, false)) !== false) {
- if ($tokens[$nextComment]['line'] !== ($tokens[$lastComment]['line'] + 1)) {
- break;
- }
-
- // Only want inline comments.
- if (substr($tokens[$nextComment]['content'], 0, 2) !== '//') {
- break;
- }
+ $lastCommentToken = Comments::findEndOfComment($phpcsFile, $stackPtr);
- // There is a comment on the very next line. If there is
- // no code between the comments, they are part of the same
- // comment block.
- $prevNonWhitespace = $phpcsFile->findPrevious(T_WHITESPACE, ($nextComment - 1), $lastComment, true);
- if ($prevNonWhitespace !== $lastComment) {
- break;
+ $commentText = '';
+ for ($current = $stackPtr; $current <= $lastCommentToken; $current++) {
+ if ($tokens[$current]['code'] === T_WHITESPACE) {
+ continue;
}
- $commentTokens[] = $nextComment;
- $lastComment = $nextComment;
- }//end while
-
- $commentText = '';
- foreach ($commentTokens as $lastCommentToken) {
- $comment = rtrim($tokens[$lastCommentToken]['content']);
+ $comment = rtrim($tokens[$current]['content']);
if (trim(substr($comment, 2)) === '') {
continue;
@@ -215,14 +200,14 @@ public function process(File $phpcsFile, $stackPtr)
ltrim(substr($comment, 2)),
$comment,
];
- $fix = $phpcsFile->addFixableError($error, $lastCommentToken, 'TabBefore', $data);
+ $fix = $phpcsFile->addFixableError($error, $current, 'TabBefore', $data);
} else if ($spaceCount === 0) {
$error = 'No space found before comment text; expected "// %s" but found "%s"';
$data = [
substr($comment, 2),
$comment,
];
- $fix = $phpcsFile->addFixableError($error, $lastCommentToken, 'NoSpaceBefore', $data);
+ $fix = $phpcsFile->addFixableError($error, $current, 'NoSpaceBefore', $data);
} else if ($spaceCount > 1) {
$error = 'Expected 1 space before comment text but found %s; use block comment if you need indentation';
$data = [
@@ -230,16 +215,16 @@ public function process(File $phpcsFile, $stackPtr)
substr($comment, (2 + $spaceCount)),
$comment,
];
- $fix = $phpcsFile->addFixableError($error, $lastCommentToken, 'SpacingBefore', $data);
+ $fix = $phpcsFile->addFixableError($error, $current, 'SpacingBefore', $data);
}//end if
if ($fix === true) {
- $newComment = '// '.ltrim($tokens[$lastCommentToken]['content'], "/\t ");
- $phpcsFile->fixer->replaceToken($lastCommentToken, $newComment);
+ $newComment = '// '.ltrim($tokens[$current]['content'], "/\t ");
+ $phpcsFile->fixer->replaceToken($current, $newComment);
}
- $commentText .= trim(substr($tokens[$lastCommentToken]['content'], 2));
- }//end foreach
+ $commentText .= trim(substr($tokens[$current]['content'], 2));
+ }//end for
if ($commentText === '') {
$error = 'Blank comments are not allowed';
@@ -251,7 +236,7 @@ public function process(File $phpcsFile, $stackPtr)
return ($lastCommentToken + 1);
}
- if (preg_match('/^\p{Ll}/u', $commentText) === 1) {
+ if (Orthography::isFirstCharLowercase($commentText) === true) {
$error = 'Inline comments must start with a capital letter';
$phpcsFile->addError($error, $stackPtr, 'NotCapital');
}
@@ -259,14 +244,13 @@ public function process(File $phpcsFile, $stackPtr)
// Only check the end of comment character if the start of the comment
// is a letter, indicating that the comment is just standard text.
if (preg_match('/^\p{L}/u', $commentText) === 1) {
- $commentCloser = $commentText[(strlen($commentText) - 1)];
$acceptedClosers = [
'full-stops' => '.',
'exclamation marks' => '!',
'or question marks' => '?',
];
- if (in_array($commentCloser, $acceptedClosers, true) === false) {
+ if (Orthography::isLastCharPunctuation($commentText, implode('', $acceptedClosers)) === false) {
$error = 'Inline comments must end in %s';
$ender = '';
foreach ($acceptedClosers as $closerName => $symbol) {
@@ -298,11 +282,8 @@ public function process(File $phpcsFile, $stackPtr)
$errorCode = 'SpacingAfter';
if (isset($tokens[$stackPtr]['conditions']) === true) {
- $conditions = $tokens[$stackPtr]['conditions'];
- $type = end($conditions);
- $conditionPtr = key($conditions);
-
- if (($type === T_FUNCTION || $type === T_CLOSURE)
+ $conditionPtr = Conditions::validDirectScope($phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE]);
+ if ($conditionPtr !== false
&& $tokens[$conditionPtr]['scope_closer'] === $next
) {
$errorCode = 'SpacingAfterAtFunctionEnd';
diff --git a/src/Standards/Squiz/Sniffs/Commenting/PostStatementCommentSniff.php b/src/Standards/Squiz/Sniffs/Commenting/PostStatementCommentSniff.php
index 5bfa9ebef3..967cd5449a 100644
--- a/src/Standards/Squiz/Sniffs/Commenting/PostStatementCommentSniff.php
+++ b/src/Standards/Squiz/Sniffs/Commenting/PostStatementCommentSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
class PostStatementCommentSniff implements Sniff
{
@@ -34,12 +35,12 @@ class PostStatementCommentSniff implements Sniff
* @var array
*/
private $controlStructureExceptions = [
- T_IF => true,
- T_ELSEIF => true,
- T_SWITCH => true,
- T_WHILE => true,
- T_FOR => true,
- T_FOREACH => true,
+ T_IF,
+ T_ELSEIF,
+ T_SWITCH,
+ T_WHILE,
+ T_FOR,
+ T_FOREACH,
];
@@ -97,15 +98,8 @@ public function process(File $phpcsFile, $stackPtr)
}
// Special case for (trailing) comments within multi-line control structures.
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- $nestedParens = $tokens[$stackPtr]['nested_parenthesis'];
- foreach ($nestedParens as $open => $close) {
- if (isset($tokens[$open]['parenthesis_owner']) === true
- && isset($this->controlStructureExceptions[$tokens[$tokens[$open]['parenthesis_owner']]['code']]) === true
- ) {
- return;
- }
- }
+ if (Parentheses::hasOwner($phpcsFile, $stackPtr, $this->controlStructureExceptions) === true) {
+ return;
}
$error = 'Comments may not appear after statements';
diff --git a/src/Standards/Squiz/Sniffs/Commenting/VariableCommentSniff.php b/src/Standards/Squiz/Sniffs/Commenting/VariableCommentSniff.php
index 8c9fdeb3c7..edff62ab98 100644
--- a/src/Standards/Squiz/Sniffs/Commenting/VariableCommentSniff.php
+++ b/src/Standards/Squiz/Sniffs/Commenting/VariableCommentSniff.php
@@ -11,11 +11,22 @@
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
use PHP_CodeSniffer\Files\File;
-use PHP_CodeSniffer\Util\Common;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
class VariableCommentSniff extends AbstractVariableSniff
{
+ /**
+ * Whether short or long form parameter/return types are preferred.
+ *
+ * Valid values: 'short' or 'long'. Defaults to 'long'.
+ *
+ * This applies to just two types: `int` vs `integer` and `bool` vs `boolean`.
+ *
+ * @var string
+ */
+ public $typeFormat = 'long';
+
/**
* Called to process class member vars.
@@ -28,25 +39,13 @@ class VariableCommentSniff extends AbstractVariableSniff
*/
public function processMemberVar(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
- $ignore = [
- T_PUBLIC,
- T_PRIVATE,
- T_PROTECTED,
- T_VAR,
- T_STATIC,
- T_WHITESPACE,
- ];
-
- $commentEnd = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true);
- if ($commentEnd === false
- || ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
- && $tokens[$commentEnd]['code'] !== T_COMMENT)
- ) {
+ $commentEnd = Comments::findPropertyComment($phpcsFile, $stackPtr);
+ if ($commentEnd === false) {
$phpcsFile->addError('Missing member variable doc comment', $stackPtr, 'Missing');
return;
}
+ $tokens = $phpcsFile->getTokens();
if ($tokens[$commentEnd]['code'] === T_COMMENT) {
$phpcsFile->addError('You must use "/**" style comments for a member variable comment', $stackPtr, 'WrongStyle');
return;
@@ -99,7 +98,7 @@ public function processMemberVar(File $phpcsFile, $stackPtr)
}
// Support both a var type and a description.
- preg_match('`^((?:\|?(?:array\([^\)]*\)|[\\\\a-z0-9\[\]]+))*)( .*)?`i', $tokens[($foundVar + 2)]['content'], $varParts);
+ preg_match('`^((?:\|?(?:array(?:\([^\)]*\)|<[^>]*>+)|\(?[\\\\a-z0-9\[\]]+\)?))*)( .*)?`i', $tokens[($foundVar + 2)]['content'], $varParts);
if (isset($varParts[1]) === false) {
return;
}
@@ -107,16 +106,7 @@ public function processMemberVar(File $phpcsFile, $stackPtr)
$varType = $varParts[1];
// Check var type (can be multiple, separated by '|').
- $typeNames = explode('|', $varType);
- $suggestedNames = [];
- foreach ($typeNames as $i => $typeName) {
- $suggestedName = Common::suggestType($typeName);
- if (in_array($suggestedName, $suggestedNames, true) === false) {
- $suggestedNames[] = $suggestedName;
- }
- }
-
- $suggestedType = implode('|', $suggestedNames);
+ $suggestedType = Comments::suggestTypeString($varType, $this->typeFormat);
if ($varType !== $suggestedType) {
$error = 'Expected "%s" but found "%s" for @var tag in member variable comment';
$data = [
diff --git a/src/Standards/Squiz/Sniffs/ControlStructures/InlineIfDeclarationSniff.php b/src/Standards/Squiz/Sniffs/ControlStructures/InlineIfDeclarationSniff.php
index f6513329d7..63243e1b70 100644
--- a/src/Standards/Squiz/Sniffs/ControlStructures/InlineIfDeclarationSniff.php
+++ b/src/Standards/Squiz/Sniffs/ControlStructures/InlineIfDeclarationSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
class InlineIfDeclarationSniff implements Sniff
{
@@ -41,12 +42,10 @@ public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
- $openBracket = null;
- $closeBracket = null;
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- $parens = $tokens[$stackPtr]['nested_parenthesis'];
- $openBracket = array_pop($parens);
- $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
+ $closeBracket = null;
+ $lastParenCloser = Parentheses::getLastCloser($phpcsFile, $stackPtr);
+ if ($lastParenCloser !== false) {
+ $closeBracket = $lastParenCloser;
}
// Find the beginning of the statement. If we don't find a
diff --git a/src/Standards/Squiz/Sniffs/Debug/JavaScriptLintSniff.php b/src/Standards/Squiz/Sniffs/Debug/JavaScriptLintSniff.php
index 033327bdeb..99a4bbd659 100644
--- a/src/Standards/Squiz/Sniffs/Debug/JavaScriptLintSniff.php
+++ b/src/Standards/Squiz/Sniffs/Debug/JavaScriptLintSniff.php
@@ -45,6 +45,7 @@ public function register()
* the token was found.
*
* @return void
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If Javascript Lint ran into trouble.
*/
public function process(File $phpcsFile, $stackPtr)
{
diff --git a/src/Standards/Squiz/Sniffs/Formatting/OperatorBracketSniff.php b/src/Standards/Squiz/Sniffs/Formatting/OperatorBracketSniff.php
index 9a5bbb833c..356dd170f5 100644
--- a/src/Standards/Squiz/Sniffs/Formatting/OperatorBracketSniff.php
+++ b/src/Standards/Squiz/Sniffs/Formatting/OperatorBracketSniff.php
@@ -11,6 +11,8 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
+use PHP_CodeSniffer\Util\Sniffs\TokenIs;
use PHP_CodeSniffer\Util\Tokens;
class OperatorBracketSniff implements Sniff
@@ -52,49 +54,52 @@ public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
- if ($phpcsFile->tokenizerType === 'JS' && $tokens[$stackPtr]['code'] === T_PLUS) {
- // JavaScript uses the plus operator for string concatenation as well
- // so we cannot accurately determine if it is a string concat or addition.
- // So just ignore it.
- return;
- }
-
// If the & is a reference, then we don't want to check for brackets.
- if ($tokens[$stackPtr]['code'] === T_BITWISE_AND && $phpcsFile->isReference($stackPtr) === true) {
+ if ($tokens[$stackPtr]['code'] === T_BITWISE_AND
+ && TokenIs::isReference($phpcsFile, $stackPtr) === true
+ ) {
return;
}
// There is one instance where brackets aren't needed, which involves
- // the minus sign being used to assign a negative number to a variable.
- if ($tokens[$stackPtr]['code'] === T_MINUS) {
- // Check to see if we are trying to return -n.
- $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
- if ($tokens[$prev]['code'] === T_RETURN) {
- return;
- }
-
- $number = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
- if ($tokens[$number]['code'] === T_LNUMBER || $tokens[$number]['code'] === T_DNUMBER) {
- $previous = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
- if ($previous !== false) {
- $isAssignment = isset(Tokens::$assignmentTokens[$tokens[$previous]['code']]);
- $isEquality = isset(Tokens::$equalityTokens[$tokens[$previous]['code']]);
- $isComparison = isset(Tokens::$comparisonTokens[$tokens[$previous]['code']]);
- if ($isAssignment === true || $isEquality === true || $isComparison === true) {
- // This is a negative assignment or comparison.
- // We need to check that the minus and the number are
- // adjacent.
- if (($number - $stackPtr) !== 1) {
- $error = 'No space allowed between minus sign and number';
- $phpcsFile->addError($error, $stackPtr, 'SpacingAfterMinus');
+ // the plus/minus sign being used to assign a positive/negative number to a variable.
+ if ($tokens[$stackPtr]['code'] === T_MINUS || $tokens[$stackPtr]['code'] === T_PLUS) {
+ if (TokenIs::isUnaryPlusMinus($phpcsFile, $stackPtr) === true) {
+ // This is a positive/negative assignment or comparison.
+ // We need to check that the sign and the number are adjacent.
+ $number = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+ if (($tokens[$number]['code'] === T_LNUMBER || $tokens[$number]['code'] === T_DNUMBER)
+ && (($number - $stackPtr) !== 1)
+ ) {
+ $error = 'No space allowed between the unary sign and the number';
+
+ $nextNonWhitespace = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($nextNonWhitespace === $number) {
+ $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterSign');
+ if ($fix === true) {
+ $phpcsFile->fixer->beginChangeset();
+ for ($i = ($stackPtr + 1); $i < $number; $i++) {
+ $phpcsFile->fixer->replaceToken($i, '');
+ }
+
+ $phpcsFile->fixer->endChangeset();
}
-
- return;
+ } else {
+ $phpcsFile->addError($error, $stackPtr, 'SpacingAfterSign');
}
}
- }
+
+ return;
+ }//end if
}//end if
+ if ($phpcsFile->tokenizerType === 'JS' && $tokens[$stackPtr]['code'] === T_PLUS) {
+ // JavaScript uses the plus operator for string concatenation as well
+ // so we cannot accurately determine if it is a string concat or addition.
+ // So just ignore it.
+ return;
+ }
+
$previousToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true, null, true);
if ($previousToken !== false) {
// A list of tokens that indicate that the token is not
@@ -115,17 +120,11 @@ public function process(File $phpcsFile, $stackPtr)
}
if ($tokens[$stackPtr]['code'] === T_BITWISE_OR
- && isset($tokens[$stackPtr]['nested_parenthesis']) === true
+ && Parentheses::lastOwnerIn($phpcsFile, $stackPtr, T_CATCH) !== false
) {
- $brackets = $tokens[$stackPtr]['nested_parenthesis'];
- $lastBracket = array_pop($brackets);
- if (isset($tokens[$lastBracket]['parenthesis_owner']) === true
- && $tokens[$tokens[$lastBracket]['parenthesis_owner']]['code'] === T_CATCH
- ) {
- // This is a pipe character inside a catch statement, so it is acting
- // as an exception type separator and not an arithmetic operation.
- return;
- }
+ // This is a pipe character inside a catch statement, so it is acting
+ // as an exception type separator and not an arithmetic operation.
+ return;
}
// Tokens that are allowed inside a bracketed operation.
diff --git a/src/Standards/Squiz/Sniffs/Functions/FunctionDeclarationArgumentSpacingSniff.php b/src/Standards/Squiz/Sniffs/Functions/FunctionDeclarationArgumentSpacingSniff.php
index 98f6dfdde2..d4d47e2784 100644
--- a/src/Standards/Squiz/Sniffs/Functions/FunctionDeclarationArgumentSpacingSniff.php
+++ b/src/Standards/Squiz/Sniffs/Functions/FunctionDeclarationArgumentSpacingSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\TokenIs;
use PHP_CodeSniffer\Util\Tokens;
class FunctionDeclarationArgumentSpacingSniff implements Sniff
@@ -192,7 +193,7 @@ public function processBracket($phpcsFile, $openBracket)
// Take references into account when expecting the
// location of whitespace.
- if ($phpcsFile->isReference($checkToken) === true) {
+ if (TokenIs::isReference($phpcsFile, $checkToken) === true) {
$whitespace = ($checkToken - 1);
} else {
$whitespace = $checkToken;
@@ -205,7 +206,7 @@ public function processBracket($phpcsFile, $openBracket)
// Before we throw an error, make sure there is no type hint.
$comma = $phpcsFile->findPrevious(T_COMMA, ($nextParam - 1));
$nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($comma + 1), null, true);
- if ($phpcsFile->isReference($nextToken) === true) {
+ if (TokenIs::isReference($phpcsFile, $nextToken) === true) {
$nextToken++;
}
@@ -294,7 +295,7 @@ public function processBracket($phpcsFile, $openBracket)
// Before we throw an error, make sure there is no type hint.
$bracket = $phpcsFile->findPrevious(T_OPEN_PARENTHESIS, ($nextParam - 1));
$nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($bracket + 1), null, true);
- if ($phpcsFile->isReference($nextToken) === true) {
+ if (TokenIs::isReference($phpcsFile, $nextToken) === true) {
$nextToken++;
}
diff --git a/src/Standards/Squiz/Sniffs/Functions/GlobalFunctionSniff.php b/src/Standards/Squiz/Sniffs/Functions/GlobalFunctionSniff.php
index 25159388f6..8a0c4cfbd7 100644
--- a/src/Standards/Squiz/Sniffs/Functions/GlobalFunctionSniff.php
+++ b/src/Standards/Squiz/Sniffs/Functions/GlobalFunctionSniff.php
@@ -11,6 +11,10 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
+use PHP_CodeSniffer\Util\Tokens;
class GlobalFunctionSniff implements Sniff
{
@@ -39,22 +43,25 @@ public function register()
*/
public function process(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
-
- if (empty($tokens[$stackPtr]['conditions']) === true) {
- $functionName = $phpcsFile->getDeclarationName($stackPtr);
- if ($functionName === null) {
- return;
- }
-
- // Special exception for __autoload as it needs to be global.
- if ($functionName !== '__autoload') {
- $error = 'Consider putting global function "%s" in a static class';
- $data = [$functionName];
- $phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
- }
+ if (Conditions::hasCondition($phpcsFile, $stackPtr, Tokens::$ooScopeTokens) === true) {
+ return;
}
+ // Special exception for PHP magic functions as they need to be global.
+ if (FunctionDeclarations::isMagicFunction($phpcsFile, $stackPtr) === true) {
+ return;
+ }
+
+ $functionName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($functionName) === true) {
+ // Live coding or parse error.
+ return;
+ }
+
+ $error = 'Consider putting global function "%s" in a static class';
+ $data = [$functionName];
+ $phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
+
}//end process()
diff --git a/src/Standards/Squiz/Sniffs/NamingConventions/ValidFunctionNameSniff.php b/src/Standards/Squiz/Sniffs/NamingConventions/ValidFunctionNameSniff.php
index 537d7d6680..695472005c 100644
--- a/src/Standards/Squiz/Sniffs/NamingConventions/ValidFunctionNameSniff.php
+++ b/src/Standards/Squiz/Sniffs/NamingConventions/ValidFunctionNameSniff.php
@@ -10,8 +10,8 @@
namespace PHP_CodeSniffer\Standards\Squiz\Sniffs\NamingConventions;
use PHP_CodeSniffer\Standards\PEAR\Sniffs\NamingConventions\ValidFunctionNameSniff as PEARValidFunctionNameSniff;
-use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
class ValidFunctionNameSniff extends PEARValidFunctionNameSniff
{
@@ -28,8 +28,9 @@ class ValidFunctionNameSniff extends PEARValidFunctionNameSniff
*/
protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
{
- $functionName = $phpcsFile->getDeclarationName($stackPtr);
- if ($functionName === null) {
+ $functionName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($functionName) === true) {
+ // Live coding or parse error.
return;
}
@@ -43,7 +44,7 @@ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
$functionName = ltrim($functionName, '_');
}
- if (Common::isCamelCaps($functionName, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($functionName, false, true, false) === false) {
$error = 'Function name "%s" is not in camel caps format';
$phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
}
diff --git a/src/Standards/Squiz/Sniffs/NamingConventions/ValidVariableNameSniff.php b/src/Standards/Squiz/Sniffs/NamingConventions/ValidVariableNameSniff.php
index 2a3966fa0c..ab0f15f5a9 100644
--- a/src/Standards/Squiz/Sniffs/NamingConventions/ValidVariableNameSniff.php
+++ b/src/Standards/Squiz/Sniffs/NamingConventions/ValidVariableNameSniff.php
@@ -10,8 +10,10 @@
namespace PHP_CodeSniffer\Standards\Squiz\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
-use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\Variables;
use PHP_CodeSniffer\Util\Tokens;
class ValidVariableNameSniff extends AbstractVariableSniff
@@ -29,11 +31,10 @@ class ValidVariableNameSniff extends AbstractVariableSniff
*/
protected function processVariable(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
- $varName = ltrim($tokens[$stackPtr]['content'], '$');
+ $tokens = $phpcsFile->getTokens();
// If it's a php reserved var, then its ok.
- if (isset($this->phpReservedVars[$varName]) === true) {
+ if (Variables::isPHPReservedVarName($tokens[$stackPtr]['content']) === true) {
return;
}
@@ -54,7 +55,7 @@ protected function processVariable(File $phpcsFile, $stackPtr)
$objVarName = substr($objVarName, 1);
}
- if (Common::isCamelCaps($objVarName, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($objVarName, false, true, false) === false) {
$error = 'Variable "%s" is not in valid camel caps format';
$data = [$originalVarName];
$phpcsFile->addError($error, $var, 'NotCamelCaps', $data);
@@ -66,6 +67,7 @@ protected function processVariable(File $phpcsFile, $stackPtr)
// There is no way for us to know if the var is public or private,
// so we have to ignore a leading underscore if there is one and just
// check the main part of the variable name.
+ $varName = ltrim($tokens[$stackPtr]['content'], '$');
$originalVarName = $varName;
if (substr($varName, 0, 1) === '_') {
$objOperator = $phpcsFile->findPrevious([T_WHITESPACE], ($stackPtr - 1), null, true);
@@ -74,7 +76,7 @@ protected function processVariable(File $phpcsFile, $stackPtr)
// this: MyClass::$_variable, so we don't know its scope.
$inClass = true;
} else {
- $inClass = $phpcsFile->hasCondition($stackPtr, Tokens::$ooScopeTokens);
+ $inClass = Conditions::hasCondition($phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
}
if ($inClass === true) {
@@ -82,7 +84,7 @@ protected function processVariable(File $phpcsFile, $stackPtr)
}
}
- if (Common::isCamelCaps($varName, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($varName, false, true, false) === false) {
$error = 'Variable "%s" is not in valid camel caps format';
$data = [$originalVarName];
$phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $data);
@@ -102,10 +104,7 @@ protected function processVariable(File $phpcsFile, $stackPtr)
*/
protected function processMemberVar(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
-
- $varName = ltrim($tokens[$stackPtr]['content'], '$');
- $memberProps = $phpcsFile->getMemberProperties($stackPtr);
+ $memberProps = Variables::getMemberProperties($phpcsFile, $stackPtr);
if (empty($memberProps) === true) {
// Couldn't get any info about this variable, which
// generally means it is invalid or possibly has a parse
@@ -114,6 +113,8 @@ protected function processMemberVar(File $phpcsFile, $stackPtr)
return;
}
+ $tokens = $phpcsFile->getTokens();
+ $varName = ltrim($tokens[$stackPtr]['content'], '$');
$public = ($memberProps['scope'] !== 'private');
$errorData = [$varName];
@@ -136,7 +137,7 @@ protected function processMemberVar(File $phpcsFile, $stackPtr)
// Remove a potential underscore prefix for testing CamelCaps.
$varName = ltrim($varName, '_');
- if (Common::isCamelCaps($varName, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($varName, false, true, false) === false) {
$error = 'Member variable "%s" is not in valid camel caps format';
$phpcsFile->addError($error, $stackPtr, 'MemberNotCamelCaps', $errorData);
}
@@ -160,11 +161,11 @@ protected function processVariableInString(File $phpcsFile, $stackPtr)
if (preg_match_all('|[^\\\]\${?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)|', $tokens[$stackPtr]['content'], $matches) !== 0) {
foreach ($matches[1] as $varName) {
// If it's a php reserved var, then its ok.
- if (isset($this->phpReservedVars[$varName]) === true) {
+ if (Variables::isPHPReservedVarName($varName) === true) {
continue;
}
- if (Common::isCamelCaps($varName, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($varName, false, true, false) === false) {
$error = 'Variable "%s" is not in valid camel caps format';
$data = [$varName];
$phpcsFile->addError($error, $stackPtr, 'StringNotCamelCaps', $data);
diff --git a/src/Standards/Squiz/Sniffs/Operators/ComparisonOperatorUsageSniff.php b/src/Standards/Squiz/Sniffs/Operators/ComparisonOperatorUsageSniff.php
index 5eed89bff4..dc2b0190c6 100644
--- a/src/Standards/Squiz/Sniffs/Operators/ComparisonOperatorUsageSniff.php
+++ b/src/Standards/Squiz/Sniffs/Operators/ComparisonOperatorUsageSniff.php
@@ -44,7 +44,7 @@ class ComparisonOperatorUsageSniff implements Sniff
/**
* A list of invalid operators with their alternatives.
*
- * @var array
+ * @var array>
*/
private static $invalidOps = [
'PHP' => [
diff --git a/src/Standards/Squiz/Sniffs/PHP/CommentedOutCodeSniff.php b/src/Standards/Squiz/Sniffs/PHP/CommentedOutCodeSniff.php
index d6619045fa..5ee8f69fcb 100644
--- a/src/Standards/Squiz/Sniffs/PHP/CommentedOutCodeSniff.php
+++ b/src/Standards/Squiz/Sniffs/PHP/CommentedOutCodeSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Exceptions\TokenizerException;
@@ -67,40 +68,17 @@ public function process(File $phpcsFile, $stackPtr)
}
$content = '';
- $lastLineSeen = $tokens[$stackPtr]['line'];
$commentStyle = 'line';
if (strpos($tokens[$stackPtr]['content'], '/*') === 0) {
$commentStyle = 'block';
}
- $lastCommentBlockToken = $stackPtr;
- for ($i = $stackPtr; $i < $phpcsFile->numTokens; $i++) {
- if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === false) {
- break;
- }
-
- if ($tokens[$i]['code'] === T_WHITESPACE) {
- continue;
- }
-
- if (isset(Tokens::$phpcsCommentTokens[$tokens[$i]['code']]) === true) {
- $lastLineSeen = $tokens[$i]['line'];
- continue;
- }
-
- if ($commentStyle === 'line'
- && ($lastLineSeen + 1) <= $tokens[$i]['line']
- && strpos($tokens[$i]['content'], '/*') === 0
+ $lastCommentBlockToken = Comments::findEndOfComment($phpcsFile, $stackPtr);
+ for ($i = $stackPtr; $i <= $lastCommentBlockToken; $i++) {
+ if ($tokens[$i]['code'] === T_WHITESPACE
+ || isset(Tokens::$phpcsCommentTokens[$tokens[$i]['code']]) === true
) {
- // First non-whitespace token on a new line is start of a different style comment.
- break;
- }
-
- if ($commentStyle === 'line'
- && ($lastLineSeen + 1) < $tokens[$i]['line']
- ) {
- // Blank line breaks a '//' style comment block.
- break;
+ continue;
}
/*
@@ -109,7 +87,6 @@ public function process(File $phpcsFile, $stackPtr)
*/
$tokenContent = trim($tokens[$i]['content']);
- $break = false;
if ($commentStyle === 'line') {
if (substr($tokenContent, 0, 2) === '//') {
@@ -130,7 +107,6 @@ public function process(File $phpcsFile, $stackPtr)
if (substr($tokenContent, -2) === '*/') {
$tokenContent = substr($tokenContent, 0, -2);
- $break = true;
}
if (substr($tokenContent, 0, 1) === '*') {
@@ -138,15 +114,7 @@ public function process(File $phpcsFile, $stackPtr)
}
}//end if
- $content .= $tokenContent.$phpcsFile->eolChar;
- $lastLineSeen = $tokens[$i]['line'];
-
- $lastCommentBlockToken = $i;
-
- if ($break === true) {
- // Closer of a block comment found.
- break;
- }
+ $content .= $tokenContent.$phpcsFile->eolChar;
}//end for
// Ignore typical warning suppression annotations from other tools.
diff --git a/src/Standards/Squiz/Sniffs/PHP/DisallowBooleanStatementSniff.php b/src/Standards/Squiz/Sniffs/PHP/DisallowBooleanStatementSniff.php
index 8c9bd27a4a..2295b63f33 100644
--- a/src/Standards/Squiz/Sniffs/PHP/DisallowBooleanStatementSniff.php
+++ b/src/Standards/Squiz/Sniffs/PHP/DisallowBooleanStatementSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
class DisallowBooleanStatementSniff implements Sniff
@@ -40,14 +41,9 @@ public function register()
*/
public function process(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- foreach ($tokens[$stackPtr]['nested_parenthesis'] as $open => $close) {
- if (isset($tokens[$open]['parenthesis_owner']) === true) {
- // Any owner means we are not just a simple statement.
- return;
- }
- }
+ if (Parentheses::hasOwner($phpcsFile, $stackPtr, Tokens::$parenthesisOpeners) === true) {
+ // Any owner means we are not just a simple statement.
+ return;
}
$error = 'Boolean operators are not allowed outside of control structure conditions';
diff --git a/src/Standards/Squiz/Sniffs/PHP/DisallowComparisonAssignmentSniff.php b/src/Standards/Squiz/Sniffs/PHP/DisallowComparisonAssignmentSniff.php
index 43e0a8b1eb..8264b6803f 100644
--- a/src/Standards/Squiz/Sniffs/PHP/DisallowComparisonAssignmentSniff.php
+++ b/src/Standards/Squiz/Sniffs/PHP/DisallowComparisonAssignmentSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
class DisallowComparisonAssignmentSniff implements Sniff
@@ -43,42 +44,7 @@ public function process(File $phpcsFile, $stackPtr)
$tokens = $phpcsFile->getTokens();
// Ignore default value assignments in function definitions.
- $function = $phpcsFile->findPrevious(T_FUNCTION, ($stackPtr - 1), null, false, null, true);
- if ($function !== false) {
- $opener = $tokens[$function]['parenthesis_opener'];
- $closer = $tokens[$function]['parenthesis_closer'];
- if ($opener < $stackPtr && $closer > $stackPtr) {
- return;
- }
- }
-
- // Ignore values in array definitions.
- $array = $phpcsFile->findNext(
- T_ARRAY,
- ($stackPtr + 1),
- null,
- false,
- null,
- true
- );
-
- if ($array !== false) {
- return;
- }
-
- // Ignore function calls.
- $ignore = [
- T_STRING,
- T_WHITESPACE,
- T_OBJECT_OPERATOR,
- ];
-
- $next = $phpcsFile->findNext($ignore, ($stackPtr + 1), null, true);
- if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS
- && $tokens[($next - 1)]['code'] === T_STRING
- ) {
- // Code will look like: $var = myFunction(
- // and will be ignored.
+ if (Parentheses::lastOwnerIn($phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE]) !== false) {
return;
}
@@ -90,6 +56,43 @@ public function process(File $phpcsFile, $stackPtr)
}
for ($i = ($stackPtr + 1); $i < $endStatement; $i++) {
+ if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ // Skip over arrays.
+ if ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) {
+ $i = $tokens[$i]['bracket_closer'];
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_ARRAY && isset($tokens[$i]['parenthesis_closer']) === true) {
+ $i = $tokens[$i]['parenthesis_closer'];
+ continue;
+ }
+
+ // Skip over closures.
+ if ($tokens[$i]['code'] === T_CLOSURE) {
+ if (isset($tokens[$i]['scope_closer']) === false) {
+ // Live coding.
+ break;
+ }
+
+ $i = $tokens[$i]['scope_closer'];
+ continue;
+ }
+
+ // Skip over named function calls.
+ if ($tokens[$i]['code'] === T_STRING) {
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
+ if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS
+ && isset($tokens[$next]['parenthesis_closer']) === true
+ ) {
+ $i = $tokens[$next]['parenthesis_closer'];
+ continue;
+ }
+ }
+
if ((isset(Tokens::$comparisonTokens[$tokens[$i]['code']]) === true
&& $tokens[$i]['code'] !== T_COALESCE)
|| $tokens[$i]['code'] === T_INLINE_THEN
@@ -106,7 +109,7 @@ public function process(File $phpcsFile, $stackPtr)
$phpcsFile->addError($error, $stackPtr, 'AssignedBool');
break;
}
- }
+ }//end for
}//end process()
diff --git a/src/Standards/Squiz/Sniffs/PHP/DisallowMultipleAssignmentsSniff.php b/src/Standards/Squiz/Sniffs/PHP/DisallowMultipleAssignmentsSniff.php
index 01c2818ec0..ac145e5374 100644
--- a/src/Standards/Squiz/Sniffs/PHP/DisallowMultipleAssignmentsSniff.php
+++ b/src/Standards/Squiz/Sniffs/PHP/DisallowMultipleAssignmentsSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
class DisallowMultipleAssignmentsSniff implements Sniff
@@ -42,26 +43,14 @@ public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
- // Ignore default value assignments in function definitions.
- $function = $phpcsFile->findPrevious([T_FUNCTION, T_CLOSURE], ($stackPtr - 1), null, false, null, true);
- if ($function !== false) {
- $opener = $tokens[$function]['parenthesis_opener'];
- $closer = $tokens[$function]['parenthesis_closer'];
- if ($opener < $stackPtr && $closer > $stackPtr) {
- return;
- }
+ // Ignore default value assignments in function definitions and assignments in WHILE loop conditions.
+ if (Parentheses::lastOwnerIn($phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE]) !== false) {
+ return;
}
// Ignore assignments in WHILE loop conditions.
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- $nested = $tokens[$stackPtr]['nested_parenthesis'];
- foreach ($nested as $opener => $closer) {
- if (isset($tokens[$opener]['parenthesis_owner']) === true
- && $tokens[$tokens[$opener]['parenthesis_owner']]['code'] === T_WHILE
- ) {
- return;
- }
- }
+ if (Parentheses::hasOwner($phpcsFile, $stackPtr, T_WHILE) === true) {
+ return;
}
/*
@@ -135,11 +124,10 @@ public function process(File $phpcsFile, $stackPtr)
// Ignore the first part of FOR loops as we are allowed to
// assign variables there even though the variable is not the
// first thing on the line.
- if ($tokens[$varToken]['code'] === T_OPEN_PARENTHESIS && isset($tokens[$varToken]['parenthesis_owner']) === true) {
- $owner = $tokens[$varToken]['parenthesis_owner'];
- if ($tokens[$owner]['code'] === T_FOR) {
- return;
- }
+ if ($tokens[$varToken]['code'] === T_OPEN_PARENTHESIS
+ && Parentheses::isOwnerIn($phpcsFile, $varToken, T_FOR) === true
+ ) {
+ return;
}
if ($tokens[$varToken]['code'] === T_VARIABLE
diff --git a/src/Standards/Squiz/Sniffs/PHP/InnerFunctionsSniff.php b/src/Standards/Squiz/Sniffs/PHP/InnerFunctionsSniff.php
index 5df214db03..d93fb86cae 100644
--- a/src/Standards/Squiz/Sniffs/PHP/InnerFunctionsSniff.php
+++ b/src/Standards/Squiz/Sniffs/PHP/InnerFunctionsSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
class InnerFunctionsSniff implements Sniff
{
@@ -41,13 +42,13 @@ public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
- $function = $phpcsFile->getCondition($stackPtr, T_FUNCTION);
+ $function = Conditions::getCondition($phpcsFile, $stackPtr, T_FUNCTION);
if ($function === false) {
// Not a nested function.
return;
}
- $class = $phpcsFile->getCondition($stackPtr, T_ANON_CLASS);
+ $class = Conditions::getCondition($phpcsFile, $stackPtr, T_ANON_CLASS);
if ($class !== false && $class > $function) {
// Ignore methods in anon classes.
return;
diff --git a/src/Standards/Squiz/Sniffs/PHP/NonExecutableCodeSniff.php b/src/Standards/Squiz/Sniffs/PHP/NonExecutableCodeSniff.php
index 64e34df130..ebf7fbb882 100644
--- a/src/Standards/Squiz/Sniffs/PHP/NonExecutableCodeSniff.php
+++ b/src/Standards/Squiz/Sniffs/PHP/NonExecutableCodeSniff.php
@@ -11,6 +11,8 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
class NonExecutableCodeSniff implements Sniff
@@ -139,24 +141,14 @@ public function process(File $phpcsFile, $stackPtr)
// If we find a closing parenthesis that belongs to a condition
// we should ignore this token.
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
- if (isset($tokens[$prev]['parenthesis_owner']) === true) {
- $owner = $tokens[$prev]['parenthesis_owner'];
- $ignore = [
- T_IF => true,
- T_ELSE => true,
- T_ELSEIF => true,
- ];
- if (isset($ignore[$tokens[$owner]['code']]) === true) {
- return;
- }
+ if (Parentheses::isOwnerIn($phpcsFile, $prev, [T_IF, T_ELSE, T_ELSEIF]) === true) {
+ return;
}
- $ourConditions = array_keys($tokens[$stackPtr]['conditions']);
-
- if (empty($ourConditions) === false) {
- $condition = array_pop($ourConditions);
+ $lastCondition = Conditions::getLastCondition($phpcsFile, $stackPtr);
- if (isset($tokens[$condition]['scope_closer']) === false) {
+ if ($lastCondition !== false) {
+ if (isset($tokens[$lastCondition]['scope_closer']) === false) {
return;
}
@@ -165,13 +157,13 @@ public function process(File $phpcsFile, $stackPtr)
// used to close a CASE statement, so it is most likely non-executable
// code itself (as is the case when you put return; break; at the end of
// a case). So we need to ignore this token.
- if ($tokens[$condition]['code'] === T_SWITCH
+ if ($tokens[$lastCondition]['code'] === T_SWITCH
&& $tokens[$stackPtr]['code'] === T_BREAK
) {
return;
}
- $closer = $tokens[$condition]['scope_closer'];
+ $closer = $tokens[$lastCondition]['scope_closer'];
// If the closer for our condition is shared with other openers,
// we will need to throw errors from this token to the next
diff --git a/src/Standards/Squiz/Sniffs/Scope/MemberVarScopeSniff.php b/src/Standards/Squiz/Sniffs/Scope/MemberVarScopeSniff.php
index 2f3c525373..11292f57a2 100644
--- a/src/Standards/Squiz/Sniffs/Scope/MemberVarScopeSniff.php
+++ b/src/Standards/Squiz/Sniffs/Scope/MemberVarScopeSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Variables;
class MemberVarScopeSniff extends AbstractVariableSniff
{
@@ -26,15 +27,14 @@ class MemberVarScopeSniff extends AbstractVariableSniff
*/
protected function processMemberVar(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
- $properties = $phpcsFile->getMemberProperties($stackPtr);
-
+ $properties = Variables::getMemberProperties($phpcsFile, $stackPtr);
if ($properties === [] || $properties['scope_specified'] !== false) {
return;
}
- $error = 'Scope modifier not specified for member variable "%s"';
- $data = [$tokens[$stackPtr]['content']];
+ $tokens = $phpcsFile->getTokens();
+ $error = 'Scope modifier not specified for member variable "%s"';
+ $data = [$tokens[$stackPtr]['content']];
$phpcsFile->addError($error, $stackPtr, 'Missing', $data);
}//end processMemberVar()
diff --git a/src/Standards/Squiz/Sniffs/Scope/MethodScopeSniff.php b/src/Standards/Squiz/Sniffs/Scope/MethodScopeSniff.php
index 2e9c185eb0..7fbb1e3de5 100644
--- a/src/Standards/Squiz/Sniffs/Scope/MethodScopeSniff.php
+++ b/src/Standards/Squiz/Sniffs/Scope/MethodScopeSniff.php
@@ -11,6 +11,8 @@
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
use PHP_CodeSniffer\Util\Tokens;
class MethodScopeSniff extends AbstractScopeSniff
@@ -41,16 +43,14 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$tokens = $phpcsFile->getTokens();
// Determine if this is a function which needs to be examined.
- $conditions = $tokens[$stackPtr]['conditions'];
- end($conditions);
- $deepestScope = key($conditions);
+ $deepestScope = Conditions::getLastCondition($phpcsFile, $stackPtr);
if ($deepestScope !== $currScope) {
return;
}
- $methodName = $phpcsFile->getDeclarationName($stackPtr);
- if ($methodName === null) {
- // Ignore closures.
+ $methodName = ConstructNames::getDeclarationName($phpcsFile, $stackPtr);
+ if (empty($methodName) === true) {
+ // Live coding or parse error.
return;
}
diff --git a/src/Standards/Squiz/Sniffs/Scope/StaticThisUsageSniff.php b/src/Standards/Squiz/Sniffs/Scope/StaticThisUsageSniff.php
index b5a11b09ba..a02d8e0080 100644
--- a/src/Standards/Squiz/Sniffs/Scope/StaticThisUsageSniff.php
+++ b/src/Standards/Squiz/Sniffs/Scope/StaticThisUsageSniff.php
@@ -11,6 +11,8 @@
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
use PHP_CodeSniffer\Util\Tokens;
class StaticThisUsageSniff extends AbstractScopeSniff
@@ -42,9 +44,7 @@ public function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
$tokens = $phpcsFile->getTokens();
// Determine if this is a function which needs to be examined.
- $conditions = $tokens[$stackPtr]['conditions'];
- end($conditions);
- $deepestScope = key($conditions);
+ $deepestScope = Conditions::getLastCondition($phpcsFile, $stackPtr);
if ($deepestScope !== $currScope) {
return;
}
@@ -60,7 +60,7 @@ public function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
return;
}
- $methodProps = $phpcsFile->getMethodProperties($stackPtr);
+ $methodProps = FunctionDeclarations::getProperties($phpcsFile, $stackPtr);
if ($methodProps['is_static'] === false) {
return;
}
diff --git a/src/Standards/Squiz/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php b/src/Standards/Squiz/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php
index 4afa688400..585d3c50e8 100644
--- a/src/Standards/Squiz/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php
+++ b/src/Standards/Squiz/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
use PHP_CodeSniffer\Util\Tokens;
class ControlStructureSpacingSniff implements Sniff
@@ -292,7 +293,7 @@ public function process(File $phpcsFile, $stackPtr)
}
if ($tokens[$owner]['code'] === T_CLOSURE
- && ($phpcsFile->hasCondition($stackPtr, [T_FUNCTION, T_CLOSURE]) === true
+ && (Conditions::hasCondition($phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE]) === true
|| isset($tokens[$stackPtr]['nested_parenthesis']) === true)
) {
return;
diff --git a/src/Standards/Squiz/Sniffs/WhiteSpace/FunctionClosingBraceSpaceSniff.php b/src/Standards/Squiz/Sniffs/WhiteSpace/FunctionClosingBraceSpaceSniff.php
index d9ee5954e2..ef88b86b1f 100644
--- a/src/Standards/Squiz/Sniffs/WhiteSpace/FunctionClosingBraceSpaceSniff.php
+++ b/src/Standards/Squiz/Sniffs/WhiteSpace/FunctionClosingBraceSpaceSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
class FunctionClosingBraceSpaceSniff implements Sniff
{
@@ -83,7 +84,7 @@ public function process(File $phpcsFile, $stackPtr)
}
$nestedFunction = false;
- if ($phpcsFile->hasCondition($stackPtr, [T_FUNCTION, T_CLOSURE]) === true
+ if (Conditions::hasCondition($phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE]) === true
|| isset($tokens[$stackPtr]['nested_parenthesis']) === true
) {
$nestedFunction = true;
diff --git a/src/Standards/Squiz/Sniffs/WhiteSpace/OperatorSpacingSniff.php b/src/Standards/Squiz/Sniffs/WhiteSpace/OperatorSpacingSniff.php
index 1cfc8a6f42..8ec5152831 100644
--- a/src/Standards/Squiz/Sniffs/WhiteSpace/OperatorSpacingSniff.php
+++ b/src/Standards/Squiz/Sniffs/WhiteSpace/OperatorSpacingSniff.php
@@ -11,6 +11,8 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
+use PHP_CodeSniffer\Util\Sniffs\TokenIs;
use PHP_CodeSniffer\Util\Tokens;
class OperatorSpacingSniff implements Sniff
@@ -255,18 +257,8 @@ protected function isOperator(File $phpcsFile, $stackPtr)
if ($tokens[$stackPtr]['code'] === T_EQUAL
|| $tokens[$stackPtr]['code'] === T_MINUS
) {
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- $parenthesis = array_keys($tokens[$stackPtr]['nested_parenthesis']);
- $bracket = array_pop($parenthesis);
- if (isset($tokens[$bracket]['parenthesis_owner']) === true) {
- $function = $tokens[$bracket]['parenthesis_owner'];
- if ($tokens[$function]['code'] === T_FUNCTION
- || $tokens[$function]['code'] === T_CLOSURE
- || $tokens[$function]['code'] === T_DECLARE
- ) {
- return false;
- }
- }
+ if (Parentheses::lastOwnerIn($phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE, T_DECLARE]) !== false) {
+ return false;
}
}
@@ -282,60 +274,17 @@ protected function isOperator(File $phpcsFile, $stackPtr)
if ($tokens[$stackPtr]['code'] === T_BITWISE_AND) {
// If it's not a reference, then we expect one space either side of the
// bitwise operator.
- if ($phpcsFile->isReference($stackPtr) === true) {
+ if (TokenIs::isReference($phpcsFile, $stackPtr) === true) {
return false;
}
}
if ($tokens[$stackPtr]['code'] === T_MINUS || $tokens[$stackPtr]['code'] === T_PLUS) {
- // Check minus spacing, but make sure we aren't just assigning
- // a minus value or returning one.
- $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
- if ($tokens[$prev]['code'] === T_RETURN) {
- // Just returning a negative value; eg. (return -1).
- return false;
- }
-
- if (isset(Tokens::$operators[$tokens[$prev]['code']]) === true) {
- // Just trying to operate on a negative value; eg. ($var * -1).
- return false;
- }
-
- if (isset(Tokens::$comparisonTokens[$tokens[$prev]['code']]) === true) {
- // Just trying to compare a negative value; eg. ($var === -1).
- return false;
- }
-
- if (isset(Tokens::$booleanOperators[$tokens[$prev]['code']]) === true) {
- // Just trying to compare a negative value; eg. ($var || -1 === $b).
- return false;
- }
-
- if (isset(Tokens::$assignmentTokens[$tokens[$prev]['code']]) === true) {
- // Just trying to assign a negative value; eg. ($var = -1).
+ // Check that we aren't just assigning a minus/plus value or returning one.
+ if (TokenIs::isUnaryPlusMinus($phpcsFile, $stackPtr) === true) {
return false;
}
-
- // A list of tokens that indicate that the token is not
- // part of an arithmetic operation.
- $invalidTokens = [
- T_COMMA => true,
- T_OPEN_PARENTHESIS => true,
- T_OPEN_SQUARE_BRACKET => true,
- T_OPEN_SHORT_ARRAY => true,
- T_DOUBLE_ARROW => true,
- T_COLON => true,
- T_INLINE_THEN => true,
- T_INLINE_ELSE => true,
- T_CASE => true,
- T_OPEN_CURLY_BRACKET => true,
- ];
-
- if (isset($invalidTokens[$tokens[$prev]['code']]) === true) {
- // Just trying to use a negative value; eg. myFunction($var, -2).
- return false;
- }
- }//end if
+ }
return true;
diff --git a/src/Standards/Squiz/Sniffs/WhiteSpace/SemicolonSpacingSniff.php b/src/Standards/Squiz/Sniffs/WhiteSpace/SemicolonSpacingSniff.php
index 72a5e91b52..c820b0288e 100644
--- a/src/Standards/Squiz/Sniffs/WhiteSpace/SemicolonSpacingSniff.php
+++ b/src/Standards/Squiz/Sniffs/WhiteSpace/SemicolonSpacingSniff.php
@@ -11,6 +11,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
use PHP_CodeSniffer\Util\Tokens;
class SemicolonSpacingSniff implements Sniff
@@ -61,18 +62,10 @@ public function process(File $phpcsFile, $stackPtr)
// Detect whether this is a semi-colons for a conditions in a `for()` control structure.
$forCondition = false;
- if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
- $nestedParens = $tokens[$stackPtr]['nested_parenthesis'];
- $closeParenthesis = end($nestedParens);
-
- if (isset($tokens[$closeParenthesis]['parenthesis_owner']) === true) {
- $owner = $tokens[$closeParenthesis]['parenthesis_owner'];
-
- if ($tokens[$owner]['code'] === T_FOR) {
- $forCondition = true;
- $nonSpace = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 2), null, true);
- }
- }
+ $owner = Parentheses::getLastOwner($phpcsFile, $stackPtr);
+ if ($owner !== false && $tokens[$owner]['code'] === T_FOR) {
+ $forCondition = true;
+ $nonSpace = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 2), null, true);
}
if ($tokens[$nonSpace]['code'] === T_SEMICOLON
diff --git a/src/Standards/Squiz/Sniffs/WhiteSpace/SuperfluousWhitespaceSniff.php b/src/Standards/Squiz/Sniffs/WhiteSpace/SuperfluousWhitespaceSniff.php
index 376fbd24ba..b1e2d2d9c8 100644
--- a/src/Standards/Squiz/Sniffs/WhiteSpace/SuperfluousWhitespaceSniff.php
+++ b/src/Standards/Squiz/Sniffs/WhiteSpace/SuperfluousWhitespaceSniff.php
@@ -15,6 +15,7 @@
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
class SuperfluousWhitespaceSniff implements Sniff
{
@@ -224,7 +225,7 @@ public function process(File $phpcsFile, $stackPtr)
Check for multiple blank lines in a function.
*/
- if (($phpcsFile->hasCondition($stackPtr, [T_FUNCTION, T_CLOSURE]) === true)
+ if ((Conditions::hasCondition($phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE]) === true)
&& $tokens[($stackPtr - 1)]['line'] < $tokens[$stackPtr]['line']
&& $tokens[($stackPtr - 2)]['line'] === $tokens[($stackPtr - 1)]['line']
) {
diff --git a/src/Standards/Squiz/Tests/Arrays/ArrayDeclarationUnitTest.1.inc b/src/Standards/Squiz/Tests/Arrays/ArrayDeclarationUnitTest.1.inc
index 8c3101be77..08422605c6 100644
--- a/src/Standards/Squiz/Tests/Arrays/ArrayDeclarationUnitTest.1.inc
+++ b/src/Standards/Squiz/Tests/Arrays/ArrayDeclarationUnitTest.1.inc
@@ -1,5 +1,5 @@
1,
8 => 2,
10 => 2,
22 => 1,
@@ -116,7 +115,6 @@ public function getErrorList($testFile='')
];
case 'ArrayDeclarationUnitTest.2.inc':
return [
- 2 => 1,
10 => 1,
23 => 2,
24 => 2,
diff --git a/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc b/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc
index 0a0729a0a9..43d4f58598 100644
--- a/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc
+++ b/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc
@@ -70,8 +70,8 @@ class MyClass {
MyClass::test($value);
};
- $array = array(1,2,3);
- array_walk($array, $callback);
+ $array = MyClass::getValues(); // Non-namespaced relative, should be picked up.
+ $array = \MyClass::getValues(); // Non-namespaced absolute, should be picked up.
}
}
@@ -96,11 +96,11 @@ class Foo
namespace TYPO3\CMS\Reports;
class Status {
- const NOTICE = -2;
- const INFO = -1;
- const OK = 0;
- const WARNING = 1;
- const ERROR = 2;
+
+ function test() {
+ $array = TYPO3\CMS\Reports\Status::getValues(); // Namespaced relative, should NOT be picked up.
+ $array = \TYPO3\CMS\Reports\Status::getValues(); // Namespaced absolute, should be picked up.
+ }
}
namespace TYPO3\CMS\Reports\Report\Status;
@@ -159,7 +159,7 @@ class Nested_Anon_Class {
namespace Foo\Baz {
class BarFoo {
public function foo() {
- echo Foo\Baz\BarFoo::$prop;
+ echo \Foo\Baz\BarFoo::$prop;
}
}
}
@@ -168,7 +168,43 @@ namespace Foo\Baz {
namespace Foo /*comment*/ \ Bah {
class BarFoo {
public function foo() {
- echo Foo \ /*comment*/ Bah\BarFoo::$prop;
+ echo \Foo \ /*comment*/ Bah\BarFoo::$prop;
}
}
}
+
+namespace FoolBah;
+
+$a = namespace\functionCall();
+
+class Bah {
+
+ function myFunction()
+ {
+ \FoolBah\Whatever::something();
+ \FoolBah\Bah::something();
+ FoolBah\Bah::something(); // Calls \FoolBah\FoolBah\Bah::something() !
+ Bah::something();
+ }
+}
+
+namespace BahHumbug ?>
+
+
+
+ 1,
32 => 2,
40 => 2,
+ 73 => 1,
+ 74 => 1,
92 => 1,
+ 102 => 1,
121 => 1,
132 => 1,
139 => 3,
@@ -41,6 +44,10 @@ public function getErrorList()
143 => 2,
162 => 1,
171 => 1,
+ 185 => 1,
+ 187 => 1,
+ 199 => 1,
+ 207 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.inc b/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.inc
index 709b5f6ae4..1396d8c78b 100644
--- a/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.inc
+++ b/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.inc
@@ -242,3 +242,26 @@ $y = 10 + /* test */ -2;
* See: {@link https://github.com/squizlabs/PHP_CodeSniffer/issues/1918}
* @phpcs:disable Standard.Category.Sniff
*/
+
+/*
+ * block comment with leading asterixes
+ * no capital letter.
+ */
+
+/*
+ - List comment, correctly capitalized.
+ - Another list item, correctly capitalized.
+*/
+
+/*
+ - list comment, no capital letter.
+*/
+
+/*
+ * - List comment, correctly capitalized.
+ * - Another list item, correctly capitalized.
+ */
+
+/*
+ * - list comment with leading asterixes, no capital letter
+ */
diff --git a/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.inc.fixed b/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.inc.fixed
index 8038ecb97b..d8ffce07e0 100644
--- a/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.inc.fixed
+++ b/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.inc.fixed
@@ -244,3 +244,26 @@ $y = 10 + /* test */ -2;
* See: {@link https://github.com/squizlabs/PHP_CodeSniffer/issues/1918}
* @phpcs:disable Standard.Category.Sniff
*/
+
+/*
+ * block comment with leading asterixes
+ * no capital letter.
+ */
+
+/*
+ - List comment, correctly capitalized.
+ - Another list item, correctly capitalized.
+*/
+
+/*
+ - list comment, no capital letter.
+*/
+
+/*
+ * - List comment, correctly capitalized.
+ * - Another list item, correctly capitalized.
+ */
+
+/*
+ * - list comment with leading asterixes, no capital letter
+ */
diff --git a/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.php b/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.php
index 113c987639..c256b04855 100644
--- a/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.php
+++ b/src/Standards/Squiz/Tests/Commenting/BlockCommentUnitTest.php
@@ -75,6 +75,9 @@ public function getErrorList()
227 => 1,
232 => 1,
233 => 1,
+ 247 => 1,
+ 257 => 1,
+ 266 => 1,
];
return $errors;
diff --git a/src/Standards/Squiz/Tests/Commenting/ClassCommentUnitTest.inc b/src/Standards/Squiz/Tests/Commenting/ClassCommentUnitTest.inc
index eefa44c9bb..e09f839e97 100644
--- a/src/Standards/Squiz/Tests/Commenting/ClassCommentUnitTest.inc
+++ b/src/Standards/Squiz/Tests/Commenting/ClassCommentUnitTest.inc
@@ -123,3 +123,5 @@ class Space_At_end
{
}//end class
+
+class NoCommentConfusedWithTrailingEndComment {}
diff --git a/src/Standards/Squiz/Tests/Commenting/ClassCommentUnitTest.php b/src/Standards/Squiz/Tests/Commenting/ClassCommentUnitTest.php
index 80a404be07..e2c6463631 100644
--- a/src/Standards/Squiz/Tests/Commenting/ClassCommentUnitTest.php
+++ b/src/Standards/Squiz/Tests/Commenting/ClassCommentUnitTest.php
@@ -26,10 +26,11 @@ class ClassCommentUnitTest extends AbstractSniffUnitTest
public function getErrorList()
{
return [
- 2 => 1,
- 15 => 1,
- 31 => 1,
- 54 => 1,
+ 2 => 1,
+ 15 => 1,
+ 31 => 1,
+ 54 => 1,
+ 127 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.inc b/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.inc
index 02752af523..49649f7b83 100644
--- a/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.inc
+++ b/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.inc
@@ -482,5 +482,12 @@ namespace {
public function ThrowGlobalExceptionToo() {
throw new SomeGlobalException();
}
+
+ /**
+ * Missing throws tag.
+ */
+ public /* comment */ function ThrowGlobalExceptionToo() {
+ throw new SomeGlobalException();
+ }
}
}
diff --git a/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.php b/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.php
index 1e2d07fafb..3e24c60101 100644
--- a/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.php
+++ b/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.php
@@ -37,6 +37,7 @@ public function getErrorList()
219 => 1,
287 => 1,
397 => 1,
+ 488 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.inc b/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.inc
index 1a7020003f..8255efb1b7 100644
--- a/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.inc
+++ b/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.inc
@@ -1000,3 +1000,33 @@ public function foo(object $a, ?object $b) {}
* @return void
*/
function foo($foo) {}
+
+// phpcs:set Squiz.Commenting.FunctionComment typeFormat short
+
+/**
+ * Test short form type preference.
+ *
+ * @param integer $fooLong Comment.
+ * @param boolean $barLong Comment.
+ * @param int $fooShort Comment.
+ * @param bool $barShort Comment.
+ *
+ * @return void
+ */
+public function shortForm($fooLong, $barLong, $fooShort, $barShort) {}
+
+// phpcs:set Squiz.Commenting.FunctionComment typeFormat long
+
+/**
+ * Description
+ *
+ * @return (int|bool)[]
+ */
+function MultiTypeArrayReturn() {
+ return [];
+}
+
+class FooBar {/** Unrelated trailing comment using wrong comment style. */
+
+ public static function NoDocblock() {}
+}
diff --git a/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.inc.fixed b/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.inc.fixed
index 4d91ecdcab..c51fe1c804 100644
--- a/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.inc.fixed
+++ b/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.inc.fixed
@@ -871,7 +871,7 @@ function returnTypeWithDescriptionD()
/**
* Yield from test
*
- * @return int[]
+ * @return integer[]
*/
function yieldFromTest()
{
@@ -1000,3 +1000,33 @@ public function foo(object $a, ?object $b) {}
* @return void
*/
function foo($foo) {}
+
+// phpcs:set Squiz.Commenting.FunctionComment typeFormat short
+
+/**
+ * Test short form type preference.
+ *
+ * @param int $fooLong Comment.
+ * @param bool $barLong Comment.
+ * @param int $fooShort Comment.
+ * @param bool $barShort Comment.
+ *
+ * @return void
+ */
+public function shortForm($fooLong, $barLong, $fooShort, $barShort) {}
+
+// phpcs:set Squiz.Commenting.FunctionComment typeFormat long
+
+/**
+ * Description
+ *
+ * @return (integer|boolean)[]
+ */
+function MultiTypeArrayReturn() {
+ return [];
+}
+
+class FooBar {/** Unrelated trailing comment using wrong comment style. */
+
+ public static function NoDocblock() {}
+}
diff --git a/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.php b/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.php
index e8e02f8496..5e3329f540 100644
--- a/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.php
+++ b/src/Standards/Squiz/Tests/Commenting/FunctionCommentUnitTest.php
@@ -26,93 +26,98 @@ class FunctionCommentUnitTest extends AbstractSniffUnitTest
public function getErrorList()
{
$errors = [
- 5 => 1,
- 10 => 3,
- 12 => 2,
- 13 => 2,
- 14 => 1,
- 15 => 1,
- 28 => 1,
- 43 => 1,
- 76 => 1,
- 87 => 1,
- 103 => 1,
- 109 => 1,
- 112 => 1,
- 122 => 1,
- 123 => 3,
- 124 => 2,
- 125 => 1,
- 126 => 1,
- 137 => 4,
- 138 => 4,
- 139 => 4,
- 143 => 2,
- 152 => 1,
- 155 => 2,
- 159 => 1,
- 166 => 1,
- 173 => 1,
- 183 => 1,
- 190 => 2,
- 193 => 2,
- 196 => 1,
- 199 => 2,
- 210 => 1,
- 211 => 1,
- 222 => 1,
- 223 => 1,
- 224 => 1,
- 225 => 1,
- 226 => 1,
- 227 => 1,
- 230 => 2,
- 232 => 2,
- 246 => 1,
- 248 => 4,
- 261 => 1,
- 263 => 1,
- 276 => 1,
- 277 => 1,
- 278 => 1,
- 279 => 1,
- 280 => 1,
- 281 => 1,
- 284 => 1,
- 286 => 7,
- 294 => 1,
- 302 => 1,
- 312 => 1,
- 358 => 1,
- 359 => 2,
- 372 => 1,
- 373 => 1,
- 387 => 1,
- 407 => 1,
- 441 => 1,
- 500 => 1,
- 526 => 1,
- 548 => 1,
- 641 => 1,
- 669 => 1,
- 688 => 1,
- 744 => 1,
- 748 => 1,
- 767 => 1,
- 789 => 1,
- 792 => 1,
- 794 => 1,
- 797 => 1,
- 801 => 1,
- 828 => 1,
- 840 => 1,
- 852 => 1,
- 864 => 1,
- 886 => 1,
- 888 => 1,
- 890 => 1,
- 978 => 1,
- 997 => 1,
+ 5 => 1,
+ 10 => 3,
+ 12 => 2,
+ 13 => 2,
+ 14 => 1,
+ 15 => 1,
+ 28 => 1,
+ 43 => 1,
+ 76 => 1,
+ 87 => 1,
+ 103 => 1,
+ 109 => 1,
+ 112 => 1,
+ 122 => 1,
+ 123 => 3,
+ 124 => 2,
+ 125 => 1,
+ 126 => 1,
+ 137 => 4,
+ 138 => 4,
+ 139 => 4,
+ 143 => 2,
+ 152 => 1,
+ 155 => 2,
+ 159 => 1,
+ 166 => 1,
+ 173 => 1,
+ 183 => 1,
+ 190 => 2,
+ 193 => 2,
+ 196 => 1,
+ 199 => 2,
+ 210 => 1,
+ 211 => 1,
+ 222 => 1,
+ 223 => 1,
+ 224 => 1,
+ 225 => 1,
+ 226 => 1,
+ 227 => 1,
+ 230 => 2,
+ 232 => 2,
+ 246 => 1,
+ 248 => 4,
+ 261 => 1,
+ 263 => 1,
+ 276 => 1,
+ 277 => 1,
+ 278 => 1,
+ 279 => 1,
+ 280 => 1,
+ 281 => 1,
+ 284 => 1,
+ 286 => 7,
+ 294 => 1,
+ 302 => 1,
+ 312 => 1,
+ 358 => 1,
+ 359 => 2,
+ 372 => 1,
+ 373 => 1,
+ 387 => 1,
+ 407 => 1,
+ 441 => 1,
+ 500 => 1,
+ 526 => 1,
+ 548 => 1,
+ 641 => 1,
+ 669 => 1,
+ 688 => 1,
+ 744 => 1,
+ 748 => 1,
+ 767 => 1,
+ 789 => 1,
+ 792 => 1,
+ 794 => 1,
+ 797 => 1,
+ 801 => 1,
+ 828 => 1,
+ 840 => 1,
+ 852 => 1,
+ 864 => 1,
+ 874 => 1,
+ 886 => 1,
+ 888 => 1,
+ 890 => 1,
+ 978 => 1,
+ 997 => 1,
+ 1009 => 1,
+ 1010 => 1,
+ 1023 => 1,
+ 1031 => 1,
];
// Scalar type hints only work from PHP 7 onwards.
@@ -128,6 +133,7 @@ public function getErrorList()
$errors[575] = 2;
$errors[627] = 1;
$errors[1002] = 1;
+ $errors[1016] = 4;
} else {
$errors[729] = 4;
$errors[740] = 2;
diff --git a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc
index 0eedb686a1..a5e88db052 100644
--- a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc
+++ b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc
@@ -139,7 +139,7 @@ class VariableCommentUnitTest
/**
- * Var type checking (array(int => string) v.s. array(int => string)).
+ * Var type checking (array(int => string) v.s. array(integer => string)).
*
* @var array(int => string)
*/
@@ -343,3 +343,45 @@ class Foo
public static $variableName = array();
}
+
+// phpcs:set Squiz.Commenting.VariableComment typeFormat short
+class ShortForm {
+
+ /**
+ * @var boolean
+ */
+ public $fooLong = true;
+
+ /**
+ * @var bool
+ */
+ public $fooShort = true;
+
+ /**
+ * @var integer
+ */
+ public $barLong = 1;
+
+ /**
+ * @var int
+ */
+ public $barShort = 1;
+}
+// phpcs:set Squiz.Commenting.VariableComment typeFormat long
+
+class PSR5Types {
+ /**
+ * @var bool|int
+ */
+ public $foo = true;
+
+ /**
+ * @var real[]
+ */
+ public $bar = 1.5;
+}
+
+class CommentConfusion { /** This is a trailing end comment, not a property comment. */
+
+ public $prop;
+}
diff --git a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc.fixed b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc.fixed
index c1af18faed..959f2879c5 100644
--- a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc.fixed
+++ b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc.fixed
@@ -139,7 +139,7 @@ class VariableCommentUnitTest
/**
- * Var type checking (array(int => string) v.s. array(int => string)).
+ * Var type checking (array(int => string) v.s. array(integer => string)).
*
* @var array(integer => string)
*/
@@ -343,3 +343,45 @@ class Foo
public static $variableName = array();
}
+
+// phpcs:set Squiz.Commenting.VariableComment typeFormat short
+class ShortForm {
+
+ /**
+ * @var bool
+ */
+ public $fooLong = true;
+
+ /**
+ * @var bool
+ */
+ public $fooShort = true;
+
+ /**
+ * @var int
+ */
+ public $barLong = 1;
+
+ /**
+ * @var int
+ */
+ public $barShort = 1;
+}
+// phpcs:set Squiz.Commenting.VariableComment typeFormat long
+
+class PSR5Types {
+ /**
+ * @var boolean|integer
+ */
+ public $foo = true;
+
+ /**
+ * @var float[]
+ */
+ public $bar = 1.5;
+}
+
+class CommentConfusion { /** This is a trailing end comment, not a property comment. */
+
+ public $prop;
+}
diff --git a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.php b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.php
index 7e9d1dcd8c..a8c2b7572c 100644
--- a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.php
+++ b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.php
@@ -56,6 +56,11 @@ public function getErrorList()
294 => 1,
311 => 1,
336 => 1,
+ 351 => 1,
+ 361 => 1,
+ 374 => 1,
+ 379 => 1,
+ 386 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.inc b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.inc
index 30a2b98fbe..5e16b07926 100644
--- a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.inc
+++ b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.inc
@@ -167,3 +167,9 @@ $foo = $bar ?? $baz ?? '';
$foo = $myString{-1};
$value = (binary) $blah + b"binary $foo";
+
+$value = +
+ 1.1;
+
+$value = + /*comment*/
+ 1.1;
diff --git a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.inc.fixed b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.inc.fixed
index 409091db62..0703e98de3 100644
--- a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.inc.fixed
+++ b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.inc.fixed
@@ -22,7 +22,7 @@ $value = ($one + ($two + $three));
$value++;
$value--;
$value = -1;
-$value = - 1;
+$value = -1;
$value = (1 + 2);
$value = (1 + 2);
@@ -106,7 +106,7 @@ class MyClass
}//end class
if ($index < -1) $index = 0;
-if ($index < - 1) $index = 0;
+if ($index < -1) $index = 0;
$three = ceil($one / $two);
$three = ceil(($one / $two) / $four);
@@ -127,7 +127,7 @@ $expectedPermission = array(
'denied' => 1,
'cascade' => TRUE,
'blockergranted' => 2,
- 'blockerdenied' => - 3,
+ 'blockerdenied' => -3,
'effective' => TRUE,
);
@@ -167,3 +167,8 @@ $foo = ($bar ?? $baz ?? '');
$foo = $myString{-1};
$value = ((binary) $blah + b"binary $foo");
+
+$value = +1.1;
+
+$value = + /*comment*/
+ 1.1;
diff --git a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.js b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.js
index 92ed8038a3..f530c5d3c6 100644
--- a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.js
+++ b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.js
@@ -116,3 +116,6 @@ if (something === true
if (true === /^\d*\.?\d*$/.test(input)) return true;
if ( ! /^(?:a|select)$/i.test( element.tagName ) ) return true;
+
+value = +
+ 1;
diff --git a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.js.fixed b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.js.fixed
index 04e35d977c..e93046696e 100644
--- a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.js.fixed
+++ b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.js.fixed
@@ -21,7 +21,7 @@ value = one + (two + three);
value++;
value--;
value = -1;
-value = - 1;
+value = -1;
value = (1 + 2);
value = 1 + 2;
@@ -60,7 +60,7 @@ function myFunction()
params['mode'] = id.replace(/WidgetType/, '');
if (index < -1) index = 0;
-if (index < - 1) index = 0;
+if (index < -1) index = 0;
var classN = prvId.replace(/\./g, '-');
@@ -116,3 +116,5 @@ if (something === true
if (true === /^\d*\.?\d*$/.test(input)) return true;
if ( ! /^(?:a|select)$/i.test( element.tagName ) ) return true;
+
+value = +1;
diff --git a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.php b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.php
index 651319b877..a8005e508e 100644
--- a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.php
+++ b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.php
@@ -66,6 +66,8 @@ public function getErrorList($testFile='OperatorBracketUnitTest.inc')
163 => 2,
165 => 2,
169 => 1,
+ 171 => 1,
+ 174 => 1,
];
break;
case 'OperatorBracketUnitTest.js':
@@ -83,8 +85,9 @@ public function getErrorList($testFile='OperatorBracketUnitTest.inc')
47 => 1,
63 => 1,
108 => 1,
+ 120 => 1,
];
- break;
+ break;
default:
return [];
break;
diff --git a/src/Standards/Squiz/Tests/Functions/GlobalFunctionUnitTest.inc b/src/Standards/Squiz/Tests/Functions/GlobalFunctionUnitTest.inc
index b7b7f205c7..3c43633be8 100644
--- a/src/Standards/Squiz/Tests/Functions/GlobalFunctionUnitTest.inc
+++ b/src/Standards/Squiz/Tests/Functions/GlobalFunctionUnitTest.inc
@@ -8,10 +8,13 @@ class MyClass
interface MyInterface
{
- function func1() {}
+ function func1();
}
function __autoload($class) {}
echo preg_replace_callback('~-([a-z])~', function ($match) { return strtoupper($match[1]); }, 'hello-world');
-?>
+
+if ( ! function_exists('conditionalFunction') ) {
+ function conditionalFunction() {}
+}
diff --git a/src/Standards/Squiz/Tests/Functions/GlobalFunctionUnitTest.php b/src/Standards/Squiz/Tests/Functions/GlobalFunctionUnitTest.php
index 7be76488e6..b425b18fe9 100644
--- a/src/Standards/Squiz/Tests/Functions/GlobalFunctionUnitTest.php
+++ b/src/Standards/Squiz/Tests/Functions/GlobalFunctionUnitTest.php
@@ -40,7 +40,10 @@ public function getErrorList()
*/
public function getWarningList()
{
- return [2 => 1];
+ return [
+ 2 => 1,
+ 19 => 1,
+ ];
}//end getWarningList()
diff --git a/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.inc b/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.inc
index 4d574ef32f..f638ac883c 100644
--- a/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.inc
+++ b/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.inc
@@ -34,7 +34,7 @@ $var = myFunction(
$this,
!$directLinks,
FALSE,
- );
+ ),
);
for ($node = $fieldsTag->nextSibling; $node; $node = $node->nextSibling) {
@@ -54,3 +54,16 @@ $a = $b === true ? $c : $d;
$this->_args = $this->_getArgs(($_SERVER['argv'] ?? []));
$args = ($_SERVER['argv'] ?? []);
+
+function ignoreMe($a = !SOME_CONSTANT){}
+$closure = function ($a = !SOME_CONSTANT){};
+
+$var = /* comment */ myFunction(!$var);
+$var = namespace\myFunction(!$var);
+$var = self::myFunction(!$var);
+$var = myFunction($var) || $bar;
+
+$compound = $array + array(!$something);
+$compound = $array !== array($something);
+$compound = $array + [!$something];
+$compound = $array !== [$something];
diff --git a/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.php b/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.php
index c9bb3da60f..1696d93c0b 100644
--- a/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.php
+++ b/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.php
@@ -34,6 +34,9 @@ public function getErrorList()
10 => 1,
52 => 1,
53 => 1,
+ 64 => 1,
+ 67 => 1,
+ 69 => 1,
];
}//end getErrorList()
diff --git a/src/Standards/Squiz/Tests/Scope/MemberVarScopeUnitTest.inc b/src/Standards/Squiz/Tests/Scope/MemberVarScopeUnitTest.inc
index d8e3069e88..23a5548efe 100644
--- a/src/Standards/Squiz/Tests/Scope/MemberVarScopeUnitTest.inc
+++ b/src/Standards/Squiz/Tests/Scope/MemberVarScopeUnitTest.inc
@@ -22,8 +22,8 @@ class Foo {
}//end class
class MyClass {
- $anonFunc = function($foo) use ($bar) {};
- public $anonFunc2 = function($foo2) use ($bar2) {};
+ $anonFunc = function($foo) use ($bar) {}; // Intentional parse error.
+ public $anonFunc2 = function($foo2) use ($bar2) {}; // Intentional parse error.
public function method($var1, $var2) {
$anon = new class {
$p1 = null;
diff --git a/src/Standards/Squiz/Tests/Scope/MemberVarScopeUnitTest.php b/src/Standards/Squiz/Tests/Scope/MemberVarScopeUnitTest.php
index 59e49910df..782a93c765 100644
--- a/src/Standards/Squiz/Tests/Scope/MemberVarScopeUnitTest.php
+++ b/src/Standards/Squiz/Tests/Scope/MemberVarScopeUnitTest.php
@@ -27,7 +27,7 @@ public function getErrorList()
{
return [
7 => 1,
- 25 => 1,
+ 25 => 3,
29 => 1,
33 => 1,
39 => 1,
@@ -49,7 +49,7 @@ public function getErrorList()
*/
public function getWarningList()
{
- return [71 => 1];
+ return [];
}//end getWarningList()
diff --git a/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc b/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc
index 4ae1fd0340..575bc2193b 100644
--- a/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc
+++ b/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc
@@ -11,7 +11,7 @@ class MyClass
}//end class
-interface MyInterface
+trait MyTrait
{
public $var1 = 'value';
@@ -45,7 +45,7 @@ class MyClass
}//end class
-interface MyInterface
+trait MyTrait
{
public $var1 = 'value';
function myFunction();
@@ -202,7 +202,7 @@ class MyClass
}//end class
-interface MyInterface
+trait MyTrait
{
/* testing */
diff --git a/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc.fixed b/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc.fixed
index 38324473b4..b07caba8e6 100644
--- a/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc.fixed
+++ b/src/Standards/Squiz/Tests/WhiteSpace/MemberVarSpacingUnitTest.inc.fixed
@@ -11,7 +11,7 @@ class MyClass
}//end class
-interface MyInterface
+trait MyTrait
{
public $var1 = 'value';
@@ -43,7 +43,7 @@ class MyClass
}//end class
-interface MyInterface
+trait MyTrait
{
public $var1 = 'value';
@@ -199,7 +199,7 @@ class MyClass
}//end class
-interface MyInterface
+trait MyTrait
{
/* testing */
public $var1 = 'value';
diff --git a/src/Standards/Squiz/Tests/WhiteSpace/OperatorSpacingUnitTest.inc b/src/Standards/Squiz/Tests/WhiteSpace/OperatorSpacingUnitTest.inc
index e50c094948..09010986c7 100644
--- a/src/Standards/Squiz/Tests/WhiteSpace/OperatorSpacingUnitTest.inc
+++ b/src/Standards/Squiz/Tests/WhiteSpace/OperatorSpacingUnitTest.inc
@@ -260,3 +260,5 @@ function bar(): array {}
if ($line{-1} === ':') {
$line = substr($line, 0, -1);
}
+
+$a = (bool) -1;
diff --git a/src/Standards/Squiz/Tests/WhiteSpace/OperatorSpacingUnitTest.inc.fixed b/src/Standards/Squiz/Tests/WhiteSpace/OperatorSpacingUnitTest.inc.fixed
index 7d61c83ed9..cc33ec1e28 100644
--- a/src/Standards/Squiz/Tests/WhiteSpace/OperatorSpacingUnitTest.inc.fixed
+++ b/src/Standards/Squiz/Tests/WhiteSpace/OperatorSpacingUnitTest.inc.fixed
@@ -254,3 +254,5 @@ function bar(): array {}
if ($line{-1} === ':') {
$line = substr($line, 0, -1);
}
+
+$a = (bool) -1;
diff --git a/src/Standards/Zend/Sniffs/Debug/CodeAnalyzerSniff.php b/src/Standards/Zend/Sniffs/Debug/CodeAnalyzerSniff.php
index 9071209782..d18c947294 100644
--- a/src/Standards/Zend/Sniffs/Debug/CodeAnalyzerSniff.php
+++ b/src/Standards/Zend/Sniffs/Debug/CodeAnalyzerSniff.php
@@ -39,6 +39,7 @@ public function register()
* the token was found.
*
* @return int
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If ZendCodeAnalyzer could not be run.
*/
public function process(File $phpcsFile, $stackPtr)
{
diff --git a/src/Standards/Zend/Sniffs/NamingConventions/ValidVariableNameSniff.php b/src/Standards/Zend/Sniffs/NamingConventions/ValidVariableNameSniff.php
index ea98fd2847..3de8f76a99 100644
--- a/src/Standards/Zend/Sniffs/NamingConventions/ValidVariableNameSniff.php
+++ b/src/Standards/Zend/Sniffs/NamingConventions/ValidVariableNameSniff.php
@@ -10,8 +10,10 @@
namespace PHP_CodeSniffer\Standards\Zend\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
-use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHP_CodeSniffer\Util\Sniffs\Variables;
use PHP_CodeSniffer\Util\Tokens;
class ValidVariableNameSniff extends AbstractVariableSniff
@@ -29,11 +31,10 @@ class ValidVariableNameSniff extends AbstractVariableSniff
*/
protected function processVariable(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
- $varName = ltrim($tokens[$stackPtr]['content'], '$');
+ $tokens = $phpcsFile->getTokens();
// If it's a php reserved var, then its ok.
- if (isset($this->phpReservedVars[$varName]) === true) {
+ if (Variables::isPHPReservedVarName($tokens[$stackPtr]['content']) === true) {
return;
}
@@ -56,7 +57,7 @@ protected function processVariable(File $phpcsFile, $stackPtr)
$objVarName = substr($objVarName, 1);
}
- if (Common::isCamelCaps($objVarName, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($objVarName, false, true, false) === false) {
$error = 'Variable "%s" is not in valid camel caps format';
$data = [$originalVarName];
$phpcsFile->addError($error, $var, 'NotCamelCaps', $data);
@@ -72,6 +73,7 @@ protected function processVariable(File $phpcsFile, $stackPtr)
// There is no way for us to know if the var is public or private,
// so we have to ignore a leading underscore if there is one and just
// check the main part of the variable name.
+ $varName = ltrim($tokens[$stackPtr]['content'], '$');
$originalVarName = $varName;
if (substr($varName, 0, 1) === '_') {
$objOperator = $phpcsFile->findPrevious([T_WHITESPACE], ($stackPtr - 1), null, true);
@@ -80,7 +82,7 @@ protected function processVariable(File $phpcsFile, $stackPtr)
// this: MyClass::$_variable, so we don't know its scope.
$inClass = true;
} else {
- $inClass = $phpcsFile->hasCondition($stackPtr, Tokens::$ooScopeTokens);
+ $inClass = Conditions::hasCondition($phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
}
if ($inClass === true) {
@@ -88,7 +90,7 @@ protected function processVariable(File $phpcsFile, $stackPtr)
}
}
- if (Common::isCamelCaps($varName, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($varName, false, true, false) === false) {
$error = 'Variable "%s" is not in valid camel caps format';
$data = [$originalVarName];
$phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $data);
@@ -112,15 +114,14 @@ protected function processVariable(File $phpcsFile, $stackPtr)
*/
protected function processMemberVar(File $phpcsFile, $stackPtr)
{
- $tokens = $phpcsFile->getTokens();
- $varName = ltrim($tokens[$stackPtr]['content'], '$');
- $memberProps = $phpcsFile->getMemberProperties($stackPtr);
+ $memberProps = Variables::getMemberProperties($phpcsFile, $stackPtr);
if (empty($memberProps) === true) {
- // Exception encountered.
return;
}
- $public = ($memberProps['scope'] === 'public');
+ $tokens = $phpcsFile->getTokens();
+ $varName = ltrim($tokens[$stackPtr]['content'], '$');
+ $public = ($memberProps['scope'] === 'public');
if ($public === true) {
if (substr($varName, 0, 1) === '_') {
@@ -143,7 +144,7 @@ protected function processMemberVar(File $phpcsFile, $stackPtr)
// Remove a potential underscore prefix for testing CamelCaps.
$varName = ltrim($varName, '_');
- if (Common::isCamelCaps($varName, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($varName, false, true, false) === false) {
$error = 'Member variable "%s" is not in valid camel caps format';
$data = [$varName];
$phpcsFile->addError($error, $stackPtr, 'MemberVarNotCamelCaps', $data);
@@ -172,11 +173,11 @@ protected function processVariableInString(File $phpcsFile, $stackPtr)
if (preg_match_all('|[^\\\]\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)|', $tokens[$stackPtr]['content'], $matches) !== 0) {
foreach ($matches[1] as $varName) {
// If it's a php reserved var, then its ok.
- if (isset($this->phpReservedVars[$varName]) === true) {
+ if (Variables::isPHPReservedVarName($varName) === true) {
continue;
}
- if (Common::isCamelCaps($varName, false, true, false) === false) {
+ if (ConstructNames::isCamelCaps($varName, false, true, false) === false) {
$error = 'Variable "%s" is not in valid camel caps format';
$data = [$varName];
$phpcsFile->addError($error, $stackPtr, 'StringVarNotCamelCaps', $data);
diff --git a/src/Standards/Zend/Tests/NamingConventions/ValidVariableNameUnitTest.inc b/src/Standards/Zend/Tests/NamingConventions/ValidVariableNameUnitTest.inc
index 1bf486cab6..afce96a975 100644
--- a/src/Standards/Zend/Tests/NamingConventions/ValidVariableNameUnitTest.inc
+++ b/src/Standards/Zend/Tests/NamingConventions/ValidVariableNameUnitTest.inc
@@ -102,7 +102,7 @@ var_dump($http_response_header);
var_dump($HTTP_RAW_POST_DATA);
var_dump($php_errormsg);
-interface Base
+trait Base
{
protected $anonymous;
diff --git a/src/Standards/Zend/Tests/NamingConventions/ValidVariableNameUnitTest.php b/src/Standards/Zend/Tests/NamingConventions/ValidVariableNameUnitTest.php
index d22b24fd3f..b5fcfee57f 100644
--- a/src/Standards/Zend/Tests/NamingConventions/ValidVariableNameUnitTest.php
+++ b/src/Standards/Zend/Tests/NamingConventions/ValidVariableNameUnitTest.php
@@ -51,6 +51,7 @@ public function getErrorList()
79 => 1,
96 => 1,
99 => 1,
+ 107 => 1,
113 => 1,
116 => 1,
];
@@ -69,22 +70,21 @@ public function getErrorList()
public function getWarningList()
{
return [
- 6 => 1,
- 14 => 1,
- 20 => 1,
- 26 => 1,
- 32 => 1,
- 39 => 1,
- 45 => 1,
- 51 => 1,
- 64 => 1,
- 70 => 1,
- 73 => 1,
- 76 => 1,
- 79 => 1,
- 82 => 1,
- 94 => 1,
- 107 => 1,
+ 6 => 1,
+ 14 => 1,
+ 20 => 1,
+ 26 => 1,
+ 32 => 1,
+ 39 => 1,
+ 45 => 1,
+ 51 => 1,
+ 64 => 1,
+ 70 => 1,
+ 73 => 1,
+ 76 => 1,
+ 79 => 1,
+ 82 => 1,
+ 94 => 1,
];
}//end getWarningList()
diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php
index e52dc43e27..96b27e930e 100644
--- a/src/Tokenizers/PHP.php
+++ b/src/Tokenizers/PHP.php
@@ -285,7 +285,7 @@ class PHP extends Tokenizer
/**
* Known lengths of tokens.
*
- * @var array
+ * @var array
*/
public $knownLengths = [
T_ABSTRACT => 8,
diff --git a/src/Tokenizers/Tokenizer.php b/src/Tokenizers/Tokenizer.php
index bc904586ca..a9d3fd4050 100644
--- a/src/Tokenizers/Tokenizer.php
+++ b/src/Tokenizers/Tokenizer.php
@@ -9,7 +9,7 @@
namespace PHP_CodeSniffer\Tokenizers;
-use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Exceptions\TokenizerException;
use PHP_CodeSniffer\Util;
abstract class Tokenizer
@@ -60,7 +60,7 @@ abstract class Tokenizer
/**
* Known lengths of tokens.
*
- * @var array
+ * @var array
*/
public $knownLengths = [];
@@ -879,6 +879,7 @@ private function createScopeMap()
* @param int $ignore How many curly braces we are ignoring.
*
* @return int The position in the stack that closed the scope.
+ * @throws \PHP_CodeSniffer\Exceptions\TokenizerException If the nesting level gets too deep.
*/
private function recurseScopeMap($stackPtr, $depth=1, &$ignore=0)
{
@@ -1215,7 +1216,7 @@ private function recurseScopeMap($stackPtr, $depth=1, &$ignore=0)
echo '* reached maximum nesting level; aborting *'.PHP_EOL;
}
- throw new RuntimeException('Maximum nesting level reached; file could not be processed');
+ throw new TokenizerException('Maximum nesting level reached; file could not be processed');
}
$oldDepth = $depth;
diff --git a/src/Util/Common.php b/src/Util/Common.php
index e65c923ae7..eaed5eb519 100644
--- a/src/Util/Common.php
+++ b/src/Util/Common.php
@@ -16,6 +16,8 @@ class Common
* An array of variable types for param/var we will check.
*
* @var string[]
+ *
+ * @deprecated 3.5.0 Use PHP_CodeSniffer\Util\Sniffs\Comments::$allowedTypes instead.
*/
public static $allowedTypes = [
'array',
@@ -279,6 +281,8 @@ public static function prepareForOutput($content, $exclude=[])
* for acronyms.
*
* @return boolean
+ *
+ * @deprecated 3.5.0 Use PHP_CodeSniffer\Util\Sniffs\ConstructNames::isCamelCaps() instead.
*/
public static function isCamelCaps(
$string,
@@ -286,61 +290,7 @@ public static function isCamelCaps(
$public=true,
$strict=true
) {
- // Check the first character first.
- if ($classFormat === false) {
- $legalFirstChar = '';
- if ($public === false) {
- $legalFirstChar = '[_]';
- }
-
- if ($strict === false) {
- // Can either start with a lowercase letter, or multiple uppercase
- // in a row, representing an acronym.
- $legalFirstChar .= '([A-Z]{2,}|[a-z])';
- } else {
- $legalFirstChar .= '[a-z]';
- }
- } else {
- $legalFirstChar = '[A-Z]';
- }
-
- if (preg_match("/^$legalFirstChar/", $string) === 0) {
- return false;
- }
-
- // Check that the name only contains legal characters.
- $legalChars = 'a-zA-Z0-9';
- if (preg_match("|[^$legalChars]|", substr($string, 1)) > 0) {
- return false;
- }
-
- if ($strict === true) {
- // Check that there are not two capital letters next to each other.
- $length = strlen($string);
- $lastCharWasCaps = $classFormat;
-
- for ($i = 1; $i < $length; $i++) {
- $ascii = ord($string{$i});
- if ($ascii >= 48 && $ascii <= 57) {
- // The character is a number, so it cant be a capital.
- $isCaps = false;
- } else {
- if (strtoupper($string{$i}) === $string{$i}) {
- $isCaps = true;
- } else {
- $isCaps = false;
- }
- }
-
- if ($isCaps === true && $lastCharWasCaps === true) {
- return false;
- }
-
- $lastCharWasCaps = $isCaps;
- }
- }//end if
-
- return true;
+ return Sniffs\ConstructNames::isCamelCaps($string, $classFormat, $public, $strict);
}//end isCamelCaps()
@@ -351,34 +301,12 @@ public static function isCamelCaps(
* @param string $string The string to verify.
*
* @return boolean
+ *
+ * @deprecated 3.5.0 Use PHP_CodeSniffer\Util\Sniffs\ConstructNames::isUnderscoreName() instead.
*/
public static function isUnderscoreName($string)
{
- // If there are space in the name, it can't be valid.
- if (strpos($string, ' ') !== false) {
- return false;
- }
-
- $validName = true;
- $nameBits = explode('_', $string);
-
- if (preg_match('|^[A-Z]|', $string) === 0) {
- // Name does not begin with a capital letter.
- $validName = false;
- } else {
- foreach ($nameBits as $bit) {
- if ($bit === '') {
- continue;
- }
-
- if ($bit{0} !== strtoupper($bit{0})) {
- $validName = false;
- break;
- }
- }
- }
-
- return $validName;
+ return Sniffs\ConstructNames::isUnderscoreName($string);
}//end isUnderscoreName()
@@ -392,67 +320,16 @@ public static function isUnderscoreName($string)
* @param string $varType The variable type to process.
*
* @return string
+ *
+ * @deprecated 3.5.0 Use PHP_CodeSniffer\Util\Sniffs\Comments::suggestType() instead.
+ * Note: the moved function has undergone significant changes
+ * compared to the original function.
+ * The call from this function preserves BC as much as possible.
*/
public static function suggestType($varType)
{
- if ($varType === '') {
- return '';
- }
-
- if (in_array($varType, self::$allowedTypes, true) === true) {
- return $varType;
- } else {
- $lowerVarType = strtolower($varType);
- switch ($lowerVarType) {
- case 'bool':
- case 'boolean':
- return 'boolean';
- case 'double':
- case 'real':
- case 'float':
- return 'float';
- case 'int':
- case 'integer':
- return 'integer';
- case 'array()':
- case 'array':
- return 'array';
- }//end switch
-
- if (strpos($lowerVarType, 'array(') !== false) {
- // Valid array declaration:
- // array, array(type), array(type1 => type2).
- $matches = [];
- $pattern = '/^array\(\s*([^\s^=^>]*)(\s*=>\s*(.*))?\s*\)/i';
- if (preg_match($pattern, $varType, $matches) !== 0) {
- $type1 = '';
- if (isset($matches[1]) === true) {
- $type1 = $matches[1];
- }
-
- $type2 = '';
- if (isset($matches[3]) === true) {
- $type2 = $matches[3];
- }
-
- $type1 = self::suggestType($type1);
- $type2 = self::suggestType($type2);
- if ($type2 !== '') {
- $type2 = ' => '.$type2;
- }
-
- return "array($type1$type2)";
- } else {
- return 'array';
- }//end if
- } else if (in_array($lowerVarType, self::$allowedTypes, true) === true) {
- // A valid type, but not lower cased.
- return $lowerVarType;
- } else {
- // Must be a custom type name.
- return $varType;
- }//end if
- }//end if
+ $types = array_combine(self::$allowedTypes, self::$allowedTypes);
+ return Sniffs\Comments::suggestType($varType, 'long', $types);
}//end suggestType()
diff --git a/src/Util/Sniffs/Comments.php b/src/Util/Sniffs/Comments.php
new file mode 100644
index 0000000000..26d38ebbfb
--- /dev/null
+++ b/src/Util/Sniffs/Comments.php
@@ -0,0 +1,755 @@
+
+ * @author Juliette Reinders Folmer
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class Comments
+{
+
+ /**
+ * Regex to split unioned type strings without splitting multi-type PSR-5
+ * array types or "old-style" array types.
+ *
+ * @var string
+ */
+ const SPLIT_UNION_TYPES = '`(?:^(?Parray\(\s*([^\s=>]*)(?:\s*=>\s*+(.*))?\s*\)|array<\s*([^\s,]*)(?:\s*,\s*+(.*))?\s*>|\([^)]+\)\[\]|[^|]+)(?=|)|(?<=|)(?P>type)$|(?<=|)(?P>type)(?=|))`i';
+
+ /**
+ * Regex to match array(type), array(type1 => type2) types.
+ *
+ * @var string
+ */
+ const MATCH_ARRAY = '`^array\(\s*([^\s=>]*)(?:\s*=>\s*+(.*))?\s*\)`i';
+
+ /**
+ * Regex to match array types.
+ *
+ * @var string
+ */
+ const MATCH_ARRAY_SQUARE = '`^array<\s*([^\s,]*)(?:\s*,\s*+(.*))?\s*>`i';
+
+
+ /**
+ * Valid variable types for param/var/return tags.
+ *
+ * Keys are short-form, values long-form.
+ *
+ * @var string[]
+ */
+ public static $allowedTypes = [
+ 'array' => 'array',
+ 'bool' => 'boolean',
+ 'callable' => 'callable',
+ 'false' => 'false',
+ 'float' => 'float',
+ 'int' => 'integer',
+ 'iterable' => 'iterable',
+ 'mixed' => 'mixed',
+ 'null' => 'null',
+ 'object' => 'object',
+ 'resource' => 'resource',
+ 'self' => 'self',
+ 'static' => 'static',
+ 'string' => 'string',
+ 'true' => 'true',
+ 'void' => 'void',
+ '$this' => '$this',
+ ];
+
+
+ /**
+ * Examine a complete variable type string for param/var tags.
+ *
+ * Examines the individual parts of unioned and intersectioned types.
+ * - Where relevant, will unduplicate types.
+ * - Where relevant, will combine multiple single/multi-types array types into one.
+ * - Where relevant, will remove duplicate union/intersect separators.
+ *
+ * @param string $typeString The complete variable type string to process.
+ * @param string $form Optional. Whether to prefer long-form or short-form
+ * types. By default, this only affects the integer and
+ * boolean types.
+ * Accepted values: 'long', 'short'. Defaults to `short`.
+ * @param array|null $allowedTypes Optional. Array of allowed variable types.
+ * Keys are short form types, values long form.
+ * Both lowercase.
+ * If for a particular standard, long/short form does
+ * not apply, keys and values should be the same.
+ *
+ * @return string Valid variable type string.
+ */
+ public static function suggestTypeString($typeString, $form='short', $allowedTypes=null)
+ {
+ // Check for PSR-5 Union types, like `int|null`.
+ if (strpos($typeString, '|') !== false && $typeString !== '|') {
+ $arrayCount = substr_count($typeString, '[]');
+ $typeCount = preg_match_all(self::SPLIT_UNION_TYPES, $typeString, $matches);
+ $types = $matches[0];
+ if ($typeCount > 0) {
+ if ($arrayCount < 2) {
+ // No or only one array type found, process like normal.
+ $formArray = array_fill(0, $typeCount, $form);
+ $allowedArray = array_fill(0, $typeCount, $allowedTypes);
+ $types = array_map('self::suggestType', $types, $formArray, $allowedArray);
+ $types = array_unique($types);
+ } else {
+ // Ok, so there were two or more array types in this type string. Let's combine them.
+ $newTypes = [];
+ $arrayTypes = [];
+ $firstArrayType = null;
+ foreach ($types as $order => $type) {
+ if (substr($type, 0, 1) === '(' && substr($type, -3) === ')[]') {
+ if ($firstArrayType === null) {
+ $firstArrayType = $order;
+ }
+
+ $subTypes = explode('|', substr($type, 1, -3));
+ // Remove empty entries.
+ $subTypes = array_filter($subTypes);
+ foreach ($subTypes as $subType) {
+ $arrayTypes[] = self::suggestType($subType, $form, $allowedTypes);
+ }
+ } else if (substr($type, -2) === '[]') {
+ if ($firstArrayType === null) {
+ $firstArrayType = $order;
+ }
+
+ $arrayTypes[] = self::suggestType(substr($type, 0, -2), $form, $allowedTypes);
+ } else {
+ $newTypes[$order] = self::suggestTypeString($type, $form, $allowedTypes);
+ }
+ }//end foreach
+
+ $newTypes = array_unique($newTypes);
+ $arrayTypes = array_unique($arrayTypes);
+ $arrayTypeCount = count($arrayTypes);
+ if ($arrayTypeCount > 1) {
+ $newTypes[$firstArrayType] = '('.implode('|', $arrayTypes).')[]';
+ } else if ($arrayTypeCount === 1) {
+ $newTypes[$firstArrayType] = implode('', $arrayTypes).'[]';
+ }
+
+ $types = $newTypes;
+ ksort($types);
+ }//end if
+ }//end if
+
+ // Check if both null as well as nullable types are used and if so, remove nullable indicator.
+ if (array_search('null', $types, true) !== false) {
+ foreach ($types as $key => $type) {
+ if (strpos($type, '?') === 0) {
+ $types[$key] = ltrim($type, '?');
+ }
+ }
+ }
+
+ return implode('|', $types);
+ }//end if
+
+ // Check for PSR-5 Intersection types, like `\MyClass&\PHPUnit\Framework\MockObject\MockObject`.
+ if (strpos($typeString, '&') !== false && $typeString !== '&') {
+ $types = explode('&', $typeString);
+ // Remove empty entries.
+ $types = array_filter($types);
+ $typeCount = count($types);
+ $formArray = array_fill(0, $typeCount, $form);
+ $allowedArray = array_fill(0, $typeCount, $allowedTypes);
+ $types = array_map('self::suggestType', $types, $formArray, $allowedArray);
+ $types = array_unique($types);
+ return implode('&', $types);
+ }
+
+ // Simple type.
+ return self::suggestType($typeString, $form, $allowedTypes);
+
+ }//end suggestTypeString()
+
+
+ /**
+ * Returns a valid variable type for param/var tags.
+ *
+ * If type is not one of the standard types, it must be a custom type.
+ * Returns the correct type name suggestion if type name is invalid.
+ *
+ * @param string $varType The variable type to process.
+ * @param string $form Optional. Whether to prefer long-form or short-form
+ * types. By default, this only affects the integer and
+ * boolean types.
+ * Accepted values: 'long', 'short'. Defaults to `short`.
+ * @param array|null $allowedTypes Optional. Array of allowed variable types.
+ * Keys are short form types, values long form.
+ * Both lowercase.
+ * If for a particular standard, long/short form does
+ * not apply, keys and values should be the same.
+ *
+ * @return string
+ */
+ public static function suggestType($varType, $form='short', $allowedTypes=null)
+ {
+ if ($allowedTypes === null) {
+ $allowedTypes = self::$allowedTypes;
+ }
+
+ if ($varType === '') {
+ return '';
+ }
+
+ $lowerVarType = strtolower(trim($varType));
+
+ if (($form === 'short' && isset($allowedTypes[$lowerVarType]) === true)
+ || ($form === 'long' && in_array($lowerVarType, $allowedTypes, true) === true)
+ ) {
+ return $lowerVarType;
+ }
+
+ // Check for short form use when long form is expected and visa versa.
+ if ($form === 'long' && isset($allowedTypes[$lowerVarType]) === true) {
+ return $allowedTypes[$lowerVarType];
+ }
+
+ if ($form === 'short' && in_array($lowerVarType, $allowedTypes, true) === true) {
+ return array_search($lowerVarType, $allowedTypes, true);
+ }
+
+ // Not listed in allowed types, check for a limited set of known variations.
+ switch ($lowerVarType) {
+ case 'double':
+ case 'real':
+ return 'float';
+
+ case 'array()':
+ case 'array<>':
+ return 'array';
+ }//end switch
+
+ // Handle more complex types, like arrays.
+ if (strpos($lowerVarType, 'array(') !== false || strpos($lowerVarType, 'array<') !== false) {
+ // Valid array declarations:
+ // array, array(type), array(type1 => type2), array.
+ $open = '(';
+ $close = ')';
+ $sep = ' =>';
+ $pattern = self::MATCH_ARRAY;
+ if (strpos($lowerVarType, 'array<') !== false) {
+ $open = '<';
+ $close = '>';
+ $sep = ',';
+ $pattern = self::MATCH_ARRAY_SQUARE;
+ }
+
+ $matches = [];
+ if (preg_match($pattern, $varType, $matches) === 1) {
+ $type1 = '';
+ if (isset($matches[1]) === true) {
+ $type1 = self::suggestTypeString($matches[1], $form, $allowedTypes);
+ }
+
+ $type2 = '';
+ if (isset($matches[2]) === true) {
+ $type2 = self::suggestTypeString($matches[2], $form, $allowedTypes);
+ if ($type2 !== '') {
+ $type2 = $sep.' '.$type2;
+ }
+ }
+
+ return 'array'.$open.$type1.$type2.$close;
+ }//end if
+
+ return 'array';
+ }//end if
+
+ // Check for PSR-5 multiple type array format, like `(int|string)[]`.
+ if (strpos($varType, '(') === 0 && substr($varType, -3) === ')[]' && $varType !== '()[]') {
+ return '('.self::suggestTypeString(substr($varType, 1, -3), $form, $allowedTypes).')[]';
+ }
+
+ // Check for PSR-5 single type array format, like `int[]`.
+ if (strpos($varType, '|') === false && substr($varType, -2) === '[]' && $varType !== '[]') {
+ return self::suggestType(substr($varType, 0, -2), $form, $allowedTypes).'[]';
+ }
+
+ // Allow for nullable type format, like `?string`.
+ if (strpos($varType, '?') === 0) {
+ return '?'.self::suggestType(substr($varType, 1), $form, $allowedTypes);
+ }
+
+ // Must be a custom type name.
+ return $varType;
+
+ }//end suggestType()
+
+
+ /**
+ * Find the end of a docblock, inline or block comment sequence.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * start of the comment.
+ *
+ * @return int Stack pointer to the end of the comment.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is
+ * not of type T_COMMENT or
+ * T_DOC_COMMENT_OPEN_TAG or if it
+ * is not the start of a comment.
+ */
+ public static function findEndOfComment(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] !== T_COMMENT
+ && $tokens[$stackPtr]['code'] !== T_DOC_COMMENT_OPEN_TAG
+ ) {
+ throw new RuntimeException('$stackPtr must be of type T_COMMENT or T_DOC_COMMENT_OPEN_TAG');
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_DOC_COMMENT_OPEN_TAG) {
+ return $tokens[$stackPtr]['comment_closer'];
+ }
+
+ // Find the end of inline comment blocks.
+ if (strpos($tokens[$stackPtr]['content'], '//') === 0
+ || strpos($tokens[$stackPtr]['content'], '#') === 0
+ ) {
+ $commentPrefix = '//';
+ if (strpos($tokens[$stackPtr]['content'], '#') === 0) {
+ $commentPrefix = '#';
+ }
+
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($prev !== false) {
+ if ($tokens[$prev]['line'] === $tokens[$stackPtr]['line']) {
+ // Stand-alone trailing comment.
+ return $stackPtr;
+ } else if ($tokens[$prev]['line'] === ($tokens[$stackPtr]['line'] - 1)) {
+ // Previous token was on the previous line.
+ // Now make sure it wasn't a stand-alone trailing comment.
+ if ($tokens[$prev]['code'] === T_COMMENT
+ && strpos($tokens[$prev]['content'], $commentPrefix) === 0
+ ) {
+ $pprev = $phpcsFile->findPrevious(T_WHITESPACE, ($prev - 1), null, true);
+ if ($pprev === false
+ || $tokens[$pprev]['line'] !== $tokens[$prev]['line']
+ ) {
+ throw new RuntimeException('$stackPtr must point to the start of a comment');
+ }
+ }
+ }
+ }
+
+ $commentEnd = $stackPtr;
+ for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
+ if ($tokens[$i]['code'] === T_WHITESPACE) {
+ continue;
+ }
+
+ if ($tokens[$i]['code'] !== T_COMMENT
+ && isset(Tokens::$phpcsCommentTokens[$tokens[$i]['code']]) === false
+ ) {
+ break;
+ }
+
+ if (strpos($tokens[$i]['content'], $commentPrefix) !== 0) {
+ // Not an inline comment or not same style comment, so not part of this comment sequence.
+ break;
+ }
+
+ if ($tokens[$i]['line'] !== ($tokens[$commentEnd]['line'] + 1)) {
+ // There must have been a blank line between these comments.
+ break;
+ }
+
+ $commentEnd = $i;
+ }//end for
+
+ if (isset(Tokens::$phpcsCommentTokens[$tokens[$commentEnd]['code']]) === true) {
+ // Inline comment blocks can't end on a PHPCS annotation, so move one back.
+ // We already know that the previous token must exist and be a comment token,
+ // so no extra validation needed.
+ $commentEnd = $phpcsFile->findPrevious(T_WHITESPACE, ($commentEnd - 1), null, true);
+ }
+
+ return $commentEnd;
+ }//end if
+
+ // Deal with block comments which start with a PHPCS annotation.
+ if (strpos($tokens[$stackPtr]['content'], '/*') !== 0) {
+ do {
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if (isset(Tokens::$phpcsCommentTokens[$tokens[$prev]['code']]) === false) {
+ throw new RuntimeException('$stackPtr must point to the start of a comment');
+ }
+
+ $stackPtr = $prev;
+
+ if (strpos($tokens[$prev]['content'], '/*') === 0) {
+ break;
+ }
+ } while ($stackPtr >= 0);
+ }
+
+ // Find the end of block comments.
+ if (strpos($tokens[$stackPtr]['content'], '/*') === 0) {
+ if (substr($tokens[$stackPtr]['content'], -2) === '*/') {
+ // Single line block comment.
+ return $stackPtr;
+ }
+
+ $valid = Tokens::$phpcsCommentTokens;
+ $valid[T_COMMENT] = T_COMMENT;
+
+ $commentEnd = $stackPtr;
+ $i = ($stackPtr + 1);
+ while ($i < $phpcsFile->numTokens && isset($valid[$tokens[$i]['code']]) === true) {
+ $commentEnd = $i;
+ if (substr($tokens[$i]['content'], -2) === '*/') {
+ // Found end of the comment.
+ break;
+ }
+
+ ++$i;
+ }
+
+ return $commentEnd;
+ }//end if
+
+ }//end findEndOfComment()
+
+
+ /**
+ * Find the start of a docblock, inline or block comment sequence.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * end of the comment.
+ *
+ * @return int Stack pointer to the start of the comment.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is
+ * not of type T_COMMENT or
+ * T_DOC_COMMENT_CLOSE_TAG or if it
+ * is not the end of a comment.
+ */
+ public static function findStartOfComment(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] !== T_COMMENT
+ && $tokens[$stackPtr]['code'] !== T_DOC_COMMENT_CLOSE_TAG
+ ) {
+ throw new RuntimeException('$stackPtr must be of type T_COMMENT or T_DOC_COMMENT_CLOSE_TAG');
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
+ return $tokens[$stackPtr]['comment_opener'];
+ }
+
+ // Find the start of inline comment blocks.
+ if (strpos($tokens[$stackPtr]['content'], '//') === 0
+ || strpos($tokens[$stackPtr]['content'], '#') === 0
+ ) {
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($prev !== false && $tokens[$prev]['line'] === $tokens[$stackPtr]['line']) {
+ // Stand-alone trailing comment.
+ return $stackPtr;
+ }
+
+ $commentPrefix = '//';
+ if (strpos($tokens[$stackPtr]['content'], '#') === 0) {
+ $commentPrefix = '#';
+ }
+
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if ($next !== false
+ && $tokens[$next]['code'] === T_COMMENT
+ && strpos($tokens[$next]['content'], $commentPrefix) === 0
+ && $tokens[$next]['line'] === ($tokens[$stackPtr]['line'] + 1)
+ ) {
+ throw new RuntimeException('$stackPtr must point to the end of a comment');
+ }
+
+ $commentStart = $stackPtr;
+ for ($i = ($stackPtr - 1); $i >= 0; $i--) {
+ if ($tokens[$i]['code'] === T_WHITESPACE) {
+ continue;
+ }
+
+ if ($tokens[$i]['code'] !== T_COMMENT
+ && isset(Tokens::$phpcsCommentTokens[$tokens[$i]['code']]) === false
+ ) {
+ break;
+ }
+
+ if (strpos($tokens[$i]['content'], $commentPrefix) !== 0) {
+ // Not an inline comment or not same style comment, so not part of this comment sequence.
+ break;
+ }
+
+ if ($tokens[$i]['line'] !== ($tokens[$commentStart]['line'] - 1)) {
+ // There must have been a blank line between these comments.
+ break;
+ }
+
+ $commentStart = $i;
+ }//end for
+
+ if (isset(Tokens::$phpcsCommentTokens[$tokens[$commentStart]['code']]) === true) {
+ // Inline comment blocks can't start on a PHPCS annotation, so move one forward.
+ // We already know that the next token must exist and be a comment token,
+ // so no extra validation needed.
+ $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($commentStart + 1), null, true);
+ } else {
+ // Check that the current token we are at isn't a trailing comment.
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($commentStart - 1), null, true);
+ if ($prev !== false && $tokens[$prev]['line'] === $tokens[$commentStart]['line']) {
+ // Trailing comment, so move one forward.
+ $commentStart = $phpcsFile->findNext(T_WHITESPACE, ($commentStart + 1), null, true);
+ }
+ }
+
+ return $commentStart;
+ }//end if
+
+ // Deal with block comments which end with a PHPCS annotation.
+ if (substr($tokens[$stackPtr]['content'], -2) !== '*/') {
+ do {
+ $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
+ if (isset(Tokens::$phpcsCommentTokens[$tokens[$next]['code']]) === false) {
+ throw new RuntimeException('$stackPtr must point to the end of a comment');
+ }
+
+ $stackPtr = $next;
+
+ if (substr($tokens[$next]['content'], -2) === '*/') {
+ break;
+ }
+ } while ($stackPtr >= 0);
+ }
+
+ // Find the start of block comments.
+ if (substr($tokens[$stackPtr]['content'], -2) === '*/') {
+ if (strpos($tokens[$stackPtr]['content'], '/*') === 0) {
+ // Single line block comment.
+ return $stackPtr;
+ }
+
+ $valid = Tokens::$phpcsCommentTokens;
+ $valid[T_COMMENT] = T_COMMENT;
+
+ $commentStart = $stackPtr;
+ $i = ($stackPtr - 1);
+ while ($i >= 0 && isset($valid[$tokens[$i]['code']]) === true) {
+ $commentStart = $i;
+ if (strpos($tokens[$i]['content'], '/*') === 0) {
+ // Found start of the comment.
+ break;
+ }
+
+ --$i;
+ }
+
+ return $commentStart;
+ }//end if
+
+ }//end findStartOfComment()
+
+
+ /**
+ * Find the related docblock/comment based on a T_CONST token.
+ *
+ * Note: As this function is based on the `T_CONST` token, it can not find
+ * individual docblocks for each constant in a multi-constant declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * T_CONST token.
+ *
+ * @return int|false Integer stack pointer to the docblock/comment end (close) token;
+ * or false if no docblock or comment was found.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is
+ * not of type T_CONST.
+ */
+ public static function findConstantComment(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ if ($tokens[$stackPtr]['code'] !== T_CONST) {
+ throw new RuntimeException('$stackPtr must be of type T_CONST');
+ }
+
+ $ignore = Tokens::$scopeModifiers;
+
+ return self::findCommentAbove($phpcsFile, $stackPtr, $ignore);
+
+ }//end findConstantComment()
+
+
+ /**
+ * Find the related docblock/comment based on a T_FUNCTION token.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * T_FUNCTION token.
+ *
+ * @return int|false Integer stack pointer to the docblock/comment end (close) token;
+ * or false if no docblock or comment was found.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is
+ * not of type T_FUNCTION.
+ */
+ public static function findFunctionComment(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ if ($tokens[$stackPtr]['code'] !== T_FUNCTION) {
+ throw new RuntimeException('$stackPtr must be of type T_FUNCTION');
+ }
+
+ $ignore = Tokens::$methodPrefixes;
+
+ return self::findCommentAbove($phpcsFile, $stackPtr, $ignore);
+
+ }//end findFunctionComment()
+
+
+ /**
+ * Find the related docblock/comment based on a class/interface/trait token.
+ *
+ * Note: anonymous classes are not supported by this method as what tokens should
+ * be allowed to precede them is too arbitrary.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * OO construct to find the comment for.
+ *
+ * @return int|false Integer stack pointer to the docblock/comment end (close) token;
+ * or false if no docblock or comment was found.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is
+ * not a class, interface or trait
+ * token.
+ */
+ public static function findOOStructureComment(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ if (isset(Tokens::$ooScopeTokens[$tokens[$stackPtr]['code']]) === false
+ || $tokens[$stackPtr]['code'] === T_ANON_CLASS
+ ) {
+ throw new RuntimeException('$stackPtr must be a class, interface or trait token');
+ }
+
+ $ignore = [];
+ if ($tokens[$stackPtr]['code'] === T_CLASS) {
+ // Only classes can be abstract/final.
+ $ignore = [
+ T_ABSTRACT => T_ABSTRACT,
+ T_FINAL => T_FINAL,
+ ];
+ }
+
+ return self::findCommentAbove($phpcsFile, $stackPtr, $ignore);
+
+ }//end findOOStructureComment()
+
+
+ /**
+ * Find the related docblock/comment based on the T_VARIABLE token for a class/trait property.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * T_VARIABLE token.
+ *
+ * @return int|false Integer stack pointer to the docblock/comment end (close) token;
+ * or false if no docblock or comment was found.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is
+ * not an OO property.
+ */
+ public static function findPropertyComment(File $phpcsFile, $stackPtr)
+ {
+ if (Conditions::isOOProperty($phpcsFile, $stackPtr) === false) {
+ throw new RuntimeException('$stackPtr must be an OO property');
+ }
+
+ $ignore = Tokens::$scopeModifiers;
+ $ignore[] = T_STATIC;
+ $ignore[] = T_VAR;
+
+ return self::findCommentAbove($phpcsFile, $stackPtr, $ignore);
+
+ }//end findPropertyComment()
+
+
+ /**
+ * Find docblock/comment based on construct token.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * construct to find the comment for.
+ * @param array $ignore Array of tokens to ignore if found
+ * before the construct token while looking
+ * for the comment/docblock.
+ * Note: T_WHITESPACE tokens and PHPCS
+ * native annotations will always be
+ * ignored.
+ *
+ * @return int|false Integer stack pointer to the docblock/comment end (close) token;
+ * or false if no docblock or comment was found.
+ */
+ public static function findCommentAbove(File $phpcsFile, $stackPtr, $ignore=[])
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Check for the existence of the token.
+ if (isset($tokens[$stackPtr]) === false) {
+ return false;
+ }
+
+ $customIgnore = $ignore;
+
+ $ignore[] = T_WHITESPACE;
+ $ignore += Tokens::$phpcsCommentTokens;
+ $commentEnd = $stackPtr;
+
+ // Find the right comment.
+ do {
+ $commentEnd = $phpcsFile->findPrevious($ignore, ($commentEnd - 1), null, true);
+
+ if ($commentEnd === false
+ || ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
+ && $tokens[$commentEnd]['code'] !== T_COMMENT)
+ ) {
+ return false;
+ }
+
+ $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($commentEnd - 1), null, true);
+
+ // Handle structures interlaced with inline comments where we need an earlier comment.
+ if (in_array($tokens[$prevNonEmpty]['code'], $customIgnore, true) === true) {
+ $commentEnd = $prevNonEmpty;
+ continue;
+ }
+
+ // Handle end comments for preceeding structures, such as control structures
+ // or function declarations. Assume the end comment belongs to the preceeding structure.
+ if ($tokens[$prevNonEmpty]['line'] === $tokens[$commentEnd]['line']) {
+ return false;
+ }
+
+ return $commentEnd;
+ } while (true);
+
+ }//end findCommentAbove()
+
+
+}//end class
diff --git a/src/Util/Sniffs/Conditions.php b/src/Util/Sniffs/Conditions.php
new file mode 100644
index 0000000000..1350be780d
--- /dev/null
+++ b/src/Util/Sniffs/Conditions.php
@@ -0,0 +1,266 @@
+
+ * @author Juliette Reinders Folmer
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class Conditions
+{
+
+
+ /**
+ * Return the position of the condition for the passed token.
+ *
+ * If no types are specified, the first condition for the token - or if $reverse=true,
+ * the last condition - will be returned.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $types Optional. The type(s) of tokens to search for.
+ * @param bool $reverse Optional. Whether to search for the highest
+ * (false) or the deepest condition (true) of
+ * the specified type(s).
+ *
+ * @return int|false StackPtr to the condition or false if the token does not have the condition.
+ */
+ public static function getCondition(File $phpcsFile, $stackPtr, $types=[], $reverse=false)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Check for the existence of the token.
+ if (isset($tokens[$stackPtr]) === false) {
+ return false;
+ }
+
+ // Make sure the token has conditions.
+ if (empty($tokens[$stackPtr]['conditions']) === true) {
+ return false;
+ }
+
+ $types = (array) $types;
+ $conditions = $tokens[$stackPtr]['conditions'];
+
+ if (empty($types) === true) {
+ // No types specified, just return the first/last condition pointer.
+ if ($reverse === true) {
+ end($conditions);
+ } else {
+ reset($conditions);
+ }
+
+ return key($conditions);
+ }
+
+ if ($reverse === true) {
+ $conditions = array_reverse($conditions, true);
+ }
+
+ foreach ($conditions as $ptr => $type) {
+ if (isset($tokens[$ptr]) === true
+ && in_array($type, $types, true) === true
+ ) {
+ // We found a token with the required type.
+ return $ptr;
+ }
+ }
+
+ return false;
+
+ }//end getCondition()
+
+
+ /**
+ * Determine if the passed token has a condition of one of the passed types.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $types The type(s) of tokens to search for.
+ *
+ * @return bool
+ */
+ public static function hasCondition(File $phpcsFile, $stackPtr, $types)
+ {
+ return (self::getCondition($phpcsFile, $stackPtr, $types) !== false);
+
+ }//end hasCondition()
+
+
+ /**
+ * Return the position of the first condition of a certain type for the passed token.
+ *
+ * If no types are specified, the first condition for the token, independently of type,
+ * will be returned.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $types Optional. The type(s) of tokens to search for.
+ *
+ * @return int|false StackPtr to the condition or false if the token does not have the condition.
+ */
+ public static function getFirstCondition(File $phpcsFile, $stackPtr, $types=[])
+ {
+ return self::getCondition($phpcsFile, $stackPtr, $types, false);
+
+ }//end getFirstCondition()
+
+
+ /**
+ * Return the position of the last condition of a certain type for the passed token.
+ *
+ * If no types are specified, the last condition for the token, independently of type,
+ * will be returned.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $types Optional. The type(s) of tokens to search for.
+ *
+ * @return int|false StackPtr to the condition or false if the token does not have the condition.
+ */
+ public static function getLastCondition(File $phpcsFile, $stackPtr, $types=[])
+ {
+ return self::getCondition($phpcsFile, $stackPtr, $types, true);
+
+ }//end getLastCondition()
+
+
+ /**
+ * Check whether a T_VARIABLE token is a class/trait property declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * T_VARIABLE token to verify.
+ *
+ * @return bool
+ */
+ public static function isOOProperty(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== T_VARIABLE) {
+ return false;
+ }
+
+ // Note: interfaces can not declare properties.
+ $validScopes = [
+ T_CLASS,
+ T_ANON_CLASS,
+ T_TRAIT,
+ ];
+
+ $scopePtr = self::validDirectScope($phpcsFile, $stackPtr, $validScopes);
+ if ($scopePtr !== false) {
+ // Make sure it's not a method parameter.
+ $deepestOpen = Parentheses::getLastOpener($phpcsFile, $stackPtr);
+ if ($deepestOpen === false
+ || $deepestOpen < $scopePtr
+ || Parentheses::isOwnerIn($phpcsFile, $deepestOpen, T_FUNCTION) === false
+ ) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }//end isOOProperty()
+
+
+ /**
+ * Check whether a T_CONST token is a class/interface constant declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * T_CONST token to verify.
+ *
+ * @return bool
+ */
+ public static function isOOConstant(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== T_CONST) {
+ return false;
+ }
+
+ // Note: traits can not declare constants.
+ $validScopes = [
+ T_CLASS,
+ T_ANON_CLASS,
+ T_INTERFACE,
+ ];
+
+ if (self::validDirectScope($phpcsFile, $stackPtr, $validScopes) !== false) {
+ return true;
+ }
+
+ return false;
+
+ }//end isOOConstant()
+
+
+ /**
+ * Check whether a T_FUNCTION token is a class/interface/trait method declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * T_FUNCTION token to verify.
+ *
+ * @return bool
+ */
+ public static function isOOMethod(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== T_FUNCTION) {
+ return false;
+ }
+
+ if (self::validDirectScope($phpcsFile, $stackPtr, Tokens::$ooScopeTokens) !== false) {
+ return true;
+ }
+
+ return false;
+
+ }//end isOOMethod()
+
+
+ /**
+ * Check whether the direct wrapping scope of a token is within a limited set of
+ * acceptable tokens.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * token to verify.
+ * @param int|string|array $validScopes Array of token constants.
+ *
+ * @return int|false StackPtr to the valid direct scope or false if no valid direct scope was found.
+ */
+ public static function validDirectScope(File $phpcsFile, $stackPtr, $validScopes)
+ {
+ $ptr = self::getLastCondition($phpcsFile, $stackPtr);
+
+ if ($ptr !== false) {
+ $tokens = $phpcsFile->getTokens();
+ $validScopes = (array) $validScopes;
+
+ if (isset($tokens[$ptr]) === true
+ && in_array($tokens[$ptr]['code'], $validScopes, true) === true
+ ) {
+ return $ptr;
+ }
+ }
+
+ return false;
+
+ }//end validDirectScope()
+
+
+}//end class
diff --git a/src/Util/Sniffs/ConstructNames.php b/src/Util/Sniffs/ConstructNames.php
new file mode 100644
index 0000000000..c0a8cc370d
--- /dev/null
+++ b/src/Util/Sniffs/ConstructNames.php
@@ -0,0 +1,330 @@
+
+ * @author Juliette Reinders Folmer
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ConstructNames
+{
+
+
+ /**
+ * Returns the declaration names for classes, interfaces, traits, and functions.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the declaration token which
+ * declared the class, interface, trait, or function.
+ *
+ * @return string|null The name of the class, interface, trait, or function;
+ * NULL if the function or class is anonymous; or
+ * an empty string in case of a parse error/live coding.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified token is not of type
+ * T_FUNCTION, T_CLASS, T_TRAIT, or T_INTERFACE.
+ */
+ public static function getDeclarationName(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ $tokenCode = $tokens[$stackPtr]['code'];
+
+ if ($tokenCode === T_ANON_CLASS || $tokenCode === T_CLOSURE) {
+ return null;
+ }
+
+ if ($tokenCode !== T_FUNCTION
+ && $tokenCode !== T_CLASS
+ && $tokenCode !== T_INTERFACE
+ && $tokenCode !== T_TRAIT
+ ) {
+ throw new RuntimeException('Token type "'.$tokens[$stackPtr]['type'].'" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT');
+ }
+
+ if ($tokenCode === T_FUNCTION
+ && strtolower($tokens[$stackPtr]['content']) !== 'function'
+ ) {
+ // JS specific: This is a function declared without the "function" keyword.
+ // So this token is the function name.
+ return $tokens[$stackPtr]['content'];
+ }
+
+ /*
+ * Determine the name. Note that we cannot simply look for the first T_STRING
+ * because an (invalid) class name starting with the number will be multiple tokens.
+ * Whitespace or comment are however not allowed within a name.
+ */
+
+ if ($tokenCode === T_FUNCTION && isset($tokens[$stackPtr]['parenthesis_opener']) === true) {
+ $opener = $tokens[$stackPtr]['parenthesis_opener'];
+ } else if (isset($tokens[$stackPtr]['scope_opener']) === true) {
+ $opener = $tokens[$stackPtr]['scope_opener'];
+ }
+
+ if (isset($opener) === false) {
+ // Live coding or parse error.
+ return '';
+ }
+
+ $nameStart = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), $opener, true);
+ if ($nameStart === false) {
+ // Live coding or parse error.
+ return '';
+ }
+
+ $nameEnd = $phpcsFile->findNext(Tokens::$emptyTokens, $nameStart, $opener);
+ if ($nameEnd === false) {
+ return $tokens[$nameStart]['content'];
+ }
+
+ // Name starts with number, so is composed of multiple tokens.
+ return trim($phpcsFile->getTokensAsString($nameStart, ($nameEnd - $nameStart)));
+
+ }//end getDeclarationName()
+
+
+ /**
+ * Returns true if the specified string is in the camel caps format.
+ *
+ * @param string $string The string the verify.
+ * @param boolean $classFormat If true, check to see if the string is in the
+ * class format. Class format strings must start
+ * with a capital letter and contain no
+ * underscores.
+ * @param boolean $public If true, the first character in the string
+ * must be an a-z character. If false, the
+ * character must be an underscore. This
+ * argument is only applicable if $classFormat
+ * is false.
+ * @param boolean $strict If true, the string must not have two capital
+ * letters next to each other. If false, a
+ * relaxed camel caps policy is used to allow
+ * for acronyms.
+ *
+ * @return boolean
+ */
+ public static function isCamelCaps(
+ $string,
+ $classFormat=false,
+ $public=true,
+ $strict=true
+ ) {
+ // Check the first character first.
+ if ($classFormat === false) {
+ $legalFirstChar = '';
+ if ($public === false) {
+ $legalFirstChar = '[_]';
+ }
+
+ if ($strict === false) {
+ // Can either start with a lowercase letter, or multiple uppercase
+ // in a row, representing an acronym.
+ $legalFirstChar .= '([A-Z]{2,}|[a-z])';
+ } else {
+ $legalFirstChar .= '[a-z]';
+ }
+ } else {
+ $legalFirstChar = '[A-Z]';
+ }
+
+ if (preg_match("/^$legalFirstChar/", $string) === 0) {
+ return false;
+ }
+
+ // Check that the name only contains legal characters.
+ $legalChars = 'a-zA-Z0-9';
+ if (preg_match("|[^$legalChars]|", substr($string, 1)) > 0) {
+ return false;
+ }
+
+ if ($strict === true) {
+ // Check that there are not two capital letters next to each other.
+ $length = strlen($string);
+ $lastCharWasCaps = $classFormat;
+
+ for ($i = 1; $i < $length; $i++) {
+ $ascii = ord($string{$i});
+ if ($ascii >= 48 && $ascii <= 57) {
+ // The character is a number, so it cant be a capital.
+ $isCaps = false;
+ } else {
+ if (strtoupper($string{$i}) === $string{$i}) {
+ $isCaps = true;
+ } else {
+ $isCaps = false;
+ }
+ }
+
+ if ($isCaps === true && $lastCharWasCaps === true) {
+ return false;
+ }
+
+ $lastCharWasCaps = $isCaps;
+ }
+ }//end if
+
+ return true;
+
+ }//end isCamelCaps()
+
+
+ /**
+ * Returns true if the specified string is in the underscore caps format.
+ *
+ * @param string $string The string to verify.
+ *
+ * @return boolean
+ */
+ public static function isUnderscoreName($string)
+ {
+ // If there is a space in the name, it can't be valid.
+ if (strpos($string, ' ') !== false) {
+ return false;
+ }
+
+ $validName = true;
+ $nameBits = explode('_', $string);
+
+ if (preg_match('|^[A-Z]|', $string) === 0) {
+ // Name does not begin with a capital letter.
+ $validName = false;
+ } else {
+ foreach ($nameBits as $bit) {
+ if ($bit === '') {
+ continue;
+ }
+
+ if ($bit{0} !== strtoupper($bit{0})) {
+ $validName = false;
+ break;
+ }
+ }
+ }
+
+ return $validName;
+
+ }//end isUnderscoreName()
+
+
+ /**
+ * Verify whether a name contains numeric characters.
+ *
+ * @param string $name The string.
+ *
+ * @return bool
+ */
+ public static function hasNumbers($name)
+ {
+ if ($name === '') {
+ return false;
+ }
+
+ return preg_match('`\pN`u', $name) === 1;
+
+ }//end hasNumbers()
+
+
+ /**
+ * Remove numeric characters from the start of a string.
+ *
+ * @param string $name The string.
+ *
+ * @return string
+ */
+ public static function ltrimNumbers($name)
+ {
+ if ($name === '') {
+ return '';
+ }
+
+ return preg_replace('`^[\pN]+(\X*)`u', '$1', $name);
+
+ }//end ltrimNumbers()
+
+
+ /**
+ * Remove all numeric characters from a string.
+ *
+ * @param string $name The string.
+ *
+ * @return string
+ */
+ public static function removeNumbers($name)
+ {
+ if ($name === '') {
+ return '';
+ }
+
+ return preg_replace('`[\pN]+`u', '', $name);
+
+ }//end removeNumbers()
+
+
+ /**
+ * Transform consecutive uppercase characters to lowercase.
+ *
+ * Important: this function will only work on non-ascii strings when the MBString
+ * extension is enabled.
+ *
+ * @param string $name The string.
+ *
+ * @return string The adjusted name or the original name if no consecutive uppercase
+ * characters where found or when MBString is not available and the input
+ * was non-ascii.
+ */
+ public static function lowerConsecutiveCaps($name)
+ {
+ static $mbstring = null, $encoding = null;
+
+ if ($name === '') {
+ return '';
+ }
+
+ // Cache the results of MbString check and encoding. These values won't change during a run.
+ if (isset($mbstring) === false) {
+ $mbstring = function_exists('mb_strtolower');
+ }
+
+ if (isset($encoding) === false) {
+ $encoding = Config::getConfigData('encoding');
+ if ($encoding === null) {
+ $encoding = 'utf-8';
+ }
+ }
+
+ // MBString can mangle non-ascii text when the encoding is not correctly set and
+ // strtolower will mangle any non-ascii, so just return the name unchanged in that case.
+ if (utf8_decode($name) !== $name && $mbstring === false) {
+ return $name;
+ }
+
+ $name = preg_replace_callback(
+ '`([\p{Lt}\p{Lu}])([\p{Lt}\p{Lu}]+?)(\b|$|\PL|[\p{Lt}\p{Lu}](?=[^\p{Lt}\p{Lu}])\pL|(?=[^\p{Lt}\p{Lu}])\pL)`u',
+ function ($matches) use ($mbstring, $encoding) {
+ if ($mbstring === true) {
+ $consecutiveChars = mb_strtolower($matches[2], $encoding);
+ } else {
+ $consecutiveChars = strtolower($matches[2]);
+ }
+
+ return $matches[1].$consecutiveChars.$matches[3];
+ },
+ $name
+ );
+
+ return $name;
+
+ }//end lowerConsecutiveCaps()
+
+
+}//end class
diff --git a/src/Util/Sniffs/FunctionDeclarations.php b/src/Util/Sniffs/FunctionDeclarations.php
new file mode 100644
index 0000000000..129c176f28
--- /dev/null
+++ b/src/Util/Sniffs/FunctionDeclarations.php
@@ -0,0 +1,568 @@
+
+ * @author Juliette Reinders Folmer
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class FunctionDeclarations
+{
+
+ /**
+ * A list of all PHP magic methods.
+ *
+ * @var array =>
+ */
+ public static $magicMethods = [
+ '__construct' => 'construct',
+ '__destruct' => 'destruct',
+ '__call' => 'call',
+ '__callstatic' => 'callstatic',
+ '__get' => 'get',
+ '__set' => 'set',
+ '__isset' => 'isset',
+ '__unset' => 'unset',
+ '__sleep' => 'sleep',
+ '__wakeup' => 'wakeup',
+ '__tostring' => 'tostring',
+ '__set_state' => 'set_state',
+ '__clone' => 'clone',
+ '__invoke' => 'invoke',
+ '__debuginfo' => 'debuginfo',
+ ];
+
+ /**
+ * A list of all PHP non-magic methods starting with a double underscore.
+ *
+ * These come from PHP modules such as SOAPClient.
+ *
+ * @var array =>
+ */
+ public static $methodsDoubleUnderscore = [
+ '__dorequest' => 'SOAPClient',
+ '__getcookies' => 'SOAPClient',
+ '__getfunctions' => 'SOAPClient',
+ '__getlastrequest' => 'SOAPClient',
+ '__getlastrequestheaders' => 'SOAPClient',
+ '__getlastresponse' => 'SOAPClient',
+ '__getlastresponseheaders' => 'SOAPClient',
+ '__gettypes' => 'SOAPClient',
+ '__setcookie' => 'SOAPClient',
+ '__setlocation' => 'SOAPClient',
+ '__setsoapheaders' => 'SOAPClient',
+ '__soapcall' => 'SOAPClient',
+ ];
+
+ /**
+ * A list of all PHP magic functions.
+ *
+ * @var array =>
+ */
+ public static $magicFunctions = ['__autoload' => 'autoload'];
+
+
+ /**
+ * Returns the parameters for the specified function token.
+ *
+ * Each parameter is in the following format:
+ *
+ *
+ * 0 => array(
+ * 'name' => '$var', // The variable name.
+ * 'token' => integer, // The stack pointer to the variable name.
+ * 'content' => string, // The full content of the variable definition.
+ * 'pass_by_reference' => boolean, // Is the variable passed by reference?
+ * 'variable_length' => boolean, // Is the param of variable length through use of `...` ?
+ * 'type_hint' => string, // The type hint for the variable.
+ * 'type_hint_token' => integer, // The stack pointer to the type hint
+ * // or false if there is no type hint.
+ * 'nullable_type' => boolean, // Is the variable using a nullable type?
+ * )
+ *
+ *
+ * Parameters with default values have an additional array index of
+ * 'default' with the value of the default as a string.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the function token
+ * to acquire the parameters for.
+ *
+ * @return array
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is not of
+ * type T_FUNCTION or T_CLOSURE.
+ */
+ public static function getParameters(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] !== T_FUNCTION
+ && $tokens[$stackPtr]['code'] !== T_CLOSURE
+ ) {
+ throw new RuntimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
+ }
+
+ $opener = $tokens[$stackPtr]['parenthesis_opener'];
+ $closer = $tokens[$stackPtr]['parenthesis_closer'];
+
+ $vars = [];
+ $currVar = null;
+ $paramStart = ($opener + 1);
+ $defaultStart = null;
+ $paramCount = 0;
+ $passByReference = false;
+ $variableLength = false;
+ $typeHint = '';
+ $typeHintToken = false;
+ $nullableType = false;
+
+ for ($i = $paramStart; $i <= $closer; $i++) {
+ // Check to see if this token has a parenthesis or bracket opener. If it does
+ // it's likely to be an array which might have arguments in it. This
+ // could cause problems in our parsing below, so lets just skip to the
+ // end of it.
+ if (isset($tokens[$i]['parenthesis_opener']) === true) {
+ // Don't do this if it's the close parenthesis for the method.
+ if ($i !== $tokens[$i]['parenthesis_closer']) {
+ $i = ($tokens[$i]['parenthesis_closer'] + 1);
+ }
+ }
+
+ if (isset($tokens[$i]['bracket_opener']) === true) {
+ // Don't do this if it's the close parenthesis for the method.
+ if ($i !== $tokens[$i]['bracket_closer']) {
+ $i = ($tokens[$i]['bracket_closer'] + 1);
+ }
+ }
+
+ switch ($tokens[$i]['code']) {
+ case T_BITWISE_AND:
+ if ($defaultStart === null) {
+ $passByReference = true;
+ }
+ break;
+ case T_VARIABLE:
+ $currVar = $i;
+ break;
+ case T_ELLIPSIS:
+ $variableLength = true;
+ break;
+ case T_CALLABLE:
+ if ($typeHintToken === false) {
+ $typeHintToken = $i;
+ }
+
+ $typeHint .= $tokens[$i]['content'];
+ break;
+ case T_SELF:
+ case T_PARENT:
+ case T_STATIC:
+ // Self and parent are valid, static invalid, but was probably intended as type hint.
+ if (isset($defaultStart) === false) {
+ if ($typeHintToken === false) {
+ $typeHintToken = $i;
+ }
+
+ $typeHint .= $tokens[$i]['content'];
+ }
+ break;
+ case T_STRING:
+ // This is a string, so it may be a type hint, but it could
+ // also be a constant used as a default value.
+ $prevComma = false;
+ for ($t = $i; $t >= $opener; $t--) {
+ if ($tokens[$t]['code'] === T_COMMA) {
+ $prevComma = $t;
+ break;
+ }
+ }
+
+ if ($prevComma !== false) {
+ $nextEquals = false;
+ for ($t = $prevComma; $t < $i; $t++) {
+ if ($tokens[$t]['code'] === T_EQUAL) {
+ $nextEquals = $t;
+ break;
+ }
+ }
+
+ if ($nextEquals !== false) {
+ break;
+ }
+ }
+
+ if ($defaultStart === null) {
+ if ($typeHintToken === false) {
+ $typeHintToken = $i;
+ }
+
+ $typeHint .= $tokens[$i]['content'];
+ }
+ break;
+ case T_NS_SEPARATOR:
+ // Part of a type hint or default value.
+ if ($defaultStart === null) {
+ if ($typeHintToken === false) {
+ $typeHintToken = $i;
+ }
+
+ $typeHint .= $tokens[$i]['content'];
+ }
+ break;
+ case T_NULLABLE:
+ if ($defaultStart === null) {
+ $nullableType = true;
+ $typeHint .= $tokens[$i]['content'];
+ }
+ break;
+ case T_CLOSE_PARENTHESIS:
+ case T_COMMA:
+ // If it's null, then there must be no parameters for this
+ // method.
+ if ($currVar === null) {
+ continue 2;
+ }
+
+ $vars[$paramCount] = [];
+ $vars[$paramCount]['token'] = $currVar;
+ $vars[$paramCount]['name'] = $tokens[$currVar]['content'];
+ $vars[$paramCount]['content'] = trim($phpcsFile->getTokensAsString($paramStart, ($i - $paramStart)));
+
+ if ($defaultStart !== null) {
+ $vars[$paramCount]['default'] = trim($phpcsFile->getTokensAsString($defaultStart, ($i - $defaultStart)));
+ }
+
+ $vars[$paramCount]['pass_by_reference'] = $passByReference;
+ $vars[$paramCount]['variable_length'] = $variableLength;
+ $vars[$paramCount]['type_hint'] = $typeHint;
+ $vars[$paramCount]['type_hint_token'] = $typeHintToken;
+ $vars[$paramCount]['nullable_type'] = $nullableType;
+
+ // Reset the vars, as we are about to process the next parameter.
+ $defaultStart = null;
+ $paramStart = ($i + 1);
+ $passByReference = false;
+ $variableLength = false;
+ $typeHint = '';
+ $typeHintToken = false;
+ $nullableType = false;
+
+ $paramCount++;
+ break;
+ case T_EQUAL:
+ $defaultStart = ($i + 1);
+ break;
+ }//end switch
+ }//end for
+
+ return $vars;
+
+ }//end getParameters()
+
+
+ /**
+ * Returns the visibility and implementation properties of a function or method.
+ *
+ * The format of the array is:
+ *
+ * array(
+ * 'scope' => 'public', // public protected or protected
+ * 'scope_specified' => true, // true is scope keyword was found.
+ * 'return_type' => '', // the return type of the method.
+ * 'return_type_token' => integer, // The stack pointer to the start of the return type
+ * // or false if there is no return type.
+ * 'nullable_return_type' => false, // true if the return type is nullable.
+ * 'is_abstract' => false, // true if the abstract keyword was found.
+ * 'is_final' => false, // true if the final keyword was found.
+ * 'is_static' => false, // true if the static keyword was found.
+ * 'has_body' => false, // true if the method has a body
+ * );
+ *
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the function token to
+ * acquire the properties for.
+ *
+ * @return array
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
+ * T_FUNCTION token.
+ */
+ public static function getProperties(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] !== T_FUNCTION
+ && $tokens[$stackPtr]['code'] !== T_CLOSURE
+ ) {
+ throw new RuntimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE');
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_FUNCTION) {
+ $valid = [
+ T_PUBLIC => T_PUBLIC,
+ T_PRIVATE => T_PRIVATE,
+ T_PROTECTED => T_PROTECTED,
+ T_STATIC => T_STATIC,
+ T_FINAL => T_FINAL,
+ T_ABSTRACT => T_ABSTRACT,
+ T_WHITESPACE => T_WHITESPACE,
+ T_COMMENT => T_COMMENT,
+ T_DOC_COMMENT => T_DOC_COMMENT,
+ ];
+ } else {
+ $valid = [
+ T_STATIC => T_STATIC,
+ T_WHITESPACE => T_WHITESPACE,
+ T_COMMENT => T_COMMENT,
+ T_DOC_COMMENT => T_DOC_COMMENT,
+ ];
+ }
+
+ $scope = 'public';
+ $scopeSpecified = false;
+ $isAbstract = false;
+ $isFinal = false;
+ $isStatic = false;
+
+ for ($i = ($stackPtr - 1); $i > 0; $i--) {
+ if (isset($valid[$tokens[$i]['code']]) === false) {
+ break;
+ }
+
+ switch ($tokens[$i]['code']) {
+ case T_PUBLIC:
+ $scope = 'public';
+ $scopeSpecified = true;
+ break;
+ case T_PRIVATE:
+ $scope = 'private';
+ $scopeSpecified = true;
+ break;
+ case T_PROTECTED:
+ $scope = 'protected';
+ $scopeSpecified = true;
+ break;
+ case T_ABSTRACT:
+ $isAbstract = true;
+ break;
+ case T_FINAL:
+ $isFinal = true;
+ break;
+ case T_STATIC:
+ $isStatic = true;
+ break;
+ }//end switch
+ }//end for
+
+ $returnType = '';
+ $returnTypeToken = false;
+ $nullableReturnType = false;
+ $hasBody = true;
+
+ if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
+ $scopeOpener = null;
+ if (isset($tokens[$stackPtr]['scope_opener']) === true) {
+ $scopeOpener = $tokens[$stackPtr]['scope_opener'];
+ }
+
+ $valid = [
+ T_STRING => T_STRING,
+ T_CALLABLE => T_CALLABLE,
+ T_SELF => T_SELF,
+ T_PARENT => T_PARENT,
+ T_NS_SEPARATOR => T_NS_SEPARATOR,
+ ];
+
+ for ($i = $tokens[$stackPtr]['parenthesis_closer']; $i < $phpcsFile->numTokens; $i++) {
+ if (($scopeOpener === null && $tokens[$i]['code'] === T_SEMICOLON)
+ || ($scopeOpener !== null && $i === $scopeOpener)
+ ) {
+ // End of function definition.
+ break;
+ }
+
+ if ($tokens[$i]['code'] === T_NULLABLE) {
+ $nullableReturnType = true;
+ }
+
+ if (isset($valid[$tokens[$i]['code']]) === true) {
+ if ($returnTypeToken === false) {
+ $returnTypeToken = $i;
+ }
+
+ $returnType .= $tokens[$i]['content'];
+ }
+ }
+
+ $end = $phpcsFile->findNext([T_OPEN_CURLY_BRACKET, T_SEMICOLON], $tokens[$stackPtr]['parenthesis_closer']);
+ $hasBody = $tokens[$end]['code'] === T_OPEN_CURLY_BRACKET;
+ }//end if
+
+ if ($returnType !== '' && $nullableReturnType === true) {
+ $returnType = '?'.$returnType;
+ }
+
+ return [
+ 'scope' => $scope,
+ 'scope_specified' => $scopeSpecified,
+ 'return_type' => $returnType,
+ 'return_type_token' => $returnTypeToken,
+ 'nullable_return_type' => $nullableReturnType,
+ 'is_abstract' => $isAbstract,
+ 'is_final' => $isFinal,
+ 'is_static' => $isStatic,
+ 'has_body' => $hasBody,
+ ];
+
+ }//end getProperties()
+
+
+ /**
+ * Checks if a given function is a PHP magic function.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The T_FUNCTION token to check.
+ *
+ * @return bool
+ */
+ public static function isMagicFunction(File $phpcsFile, $stackPtr)
+ {
+ if (Conditions::hasCondition($phpcsFile, $stackPtr, Tokens::$ooScopeTokens) === true) {
+ return false;
+ }
+
+ $name = $phpcsFile->getDeclarationName($stackPtr);
+ return self::isMagicFunctionName($name);
+
+ }//end isMagicFunction()
+
+
+ /**
+ * Verify if a given function name is the name of a PHP magic function.
+ *
+ * @param string $name The full function name.
+ *
+ * @return bool
+ */
+ public static function isMagicFunctionName($name)
+ {
+ $name = strtolower($name);
+ return (isset(self::$magicFunctions[$name]) === true);
+
+ }//end isMagicFunctionName()
+
+
+ /**
+ * Checks if a given function is a PHP magic method.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The T_FUNCTION token to check.
+ *
+ * @return bool
+ */
+ public static function isMagicMethod(File $phpcsFile, $stackPtr)
+ {
+ if (Conditions::isOOMethod($phpcsFile, $stackPtr) === false) {
+ return false;
+ }
+
+ $name = $phpcsFile->getDeclarationName($stackPtr);
+ return self::isMagicMethodName($name);
+
+ }//end isMagicMethod()
+
+
+ /**
+ * Verify if a given function name is the name of a PHP magic method.
+ *
+ * @param string $name The full function name.
+ *
+ * @return bool
+ */
+ public static function isMagicMethodName($name)
+ {
+ $name = strtolower($name);
+ return (isset(self::$magicMethods[$name]) === true);
+
+ }//end isMagicMethodName()
+
+
+ /**
+ * Checks if a given function is a PHP native double underscore method.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The T_FUNCTION token to check.
+ *
+ * @return bool
+ */
+ public static function isPHPDoubleUnderscoreMethod(File $phpcsFile, $stackPtr)
+ {
+ if (Conditions::isOOMethod($phpcsFile, $stackPtr) === false) {
+ return false;
+ }
+
+ $name = $phpcsFile->getDeclarationName($stackPtr);
+ return self::isPHPDoubleUnderscoreMethodName($name);
+
+ }//end isPHPDoubleUnderscoreMethod()
+
+
+ /**
+ * Verify if a given function name is the name of a PHP native double underscore method.
+ *
+ * @param string $name The full function name.
+ *
+ * @return bool
+ */
+ public static function isPHPDoubleUnderscoreMethodName($name)
+ {
+ $name = strtolower($name);
+ return (isset(self::$methodsDoubleUnderscore[$name]) === true);
+
+ }//end isPHPDoubleUnderscoreMethodName()
+
+
+ /**
+ * Checks if a given function is a magic method or a PHP native double underscore method.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The T_FUNCTION token to check.
+ *
+ * @return bool
+ */
+ public static function isSpecialMethod(File $phpcsFile, $stackPtr)
+ {
+ if (Conditions::isOOMethod($phpcsFile, $stackPtr) === false) {
+ return false;
+ }
+
+ $name = $phpcsFile->getDeclarationName($stackPtr);
+ return self::isSpecialMethodName($name);
+
+ }//end isSpecialMethod()
+
+
+ /**
+ * Verify if a given function name is the name of a magic method or a PHP native double underscore method.
+ *
+ * @param string $name The full function name.
+ *
+ * @return bool
+ */
+ public static function isSpecialMethodName($name)
+ {
+ $name = strtolower($name);
+ return (isset(self::$magicMethods[$name]) === true || isset(self::$methodsDoubleUnderscore[$name]) === true);
+
+ }//end isSpecialMethodName()
+
+
+}//end class
diff --git a/src/Util/Sniffs/Namespaces.php b/src/Util/Sniffs/Namespaces.php
new file mode 100644
index 0000000000..4c98840ee7
--- /dev/null
+++ b/src/Util/Sniffs/Namespaces.php
@@ -0,0 +1,298 @@
+
+ * @copyright 2017-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class Namespaces
+{
+
+ /**
+ * List of tokens which can end a namespace declaration statement.
+ *
+ * @var array
+ */
+ public static $statementClosers = [
+ T_SEMICOLON => T_SEMICOLON,
+ T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
+ T_CLOSE_TAG => T_CLOSE_TAG,
+ ];
+
+
+ /**
+ * Determine what a T_NAMESPACE token is used for.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the T_NAMESPACE token.
+ *
+ * @return string Either 'declaration', 'operator'.
+ * An empty string will be returned if it couldn't be
+ * reliably determined what the T_NAMESPACE token is used for.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is
+ * not a T_NAMESPACE token.
+ */
+ public static function getType(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== T_NAMESPACE) {
+ throw new RuntimeException('$stackPtr must be of type T_NAMESPACE');
+ }
+
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+ if ($next === false) {
+ // Live coding or parse error.
+ return '';
+ }
+
+ if ($tokens[$next]['code'] === T_STRING
+ || isset(self::$statementClosers[$tokens[$next]['code']]) === true
+ ) {
+ return 'declaration';
+ }
+
+ if ($tokens[$next]['code'] === T_NS_SEPARATOR) {
+ return 'operator';
+ }
+
+ return '';
+
+ }//end getType()
+
+
+ /**
+ * Determine whether a T_NAMESPACE token is the keyword for a namespace declaration.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of a T_NAMESPACE token.
+ *
+ * @return bool True if the token passed is the keyword for a namespace declaration.
+ * False if not.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is
+ * not a T_NAMESPACE token.
+ */
+ public static function isDeclaration(File $phpcsFile, $stackPtr)
+ {
+ return (self::getType($phpcsFile, $stackPtr) === 'declaration');
+
+ }//end isDeclaration()
+
+
+ /**
+ * Determine whether a T_NAMESPACE token is used as an operator.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of a T_NAMESPACE token.
+ *
+ * @return bool True if the token passed is used as an operator. False if not.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is
+ * not a T_NAMESPACE token.
+ */
+ public static function isOperator(File $phpcsFile, $stackPtr)
+ {
+ return (self::getType($phpcsFile, $stackPtr) === 'operator');
+
+ }//end isOperator()
+
+
+ /**
+ * Get the complete namespace name as declared.
+ *
+ * For hierarchical namespaces, the name will be composed of several tokens,
+ * i.e. MyProject\Sub\Level which will be returned together as one string.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of a T_NAMESPACE token.
+ * @param bool $clean Optional. Whether to get the name stripped
+ * of potentially interlaced whitespace and/or
+ * comments. Defaults to true.
+ *
+ * @return string|false The namespace name, or false if the specified position is not a
+ * T_NAMESPACE token, not the keyword for a namespace declaration
+ * or when parse errors are encountered/during live coding.
+ * Note: The name can be an empty string for a valid global
+ * namespace declaration.
+ */
+ public static function getDeclaredName(File $phpcsFile, $stackPtr, $clean=true)
+ {
+ try {
+ if (self::isDeclaration($phpcsFile, $stackPtr) === false) {
+ // Not a namespace declaration.
+ return false;
+ }
+ } catch (RuntimeException $e) {
+ // Non-existent token or not a namespace keyword token.
+ return false;
+ }
+
+ $endOfStatement = $phpcsFile->findNext(self::$statementClosers, ($stackPtr + 1));
+ if ($endOfStatement === false) {
+ // Live coding or parse error.
+ return false;
+ }
+
+ $tokens = $phpcsFile->getTokens();
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), ($endOfStatement + 1), true);
+ if ($next === $endOfStatement) {
+ // Declaration of global namespace. I.e.: namespace {}.
+ // If not a scoped {} namespace declaration, no name/global declarations are invalid
+ // and result in parse errors, but that's not our concern.
+ return '';
+ }
+
+ if ($clean === false) {
+ return trim($phpcsFile->getTokensAsString($next, ($endOfStatement - $next), true));
+ }
+
+ $name = '';
+ for ($i = $next; $i < $endOfStatement; $i++) {
+ if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ $name .= $tokens[$i]['content'];
+ }
+
+ return trim($name);
+
+ }//end getDeclaredName()
+
+
+ /**
+ * Determine the namespace an arbitrary token lives in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The token for which to determine
+ * the namespace.
+ *
+ * @return int|false Token pointer to the applicable namespace keyword or
+ * false if it couldn't be determined or no namespace applies.
+ */
+ public static function findNamespacePtr(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Check for the existence of the token.
+ if (isset($tokens[$stackPtr]) === false) {
+ return false;
+ }
+
+ // Check for scoped namespace {}.
+ $namespacePtr = Conditions::getCondition($phpcsFile, $stackPtr, T_NAMESPACE);
+ if ($namespacePtr !== false) {
+ return $namespacePtr;
+ }
+
+ /*
+ * Not in a scoped namespace, so let's see if we can find a non-scoped namespace instead.
+ * Keeping in mind that:
+ * - there can be multiple non-scoped namespaces in a file (bad practice, but is allowed);
+ * - the namespace keyword can also be used as an operator;
+ * - a non-named namespace resolves to the global namespace;
+ * - and that namespace declarations can't be nested in anything, so we can skip over any
+ * nesting structures.
+ */
+
+ $previousNSToken = $stackPtr;
+ $find = [
+ T_NAMESPACE,
+ T_CLOSE_CURLY_BRACKET,
+ T_CLOSE_PARENTHESIS,
+ T_CLOSE_SHORT_ARRAY,
+ ];
+
+ do {
+ $previousNSToken = $phpcsFile->findPrevious($find, ($previousNSToken - 1));
+ if ($previousNSToken === false) {
+ break;
+ }
+
+ if ($tokens[$previousNSToken]['code'] === T_CLOSE_CURLY_BRACKET) {
+ // Stop if we encounter a scoped namespace declaration as we already know we're not in one.
+ if (isset($tokens[$previousNSToken]['scope_condition']) === true
+ && $tokens[$tokens[$previousNSToken]['scope_condition']]['code'] === T_NAMESPACE
+ ) {
+ break;
+ }
+
+ // Skip over other scoped structures for efficiency.
+ if (isset($tokens[$previousNSToken]['scope_condition']) === true) {
+ $previousNSToken = $tokens[$previousNSToken]['scope_condition'];
+ } else if (isset($tokens[$previousNSToken]['bracket_opener']) === true) {
+ $previousNSToken = $tokens[$previousNSToken]['bracket_opener'];
+ }
+
+ continue;
+ }
+
+ // Skip over other nesting structures for efficiency.
+ if ($tokens[$previousNSToken]['code'] === T_CLOSE_SHORT_ARRAY) {
+ if (isset($tokens[$previousNSToken]['bracket_opener']) === true) {
+ $previousNSToken = $tokens[$previousNSToken]['bracket_opener'];
+ }
+
+ continue;
+ }
+
+ if ($tokens[$previousNSToken]['code'] === T_CLOSE_PARENTHESIS) {
+ if (isset($tokens[$previousNSToken]['parenthesis_owner']) === true) {
+ $previousNSToken = $tokens[$previousNSToken]['parenthesis_owner'];
+ } else if (isset($tokens[$previousNSToken]['parenthesis_opener']) === true) {
+ $previousNSToken = $tokens[$previousNSToken]['parenthesis_opener'];
+ }
+
+ continue;
+ }
+
+ // So this is a namespace keyword, check if it's a declaration.
+ if (self::isDeclaration($phpcsFile, $previousNSToken) === true) {
+ return $previousNSToken;
+ }
+ } while (true);
+
+ return false;
+
+ }//end findNamespacePtr()
+
+
+ /**
+ * Determine the namespace name an arbitrary token lives in.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The token for which to determine
+ * the namespace.
+ *
+ * @return string Namespace name or empty string if it couldn't be determined
+ * or no namespace applies.
+ */
+ public static function determineNamespace(File $phpcsFile, $stackPtr)
+ {
+ $namespacePtr = self::findNamespacePtr($phpcsFile, $stackPtr);
+ if ($namespacePtr === false) {
+ return '';
+ }
+
+ $namespace = self::getDeclaredName($phpcsFile, $namespacePtr);
+ if ($namespace !== false) {
+ return $namespace;
+ }
+
+ return '';
+
+ }//end determineNamespace()
+
+
+}//end class
diff --git a/src/Util/Sniffs/ObjectDeclarations.php b/src/Util/Sniffs/ObjectDeclarations.php
new file mode 100644
index 0000000000..7e450df68e
--- /dev/null
+++ b/src/Util/Sniffs/ObjectDeclarations.php
@@ -0,0 +1,238 @@
+
+ * @author Juliette Reinders Folmer
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ObjectDeclarations
+{
+
+
+ /**
+ * Returns the visibility and implementation properties of a class.
+ *
+ * The format of the array is:
+ *
+ * array(
+ * 'is_abstract' => false, // true if the abstract keyword was found.
+ * 'is_final' => false, // true if the final keyword was found.
+ * );
+ *
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the T_CLASS
+ * token to acquire the properties for.
+ *
+ * @return array
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
+ * T_CLASS token.
+ */
+ public static function getClassProperties(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] !== T_CLASS) {
+ throw new RuntimeException('$stackPtr must be of type T_CLASS');
+ }
+
+ $valid = Tokens::$emptyTokens;
+ $valid[T_FINAL] = T_FINAL;
+ $valid[T_ABSTRACT] = T_ABSTRACT;
+
+ $isAbstract = false;
+ $isFinal = false;
+
+ for ($i = ($stackPtr - 1); $i > 0; $i--) {
+ if (isset($valid[$tokens[$i]['code']]) === false) {
+ break;
+ }
+
+ switch ($tokens[$i]['code']) {
+ case T_ABSTRACT:
+ $isAbstract = true;
+ break;
+
+ case T_FINAL:
+ $isFinal = true;
+ break;
+ }
+ }//end for
+
+ return [
+ 'is_abstract' => $isAbstract,
+ 'is_final' => $isFinal,
+ ];
+
+ }//end getClassProperties()
+
+
+ /**
+ * Returns the name of the class that the specified class extends.
+ *
+ * Works for classes, anonymous classes and interfaces, though it is
+ * strongly recommended to use the findExtendedInterfaceNames() method
+ * to examine interfaces as they can extend multiple parent interfaces.
+ *
+ * Returns FALSE on error or if there is no extended class name.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The stack position of the
+ * class/interface keyword.
+ *
+ * @return string|false
+ */
+ public static function findExtendedClassName(File $phpcsFile, $stackPtr)
+ {
+ $validStructures = [
+ T_CLASS => true,
+ T_ANON_CLASS => true,
+ T_INTERFACE => true,
+ ];
+
+ $classes = self::findExtendedImplemented($phpcsFile, $stackPtr, $validStructures, T_EXTENDS);
+
+ if (empty($classes) === true) {
+ return false;
+ }
+
+ // Classes can only extend one parent class.
+ return $classes[0];
+
+ }//end findExtendedClassName()
+
+
+ /**
+ * Returns the names of the interfaces that the specified interface extends.
+ *
+ * Returns FALSE on error or if there is no extended interface name.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The stack position of the interface keyword.
+ *
+ * @return array|false
+ */
+ public static function findExtendedInterfaceNames(File $phpcsFile, $stackPtr)
+ {
+ $validStructures = [T_INTERFACE => true];
+
+ $interfaces = self::findExtendedImplemented($phpcsFile, $stackPtr, $validStructures, T_EXTENDS);
+
+ if (empty($interfaces) === true) {
+ return false;
+ }
+
+ return $interfaces;
+
+ }//end findExtendedInterfaceNames()
+
+
+ /**
+ * Returns the names of the interfaces that the specified class implements.
+ *
+ * Returns FALSE on error or if there are no implemented interface names.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The stack position of the class keyword.
+ *
+ * @return array|false
+ */
+ public static function findImplementedInterfaceNames(File $phpcsFile, $stackPtr)
+ {
+ $validStructures = [
+ T_CLASS => true,
+ T_ANON_CLASS => true,
+ ];
+
+ $interfaces = self::findExtendedImplemented($phpcsFile, $stackPtr, $validStructures, T_IMPLEMENTS);
+
+ if (empty($interfaces) === true) {
+ return false;
+ }
+
+ return $interfaces;
+
+ }//end findImplementedInterfaceNames()
+
+
+ /**
+ * Returns the names of the extended classes or interfaces or the implemented
+ * interfaces that the specific class/interface declaration extends/implements.
+ *
+ * Returns FALSE on error or if the object does not extend/implement another object.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The stack position of the
+ * class/interface keyword.
+ * @param array $OOTypes Array of accepted token types.
+ * Array format => true.
+ * @param int $keyword The token constant for the keyword to examine.
+ * Either `T_EXTENDS` or `T_IMPLEMENTS`.
+ *
+ * @return array|false
+ */
+ private static function findExtendedImplemented(File $phpcsFile, $stackPtr, array $OOTypes, $keyword)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Check for the existence of the token.
+ if (isset($tokens[$stackPtr]) === false) {
+ return false;
+ }
+
+ if (isset($OOTypes[$tokens[$stackPtr]['code']]) === false) {
+ return false;
+ }
+
+ if (isset($tokens[$stackPtr]['scope_opener']) === false) {
+ return false;
+ }
+
+ $openerIndex = $tokens[$stackPtr]['scope_opener'];
+ $keywordIndex = $phpcsFile->findNext($keyword, ($stackPtr + 1), $openerIndex);
+ if ($keywordIndex === false) {
+ return false;
+ }
+
+ $find = Tokens::$emptyTokens;
+ $find[] = T_NS_SEPARATOR;
+ $find[] = T_STRING;
+ $find[] = T_COMMA;
+
+ $end = $phpcsFile->findNext($find, ($keywordIndex + 1), ($openerIndex + 1), true);
+ $names = [];
+ $name = '';
+ for ($i = ($keywordIndex + 1); $i < $end; $i++) {
+ if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ if ($tokens[$i]['code'] === T_COMMA && $name !== '') {
+ $names[] = $name;
+ $name = '';
+ continue;
+ }
+
+ $name .= $tokens[$i]['content'];
+ }
+
+ // Add the last name.
+ if ($name !== '') {
+ $names[] = $name;
+ }
+
+ return $names;
+
+ }//end findExtendedImplemented()
+
+
+}//end class
diff --git a/src/Util/Sniffs/Orthography.php b/src/Util/Sniffs/Orthography.php
new file mode 100644
index 0000000000..1fd9073b89
--- /dev/null
+++ b/src/Util/Sniffs/Orthography.php
@@ -0,0 +1,103 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+class Orthography
+{
+
+ /**
+ * Characters which are considered terminal points for a sentence.
+ *
+ * @link https://www.thepunctuationguide.com/terminal-points.html
+ *
+ * @var string
+ */
+ const TERMINAL_POINTS = '.?!';
+
+
+ /**
+ * Check if the first character of an arbitrary text string is a capital letter.
+ *
+ * Letter characters which do not have a concept of lower/uppercase will
+ * be accepted as correctly capitalized.
+ *
+ * @param string $string The text string to examine.
+ * This can be the contents of a text string token,
+ * but also, for instance, a comment text.
+ * Potential text delimiter quotes should be stripped
+ * off a text string before passing it to this method.
+ *
+ * @return boolean True when the first character is a capital letter or a letter
+ * which doesn't have a concept of capitalization.
+ * False otherwise, including for non-letter characters.
+ */
+ public static function isFirstCharCapitalized($string)
+ {
+ $string = ltrim($string);
+ return (preg_match('`^[\p{Lu}\p{Lt}\p{Lo}]`u', $string) > 0);
+
+ }//end isFirstCharCapitalized()
+
+
+ /**
+ * Check if the first character of an arbitrary text string is a lowercase letter.
+ *
+ * @param string $string The text string to examine.
+ * This can be the contents of a text string token,
+ * but also, for instance, a comment text.
+ * Potential text delimiter quotes should be stripped
+ * off a text string before passing it to this method.
+ *
+ * @return boolean True when the first character is a lowercase letter.
+ * False otherwise, including for letters which don't have a concept of
+ * capitalization and for non-letter characters.
+ */
+ public static function isFirstCharLowercase($string)
+ {
+ $string = ltrim($string);
+ return (preg_match('`^\p{Ll}`u', $string) > 0);
+
+ }//end isFirstCharLowercase()
+
+
+ /**
+ * Check if the last character of an arbitrary text string is a valid punctuation character.
+ *
+ * @param string $string The text string to examine.
+ * This can be the contents of a text string token,
+ * but also, for instance, a comment text.
+ * Potential text delimiter quotes should be stripped
+ * off a text string before passing it to this method.
+ * @param string $allowedChars Characters which are considered valid punctuation
+ * to end the text string.
+ * Defaults to '.?!', i.e. a full stop, question mark
+ * or exclamation mark.
+ *
+ * @return boolean
+ */
+ public static function isLastCharPunctuation($string, $allowedChars=self::TERMINAL_POINTS)
+ {
+ $string = rtrim($string);
+ if (function_exists('iconv_substr') === true) {
+ $lastChar = iconv_substr($string, -1);
+ } else {
+ $lastChar = substr($string, -1);
+ }
+
+ if (function_exists('iconv_strpos') === true) {
+ return (iconv_strpos($allowedChars, $lastChar) !== false);
+ } else {
+ return (strpos($allowedChars, $lastChar) !== false);
+ }
+
+ }//end isLastCharPunctuation()
+
+
+}//end class
diff --git a/src/Util/Sniffs/Parentheses.php b/src/Util/Sniffs/Parentheses.php
new file mode 100644
index 0000000000..6aea1c271e
--- /dev/null
+++ b/src/Util/Sniffs/Parentheses.php
@@ -0,0 +1,332 @@
+
+ * @copyright 2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Files\File;
+
+class Parentheses
+{
+
+
+ /**
+ * Get the pointer to the parentheses owner of an open/close parenthesis.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of T_OPEN/CLOSE_PARENTHESIS token.
+ *
+ * @return int|false StackPtr to the parentheses owner or false if the parenthesis
+ * does not have a (direct) owner.
+ */
+ public static function getOwner(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+ if (isset($tokens[$stackPtr], $tokens[$stackPtr]['parenthesis_owner']) === false) {
+ return false;
+ }
+
+ return $tokens[$stackPtr]['parenthesis_owner'];
+
+ }//end getOwner()
+
+
+ /**
+ * Check whether the parenthesis owner of an open/close parenthesis is within a
+ * limited set of valid owners.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of T_OPEN/CLOSE_PARENTHESIS token.
+ * @param int|string|array $validOwners Array of token constants for the owners
+ * which should be considered valid.
+ *
+ * @return boolean True if the owner is within the list of $validOwners, false if not and
+ * if the parenthesis does not have a (direct) owner.
+ */
+ public static function isOwnerIn(File $phpcsFile, $stackPtr, $validOwners)
+ {
+ $owner = self::getOwner($phpcsFile, $stackPtr);
+ if ($owner === false) {
+ return false;
+ }
+
+ $tokens = $phpcsFile->getTokens();
+ $validOwners = (array) $validOwners;
+ return in_array($tokens[$owner]['code'], $validOwners, true);
+
+ }//end isOwnerIn()
+
+
+ /**
+ * Check whether the passed token is nested within parentheses owned by one of the valid owners.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $validOwners Array of token constants for the owners
+ * which should be considered valid.
+ *
+ * @return boolean
+ */
+ public static function hasOwner(File $phpcsFile, $stackPtr, $validOwners)
+ {
+ return (self::nestedParensWalker($phpcsFile, $stackPtr, $validOwners) !== false);
+
+ }//end hasOwner()
+
+
+ /**
+ * Retrieve the position of the opener to the first set of parentheses an arbitrary token
+ * is wrapped in where the parentheses owner is within the set of valid owners.
+ *
+ * If no $validOwners are specified, the opener to the first set of parentheses surrounding
+ * the token will be returned.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $validOwners Array of token constants for the owners
+ * which should be considered valid.
+ *
+ * @return int|false StackPtr to the parentheses opener or false if the token does not have
+ * parentheses owned by any of the valid owners or if the token is not
+ * nested in parentheses at all.
+ */
+ public static function getFirstOpener(File $phpcsFile, $stackPtr, $validOwners=[])
+ {
+ return self::nestedParensWalker($phpcsFile, $stackPtr, $validOwners, false);
+
+ }//end getFirstOpener()
+
+
+ /**
+ * Retrieve the position of the closer to the first set of parentheses an arbitrary token
+ * is wrapped in where the parentheses owner is within the set of valid owners.
+ *
+ * If no $validOwners are specified, the closer to the first set of parentheses surrounding
+ * the token will be returned.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $validOwners Array of token constants for the owners
+ * which should be considered valid.
+ *
+ * @return int|false StackPtr to the parentheses closer or false if the token does not have
+ * parentheses owned by any of the valid owners or if the token is not
+ * nested in parentheses at all.
+ */
+ public static function getFirstCloser(File $phpcsFile, $stackPtr, $validOwners=[])
+ {
+ $opener = self::getFirstOpener($phpcsFile, $stackPtr, $validOwners);
+ $tokens = $phpcsFile->getTokens();
+ if ($opener !== false && isset($tokens[$opener]['parenthesis_closer']) === true) {
+ return $tokens[$opener]['parenthesis_closer'];
+ }
+
+ return false;
+
+ }//end getFirstCloser()
+
+
+ /**
+ * Retrieve the position of the parentheses owner to the first set of parentheses an
+ * arbitrary token is wrapped in where the parentheses owner is within the set of valid owners.
+ *
+ * If no $validOwners are specified, the owner to the first set of parentheses surrounding
+ * the token will be returned or false if the first set of parentheses does not have an owner.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $validOwners Array of token constants for the owners
+ * which should be considered valid.
+ *
+ * @return int|false StackPtr to the parentheses owner or false if the token does not have
+ * parentheses owned by any of the valid owners or if the token is not
+ * nested in parentheses at all.
+ */
+ public static function getFirstOwner(File $phpcsFile, $stackPtr, $validOwners=[])
+ {
+ $opener = self::getFirstOpener($phpcsFile, $stackPtr, $validOwners);
+ if ($opener !== false) {
+ return self::getOwner($phpcsFile, $opener);
+ }
+
+ return false;
+
+ }//end getFirstOwner()
+
+
+ /**
+ * Retrieve the position of the opener to the last set of parentheses an arbitrary token
+ * is wrapped in where the parentheses owner is within the set of valid owners.
+ *
+ * If no $validOwners are specified, the opener to the last set of parentheses surrounding
+ * the token will be returned.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $validOwners Array of token constants for the owners
+ * which should be considered valid.
+ *
+ * @return int|false StackPtr to the parentheses opener or false if the token does not have
+ * parentheses owned by any of the valid owners or if the token is not
+ * nested in parentheses at all.
+ */
+ public static function getLastOpener(File $phpcsFile, $stackPtr, $validOwners=[])
+ {
+ return self::nestedParensWalker($phpcsFile, $stackPtr, $validOwners, true);
+
+ }//end getLastOpener()
+
+
+ /**
+ * Retrieve the position of the closer to the last set of parentheses an arbitrary token
+ * is wrapped in where the parentheses owner is within the set of valid owners.
+ *
+ * If no $validOwners are specified, the closer to the last set of parentheses surrounding
+ * the token will be returned.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $validOwners Array of token constants for the owners
+ * which should be considered valid.
+ *
+ * @return int|false StackPtr to the parentheses closer or false if the token does not have
+ * parentheses owned by any of the valid owners or if the token is not
+ * nested in parentheses at all.
+ */
+ public static function getLastCloser(File $phpcsFile, $stackPtr, $validOwners=[])
+ {
+ $opener = self::getLastOpener($phpcsFile, $stackPtr, $validOwners);
+ $tokens = $phpcsFile->getTokens();
+ if ($opener !== false && isset($tokens[$opener]['parenthesis_closer']) === true) {
+ return $tokens[$opener]['parenthesis_closer'];
+ }
+
+ return false;
+
+ }//end getLastCloser()
+
+
+ /**
+ * Retrieve the position of the parentheses owner to the last set of parentheses an
+ * arbitrary token is wrapped in where the parentheses owner is within the set of valid owners.
+ *
+ * If no $validOwners are specified, the owner to the last set of parentheses surrounding
+ * the token will be returned or false if the last set of parentheses does not have an owner.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $validOwners Array of token constants for the owners
+ * which should be considered valid.
+ *
+ * @return int|false StackPtr to the parentheses owner or false if the token does not have
+ * parentheses owned by any of the valid owners or if the token is not
+ * nested in parentheses at all.
+ */
+ public static function getLastOwner(File $phpcsFile, $stackPtr, $validOwners=[])
+ {
+ $opener = self::getLastOpener($phpcsFile, $stackPtr, $validOwners);
+ if ($opener !== false) {
+ return self::getOwner($phpcsFile, $opener);
+ }
+
+ return false;
+
+ }//end getLastOwner()
+
+
+ /**
+ * Check whether the owner of a direct wrapping set of parentheses is within a limited set of
+ * acceptable tokens.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the
+ * token to verify.
+ * @param int|string|array $validOwners Array of token constants for the owners
+ * which should be considered valid.
+ *
+ * @return int|false StackPtr to the valid parentheses owner or false if the token was not
+ * wrapped in parentheses or if the last set of parentheses in which the
+ * token is wrapped does not have an owner within the set of owners
+ * considered valid.
+ */
+ public static function lastOwnerIn(File $phpcsFile, $stackPtr, $validOwners)
+ {
+ $opener = self::getLastOpener($phpcsFile, $stackPtr);
+
+ if ($opener !== false && self::isOwnerIn($phpcsFile, $opener, $validOwners) === true) {
+ return self::getOwner($phpcsFile, $opener);
+ }
+
+ return false;
+
+ }//end lastOwnerIn()
+
+
+ /**
+ * Helper method. Retrieve the position of a parentheses opener for an arbitrary passed token.
+ *
+ * If no $validOwners are specified, the opener to the first set of parentheses surrounding
+ * the token - or if $reverse=true, the last set of parentheses - will be returned.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the token we are checking.
+ * @param int|string|array $validOwners Optional. Array of token constants for the owners
+ * which should be considered valid.
+ * @param bool $reverse Optional. Whether to search for the highest
+ * (false) or the deepest set of parentheses (true)
+ * with the specified owner(s).
+ *
+ * @return int|false StackPtr to the parentheses opener or false if the token does not have
+ * parentheses owned by any of the valid owners or if the token is not
+ * nested in parentheses at all.
+ */
+ private static function nestedParensWalker(File $phpcsFile, $stackPtr, $validOwners=[], $reverse=false)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Check for the existence of the token.
+ if (isset($tokens[$stackPtr]) === false) {
+ return false;
+ }
+
+ // Make sure the token is nested in parenthesis.
+ if (empty($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ return false;
+ }
+
+ $validOwners = (array) $validOwners;
+ $parentheses = $tokens[$stackPtr]['nested_parenthesis'];
+
+ if (empty($validOwners) === true) {
+ // No owners specified, just return the first/last parentheses opener.
+ if ($reverse === true) {
+ end($parentheses);
+ } else {
+ reset($parentheses);
+ }
+
+ return key($parentheses);
+ }
+
+ if ($reverse === true) {
+ $parentheses = array_reverse($parentheses, true);
+ }
+
+ foreach ($parentheses as $opener => $closer) {
+ if (self::isOwnerIn($phpcsFile, $opener, $validOwners) === true) {
+ // We found a token with a valid owner.
+ return $opener;
+ }
+ }
+
+ return false;
+
+ }//end nestedParensWalker()
+
+
+}//end class
diff --git a/src/Util/Sniffs/PassedParameters.php b/src/Util/Sniffs/PassedParameters.php
new file mode 100644
index 0000000000..cfd01958a6
--- /dev/null
+++ b/src/Util/Sniffs/PassedParameters.php
@@ -0,0 +1,385 @@
+
+ * @copyright 2016-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class PassedParameters
+{
+
+
+ /**
+ * The token types these methods can handle.
+ *
+ * @var array
+ */
+ private static $allowedConstructs = [
+ T_STRING => true,
+ T_VARIABLE => true,
+ T_SELF => true,
+ T_STATIC => true,
+ T_CLOSE_CURLY_BRACKET => true,
+ T_CLOSE_PARENTHESIS => true,
+ T_ARRAY => true,
+ T_OPEN_SHORT_ARRAY => true,
+ T_ISSET => true,
+ T_UNSET => true,
+ T_LIST => true,
+ ];
+
+ /**
+ * Tokens which are considered stop point, either because they are the end
+ * of the parameter (comma) or because we need to skip over them.
+ *
+ * @var array
+ */
+ private static $callParsingStopPoints = [
+ T_COMMA => T_COMMA,
+ T_ARRAY => T_ARRAY,
+ T_OPEN_SHORT_ARRAY => T_OPEN_SHORT_ARRAY,
+ T_CLOSURE => T_CLOSURE,
+ T_ANON_CLASS => T_ANON_CLASS,
+ ];
+
+
+ /**
+ * The tokens to target to find the double arrow in an array item.
+ *
+ * @var array
+ */
+ private static $doubleArrowTargets = [
+ T_DOUBLE_ARROW,
+ T_ARRAY,
+ T_OPEN_SHORT_ARRAY,
+ ];
+
+
+ /**
+ * Checks if any parameters have been passed.
+ *
+ * Expects to be passed the T_STRING or T_VARIABLE stack pointer for a function call.
+ * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
+ *
+ * Extra features:
+ * - If passed a T_SELF or T_STATIC stack pointer, it will accept it as a
+ * function call when used like `new self()`.
+ * - A T_CLOSE_CURLY_BRACKET and a T_CLOSE_PARENTHESIS stack pointer will be
+ * checked as a function call.
+ * - If passed a T_ARRAY or T_OPEN_SHORT_ARRAY stack pointer, it will detect
+ * whether the array (or short list) has values or is empty.
+ * - If passed a T_ISSET, T_UNSET or T_LIST stack pointer, it will detect whether
+ * those language constructs have "parameters".
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the T_STRING, T_VARIABLE, T_ARRAY,
+ * T_OPEN_SHORT_ARRAY, T_ISSET, T_UNSET or T_LIST token.
+ *
+ * @return bool
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the token passed is not one of the
+ * accepted types.
+ */
+ public static function hasParameters(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Is this one of the tokens this function handles ?
+ if (isset(self::$allowedConstructs[$tokens[$stackPtr]['code']]) === false) {
+ throw new RuntimeException(
+ 'The hasParameters() method expects a function call, array, list, isset or unset token to be passed. Received "'.$tokens[$stackPtr]['type'].'" instead'
+ );
+ }
+
+ // Weed out some of the obvious non-function calls.
+ if ($tokens[$stackPtr]['code'] === T_SELF || $tokens[$stackPtr]['code'] === T_STATIC) {
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($tokens[$prev]['code'] !== T_NEW) {
+ throw new RuntimeException(
+ 'The hasParameters() method expects a function call, array, list, isset or unset token to be passed. Received "'.$tokens[$stackPtr]['type'].'" instead'
+ );
+ }
+ } else if ($tokens[$stackPtr]['code'] === T_CLOSE_CURLY_BRACKET
+ && isset($tokens[$stackPtr]['scope_condition']) === true
+ ) {
+ throw new RuntimeException(
+ 'The hasParameters() method expects a function call, array, list, isset or unset token to be passed. Received a "'.$tokens[$stackPtr]['type'].'" which is not function call'
+ );
+ } else if ($tokens[$stackPtr]['code'] === T_CLOSE_PARENTHESIS
+ && isset($tokens[$stackPtr]['parenthesis_owner']) === true
+ ) {
+ throw new RuntimeException(
+ 'The hasParameters() method expects a function call, array, list, isset or unset token to be passed. Received a "'.$tokens[$stackPtr]['type'].'" which is not function call'
+ );
+ }//end if
+
+ $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
+
+ if ($nextNonEmpty === false) {
+ return false;
+ }
+
+ // Deal with short array and short list syntax.
+ if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY) {
+ if ($nextNonEmpty === $tokens[$stackPtr]['bracket_closer']) {
+ // No parameters.
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ // Deal with function calls, long arrays, long lists, isset and unset.
+ // Next non-empty token should be the open parenthesis.
+ if ($tokens[$nextNonEmpty]['code'] !== T_OPEN_PARENTHESIS) {
+ return false;
+ }
+
+ if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) {
+ return false;
+ }
+
+ $closeParenthesis = $tokens[$nextNonEmpty]['parenthesis_closer'];
+ $nextNextNonEmpty = $phpcsFile->findNext(
+ Tokens::$emptyTokens,
+ ($nextNonEmpty + 1),
+ ($closeParenthesis + 1),
+ true
+ );
+
+ if ($nextNextNonEmpty === $closeParenthesis) {
+ // No parameters.
+ return false;
+ }
+
+ return true;
+
+ }//end hasParameters()
+
+
+ /**
+ * Get information on all parameters passed to a function call.
+ *
+ * Expects to be passed the T_STRING or T_VARIABLE stack pointer for the function call.
+ * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
+ *
+ * Will return an multi-dimentional array with the start token pointer, end token
+ * pointer and raw parameter value for all parameters. Index will be 1-based.
+ * If no parameters are found, will return an empty array.
+ *
+ * Extra feature: If passed an T_ARRAY or T_OPEN_SHORT_ARRAY stack pointer,
+ * it will tokenize the values / key/value pairs contained in the array call.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the T_STRING, T_VARIABLE, T_ARRAY,
+ * T_OPEN_SHORT_ARRAY, T_ISSET, T_UNSET or T_LIST token.
+ *
+ * @return array
+ */
+ public static function getParameters(File $phpcsFile, $stackPtr)
+ {
+ if (self::hasParameters($phpcsFile, $stackPtr) === false) {
+ return [];
+ }
+
+ // Ok, we know we have a valid token with parameters and valid open & close brackets/parenthesis.
+ $tokens = $phpcsFile->getTokens();
+
+ // Mark the beginning and end tokens.
+ if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY) {
+ $opener = $stackPtr;
+ $closer = $tokens[$stackPtr]['bracket_closer'];
+
+ $nestedParenthesisCount = 0;
+ } else {
+ $opener = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
+ $closer = $tokens[$opener]['parenthesis_closer'];
+
+ $nestedParenthesisCount = 1;
+ }
+
+ // Which nesting level is the one we are interested in ?
+ if (isset($tokens[$opener]['nested_parenthesis']) === true) {
+ $nestedParenthesisCount += count($tokens[$opener]['nested_parenthesis']);
+ }
+
+ $parameters = [];
+ $nextComma = $opener;
+ $paramStart = ($opener + 1);
+ $cnt = 1;
+ $stopPoints = self::$callParsingStopPoints;
+ $stopPoints[] = $tokens[$closer]['code'];
+
+ while (($nextComma = $phpcsFile->findNext($stopPoints, ($nextComma + 1), ($closer + 1))) !== false) {
+ // Ignore anything within short array definition brackets.
+ if ($tokens[$nextComma]['code'] === T_OPEN_SHORT_ARRAY) {
+ // Skip forward to the end of the short array definition.
+ $nextComma = $tokens[$nextComma]['bracket_closer'];
+ continue;
+ }
+
+ // Skip past nested arrays.
+ if ($tokens[$nextComma]['code'] === T_ARRAY
+ && isset($tokens[$nextComma]['parenthesis_opener'], $tokens[$tokens[$nextComma]['parenthesis_opener']]['parenthesis_closer']) === true
+ ) {
+ $nextComma = $tokens[$tokens[$nextComma]['parenthesis_opener']]['parenthesis_closer'];
+ continue;
+ }
+
+ // Skip past closures and anonymous classes passed as function parameters.
+ if (($tokens[$nextComma]['code'] === T_CLOSURE
+ || $tokens[$nextComma]['code'] === T_ANON_CLASS)
+ && (isset($tokens[$nextComma]['scope_condition']) === true
+ && $tokens[$nextComma]['scope_condition'] === $nextComma)
+ && isset($tokens[$nextComma]['scope_closer']) === true
+ ) {
+ // Skip forward to the end of the closure/anonymous class declaration.
+ $nextComma = $tokens[$nextComma]['scope_closer'];
+ continue;
+ }
+
+ // Ignore comma's at a lower nesting level.
+ if ($tokens[$nextComma]['code'] === T_COMMA
+ && isset($tokens[$nextComma]['nested_parenthesis']) === true
+ && count($tokens[$nextComma]['nested_parenthesis']) !== $nestedParenthesisCount
+ ) {
+ continue;
+ }
+
+ // Ignore closing parenthesis/bracket if not 'ours'.
+ if ($tokens[$nextComma]['code'] === $tokens[$closer]['code'] && $nextComma !== $closer) {
+ continue;
+ }
+
+ // Ok, we've reached the end of the parameter.
+ $parameters[$cnt]['start'] = $paramStart;
+ $parameters[$cnt]['end'] = ($nextComma - 1);
+ $parameters[$cnt]['raw'] = trim($phpcsFile->getTokensAsString($paramStart, ($nextComma - $paramStart)));
+
+ // Check if there are more tokens before the closing parenthesis.
+ // Prevents function calls with trailing comma's from setting an extra parameter:
+ // `functionCall( $param1, $param2, );`.
+ $hasNextParam = $phpcsFile->findNext(
+ Tokens::$emptyTokens,
+ ($nextComma + 1),
+ $closer,
+ true,
+ null,
+ true
+ );
+ if ($hasNextParam === false) {
+ break;
+ }
+
+ // Prepare for the next parameter.
+ $paramStart = ($nextComma + 1);
+ $cnt++;
+ }//end while
+
+ return $parameters;
+
+ }//end getParameters()
+
+
+ /**
+ * Get information on a specific parameter passed.
+ *
+ * Expects to be passed the T_STRING or T_VARIABLE stack pointer for the function call.
+ * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
+ *
+ * Will return a array with the start token pointer, end token pointer and the raw value
+ * of the parameter at a specific offset.
+ * If the specified parameter is not found, will return false.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the T_STRING, T_VARIABLE, T_ARRAY,
+ * T_OPEN_SHORT_ARRAY, T_ISSET, T_UNSET or T_LIST token.
+ * @param int $paramOffset The 1-based index position of the parameter to retrieve.
+ *
+ * @return array|false
+ */
+ public static function getParameter(File $phpcsFile, $stackPtr, $paramOffset)
+ {
+ $parameters = self::getParameters($phpcsFile, $stackPtr);
+
+ if (isset($parameters[$paramOffset]) === false) {
+ return false;
+ }
+
+ return $parameters[$paramOffset];
+
+ }//end getParameter()
+
+
+ /**
+ * Count the number of parameters which have been passed.
+ *
+ * Expects to be passed the T_STRING or T_VARIABLE stack pointer for the function call.
+ * If passed a T_STRING which is *not* a function call, the behaviour is unreliable.
+ *
+ * Extra feature: If passed an T_ARRAY or T_OPEN_SHORT_ARRAY stack pointer,
+ * it will return the number of values in the array.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the T_STRING, T_VARIABLE, T_ARRAY,
+ * T_OPEN_SHORT_ARRAY, T_ISSET, T_UNSET or T_LIST token.
+ *
+ * @return int
+ */
+ public static function getParameterCount(File $phpcsFile, $stackPtr)
+ {
+ if (self::hasParameters($phpcsFile, $stackPtr) === false) {
+ return 0;
+ }
+
+ return count(self::getParameters($phpcsFile, $stackPtr));
+
+ }//end getParameterCount()
+
+
+ /**
+ * Get the position of the double arrow within an array item.
+ *
+ * Expects to be passed the parameter start/end as retrieved via getParameters().
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being examined.
+ * @param int $start Stack pointer to the start of the array item.
+ * @param int $end Stack pointer to the end of the array item.
+ *
+ * @return int|false Pointer to the double arrow if this array item has an index or false otherwise.
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the start or end positions are invalid.
+ */
+ public static function getDoubleArrowPosition(File $phpcsFile, $start, $end)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if (isset($tokens[$start], $tokens[$end]) === false || $start > $end) {
+ throw new RuntimeException(
+ 'Invalid start and/or end position passed to getDoubleArrowPosition(). Received: $start '.$start.', $end '.$end
+ );
+ }
+
+ $doubleArrow = $phpcsFile->findNext(
+ self::$doubleArrowTargets,
+ $start,
+ ($end + 1)
+ );
+
+ if ($doubleArrow !== false && $tokens[$doubleArrow]['code'] === T_DOUBLE_ARROW) {
+ return $doubleArrow;
+ }
+
+ return false;
+
+ }//end getDoubleArrowPosition()
+
+
+}//end class
diff --git a/src/Util/Sniffs/TextStrings.php b/src/Util/Sniffs/TextStrings.php
new file mode 100644
index 0000000000..dbec9dc6c6
--- /dev/null
+++ b/src/Util/Sniffs/TextStrings.php
@@ -0,0 +1,111 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Files\File;
+
+class TextStrings
+{
+
+
+ /**
+ * Get the complete contents of a multi-line text string.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr Pointer to the first text string token
+ * of a multi-line text string or to a
+ * Nowdoc/Heredoc opener.
+ * @param bool $stripQuotes Optional. Whether to strip text delimiter
+ * quotes off the resulting text string.
+ * Defaults to true.
+ *
+ * @return string
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
+ * valid text string token or if the
+ * token is not the first text string token.
+ */
+ public static function getCompleteTextString(File $phpcsFile, $stackPtr, $stripQuotes=true)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Must be the start of a text string token.
+ if ($tokens[$stackPtr]['code'] !== T_START_HEREDOC
+ && $tokens[$stackPtr]['code'] !== T_START_NOWDOC
+ && $tokens[$stackPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING
+ && $tokens[$stackPtr]['code'] !== T_DOUBLE_QUOTED_STRING
+ ) {
+ throw new RuntimeException('$stackPtr must be of type T_START_HEREDOC, T_START_NOWDOC, T_CONSTANT_ENCAPSED_STRING or T_DOUBLE_QUOTED_STRING');
+ }
+
+ if ($tokens[$stackPtr]['code'] === T_CONSTANT_ENCAPSED_STRING
+ || $tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING
+ ) {
+ $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
+ if ($tokens[$stackPtr]['code'] === $tokens[$prev]['code']) {
+ throw new RuntimeException('$stackPtr must be the start of the text string');
+ }
+ }
+
+ switch ($tokens[$stackPtr]['code']) {
+ case T_START_HEREDOC:
+ $stripQuotes = false;
+ $targetType = T_HEREDOC;
+ $current = ($stackPtr + 1);
+ break;
+
+ case T_START_NOWDOC:
+ $stripQuotes = false;
+ $targetType = T_NOWDOC;
+ $current = ($stackPtr + 1);
+ break;
+
+ default:
+ $targetType = $tokens[$stackPtr]['code'];
+ $current = $stackPtr;
+ break;
+ }
+
+ $string = '';
+ do {
+ $string .= $tokens[$current]['content'];
+ ++$current;
+ } while ($tokens[$current]['code'] === $targetType);
+
+ if ($stripQuotes === true) {
+ return self::stripQuotes($string);
+ }
+
+ return $string;
+
+ }//end getCompleteTextString()
+
+
+ /**
+ * Strip text delimiter quotes from an arbitrary string.
+ *
+ * Intended for use with the "contents" of a T_CONSTANT_ENCAPSED_STRING / T_DOUBLE_QUOTED_STRING.
+ *
+ * Prevents stripping mis-matched quotes.
+ * Prevents stripping quotes from the textual content of the string.
+ *
+ * @param string $string The raw string.
+ *
+ * @return string String without quotes around it.
+ */
+ public static function stripQuotes($string)
+ {
+ return preg_replace('`^([\'"])(.*)\1$`Ds', '$2', $string);
+
+ }//end stripQuotes()
+
+
+}//end class
diff --git a/src/Util/Sniffs/TokenIs.php b/src/Util/Sniffs/TokenIs.php
new file mode 100644
index 0000000000..3eb6b0427a
--- /dev/null
+++ b/src/Util/Sniffs/TokenIs.php
@@ -0,0 +1,296 @@
+
+ * @author Juliette Reinders Folmer
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class TokenIs
+{
+
+
+ /**
+ * Determine if the passed token is a reference operator.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position of the T_BITWISE_AND token.
+ *
+ * @return boolean True if the specified token represents a reference.
+ * False if the token represents a bitwise operator or is not
+ * a T_BITWISE_AND token.
+ */
+ public static function isReference(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] !== T_BITWISE_AND) {
+ return false;
+ }
+
+ $tokenBefore = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+
+ if ($tokens[$tokenBefore]['code'] === T_FUNCTION) {
+ // Function returns a reference.
+ return true;
+ }
+
+ if ($tokens[$tokenBefore]['code'] === T_DOUBLE_ARROW) {
+ // Inside a foreach loop or array assignment, this is a reference.
+ return true;
+ }
+
+ if ($tokens[$tokenBefore]['code'] === T_AS) {
+ // Inside a foreach loop, this is a reference.
+ return true;
+ }
+
+ if (isset(Tokens::$assignmentTokens[$tokens[$tokenBefore]['code']]) === true) {
+ // This is directly after an assignment. It's a reference. Even if
+ // it is part of an operation, the other tests will handle it.
+ return true;
+ }
+
+ $tokenAfter = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+
+ if ($tokens[$tokenAfter]['code'] === T_NEW) {
+ return true;
+ }
+
+ $lastOpener = Parentheses::getLastOpener($phpcsFile, $stackPtr);
+ if ($lastOpener !== false) {
+ $lastOwner = Parentheses::lastOwnerIn($phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE]);
+ if ($lastOwner !== false) {
+ $params = FunctionDeclarations::getParameters($phpcsFile, $lastOwner);
+ foreach ($params as $param) {
+ $varToken = $tokenAfter;
+ if ($param['variable_length'] === true) {
+ $varToken = $phpcsFile->findNext(
+ (Tokens::$emptyTokens + [T_ELLIPSIS]),
+ ($stackPtr + 1),
+ null,
+ true
+ );
+ }
+
+ if ($param['token'] === $varToken
+ && $param['pass_by_reference'] === true
+ ) {
+ // Function parameter declared to be passed by reference.
+ return true;
+ }
+ }
+ } else if (isset($tokens[$lastOpener]['parenthesis_owner']) === false) {
+ $prev = false;
+ for ($t = ($lastOpener - 1); $t >= 0; $t--) {
+ if ($tokens[$t]['code'] !== T_WHITESPACE) {
+ $prev = $t;
+ break;
+ }
+ }
+
+ if ($prev !== false && $tokens[$prev]['code'] === T_USE) {
+ // Closure use by reference.
+ return true;
+ }
+ }//end if
+ }//end if
+
+ // Pass by reference in function calls and assign by reference in arrays.
+ if ($tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS
+ || $tokens[$tokenBefore]['code'] === T_COMMA
+ || $tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY
+ ) {
+ if ($tokens[$tokenAfter]['code'] === T_VARIABLE) {
+ return true;
+ } else {
+ $skip = Tokens::$emptyTokens;
+ $skip[] = T_NS_SEPARATOR;
+ $skip[] = T_SELF;
+ $skip[] = T_PARENT;
+ $skip[] = T_STATIC;
+ $skip[] = T_STRING;
+ $skip[] = T_NAMESPACE;
+ $skip[] = T_DOUBLE_COLON;
+
+ $nextSignificantAfter = $phpcsFile->findNext(
+ $skip,
+ ($stackPtr + 1),
+ null,
+ true
+ );
+ if ($tokens[$nextSignificantAfter]['code'] === T_VARIABLE) {
+ return true;
+ }
+ }//end if
+ }//end if
+
+ return false;
+
+ }//end isReference()
+
+
+ /**
+ * Determine whether a T_OPEN/CLOSE_SHORT_ARRAY token is a short list() construct.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the array bracket token.
+ *
+ * @return bool True if the token passed is the open/close bracket of a short list.
+ * False if the token is a short array bracket or not
+ * a T_OPEN/CLOSE_SHORT_ARRAY token.
+ */
+ public static function isShortList(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ // Is this one of the tokens this function handles ?
+ if ($tokens[$stackPtr]['code'] !== T_OPEN_SHORT_ARRAY
+ && $tokens[$stackPtr]['code'] !== T_CLOSE_SHORT_ARRAY
+ ) {
+ return false;
+ }
+
+ switch ($tokens[$stackPtr]['code']) {
+ case T_OPEN_SHORT_ARRAY:
+ $opener = $stackPtr;
+ $closer = $tokens[$stackPtr]['bracket_closer'];
+ break;
+
+ case T_CLOSE_SHORT_ARRAY:
+ $opener = $tokens[$stackPtr]['bracket_opener'];
+ $closer = $stackPtr;
+ break;
+ }
+
+ $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($closer + 1), null, true, null, true);
+ if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === T_EQUAL) {
+ return true;
+ }
+
+ // Check for short list in foreach, i.e. `foreach($array as [$a, $b])`.
+ $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), null, true, null, true);
+ if ($prevNonEmpty !== false
+ && ($tokens[$prevNonEmpty]['code'] === T_AS
+ || $tokens[$prevNonEmpty]['code'] === T_DOUBLE_ARROW)
+ && Parentheses::lastOwnerIn($phpcsFile, $prevNonEmpty, T_FOREACH) !== false
+ ) {
+ return true;
+ }
+
+ // Maybe this is a short list syntax nested inside another short list syntax ?
+ $parentOpen = $opener;
+ do {
+ $parentOpen = $phpcsFile->findPrevious(
+ T_OPEN_SHORT_ARRAY,
+ ($parentOpen - 1),
+ null,
+ false,
+ null,
+ true
+ );
+
+ if ($parentOpen === false) {
+ return false;
+ }
+ } while ($tokens[$parentOpen]['bracket_closer'] < $opener);
+
+ return self::isShortList($phpcsFile, $parentOpen);
+
+ }//end isShortList()
+
+
+ /**
+ * Determine whether a T_MINUS/T_PLUS token is a unary operator.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the plus/minus token.
+ *
+ * @return bool True if the token passed is a unary operator.
+ * False otherwise or if the token is not a T_PLUS/T_MINUS token.
+ */
+ public static function isUnaryPlusMinus(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if (isset($tokens[$stackPtr]) === false
+ || ($tokens[$stackPtr]['code'] !== T_PLUS
+ && $tokens[$stackPtr]['code'] !== T_MINUS)
+ ) {
+ return false;
+ }
+
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+ if ($next === false) {
+ // Live coding or parse error.
+ return false;
+ }
+
+ if (isset(Tokens::$operators[$tokens[$next]['code']]) === true) {
+ // Next token is an operator, so this is not a unary.
+ return false;
+ }
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+
+ if ($tokens[$prev]['code'] === T_RETURN) {
+ // Just returning a positive/negative value; eg. (return -1).
+ return true;
+ }
+
+ if (isset(Tokens::$operators[$tokens[$prev]['code']]) === true) {
+ // Just trying to operate on a positive/negative value; eg. ($var * -1).
+ return true;
+ }
+
+ if (isset(Tokens::$comparisonTokens[$tokens[$prev]['code']]) === true) {
+ // Just trying to compare a positive/negative value; eg. ($var === -1).
+ return true;
+ }
+
+ if (isset(Tokens::$booleanOperators[$tokens[$prev]['code']]) === true) {
+ // Just trying to compare a positive/negative value; eg. ($var || -1 === $b).
+ return true;
+ }
+
+ if (isset(Tokens::$assignmentTokens[$tokens[$prev]['code']]) === true) {
+ // Just trying to assign a positive/negative value; eg. ($var = -1).
+ return true;
+ }
+
+ if (isset(Tokens::$castTokens[$tokens[$prev]['code']]) === true) {
+ // Just casting a positive/negative value; eg. (string) -$var.
+ return true;
+ }
+
+ // Other indicators that a plus/minus sign is a unary operator.
+ $invalidTokens = [
+ T_COMMA => true,
+ T_OPEN_PARENTHESIS => true,
+ T_OPEN_SQUARE_BRACKET => true,
+ T_OPEN_SHORT_ARRAY => true,
+ T_COLON => true,
+ T_INLINE_THEN => true,
+ T_INLINE_ELSE => true,
+ T_CASE => true,
+ T_OPEN_CURLY_BRACKET => true,
+ ];
+
+ if (isset($invalidTokens[$tokens[$prev]['code']]) === true) {
+ // Just trying to use a positive/negative value; eg. myFunction($var, -2).
+ return true;
+ }
+
+ return false;
+
+ }//end isUnaryPlusMinus()
+
+
+}//end class
diff --git a/src/Util/Sniffs/UseStatements.php b/src/Util/Sniffs/UseStatements.php
new file mode 100644
index 0000000000..9b138b82eb
--- /dev/null
+++ b/src/Util/Sniffs/UseStatements.php
@@ -0,0 +1,284 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Tokens;
+
+class UseStatements
+{
+
+
+ /**
+ * Determine what a T_USE token is used for.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the T_USE token.
+ *
+ * @return string Either 'closure', 'import' or 'trait'.
+ * An empty string will be returned if the token is used in an
+ * invalid context or if it couldn't be reliably determined
+ * what the T_USE token is used for.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
+ * T_USE token.
+ */
+ public static function getType(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if (isset($tokens[$stackPtr]) === false
+ || $tokens[$stackPtr]['code'] !== T_USE
+ ) {
+ throw new RuntimeException('$stackPtr must be of type T_USE');
+ }
+
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+ if ($next === false) {
+ // Live coding or parse error.
+ return '';
+ }
+
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ if ($prev !== false && $tokens[$prev]['code'] === T_CLOSE_PARENTHESIS
+ && Parentheses::isOwnerIn($phpcsFile, $prev, T_CLOSURE) === true
+ ) {
+ return 'closure';
+ }
+
+ $lastCondition = Conditions::getLastCondition($phpcsFile, $stackPtr);
+
+ if ($lastCondition === false || $tokens[$lastCondition]['code'] === T_NAMESPACE) {
+ // Global or scoped namespace and not a closure use statement.
+ return 'import';
+ }
+
+ $traitScopes = Tokens::$ooScopeTokens;
+ // Only classes and traits can import traits.
+ unset($traitScopes[T_INTERFACE]);
+
+ if (isset($traitScopes[$tokens[$lastCondition]['code']]) === true) {
+ return 'trait';
+ }
+
+ return '';
+
+ }//end getType()
+
+
+ /**
+ * Determine whether a T_USE token represents a closure use statement.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the T_USE token.
+ *
+ * @return bool True if the token passed is a closure use statement.
+ * False if it's not.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
+ * T_USE token.
+ */
+ public static function isClosureUse(File $phpcsFile, $stackPtr)
+ {
+ return (self::getType($phpcsFile, $stackPtr) === 'closure');
+
+ }//end isClosureUse()
+
+
+ /**
+ * Determine whether a T_USE token represents a class/function/constant import use statement.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the T_USE token.
+ *
+ * @return bool True if the token passed is an import use statement.
+ * False if it's not.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
+ * T_USE token.
+ */
+ public static function isImportUse(File $phpcsFile, $stackPtr)
+ {
+ return (self::getType($phpcsFile, $stackPtr) === 'import');
+
+ }//end isImportUse()
+
+
+ /**
+ * Determine whether a T_USE token represents a trait use statement.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the T_USE token.
+ *
+ * @return bool True if the token passed is a trait use statement.
+ * False if it's not.
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
+ * T_USE token.
+ */
+ public static function isTraitUse(File $phpcsFile, $stackPtr)
+ {
+ return (self::getType($phpcsFile, $stackPtr) === 'trait');
+
+ }//end isTraitUse()
+
+
+ /**
+ * Split an import use statement into individual imports.
+ *
+ * Handles single import, multi-import and group-import statements.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the T_USE token.
+ *
+ * @return array A multi-level array containing information about the use statement.
+ * The first level is 'name', 'function' and 'const'. These keys will always exist.
+ * If any statements are found for any of these categories, the second level
+ * will contain the alias/name as the key and the full original use name as the
+ * value for each of the found imports or an empty array if no imports were found
+ * in this use statement for this category.
+ *
+ * For example, for this function group use statement:
+ * `use function Vendor\Package\{LevelA\Name as Alias, LevelB\Another_Name}`
+ * the return value would look like this:
+ * `[
+ * 'name' => [],
+ * 'function' => [
+ * 'Alias' => 'Vendor\Package\LevelA\Name',
+ * 'Another_Name' => 'Vendor\Package\LevelB\Another_Name',
+ * ],
+ * 'const' => [],
+ * ]`
+ *
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
+ * T_USE token or not an import use statement.
+ */
+ public static function splitImportUseStatement(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] !== T_USE) {
+ throw new RuntimeException('$stackPtr must be of type T_USE');
+ }
+
+ if (self::isImportUse($phpcsFile, $stackPtr) === false) {
+ throw new RuntimeException('$stackPtr must be an import use statement');
+ }
+
+ $statements = [
+ 'name' => [],
+ 'function' => [],
+ 'const' => [],
+ ];
+
+ $endOfStatement = $phpcsFile->findNext([T_SEMICOLON, T_CLOSE_TAG], ($stackPtr + 1));
+ if ($endOfStatement === false) {
+ // Live coding or parse error.
+ return $statements;
+ }
+
+ $endOfStatement++;
+
+ $start = true;
+ $useGroup = false;
+ $hasAlias = false;
+ $baseName = '';
+ $name = '';
+ $type = '';
+ $fixedType = false;
+ $alias = '';
+
+ for ($i = ($stackPtr + 1); $i < $endOfStatement; $i++) {
+ if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
+ continue;
+ }
+
+ switch ($tokens[$i]['code']) {
+ case T_STRING:
+ // Only when either at the start of the statement or at the start of a new sub within a group.
+ if ($start === true && $fixedType === false) {
+ $content = strtolower($tokens[$i]['content']);
+ if ($content === 'function'
+ || $content === 'const'
+ ) {
+ $type = $content;
+ $start = false;
+ if ($useGroup === false) {
+ $fixedType = true;
+ }
+
+ break;
+ } else {
+ $type = 'name';
+ }
+ }
+
+ $start = false;
+
+ if ($hasAlias === false) {
+ $name .= $tokens[$i]['content'];
+ }
+
+ $alias = $tokens[$i]['content'];
+ break;
+
+ case T_AS:
+ $hasAlias = true;
+ break;
+
+ case T_OPEN_USE_GROUP:
+ $start = true;
+ $useGroup = true;
+ $baseName = $name;
+ $name = '';
+ break;
+
+ case T_SEMICOLON:
+ case T_CLOSE_TAG:
+ case T_CLOSE_USE_GROUP:
+ case T_COMMA:
+ if ($name !== '') {
+ if ($useGroup === true) {
+ $statements[$type][$alias] = $baseName.$name;
+ } else {
+ $statements[$type][$alias] = $name;
+ }
+ }
+
+ if ($tokens[$i]['code'] !== T_COMMA) {
+ return $statements;
+ }
+
+ // Reset.
+ $start = true;
+ $name = '';
+ $hasAlias = false;
+ if ($fixedType === false) {
+ $type = '';
+ }
+ break;
+
+ case T_NS_SEPARATOR:
+ $name .= $tokens[$i]['content'];
+ break;
+
+ // Fall back in case reserved keyword is (illegally) used in name.
+ // Parse error, but not our concern.
+ default:
+ $name .= $tokens[$i]['content'];
+ break;
+ }//end switch
+ }//end for
+
+ }//end splitImportUseStatement()
+
+
+}//end class
diff --git a/src/Util/Sniffs/Variables.php b/src/Util/Sniffs/Variables.php
new file mode 100644
index 0000000000..7df142dec8
--- /dev/null
+++ b/src/Util/Sniffs/Variables.php
@@ -0,0 +1,290 @@
+
+ * @author Juliette Reinders Folmer
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Util\Sniffs;
+
+use PHP_CodeSniffer\Exceptions\RuntimeException;
+use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Util\Sniffs\TextStrings;
+use PHP_CodeSniffer\Util\Tokens;
+
+class Variables
+{
+
+
+ /**
+ * List of PHP Reserved variables.
+ *
+ * @var array =>
+ *
+ * @link http://php.net/manual/en/reserved.variables.php
+ */
+ public static $phpReservedVars = [
+ '_SERVER' => true,
+ '_GET' => true,
+ '_POST' => true,
+ '_REQUEST' => true,
+ '_SESSION' => true,
+ '_ENV' => true,
+ '_COOKIE' => true,
+ '_FILES' => true,
+ 'GLOBALS' => true,
+ 'http_response_header' => false,
+ 'argc' => false,
+ 'argv' => false,
+
+ // Deprecated.
+ 'php_errormsg' => false,
+
+ // Removed PHP 5.4.0.
+ 'HTTP_SERVER_VARS' => false,
+ 'HTTP_GET_VARS' => false,
+ 'HTTP_POST_VARS' => false,
+ 'HTTP_SESSION_VARS' => false,
+ 'HTTP_ENV_VARS' => false,
+ 'HTTP_COOKIE_VARS' => false,
+ 'HTTP_POST_FILES' => false,
+
+ // Removed PHP 5.6.0.
+ 'HTTP_RAW_POST_DATA' => false,
+ ];
+
+
+ /**
+ * Returns the visibility and implementation properties of the class member
+ * variable found at the specified position in the stack.
+ *
+ * The format of the array is:
+ *
+ *
+ * array(
+ * 'scope' => 'public', // public protected or protected.
+ * 'scope_specified' => false, // true if the scope was explicitly specified.
+ * 'is_static' => false, // true if the static keyword was found.
+ * );
+ *
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of the T_VARIABLE token to
+ * acquire the properties for.
+ *
+ * @return array
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
+ * T_VARIABLE token, or if the position is not
+ * a class member variable.
+ */
+ public static function getMemberProperties(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] !== T_VARIABLE) {
+ throw new RuntimeException('$stackPtr must be of type T_VARIABLE');
+ }
+
+ if (Conditions::isOOProperty($phpcsFile, $stackPtr) === false) {
+ $lastCondition = Conditions::getlastCondition($phpcsFile, $stackPtr);
+ if ($lastCondition !== false
+ && $tokens[$lastCondition]['code'] === T_INTERFACE
+ ) {
+ // T_VARIABLEs in interfaces can actually be method arguments
+ // but they wont be seen as being inside the method because there
+ // are no scope openers and closers for abstract methods. If it is in
+ // parentheses, we can be pretty sure it is a method argument.
+ if (empty($tokens[$stackPtr]['nested_parenthesis']) === true) {
+ $error = 'Possible parse error: interfaces may not include member vars';
+ $phpcsFile->addWarning($error, $stackPtr, 'Internal.ParseError.InterfaceHasMemberVar');
+ return [];
+ }
+ } else {
+ throw new RuntimeException('$stackPtr is not a class member var');
+ }
+ }
+
+ $valid = [
+ T_PUBLIC => T_PUBLIC,
+ T_PRIVATE => T_PRIVATE,
+ T_PROTECTED => T_PROTECTED,
+ T_STATIC => T_STATIC,
+ T_VAR => T_VAR,
+ ];
+
+ $valid += Tokens::$emptyTokens;
+
+ $scope = 'public';
+ $scopeSpecified = false;
+ $isStatic = false;
+
+ $startOfStatement = $phpcsFile->findPrevious(
+ [
+ T_SEMICOLON,
+ T_OPEN_CURLY_BRACKET,
+ T_CLOSE_CURLY_BRACKET,
+ ],
+ ($stackPtr - 1)
+ );
+
+ for ($i = ($startOfStatement + 1); $i < $stackPtr; $i++) {
+ if (isset($valid[$tokens[$i]['code']]) === false) {
+ break;
+ }
+
+ switch ($tokens[$i]['code']) {
+ case T_PUBLIC:
+ $scope = 'public';
+ $scopeSpecified = true;
+ break;
+ case T_PRIVATE:
+ $scope = 'private';
+ $scopeSpecified = true;
+ break;
+ case T_PROTECTED:
+ $scope = 'protected';
+ $scopeSpecified = true;
+ break;
+ case T_STATIC:
+ $isStatic = true;
+ break;
+ }
+ }//end for
+
+ return [
+ 'scope' => $scope,
+ 'scope_specified' => $scopeSpecified,
+ 'is_static' => $isStatic,
+ ];
+
+ }//end getMemberProperties()
+
+
+ /**
+ * Verify if a given variable name is the name of a PHP reserved variable.
+ *
+ * @param string $name The full variable name with or without leading dollar sign.
+ * This allows for passing an array key variable name, such as
+ * '_GET' retrieved from $GLOBALS['_GET'].
+ * Note: when passing an array key, string quotes are expected
+ * to have been stripped already.
+ *
+ * @return bool
+ */
+ public static function isPHPReservedVarName($name)
+ {
+ if (strpos($name, '$') === 0) {
+ $name = substr($name, 1);
+ }
+
+ return (isset(self::$phpReservedVars[$name]) === true);
+
+ }//end isPHPReservedVarName()
+
+
+ /**
+ * Verify if a given variable or array key token points to a PHP superglobal.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr The position in the stack of a T_VARIABLE
+ * token or the array key to a variable in $GLOBALS.
+ *
+ * @return bool
+ */
+ public static function isSuperglobal(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] !== T_VARIABLE
+ && $tokens[$stackPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING
+ ) {
+ return false;
+ }
+
+ $content = $tokens[$stackPtr]['content'];
+
+ if ($tokens[$stackPtr]['code'] === T_CONSTANT_ENCAPSED_STRING) {
+ $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+ $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
+ if ($prev === false || $tokens[$prev]['code'] !== T_OPEN_SQUARE_BRACKET
+ && $next === false || $tokens[$next]['code'] !== T_CLOSE_SQUARE_BRACKET
+ ) {
+ return false;
+ }
+
+ $pprev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true);
+ if ($pprev === false
+ || $tokens[$pprev]['code'] !== T_VARIABLE
+ || $tokens[$pprev]['content'] !== '$GLOBALS'
+ ) {
+ return false;
+ }
+
+ $content = TextStrings::stripQuotes($content);
+ }
+
+ return self::isSuperglobalName($content);
+
+ }//end isSuperglobal()
+
+
+ /**
+ * Verify if a given variable name is the name of a PHP superglobal.
+ *
+ * @param string $name The full variable name with or without leading dollar sign.
+ * This allows for passing an array key variable name, such as
+ * '_GET' retrieved from $GLOBALS['_GET'].
+ * Note: when passing an array key, string quotes are expected
+ * to have been stripped already.
+ *
+ * @return bool
+ */
+ public static function isSuperglobalName($name)
+ {
+ if (strpos($name, '$') === 0) {
+ $name = substr($name, 1);
+ }
+
+ if (isset(self::$phpReservedVars[$name]) === false) {
+ return false;
+ }
+
+ return self::$phpReservedVars[$name];
+
+ }//end isSuperglobalName()
+
+
+ /**
+ * Determine if a variable is in the `as $key => $value` part of a foreach condition.
+ *
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
+ * @param int $stackPtr Pointer to the variable.
+ *
+ * @return bool True if it is. False otherwise.
+ * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
+ * T_VARIABLE token.
+ */
+ public static function isForeachAs(File $phpcsFile, $stackPtr)
+ {
+ $tokens = $phpcsFile->getTokens();
+
+ if ($tokens[$stackPtr]['code'] !== T_VARIABLE) {
+ throw new RuntimeException('$stackPtr must be of type T_VARIABLE');
+ }
+
+ if (Parentheses::lastOwnerIn($phpcsFile, $stackPtr, T_FOREACH) === false) {
+ return false;
+ }
+
+ $openParenthesis = Parentheses::getLastOpener($phpcsFile, $stackPtr);
+ $closeParenthesis = Parentheses::getLastCloser($phpcsFile, $stackPtr);
+ $asPtr = $phpcsFile->findNext(T_AS, ($openParenthesis + 1), $closeParenthesis);
+ return ($asPtr !== false && $stackPtr > $asPtr);
+
+ }//end isForeachAs()
+
+
+}//end class
diff --git a/src/Util/Tokens.php b/src/Util/Tokens.php
index 19daf1b683..2f0777d677 100644
--- a/src/Util/Tokens.php
+++ b/src/Util/Tokens.php
@@ -135,7 +135,7 @@ final class Tokens
/**
* The token weightings.
*
- * @var array
+ * @var array
*/
public static $weightings = [
T_CLASS => 1000,
@@ -216,7 +216,7 @@ final class Tokens
/**
* Tokens that represent assignments.
*
- * @var array
+ * @var array
*/
public static $assignmentTokens = [
T_EQUAL => T_EQUAL,
@@ -240,7 +240,7 @@ final class Tokens
/**
* Tokens that represent equality comparisons.
*
- * @var array
+ * @var array
*/
public static $equalityTokens = [
T_IS_EQUAL => T_IS_EQUAL,
@@ -254,7 +254,7 @@ final class Tokens
/**
* Tokens that represent comparison operator.
*
- * @var array
+ * @var array
*/
public static $comparisonTokens = [
T_IS_EQUAL => T_IS_EQUAL,
@@ -272,7 +272,7 @@ final class Tokens
/**
* Tokens that represent arithmetic operators.
*
- * @var array
+ * @var array
*/
public static $arithmeticTokens = [
T_PLUS => T_PLUS,
@@ -286,7 +286,7 @@ final class Tokens
/**
* Tokens that perform operations.
*
- * @var array
+ * @var array
*/
public static $operators = [
T_MINUS => T_MINUS,
@@ -307,7 +307,7 @@ final class Tokens
/**
* Tokens that perform boolean operations.
*
- * @var array
+ * @var array
*/
public static $booleanOperators = [
T_BOOLEAN_AND => T_BOOLEAN_AND,
@@ -320,7 +320,7 @@ final class Tokens
/**
* Tokens that represent casting.
*
- * @var array
+ * @var array
*/
public static $castTokens = [
T_INT_CAST => T_INT_CAST,
@@ -336,7 +336,7 @@ final class Tokens
/**
* Token types that open parenthesis.
*
- * @var array
+ * @var array
*/
public static $parenthesisOpeners = [
T_ARRAY => T_ARRAY,
@@ -355,7 +355,7 @@ final class Tokens
/**
* Tokens that are allowed to open scopes.
*
- * @var array
+ * @var array
*/
public static $scopeOpeners = [
T_CLASS => T_CLASS,
@@ -387,7 +387,7 @@ final class Tokens
/**
* Tokens that represent scope modifiers.
*
- * @var array
+ * @var array
*/
public static $scopeModifiers = [
T_PRIVATE => T_PRIVATE,
@@ -398,7 +398,7 @@ final class Tokens
/**
* Tokens that can prefix a method name
*
- * @var array
+ * @var array
*/
public static $methodPrefixes = [
T_PRIVATE => T_PRIVATE,
@@ -412,7 +412,7 @@ final class Tokens
/**
* Tokens that open code blocks.
*
- * @var array
+ * @var array
*/
public static $blockOpeners = [
T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
@@ -424,7 +424,7 @@ final class Tokens
/**
* Tokens that don't represent code.
*
- * @var array
+ * @var array
*/
public static $emptyTokens = [
T_WHITESPACE => T_WHITESPACE,
@@ -446,7 +446,7 @@ final class Tokens
/**
* Tokens that are comments.
*
- * @var array
+ * @var array
*/
public static $commentTokens = [
T_COMMENT => T_COMMENT,
@@ -467,7 +467,7 @@ final class Tokens
/**
* Tokens that are comments containing PHPCS instructions.
*
- * @var array
+ * @var array
*/
public static $phpcsCommentTokens = [
T_PHPCS_ENABLE => T_PHPCS_ENABLE,
@@ -482,7 +482,7 @@ final class Tokens
*
* Note that T_STRINGS are NOT represented in this list.
*
- * @var array
+ * @var array
*/
public static $stringTokens = [
T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING,
@@ -492,7 +492,7 @@ final class Tokens
/**
* Tokens that represent text strings.
*
- * @var array
+ * @var array
*/
public static $textStringTokens = [
T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING,
@@ -505,7 +505,7 @@ final class Tokens
/**
* Tokens that represent brackets and parenthesis.
*
- * @var array
+ * @var array
*/
public static $bracketTokens = [
T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET,
@@ -519,7 +519,7 @@ final class Tokens
/**
* Tokens that include files.
*
- * @var array
+ * @var array
*/
public static $includeTokens = [
T_REQUIRE_ONCE => T_REQUIRE_ONCE,
@@ -531,7 +531,7 @@ final class Tokens
/**
* Tokens that make up a heredoc string.
*
- * @var array
+ * @var array
*/
public static $heredocTokens = [
T_START_HEREDOC => T_START_HEREDOC,
@@ -548,7 +548,7 @@ final class Tokens
* Mostly, these are just strings. But PHP tokenizes some language
* constructs and functions using their own tokens.
*
- * @var array
+ * @var array
*/
public static $functionNameTokens = [
T_STRING => T_STRING,
@@ -568,7 +568,7 @@ final class Tokens
/**
* Tokens that open class and object scopes.
*
- * @var array
+ * @var array
*/
public static $ooScopeTokens = [
T_CLASS => T_CLASS,
@@ -585,7 +585,7 @@ final class Tokens
* function. If passed a string, it is assumed to be a PHPCS-supplied token
* that begins with PHPCS_T_, so the name is sourced from the token value itself.
*
- * @param int|string $token The token to get the name for.
+ * @param integer|string $token The token to get the name for.
*
* @return string
*/
@@ -611,8 +611,8 @@ public static function tokenName($token)
*
* Returns false if there are no weightings for any of the specified tokens.
*
- * @param array $tokens The token types to get the highest weighted
- * type for.
+ * @param array $tokens The token types to get the highest weighted
+ * type for.
*
* @return int The highest weighted token.
*/
diff --git a/tests/Core/AbstractMethodUnitTest.php b/tests/Core/AbstractMethodUnitTest.php
new file mode 100644
index 0000000000..327bb76acb
--- /dev/null
+++ b/tests/Core/AbstractMethodUnitTest.php
@@ -0,0 +1,137 @@
+
+ * @copyright 2018-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core;
+
+use PHP_CodeSniffer\Config;
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Files\DummyFile;
+use PHPUnit\Framework\TestCase;
+
+abstract class AbstractMethodUnitTest extends TestCase
+{
+
+ /**
+ * The file extension of the test case file (without leading dot).
+ *
+ * This allows child classes to overrule the default `inc` with, for instance,
+ * `js` or `css` when applicable.
+ *
+ * @var string
+ */
+ protected static $fileExtension = 'inc';
+
+ /**
+ * The \PHP_CodeSniffer\Files\File object containing the parsed contents of the test case file.
+ *
+ * @var \PHP_CodeSniffer\Files\File
+ */
+ protected static $phpcsFile;
+
+
+ /**
+ * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from the test case file.
+ *
+ * The test case file for a unit test class has to be in the same directory
+ * directory and use the same file name as the test class, using the .inc extension.
+ *
+ * @return void
+ */
+ public static function setUpBeforeClass()
+ {
+ $config = new Config();
+ $config->standards = ['PSR1'];
+
+ $ruleset = new Ruleset($config);
+
+ // Default to a file with the same name as the test class. Extension is property based.
+ $relativeCN = str_replace(__NAMESPACE__, '', get_called_class());
+ $relativePath = str_replace('\\', DIRECTORY_SEPARATOR, $relativeCN);
+ $pathToTestFile = realpath(__DIR__).$relativePath.'.'.static::$fileExtension;
+
+ // Make sure the file gets parsed correctly based on the file type.
+ $contents = 'phpcs_input_file: '.$pathToTestFile.PHP_EOL;
+ $contents .= file_get_contents($pathToTestFile);
+
+ self::$phpcsFile = new DummyFile($contents, $ruleset, $config);
+ self::$phpcsFile->process();
+
+ }//end setUpBeforeClass()
+
+
+ /**
+ * Clean up after finished test.
+ *
+ * @return void
+ */
+ public static function tearDownAfterClass()
+ {
+ self::$phpcsFile = null;
+
+ }//end tearDownAfterClass()
+
+
+ /**
+ * Get the token pointer for a target token based on a specific comment found on the line before.
+ *
+ * @param string $commentString The comment to look for.
+ * @param int|string|array $tokenType The type of token(s) to look for.
+ * @param string $tokenContent Optional. The token content for the target token.
+ *
+ * @return int
+ */
+ public function getTargetToken($commentString, $tokenType, $tokenContent=null)
+ {
+ $start = (self::$phpcsFile->numTokens - 1);
+ $comment = self::$phpcsFile->findPrevious(
+ T_COMMENT,
+ $start,
+ null,
+ false,
+ $commentString
+ );
+
+ $tokens = self::$phpcsFile->getTokens();
+ $end = ($start + 1);
+
+ // Limit the token finding to between this and the next case comment.
+ for ($i = ($comment + 1); $i < $end; $i++) {
+ if ($tokens[$i]['code'] !== T_COMMENT) {
+ continue;
+ }
+
+ if (stripos($tokens[$i]['content'], '/* test') === 0) {
+ $end = $i;
+ break;
+ }
+ }
+
+ $target = self::$phpcsFile->findNext(
+ $tokenType,
+ ($comment + 1),
+ $end,
+ false,
+ $tokenContent
+ );
+
+ if ($target === false) {
+ $msg = 'Failed to find test target token for comment string: '.$commentString;
+ if ($tokenContent !== null) {
+ $msg .= ' With token content: '.$tokenContent;
+ }
+
+ $this->assertFalse(true, $msg);
+ }
+
+ return $target;
+
+ }//end getTargetToken()
+
+
+}//end class
diff --git a/tests/Core/AllTests.php b/tests/Core/AllTests.php
index 36a98a9ba7..d6587f076e 100644
--- a/tests/Core/AllTests.php
+++ b/tests/Core/AllTests.php
@@ -12,15 +12,7 @@
use PHPUnit\TextUI\TestRunner;
use PHPUnit\Framework\TestSuite;
-require_once 'IsCamelCapsTest.php';
-require_once 'ErrorSuppressionTest.php';
-require_once 'File/FindEndOfStatementTest.php';
-require_once 'File/FindExtendedClassNameTest.php';
-require_once 'File/FindImplementedInterfaceNamesTest.php';
-require_once 'File/GetMemberPropertiesTest.php';
-require_once 'File/GetMethodParametersTest.php';
-require_once 'File/GetMethodPropertiesTest.php';
-require_once 'File/IsReferenceTest.php';
+require_once dirname(dirname(__DIR__)).'/scripts/ValidatePEAR/FileList.php';
class AllTests
{
@@ -46,15 +38,23 @@ public static function main()
public static function suite()
{
$suite = new TestSuite('PHP CodeSniffer Core');
- $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\IsCamelCapsTest');
- $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\ErrorSuppressionTest');
- $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\FindEndOfStatementTest');
- $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\FindExtendedClassNameTest');
- $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\FindImplementedInterfaceNamesTest');
- $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\GetMemberPropertiesTest');
- $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\GetMethodParametersTest');
- $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\GetMethodPropertiesTest');
- $suite->addTestSuite('PHP_CodeSniffer\Tests\Core\File\IsReferenceTest');
+
+ $testFileIterator = (new \FileList(__DIR__, '', '`Test\.php$`Di'))->getList();
+ foreach ($testFileIterator as $file) {
+ if (strpos($file, 'AbstractMethodUnitTest.php') !== false) {
+ continue;
+ }
+
+ include_once $file;
+
+ $class = str_replace(__DIR__, '', $file);
+ $class = str_replace('.php', '', $class);
+ $class = str_replace('/', '\\', $class);
+ $class = 'PHP_CodeSniffer\Tests\Core'.$class;
+
+ $suite->addTestSuite($class);
+ }
+
return $suite;
}//end suite()
diff --git a/tests/Core/File/FindEndOfStatementTest.php b/tests/Core/File/FindEndOfStatementTest.php
index d3cd104f56..deacd9a49c 100644
--- a/tests/Core/File/FindEndOfStatementTest.php
+++ b/tests/Core/File/FindEndOfStatementTest.php
@@ -9,55 +9,11 @@
namespace PHP_CodeSniffer\Tests\Core\File;
-use PHP_CodeSniffer\Config;
-use PHP_CodeSniffer\Ruleset;
-use PHP_CodeSniffer\Files\DummyFile;
-use PHPUnit\Framework\TestCase;
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
-class FindEndOfStatementTest extends TestCase
+class FindEndOfStatementTest extends AbstractMethodUnitTest
{
- /**
- * The PHP_CodeSniffer_File object containing parsed contents of the test case file.
- *
- * @var \PHP_CodeSniffer\Files\File
- */
- private $phpcsFile;
-
-
- /**
- * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from the test case file.
- *
- * Methods used for these tests can be found in a test case file in the same
- * directory and with the same name, using the .inc extension.
- *
- * @return void
- */
- public function setUp()
- {
- $config = new Config();
- $config->standards = ['Generic'];
-
- $ruleset = new Ruleset($config);
-
- $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
- $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
- $this->phpcsFile->process();
-
- }//end setUp()
-
-
- /**
- * Clean up after finished test.
- *
- * @return void
- */
- public function tearDown()
- {
- unset($this->phpcsFile);
-
- }//end tearDown()
-
/**
* Test a simple assignment.
@@ -66,10 +22,10 @@ public function tearDown()
*/
public function testSimpleAssignment()
{
- $start = ($this->phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testSimpleAssignment */') + 2);
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $start = (self::$phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testSimpleAssignment */') + 2);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 5)], $tokens[$found]);
}//end testSimpleAssignment()
@@ -82,10 +38,10 @@ public function testSimpleAssignment()
*/
public function testControlStructure()
{
- $start = ($this->phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testControlStructure */') + 2);
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $start = (self::$phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testControlStructure */') + 2);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 6)], $tokens[$found]);
}//end testControlStructure()
@@ -98,10 +54,10 @@ public function testControlStructure()
*/
public function testClosureAssignment()
{
- $start = ($this->phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testClosureAssignment */') + 2);
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $start = (self::$phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testClosureAssignment */') + 2);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 13)], $tokens[$found]);
}//end testClosureAssignment()
@@ -115,24 +71,24 @@ public function testClosureAssignment()
public function testHeredocFunctionArg()
{
// Find the end of the function.
- $start = ($this->phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testHeredocFunctionArg */') + 2);
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $start = (self::$phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testHeredocFunctionArg */') + 2);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 10)], $tokens[$found]);
// Find the end of the heredoc.
$start += 2;
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 4)], $tokens[$found]);
// Find the end of the last arg.
$start = ($found + 2);
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[$start], $tokens[$found]);
}//end testHeredocFunctionArg()
@@ -146,24 +102,24 @@ public function testHeredocFunctionArg()
public function testSwitch()
{
// Find the end of the switch.
- $start = ($this->phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testSwitch */') + 2);
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $start = (self::$phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testSwitch */') + 2);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 28)], $tokens[$found]);
// Find the end of the case.
$start += 9;
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 8)], $tokens[$found]);
// Find the end of default case.
$start += 11;
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 6)], $tokens[$found]);
}//end testSwitch()
@@ -177,24 +133,24 @@ public function testSwitch()
public function testStatementAsArrayValue()
{
// Test short array syntax.
- $start = ($this->phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testStatementAsArrayValue */') + 7);
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $start = (self::$phpcsFile->findNext(T_COMMENT, 0, null, false, '/* testStatementAsArrayValue */') + 7);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 2)], $tokens[$found]);
// Test long array syntax.
$start += 12;
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 2)], $tokens[$found]);
// Test same statement outside of array.
$start += 10;
- $found = $this->phpcsFile->findEndOfStatement($start);
+ $found = self::$phpcsFile->findEndOfStatement($start);
- $tokens = $this->phpcsFile->getTokens();
+ $tokens = self::$phpcsFile->getTokens();
$this->assertSame($tokens[($start + 3)], $tokens[$found]);
}//end testStatementAsArrayValue()
diff --git a/tests/Core/File/FindExtendedClassNameTest.php b/tests/Core/File/FindExtendedClassNameTest.php
deleted file mode 100644
index 5ebb4d9f56..0000000000
--- a/tests/Core/File/FindExtendedClassNameTest.php
+++ /dev/null
@@ -1,146 +0,0 @@
-
- * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
- * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
- */
-
-namespace PHP_CodeSniffer\Tests\Core\File;
-
-use PHP_CodeSniffer\Config;
-use PHP_CodeSniffer\Ruleset;
-use PHP_CodeSniffer\Files\DummyFile;
-use PHPUnit\Framework\TestCase;
-
-class FindExtendedClassNameTest extends TestCase
-{
-
- /**
- * The PHP_CodeSniffer_File object containing parsed contents of the test case file.
- *
- * @var \PHP_CodeSniffer\Files\File
- */
- private $phpcsFile;
-
-
- /**
- * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from the test case file.
- *
- * Methods used for these tests can be found in a test case file in the same
- * directory and with the same name, using the .inc extension.
- *
- * @return void
- */
- public function setUp()
- {
- $config = new Config();
- $config->standards = ['Generic'];
-
- $ruleset = new Ruleset($config);
-
- $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
- $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
- $this->phpcsFile->process();
-
- }//end setUp()
-
-
- /**
- * Clean up after finished test.
- *
- * @return void
- */
- public function tearDown()
- {
- unset($this->phpcsFile);
-
- }//end tearDown()
-
-
- /**
- * Test retrieving the name of the class being extended by another class
- * (or interface).
- *
- * @param string $identifier Comment which precedes the test case.
- * @param bool $expected Expected function output.
- *
- * @dataProvider dataExtendedClass
- *
- * @return void
- */
- public function testFindExtendedClassName($identifier, $expected)
- {
- $start = ($this->phpcsFile->numTokens - 1);
- $delim = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- $identifier
- );
- $OOToken = $this->phpcsFile->findNext([T_CLASS, T_ANON_CLASS, T_INTERFACE], ($delim + 1));
-
- $result = $this->phpcsFile->findExtendedClassName($OOToken);
- $this->assertSame($expected, $result);
-
- }//end testFindExtendedClassName()
-
-
- /**
- * Data provider for the FindExtendedClassName test.
- *
- * @see testFindExtendedClassName()
- *
- * @return array
- */
- public function dataExtendedClass()
- {
- return [
- [
- '/* testExtendedClass */',
- 'testFECNClass',
- ],
- [
- '/* testNamespacedClass */',
- '\PHP_CodeSniffer\Tests\Core\File\testFECNClass',
- ],
- [
- '/* testNonExtendedClass */',
- false,
- ],
- [
- '/* testInterface */',
- false,
- ],
- [
- '/* testInterfaceThatExtendsInterface */',
- 'testFECNInterface',
- ],
- [
- '/* testInterfaceThatExtendsFQCNInterface */',
- '\PHP_CodeSniffer\Tests\Core\File\testFECNInterface',
- ],
- [
- '/* testNestedExtendedClass */',
- false,
- ],
- [
- '/* testNestedExtendedAnonClass */',
- 'testFECNAnonClass',
- ],
- [
- '/* testClassThatExtendsAndImplements */',
- 'testFECNClass',
- ],
- [
- '/* testClassThatImplementsAndExtends */',
- 'testFECNClass',
- ],
- ];
-
- }//end dataExtendedClass()
-
-
-}//end class
diff --git a/tests/Core/File/FindImplementedInterfaceNamesTest.php b/tests/Core/File/FindImplementedInterfaceNamesTest.php
deleted file mode 100644
index 6a8ee623ae..0000000000
--- a/tests/Core/File/FindImplementedInterfaceNamesTest.php
+++ /dev/null
@@ -1,142 +0,0 @@
-
- * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
- * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
- */
-
-namespace PHP_CodeSniffer\Tests\Core\File;
-
-use PHP_CodeSniffer\Config;
-use PHP_CodeSniffer\Ruleset;
-use PHP_CodeSniffer\Files\DummyFile;
-use PHPUnit\Framework\TestCase;
-
-class FindImplementedInterfaceNamesTest extends TestCase
-{
-
- /**
- * The \PHP_CodeSniffer\Files\File object containing parsed contents of the test case file.
- *
- * @var \PHP_CodeSniffer\Files\File
- */
- private $phpcsFile;
-
-
- /**
- * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from the test case file.
- *
- * Methods used for these tests can be found in a test case file in the same
- * directory and with the same name, using the .inc extension.
- *
- * @return void
- */
- public function setUp()
- {
- $config = new Config();
- $config->standards = ['Generic'];
-
- $ruleset = new Ruleset($config);
-
- $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
- $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
- $this->phpcsFile->process();
-
- }//end setUp()
-
-
- /**
- * Clean up after finished test.
- *
- * @return void
- */
- public function tearDown()
- {
- unset($this->phpcsFile);
-
- }//end tearDown()
-
-
- /**
- * Test retrieving the name(s) of the interfaces being implemented by a class.
- *
- * @param string $identifier Comment which precedes the test case.
- * @param bool $expected Expected function output.
- *
- * @dataProvider dataImplementedInterface
- *
- * @return void
- */
- public function testFindImplementedInterfaceNames($identifier, $expected)
- {
- $start = ($this->phpcsFile->numTokens - 1);
- $delim = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- $identifier
- );
- $OOToken = $this->phpcsFile->findNext([T_CLASS, T_ANON_CLASS, T_INTERFACE], ($delim + 1));
-
- $result = $this->phpcsFile->findImplementedInterfaceNames($OOToken);
- $this->assertSame($expected, $result);
-
- }//end testFindImplementedInterfaceNames()
-
-
- /**
- * Data provider for the FindImplementedInterfaceNames test.
- *
- * @see testFindImplementedInterfaceNames()
- *
- * @return array
- */
- public function dataImplementedInterface()
- {
- return [
- [
- '/* testImplementedClass */',
- ['testFIINInterface'],
- ],
- [
- '/* testMultiImplementedClass */',
- [
- 'testFIINInterface',
- 'testFIINInterface2',
- ],
- ],
- [
- '/* testNamespacedClass */',
- ['\PHP_CodeSniffer\Tests\Core\File\testFIINInterface'],
- ],
- [
- '/* testNonImplementedClass */',
- false,
- ],
- [
- '/* testInterface */',
- false,
- ],
- [
- '/* testClassThatExtendsAndImplements */',
- [
- 'InterfaceA',
- '\NameSpaced\Cat\InterfaceB',
- ],
- ],
- [
- '/* testClassThatImplementsAndExtends */',
- [
- '\InterfaceA',
- 'InterfaceB',
- ],
- ],
- ];
-
- }//end dataImplementedInterface()
-
-
-}//end class
diff --git a/tests/Core/File/GetMethodParametersTest.inc b/tests/Core/File/GetMethodParametersTest.inc
deleted file mode 100644
index a0ae183d42..0000000000
--- a/tests/Core/File/GetMethodParametersTest.inc
+++ /dev/null
@@ -1,29 +0,0 @@
-
- * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
- * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
- */
-
-namespace PHP_CodeSniffer\Tests\Core\File;
-
-use PHP_CodeSniffer\Config;
-use PHP_CodeSniffer\Ruleset;
-use PHP_CodeSniffer\Files\DummyFile;
-use PHPUnit\Framework\TestCase;
-
-class GetMethodParametersTest extends TestCase
-{
-
- /**
- * The PHP_CodeSniffer_File object containing parsed contents of the test case file.
- *
- * @var \PHP_CodeSniffer\Files\File
- */
- private $phpcsFile;
-
-
- /**
- * Initialize & tokenize PHP_CodeSniffer_File with code from the test case file.
- *
- * Methods used for these tests can be found in a test case file in the same
- * directory and with the same name, using the .inc extension.
- *
- * @return void
- */
- public function setUp()
- {
- $config = new Config();
- $config->standards = ['Generic'];
-
- $ruleset = new Ruleset($config);
-
- $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
- $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
- $this->phpcsFile->process();
-
- }//end setUp()
-
-
- /**
- * Clean up after finished test.
- *
- * @return void
- */
- public function tearDown()
- {
- unset($this->phpcsFile);
-
- }//end tearDown()
-
-
- /**
- * Verify pass-by-reference parsing.
- *
- * @return void
- */
- public function testPassByReference()
- {
- $expected = [];
- $expected[0] = [
- 'name' => '$var',
- 'content' => '&$var',
- 'pass_by_reference' => true,
- 'variable_length' => false,
- 'type_hint' => '',
- 'nullable_type' => false,
- ];
-
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testPassByReference */'
- );
-
- $found = $this->phpcsFile->getMethodParameters(($function + 2));
- unset($found[0]['token']);
- unset($found[0]['type_hint_token']);
- $this->assertSame($expected, $found);
-
- }//end testPassByReference()
-
-
- /**
- * Verify array hint parsing.
- *
- * @return void
- */
- public function testArrayHint()
- {
- $expected = [];
- $expected[0] = [
- 'name' => '$var',
- 'content' => 'array $var',
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => 'array',
- 'nullable_type' => false,
- ];
-
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testArrayHint */'
- );
-
- $found = $this->phpcsFile->getMethodParameters(($function + 2));
- unset($found[0]['token']);
- unset($found[0]['type_hint_token']);
- $this->assertSame($expected, $found);
-
- }//end testArrayHint()
-
-
- /**
- * Verify type hint parsing.
- *
- * @return void
- */
- public function testTypeHint()
- {
- $expected = [];
- $expected[0] = [
- 'name' => '$var1',
- 'content' => 'foo $var1',
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => 'foo',
- 'nullable_type' => false,
- ];
-
- $expected[1] = [
- 'name' => '$var2',
- 'content' => 'bar $var2',
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => 'bar',
- 'nullable_type' => false,
- ];
-
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testTypeHint */'
- );
-
- $found = $this->phpcsFile->getMethodParameters(($function + 2));
- unset($found[0]['token']);
- unset($found[1]['token']);
- unset($found[0]['type_hint_token']);
- unset($found[1]['type_hint_token']);
- $this->assertSame($expected, $found);
-
- }//end testTypeHint()
-
-
- /**
- * Verify self type hint parsing.
- *
- * @return void
- */
- public function testSelfTypeHint()
- {
- $expected = [];
- $expected[0] = [
- 'name' => '$var',
- 'content' => 'self $var',
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => 'self',
- 'nullable_type' => false,
- ];
-
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testSelfTypeHint */'
- );
-
- $found = $this->phpcsFile->getMethodParameters(($function + 2));
- unset($found[0]['token']);
- unset($found[0]['type_hint_token']);
- $this->assertSame($expected, $found);
-
- }//end testSelfTypeHint()
-
-
- /**
- * Verify nullable type hint parsing.
- *
- * @return void
- */
- public function testNullableTypeHint()
- {
- $expected = [];
- $expected[0] = [
- 'name' => '$var1',
- 'content' => '?int $var1',
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => '?int',
- 'nullable_type' => true,
- ];
-
- $expected[1] = [
- 'name' => '$var2',
- 'content' => '?\bar $var2',
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => '?\bar',
- 'nullable_type' => true,
- ];
-
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testNullableTypeHint */'
- );
-
- $found = $this->phpcsFile->getMethodParameters(($function + 2));
- unset($found[0]['token']);
- unset($found[1]['token']);
- unset($found[0]['type_hint_token']);
- unset($found[1]['type_hint_token']);
- $this->assertSame($expected, $found);
-
- }//end testNullableTypeHint()
-
-
- /**
- * Verify variable.
- *
- * @return void
- */
- public function testVariable()
- {
- $expected = [];
- $expected[0] = [
- 'name' => '$var',
- 'content' => '$var',
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => '',
- 'nullable_type' => false,
- ];
-
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testVariable */'
- );
-
- $found = $this->phpcsFile->getMethodParameters(($function + 2));
- unset($found[0]['token']);
- unset($found[0]['type_hint_token']);
- $this->assertSame($expected, $found);
-
- }//end testVariable()
-
-
- /**
- * Verify default value parsing with a single function param.
- *
- * @return void
- */
- public function testSingleDefaultValue()
- {
- $expected = [];
- $expected[0] = [
- 'name' => '$var1',
- 'content' => '$var1=self::CONSTANT',
- 'default' => 'self::CONSTANT',
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => '',
- 'nullable_type' => false,
- ];
-
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testSingleDefaultValue */'
- );
-
- $found = $this->phpcsFile->getMethodParameters(($function + 2));
- unset($found[0]['token']);
- unset($found[0]['type_hint_token']);
- $this->assertSame($expected, $found);
-
- }//end testSingleDefaultValue()
-
-
- /**
- * Verify default value parsing.
- *
- * @return void
- */
- public function testDefaultValues()
- {
- $expected = [];
- $expected[0] = [
- 'name' => '$var1',
- 'content' => '$var1=1',
- 'default' => '1',
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => '',
- 'nullable_type' => false,
- ];
- $expected[1] = [
- 'name' => '$var2',
- 'content' => "\$var2='value'",
- 'default' => "'value'",
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => '',
- 'nullable_type' => false,
- ];
-
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testDefaultValues */'
- );
-
- $found = $this->phpcsFile->getMethodParameters(($function + 2));
- unset($found[0]['token']);
- unset($found[1]['token']);
- unset($found[0]['type_hint_token']);
- unset($found[1]['type_hint_token']);
- $this->assertSame($expected, $found);
-
- }//end testDefaultValues()
-
-
- /**
- * Verify "bitwise and" in default value !== pass-by-reference.
- *
- * @return void
- */
- public function testBitwiseAndConstantExpressionDefaultValue()
- {
- $expected = [];
- $expected[0] = [
- 'name' => '$a',
- 'content' => '$a = 10 & 20',
- 'default' => '10 & 20',
- 'pass_by_reference' => false,
- 'variable_length' => false,
- 'type_hint' => '',
- 'nullable_type' => false,
- ];
-
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testBitwiseAndConstantExpressionDefaultValue */'
- );
-
- $found = $this->phpcsFile->getMethodParameters(($function + 2));
- unset($found[0]['token']);
- unset($found[0]['type_hint_token']);
- $this->assertSame($expected, $found);
-
- }//end testBitwiseAndConstantExpressionDefaultValue()
-
-
-}//end class
diff --git a/tests/Core/File/IsReferenceTest.php b/tests/Core/File/IsReferenceTest.php
deleted file mode 100644
index a72546a31f..0000000000
--- a/tests/Core/File/IsReferenceTest.php
+++ /dev/null
@@ -1,285 +0,0 @@
-
- * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
- * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
- */
-
-namespace PHP_CodeSniffer\Tests\Core\File;
-
-use PHP_CodeSniffer\Config;
-use PHP_CodeSniffer\Ruleset;
-use PHP_CodeSniffer\Files\DummyFile;
-use PHPUnit\Framework\TestCase;
-
-class IsReferenceTest extends TestCase
-{
-
- /**
- * The PHP_CodeSniffer_File object containing parsed contents of the test case file.
- *
- * @var \PHP_CodeSniffer\Files\File
- */
- private $phpcsFile;
-
-
- /**
- * Initialize & tokenize \PHP_CodeSniffer\Files\File with code from the test case file.
- *
- * Methods used for these tests can be found in a test case file in the same
- * directory and with the same name, using the .inc extension.
- *
- * @return void
- */
- public function setUp()
- {
- $config = new Config();
- $config->standards = ['Generic'];
-
- $ruleset = new Ruleset($config);
-
- $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
- $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
- $this->phpcsFile->process();
-
- }//end setUp()
-
-
- /**
- * Clean up after finished test.
- *
- * @return void
- */
- public function tearDown()
- {
- unset($this->phpcsFile);
-
- }//end tearDown()
-
-
- /**
- * Test a class that extends another.
- *
- * @param string $identifier Comment which precedes the test case.
- * @param bool $expected Expected function output.
- *
- * @dataProvider dataIsReference
- *
- * @return void
- */
- public function testIsReference($identifier, $expected)
- {
- $start = ($this->phpcsFile->numTokens - 1);
- $delim = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- $identifier
- );
- $bitwiseAnd = $this->phpcsFile->findNext(T_BITWISE_AND, ($delim + 1));
-
- $result = $this->phpcsFile->isReference($bitwiseAnd);
- $this->assertSame($expected, $result);
-
- }//end testIsReference()
-
-
- /**
- * Data provider for the IsReference test.
- *
- * @see testIsReference()
- *
- * @return array
- */
- public function dataIsReference()
- {
- return [
- [
- '/* bitwiseAndA */',
- false,
- ],
- [
- '/* bitwiseAndB */',
- false,
- ],
- [
- '/* bitwiseAndC */',
- false,
- ],
- [
- '/* bitwiseAndD */',
- false,
- ],
- [
- '/* bitwiseAndE */',
- false,
- ],
- [
- '/* bitwiseAndF */',
- false,
- ],
- [
- '/* bitwiseAndG */',
- false,
- ],
- [
- '/* bitwiseAndH */',
- false,
- ],
- [
- '/* bitwiseAndI */',
- false,
- ],
- [
- '/* functionReturnByReference */',
- true,
- ],
- [
- '/* functionPassByReferenceA */',
- true,
- ],
- [
- '/* functionPassByReferenceB */',
- true,
- ],
- [
- '/* functionPassByReferenceC */',
- true,
- ],
- [
- '/* functionPassByReferenceD */',
- true,
- ],
- [
- '/* functionPassByReferenceE */',
- true,
- ],
- [
- '/* functionPassByReferenceF */',
- true,
- ],
- [
- '/* functionPassByReferenceG */',
- true,
- ],
- [
- '/* foreachValueByReference */',
- true,
- ],
- [
- '/* foreachKeyByReference */',
- true,
- ],
- [
- '/* arrayValueByReferenceA */',
- true,
- ],
- [
- '/* arrayValueByReferenceB */',
- true,
- ],
- [
- '/* arrayValueByReferenceC */',
- true,
- ],
- [
- '/* arrayValueByReferenceD */',
- true,
- ],
- [
- '/* arrayValueByReferenceE */',
- true,
- ],
- [
- '/* arrayValueByReferenceF */',
- true,
- ],
- [
- '/* arrayValueByReferenceG */',
- true,
- ],
- [
- '/* arrayValueByReferenceH */',
- true,
- ],
- [
- '/* assignByReferenceA */',
- true,
- ],
- [
- '/* assignByReferenceB */',
- true,
- ],
- [
- '/* assignByReferenceC */',
- true,
- ],
- [
- '/* assignByReferenceD */',
- true,
- ],
- [
- '/* assignByReferenceE */',
- true,
- ],
- [
- '/* passByReferenceA */',
- true,
- ],
- [
- '/* passByReferenceB */',
- true,
- ],
- [
- '/* passByReferenceC */',
- true,
- ],
- [
- '/* passByReferenceD */',
- true,
- ],
- [
- '/* passByReferenceE */',
- true,
- ],
- [
- '/* passByReferenceF */',
- true,
- ],
- [
- '/* passByReferenceG */',
- true,
- ],
- [
- '/* passByReferenceH */',
- true,
- ],
- [
- '/* passByReferenceI */',
- true,
- ],
- [
- '/* passByReferenceJ */',
- true,
- ],
- [
- '/* newByReferenceA */',
- true,
- ],
- [
- '/* newByReferenceB */',
- true,
- ],
- [
- '/* useByReference */',
- true,
- ],
- ];
-
- }//end dataIsReference()
-
-
-}//end class
diff --git a/tests/Core/IsCamelCapsTest.php b/tests/Core/IsCamelCapsTest.php
deleted file mode 100644
index b60d524b03..0000000000
--- a/tests/Core/IsCamelCapsTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
-
- * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
- * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
- */
-
-namespace PHP_CodeSniffer\Tests\Core;
-
-use PHP_CodeSniffer\Util\Common;
-use PHPUnit\Framework\TestCase;
-
-class IsCamelCapsTest extends TestCase
-{
-
-
- /**
- * Test valid public function/method names.
- *
- * @return void
- */
- public function testValidNotClassFormatPublic()
- {
- $this->assertTrue(Common::isCamelCaps('thisIsCamelCaps', false, true, true));
- $this->assertTrue(Common::isCamelCaps('thisISCamelCaps', false, true, false));
-
- }//end testValidNotClassFormatPublic()
-
-
- /**
- * Test invalid public function/method names.
- *
- * @return void
- */
- public function testInvalidNotClassFormatPublic()
- {
- $this->assertFalse(Common::isCamelCaps('_thisIsCamelCaps', false, true, true));
- $this->assertFalse(Common::isCamelCaps('thisISCamelCaps', false, true, true));
- $this->assertFalse(Common::isCamelCaps('ThisIsCamelCaps', false, true, true));
-
- $this->assertFalse(Common::isCamelCaps('3thisIsCamelCaps', false, true, true));
- $this->assertFalse(Common::isCamelCaps('*thisIsCamelCaps', false, true, true));
- $this->assertFalse(Common::isCamelCaps('-thisIsCamelCaps', false, true, true));
-
- $this->assertFalse(Common::isCamelCaps('this*IsCamelCaps', false, true, true));
- $this->assertFalse(Common::isCamelCaps('this-IsCamelCaps', false, true, true));
- $this->assertFalse(Common::isCamelCaps('this_IsCamelCaps', false, true, true));
- $this->assertFalse(Common::isCamelCaps('this_is_camel_caps', false, true, true));
-
- }//end testInvalidNotClassFormatPublic()
-
-
- /**
- * Test valid private method names.
- *
- * @return void
- */
- public function testValidNotClassFormatPrivate()
- {
- $this->assertTrue(Common::isCamelCaps('_thisIsCamelCaps', false, false, true));
- $this->assertTrue(Common::isCamelCaps('_thisISCamelCaps', false, false, false));
- $this->assertTrue(Common::isCamelCaps('_i18N', false, false, true));
- $this->assertTrue(Common::isCamelCaps('_i18n', false, false, true));
-
- }//end testValidNotClassFormatPrivate()
-
-
- /**
- * Test invalid private method names.
- *
- * @return void
- */
- public function testInvalidNotClassFormatPrivate()
- {
- $this->assertFalse(Common::isCamelCaps('thisIsCamelCaps', false, false, true));
- $this->assertFalse(Common::isCamelCaps('_thisISCamelCaps', false, false, true));
- $this->assertFalse(Common::isCamelCaps('_ThisIsCamelCaps', false, false, true));
- $this->assertFalse(Common::isCamelCaps('__thisIsCamelCaps', false, false, true));
- $this->assertFalse(Common::isCamelCaps('__thisISCamelCaps', false, false, false));
-
- $this->assertFalse(Common::isCamelCaps('3thisIsCamelCaps', false, false, true));
- $this->assertFalse(Common::isCamelCaps('*thisIsCamelCaps', false, false, true));
- $this->assertFalse(Common::isCamelCaps('-thisIsCamelCaps', false, false, true));
- $this->assertFalse(Common::isCamelCaps('_this_is_camel_caps', false, false, true));
-
- }//end testInvalidNotClassFormatPrivate()
-
-
- /**
- * Test valid class names.
- *
- * @return void
- */
- public function testValidClassFormatPublic()
- {
- $this->assertTrue(Common::isCamelCaps('ThisIsCamelCaps', true, true, true));
- $this->assertTrue(Common::isCamelCaps('ThisISCamelCaps', true, true, false));
- $this->assertTrue(Common::isCamelCaps('This3IsCamelCaps', true, true, false));
-
- }//end testValidClassFormatPublic()
-
-
- /**
- * Test invalid class names.
- *
- * @return void
- */
- public function testInvalidClassFormat()
- {
- $this->assertFalse(Common::isCamelCaps('thisIsCamelCaps', true));
- $this->assertFalse(Common::isCamelCaps('This-IsCamelCaps', true));
- $this->assertFalse(Common::isCamelCaps('This_Is_Camel_Caps', true));
-
- }//end testInvalidClassFormat()
-
-
- /**
- * Test invalid class names with the private flag set.
- *
- * Note that the private flag is ignored if the class format
- * flag is set, so these names are all invalid.
- *
- * @return void
- */
- public function testInvalidClassFormatPrivate()
- {
- $this->assertFalse(Common::isCamelCaps('_ThisIsCamelCaps', true, true));
- $this->assertFalse(Common::isCamelCaps('_ThisIsCamelCaps', true, false));
-
- }//end testInvalidClassFormatPrivate()
-
-
-}//end class
diff --git a/tests/Core/Util/Sniffs/Comments/FindConstantCommentTest.inc b/tests/Core/Util/Sniffs/Comments/FindConstantCommentTest.inc
new file mode 100644
index 0000000000..b5d3b3723c
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Comments/FindConstantCommentTest.inc
@@ -0,0 +1,86 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Comments;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+use PHP_CodeSniffer\Util\Tokens;
+
+class FindConstantCommentTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a token which is not supported is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_CONST
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findConstantComment
+ *
+ * @return void
+ */
+ public function testNotAConstTokenException()
+ {
+ $stackPtr = self::$phpcsFile->findNext(
+ T_STRING,
+ 0,
+ null,
+ false,
+ 'GLOBAL_CONST_NO_DOCBLOCK'
+ );
+ $result = Comments::findConstantComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotAConstTokenException()
+
+
+ /**
+ * Test correctly identifying a comment above a T_CONST token.
+ *
+ * @param string $constName The name of the constant for which to find the comment.
+ * @param int|bool $expected The expected function return value.
+ *
+ * @dataProvider dataFindConstantComment
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findConstantComment
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findCommentAbove
+ *
+ * @return void
+ */
+ public function testFindConstantComment($constName, $expected)
+ {
+ $stackPtr = $this->getTargetToken($constName, T_CONST);
+
+ // End token position values are set as offsets in relation to the target token.
+ // Change these to exact positions based on the retrieved stackPtr.
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $result = Comments::findConstantComment(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testFindConstantComment()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testFindConstantComment()
+ *
+ * @return array
+ */
+ public function dataFindConstantComment()
+ {
+ return [
+ [
+ 'GLOBAL_CONST_NO_DOCBLOCK',
+ false,
+ ],
+ [
+ 'GLOBAL_CONST_UNRELATED_COMMENT',
+ false,
+ ],
+ [
+ 'GLOBAL_CONST_HAS_COMMENT_1',
+ -1,
+ ],
+ [
+ 'GLOBAL_CONST_HAS_COMMENT_2',
+ -1,
+ ],
+ [
+ 'GLOBAL_CONST_HAS_COMMENT_3',
+ -2,
+ ],
+ [
+ 'GLOBAL_CONST_HAS_DOCBLOCK',
+ -2,
+ ],
+ [
+ 'CLASS_CONST_NO_DOCBLOCK',
+ false,
+ ],
+ [
+ 'CLASS_CONST_UNRELATED_COMMENT',
+ false,
+ ],
+ [
+ 'CLASS_CONST_HAS_COMMENT_1',
+ -2,
+ ],
+ [
+ 'CLASS_CONST_HAS_COMMENT_2',
+ -2,
+ ],
+ [
+ 'CLASS_CONST_HAS_COMMENT_3',
+ -3,
+ ],
+ [
+ 'CLASS_CONST_HAS_COMMENT_4',
+ -3,
+ ],
+ [
+ 'CLASS_CONST_HAS_DOCBLOCK_1',
+ -3,
+ ],
+ [
+ 'CLASS_CONST_HAS_DOCBLOCK_2',
+ -7,
+ ],
+ [
+ 'CLASS_CONST_HAS_DOCBLOCK_3',
+ -7,
+ ],
+ [
+ 'CLASS_CONST_HAS_DOCBLOCK_4',
+ -7,
+ ],
+ [
+ 'CLASS_CONST_HAS_DOCBLOCK_5',
+ -14,
+ ],
+ ];
+
+ }//end dataFindConstantComment()
+
+
+ /**
+ * Helper method. Get the token pointer for a target token based on a specific constant name.
+ *
+ * Overloading the parent method as we can't look for marker comments for these methods as they
+ * would confuse the tests.
+ *
+ * @param string $constName The constant name to look for.
+ * @param int|string|array $tokenType The type of token(s) to look for.
+ * @param null $notUsed Parameter not used in this implementation of the method.
+ *
+ * @return int
+ */
+ public function getTargetToken($constName, $tokenType, $notUsed=null)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+ $namePtr = self::$phpcsFile->findPrevious(
+ T_STRING,
+ (self::$phpcsFile->numTokens - 1),
+ null,
+ false,
+ $constName
+ );
+ $target = self::$phpcsFile->findPrevious(Tokens::$emptyTokens, ($namePtr - 1), null, true);
+
+ if ($target === false || $tokens[$target]['code'] !== $tokenType) {
+ $this->assertFalse(true, 'Failed to find test target token for '.$constName);
+ }
+
+ return $target;
+
+ }//end getTargetToken()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Comments/FindEndOfCommentTest.inc b/tests/Core/Util/Sniffs/Comments/FindEndOfCommentTest.inc
new file mode 100644
index 0000000000..63062958a2
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Comments/FindEndOfCommentTest.inc
@@ -0,0 +1,187 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Comments;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+
+class FindEndOfCommentTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a token which is not supported is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_COMMENT or T_DOC_COMMENT_OPEN_TAG
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findEndOfComment
+ *
+ * @return void
+ */
+ public function testNotACommentException()
+ {
+ $stackPtr = self::$phpcsFile->findNext(T_ECHO, 0);
+ $result = Comments::findEndOfComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotACommentException()
+
+
+ /**
+ * Test receiving an expected exception when an inline comment token which is
+ * not the *start* of the inline comment is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must point to the start of a comment
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findEndOfComment
+ *
+ * @return void
+ */
+ public function testNotStartOfAnInlineCommentException()
+ {
+ $stackPtr = self::$phpcsFile->findNext(
+ T_COMMENT,
+ 0,
+ null,
+ false,
+ '//line 2
+'
+ );
+ $result = Comments::findEndOfComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotStartOfAnInlineCommentException()
+
+
+ /**
+ * Test receiving an expected exception when an inline comment token which is
+ * not the *start* of a block comment is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must point to the start of a comment
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findEndOfComment
+ *
+ * @return void
+ */
+ public function testNotStartOfABlockCommentException()
+ {
+ $stackPtr = self::$phpcsFile->findPrevious(
+ T_COMMENT,
+ (self::$phpcsFile->numTokens - 1),
+ null,
+ false,
+ ' * line 2
+'
+ );
+ $result = Comments::findEndOfComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotStartOfABlockCommentException()
+
+
+ /**
+ * Test receiving an expected exception when an inline comment token which is
+ * not the *start* of a block comment is passed and the comment contents starts with //.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must point to the start of a comment
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findEndOfComment
+ *
+ * @return void
+ */
+ public function testMixedInlineBlockCommentException()
+ {
+ $stackPtr = $this->getTargetToken(
+ "'testBlockCommentSequence_13'",
+ null,
+ ' // line 2
+'
+ );
+ $result = Comments::findEndOfComment(self::$phpcsFile, $stackPtr);
+
+ }//end testMixedInlineBlockCommentException()
+
+
+ /**
+ * Test correctly identifying the end of an inline or block comment.
+ *
+ * @param string $delimiter The text string delimiter to use to find the
+ * start of the comment.
+ * @param int $expected The expected function return value as an offset
+ * from the start of the comment.
+ * @param string|null $tokenContent Optional. Specific content to get the correct
+ * comment token.
+ *
+ * @dataProvider dataFindEndOfComment
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findEndOfComment
+ *
+ * @return void
+ */
+ public function testFindEndOfComment($delimiter, $expected, $tokenContent=null)
+ {
+ $stackPtr = $this->getTargetToken($delimiter, null, $tokenContent);
+
+ // Expected end token position values are set as offsets in relation to
+ // the target token.
+ // Change these to exact positions based on the retrieved stackPtr.
+ $expected += $stackPtr;
+
+ $result = Comments::findEndOfComment(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testFindEndOfComment()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testFindEndOfComment()
+ *
+ * @return array
+ */
+ public function dataFindEndOfComment()
+ {
+ return [
+ // Docblock.
+ [
+ "'testDocblockSequence'",
+ 8,
+ ],
+
+ // Inline comments.
+ [
+ "'testInlineCommentSequence_1'",
+ 0,
+ ],
+ [
+ "'testInlineCommentSequence_2'",
+ 6,
+ ],
+ [
+ "'testInlineCommentSequence_3'",
+ 4,
+ ],
+ [
+ "'testInlineCommentSequence_4'",
+ 4,
+ ],
+ [
+ "'testInlineCommentSequence_5'",
+ 4,
+ ],
+ [
+ "'testInlineCommentSequence_6'",
+ 4,
+ ],
+ [
+ "'testInlineCommentSequence_7'",
+ 0,
+ ],
+ [
+ "'testInlineCommentSequence_7'",
+ 0,
+ '// This starts a new inline comment
+',
+ ],
+ [
+ "'testInlineCommentSequence_8'",
+ 0,
+ '/* Stand-alone block comment.*/',
+ ],
+ [
+ "'testInlineCommentSequence_8'",
+ 0,
+ '// Stand-alone inline trailing comment.
+',
+ ],
+ [
+ "'testInlineCommentSequence_8'",
+ 0,
+ '// This starts a new inline comment
+',
+ ],
+ [
+ "'testInlineCommentSequence_9'",
+ 0,
+ '// Stand alone inline comment.
+',
+ ],
+ [
+ "'testInlineCommentSequence_9'",
+ 0,
+ '// Another stand alone inline comment.
+',
+ ],
+ [
+ "'testInlineCommentSequence_10'",
+ 2,
+ ],
+ [
+ "'testInlineCommentSequence_10'",
+ 0,
+ '# Perl-style comment not belonging to the sequence.
+',
+ ],
+ [
+ "'testInlineCommentSequence_11'",
+ 2,
+ ],
+ [
+ "'testInlineCommentSequence_11'",
+ 0,
+ '// Slash-style comment not belonging to the sequence.
+',
+ ],
+ [
+ "'testInlineCommentSequence_12'",
+ 4,
+ ],
+
+ // Block comments.
+ [
+ "'testBlockCommentSequence_1'",
+ 0,
+ ],
+ [
+ "'testBlockCommentSequence_2'",
+ 0,
+ ],
+ [
+ "'testBlockCommentSequence_3'",
+ 2,
+ ],
+ [
+ "'testBlockCommentSequence_4'",
+ 4,
+ ],
+ [
+ "'testBlockCommentSequence_5'",
+ 4,
+ ],
+ [
+ "'testBlockCommentSequence_6'",
+ 5,
+ ],
+ [
+ "'testBlockCommentSequence_7'",
+ 4,
+ ],
+ [
+ "'testBlockCommentSequence_8'",
+ 3,
+ ],
+ [
+ "'testBlockCommentSequence_9'",
+ 3,
+ ],
+ [
+ "'testBlockCommentSequence_10'",
+ 2,
+ ],
+ [
+ "'testBlockCommentSequence_11'",
+ 2,
+ ],
+ [
+ "'testBlockCommentSequence_12'",
+ 0,
+ ],
+ [
+ "'testBlockCommentSequence_13'",
+ 4,
+ ],
+ [
+ "'testBlockCommentSequence_last'",
+ 3,
+ ],
+ ];
+
+ }//end dataFindEndOfComment()
+
+
+ /**
+ * Helper method. Get the token pointer for a target token based on a specific text string.
+ *
+ * Overloading the parent method as we can't look for marker comments for these methods as they
+ * would confuse the tests.
+ *
+ * @param string $delimiter The text string content to look for.
+ * @param null $notUsed Parameter not used in this implementation of the method.
+ * @param string|null $tokenContent Optional. Specific content to get the correct
+ * comment token.
+ *
+ * @return int
+ */
+ public function getTargetToken($delimiter, $notUsed, $tokenContent=null)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+ $delimiterPtr = self::$phpcsFile->findPrevious(
+ T_CONSTANT_ENCAPSED_STRING,
+ (self::$phpcsFile->numTokens - 1),
+ null,
+ false,
+ $delimiter
+ );
+
+ $target = self::$phpcsFile->findNext(
+ [
+ T_COMMENT,
+ T_DOC_COMMENT_OPEN_TAG,
+ ],
+ ($delimiterPtr + 1),
+ null,
+ false,
+ $tokenContent
+ );
+
+ if ($target === false) {
+ $msg = 'Failed to find test target token for '.$delimiter;
+ if (isset($tokenContent) === true) {
+ $msg .= ' with content '.$tokenContent;
+ }
+
+ $this->assertFalse(true, $msg);
+ }
+
+ return $target;
+
+ }//end getTargetToken()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Comments/FindFunctionCommentTest.inc b/tests/Core/Util/Sniffs/Comments/FindFunctionCommentTest.inc
new file mode 100644
index 0000000000..2c0111052a
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Comments/FindFunctionCommentTest.inc
@@ -0,0 +1,80 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Comments;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+use PHP_CodeSniffer\Util\Tokens;
+
+class FindFunctionCommentTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a token which is not supported is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_FUNCTION
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findFunctionComment
+ *
+ * @return void
+ */
+ public function testNotAFunctionTokenException()
+ {
+ $stackPtr = self::$phpcsFile->findNext(
+ T_STRING,
+ 0,
+ null,
+ false,
+ 'functionNoDocblock'
+ );
+ $result = Comments::findFunctionComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotAFunctionTokenException()
+
+
+ /**
+ * Test correctly identifying a comment above a T_FUNCTION token.
+ *
+ * @param string $functionName The name of the function for which to find the comment.
+ * @param int|bool $expected The expected function return value.
+ *
+ * @dataProvider dataFindFunctionComment
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findFunctionComment
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findCommentAbove
+ *
+ * @return void
+ */
+ public function testFindFunctionComment($functionName, $expected)
+ {
+ $stackPtr = $this->getTargetToken($functionName, T_FUNCTION);
+
+ // End token position values are set as offsets in relation to the target token.
+ // Change these to exact positions based on the retrieved stackPtr.
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $result = Comments::findFunctionComment(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testFindFunctionComment()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testFindFunctionComment()
+ *
+ * @return array
+ */
+ public function dataFindFunctionComment()
+ {
+ return [
+ [
+ 'functionNoDocblock',
+ false,
+ ],
+ [
+ 'functionUnrelatedComment',
+ false,
+ ],
+ [
+ 'functionHasComment_1',
+ -1,
+ ],
+ [
+ 'functionHasComment_2',
+ -1,
+ ],
+ [
+ 'functionHasComment_3',
+ -2,
+ ],
+ [
+ 'functionHasDocblock',
+ -2,
+ ],
+ [
+ 'methodNoDocblock_1',
+ false,
+ ],
+ [
+ 'methodUnrelatedComment',
+ false,
+ ],
+ [
+ 'methodHasComment_1',
+ -4,
+ ],
+ [
+ 'methodHasComment_2',
+ -6,
+ ],
+ [
+ 'methodHasComment_3',
+ -5,
+ ],
+ [
+ 'methodHasComment_4',
+ -7,
+ ],
+ [
+ 'methodHasDocblock_1',
+ -5,
+ ],
+ [
+ 'methodHasDocblock_2',
+ -7,
+ ],
+ [
+ 'methodHasDocblock_3',
+ -20,
+ ],
+ [
+ 'methodHasDocblock_4',
+ -14,
+ ],
+ [
+ 'methodNoDocblock_2',
+ false,
+ ],
+ ];
+
+ }//end dataFindFunctionComment()
+
+
+ /**
+ * Helper method. Get the token pointer for a target token based on a specific function name.
+ *
+ * Overloading the parent method as we can't look for marker comments for these methods as they
+ * would confuse the tests.
+ *
+ * @param string $functionName The function name to look for.
+ * @param int|string|array $tokenType The type of token(s) to look for.
+ * @param null $notUsed Parameter not used in this implementation of the method.
+ *
+ * @return int
+ */
+ public function getTargetToken($functionName, $tokenType, $notUsed=null)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+ $namePtr = self::$phpcsFile->findPrevious(
+ T_STRING,
+ (self::$phpcsFile->numTokens - 1),
+ null,
+ false,
+ $functionName
+ );
+ $target = self::$phpcsFile->findPrevious(Tokens::$emptyTokens, ($namePtr - 1), null, true);
+
+ if ($target === false || $tokens[$target]['code'] !== $tokenType) {
+ $this->assertFalse(true, 'Failed to find test target token for '.$functionName);
+ }
+
+ return $target;
+
+ }//end getTargetToken()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Comments/FindOOStructureCommentTest.inc b/tests/Core/Util/Sniffs/Comments/FindOOStructureCommentTest.inc
new file mode 100644
index 0000000000..d60d98c11d
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Comments/FindOOStructureCommentTest.inc
@@ -0,0 +1,68 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Comments;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+use PHP_CodeSniffer\Util\Tokens;
+
+class FindOOStructureCommentTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a token which is not supported is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be a class, interface or trait token
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findOOStructureComment
+ *
+ * @return void
+ */
+ public function testNotAnOOStructureTokenException()
+ {
+ $stackPtr = self::$phpcsFile->findNext(T_ANON_CLASS, 0);
+ $result = Comments::findOOStructureComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotAnOOStructureTokenException()
+
+
+ /**
+ * Test correctly identifying a comment above an OO structure token.
+ *
+ * @param string $OOName The name of the OO structure for which to find the comment.
+ * @param int|bool $expected The expected function return value.
+ * @param int|string|array $tokenType Optional. The type of token(s) to look for.
+ *
+ * @dataProvider dataFindOOStructureComment
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findOOStructureComment
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findCommentAbove
+ *
+ * @return void
+ */
+ public function testFindOOStructureComment($OOName, $expected, $tokenType=T_CLASS)
+ {
+ $stackPtr = $this->getTargetToken($OOName, $tokenType);
+
+ // End token position values are set as offsets in relation to the target token.
+ // Change these to exact positions based on the retrieved stackPtr.
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $result = Comments::findOOStructureComment(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testFindOOStructureComment()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testFindOOStructureComment()
+ *
+ * @return array
+ */
+ public function dataFindOOStructureComment()
+ {
+ return [
+ [
+ 'ClassNoDocblock',
+ false,
+ ],
+ [
+ 'ClassNoDocblockTrailingCommentPrevious',
+ false,
+ ],
+ [
+ 'ClassComment_1',
+ -1,
+ ],
+ [
+ 'ClassComment_2',
+ -2,
+ ],
+ [
+ 'ClassDocblock_1',
+ -2,
+ ],
+ [
+ 'ClassDocblock_2',
+ -7,
+ ],
+ [
+ 'ClassDocblock_3',
+ -11,
+ ],
+ [
+ 'ClassNoDocblockWithInterlacedComments',
+ false,
+ ],
+ [
+ 'InterfaceNoDocblock',
+ false,
+ T_INTERFACE,
+ ],
+ [
+ 'InterfaceComment',
+ -2,
+ T_INTERFACE,
+ ],
+ [
+ 'InterfaceDocblock',
+ -2,
+ T_INTERFACE,
+ ],
+ [
+ 'TraitNoDocblock',
+ false,
+ T_TRAIT,
+ ],
+ [
+ 'TraitComment',
+ -1,
+ T_TRAIT,
+ ],
+ [
+ 'TraitDocblock',
+ -2,
+ T_TRAIT,
+ ],
+ [
+ 'TraitParseError',
+ false,
+ T_TRAIT,
+ ],
+ ];
+
+ }//end dataFindOOStructureComment()
+
+
+ /**
+ * Helper method. Get the token pointer for a target token based on a specific function name.
+ *
+ * Overloading the parent method as we can't look for marker comments for these methods as they
+ * would confuse the tests.
+ *
+ * @param string $OOName The OO Structure name to look for.
+ * @param int|string|array $tokenType The type of token(s) to look for.
+ * @param null $notUsed Parameter not used in this implementation of the method.
+ *
+ * @return int
+ */
+ public function getTargetToken($OOName, $tokenType, $notUsed=null)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+ $namePtr = self::$phpcsFile->findPrevious(
+ T_STRING,
+ (self::$phpcsFile->numTokens - 1),
+ null,
+ false,
+ $OOName
+ );
+ $target = self::$phpcsFile->findPrevious(Tokens::$emptyTokens, ($namePtr - 1), null, true);
+
+ if ($target === false || $tokens[$target]['code'] !== $tokenType) {
+ $this->assertFalse(true, 'Failed to find test target token for '.$OOName);
+ }
+
+ return $target;
+
+ }//end getTargetToken()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Comments/FindPropertyCommentTest.inc b/tests/Core/Util/Sniffs/Comments/FindPropertyCommentTest.inc
new file mode 100644
index 0000000000..918740b294
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Comments/FindPropertyCommentTest.inc
@@ -0,0 +1,43 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Comments;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+
+class FindPropertyCommentTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a token which is not supported is passed.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findCommentAbove
+ *
+ * @return void
+ */
+ public function testNonExistentToken()
+ {
+ $this->assertFalse(Comments::findCommentAbove(self::$phpcsFile, 100000));
+
+ }//end testNonExistentToken()
+
+
+ /**
+ * Test receiving an expected exception when a non-variable token is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be an OO property
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findPropertyComment
+ *
+ * @return void
+ */
+ public function testNotAnOOPropertyTokenExceptionNotVar()
+ {
+ $stackPtr = self::$phpcsFile->findNext(T_CLASS, 0);
+ $result = Comments::findPropertyComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotAnOOPropertyTokenExceptionNotVar()
+
+
+ /**
+ * Test receiving an expected exception when a variable token which is not an OO property is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be an OO property
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findPropertyComment
+ *
+ * @return void
+ */
+ public function testNotAnOOPropertyTokenExceptionGlobalVar()
+ {
+ $stackPtr = self::$phpcsFile->findNext(
+ T_VARIABLE,
+ 0,
+ null,
+ false,
+ '$notAProperty'
+ );
+ $result = Comments::findPropertyComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotAnOOPropertyTokenExceptionGlobalVar()
+
+
+ /**
+ * Test receiving an expected exception when a variable token which is a method parameter, not
+ * a property, is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be an OO property
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findPropertyComment
+ *
+ * @return void
+ */
+ public function testNotAnOOPropertyTokenExceptionParam()
+ {
+ $stackPtr = self::$phpcsFile->findNext(
+ T_VARIABLE,
+ 0,
+ null,
+ false,
+ '$paramNotProperty'
+ );
+ $result = Comments::findPropertyComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotAnOOPropertyTokenExceptionParam()
+
+
+ /**
+ * Test correctly identifying a comment above an OO property T_VARIABLE token.
+ *
+ * @param string $propertyName The name of the property for which to find the comment.
+ * @param int|bool $expected The expected function return value.
+ *
+ * @dataProvider dataFindPropertyComment
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findPropertyComment
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findCommentAbove
+ *
+ * @return void
+ */
+ public function testFindPropertyComment($propertyName, $expected)
+ {
+ $stackPtr = $this->getTargetToken($propertyName);
+
+ // End token position values are set as offsets in relation to the target token.
+ // Change these to exact positions based on the retrieved stackPtr.
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $result = Comments::findPropertyComment(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testFindPropertyComment()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testFindPropertyComment()
+ *
+ * @return array
+ */
+ public function dataFindPropertyComment()
+ {
+ return [
+ [
+ '$propertyNoDocblock',
+ false,
+ ],
+ [
+ '$propertyStaticHadDocblock',
+ -5,
+ ],
+ [
+ '$propertyNoDocblockTrailingComment',
+ false,
+ ],
+ [
+ '$propertyMultiLineComment',
+ -5,
+ ],
+ [
+ '$propertyInlineComment',
+ -4,
+ ],
+ [
+ '$propertyHadDocblockAndWhitespace',
+ -9,
+ ],
+ [
+ '$propertyHasDocblockInterlacedComments',
+ -14,
+ ],
+ ];
+
+ }//end dataFindPropertyComment()
+
+
+ /**
+ * Helper method. Get the token pointer for a target token based on a specific property name.
+ *
+ * Overloading the parent method as we can't look for marker comments for these methods as they
+ * would confuse the tests.
+ *
+ * @param string $propertyName The property name to look for.
+ * @param null $notUsed1 Parameter not used in this implementation of the method.
+ * @param null $notUsed2 Parameter not used in this implementation of the method.
+ *
+ * @return int
+ */
+ public function getTargetToken($propertyName, $notUsed1=null, $notUsed2=null)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+ $target = self::$phpcsFile->findPrevious(
+ T_VARIABLE,
+ (self::$phpcsFile->numTokens - 1),
+ null,
+ false,
+ $propertyName
+ );
+
+ if ($target === false) {
+ $this->assertFalse(true, 'Failed to find test target token for '.$propertyName);
+ }
+
+ return $target;
+
+ }//end getTargetToken()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Comments/FindStartOfCommentTest.inc b/tests/Core/Util/Sniffs/Comments/FindStartOfCommentTest.inc
new file mode 100644
index 0000000000..6c4dadbd45
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Comments/FindStartOfCommentTest.inc
@@ -0,0 +1,183 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Comments;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+
+class FindStartOfCommentTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a token which is not supported is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_COMMENT or T_DOC_COMMENT_CLOSE_TAG
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findStartOfComment
+ *
+ * @return void
+ */
+ public function testNotACommentException()
+ {
+ $stackPtr = self::$phpcsFile->findNext(T_ECHO, 0);
+ $result = Comments::findStartOfComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotACommentException()
+
+
+ /**
+ * Test receiving an expected exception when an inline comment token which is
+ * not the *end* of the inline comment is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must point to the end of a comment
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findStartOfComment
+ *
+ * @return void
+ */
+ public function testNotEndOfAnInlineCommentException()
+ {
+ $stackPtr = self::$phpcsFile->findNext(
+ T_COMMENT,
+ 0,
+ null,
+ false,
+ '//line 2
+'
+ );
+ $result = Comments::findStartOfComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotEndOfAnInlineCommentException()
+
+
+ /**
+ * Test receiving an expected exception when an inline comment token which is
+ * not the *end* of a block comment is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must point to the end of a comment
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findStartOfComment
+ *
+ * @return void
+ */
+ public function testNotEndOfABlockCommentException()
+ {
+ $stackPtr = self::$phpcsFile->findPrevious(
+ T_COMMENT,
+ (self::$phpcsFile->numTokens - 1),
+ null,
+ false,
+ ' * line 2
+'
+ );
+ $result = Comments::findStartOfComment(self::$phpcsFile, $stackPtr);
+
+ }//end testNotEndOfABlockCommentException()
+
+
+ /**
+ * Test receiving an expected exception when an inline comment token which is
+ * not the *end* of a block comment is passed and the comment contents starts with //.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must point to the end of a comment
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findEndOfComment
+ *
+ * @return void
+ */
+ public function testMixedInlineBlockCommentException()
+ {
+ $stackPtr = $this->getTargetToken(
+ "'testBlockCommentSequence_13'",
+ null,
+ ' // line 2
+'
+ );
+ $result = Comments::findStartOfComment(self::$phpcsFile, $stackPtr);
+
+ }//end testMixedInlineBlockCommentException()
+
+
+ /**
+ * Test correctly identifying the start of an inline or block comment.
+ *
+ * @param string $delimiter The text string delimiter to use to find the
+ * end of the comment.
+ * @param int $expected The expected function return value as an offset
+ * from the end of the comment.
+ * @param string|null $tokenContent Optional. Specific content to get the correct
+ * comment token.
+ *
+ * @dataProvider dataFindStartOfComment
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::findStartOfComment
+ *
+ * @return void
+ */
+ public function testFindStartOfComment($delimiter, $expected, $tokenContent=null)
+ {
+ $stackPtr = $this->getTargetToken($delimiter, null, $tokenContent);
+
+ // Expected start token position values are set as offsets in relation to
+ // the target token.
+ // Change these to exact positions based on the retrieved stackPtr.
+ $expected += $stackPtr;
+
+ $result = Comments::findStartOfComment(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testFindStartOfComment()
+
+
+ /**
+ * Data provider.
+ *
+ * Note: the delimiters for these tests are *below* the "end of comment" target token!
+ *
+ * @see testFindStartOfComment()
+ *
+ * @return array
+ */
+ public function dataFindStartOfComment()
+ {
+ return [
+ // Docblock.
+ [
+ "'testDocblockSequence'",
+ -8,
+ ],
+
+ // Inline comments.
+ [
+ "'testInlineCommentSequence_1'",
+ 0,
+ ],
+ [
+ "'testInlineCommentSequence_2'",
+ -6,
+ ],
+ [
+ "'testInlineCommentSequence_3'",
+ -4,
+ ],
+ [
+ "'testInlineCommentSequence_4'",
+ -4,
+ ],
+ [
+ "'testInlineCommentSequence_5'",
+ -4,
+ ],
+ [
+ "'testInlineCommentSequence_6'",
+ -4,
+ ],
+ [
+ "'testInlineCommentSequence_7'",
+ 0,
+ ],
+ [
+ "'testInlineCommentSequence_7'",
+ 0,
+ '// Stand-alone inline trailing comment.
+',
+ ],
+ [
+ "'testInlineCommentSequence_8'",
+ 0,
+ '// This starts (and ends) a new inline comment
+',
+ ],
+ [
+ "'testInlineCommentSequence_8'",
+ 0,
+ '// Stand-alone inline trailing comment.
+',
+ ],
+ [
+ "'testInlineCommentSequence_8'",
+ 0,
+ '/* Stand-alone block comment.*/',
+ ],
+ [
+ "'testInlineCommentSequence_9'",
+ 0,
+ '// Stand alone inline comment.
+',
+ ],
+ [
+ "'testInlineCommentSequence_9'",
+ 0,
+ '// Another stand alone inline comment.
+',
+ ],
+ [
+ "'testInlineCommentSequence_10'",
+ -2,
+ ],
+ [
+ "'testInlineCommentSequence_10'",
+ 0,
+ '# Perl-style comment not belonging to the sequence.
+',
+ ],
+ [
+ "'testInlineCommentSequence_11'",
+ -2,
+ ],
+ [
+ "'testInlineCommentSequence_11'",
+ 0,
+ '// Slash-style comment not belonging to the sequence.
+',
+ ],
+ [
+ "'testInlineCommentSequence_12'",
+ -4,
+ ],
+
+ // Block comments.
+ [
+ "'testBlockCommentSequence_1'",
+ 0,
+ ],
+ [
+ "'testBlockCommentSequence_2'",
+ 0,
+ ],
+ [
+ "'testBlockCommentSequence_3'",
+ -2,
+ ],
+ [
+ "'testBlockCommentSequence_4'",
+ -4,
+ ],
+ [
+ "'testBlockCommentSequence_5'",
+ -4,
+ ],
+ [
+ "'testBlockCommentSequence_6'",
+ -5,
+ ],
+ [
+ "'testBlockCommentSequence_7'",
+ -4,
+ ],
+ [
+ "'testBlockCommentSequence_8'",
+ -1,
+ ],
+ [
+ "'testBlockCommentSequence_9'",
+ -1,
+ ],
+ [
+ "'testBlockCommentSequence_10'",
+ -4,
+ ],
+ [
+ "'testBlockCommentSequence_11'",
+ -4,
+ ],
+ [
+ "'testBlockCommentSequence_12'",
+ 0,
+ ],
+ [
+ "'testBlockCommentSequence_13'",
+ -4,
+ ],
+ ];
+
+ }//end dataFindStartOfComment()
+
+
+ /**
+ * Helper method. Get the token pointer for a target token based on a specific text string.
+ *
+ * Overloading the parent method as we can't look for marker comments for these methods as they
+ * would confuse the tests.
+ *
+ * Note: for these tests, the delimiter is placed *below* the target token.
+ *
+ * @param string $delimiter The text string content to look for.
+ * @param null $notUsed Parameter not used in this implementation of the method.
+ * @param string|null $tokenContent Optional. Specific content to get the correct
+ * comment token.
+ *
+ * @return int
+ */
+ public function getTargetToken($delimiter, $notUsed=null, $tokenContent=null)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+ $delimiterPtr = self::$phpcsFile->findPrevious(
+ T_CONSTANT_ENCAPSED_STRING,
+ (self::$phpcsFile->numTokens - 1),
+ null,
+ false,
+ $delimiter
+ );
+
+ $target = self::$phpcsFile->findPrevious(
+ [
+ T_COMMENT,
+ T_DOC_COMMENT_CLOSE_TAG,
+ ],
+ ($delimiterPtr - 1),
+ null,
+ false,
+ $tokenContent
+ );
+
+ if ($target === false) {
+ $msg = 'Failed to find test target token for '.$delimiter;
+ if (isset($tokenContent) === true) {
+ $msg .= ' with content '.$tokenContent;
+ }
+
+ $this->assertFalse(true, $msg);
+ }
+
+ return $target;
+
+ }//end getTargetToken()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Comments/SuggestTypeStringTest.php b/tests/Core/Util/Sniffs/Comments/SuggestTypeStringTest.php
new file mode 100644
index 0000000000..67ef229722
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Comments/SuggestTypeStringTest.php
@@ -0,0 +1,264 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Comments;
+
+use PHPUnit\Framework\TestCase;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+
+class SuggestTypeStringTest extends TestCase
+{
+
+
+ /**
+ * Test the suggestTypeString() method.
+ *
+ * @param string $typeString The complete variable type string to process.
+ * @param string $expectedLong Expected suggested long-form type.
+ * @param string $expectedShort Expected suggested short-form type.
+ *
+ * @dataProvider dataSuggestTypeString
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::suggestTypeString
+ *
+ * @return void
+ */
+ public function testSuggestTypeString($typeString, $expectedLong, $expectedShort)
+ {
+ $result = Comments::suggestTypeString($typeString, 'long');
+ $this->assertSame($expectedLong, $result);
+
+ $result = Comments::suggestTypeString($typeString, 'short');
+ $this->assertSame($expectedShort, $result);
+
+ }//end testSuggestTypeString()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testSuggestTypeString()
+ *
+ * @return array
+ */
+ public function dataSuggestTypeString()
+ {
+ return [
+ // Simple, singular type.
+ [
+ 'input' => 'DoUbLe',
+ 'long' => 'float',
+ 'short' => 'float',
+ ],
+ [
+ 'input' => 'BOOLEAN[]',
+ 'long' => 'boolean[]',
+ 'short' => 'bool[]',
+ ],
+ [
+ 'input' => 'array(real)',
+ 'long' => 'array(float)',
+ 'short' => 'array(float)',
+ ],
+ [
+ 'input' => 'array',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+ [
+ 'input' => '(Int|False)[]',
+ 'long' => '(integer|false)[]',
+ 'short' => '(int|false)[]',
+ ],
+
+ // Slightly more complex array types.
+ [
+ 'input' => 'array(string => string|null)',
+ 'long' => 'array(string => string|null)',
+ 'short' => 'array(string => string|null)',
+ ],
+ [
+ 'input' => 'array(integer|string => int||null)',
+ 'long' => 'array(integer|string => integer|null)',
+ 'short' => 'array(int|string => int|null)',
+ ],
+ [
+ 'input' => 'array',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+ [
+ 'input' => 'array',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+
+ // Union types.
+ [
+ 'input' => 'int|real',
+ 'long' => 'integer|float',
+ 'short' => 'int|float',
+ ],
+ [
+ 'input' => 'NULL|MIXED|RESOURCE',
+ 'long' => 'null|mixed|resource',
+ 'short' => 'null|mixed|resource',
+ ],
+ [
+ 'input' => 'NULL|(int|False)[]',
+ 'long' => 'null|(integer|false)[]',
+ 'short' => 'null|(int|false)[]',
+ ],
+ [
+ 'input' => '\ArrayObject|\DateTime[]',
+ 'long' => '\ArrayObject|\DateTime[]',
+ 'short' => '\ArrayObject|\DateTime[]',
+ ],
+ [
+ 'input' => 'NULL|array(int => object)',
+ 'long' => 'null|array(integer => object)',
+ 'short' => 'null|array(int => object)',
+ ],
+ [
+ 'input' => 'array(int => object)|NULL',
+ 'long' => 'array(integer => object)|null',
+ 'short' => 'array(int => object)|null',
+ ],
+ [
+ 'input' => 'NULL|array',
+ 'long' => 'null|array',
+ 'short' => 'null|array',
+ ],
+ [
+ 'input' => 'array|NULL',
+ 'long' => 'array|null',
+ 'short' => 'array|null',
+ ],
+
+ // Intersect types.
+ [
+ 'input' => '\MyClass&\PHPUnit\Framework\MockObject\MockObject',
+ 'long' => '\MyClass&\PHPUnit\Framework\MockObject\MockObject',
+ 'short' => '\MyClass&\PHPUnit\Framework\MockObject\MockObject',
+ ],
+
+ // Mixed union and intersect types.
+ [
+ 'input' => 'NULL|(int|False)[]|\MyClass&\PHPUnit\Framework\MockObject\MockObject',
+ 'long' => 'null|(integer|false)[]|\MyClass&\PHPUnit\Framework\MockObject\MockObject',
+ 'short' => 'null|(int|false)[]|\MyClass&\PHPUnit\Framework\MockObject\MockObject',
+ ],
+
+ // Simple union types with duplicates.
+ [
+ 'input' => 'int|integer',
+ 'long' => 'integer',
+ 'short' => 'int',
+ ],
+ [
+ 'input' => 'bool|null|boolean|null',
+ 'long' => 'boolean|null',
+ 'short' => 'bool|null',
+ ],
+
+ // Simple union type with duplicate `|`.
+ [
+ 'input' => 'int||false',
+ 'long' => 'integer|false',
+ 'short' => 'int|false',
+ ],
+
+ // Combining PSR-5 style single/multi-type arrays.
+ [
+ 'input' => 'int[]|real[]|string[]',
+ 'long' => '(integer|float|string)[]',
+ 'short' => '(int|float|string)[]',
+ ],
+ [
+ 'input' => '\ClassName[]|\Package\AnotherClass[]',
+ 'long' => '(\ClassName|\Package\AnotherClass)[]',
+ 'short' => '(\ClassName|\Package\AnotherClass)[]',
+ ],
+ [
+ 'input' => '(int|\ClassName)[]|float[]|null',
+ 'long' => '(integer|\ClassName|float)[]|null',
+ 'short' => '(int|\ClassName|float)[]|null',
+ ],
+ [
+ 'input' => 'null|(float|int)[]|\ClassName[]',
+ 'long' => 'null|(float|integer|\ClassName)[]',
+ 'short' => 'null|(float|int|\ClassName)[]',
+ ],
+ [
+ 'input' => 'null|\ClassName[]|(real|int)[]',
+ 'long' => 'null|(\ClassName|float|integer)[]',
+ 'short' => 'null|(\ClassName|float|int)[]',
+ ],
+
+ // Combining single/multi-type arrays with duplicates.
+ [
+ 'input' => 'int[]|integer[]|false',
+ 'long' => 'integer[]|false',
+ 'short' => 'int[]|false',
+ ],
+ [
+ 'input' => '(int|\ClassName)[]|integer[]|null',
+ 'long' => '(integer|\ClassName)[]|null',
+ 'short' => '(int|\ClassName)[]|null',
+ ],
+ [
+ 'input' => '(int|\ClassName)[]|(float|int)[]',
+ 'long' => '(integer|\ClassName|float)[]',
+ 'short' => '(int|\ClassName|float)[]',
+ ],
+
+ // Combining single/multi-type arrays with duplicates and duplicate `|`.
+ [
+ 'input' => '(int||\ClassName)[]|(float||int)[]',
+ 'long' => '(integer|\ClassName|float)[]',
+ 'short' => '(int|\ClassName|float)[]',
+ ],
+
+ // Combining single/multi-type arrays with duplicates in the array and the simple types.
+ [
+ 'input' => 'int|(int|\ClassName)[]|integer[]|integer',
+ 'long' => 'integer|(integer|\ClassName)[]',
+ 'short' => 'int|(int|\ClassName)[]',
+ ],
+
+ // Intersect types with duplicates.
+ [
+ 'input' => '\MyClass&\YourClass&\AnotherClass&\MyClass',
+ 'long' => '\MyClass&\YourClass&\AnotherClass',
+ 'short' => '\MyClass&\YourClass&\AnotherClass',
+ ],
+
+ // Intersect type with duplicate `&`.
+ [
+ 'input' => '\MyClass&&\YourClass',
+ 'long' => '\MyClass&\YourClass',
+ 'short' => '\MyClass&\YourClass',
+ ],
+
+ // Nullable types.
+ [
+ 'input' => '?int|?real',
+ 'long' => '?integer|?float',
+ 'short' => '?int|?float',
+ ],
+ [
+ 'input' => '?int|?string|null',
+ 'long' => 'integer|string|null',
+ 'short' => 'int|string|null',
+ ],
+ ];
+
+ }//end dataSuggestTypeString()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Comments/SuggestTypeTest.php b/tests/Core/Util/Sniffs/Comments/SuggestTypeTest.php
new file mode 100644
index 0000000000..7848fd57ff
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Comments/SuggestTypeTest.php
@@ -0,0 +1,387 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Comments;
+
+use PHPUnit\Framework\TestCase;
+use PHP_CodeSniffer\Util\Sniffs\Comments;
+
+class SuggestTypeTest extends TestCase
+{
+
+
+ /**
+ * Test passing an empty type to the suggestType() method.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::suggestType
+ *
+ * @return void
+ */
+ public function testSuggestTypeEmpty()
+ {
+ $this->assertSame('', Comments::suggestType(''));
+
+ }//end testSuggestTypeEmpty()
+
+
+ /**
+ * Test passing one of the allowed types to the suggestType() method.
+ *
+ * @param string $varType The type.
+ * @param string $expectedLong Expected suggested long-form type.
+ * @param string $expectedShort Expected suggested short-form type.
+ *
+ * @dataProvider dataSuggestTypeAllowedType
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::suggestType
+ *
+ * @return void
+ */
+ public function testSuggestTypeAllowedType($varType, $expectedLong, $expectedShort)
+ {
+ $result = Comments::suggestType($varType, 'long');
+ $this->assertSame($expectedLong, $result);
+
+ $result = Comments::suggestType($varType, 'short');
+ $this->assertSame($expectedShort, $result);
+
+ }//end testSuggestTypeAllowedType()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testSuggestTypeAllowedType()
+ *
+ * @return array
+ */
+ public function dataSuggestTypeAllowedType()
+ {
+ $types = Comments::$allowedTypes;
+ $data = [];
+ foreach ($types as $short => $long) {
+ $data[$long] = [
+ 'input' => $short,
+ 'long' => $long,
+ 'short' => $short,
+ ];
+ }
+
+ // Add tests for input being long form.
+ $data['int'] = [
+ 'input' => 'integer',
+ 'long' => 'integer',
+ 'short' => 'int',
+ ];
+ $data['bool'] = [
+ 'input' => 'boolean',
+ 'long' => 'boolean',
+ 'short' => 'bool',
+ ];
+
+ return $data;
+
+ }//end dataSuggestTypeAllowedType()
+
+
+ /**
+ * Test passing one of the allowed types in the wrong case to the suggestType() method.
+ *
+ * @param string $varType The type.
+ * @param string $expectedLong Expected suggested long-form type.
+ * @param string $expectedShort Expected suggested short-form type.
+ *
+ * @dataProvider dataSuggestTypeAllowedTypeWrongCase
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::suggestType
+ *
+ * @return void
+ */
+ public function testSuggestTypeAllowedTypeWrongCase($varType, $expectedLong, $expectedShort)
+ {
+ $this->testSuggestTypeAllowedType($varType, $expectedLong, $expectedShort);
+
+ }//end testSuggestTypeAllowedTypeWrongCase()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testSuggestTypeAllowedTypeWrongCase()
+ *
+ * @return array
+ */
+ public function dataSuggestTypeAllowedTypeWrongCase()
+ {
+ $types = Comments::$allowedTypes;
+ $data = [];
+ foreach ($types as $short => $long) {
+ $data[] = [
+ 'input' => ucfirst($short),
+ 'long' => $long,
+ 'short' => $short,
+ ];
+ $data[] = [
+ 'input' => strtoupper($short),
+ 'long' => $long,
+ 'short' => $short,
+ ];
+ }
+
+ // Add tests for input being long form in non-lowercase.
+ $data[] = [
+ 'input' => 'Integer',
+ 'long' => 'integer',
+ 'short' => 'int',
+ ];
+ $data[] = [
+ 'input' => 'INTEGER',
+ 'long' => 'integer',
+ 'short' => 'int',
+ ];
+ $data[] = [
+ 'input' => 'Boolean',
+ 'long' => 'boolean',
+ 'short' => 'bool',
+ ];
+ $data[] = [
+ 'input' => 'BOOLEAN',
+ 'long' => 'boolean',
+ 'short' => 'bool',
+ ];
+
+ return $data;
+
+ }//end dataSuggestTypeAllowedTypeWrongCase()
+
+
+ /**
+ * Test the suggestType() method for all other cases.
+ *
+ * @param string $varType The type found.
+ * @param string $expectedLong Expected suggested long-form type.
+ * @param string $expectedShort Expected suggested short-form type.
+ *
+ * @dataProvider dataSuggestTypeOther
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Comments::suggestType
+ *
+ * @return void
+ */
+ public function testSuggestTypeOther($varType, $expectedLong, $expectedShort)
+ {
+ $result = Comments::suggestType($varType, 'long');
+ $this->assertSame($expectedLong, $result);
+
+ $result = Comments::suggestType($varType, 'short');
+ $this->assertSame($expectedShort, $result);
+
+ }//end testSuggestTypeOther()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testSuggestTypeOther()
+ *
+ * @return array
+ */
+ public function dataSuggestTypeOther()
+ {
+ return [
+ // Wrong form.
+ [
+ 'input' => 'double',
+ 'long' => 'float',
+ 'short' => 'float',
+ ],
+ [
+ 'input' => 'Real',
+ 'long' => 'float',
+ 'short' => 'float',
+ ],
+ [
+ 'input' => 'DoUbLe',
+ 'long' => 'float',
+ 'short' => 'float',
+ ],
+
+ // Array types.
+ [
+ 'input' => 'Array()',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+ [
+ 'input' => 'array(real)',
+ 'long' => 'array(float)',
+ 'short' => 'array(float)',
+ ],
+ [
+ 'input' => 'array(int => object)',
+ 'long' => 'array(integer => object)',
+ 'short' => 'array(int => object)',
+ ],
+ [
+ 'input' => 'array(integer => object)',
+ 'long' => 'array(integer => object)',
+ 'short' => 'array(int => object)',
+ ],
+ [
+ 'input' => 'array(integer => array(string => resource))',
+ 'long' => 'array(integer => array(string => resource))',
+ 'short' => 'array(int => array(string => resource))',
+ ],
+ [
+ 'input' => 'ARRAY(BOOL => DOUBLE)',
+ 'long' => 'array(boolean => float)',
+ 'short' => 'array(bool => float)',
+ ],
+ [
+ 'input' => 'array(string=>resource)',
+ 'long' => 'array(string => resource)',
+ 'short' => 'array(string => resource)',
+ ],
+ [
+ 'input' => 'ARRAY( BOOLEAN => Real )',
+ 'long' => 'array(boolean => float)',
+ 'short' => 'array(bool => float)',
+ ],
+ [
+ 'input' => 'array(string => string|null)',
+ 'long' => 'array(string => string|null)',
+ 'short' => 'array(string => string|null)',
+ ],
+ [
+ 'input' => 'array(integer|string => int||null)',
+ 'long' => 'array(integer|string => integer|null)',
+ 'short' => 'array(int|string => int|null)',
+ ],
+
+ // Arrays with <> brackets.
+ [
+ 'input' => 'Array<>',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+ [
+ 'input' => 'array',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+ [
+ 'input' => 'array',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+ [
+ 'input' => 'array',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+ [
+ 'input' => 'array>',
+ 'long' => 'array>',
+ 'short' => 'array>',
+ ],
+ [
+ 'input' => 'ARRAY',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+ [
+ 'input' => 'ARRAY< BOOLEAN , Real >',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+ [
+ 'input' => 'array',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+ [
+ 'input' => 'array',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+
+ // Incomplete array type.
+ [
+ 'input' => 'array(int =>',
+ 'long' => 'array',
+ 'short' => 'array',
+ ],
+
+ // Custom types are returned unchanged.
+ [
+ 'input' => ' => ',
+ 'long' => ' => ',
+ 'short' => ' => ',
+ ],
+ [
+ 'input' => '[]',
+ 'long' => '[]',
+ 'short' => '[]',
+ ],
+
+ // Single type PSR-5 style arrays.
+ [
+ 'input' => 'string[]',
+ 'long' => 'string[]',
+ 'short' => 'string[]',
+ ],
+ [
+ 'input' => 'BOOLEAN[]',
+ 'long' => 'boolean[]',
+ 'short' => 'bool[]',
+ ],
+ [
+ 'input' => 'int[]',
+ 'long' => 'integer[]',
+ 'short' => 'int[]',
+ ],
+ [
+ 'input' => 'double[]',
+ 'long' => 'float[]',
+ 'short' => 'float[]',
+ ],
+
+ // Multi-type PSR-5 style arrays.
+ [
+ 'input' => '(string|null)[]',
+ 'long' => '(string|null)[]',
+ 'short' => '(string|null)[]',
+ ],
+ [
+ 'input' => '(Int|False)[]',
+ 'long' => '(integer|false)[]',
+ 'short' => '(int|false)[]',
+ ],
+ [
+ 'input' => '(real|TRUE|resource)[]',
+ 'long' => '(float|true|resource)[]',
+ 'short' => '(float|true|resource)[]',
+ ],
+
+ // Multi-type PSR-5 style array with duplicate `|`.
+ [
+ 'input' => '(string||null)[]',
+ 'long' => '(string|null)[]',
+ 'short' => '(string|null)[]',
+ ],
+
+ // Nullable types.
+ [
+ 'input' => '?bool',
+ 'long' => '?boolean',
+ 'short' => '?bool',
+ ],
+ ];
+
+ }//end dataSuggestTypeOther()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Conditions/GetConditionTest.inc b/tests/Core/Util/Sniffs/Conditions/GetConditionTest.inc
new file mode 100644
index 0000000000..e7684daa97
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Conditions/GetConditionTest.inc
@@ -0,0 +1,91 @@
+ $v) {
+
+ /* condition 11-2: try */
+ try {
+ --$k;
+
+ /* condition 11-3: catch */
+ } catch (Exception $e) {
+ /* testInException */
+ echo 'oh darn';
+ /* condition 11-4: finally */
+ } finally {
+ return true;
+ }
+ }
+
+ $a++;
+ }
+ break;
+
+ /* condition 8b: default */
+ default:
+ /* testInDefault */
+ $return = 'nada';
+ return $return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/Core/Util/Sniffs/Conditions/GetConditionTest.php b/tests/Core/Util/Sniffs/Conditions/GetConditionTest.php
new file mode 100644
index 0000000000..fb51fdc6cc
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Conditions/GetConditionTest.php
@@ -0,0 +1,648 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Conditions;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+use PHP_CodeSniffer\Util\Tokens;
+
+class GetConditionTest extends AbstractMethodUnitTest
+{
+
+ /**
+ * List of all the test markers with their target token in the test case file.
+ *
+ * - The startPoint token is left out as it is tested separately.
+ * - The key is the type of token to look for after the test marker.
+ *
+ * @var array =>
+ */
+ public static $testTargets = [
+ T_VARIABLE => '/* testSeriouslyNestedMethod */',
+ T_RETURN => '/* testDeepestNested */',
+ T_ECHO => '/* testInException */',
+ T_CONSTANT_ENCAPSED_STRING => '/* testInDefault */',
+ ];
+
+ /**
+ * Cache for the test token stack pointers.
+ *
+ * @var array =>
+ */
+ private $testTokens = [];
+
+ /**
+ * List of all the condition markers in the test case file.
+ *
+ * @var string[]
+ */
+ private $conditionMarkers = [
+ '/* condition 0: namespace */',
+ '/* condition 1: if */',
+ '/* condition 2: function */',
+ '/* condition 3-1: if */',
+ '/* condition 3-2: else */',
+ '/* condition 4: if */',
+ '/* condition 5: nested class */',
+ '/* condition 6: class method */',
+ '/* condition 7: switch */',
+ '/* condition 8a: case */',
+ '/* condition 9: while */',
+ '/* condition 10-1: if */',
+ '/* condition 11-1: nested anonymous class */',
+ '/* condition 12: nested anonymous class method */',
+ '/* condition 13: closure */',
+ '/* condition 10-2: elseif */',
+ '/* condition 10-3: foreach */',
+ '/* condition 11-2: try */',
+ '/* condition 11-3: catch */',
+ '/* condition 11-4: finally */',
+ '/* condition 8b: default */',
+ ];
+
+ /**
+ * Cache for the marker token stack pointers.
+ *
+ * @var array =>
+ */
+ private $markerTokens = [];
+
+ /**
+ * Base array with all the scope opening tokens.
+ *
+ * This array is merged with expected result arrays for various unit tests
+ * to make sure all possible conditions are tested.
+ *
+ * This array should be kept in sync with the Tokens::$scopeOpeners array.
+ * This array isn't auto-generated based on the array in Tokens as for these
+ * tests we want to have access to the token constant names, not just their values.
+ *
+ * @var array =>
+ */
+ private $conditionDefaults = [
+ 'T_CLASS' => false,
+ 'T_ANON_CLASS' => false,
+ 'T_INTERFACE' => false,
+ 'T_TRAIT' => false,
+ 'T_NAMESPACE' => false,
+ 'T_FUNCTION' => false,
+ 'T_CLOSURE' => false,
+ 'T_IF' => false,
+ 'T_SWITCH' => false,
+ 'T_CASE' => false,
+ 'T_DECLARE' => false,
+ 'T_DEFAULT' => false,
+ 'T_WHILE' => false,
+ 'T_ELSE' => false,
+ 'T_ELSEIF' => false,
+ 'T_FOR' => false,
+ 'T_FOREACH' => false,
+ 'T_DO' => false,
+ 'T_TRY' => false,
+ 'T_CATCH' => false,
+ 'T_FINALLY' => false,
+ 'T_PROPERTY' => false,
+ 'T_OBJECT' => false,
+ 'T_USE' => false,
+ ];
+
+
+ /**
+ * Set up the token position caches for the tests.
+ *
+ * Retrieves the test tokens and marker token stack pointer positions
+ * only once and caches them as they won't change between the tests anyway.
+ *
+ * @return void
+ */
+ protected function setUp()
+ {
+ if (empty($this->testTokens) === true) {
+ foreach (self::$testTargets as $targetToken => $marker) {
+ $this->testTokens[$marker] = $this->getTargetToken($marker, $targetToken);
+ }
+ }
+
+ if (empty($this->markerTokens) === true) {
+ foreach ($this->conditionMarkers as $marker) {
+ $this->markerTokens[$marker] = $this->getTargetToken($marker, Tokens::$scopeOpeners);
+ }
+ }
+
+ }//end setUp()
+
+
+ /**
+ * Test passing a non-existent token pointer.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::hasCondition
+ *
+ * @return void
+ */
+ public function testNonExistentToken()
+ {
+ $result = Conditions::getCondition(self::$phpcsFile, 100000, Tokens::$ooScopeTokens);
+ $this->assertFalse($result);
+
+ $result = Conditions::hasCondition(self::$phpcsFile, 100000, T_IF);
+ $this->assertFalse($result);
+
+ }//end testNonExistentToken()
+
+
+ /**
+ * Test passing a non conditional token.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::hasCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getFirstCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getLastCondition
+ *
+ * @return void
+ */
+ public function testNonConditionalToken()
+ {
+ $stackPtr = $this->getTargetToken('/* testStartPoint */', T_STRING);
+
+ $result = Conditions::getCondition(self::$phpcsFile, $stackPtr, T_IF);
+ $this->assertFalse($result);
+
+ $result = Conditions::hasCondition(self::$phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
+ $this->assertFalse($result);
+
+ $result = Conditions::getFirstCondition(self::$phpcsFile, $stackPtr);
+ $this->assertFalse($result);
+
+ $result = Conditions::getLastCondition(self::$phpcsFile, $stackPtr);
+ $this->assertFalse($result);
+
+ }//end testNonConditionalToken()
+
+
+ /**
+ * Test retrieving a specific condition from a tokens "conditions" array.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expectedResults Array with the condition token type to search for as key
+ * and the marker for the expected stack pointer result as a value.
+ * @param array $expectedResultsReversed Array with the condition token type to search for as key
+ * and the marker for the expected stack pointer result as a value.
+ *
+ * @dataProvider dataGetCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getCondition
+ *
+ * @return void
+ */
+ public function testGetCondition($testMarker, $expectedResults, $expectedResultsReversed)
+ {
+ $stackPtr = $this->testTokens[$testMarker];
+
+ // Add expected results for all test markers not listed in the data provider.
+ $expectedResults += $this->conditionDefaults;
+
+ foreach ($expectedResults as $conditionType => $expected) {
+ if ($expected !== false) {
+ $expected = $this->markerTokens[$expected];
+ }
+
+ $result = Conditions::getCondition(self::$phpcsFile, $stackPtr, constant($conditionType));
+ $this->assertSame(
+ $expected,
+ $result,
+ "Assertion failed for test marker '{$testMarker}' with condition {$conditionType}"
+ );
+ }
+
+ foreach ($expectedResultsReversed as $conditionType => $expected) {
+ if ($expected !== false) {
+ $expected = $this->markerTokens[$expected];
+ }
+
+ $result = Conditions::getCondition(self::$phpcsFile, $stackPtr, constant($conditionType), true);
+ $this->assertSame(
+ $expected,
+ $result,
+ "Assertion failed for test marker '{$testMarker}' with condition {$conditionType} (reversed)"
+ );
+ }
+
+ }//end testGetCondition()
+
+
+ /**
+ * Data provider.
+ *
+ * Only the conditions which are expected to be *found* need to be listed here.
+ * All other potential conditions will automatically also be tested and will expect
+ * `false` as a result.
+ *
+ * @see testGetCondition()
+ *
+ * @return array
+ */
+ public function dataGetCondition()
+ {
+ $data = [
+ 'testSeriouslyNestedMethod' => [
+ '/* testSeriouslyNestedMethod */',
+ [
+ 'T_CLASS' => '/* condition 5: nested class */',
+ 'T_NAMESPACE' => '/* condition 0: namespace */',
+ 'T_FUNCTION' => '/* condition 2: function */',
+ 'T_IF' => '/* condition 1: if */',
+ 'T_ELSE' => '/* condition 3-2: else */',
+ ],
+ ],
+ 'testDeepestNested' => [
+ '/* testDeepestNested */',
+ [
+ 'T_CLASS' => '/* condition 5: nested class */',
+ 'T_ANON_CLASS' => '/* condition 11-1: nested anonymous class */',
+ 'T_NAMESPACE' => '/* condition 0: namespace */',
+ 'T_FUNCTION' => '/* condition 2: function */',
+ 'T_CLOSURE' => '/* condition 13: closure */',
+ 'T_IF' => '/* condition 1: if */',
+ 'T_SWITCH' => '/* condition 7: switch */',
+ 'T_CASE' => '/* condition 8a: case */',
+ 'T_WHILE' => '/* condition 9: while */',
+ 'T_ELSE' => '/* condition 3-2: else */',
+ ],
+ ],
+ 'testInException' => [
+ '/* testInException */',
+ [
+ 'T_CLASS' => '/* condition 5: nested class */',
+ 'T_NAMESPACE' => '/* condition 0: namespace */',
+ 'T_FUNCTION' => '/* condition 2: function */',
+ 'T_IF' => '/* condition 1: if */',
+ 'T_SWITCH' => '/* condition 7: switch */',
+ 'T_CASE' => '/* condition 8a: case */',
+ 'T_WHILE' => '/* condition 9: while */',
+ 'T_ELSE' => '/* condition 3-2: else */',
+ 'T_FOREACH' => '/* condition 10-3: foreach */',
+ 'T_CATCH' => '/* condition 11-3: catch */',
+ ],
+ ],
+ 'testInDefault' => [
+ '/* testInDefault */',
+ [
+ 'T_CLASS' => '/* condition 5: nested class */',
+ 'T_NAMESPACE' => '/* condition 0: namespace */',
+ 'T_FUNCTION' => '/* condition 2: function */',
+ 'T_IF' => '/* condition 1: if */',
+ 'T_SWITCH' => '/* condition 7: switch */',
+ 'T_DEFAULT' => '/* condition 8b: default */',
+ 'T_ELSE' => '/* condition 3-2: else */',
+ ],
+ ],
+ ];
+
+ // Set up the data for the reversed results.
+ $reversed = $data['testSeriouslyNestedMethod'][1];
+ $reversed['T_IF'] = '/* condition 4: if */';
+ $data['testSeriouslyNestedMethod'][] = $reversed;
+
+ $reversed = $data['testDeepestNested'][1];
+ $reversed['T_FUNCTION'] = '/* condition 12: nested anonymous class method */';
+ $reversed['T_IF'] = '/* condition 10-1: if */';
+ $data['testDeepestNested'][] = $reversed;
+
+ $reversed = $data['testInException'][1];
+ $reversed['T_FUNCTION'] = '/* condition 6: class method */';
+ $reversed['T_IF'] = '/* condition 4: if */';
+ $data['testInException'][] = $reversed;
+
+ $reversed = $data['testInDefault'][1];
+ $reversed['T_FUNCTION'] = '/* condition 6: class method */';
+ $reversed['T_IF'] = '/* condition 4: if */';
+ $data['testInDefault'][] = $reversed;
+
+ return $data;
+
+ }//end dataGetCondition()
+
+
+ /**
+ * Test retrieving a specific condition from a token's "conditions" array,
+ * with multiple allowed possibilities.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getCondition
+ *
+ * @return void
+ */
+ public function testGetConditionMultipleTypes()
+ {
+ $stackPtr = $this->testTokens['/* testInException */'];
+
+ $result = Conditions::getCondition(self::$phpcsFile, $stackPtr, [T_DO, T_FOR]);
+ $this->assertFalse(
+ $result,
+ 'Failed asserting that "testInException" does not have a "do" nor a "for" condition'
+ );
+
+ $result = Conditions::getCondition(self::$phpcsFile, $stackPtr, [T_DO, T_FOR, T_FOREACH]);
+ $this->assertSame(
+ $this->markerTokens['/* condition 10-3: foreach */'],
+ $result,
+ 'Failed asserting that "testInException" has a foreach condition based on the types "do", "for" and "foreach"'
+ );
+
+ $stackPtr = $this->testTokens['/* testDeepestNested */'];
+
+ $result = Conditions::getCondition(self::$phpcsFile, $stackPtr, [T_INTERFACE, T_TRAIT]);
+ $this->assertFalse(
+ $result,
+ 'Failed asserting that "testDeepestNested" does not have an interface nor a trait condition'
+ );
+
+ $result = Conditions::getCondition(self::$phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
+ $this->assertSame(
+ $this->markerTokens['/* condition 5: nested class */'],
+ $result,
+ 'Failed asserting that "testDeepestNested" has a class condition based on the OO Scope token types'
+ );
+
+ }//end testGetConditionMultipleTypes()
+
+
+ /**
+ * Test whether a token has a condition of a certain type.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expectedResults Array with the condition token type to search for as key
+ * and the expected result as a value.
+ *
+ * @dataProvider dataHasCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::hasCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getCondition
+ *
+ * @return void
+ */
+ public function testHasCondition($testMarker, $expectedResults)
+ {
+ $stackPtr = $this->testTokens[$testMarker];
+
+ // Add expected results for all test markers not listed in the data provider.
+ $expectedResults += $this->conditionDefaults;
+
+ foreach ($expectedResults as $conditionType => $expected) {
+ $result = Conditions::hasCondition(self::$phpcsFile, $stackPtr, constant($conditionType));
+ $this->assertSame(
+ $expected,
+ $result,
+ "Assertion failed for test marker '{$testMarker}' with condition {$conditionType}"
+ );
+ }
+
+ }//end testHasCondition()
+
+
+ /**
+ * Data Provider.
+ *
+ * Only list the "true" conditions in the $results array.
+ * All other potential conditions will automatically also be tested
+ * and will expect "false" as a result.
+ *
+ * @see testHasCondition()
+ *
+ * @return array
+ */
+ public function dataHasCondition()
+ {
+ return [
+ 'testSeriouslyNestedMethod' => [
+ '/* testSeriouslyNestedMethod */',
+ [
+ 'T_CLASS' => true,
+ 'T_NAMESPACE' => true,
+ 'T_FUNCTION' => true,
+ 'T_IF' => true,
+ 'T_ELSE' => true,
+ ],
+ ],
+ 'testDeepestNested' => [
+ '/* testDeepestNested */',
+ [
+ 'T_CLASS' => true,
+ 'T_ANON_CLASS' => true,
+ 'T_NAMESPACE' => true,
+ 'T_FUNCTION' => true,
+ 'T_CLOSURE' => true,
+ 'T_IF' => true,
+ 'T_SWITCH' => true,
+ 'T_CASE' => true,
+ 'T_WHILE' => true,
+ 'T_ELSE' => true,
+ ],
+ ],
+ 'testInException' => [
+ '/* testInException */',
+ [
+ 'T_CLASS' => true,
+ 'T_NAMESPACE' => true,
+ 'T_FUNCTION' => true,
+ 'T_IF' => true,
+ 'T_SWITCH' => true,
+ 'T_CASE' => true,
+ 'T_WHILE' => true,
+ 'T_ELSE' => true,
+ 'T_FOREACH' => true,
+ 'T_CATCH' => true,
+ ],
+ ],
+ 'testInDefault' => [
+ '/* testInDefault */',
+ [
+ 'T_CLASS' => true,
+ 'T_NAMESPACE' => true,
+ 'T_FUNCTION' => true,
+ 'T_IF' => true,
+ 'T_SWITCH' => true,
+ 'T_DEFAULT' => true,
+ 'T_ELSE' => true,
+ ],
+ ],
+ ];
+
+ }//end dataHasCondition()
+
+
+ /**
+ * Test whether a token has a condition of a certain type, with multiple allowed possibilities.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::hasCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getCondition
+ *
+ * @return void
+ */
+ public function testHasConditionMultipleTypes()
+ {
+ $stackPtr = $this->testTokens['/* testInException */'];
+
+ $result = Conditions::hasCondition(self::$phpcsFile, $stackPtr, [T_TRY, T_FINALLY]);
+ $this->assertFalse(
+ $result,
+ 'Failed asserting that "testInException" does not have a "try" nor a "finally" condition'
+ );
+
+ $result = Conditions::hasCondition(self::$phpcsFile, $stackPtr, [T_TRY, T_CATCH, T_FINALLY]);
+ $this->assertTrue(
+ $result,
+ 'Failed asserting that "testInException" has a "try", "catch" or "finally" condition'
+ );
+
+ $stackPtr = $this->testTokens['/* testSeriouslyNestedMethod */'];
+
+ $result = Conditions::hasCondition(self::$phpcsFile, $stackPtr, [T_ANON_CLASS, T_CLOSURE]);
+ $this->assertFalse(
+ $result,
+ 'Failed asserting that "testSeriouslyNestedMethod" does not have an anonymous class nor a closure condition'
+ );
+
+ $result = Conditions::hasCondition(self::$phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
+ $this->assertTrue(
+ $result,
+ 'Failed asserting that "testSeriouslyNestedMethod" has an OO Scope token condition'
+ );
+
+ }//end testHasConditionMultipleTypes()
+
+
+ /**
+ * Test retrieving the first condition token pointer, in general and of specific types.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ *
+ * @dataProvider dataGetFirstCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getFirstCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getCondition
+ *
+ * @return void
+ */
+ public function testGetFirstCondition($testMarker)
+ {
+ $stackPtr = $this->testTokens[$testMarker];
+
+ $result = Conditions::getFirstCondition(self::$phpcsFile, $stackPtr);
+ $this->assertSame($this->markerTokens['/* condition 0: namespace */'], $result);
+
+ $result = Conditions::getFirstCondition(self::$phpcsFile, $stackPtr, T_IF);
+ $this->assertSame($this->markerTokens['/* condition 1: if */'], $result);
+
+ $result = Conditions::getFirstCondition(self::$phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
+ $this->assertSame($this->markerTokens['/* condition 5: nested class */'], $result);
+
+ $result = Conditions::getFirstCondition(self::$phpcsFile, $stackPtr, [T_ELSEIF]);
+ $this->assertFalse($result);
+
+ }//end testGetFirstCondition()
+
+
+ /**
+ * Data provider. Pass the markers for the test tokens on.
+ *
+ * @see testGetFirstCondition()
+ *
+ * @return array
+ */
+ public function dataGetFirstCondition()
+ {
+ $data = [];
+ foreach (self::$testTargets as $marker) {
+ $data[] = [$marker];
+ }
+
+ return $data;
+
+ }//end dataGetFirstCondition()
+
+
+ /**
+ * Test retrieving the last condition token pointer, in general and of specific types.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The marker for the pointers to the expected condition
+ * results for the pre-set tests.
+ *
+ * @dataProvider dataGetLastCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getLastCondition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::getCondition
+ *
+ * @return void
+ */
+ public function testGetLastCondition($testMarker, $expected)
+ {
+ $stackPtr = $this->testTokens[$testMarker];
+
+ $result = Conditions::getLastCondition(self::$phpcsFile, $stackPtr);
+ $this->assertSame($this->markerTokens[$expected['no type']], $result);
+
+ $result = Conditions::getLastCondition(self::$phpcsFile, $stackPtr, T_IF);
+ $this->assertSame($this->markerTokens[$expected['T_IF']], $result);
+
+ $result = Conditions::getLastCondition(self::$phpcsFile, $stackPtr, Tokens::$ooScopeTokens);
+ $this->assertSame($this->markerTokens[$expected['OO tokens']], $result);
+
+ $result = Conditions::getLastCondition(self::$phpcsFile, $stackPtr, [T_FINALLY]);
+ $this->assertFalse($result);
+
+ }//end testGetLastCondition()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testGetLastCondition()
+ *
+ * @return array
+ */
+ public function dataGetLastCondition()
+ {
+ return [
+ 'testSeriouslyNestedMethod' => [
+ '/* testSeriouslyNestedMethod */',
+ [
+ 'no type' => '/* condition 5: nested class */',
+ 'T_IF' => '/* condition 4: if */',
+ 'OO tokens' => '/* condition 5: nested class */',
+ ],
+ ],
+ 'testDeepestNested' => [
+ '/* testDeepestNested */',
+ [
+ 'no type' => '/* condition 13: closure */',
+ 'T_IF' => '/* condition 10-1: if */',
+ 'OO tokens' => '/* condition 11-1: nested anonymous class */',
+ ],
+ ],
+ 'testInException' => [
+ '/* testInException */',
+ [
+ 'no type' => '/* condition 11-3: catch */',
+ 'T_IF' => '/* condition 4: if */',
+ 'OO tokens' => '/* condition 5: nested class */',
+ ],
+ ],
+ 'testInDefault' => [
+ '/* testInDefault */',
+ [
+ 'no type' => '/* condition 8b: default */',
+ 'T_IF' => '/* condition 4: if */',
+ 'OO tokens' => '/* condition 5: nested class */',
+ ],
+ ],
+ ];
+
+ }//end dataGetLastCondition()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Conditions/IsOOConstantTest.inc b/tests/Core/Util/Sniffs/Conditions/IsOOConstantTest.inc
new file mode 100644
index 0000000000..e9ad92b8f0
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Conditions/IsOOConstantTest.inc
@@ -0,0 +1,39 @@
+
+ * @copyright 2017-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Conditions;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+
+class IsOOConstantTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test passing a non-existent token pointer.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::isOOConstant
+ *
+ * @return void
+ */
+ public function testNonExistentToken()
+ {
+ $result = Conditions::isOOConstant(self::$phpcsFile, 10000);
+ $this->assertFalse($result);
+
+ }//end testNonExistentToken()
+
+
+ /**
+ * Test passing a non const token.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::isOOConstant
+ *
+ * @return void
+ */
+ public function testNonConstToken()
+ {
+ $result = Conditions::isOOConstant(self::$phpcsFile, 0);
+ $this->assertFalse($result);
+
+ }//end testNonConstToken()
+
+
+ /**
+ * Test correctly identifying whether a T_CONST token is a class constant.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expected The expected function return value.
+ *
+ * @dataProvider dataIsOOConstant
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::isOOConstant
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::validDirectScope
+ *
+ * @return void
+ */
+ public function testIsOOConstant($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_CONST);
+ $result = Conditions::isOOConstant(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testIsOOConstant()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsOOConstant()
+ *
+ * @return array
+ */
+ public function dataIsOOConstant()
+ {
+ return [
+ [
+ '/* testGlobalConst */',
+ false,
+ ],
+ [
+ '/* testFunctionConst */',
+ false,
+ ],
+ [
+ '/* testClassConst */',
+ true,
+ ],
+ [
+ '/* testClassMethodConst */',
+ false,
+ ],
+ [
+ '/* testAnonClassConst */',
+ true,
+ ],
+ [
+ '/* testInterfaceConst */',
+ true,
+ ],
+ [
+ '/* testTraitConst */',
+ false,
+ ],
+ ];
+
+ }//end dataIsOOConstant()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Conditions/IsOOMethodTest.inc b/tests/Core/Util/Sniffs/Conditions/IsOOMethodTest.inc
new file mode 100644
index 0000000000..d4e0c2ec11
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Conditions/IsOOMethodTest.inc
@@ -0,0 +1,40 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Conditions;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+
+class IsOOMethodTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test passing a non-existent token pointer.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::isOOMethod
+ *
+ * @return void
+ */
+ public function testNonExistentToken()
+ {
+ $result = Conditions::isOOMethod(self::$phpcsFile, 10000);
+ $this->assertFalse($result);
+
+ }//end testNonExistentToken()
+
+
+ /**
+ * Test passing a non function token.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::isOOMethod
+ *
+ * @return void
+ */
+ public function testNonFunctionToken()
+ {
+ $result = Conditions::isOOMethod(self::$phpcsFile, 0);
+ $this->assertFalse($result);
+
+ }//end testNonFunctionToken()
+
+
+ /**
+ * Test correctly identifying whether a T_FUNCTION token is a class method declaration.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expected The expected function return value.
+ *
+ * @dataProvider dataIsOOMethod
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::isOOMethod
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::validDirectScope
+ *
+ * @return void
+ */
+ public function testIsOOMethod($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, [T_FUNCTION, T_CLOSURE]);
+ $result = Conditions::isOOMethod(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testIsOOMethod()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsOOMethod()
+ *
+ * @return array
+ */
+ public function dataIsOOMethod()
+ {
+ return [
+ [
+ '/* testGlobalFunction */',
+ false,
+ ],
+ [
+ '/* testNestedFunction */',
+ false,
+ ],
+ [
+ '/* testNestedClosure */',
+ false,
+ ],
+ [
+ '/* testClassMethod */',
+ true,
+ ],
+ [
+ '/* testClassNestedFunction */',
+ false,
+ ],
+ [
+ '/* testClassNestedClosure */',
+ false,
+ ],
+ [
+ '/* testClassAbstractMethod */',
+ true,
+ ],
+ [
+ '/* testAnonClassMethod */',
+ true,
+ ],
+ [
+ '/* testInterfaceMethod */',
+ true,
+ ],
+ [
+ '/* testTraitMethod */',
+ true,
+ ],
+ ];
+
+ }//end dataIsOOMethod()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Conditions/IsOOPropertyTest.inc b/tests/Core/Util/Sniffs/Conditions/IsOOPropertyTest.inc
new file mode 100644
index 0000000000..176395604b
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Conditions/IsOOPropertyTest.inc
@@ -0,0 +1,89 @@
+setLogger(
+ new class {
+ /* testNestedAnonClassProp */
+ private $varName = 'hello';
+});
+
+if ( has_filter( 'comments_open' ) === false ) {
+ add_filter( 'comments_open', new class {
+ /* testDoubleNestedAnonClassProp */
+ public $year = 2017; // Ok.
+
+ /* testDoubleNestedAnonClassMethodParameter */
+ public function __construct( $open, $post_id ) {
+ /* testDoubleNestedAnonClassMethodLocalVar */
+ global $page;
+ }
+ /* testFunctionCallParameter */
+ }, $priority, 2 );
+}
diff --git a/tests/Core/Util/Sniffs/Conditions/IsOOPropertyTest.php b/tests/Core/Util/Sniffs/Conditions/IsOOPropertyTest.php
new file mode 100644
index 0000000000..d0ce19e4b3
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Conditions/IsOOPropertyTest.php
@@ -0,0 +1,181 @@
+
+ * @copyright 2017-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Conditions;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Conditions;
+
+class IsOOPropertyTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test passing a non-existent token pointer.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::isOOProperty
+ *
+ * @return void
+ */
+ public function testNonExistentToken()
+ {
+ $result = Conditions::isOOProperty(self::$phpcsFile, 10000);
+ $this->assertFalse($result);
+
+ }//end testNonExistentToken()
+
+
+ /**
+ * Test passing a non variable token.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::isOOProperty
+ *
+ * @return void
+ */
+ public function testNonVariableToken()
+ {
+ $result = Conditions::isOOProperty(self::$phpcsFile, 0);
+ $this->assertFalse($result);
+
+ }//end testNonVariableToken()
+
+
+ /**
+ * Test correctly identifying whether a T_VARIABLE token is a class property declaration.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expected The expected function return value.
+ *
+ * @dataProvider dataIsOOProperty
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::isOOProperty
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Conditions::validDirectScope
+ *
+ * @return void
+ */
+ public function testIsOOProperty($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_VARIABLE);
+ $result = Conditions::isOOProperty(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testIsOOProperty()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsOOProperty()
+ *
+ * @return array
+ */
+ public function dataIsOOProperty()
+ {
+ return [
+ [
+ '/* testGlobalVar */',
+ false,
+ ],
+ [
+ '/* testFunctionParameter */',
+ false,
+ ],
+ [
+ '/* testFunctionLocalVar */',
+ false,
+ ],
+ [
+ '/* testClassPropPublic */',
+ true,
+ ],
+ [
+ '/* testClassPropVar */',
+ true,
+ ],
+ [
+ '/* testClassPropStaticProtected */',
+ true,
+ ],
+ [
+ '/* testMethodParameter */',
+ false,
+ ],
+ [
+ '/* testMethodLocalVar */',
+ false,
+ ],
+ [
+ '/* testAnonClassPropPrivate */',
+ true,
+ ],
+ [
+ '/* testAnonMethodParameter */',
+ false,
+ ],
+ [
+ '/* testAnonMethodLocalVar */',
+ false,
+ ],
+ [
+ '/* testInterfaceProp */',
+ false,
+ ],
+ [
+ '/* testInterfaceMethodParameter */',
+ false,
+ ],
+ [
+ '/* testTraitProp */',
+ true,
+ ],
+ [
+ '/* testTraitMethodParameter */',
+ false,
+ ],
+ [
+ '/* testClassMultiProp1 */',
+ true,
+ ],
+ [
+ '/* testClassMultiProp2 */',
+ true,
+ ],
+ [
+ '/* testClassMultiProp3 */',
+ true,
+ ],
+ [
+ '/* testGlobalVarObj */',
+ false,
+ ],
+ [
+ '/* testNestedAnonClassProp */',
+ true,
+ ],
+ [
+ '/* testDoubleNestedAnonClassProp */',
+ true,
+ ],
+ [
+ '/* testDoubleNestedAnonClassMethodParameter */',
+ false,
+ ],
+ [
+ '/* testDoubleNestedAnonClassMethodLocalVar */',
+ false,
+ ],
+ [
+ '/* testFunctionCallParameter */',
+ false,
+ ],
+ ];
+
+ }//end dataIsOOProperty()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/ConstructNames/GetDeclarationNameJSTest.js b/tests/Core/Util/Sniffs/ConstructNames/GetDeclarationNameJSTest.js
new file mode 100644
index 0000000000..ee0a76a44f
--- /dev/null
+++ b/tests/Core/Util/Sniffs/ConstructNames/GetDeclarationNameJSTest.js
@@ -0,0 +1,23 @@
+/* testInvalidTokenPassed */
+print something;
+
+var object =
+{
+ /* testClosure */
+ propertyName: function () {}
+}
+
+/* testFunction */
+function functionName() {}
+
+/* testClass */
+class ClassName
+{
+ /* testMethod */
+ methodName() {
+ return false;
+ }
+}
+
+/* testFunctionUnicode */
+function π() {}
diff --git a/tests/Core/Util/Sniffs/ConstructNames/GetDeclarationNameJSTest.php b/tests/Core/Util/Sniffs/ConstructNames/GetDeclarationNameJSTest.php
new file mode 100644
index 0000000000..4aee36d606
--- /dev/null
+++ b/tests/Core/Util/Sniffs/ConstructNames/GetDeclarationNameJSTest.php
@@ -0,0 +1,93 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\ConstructNames;
+
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+
+class GetDeclarationNameJSTest extends GetDeclarationNameTest
+{
+
+ /**
+ * The file extension of the test case file (without leading dot).
+ *
+ * @var string
+ */
+ protected static $fileExtension = 'js';
+
+
+ /**
+ * Test receiving an expected exception when a non-supported token is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage Token type "T_STRING" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::getDeclarationName
+ *
+ * @return void
+ */
+ public function testInvalidTokenPassed()
+ {
+ $print = $this->getTargetToken('/* testInvalidTokenPassed */', T_STRING);
+ $result = ConstructNames::getDeclarationName(self::$phpcsFile, $print);
+
+ }//end testInvalidTokenPassed()
+
+
+ /**
+ * Data provider.
+ *
+ * @see GetDeclarationNameTest::testGetDeclarationNameNull()
+ *
+ * @return array
+ */
+ public function dataGetDeclarationNameNull()
+ {
+ return [
+ [
+ '/* testClosure */',
+ T_CLOSURE,
+ ],
+ ];
+
+ }//end dataGetDeclarationNameNull()
+
+
+ /**
+ * Data provider.
+ *
+ * @see GetDeclarationNameTest::testGetDeclarationName()
+ *
+ * @return array
+ */
+ public function dataGetDeclarationName()
+ {
+ return [
+ [
+ '/* testFunction */',
+ 'functionName',
+ ],
+ [
+ '/* testClass */',
+ 'ClassName',
+ ],
+ [
+ '/* testMethod */',
+ 'methodName',
+ ],
+ [
+ '/* testFunctionUnicode */',
+ 'π',
+ ],
+ ];
+
+ }//end dataGetDeclarationName()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/ConstructNames/GetDeclarationNameTest.inc b/tests/Core/Util/Sniffs/ConstructNames/GetDeclarationNameTest.inc
new file mode 100644
index 0000000000..764f873a45
--- /dev/null
+++ b/tests/Core/Util/Sniffs/ConstructNames/GetDeclarationNameTest.inc
@@ -0,0 +1,60 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\ConstructNames;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+
+class GetDeclarationNameTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a non-supported token is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage Token type "T_ECHO" is not T_FUNCTION, T_CLASS, T_INTERFACE or T_TRAIT
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::getDeclarationName
+ *
+ * @return void
+ */
+ public function testInvalidTokenPassed()
+ {
+ $interface = $this->getTargetToken('/* testInvalidTokenPassed */', T_ECHO);
+ $result = ConstructNames::getDeclarationName(self::$phpcsFile, $interface);
+
+ }//end testInvalidTokenPassed()
+
+
+ /**
+ * Test receiving "null" when passed an anonymous construct.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param int|string $targetType Token type of the token to get as stackPtr.
+ *
+ * @dataProvider dataGetDeclarationNameNull
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::getDeclarationName
+ *
+ * @return void
+ */
+ public function testGetDeclarationNameNull($testMarker, $targetType)
+ {
+ $target = $this->getTargetToken($testMarker, $targetType);
+ $result = ConstructNames::getDeclarationName(self::$phpcsFile, $target);
+ $this->assertNull($result);
+
+ }//end testGetDeclarationNameNull()
+
+
+ /**
+ * Data provider for the GetDeclarationNameNull test.
+ *
+ * @see testGetDeclarationNameNull()
+ *
+ * @return array
+ */
+ public function dataGetDeclarationNameNull()
+ {
+ return [
+ [
+ '/* testClosure */',
+ T_CLOSURE,
+ ],
+ [
+ '/* testAnonClass */',
+ T_ANON_CLASS,
+ ],
+ ];
+
+ }//end dataGetDeclarationNameNull()
+
+
+ /**
+ * Test retrieving the name of a function or OO structure.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param string $expected Expected function output.
+ *
+ * @dataProvider dataGetDeclarationName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::getDeclarationName
+ *
+ * @return void
+ */
+ public function testGetDeclarationName($testMarker, $expected)
+ {
+ $target = $this->getTargetToken($testMarker, [T_CLASS, T_INTERFACE, T_TRAIT, T_FUNCTION]);
+ $result = ConstructNames::getDeclarationName(self::$phpcsFile, $target);
+ $this->assertSame($expected, $result);
+
+ }//end testGetDeclarationName()
+
+
+ /**
+ * Data provider for the GetDeclarationName test.
+ *
+ * @see testGetDeclarationName()
+ *
+ * @return array
+ */
+ public function dataGetDeclarationName()
+ {
+ return [
+ [
+ '/* testFunction */',
+ 'functionName',
+ ],
+ [
+ '/* testClass */',
+ 'ClassName',
+ ],
+ [
+ '/* testMethod */',
+ 'methodName',
+ ],
+ [
+ '/* testAbstractMethod */',
+ 'abstractMethodName',
+ ],
+ [
+ '/* testExtendedClass */',
+ 'ExtendedClass',
+ ],
+ [
+ '/* testInterface */',
+ 'InterfaceName',
+ ],
+ [
+ '/* testTrait */',
+ 'TraitName',
+ ],
+ [
+ '/* testClassWithCommentsAndNewLines */',
+ 'ClassWithCommentsAndNewLines',
+ ],
+ [
+ '/* testClassWithNumber */',
+ 'ClassWith1Number',
+ ],
+ [
+ '/* testInterfaceWithNumbers */',
+ 'InterfaceWith12345Numbers',
+ ],
+ [
+ '/* testTraitStartingWithNumber */',
+ '5InvalidNameStartingWithNumber',
+ ],
+ [
+ '/* testClassEndingWithNumber */',
+ 'ValidNameEndingWithNumber5',
+ ],
+ [
+ '/* testInterfaceFullyNumeric */',
+ '12345',
+ ],
+ [
+ '/* testMissingInterfaceName */',
+ '',
+ ],
+ [
+ '/* testLiveCoding */',
+ '',
+ ],
+ ];
+
+ }//end dataGetDeclarationName()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/ConstructNames/IsCamelCapsTest.php b/tests/Core/Util/Sniffs/ConstructNames/IsCamelCapsTest.php
new file mode 100644
index 0000000000..d5399bea3a
--- /dev/null
+++ b/tests/Core/Util/Sniffs/ConstructNames/IsCamelCapsTest.php
@@ -0,0 +1,149 @@
+
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\ConstructNames;
+
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHPUnit\Framework\TestCase;
+
+class IsCamelCapsTest extends TestCase
+{
+
+
+ /**
+ * Test valid public function/method names.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::isCamelCaps
+ *
+ * @return void
+ */
+ public function testValidNotClassFormatPublic()
+ {
+ $this->assertTrue(ConstructNames::isCamelCaps('thisIsCamelCaps', false, true, true));
+ $this->assertTrue(ConstructNames::isCamelCaps('thisISCamelCaps', false, true, false));
+
+ }//end testValidNotClassFormatPublic()
+
+
+ /**
+ * Test invalid public function/method names.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::isCamelCaps
+ *
+ * @return void
+ */
+ public function testInvalidNotClassFormatPublic()
+ {
+ $this->assertFalse(ConstructNames::isCamelCaps('_thisIsCamelCaps', false, true, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('thisISCamelCaps', false, true, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('ThisIsCamelCaps', false, true, true));
+
+ $this->assertFalse(ConstructNames::isCamelCaps('3thisIsCamelCaps', false, true, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('*thisIsCamelCaps', false, true, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('-thisIsCamelCaps', false, true, true));
+
+ $this->assertFalse(ConstructNames::isCamelCaps('this*IsCamelCaps', false, true, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('this-IsCamelCaps', false, true, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('this_IsCamelCaps', false, true, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('this_is_camel_caps', false, true, true));
+
+ }//end testInvalidNotClassFormatPublic()
+
+
+ /**
+ * Test valid private method names.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::isCamelCaps
+ *
+ * @return void
+ */
+ public function testValidNotClassFormatPrivate()
+ {
+ $this->assertTrue(ConstructNames::isCamelCaps('_thisIsCamelCaps', false, false, true));
+ $this->assertTrue(ConstructNames::isCamelCaps('_thisISCamelCaps', false, false, false));
+ $this->assertTrue(ConstructNames::isCamelCaps('_i18N', false, false, true));
+ $this->assertTrue(ConstructNames::isCamelCaps('_i18n', false, false, true));
+
+ }//end testValidNotClassFormatPrivate()
+
+
+ /**
+ * Test invalid private method names.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::isCamelCaps
+ *
+ * @return void
+ */
+ public function testInvalidNotClassFormatPrivate()
+ {
+ $this->assertFalse(ConstructNames::isCamelCaps('thisIsCamelCaps', false, false, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('_thisISCamelCaps', false, false, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('_ThisIsCamelCaps', false, false, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('__thisIsCamelCaps', false, false, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('__thisISCamelCaps', false, false, false));
+
+ $this->assertFalse(ConstructNames::isCamelCaps('3thisIsCamelCaps', false, false, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('*thisIsCamelCaps', false, false, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('-thisIsCamelCaps', false, false, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('_this_is_camel_caps', false, false, true));
+
+ }//end testInvalidNotClassFormatPrivate()
+
+
+ /**
+ * Test valid class names.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::isCamelCaps
+ *
+ * @return void
+ */
+ public function testValidClassFormatPublic()
+ {
+ $this->assertTrue(ConstructNames::isCamelCaps('ThisIsCamelCaps', true, true, true));
+ $this->assertTrue(ConstructNames::isCamelCaps('ThisISCamelCaps', true, true, false));
+ $this->assertTrue(ConstructNames::isCamelCaps('This3IsCamelCaps', true, true, false));
+
+ }//end testValidClassFormatPublic()
+
+
+ /**
+ * Test invalid class names.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::isCamelCaps
+ *
+ * @return void
+ */
+ public function testInvalidClassFormat()
+ {
+ $this->assertFalse(ConstructNames::isCamelCaps('thisIsCamelCaps', true));
+ $this->assertFalse(ConstructNames::isCamelCaps('This-IsCamelCaps', true));
+ $this->assertFalse(ConstructNames::isCamelCaps('This_Is_Camel_Caps', true));
+
+ }//end testInvalidClassFormat()
+
+
+ /**
+ * Test invalid class names with the private flag set.
+ *
+ * Note that the private flag is ignored if the class format
+ * flag is set, so these names are all invalid.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::isCamelCaps
+ *
+ * @return void
+ */
+ public function testInvalidClassFormatPrivate()
+ {
+ $this->assertFalse(ConstructNames::isCamelCaps('_ThisIsCamelCaps', true, true));
+ $this->assertFalse(ConstructNames::isCamelCaps('_ThisIsCamelCaps', true, false));
+
+ }//end testInvalidClassFormatPrivate()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/ConstructNames/LowerConsecutiveCapsTest.php b/tests/Core/Util/Sniffs/ConstructNames/LowerConsecutiveCapsTest.php
new file mode 100644
index 0000000000..9a72f17a2a
--- /dev/null
+++ b/tests/Core/Util/Sniffs/ConstructNames/LowerConsecutiveCapsTest.php
@@ -0,0 +1,154 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\ConstructNames;
+
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHPUnit\Framework\TestCase;
+
+class LowerConsecutiveCapsTest extends TestCase
+{
+
+
+ /**
+ * Test lowering consecutive caps in a text string.
+ *
+ * @param string $string The string.
+ * @param string $expected The expected function return value.
+ *
+ * @dataProvider dataLowerConsecutiveCaps
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::lowerConsecutiveCaps
+ *
+ * @return void
+ */
+ public function testLowerConsecutiveCaps($string, $expected)
+ {
+ $this->assertSame($expected, ConstructNames::lowerConsecutiveCaps($string));
+
+ }//end testLowerConsecutiveCaps()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testLowerConsecutiveCaps()
+ *
+ * @return array
+ */
+ public function dataLowerConsecutiveCaps()
+ {
+ $data = [
+ // Deliberately empty.
+ [
+ '',
+ '',
+ ],
+ [
+ 'nocaps',
+ 'nocaps',
+ ],
+ [
+ 'noConsecutiveCaps',
+ 'noConsecutiveCaps',
+ ],
+ [
+ 'IsAMethod',
+ 'IsAmethod',
+ ],
+ [
+ 'IsThisAI',
+ 'IsThisAi',
+ ],
+ [
+ 'IsThisAI20',
+ 'IsThisAi20',
+ ],
+ [
+ 'Is_A_Method',
+ 'Is_A_Method',
+ ],
+ [
+ 'PHPMethod',
+ 'PhpMethod',
+ ],
+ [
+ 'PHP7Method',
+ 'Php7Method',
+ ],
+ [
+ 'MyPHPMethod',
+ 'MyPhpMethod',
+ ],
+ [
+ 'My_PHP_Method',
+ 'My_Php_Method',
+ ],
+ [
+ 'MyMethodInPHP',
+ 'MyMethodInPhp',
+ ],
+ [
+ 'MyMethodInPHP7',
+ 'MyMethodInPhp7',
+ ],
+ [
+ 'My-CSS-Selector',
+ 'My-Css-Selector',
+ ],
+ [
+ 'SomeCAPSAndMoreCAPSAndMORE',
+ 'SomeCapsAndMoreCapsAndMore',
+ ],
+ ];
+
+ $unicodeData = [
+ // ASCII Extended.
+ [
+ 'IÑTËRNÂTÎÔNÀLÍŽÆTIØN',
+ 'Iñtërnâtîônàlížætiøn',
+ ],
+
+ // Russian, no consecutive caps.
+ [
+ 'МояРабота',
+ 'МояРабота',
+ ],
+
+ // Russian, consecutive caps.
+ [
+ 'МОЯРабота',
+ 'МояРабота',
+ ],
+
+ // Russian, consecutive caps with separator.
+ [
+ 'МОЯ_Работа',
+ 'Моя_Работа',
+ ],
+ ];
+
+ // Add Unicode name testcases.
+ if (function_exists('mb_strtolower') === true) {
+ $data = array_merge($data, $unicodeData);
+ } else {
+ // If MBString is not available, non-ASCII input should be returned unchanged.
+ foreach ($unicodeData as $dataset) {
+ $data[] = [
+ $dataset[0],
+ $dataset[0],
+ ];
+ }
+ }
+
+ return $data;
+
+ }//end dataLowerConsecutiveCaps()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/ConstructNames/NumbersTest.php b/tests/Core/Util/Sniffs/ConstructNames/NumbersTest.php
new file mode 100644
index 0000000000..eb7626c630
--- /dev/null
+++ b/tests/Core/Util/Sniffs/ConstructNames/NumbersTest.php
@@ -0,0 +1,181 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\ConstructNames;
+
+use PHP_CodeSniffer\Util\Sniffs\ConstructNames;
+use PHPUnit\Framework\TestCase;
+
+class NumbersTest extends TestCase
+{
+
+
+ /**
+ * Test verifying whether a text string contains numeric characters.
+ *
+ * @param string $string Input string.
+ * @param array $expected Expected function output for the various functions.
+ *
+ * @dataProvider dataStringsWithNumbers
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::hasNumbers
+ *
+ * @return void
+ */
+ public function testHasNumbers($string, $expected)
+ {
+ $this->assertSame($expected['has'], ConstructNames::hasNumbers($string));
+
+ }//end testHasNumbers()
+
+
+ /**
+ * Test trimming numbers from the beginning of a text string.
+ *
+ * @param string $string Input string.
+ * @param array $expected Expected function output for the various functions.
+ *
+ * @dataProvider dataStringsWithNumbers
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::ltrimNumbers
+ *
+ * @return void
+ */
+ public function testLtrimNumbers($string, $expected)
+ {
+ $this->assertSame($expected['ltrim'], ConstructNames::ltrimNumbers($string));
+
+ }//end testLtrimNumbers()
+
+
+ /**
+ * Test removing all numbers from a text string.
+ *
+ * @param string $string Input string.
+ * @param array $expected Expected function output for the various functions.
+ *
+ * @dataProvider dataStringsWithNumbers
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ConstructNames::removeNumbers
+ *
+ * @return void
+ */
+ public function testRemoveNumbers($string, $expected)
+ {
+ $this->assertSame($expected['remove'], ConstructNames::removeNumbers($string));
+
+ }//end testRemoveNumbers()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testHasNumbers()
+ * @see testLtrimNumbers()
+ * @see testRemoveNumbers()
+ *
+ * @return array
+ */
+ public function dataStringsWithNumbers()
+ {
+ return [
+ [
+ // Deliberately empty.
+ '',
+ [
+ 'has' => false,
+ 'ltrim' => '',
+ 'remove' => '',
+ ],
+ ],
+ [
+ 'NoNumbers',
+ [
+ 'has' => false,
+ 'ltrim' => 'NoNumbers',
+ 'remove' => 'NoNumbers',
+ ],
+ ],
+ [
+ '1',
+ [
+ 'has' => true,
+ 'ltrim' => '',
+ 'remove' => '',
+ ],
+ ],
+ [
+ '1234567890',
+ [
+ 'has' => true,
+ 'ltrim' => '',
+ 'remove' => '',
+ ],
+ ],
+ [
+ '1Pancake',
+ [
+ 'has' => true,
+ 'ltrim' => 'Pancake',
+ 'remove' => 'Pancake',
+ ],
+ ],
+ [
+ '123Pancakes',
+ [
+ 'has' => true,
+ 'ltrim' => 'Pancakes',
+ 'remove' => 'Pancakes',
+ ],
+ ],
+ [
+ '1Pancake2Pancakes',
+ [
+ 'has' => true,
+ 'ltrim' => 'Pancake2Pancakes',
+ 'remove' => 'PancakePancakes',
+ ],
+ ],
+ [
+ '123Pancake456Pancakes789Pancakes',
+ [
+ 'has' => true,
+ 'ltrim' => 'Pancake456Pancakes789Pancakes',
+ 'remove' => 'PancakePancakesPancakes',
+ ],
+ ],
+ [
+ '½Pancake⅝Pancake',
+ [
+ 'has' => true,
+ 'ltrim' => 'Pancake⅝Pancake',
+ 'remove' => 'PancakePancake',
+ ],
+ ],
+ [
+ 'ⅦPancakesⅲPancakes',
+ [
+ 'has' => true,
+ 'ltrim' => 'PancakesⅲPancakes',
+ 'remove' => 'PancakesPancakes',
+ ],
+ ],
+ [
+ '๑٦⑱Pancake٨๔⓳Pancakes㊱௫Pancakes',
+ [
+ 'has' => true,
+ 'ltrim' => 'Pancake٨๔⓳Pancakes㊱௫Pancakes',
+ 'remove' => 'PancakePancakesPancakes',
+ ],
+ ],
+ ];
+
+ }//end dataStringsWithNumbers()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/FunctionDeclarations/GetParametersTest.inc b/tests/Core/Util/Sniffs/FunctionDeclarations/GetParametersTest.inc
new file mode 100644
index 0000000000..0bb5001380
--- /dev/null
+++ b/tests/Core/Util/Sniffs/FunctionDeclarations/GetParametersTest.inc
@@ -0,0 +1,64 @@
+
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\FunctionDeclarations;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
+
+class GetParametersTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a non function token is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_FUNCTION or T_CLOSURE
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testNotAFunctionException()
+ {
+ $interface = $this->getTargetToken('/* testNotAFunction */', T_INTERFACE);
+ $result = FunctionDeclarations::getParameters(self::$phpcsFile, $interface);
+
+ }//end testNotAFunctionException()
+
+
+ /**
+ * Verify function declaration without parameters.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testFunctionNoParams()
+ {
+ $expected = [];
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testFunctionNoParams()
+
+
+ /**
+ * Verify pass-by-reference parsing.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testPassByReference()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var',
+ 'content' => '&$var',
+ 'pass_by_reference' => true,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testPassByReference()
+
+
+ /**
+ * Verify array hint parsing.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testArrayHint()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var',
+ 'content' => 'array $var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => 'array',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testArrayHint()
+
+
+ /**
+ * Verify type hint parsing.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testTypeHint()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var1',
+ 'content' => 'foo $var1',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => 'foo',
+ 'nullable_type' => false,
+ ];
+
+ $expected[1] = [
+ 'name' => '$var2',
+ 'content' => 'bar $var2',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => 'bar',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testTypeHint()
+
+
+ /**
+ * Verify self type hint parsing.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testSelfTypeHint()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var',
+ 'content' => 'self $var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => 'self',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testSelfTypeHint()
+
+
+ /**
+ * Verify callable type hint parsing.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testCallableTypeHint()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var',
+ 'content' => 'callable $var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => 'callable',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testCallableTypeHint()
+
+
+ /**
+ * Verify nullable callable type hint parsing.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testNullableCallableTypeHint()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var',
+ 'content' => '?callable $var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '?callable',
+ 'nullable_type' => true,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testNullableCallableTypeHint()
+
+
+ /**
+ * Verify nullable type hint parsing.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testNullableTypeHint()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var1',
+ 'content' => '?int $var1',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '?int',
+ 'nullable_type' => true,
+ ];
+
+ $expected[1] = [
+ 'name' => '$var2',
+ 'content' => '?\bar $var2',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '?\bar',
+ 'nullable_type' => true,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testNullableTypeHint()
+
+
+ /**
+ * Verify iterable type hint parsing.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testIterableTypeHint()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var',
+ 'content' => 'iterable $var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => 'iterable',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testIterableTypeHint()
+
+
+ /**
+ * Verify variable.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testVariable()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var',
+ 'content' => '$var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testVariable()
+
+
+ /**
+ * Verify default value parsing with a single function param.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testSingleDefaultValue()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var1',
+ 'content' => '$var1=self::CONSTANT',
+ 'default' => 'self::CONSTANT',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testSingleDefaultValue()
+
+
+ /**
+ * Verify default value parsing.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testDefaultValues()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var1',
+ 'content' => '$var1=1',
+ 'default' => '1',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+ $expected[1] = [
+ 'name' => '$var2',
+ 'content' => "\$var2='value'",
+ 'default' => "'value'",
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testDefaultValues()
+
+
+ /**
+ * Verify default value parsing with array values.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testArrayDefaultValues()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var1',
+ 'content' => '$var1 = []',
+ 'default' => '[]',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+ $expected[1] = [
+ 'name' => '$var2',
+ 'content' => '$var2 = array(1, 2, 3)',
+ 'default' => 'array(1, 2, 3)',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testArrayDefaultValues()
+
+
+ /**
+ * Verify having a T_STRING constant as a default value for the second parameter.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testConstantDefaultValueSecondParam()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var1',
+ 'content' => '$var1',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+ $expected[1] = [
+ 'name' => '$var2',
+ 'content' => '$var2 = M_PI',
+ 'default' => 'M_PI',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testConstantDefaultValueSecondParam()
+
+
+ /**
+ * Verify using ellipsis with a typehint.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testVariableLengthArgument()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$unit',
+ 'content' => '$unit',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+ $expected[1] = [
+ 'name' => '$intervals',
+ 'content' => 'DateInterval ...$intervals',
+ 'pass_by_reference' => false,
+ 'variable_length' => true,
+ 'type_hint' => 'DateInterval',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testVariableLengthArgument()
+
+
+ /**
+ * Verify "bitwise and" in default value !== pass-by-reference.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testBitwiseAndConstantExpressionDefaultValue()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$a',
+ 'content' => '$a = 10 & 20',
+ 'default' => '10 & 20',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testBitwiseAndConstantExpressionDefaultValue()
+
+
+ /**
+ * Verify a fully qualified class name being set as type declaration.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testFQCNTypeHint()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var',
+ 'content' => '\MyNS\SubCat\MyClass $var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '\MyNS\SubCat\MyClass',
+ 'nullable_type' => false,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testFQCNTypeHint()
+
+
+ /**
+ * Verify a fully qualified class name being set as type declaration interlaced
+ * with whitespace and comments.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters
+ *
+ * @return void
+ */
+ public function testFQCNTypeHintWithCommentsAndWhiteSpace()
+ {
+ $expected = [];
+ $expected[0] = [
+ 'name' => '$var',
+ 'content' => '?\MyNS /* comment */
+ \SubCat // phpcs:ignore Standard.Cat.Sniff -- for reasons.
+ \MyClass $var',
+ 'pass_by_reference' => false,
+ 'variable_length' => false,
+ 'type_hint' => '?\MyNS\SubCat\MyClass',
+ 'nullable_type' => true,
+ ];
+
+ $this->getParametersTestHelper('/* '.__FUNCTION__.' */', $expected);
+
+ }//end testFQCNTypeHintWithCommentsAndWhiteSpace()
+
+
+ /**
+ * Test helper.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected function output.
+ *
+ * @return void
+ */
+ private function getParametersTestHelper($testMarker, $expected)
+ {
+ $function = $this->getTargetToken($testMarker, [T_FUNCTION]);
+ $found = FunctionDeclarations::getParameters(self::$phpcsFile, $function);
+
+ foreach ($found as $key => $value) {
+ unset($found[$key]['token'], $found[$key]['type_hint_token']);
+ }
+
+ $this->assertSame($expected, $found);
+
+ }//end getParametersTestHelper()
+
+
+}//end class
diff --git a/tests/Core/File/GetMethodPropertiesTest.inc b/tests/Core/Util/Sniffs/FunctionDeclarations/GetPropertiesTest.inc
similarity index 91%
rename from tests/Core/File/GetMethodPropertiesTest.inc
rename to tests/Core/Util/Sniffs/FunctionDeclarations/GetPropertiesTest.inc
index ced6c13f01..015cc0079f 100644
--- a/tests/Core/File/GetMethodPropertiesTest.inc
+++ b/tests/Core/Util/Sniffs/FunctionDeclarations/GetPropertiesTest.inc
@@ -1,12 +1,16 @@
- * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
-namespace PHP_CodeSniffer\Tests\Core\File;
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\FunctionDeclarations;
-use PHP_CodeSniffer\Config;
-use PHP_CodeSniffer\Ruleset;
-use PHP_CodeSniffer\Files\DummyFile;
-use PHPUnit\Framework\TestCase;
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
-class GetMethodPropertiesTest extends TestCase
+class GetPropertiesTest extends AbstractMethodUnitTest
{
- /**
- * The PHP_CodeSniffer_File object containing parsed contents of the test case file.
- *
- * @var \PHP_CodeSniffer\Files\File
- */
- private $phpcsFile;
-
/**
- * Initialize & tokenize PHP_CodeSniffer_File with code from the test case file.
+ * Test receiving an expected exception when a non function token is passed.
*
- * Methods used for these tests can be found in a test case file in the same
- * directory and with the same name, using the .inc extension.
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_FUNCTION or T_CLOSURE
*
- * @return void
- */
- public function setUp()
- {
- $config = new Config();
- $config->standards = ['Generic'];
-
- $ruleset = new Ruleset($config);
-
- $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
- $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
- $this->phpcsFile->process();
-
- }//end setUp()
-
-
- /**
- * Clean up after finished test.
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
*
* @return void
*/
- public function tearDown()
+ public function testNotAFunctionException()
{
- unset($this->phpcsFile);
+ $interface = $this->getTargetToken('/* testNotAFunction */', T_INTERFACE);
+ $result = FunctionDeclarations::getProperties(self::$phpcsFile, $interface);
- }//end tearDown()
+ }//end testNotAFunctionException()
/**
* Test a basic function.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testBasicFunction()
@@ -77,18 +54,7 @@ public function testBasicFunction()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testBasicFunction */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 2));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testBasicFunction()
@@ -96,6 +62,8 @@ public function testBasicFunction()
/**
* Test a function with a return type.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testReturnFunction()
@@ -111,18 +79,7 @@ public function testReturnFunction()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testReturnFunction */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 2));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testReturnFunction()
@@ -130,6 +87,8 @@ public function testReturnFunction()
/**
* Test a closure used as a function argument.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testNestedClosure()
@@ -145,18 +104,7 @@ public function testNestedClosure()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testNestedClosure */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 1));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testNestedClosure()
@@ -164,6 +112,8 @@ public function testNestedClosure()
/**
* Test a basic method.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testBasicMethod()
@@ -179,18 +129,7 @@ public function testBasicMethod()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testBasicMethod */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 3));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testBasicMethod()
@@ -198,6 +137,8 @@ public function testBasicMethod()
/**
* Test a private static method.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testPrivateStaticMethod()
@@ -213,18 +154,7 @@ public function testPrivateStaticMethod()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testPrivateStaticMethod */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 7));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testPrivateStaticMethod()
@@ -232,6 +162,8 @@ public function testPrivateStaticMethod()
/**
* Test a basic final method.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testFinalMethod()
@@ -247,18 +179,7 @@ public function testFinalMethod()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testFinalMethod */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 7));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testFinalMethod()
@@ -266,6 +187,8 @@ public function testFinalMethod()
/**
* Test a protected method with a return type.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testProtectedReturnMethod()
@@ -281,18 +204,7 @@ public function testProtectedReturnMethod()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testProtectedReturnMethod */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 5));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testProtectedReturnMethod()
@@ -300,6 +212,8 @@ public function testProtectedReturnMethod()
/**
* Test a public method with a return type.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testPublicReturnMethod()
@@ -315,18 +229,7 @@ public function testPublicReturnMethod()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testPublicReturnMethod */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 5));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testPublicReturnMethod()
@@ -334,6 +237,8 @@ public function testPublicReturnMethod()
/**
* Test a public method with a nullable return type.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testNullableReturnMethod()
@@ -349,18 +254,7 @@ public function testNullableReturnMethod()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testNullableReturnMethod */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 5));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testNullableReturnMethod()
@@ -368,6 +262,8 @@ public function testNullableReturnMethod()
/**
* Test a public method with a nullable return type.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testMessyNullableReturnMethod()
@@ -383,18 +279,7 @@ public function testMessyNullableReturnMethod()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testMessyNullableReturnMethod */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 5));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testMessyNullableReturnMethod()
@@ -402,6 +287,8 @@ public function testMessyNullableReturnMethod()
/**
* Test a method with a namespaced return type.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testReturnNamespace()
@@ -417,18 +304,7 @@ public function testReturnNamespace()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testReturnNamespace */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 3));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testReturnNamespace()
@@ -436,6 +312,8 @@ public function testReturnNamespace()
/**
* Test a method with a messy namespaces return type.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testReturnMultilineNamespace()
@@ -451,18 +329,7 @@ public function testReturnMultilineNamespace()
'has_body' => true,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testReturnMultilineNamespace */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 3));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testReturnMultilineNamespace()
@@ -470,6 +337,8 @@ public function testReturnMultilineNamespace()
/**
* Test a basic abstract method.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testAbstractMethod()
@@ -485,18 +354,7 @@ public function testAbstractMethod()
'has_body' => false,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testAbstractMethod */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 5));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testAbstractMethod()
@@ -504,6 +362,8 @@ public function testAbstractMethod()
/**
* Test an abstract method with a return type.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testAbstractReturnMethod()
@@ -519,18 +379,7 @@ public function testAbstractReturnMethod()
'has_body' => false,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testAbstractReturnMethod */'
- );
-
- $found = $this->phpcsFile->getMethodProperties(($function + 7));
- unset($found['return_type_token']);
- $this->assertSame($expected, $found);
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
}//end testAbstractReturnMethod()
@@ -538,6 +387,8 @@ public function testAbstractReturnMethod()
/**
* Test a basic interface method.
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties
+ *
* @return void
*/
public function testInterfaceMethod()
@@ -553,20 +404,27 @@ public function testInterfaceMethod()
'has_body' => false,
];
- $start = ($this->phpcsFile->numTokens - 1);
- $function = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testInterfaceMethod */'
- );
+ $this->getPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected);
- $found = $this->phpcsFile->getMethodProperties(($function + 3));
+ }//end testInterfaceMethod()
+
+
+ /**
+ * Test helper.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected function output.
+ *
+ * @return void
+ */
+ private function getPropertiesTestHelper($testMarker, $expected)
+ {
+ $function = $this->getTargetToken($testMarker, [T_FUNCTION, T_CLOSURE]);
+ $found = FunctionDeclarations::getProperties(self::$phpcsFile, $function);
unset($found['return_type_token']);
$this->assertSame($expected, $found);
- }//end testInterfaceMethod()
+ }//end getPropertiesTestHelper()
}//end class
diff --git a/tests/Core/Util/Sniffs/FunctionDeclarations/IsMagicFunctionNameTest.php b/tests/Core/Util/Sniffs/FunctionDeclarations/IsMagicFunctionNameTest.php
new file mode 100644
index 0000000000..b75997c055
--- /dev/null
+++ b/tests/Core/Util/Sniffs/FunctionDeclarations/IsMagicFunctionNameTest.php
@@ -0,0 +1,90 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\FunctionDeclarations;
+
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
+use PHPUnit\Framework\TestCase;
+
+class IsMagicFunctionNameTest extends TestCase
+{
+
+
+ /**
+ * Test valid PHP magic function names.
+ *
+ * @param string $name The function name to test.
+ *
+ * @dataProvider dataIsMagicFunctionName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isMagicFunctionName
+ *
+ * @return void
+ */
+ public function testIsMagicFunctionName($name)
+ {
+ $this->assertTrue(FunctionDeclarations::isMagicFunctionName($name));
+
+ }//end testIsMagicFunctionName()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsMagicFunctionName()
+ *
+ * @return array
+ */
+ public function dataIsMagicFunctionName()
+ {
+ return [
+ 'lowercase' => ['__autoload'],
+ 'uppercase' => ['__AUTOLOAD'],
+ 'mixedcase' => ['__AutoLoad'],
+ ];
+
+ }//end dataIsMagicFunctionName()
+
+
+ /**
+ * Test non-magic function names.
+ *
+ * @param string $name The function name to test.
+ *
+ * @dataProvider dataIsMagicFunctionNameFalse
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isMagicFunctionName
+ *
+ * @return void
+ */
+ public function testIsMagicFunctionNameFalse($name)
+ {
+ $this->assertFalse(FunctionDeclarations::isMagicFunctionName($name));
+
+ }//end testIsMagicFunctionNameFalse()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsMagicFunctionNameFalse()
+ *
+ * @return array
+ */
+ public function dataIsMagicFunctionNameFalse()
+ {
+ return [
+ 'no_underscore' => ['noDoubleUnderscore'],
+ 'single_underscore' => ['_autoload'],
+ 'triple_underscore' => ['___autoload'],
+ 'not_magic_function_name' => ['__notAutoload'],
+ ];
+
+ }//end dataIsMagicFunctionNameFalse()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/FunctionDeclarations/IsMagicMethodNameTest.php b/tests/Core/Util/Sniffs/FunctionDeclarations/IsMagicMethodNameTest.php
new file mode 100644
index 0000000000..8095d753a0
--- /dev/null
+++ b/tests/Core/Util/Sniffs/FunctionDeclarations/IsMagicMethodNameTest.php
@@ -0,0 +1,154 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\FunctionDeclarations;
+
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
+use PHPUnit\Framework\TestCase;
+
+class IsMagicMethodNameTest extends TestCase
+{
+
+
+ /**
+ * Test valid PHP magic method names.
+ *
+ * @param string $name The function name to test.
+ *
+ * @dataProvider dataIsMagicMethodName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isMagicMethodName
+ *
+ * @return void
+ */
+ public function testIsMagicMethodName($name)
+ {
+ $this->assertTrue(FunctionDeclarations::isMagicMethodName($name));
+
+ }//end testIsMagicMethodName()
+
+
+ /**
+ * Test valid PHP magic method names.
+ *
+ * @param string $name The function name to test.
+ *
+ * @dataProvider dataIsMagicMethodName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isSpecialMethodName
+ *
+ * @return void
+ */
+ public function testIsSpecialMethodName($name)
+ {
+ $this->assertTrue(FunctionDeclarations::isSpecialMethodName($name));
+
+ }//end testIsSpecialMethodName()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsMagicMethodName()
+ *
+ * @return array
+ */
+ public function dataIsMagicMethodName()
+ {
+ return [
+ // Normal case.
+ ['__construct'],
+ ['__destruct'],
+ ['__call'],
+ ['__callStatic'],
+ ['__get'],
+ ['__set'],
+ ['__isset'],
+ ['__unset'],
+ ['__sleep'],
+ ['__wakeup'],
+ ['__toString'],
+ ['__set_state'],
+ ['__clone'],
+ ['__invoke'],
+ ['__debugInfo'],
+
+ // Uppercase et al.
+ ['__CONSTRUCT'],
+ ['__Destruct'],
+ ['__Call'],
+ ['__callstatic'],
+ ['__GET'],
+ ['__SeT'],
+ ['__isSet'],
+ ['__unSet'],
+ ['__SleeP'],
+ ['__wakeUp'],
+ ['__TOString'],
+ ['__Set_State'],
+ ['__CLONE'],
+ ['__Invoke'],
+ ['__Debuginfo'],
+ ];
+
+ }//end dataIsMagicMethodName()
+
+
+ /**
+ * Test non-magic method names.
+ *
+ * @param string $name The function name to test.
+ *
+ * @dataProvider dataIsMagicMethodNameFalse
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isMagicMethodName
+ *
+ * @return void
+ */
+ public function testIsMagicMethodNameFalse($name)
+ {
+ $this->assertFalse(FunctionDeclarations::isMagicMethodName($name));
+
+ }//end testIsMagicMethodNameFalse()
+
+
+ /**
+ * Test non-magic method names.
+ *
+ * @param string $name The function name to test.
+ *
+ * @dataProvider dataIsMagicMethodNameFalse
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isSpecialMethodName
+ *
+ * @return void
+ */
+ public function testIsSpecialMethodNameFalse($name)
+ {
+ $this->assertFalse(FunctionDeclarations::isSpecialMethodName($name));
+
+ }//end testIsSpecialMethodNameFalse()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsMagicMethodNameFalse()
+ *
+ * @return array
+ */
+ public function dataIsMagicMethodNameFalse()
+ {
+ return [
+ 'no_underscore' => ['construct'],
+ 'single_underscore' => ['_destruct'],
+ 'triple_underscore' => ['___call'],
+ 'not_magic_method_name' => ['__myFunction'],
+ ];
+
+ }//end dataIsMagicMethodNameFalse()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/FunctionDeclarations/IsPHPDoubleUnderscoreMethodNameTest.php b/tests/Core/Util/Sniffs/FunctionDeclarations/IsPHPDoubleUnderscoreMethodNameTest.php
new file mode 100644
index 0000000000..1c8e183612
--- /dev/null
+++ b/tests/Core/Util/Sniffs/FunctionDeclarations/IsPHPDoubleUnderscoreMethodNameTest.php
@@ -0,0 +1,148 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\FunctionDeclarations;
+
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
+use PHPUnit\Framework\TestCase;
+
+class IsPHPDoubleUnderscoreMethodNameTest extends TestCase
+{
+
+
+ /**
+ * Test valid PHP native double underscore method names.
+ *
+ * @param string $name The function name to test.
+ *
+ * @dataProvider dataIsPHPDoubleUnderscoreMethodName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isPHPDoubleUnderscoreMethodName
+ *
+ * @return void
+ */
+ public function testIsPHPDoubleUnderscoreMethodName($name)
+ {
+ $this->assertTrue(FunctionDeclarations::isPHPDoubleUnderscoreMethodName($name));
+
+ }//end testIsPHPDoubleUnderscoreMethodName()
+
+
+ /**
+ * Test valid PHP native double underscore method names.
+ *
+ * @param string $name The function name to test.
+ *
+ * @dataProvider dataIsPHPDoubleUnderscoreMethodName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isSpecialMethodName
+ *
+ * @return void
+ */
+ public function testIsSpecialMethodName($name)
+ {
+ $this->assertTrue(FunctionDeclarations::isSpecialMethodName($name));
+
+ }//end testIsSpecialMethodName()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsPHPDoubleUnderscoreMethodName()
+ *
+ * @return array
+ */
+ public function dataIsPHPDoubleUnderscoreMethodName()
+ {
+ return [
+ // Normal case.
+ ['__doRequest'],
+ ['__getCookies'],
+ ['__getFunctions'],
+ ['__getLastRequest'],
+ ['__getLastRequestHeaders'],
+ ['__getLastResponse'],
+ ['__getLastResponseHeaders'],
+ ['__getTypes'],
+ ['__setCookie'],
+ ['__setLocation'],
+ ['__setSoapHeaders'],
+ ['__soapCall'],
+
+ // Uppercase et al.
+ ['__DOREQUEST'],
+ ['__getcookies'],
+ ['__Getfunctions'],
+ ['__GETLASTREQUEST'],
+ ['__getlastrequestheaders'],
+ ['__GetlastResponse'],
+ ['__GETLASTRESPONSEHEADERS'],
+ ['__GetTypes'],
+ ['__SETCookie'],
+ ['__sETlOCATION'],
+ ['__SetSOAPHeaders'],
+ ['__SOAPCall'],
+ ];
+
+ }//end dataIsPHPDoubleUnderscoreMethodName()
+
+
+ /**
+ * Test function names which are not valid PHP native double underscore methods.
+ *
+ * @param string $name The function name to test.
+ *
+ * @dataProvider dataIsPHPDoubleUnderscoreMethodNameFalse
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isPHPDoubleUnderscoreMethodName
+ *
+ * @return void
+ */
+ public function testIsPHPDoubleUnderscoreMethodNameFalse($name)
+ {
+ $this->assertFalse(FunctionDeclarations::isPHPDoubleUnderscoreMethodName($name));
+
+ }//end testIsPHPDoubleUnderscoreMethodNameFalse()
+
+
+ /**
+ * Test function names which are not valid PHP native double underscore methods.
+ *
+ * @param string $name The function name to test.
+ *
+ * @dataProvider dataIsPHPDoubleUnderscoreMethodNameFalse
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isSpecialMethodName
+ *
+ * @return void
+ */
+ public function testIsSpecialMethodNameFalse($name)
+ {
+ $this->assertFalse(FunctionDeclarations::isSpecialMethodName($name));
+
+ }//end testIsSpecialMethodNameFalse()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsPHPDoubleUnderscoreMethodNameFalse()
+ *
+ * @return array
+ */
+ public function dataIsPHPDoubleUnderscoreMethodNameFalse()
+ {
+ return [
+ 'no_underscore' => ['getLastResponseHeaders'],
+ 'single_underscore' => ['_setLocation'],
+ 'triple_underscore' => ['___getCookies'],
+ 'not_magic_function_name' => ['__getFirstRequestHeader'],
+ ];
+
+ }//end dataIsPHPDoubleUnderscoreMethodNameFalse()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/FunctionDeclarations/SpecialFunctionsTest.inc b/tests/Core/Util/Sniffs/FunctionDeclarations/SpecialFunctionsTest.inc
new file mode 100644
index 0000000000..a52f0c5fdb
--- /dev/null
+++ b/tests/Core/Util/Sniffs/FunctionDeclarations/SpecialFunctionsTest.inc
@@ -0,0 +1,82 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\FunctionDeclarations;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations;
+
+class SpecialFunctionsTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test correctly detecting magic methods.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected return values for the various functions.
+ *
+ * @dataProvider dataItsAKindOfMagic
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isMagicMethod
+ *
+ * @return void
+ */
+ public function testIsMagicMethod($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_FUNCTION);
+ $result = FunctionDeclarations::isMagicMethod(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected['method'], $result);
+
+ }//end testIsMagicMethod()
+
+
+ /**
+ * Test correctly detecting PHP native double underscore methods.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected return values for the various functions.
+ *
+ * @dataProvider dataItsAKindOfMagic
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isPHPDoubleUnderscoreMethod
+ *
+ * @return void
+ */
+ public function testIsPHPDoubleUnderscoreMethod($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_FUNCTION);
+ $result = FunctionDeclarations::isPHPDoubleUnderscoreMethod(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected['double'], $result);
+
+ }//end testIsPHPDoubleUnderscoreMethod()
+
+
+ /**
+ * Test correctly detecting magic functions.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected return values for the various functions.
+ *
+ * @dataProvider dataItsAKindOfMagic
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isMagicFunction
+ *
+ * @return void
+ */
+ public function testIsMagicFunction($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_FUNCTION);
+ $result = FunctionDeclarations::isMagicFunction(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected['function'], $result);
+
+ }//end testIsMagicFunction()
+
+
+ /**
+ * Test correctly detecting magic methods and double underscore methods.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected return values for the various functions.
+ *
+ * @dataProvider dataItsAKindOfMagic
+ * @covers \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::isSpecialMethod
+ *
+ * @return void
+ */
+ public function testIsSpecialMethod($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_FUNCTION);
+ $result = FunctionDeclarations::isSpecialMethod(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected['special'], $result);
+
+ }//end testIsSpecialMethod()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsMagicMethod()
+ * @see testIsPHPDoubleUnderscoreMethod()
+ * @see testIsMagicFunction()
+ * @see testIsSpecialMethod()
+ *
+ * @return array
+ */
+ public function dataItsAKindOfMagic()
+ {
+ return [
+ [
+ '/* testMagicMethodInClass */',
+ [
+ 'method' => true,
+ 'double' => false,
+ 'function' => false,
+ 'special' => true,
+ ],
+ ],
+ [
+ '/* testMagicMethodInClassUppercase */',
+ [
+ 'method' => true,
+ 'double' => false,
+ 'function' => false,
+ 'special' => true,
+ ],
+ ],
+ [
+ '/* testMagicMethodInClassMixedCase */',
+ [
+ 'method' => true,
+ 'double' => false,
+ 'function' => false,
+ 'special' => true,
+ ],
+ ],
+ [
+ '/* testMagicFunctionInClassNotGlobal */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testMethodInClassNotMagicName */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testMagicMethodNotInClass */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testMagicFunction */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => true,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testMagicFunctionInConditionMixedCase */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => true,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testFunctionNotMagicName */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testMagicMethodInAnonClass */',
+ [
+ 'method' => true,
+ 'double' => false,
+ 'function' => false,
+ 'special' => true,
+ ],
+ ],
+ [
+ '/* testMagicMethodInAnonClassUppercase */',
+ [
+ 'method' => true,
+ 'double' => false,
+ 'function' => false,
+ 'special' => true,
+ ],
+ ],
+ [
+ '/* testMagicFunctionInAnonClassNotGlobal */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testMethodInAnonClassNotMagicName */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testDoubleUnderscoreMethodInClass */',
+ [
+ 'method' => false,
+ 'double' => true,
+ 'function' => false,
+ 'special' => true,
+ ],
+ ],
+ [
+ '/* testDoubleUnderscoreMethodInClassMixedcase */',
+ [
+ 'method' => false,
+ 'double' => true,
+ 'function' => false,
+ 'special' => true,
+ ],
+ ],
+ [
+ '/* testDoubleUnderscoreMethodNotInClass */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testMagicMethodInTrait */',
+ [
+ 'method' => true,
+ 'double' => false,
+ 'function' => false,
+ 'special' => true,
+ ],
+ ],
+ [
+ '/* testMagicFunctionInTraitNotGlobal */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testMethodInTraitNotMagicName */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testMagicMethodInInterface */',
+ [
+ 'method' => true,
+ 'double' => false,
+ 'function' => false,
+ 'special' => true,
+ ],
+ ],
+ [
+ '/* testMagicFunctionInInterfaceNotGlobal */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ [
+ '/* testMethodInInterfaceNotMagicName */',
+ [
+ 'method' => false,
+ 'double' => false,
+ 'function' => false,
+ 'special' => false,
+ ],
+ ],
+ ];
+
+ }//end dataItsAKindOfMagic()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Namespaces/DetermineNamespaceTest.inc b/tests/Core/Util/Sniffs/Namespaces/DetermineNamespaceTest.inc
new file mode 100644
index 0000000000..e12003615d
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Namespaces/DetermineNamespaceTest.inc
@@ -0,0 +1,119 @@
+
+ * @copyright 2017-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Namespaces;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Namespaces;
+
+class DetermineNamespaceTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test that false is returned when an invalid token is passed.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::findNamespacePtr
+ *
+ * @return void
+ */
+ public function testInvalidTokenPassed()
+ {
+ $this->assertFalse(Namespaces::findNamespacePtr(self::$phpcsFile, 100000));
+
+ }//end testInvalidTokenPassed()
+
+
+ /**
+ * Test finding the correct namespace token for an arbitrary token in a file.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected output for the functions.
+ *
+ * @dataProvider dataDetermineNamespace
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::findNamespacePtr
+ *
+ * @return void
+ */
+ public function testFindNamespacePtr($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_ECHO);
+
+ if ($expected['ptr'] !== false) {
+ $expected['ptr'] = $this->getTargetToken($expected['ptr'], T_NAMESPACE);
+ }
+
+ $result = Namespaces::findNamespacePtr(self::$phpcsFile, $stackPtr);
+
+ $this->assertSame($expected['ptr'], $result);
+
+ }//end testFindNamespacePtr()
+
+
+ /**
+ * Test retrieving the applicabe namespace name for an arbitrary token in a file.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected output for the functions.
+ *
+ * @dataProvider dataDetermineNamespace
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::determineNamespace
+ *
+ * @return void
+ */
+ public function testDetermineNamespace($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_ECHO);
+ $result = Namespaces::determineNamespace(self::$phpcsFile, $stackPtr);
+
+ $this->assertSame($expected['name'], $result);
+
+ }//end testDetermineNamespace()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testDetermineNamespace()
+ *
+ * @return array
+ */
+ public function dataDetermineNamespace()
+ {
+ return [
+ [
+ '/* testNoNamespace */',
+ [
+ 'ptr' => false,
+ 'name' => '',
+ ],
+ ],
+ [
+ '/* testNoNamespaceNested */',
+ [
+ 'ptr' => false,
+ 'name' => '',
+ ],
+ ],
+ [
+ '/* testNonScopedNamedNamespace1 */',
+ [
+ 'ptr' => '/* Non-scoped named namespace 1 */',
+ 'name' => 'Vendor\Package\Baz',
+ ],
+ ],
+ [
+ '/* testNonScopedNamedNamespace1Nested */',
+ [
+ 'ptr' => '/* Non-scoped named namespace 1 */',
+ 'name' => 'Vendor\Package\Baz',
+ ],
+ ],
+ [
+ '/* testGlobalNamespaceScoped */',
+ [
+ 'ptr' => '/* Scoped global namespace */',
+ 'name' => '',
+ ],
+ ],
+ [
+ '/* testGlobalNamespaceScopedNested */',
+ [
+ 'ptr' => '/* Scoped global namespace */',
+ 'name' => '',
+ ],
+ ],
+ [
+ '/* testNoNamespaceAfterScoped */',
+ [
+ 'ptr' => false,
+ 'name' => '',
+ ],
+ ],
+ [
+ '/* testNoNamespaceNestedAfterScoped */',
+ [
+ 'ptr' => false,
+ 'name' => '',
+ ],
+ ],
+ [
+ '/* testNamedNamespaceScoped */',
+ [
+ 'ptr' => '/* Scoped named namespace */',
+ 'name' => 'Vendor\Package\Foo',
+ ],
+ ],
+ [
+ '/* testNamedNamespaceScopedNested */',
+ [
+ 'ptr' => '/* Scoped named namespace */',
+ 'name' => 'Vendor\Package\Foo',
+ ],
+ ],
+ [
+ '/* testNonScopedGlobalNamespace */',
+ [
+ 'ptr' => '/* Non-scoped global namespace */',
+ 'name' => '',
+ ],
+ ],
+ [
+ '/* testNonScopedGlobalNamespaceNested */',
+ [
+ 'ptr' => '/* Non-scoped global namespace */',
+ 'name' => '',
+ ],
+ ],
+ [
+ '/* testNonScopedNamedNamespace2 */',
+ [
+ 'ptr' => '/* Non-scoped named namespace 2 */',
+ 'name' => 'Vendor\Package\Foz',
+ ],
+ ],
+ [
+ '/* testNonScopedNamedNamespace2Nested */',
+ [
+ 'ptr' => '/* Non-scoped named namespace 2 */',
+ 'name' => 'Vendor\Package\Foz',
+ ],
+ ],
+ ];
+
+ }//end dataDetermineNamespace()
+
+
+ /**
+ * Test returning an empty string if the namespace could not be determined (parse error).
+ *
+ * @dataProvider dataDetermineNamespace
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::findNamespacePtr
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::determineNamespace
+ *
+ * @return void
+ */
+ public function testFallbackToEmptyString()
+ {
+ $stackPtr = $this->getTargetToken('/* testParseError */', T_COMMENT, '/* comment */');
+
+ $expected = $this->getTargetToken('/* testParseError */', T_NAMESPACE);
+ $result = Namespaces::findNamespacePtr(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ $result = Namespaces::determineNamespace(self::$phpcsFile, $stackPtr, false);
+ $this->assertSame('', $result);
+
+ }//end testFallbackToEmptyString()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Namespaces/GetDeclaredNameTest.inc b/tests/Core/Util/Sniffs/Namespaces/GetDeclaredNameTest.inc
new file mode 100644
index 0000000000..b202d4dda6
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Namespaces/GetDeclaredNameTest.inc
@@ -0,0 +1,52 @@
+
+
+
+
+ * @copyright 2017-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Namespaces;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Namespaces;
+
+class GetDeclaredNameTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test that false is returned when an invalid token is passed.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::getDeclaredName
+ *
+ * @return void
+ */
+ public function testInvalidTokenPassed()
+ {
+ // Non-existent token.
+ $this->assertFalse(Namespaces::getDeclaredName(self::$phpcsFile, 100000));
+
+ // Non namespace token.
+ $this->assertFalse(Namespaces::getDeclaredName(self::$phpcsFile, 0));
+
+ }//end testInvalidTokenPassed()
+
+
+ /**
+ * Test retrieving the cleaned up namespace name based on a T_NAMESPACE token.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected output for the function.
+ *
+ * @dataProvider dataGetDeclaredName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::getDeclaredName
+ *
+ * @return void
+ */
+ public function testGetDeclaredNameClean($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_NAMESPACE);
+ $result = Namespaces::getDeclaredName(self::$phpcsFile, $stackPtr, true);
+
+ $this->assertSame($expected['clean'], $result);
+
+ }//end testGetDeclaredNameClean()
+
+
+ /**
+ * Test retrieving the "dirty" namespace name based on a T_NAMESPACE token.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected output for the function.
+ *
+ * @dataProvider dataGetDeclaredName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::getDeclaredName
+ *
+ * @return void
+ */
+ public function testGetDeclaredNameDirty($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_NAMESPACE);
+ $result = Namespaces::getDeclaredName(self::$phpcsFile, $stackPtr, false);
+
+ $this->assertSame($expected['dirty'], $result);
+
+ }//end testGetDeclaredNameDirty()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testGetDeclaredName()
+ *
+ * @return array
+ */
+ public function dataGetDeclaredName()
+ {
+ return [
+ [
+ '/* testNamespaceOperator */',
+ [
+ 'clean' => false,
+ 'dirty' => false,
+ ],
+ ],
+ [
+ '/* testGlobalNamespaceSemiColon */',
+ [
+ 'clean' => '',
+ 'dirty' => '',
+ ],
+ ],
+ [
+ '/* testGlobalNamespaceCurlies */',
+ [
+ 'clean' => '',
+ 'dirty' => '',
+ ],
+ ],
+ [
+ '/* testGlobalNamespaceCloseTag */',
+ [
+ 'clean' => '',
+ 'dirty' => '',
+ ],
+ ],
+ [
+ '/* testNamespaceSemiColon */',
+ [
+ 'clean' => 'Vendor',
+ 'dirty' => 'Vendor',
+ ],
+ ],
+ [
+ '/* testNamespaceCurlies */',
+ [
+ 'clean' => 'Vendor\Package\Sublevel\Deeperlevel\End',
+ 'dirty' => 'Vendor\Package\Sublevel\Deeperlevel\End',
+ ],
+ ],
+ [
+ '/* testNamespaceCurliesNoSpaceAtEnd */',
+ [
+ 'clean' => 'Vendor\Package\Sublevel\Deeperlevel\End',
+ 'dirty' => 'Vendor\Package\Sublevel\Deeperlevel\End',
+ ],
+ ],
+ [
+ '/* testNamespaceCloseTag */',
+ [
+ 'clean' => 'PHP_CodeSniffer\Exceptions\RuntimeException',
+ 'dirty' => 'PHP_CodeSniffer\Exceptions\RuntimeException',
+ ],
+ ],
+ [
+ '/* testNamespaceCloseTagNoSpaceAtEnd */',
+ [
+ 'clean' => 'PHP_CodeSniffer\Exceptions\RuntimeException',
+ 'dirty' => 'PHP_CodeSniffer\Exceptions\RuntimeException',
+ ],
+ ],
+ [
+ '/* testNamespaceLotsOfWhitespace */',
+ [
+ 'clean' => 'Vendor\Package\Sublevel\Deeperlevel\End',
+ 'dirty' => 'Vendor \
+ Package\
+ Sublevel \
+ Deeperlevel\
+ End',
+ ],
+ ],
+ [
+ '/* testNamespaceWithCommentsWhitespaceAndAnnotations */',
+ [
+ 'clean' => 'Vendor\Package\Sublevel\Deeperlevel\End',
+ 'dirty' => 'Vendor\/*comment*/
+ Package\Sublevel \ //phpcs:ignore Standard.Category.Sniff -- for reasons.
+ Deeperlevel\ // Another comment
+ End',
+ ],
+ ],
+ [
+ '/* testNamespaceParseError */',
+ [
+ 'clean' => 'Vendor\while\Package\protected\name\try\this',
+ 'dirty' => 'Vendor\while\Package\protected\name\try\this',
+ ],
+ ],
+ [
+ '/* testLiveCoding */',
+ [
+ 'clean' => false,
+ 'dirty' => false,
+ ],
+ ],
+ ];
+
+ }//end dataGetDeclaredName()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Namespaces/NamespaceTypeTest.inc b/tests/Core/Util/Sniffs/Namespaces/NamespaceTypeTest.inc
new file mode 100644
index 0000000000..1f54b0580d
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Namespaces/NamespaceTypeTest.inc
@@ -0,0 +1,26 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Namespaces;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Namespaces;
+
+class NamespaceTypeTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when passing a non-existent token pointer.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_NAMESPACE
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::getType
+ *
+ * @return void
+ */
+ public function testNonExistentToken()
+ {
+ $result = Namespaces::getType(self::$phpcsFile, 100000);
+
+ }//end testNonExistentToken()
+
+
+ /**
+ * Test receiving an expected exception when passing a non T_NAMESPACE token.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_NAMESPACE
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::getType
+ *
+ * @return void
+ */
+ public function testNonNamespaceToken()
+ {
+ $result = Namespaces::getType(self::$phpcsFile, 0);
+
+ }//end testNonNamespaceToken()
+
+
+ /**
+ * Test whether a T_NAMESPACE token is used as the keyword for a namespace declaration.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected output for the functions.
+ *
+ * @dataProvider dataNamespaceType
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::isDeclaration
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::getType
+ *
+ * @return void
+ */
+ public function testIsDeclaration($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_NAMESPACE);
+ $result = Namespaces::isDeclaration(self::$phpcsFile, $stackPtr);
+
+ $this->assertSame($expected['declaration'], $result);
+
+ }//end testIsDeclaration()
+
+
+ /**
+ * Test whether a T_NAMESPACE token is used as an operator.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected output for the functions.
+ *
+ * @dataProvider dataNamespaceType
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::isOperator
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Namespaces::getType
+ *
+ * @return void
+ */
+ public function testIsOperator($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_NAMESPACE);
+ $result = Namespaces::isOperator(self::$phpcsFile, $stackPtr);
+
+ $this->assertSame($expected['operator'], $result);
+
+ }//end testIsOperator()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsDeclaration()
+ * @see testIsOperator()
+ *
+ * @return array
+ */
+ public function dataNamespaceType()
+ {
+ return [
+ [
+ '/* testNamespaceDeclaration */',
+ [
+ 'declaration' => true,
+ 'operator' => false,
+ ],
+ ],
+ [
+ '/* testNamespaceDeclarationWithComment */',
+ [
+ 'declaration' => true,
+ 'operator' => false,
+ ],
+ ],
+ [
+ '/* testNamespaceDeclarationScoped */',
+ [
+ 'declaration' => true,
+ 'operator' => false,
+ ],
+ ],
+ [
+ '/* testNamespaceOperator */',
+ [
+ 'declaration' => false,
+ 'operator' => true,
+ ],
+ ],
+ [
+ '/* testNamespaceOperatorWithAnnotation */',
+ [
+ 'declaration' => false,
+ 'operator' => true,
+ ],
+ ],
+ [
+ '/* testParseError */',
+ [
+ 'declaration' => false,
+ 'operator' => false,
+ ],
+ ],
+ [
+ '/* testLiveCoding */',
+ [
+ 'declaration' => false,
+ 'operator' => false,
+ ],
+ ],
+ ];
+
+ }//end dataNamespaceType()
+
+
+}//end class
diff --git a/tests/Core/File/FindExtendedClassNameTest.inc b/tests/Core/Util/Sniffs/ObjectDeclarations/FindExtendedClassNameTest.inc
similarity index 73%
rename from tests/Core/File/FindExtendedClassNameTest.inc
rename to tests/Core/Util/Sniffs/ObjectDeclarations/FindExtendedClassNameTest.inc
index aead06cd9d..7f2a3b1925 100644
--- a/tests/Core/File/FindExtendedClassNameTest.inc
+++ b/tests/Core/Util/Sniffs/ObjectDeclarations/FindExtendedClassNameTest.inc
@@ -1,6 +1,6 @@
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\ObjectDeclarations;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations;
+
+class FindExtendedClassNameTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test retrieving the name of the class being extended by another class
+ * (or interface).
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param string|false $expected Expected function output.
+ *
+ * @dataProvider dataExtendedClass
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findExtendedClassName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findExtendedImplemented
+ *
+ * @return void
+ */
+ public function testFindExtendedClassName($testMarker, $expected)
+ {
+ $OOToken = $this->getTargetToken($testMarker, [T_CLASS, T_ANON_CLASS, T_INTERFACE]);
+ $result = ObjectDeclarations::findExtendedClassName(self::$phpcsFile, $OOToken);
+ $this->assertSame($expected, $result);
+
+ }//end testFindExtendedClassName()
+
+
+ /**
+ * Data provider for the FindExtendedClassName test.
+ *
+ * @see testFindExtendedClassName()
+ *
+ * @return array
+ */
+ public function dataExtendedClass()
+ {
+ return [
+ [
+ '/* testExtendedClass */',
+ 'testFECNClass',
+ ],
+ [
+ '/* testNamespacedClass */',
+ '\PHP_CodeSniffer\Tests\Core\File\testFECNClass',
+ ],
+ [
+ '/* testNonExtendedClass */',
+ false,
+ ],
+ [
+ '/* testInterface */',
+ false,
+ ],
+ [
+ '/* testInterfaceThatExtendsInterface */',
+ 'testFECNInterface',
+ ],
+ [
+ '/* testInterfaceThatExtendsFQCNInterface */',
+ '\PHP_CodeSniffer\Tests\Core\File\testFECNInterface',
+ ],
+ [
+ '/* testNestedExtendedClass */',
+ false,
+ ],
+ [
+ '/* testNestedExtendedAnonClass */',
+ 'testFECNAnonClass',
+ ],
+ [
+ '/* testClassThatExtendsAndImplements */',
+ 'testFECNClass',
+ ],
+ [
+ '/* testClassThatImplementsAndExtends */',
+ 'testFECNClass',
+ ],
+ [
+ '/* testExtendedClassWithComments */',
+ '\PHP_CodeSniffer\Tests\testFECNClass',
+ ],
+ [
+ '/* testParseError */',
+ false,
+ ],
+ ];
+
+ }//end dataExtendedClass()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/ObjectDeclarations/FindExtendedInterfaceNamesTest.inc b/tests/Core/Util/Sniffs/ObjectDeclarations/FindExtendedInterfaceNamesTest.inc
new file mode 100644
index 0000000000..b6d81669d1
--- /dev/null
+++ b/tests/Core/Util/Sniffs/ObjectDeclarations/FindExtendedInterfaceNamesTest.inc
@@ -0,0 +1,26 @@
+
+ * @copyright 2018-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\ObjectDeclarations;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations;
+
+class FindExtendedInterfaceNamesTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test retrieving the names of the interfaces being extended by another interface.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array|false $expected Expected function output.
+ *
+ * @dataProvider dataExtendedInterface
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findExtendedInterfaceNames
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findExtendedImplemented
+ *
+ * @return void
+ */
+ public function testFindExtendedInterfaceNames($testMarker, $expected)
+ {
+ $interface = $this->getTargetToken($testMarker, [T_INTERFACE]);
+ $result = ObjectDeclarations::findExtendedInterfaceNames(self::$phpcsFile, $interface);
+ $this->assertSame($expected, $result);
+
+ }//end testFindExtendedInterfaceNames()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testFindExtendedInterfaceNames()
+ *
+ * @return array
+ */
+ public function dataExtendedInterface()
+ {
+ return [
+ [
+ '/* testInterface */',
+ false,
+ ],
+ [
+ '/* testExtendedInterface */',
+ ['testFEINInterface'],
+ ],
+ [
+ '/* testMultiExtendedInterface */',
+ [
+ 'testFEINInterface',
+ 'testFEINInterface2',
+ ],
+ ],
+ [
+ '/* testNamespacedInterface */',
+ ['\PHP_CodeSniffer\Tests\Core\File\testFEINInterface'],
+ ],
+ [
+ '/* testMultiNamespacedInterface */',
+ [
+ '\PHP_CodeSniffer\Tests\Core\File\testFEINInterface',
+ '\PHP_CodeSniffer\Tests\Core\File\testFEINInterface2',
+ ],
+ ],
+ [
+ '/* testMultiExtendedInterfaceWithComment */',
+ [
+ 'testFEINInterface',
+ '\PHP_CodeSniffer\Tests\Core\File\testFEINInterface2',
+ '\testFEINInterface3',
+ ],
+ ],
+ ];
+
+ }//end dataExtendedInterface()
+
+
+}//end class
diff --git a/tests/Core/File/FindImplementedInterfaceNamesTest.inc b/tests/Core/Util/Sniffs/ObjectDeclarations/FindImplementedInterfaceNamesTest.inc
similarity index 70%
rename from tests/Core/File/FindImplementedInterfaceNamesTest.inc
rename to tests/Core/Util/Sniffs/ObjectDeclarations/FindImplementedInterfaceNamesTest.inc
index 3885b27e1d..edb5889244 100644
--- a/tests/Core/File/FindImplementedInterfaceNamesTest.inc
+++ b/tests/Core/Util/Sniffs/ObjectDeclarations/FindImplementedInterfaceNamesTest.inc
@@ -1,6 +1,6 @@
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\ObjectDeclarations;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations;
+
+class FindImplementedInterfaceNamesTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test getting a `false` result when a non-existent token is passed.
+ *
+ * @dataProvider dataImplementedInterface
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findExtendedImplemented
+ *
+ * @return void
+ */
+ public function testNonExistentToken()
+ {
+ $result = ObjectDeclarations::findImplementedInterfaceNames(self::$phpcsFile, 100000);
+ $this->assertFalse($result);
+
+ }//end testNonExistentToken()
+
+
+ /**
+ * Test retrieving the name(s) of the interfaces being implemented by a class.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array|false $expected Expected function output.
+ *
+ * @dataProvider dataImplementedInterface
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findImplementedInterfaceNames
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findExtendedImplemented
+ *
+ * @return void
+ */
+ public function testFindImplementedInterfaceNames($testMarker, $expected)
+ {
+ $OOToken = $this->getTargetToken($testMarker, [T_CLASS, T_ANON_CLASS, T_INTERFACE]);
+ $result = ObjectDeclarations::findImplementedInterfaceNames(self::$phpcsFile, $OOToken);
+ $this->assertSame($expected, $result);
+
+ }//end testFindImplementedInterfaceNames()
+
+
+ /**
+ * Data provider for the FindImplementedInterfaceNames test.
+ *
+ * @see testFindImplementedInterfaceNames()
+ *
+ * @return array
+ */
+ public function dataImplementedInterface()
+ {
+ return [
+ [
+ '/* testImplementedClass */',
+ ['testFIINInterface'],
+ ],
+ [
+ '/* testMultiImplementedClass */',
+ [
+ 'testFIINInterface',
+ 'testFIINInterface2',
+ ],
+ ],
+ [
+ '/* testNamespacedClass */',
+ ['\PHP_CodeSniffer\Tests\Core\File\testFIINInterface'],
+ ],
+ [
+ '/* testNonImplementedClass */',
+ false,
+ ],
+ [
+ '/* testInterface */',
+ false,
+ ],
+ [
+ '/* testClassThatExtendsAndImplements */',
+ [
+ 'InterfaceA',
+ '\NameSpaced\Cat\InterfaceB',
+ ],
+ ],
+ [
+ '/* testClassThatImplementsAndExtends */',
+ [
+ '\InterfaceA',
+ 'InterfaceB',
+ ],
+ ],
+ [
+ '/* testImplementedClassWithComments */',
+ ['\PHP_CodeSniffer\Tests\Core\File\testFIINInterface'],
+ ],
+ ];
+
+ }//end dataImplementedInterface()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/ObjectDeclarations/GetClassPropertiesTest.inc b/tests/Core/Util/Sniffs/ObjectDeclarations/GetClassPropertiesTest.inc
new file mode 100644
index 0000000000..f39c3d327c
--- /dev/null
+++ b/tests/Core/Util/Sniffs/ObjectDeclarations/GetClassPropertiesTest.inc
@@ -0,0 +1,45 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\ObjectDeclarations;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations;
+
+class GetClassPropertiesTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a non class token is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_CLASS
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::getClassProperties
+ *
+ * @return void
+ */
+ public function testNotAClassException()
+ {
+ $interface = $this->getTargetToken('/* testNotAClass */', T_INTERFACE);
+ $result = ObjectDeclarations::getClassProperties(self::$phpcsFile, $interface);
+
+ }//end testNotAClassException()
+
+
+ /**
+ * Test the retrieving the properties for a class declaration.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected Expected function output.
+ *
+ * @dataProvider dataGetClassProperties
+ * @covers \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::getClassProperties
+ *
+ * @return void
+ */
+ public function testGetClassProperties($testMarker, $expected)
+ {
+ $class = $this->getTargetToken($testMarker, T_CLASS);
+ $result = ObjectDeclarations::getClassProperties(self::$phpcsFile, $class);
+ $this->assertSame($expected, $result);
+
+ }//end testGetClassProperties()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testGetClassProperties()
+ *
+ * @return array
+ */
+ public function dataGetClassProperties()
+ {
+ return [
+ [
+ '/* testClassWithoutProperties */',
+ [
+ 'is_abstract' => false,
+ 'is_final' => false,
+ ],
+ ],
+ [
+ '/* testAbstractClass */',
+ [
+ 'is_abstract' => true,
+ 'is_final' => false,
+ ],
+ ],
+ [
+ '/* testFinalClass */',
+ [
+ 'is_abstract' => false,
+ 'is_final' => true,
+ ],
+ ],
+ [
+ '/* testWithCommentsAndNewLines */',
+ [
+ 'is_abstract' => true,
+ 'is_final' => false,
+ ],
+ ],
+ [
+ '/* testWithDocblockWithoutProperties */',
+ [
+ 'is_abstract' => false,
+ 'is_final' => false,
+ ],
+ ],
+ [
+ '/* testWithPHPCSAnnotation */',
+ [
+ 'is_abstract' => false,
+ 'is_final' => true,
+ ],
+ ],
+ [
+ '/* testWithDocblockWithWeirdlyPlacedProperty */',
+ [
+ 'is_abstract' => false,
+ 'is_final' => true,
+ ],
+ ],
+ ];
+
+ }//end dataGetClassProperties()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Orthography/FirstCharTest.php b/tests/Core/Util/Sniffs/Orthography/FirstCharTest.php
new file mode 100644
index 0000000000..cd0c117c54
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Orthography/FirstCharTest.php
@@ -0,0 +1,277 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Orthography;
+
+use PHPUnit\Framework\TestCase;
+use PHP_CodeSniffer\Util\Sniffs\Orthography;
+
+class FirstCharTest extends TestCase
+{
+
+
+ /**
+ * Test correctly detecting whether the first character of a phrase is capitalized.
+ *
+ * @param string $input The input string.
+ * @param array $expected The expected function output for the respective functions.
+ *
+ * @dataProvider dataFirstChar
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Orthography::isFirstCharCapitalized
+ *
+ * @return void
+ */
+ public function testIsFirstCharCapitalized($input, $expected)
+ {
+ $this->assertSame($expected['capitalized'], Orthography::isFirstCharCapitalized($input));
+
+ }//end testIsFirstCharCapitalized()
+
+
+ /**
+ * Test correctly detecting whether the first character of a phrase is lowercase.
+ *
+ * @param string $input The input string.
+ * @param array $expected The expected function output for the respective functions.
+ *
+ * @dataProvider dataFirstChar
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Orthography::isFirstCharLowercase
+ *
+ * @return void
+ */
+ public function testIsFirstCharLowercase($input, $expected)
+ {
+ $this->assertSame($expected['lowercase'], Orthography::isFirstCharLowercase($input));
+
+ }//end testIsFirstCharLowercase()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsFirstCharCapitalized()
+ * @see testIsFirstCharLowercase()
+ *
+ * @return array
+ */
+ public function dataFirstChar()
+ {
+ $data = [
+ // Quotes should be stripped before passing the string.
+ 'double-quoted' => [
+ '"This is a test"',
+ [
+ 'capitalized' => false,
+ 'lowercase' => false,
+ ],
+ ],
+ 'single-quoted' => [
+ "'This is a test'",
+ [
+ 'capitalized' => false,
+ 'lowercase' => false,
+ ],
+ ],
+
+ // Not starting with a letter.
+ 'start-numeric' => [
+ '12 Foostreet',
+ [
+ 'capitalized' => false,
+ 'lowercase' => false,
+ ],
+ ],
+ 'start-bracket' => [
+ '[Optional]',
+ [
+ 'capitalized' => false,
+ 'lowercase' => false,
+ ],
+ ],
+
+ // Leading whitespace.
+ 'english-lowercase-leading-whitespace' => [
+ '
+ this is a test',
+ [
+ 'capitalized' => false,
+ 'lowercase' => true,
+ ],
+ ],
+ 'english-propercase-leading-whitespace' => [
+ '
+ This is a test',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+
+ // First character lowercase.
+ 'english-lowercase' => [
+ 'this is a test',
+ [
+ 'capitalized' => false,
+ 'lowercase' => true,
+ ],
+ ],
+ 'russian-lowercase' => [
+ 'предназначена для',
+ [
+ 'capitalized' => false,
+ 'lowercase' => true,
+ ],
+ ],
+ 'latvian-lowercase' => [
+ 'ir domāta',
+ [
+ 'capitalized' => false,
+ 'lowercase' => true,
+ ],
+ ],
+ 'armenian-lowercase' => [
+ 'սա թեստ է',
+ [
+ 'capitalized' => false,
+ 'lowercase' => true,
+ ],
+ ],
+ 'mandinka-lowercase' => [
+ 'ŋanniya',
+ [
+ 'capitalized' => false,
+ 'lowercase' => true,
+ ],
+ ],
+ 'greek-lowercase' => [
+ 'δημιουργήθηκε από',
+ [
+ 'capitalized' => false,
+ 'lowercase' => true,
+ ],
+ ],
+
+ // First character capitalized.
+ 'english-propercase' => [
+ 'This is a test',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+ 'russian-propercase' => [
+ 'Дата написания этой книги',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+ 'latvian-propercase' => [
+ 'Šodienas datums',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+ 'armenian-propercase' => [
+ 'Սա թեստ է',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+ 'igbo-propercase' => [
+ 'Ụbọchị tata bụ',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+ 'greek-propercase' => [
+ 'Η σημερινή ημερομηνία',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+
+ // No concept of "case", but starting with a letter.
+ 'arabic' => [
+ 'هذا اختبار',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+ 'pashto' => [
+ 'دا یوه آزموینه ده',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+ 'hebrew' => [
+ 'זה מבחן',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+ 'chinese-traditional' => [
+ '這是一個測試',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+ 'urdu' => [
+ 'کا منشاء برائے',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ],
+ ];
+
+ /*
+ * PCRE2 - included in PHP 7.3+ - recognizes Georgian as a language with
+ * upper and lowercase letters as defined in Unicode v 11.0 / June 2018.
+ * While, as far as I can tell, this is linguistically incorrect - the upper
+ * and lowercase letters are from different alphabets used to write Georgian -,
+ * the unit test should allow for the reality as implemented in ICU/PCRE2/PHP.
+ *
+ * @link https://en.wikipedia.org/wiki/Georgian_scripts#Unicode
+ * @link https://unicode.org/charts/PDF/U10A0.pdf
+ */
+
+ if (PCRE_VERSION >= 10) {
+ $data['georgian'] = [
+ 'ეს ტესტია',
+ [
+ 'capitalized' => false,
+ 'lowercase' => true,
+ ],
+ ];
+ } else {
+ $data['georgian'] = [
+ 'ეს ტესტია',
+ [
+ 'capitalized' => true,
+ 'lowercase' => false,
+ ],
+ ];
+ }
+
+ return $data;
+
+ }//end dataFirstChar()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Orthography/IsLastCharPunctuationTest.php b/tests/Core/Util/Sniffs/Orthography/IsLastCharPunctuationTest.php
new file mode 100644
index 0000000000..4af1295ca6
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Orthography/IsLastCharPunctuationTest.php
@@ -0,0 +1,127 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Orthography;
+
+use PHPUnit\Framework\TestCase;
+use PHP_CodeSniffer\Util\Sniffs\Orthography;
+
+class IsLastCharPunctuationTest extends TestCase
+{
+
+
+ /**
+ * Test correctly detecting sentence end punctuation.
+ *
+ * @param string $input The input string.
+ * @param bool $expected The expected function output.
+ * @param string $allowedChars Optional. Custom punctuation character set.
+ *
+ * @dataProvider dataIsLastCharPunctuation
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Orthography::isLastCharPunctuation
+ *
+ * @return void
+ */
+ public function testIsLastCharPunctuation($input, $expected, $allowedChars=null)
+ {
+ if (isset($allowedChars) === true) {
+ $result = Orthography::isLastCharPunctuation($input, $allowedChars);
+ } else {
+ $result = Orthography::isLastCharPunctuation($input);
+ }
+
+ $this->assertSame($expected, $result);
+
+ }//end testIsLastCharPunctuation()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsLastCharPunctuation()
+ *
+ * @return array
+ */
+ public function dataIsLastCharPunctuation()
+ {
+ return [
+ // Quotes should be stripped before passing the string.
+ 'double-quoted' => [
+ '"This is a test."',
+ false,
+ ],
+ 'single-quoted' => [
+ "'This is a test?'",
+ false,
+ ],
+
+ // Invalid end char.
+ 'no-punctuation' => [
+ 'This is a test',
+ false,
+ ],
+ 'invalid-punctuation' => [
+ 'This is a test;',
+ false,
+ ],
+ 'invalid-punctuationtrailing-whitespace' => [
+ 'This is a test; ',
+ false,
+ ],
+
+ // Valid end char, default charset.
+ 'valid' => [
+ 'This is a test.',
+ true,
+ ],
+ 'valid-trailing-whitespace' => [
+ 'This is a test.
+',
+ true,
+ ],
+
+ // Invalid end char, custom charset.
+ 'invalid-custom' => [
+ 'This is a test.',
+ false,
+ '!?,;#',
+ ],
+
+ // Valid end char, custom charset.
+ 'valid-custom-1' => [
+ 'This is a test;',
+ true,
+ '!?,;#',
+ ],
+ 'valid-custom-2' => [
+ 'This is a test!',
+ true,
+ '!?,;#',
+ ],
+ 'valid-custom-3' => [
+ 'Is this is a test?',
+ true,
+ '!?,;#',
+ ],
+ 'valid-custom-4' => [
+ 'This is a test,',
+ true,
+ '!?,;#',
+ ],
+ 'valid-custom-5' => [
+ 'This is a test#',
+ true,
+ '!?,;#',
+ ],
+ ];
+
+ }//end dataIsLastCharPunctuation()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Parentheses/ParenthesesTest.inc b/tests/Core/Util/Sniffs/Parentheses/ParenthesesTest.inc
new file mode 100644
index 0000000000..788446a4d5
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Parentheses/ParenthesesTest.inc
@@ -0,0 +1,40 @@
+ 0 && ((function($p) {/* do something */})($result) === true)) {}
+
+/* testAnonClass */
+$anonClass = new class(
+ new class implements Countable {
+ function test($param) {
+ do {
+ try {
+ } catch( Exception $e ) {
+ }
+ } while($a === true);
+ }
+ }
+) extends DateTime {
+};
+
+// Intentional parse error. This has to be the last test in the file.
+/* testParseError */
+declare(ticks=1
diff --git a/tests/Core/Util/Sniffs/Parentheses/ParenthesesTest.php b/tests/Core/Util/Sniffs/Parentheses/ParenthesesTest.php
new file mode 100644
index 0000000000..b0c8cf7ba4
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Parentheses/ParenthesesTest.php
@@ -0,0 +1,975 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Parentheses;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Parentheses;
+use PHP_CodeSniffer\Util\Tokens;
+
+class ParenthesesTest extends AbstractMethodUnitTest
+{
+
+ /**
+ * List of all the test markers with their target token info in the test case file.
+ *
+ * @var array
+ */
+ public static $testTargets = [
+ 'testIfWithArray-$a' => [
+ 'marker' => '/* testIfWithArray */',
+ 'code' => T_VARIABLE,
+ 'content' => '$a',
+ ],
+ 'testIfWithArray-array' => [
+ 'marker' => '/* testIfWithArray */',
+ 'code' => T_ARRAY,
+ ],
+ 'testIfWithArray-$c' => [
+ 'marker' => '/* testIfWithArray */',
+ 'code' => T_VARIABLE,
+ 'content' => '$c',
+ ],
+ 'testElseIfWithClosure-$a' => [
+ 'marker' => '/* testElseIfWithClosure */',
+ 'code' => T_VARIABLE,
+ 'content' => '$a',
+ ],
+ 'testElseIfWithClosure-closure' => [
+ 'marker' => '/* testElseIfWithClosure */',
+ 'code' => T_CLOSURE,
+ ],
+ 'testElseIfWithClosure-$array' => [
+ 'marker' => '/* testElseIfWithClosure */',
+ 'code' => T_VARIABLE,
+ 'content' => '$array',
+ ],
+ 'testForeach-45' => [
+ 'marker' => '/* testForeach */',
+ 'code' => T_LNUMBER,
+ 'content' => '45',
+ ],
+ 'testForeach-$a' => [
+ 'marker' => '/* testForeach */',
+ 'code' => T_VARIABLE,
+ 'content' => '$a',
+ ],
+ 'testForeach-$c' => [
+ 'marker' => '/* testForeach */',
+ 'code' => T_VARIABLE,
+ 'content' => '$c',
+ ],
+ 'testFunctionwithArray-$param' => [
+ 'marker' => '/* testFunctionwithArray */',
+ 'code' => T_VARIABLE,
+ 'content' => '$param',
+ ],
+ 'testFunctionwithArray-2' => [
+ 'marker' => '/* testFunctionwithArray */',
+ 'code' => T_LNUMBER,
+ 'content' => '2',
+ ],
+ 'testForWithTernary-$a' => [
+ 'marker' => '/* testForWithTernary */',
+ 'code' => T_VARIABLE,
+ 'content' => '$a',
+ ],
+ 'testForWithTernary-$c' => [
+ 'marker' => '/* testForWithTernary */',
+ 'code' => T_VARIABLE,
+ 'content' => '$c',
+ ],
+ 'testForWithTernary-$array' => [
+ 'marker' => '/* testForWithTernary */',
+ 'code' => T_VARIABLE,
+ 'content' => '$array',
+ ],
+ 'testWhileWithClosure-$a' => [
+ 'marker' => '/* testWhileWithClosure */',
+ 'code' => T_VARIABLE,
+ 'content' => '$a',
+ ],
+ 'testWhileWithClosure-$p' => [
+ 'marker' => '/* testWhileWithClosure */',
+ 'code' => T_VARIABLE,
+ 'content' => '$p',
+ ],
+ 'testWhileWithClosure-$result' => [
+ 'marker' => '/* testWhileWithClosure */',
+ 'code' => T_VARIABLE,
+ 'content' => '$result',
+ ],
+ 'testAnonClass-implements' => [
+ 'marker' => '/* testAnonClass */',
+ 'code' => T_IMPLEMENTS,
+ ],
+ 'testAnonClass-$param' => [
+ 'marker' => '/* testAnonClass */',
+ 'code' => T_VARIABLE,
+ 'content' => '$param',
+ ],
+ 'testAnonClass-$e' => [
+ 'marker' => '/* testAnonClass */',
+ 'code' => T_VARIABLE,
+ 'content' => '$e',
+ ],
+ 'testAnonClass-$a' => [
+ 'marker' => '/* testAnonClass */',
+ 'code' => T_VARIABLE,
+ 'content' => '$a',
+ ],
+ 'testParseError-1' => [
+ 'marker' => '/* testParseError */',
+ 'code' => T_LNUMBER,
+ 'content' => '1',
+ ],
+ ];
+
+ /**
+ * Cache for the test token stack pointers.
+ *
+ * @var array =>
+ */
+ private $testTokens = [];
+
+
+ /**
+ * Base array with all the tokens which are assigned parenthesis owners.
+ *
+ * This array is merged with expected result arrays for various unit tests
+ * to make sure all possible parentheses owners are tested.
+ *
+ * This array should be kept in sync with the Tokens::$parenthesisOpeners array.
+ * This array isn't auto-generated based on the array in Tokens as for these
+ * tests we want to have access to the token constant names, not just their values.
+ *
+ * @var array =>
+ */
+ private $ownerDefaults = [
+ 'T_ARRAY' => false,
+ 'T_FUNCTION' => false,
+ 'T_CLOSURE' => false,
+ 'T_WHILE' => false,
+ 'T_FOR' => false,
+ 'T_FOREACH' => false,
+ 'T_SWITCH' => false,
+ 'T_IF' => false,
+ 'T_ELSEIF' => false,
+ 'T_CATCH' => false,
+ 'T_DECLARE' => false,
+ ];
+
+
+ /**
+ * Set up the token position caches for the tests.
+ *
+ * Retrieves the test tokens and marker token stack pointer positions
+ * only once and caches them as they won't change between the tests anyway.
+ *
+ * @return void
+ */
+ protected function setUp()
+ {
+ if (empty($this->testTokens) === true) {
+ foreach (self::$testTargets as $testName => $targetDetails) {
+ if (isset($targetDetails['content']) === true) {
+ $this->testTokens[$testName] = $this->getTargetToken(
+ $targetDetails['marker'],
+ $targetDetails['code'],
+ $targetDetails['content']
+ );
+ } else {
+ $this->testTokens[$testName] = $this->getTargetToken(
+ $targetDetails['marker'],
+ $targetDetails['code']
+ );
+ }
+ }
+ }
+
+ }//end setUp()
+
+
+ /**
+ * Test passing a non-existent token pointer.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::isOwnerIn
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::hasOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testNonExistentToken()
+ {
+ $result = Parentheses::getOwner(self::$phpcsFile, 100000);
+ $this->assertFalse($result);
+
+ $result = Parentheses::isOwnerIn(self::$phpcsFile, 100000, T_FUNCTION);
+ $this->assertFalse($result);
+
+ $result = Parentheses::hasOwner(self::$phpcsFile, 100000, T_FOR);
+ $this->assertFalse($result);
+
+ }//end testNonExistentToken()
+
+
+ /**
+ * Test passing a token which isn't in parentheses.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::isOwnerIn
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::hasOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getFirstOpener
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getFirstCloser
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getFirstOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getLastOpener
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getLastCloser
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getLastOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::lastOwnerIn
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testNoParentheses()
+ {
+ $stackPtr = $this->getTargetToken('/* testNoParentheses */', T_VARIABLE);
+
+ $result = Parentheses::getOwner(self::$phpcsFile, $stackPtr);
+ $this->assertFalse($result);
+
+ $result = Parentheses::isOwnerIn(self::$phpcsFile, $stackPtr, T_IF);
+ $this->assertFalse($result);
+
+ $result = Parentheses::hasOwner(self::$phpcsFile, $stackPtr, T_FOREACH);
+ $this->assertFalse($result);
+
+ $result = Parentheses::getFirstOpener(self::$phpcsFile, $stackPtr);
+ $this->assertFalse($result);
+
+ $result = Parentheses::getFirstCloser(self::$phpcsFile, $stackPtr);
+ $this->assertFalse($result);
+
+ $result = Parentheses::getFirstOwner(self::$phpcsFile, $stackPtr);
+ $this->assertFalse($result);
+
+ $result = Parentheses::getLastOpener(self::$phpcsFile, $stackPtr);
+ $this->assertFalse($result);
+
+ $result = Parentheses::getLastCloser(self::$phpcsFile, $stackPtr);
+ $this->assertFalse($result);
+
+ $result = Parentheses::getLastOwner(self::$phpcsFile, $stackPtr);
+ $this->assertFalse($result);
+
+ $result = Parentheses::lastOwnerIn(self::$phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE]);
+ $this->assertFalse($result);
+
+ }//end testNoParentheses()
+
+
+ /**
+ * Test passing a non-parenthesis token to methods which expect to receive an open/close parenthesis.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::isOwnerIn
+ *
+ * @return void
+ */
+ public function testPassingNonParenthesisTokenToMethodsWhichExpectParenthesis()
+ {
+ $stackPtr = $this->testTokens['testIfWithArray-$a'];
+
+ $result = Parentheses::getOwner(self::$phpcsFile, $stackPtr);
+ $this->assertFalse($result);
+
+ $result = Parentheses::isOwnerIn(self::$phpcsFile, $stackPtr, T_IF);
+ $this->assertFalse($result);
+
+ }//end testPassingNonParenthesisTokenToMethodsWhichExpectParenthesis()
+
+
+ /**
+ * Test passing a parenthesis token to methods which expect to receive an open/close parenthesis.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::isOwnerIn
+ *
+ * @return void
+ */
+ public function testPassingParenthesisTokenToMethodsWhichExpectParenthesis()
+ {
+ $stackPtr = ($this->testTokens['testIfWithArray-$c'] - 1);
+
+ $result = Parentheses::getOwner(self::$phpcsFile, $stackPtr);
+ $this->assertSame(($stackPtr - 1), $result);
+
+ $result = Parentheses::isOwnerIn(self::$phpcsFile, $stackPtr, T_IF);
+ $this->assertFalse($result);
+
+ $result = Parentheses::isOwnerIn(self::$phpcsFile, $stackPtr, T_ARRAY);
+ $this->assertTrue($result);
+
+ }//end testPassingParenthesisTokenToMethodsWhichExpectParenthesis()
+
+
+ /**
+ * Test correctly retrieving the first parenthesis opener for an arbitrary token.
+ *
+ * @param string $testName The name of this test as set in the cached $testTokens array.
+ * @param array $expectedResults Expected function output for the various functions.
+ *
+ * @dataProvider dataWalkParentheses
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getFirstOpener
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testGetFirstOpener($testName, $expectedResults)
+ {
+ $stackPtr = $this->testTokens[$testName];
+
+ $result = Parentheses::getFirstOpener(self::$phpcsFile, $stackPtr);
+ $expected = $expectedResults['firstOpener'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion without owners failed');
+
+ $result = Parentheses::getFirstOpener(self::$phpcsFile, $stackPtr, Tokens::$scopeOpeners);
+ $expected = $expectedResults['firstScopeOwnerOpener'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion with $validOwners failed');
+
+ }//end testGetFirstOpener()
+
+
+ /**
+ * Test correctly retrieving the first parenthesis closer for an arbitrary token.
+ *
+ * @param string $testName The name of this test as set in the cached $testTokens array.
+ * @param array $expectedResults Expected function output for the various functions.
+ *
+ * @dataProvider dataWalkParentheses
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getFirstCloser
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testGetFirstCloser($testName, $expectedResults)
+ {
+ $stackPtr = $this->testTokens[$testName];
+
+ $result = Parentheses::getFirstCloser(self::$phpcsFile, $stackPtr);
+ $expected = $expectedResults['firstCloser'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion without owners failed');
+
+ $result = Parentheses::getFirstCloser(self::$phpcsFile, $stackPtr, Tokens::$scopeOpeners);
+ $expected = $expectedResults['firstScopeOwnerCloser'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion with $validOwners failed');
+
+ }//end testGetFirstCloser()
+
+
+ /**
+ * Test correctly retrieving the first parenthesis owner for an arbitrary token.
+ *
+ * @param string $testName The name of this test as set in the cached $testTokens array.
+ * @param array $expectedResults Expected function output for the various functions.
+ *
+ * @dataProvider dataWalkParentheses
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getFirstOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testGetFirstOwner($testName, $expectedResults)
+ {
+ $stackPtr = $this->testTokens[$testName];
+
+ $result = Parentheses::getFirstOwner(self::$phpcsFile, $stackPtr);
+ $expected = $expectedResults['firstOwner'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion without owners failed');
+
+ $result = Parentheses::getFirstOwner(self::$phpcsFile, $stackPtr, Tokens::$scopeOpeners);
+ $expected = $expectedResults['firstScopeOwnerOwner'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion with $validOwners failed');
+
+ }//end testGetFirstOwner()
+
+
+ /**
+ * Test correctly retrieving the last parenthesis opener for an arbitrary token.
+ *
+ * @param string $testName The name of this test as set in the cached $testTokens array.
+ * @param array $expectedResults Expected function output for the various functions.
+ *
+ * @dataProvider dataWalkParentheses
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getLastOpener
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testGetLastOpener($testName, $expectedResults)
+ {
+ $stackPtr = $this->testTokens[$testName];
+
+ $result = Parentheses::getLastOpener(self::$phpcsFile, $stackPtr);
+ $expected = $expectedResults['lastOpener'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion without owners failed');
+
+ $result = Parentheses::getLastOpener(self::$phpcsFile, $stackPtr, [T_ARRAY]);
+ $expected = $expectedResults['lastArrayOpener'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion with $validOwners failed');
+
+ }//end testGetLastOpener()
+
+
+ /**
+ * Test correctly retrieving the last parenthesis closer for an arbitrary token.
+ *
+ * @param string $testName The name of this test as set in the cached $testTokens array.
+ * @param array $expectedResults Expected function output for the various functions.
+ *
+ * @dataProvider dataWalkParentheses
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getLastCloser
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testGetLastCloser($testName, $expectedResults)
+ {
+ $stackPtr = $this->testTokens[$testName];
+
+ $result = Parentheses::getLastCloser(self::$phpcsFile, $stackPtr);
+ $expected = $expectedResults['lastCloser'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion without owners failed');
+
+ $result = Parentheses::getLastCloser(self::$phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE]);
+ $expected = $expectedResults['lastFunctionCloser'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion with $validOwners failed');
+
+ }//end testGetLastCloser()
+
+
+ /**
+ * Test correctly retrieving the last parenthesis owner for an arbitrary token.
+ *
+ * @param string $testName The name of this test as set in the cached $testTokens array.
+ * @param array $expectedResults Expected function output for the various functions.
+ *
+ * @dataProvider dataWalkParentheses
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getLastOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testGetLastOwner($testName, $expectedResults)
+ {
+ $stackPtr = $this->testTokens[$testName];
+
+ $result = Parentheses::getLastOwner(self::$phpcsFile, $stackPtr);
+ $expected = $expectedResults['lastOwner'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion without owners failed');
+
+ $result = Parentheses::getLastOwner(self::$phpcsFile, $stackPtr, [T_IF, T_ELSEIF, T_ELSE]);
+ $expected = $expectedResults['lastIfElseOwner'];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $this->assertSame($expected, $result, 'Assertion with $validOwners failed');
+
+ }//end testGetLastOwner()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testGetFirstOpener()
+ * @see testGetFirstCloser(()
+ * @see testGetFirstOwner()
+ * @see testGetFirstOwnerTypes()
+ * @see testGetLastOpener()
+ * @see testGetLastCloser()
+ * @see testGetLastOwner()
+ * @see testGetLastOwnerTypes()
+ *
+ * @return array
+ */
+ public function dataWalkParentheses()
+ {
+ $data = [
+ 'testIfWithArray-$a' => [
+ 'testIfWithArray-$a',
+ [
+ 'firstOpener' => -2,
+ 'firstCloser' => 19,
+ 'firstOwner' => -4,
+ 'firstScopeOwnerOpener' => -2,
+ 'firstScopeOwnerCloser' => 19,
+ 'firstScopeOwnerOwner' => -4,
+ 'lastOpener' => -1,
+ 'lastCloser' => 5,
+ 'lastOwner' => false,
+ 'lastArrayOpener' => false,
+ 'lastFunctionCloser' => false,
+ 'lastIfElseOwner' => -4,
+ ],
+ ],
+ 'testIfWithArray-array' => [
+ 'testIfWithArray-array',
+ [
+ 'firstOpener' => -13,
+ 'firstCloser' => 8,
+ 'firstOwner' => -15,
+ 'firstScopeOwnerOpener' => -13,
+ 'firstScopeOwnerCloser' => 8,
+ 'firstScopeOwnerOwner' => -15,
+ 'lastOpener' => -1,
+ 'lastCloser' => 7,
+ 'lastOwner' => false,
+ 'lastArrayOpener' => false,
+ 'lastFunctionCloser' => false,
+ 'lastIfElseOwner' => -15,
+ ],
+ ],
+ 'testIfWithArray-$c' => [
+ 'testIfWithArray-$c',
+ [
+ 'firstOpener' => -15,
+ 'firstCloser' => 6,
+ 'firstOwner' => -17,
+ 'firstScopeOwnerOpener' => -15,
+ 'firstScopeOwnerCloser' => 6,
+ 'firstScopeOwnerOwner' => -17,
+ 'lastOpener' => -1,
+ 'lastCloser' => 4,
+ 'lastOwner' => -2,
+ 'lastArrayOpener' => -1,
+ 'lastFunctionCloser' => false,
+ 'lastIfElseOwner' => -17,
+ ],
+ ],
+ 'testWhileWithClosure-$a' => [
+ 'testWhileWithClosure-$a',
+ [
+ 'firstOpener' => -9,
+ 'firstCloser' => 30,
+ 'firstOwner' => -11,
+ 'firstScopeOwnerOpener' => -9,
+ 'firstScopeOwnerCloser' => 30,
+ 'firstScopeOwnerOwner' => -11,
+ 'lastOpener' => -2,
+ 'lastCloser' => 2,
+ 'lastOwner' => false,
+ 'lastArrayOpener' => false,
+ 'lastFunctionCloser' => false,
+ 'lastIfElseOwner' => false,
+ ],
+ ],
+ 'testWhileWithClosure-$p' => [
+ 'testWhileWithClosure-$p',
+ [
+ 'firstOpener' => -24,
+ 'firstCloser' => 15,
+ 'firstOwner' => -26,
+ 'firstScopeOwnerOpener' => -24,
+ 'firstScopeOwnerCloser' => 15,
+ 'firstScopeOwnerOwner' => -26,
+ 'lastOpener' => -1,
+ 'lastCloser' => 1,
+ 'lastOwner' => -2,
+ 'lastArrayOpener' => false,
+ 'lastFunctionCloser' => 1,
+ 'lastIfElseOwner' => false,
+ ],
+ ],
+ 'testWhileWithClosure-$result' => [
+ 'testWhileWithClosure-$result',
+ [
+ 'firstOpener' => -2,
+ 'firstCloser' => 37,
+ 'firstOwner' => -4,
+ 'firstScopeOwnerOpener' => -2,
+ 'firstScopeOwnerCloser' => 37,
+ 'firstScopeOwnerOwner' => -4,
+ 'lastOpener' => -1,
+ 'lastCloser' => 10,
+ 'lastOwner' => false,
+ 'lastArrayOpener' => false,
+ 'lastFunctionCloser' => false,
+ 'lastIfElseOwner' => false,
+ ],
+ ],
+ 'testParseError-1' => [
+ 'testParseError-1',
+ [
+ 'firstOpener' => false,
+ 'firstCloser' => false,
+ 'firstOwner' => false,
+ 'firstScopeOwnerOpener' => false,
+ 'firstScopeOwnerCloser' => false,
+ 'firstScopeOwnerOwner' => false,
+ 'lastOpener' => false,
+ 'lastCloser' => false,
+ 'lastOwner' => false,
+ 'lastArrayOpener' => false,
+ 'lastFunctionCloser' => false,
+ 'lastIfElseOwner' => false,
+ ],
+ ],
+ ];
+
+ return $data;
+
+ }//end dataWalkParentheses()
+
+
+ /**
+ * Test correctly determining whether a token has an owner of a certain type.
+ *
+ * @param string $testName The name of this test as set in the cached $testTokens array.
+ * @param array $expectedResults Array with the owner token type to search for as key
+ * and the expected result as a value.
+ *
+ * @dataProvider dataHasOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::hasOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testHasOwner($testName, $expectedResults)
+ {
+ $stackPtr = $this->testTokens[$testName];
+
+ // Add expected results for all owner types not listed in the data provider.
+ $expectedResults += $this->ownerDefaults;
+
+ foreach ($expectedResults as $ownerType => $expected) {
+ $result = Parentheses::hasOwner(self::$phpcsFile, $stackPtr, constant($ownerType));
+ $this->assertSame(
+ $expected,
+ $result,
+ "Assertion failed for test marker '{$testName}' with owner {$ownerType}"
+ );
+ }
+
+ }//end testHasOwner()
+
+
+ /**
+ * Data Provider.
+ *
+ * Only list the "true" owners in the $results array.
+ * All other potential owners will automatically also be tested
+ * and will expect "false" as a result.
+ *
+ * @see testHasOwner()
+ *
+ * @return array
+ */
+ public function dataHasOwner()
+ {
+ return [
+ 'testIfWithArray-$a' => [
+ 'testIfWithArray-$a',
+ ['T_IF' => true],
+ ],
+
+ 'testIfWithArray-array' => [
+ 'testIfWithArray-array',
+ ['T_IF' => true],
+ ],
+ 'testIfWithArray-$c' => [
+ 'testIfWithArray-$c',
+ [
+ 'T_ARRAY' => true,
+ 'T_IF' => true,
+ ],
+ ],
+ 'testElseIfWithClosure-$a' => [
+ 'testElseIfWithClosure-$a',
+ [
+ 'T_CLOSURE' => true,
+ 'T_ELSEIF' => true,
+ ],
+ ],
+ 'testElseIfWithClosure-closure' => [
+ 'testElseIfWithClosure-closure',
+ ['T_ELSEIF' => true],
+ ],
+ 'testElseIfWithClosure-$array' => [
+ 'testElseIfWithClosure-$array',
+ ['T_ELSEIF' => true],
+ ],
+ 'testForeach-45' => [
+ 'testForeach-45',
+ [
+ 'T_ARRAY' => true,
+ 'T_FOREACH' => true,
+ ],
+ ],
+ 'testForeach-$a' => [
+ 'testForeach-$a',
+ ['T_FOREACH' => true],
+ ],
+ 'testForeach-$c' => [
+ 'testForeach-$c',
+ ['T_FOREACH' => true],
+ ],
+ 'testFunctionwithArray-$param' => [
+ 'testFunctionwithArray-$param',
+ ['T_FUNCTION' => true],
+ ],
+ 'testFunctionwithArray-2' => [
+ 'testFunctionwithArray-2',
+ [
+ 'T_ARRAY' => true,
+ 'T_FUNCTION' => true,
+ ],
+ ],
+ 'testForWithTernary-$a' => [
+ 'testForWithTernary-$a',
+ ['T_FOR' => true],
+ ],
+ 'testForWithTernary-$c' => [
+ 'testForWithTernary-$c',
+ ['T_FOR' => true],
+ ],
+ 'testForWithTernary-$array' => [
+ 'testForWithTernary-$array',
+ ['T_FOR' => true],
+ ],
+ 'testWhileWithClosure-$a' => [
+ 'testWhileWithClosure-$a',
+ ['T_WHILE' => true],
+ ],
+ 'testWhileWithClosure-$p' => [
+ 'testWhileWithClosure-$p',
+ [
+ 'T_CLOSURE' => true,
+ 'T_WHILE' => true,
+ ],
+ ],
+ 'testWhileWithClosure-$result' => [
+ 'testWhileWithClosure-$result',
+ ['T_WHILE' => true],
+ ],
+ 'testAnonClass-implements' => [
+ 'testAnonClass-implements',
+ [],
+ ],
+ 'testAnonClass-$param' => [
+ 'testAnonClass-$param',
+ ['T_FUNCTION' => true],
+ ],
+ 'testAnonClass-$e' => [
+ 'testAnonClass-$e',
+ ['T_CATCH' => true],
+ ],
+ 'testAnonClass-$a' => [
+ 'testAnonClass-$a',
+ ['T_WHILE' => true],
+ ],
+ 'testParseError-1' => [
+ 'testParseError-1',
+ [],
+ ],
+
+ ];
+
+ }//end dataHasOwner()
+
+
+ /**
+ * Test correctly determining whether a token is nested in parentheses with an owner
+ * of a certain type, with multiple allowed possibilities.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::hasOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testHasOwnerMultipleTypes()
+ {
+ $stackPtr = $this->testTokens['testElseIfWithClosure-$array'];
+
+ $result = Parentheses::hasOwner(self::$phpcsFile, $stackPtr, [T_FUNCTION, T_CLOSURE]);
+ $this->assertFalse(
+ $result,
+ 'Failed asserting that $array in "testElseIfWithClosure" does not have a "function" nor a "closure" owner'
+ );
+
+ $result = Parentheses::hasOwner(self::$phpcsFile, $stackPtr, [T_IF, T_ELSEIF, T_ELSE]);
+ $this->assertTrue(
+ $result,
+ 'Failed asserting that $array in "testElseIfWithClosure" has an "if", "elseif" or "else" owner'
+ );
+
+ $stackPtr = $this->testTokens['testForWithTernary-$array'];
+
+ $result = Parentheses::hasOwner(self::$phpcsFile, $stackPtr, [T_ARRAY, T_LIST]);
+ $this->assertFalse(
+ $result,
+ 'Failed asserting that $array in "testForWithTernary" does not have an anonymous class nor a closure condition'
+ );
+
+ $result = Parentheses::hasOwner(self::$phpcsFile, $stackPtr, Tokens::$scopeOpeners);
+ $this->assertTrue(
+ $result,
+ 'Failed asserting that $array in "testForWithTernary" has an owner which is also a scope opener'
+ );
+
+ }//end testHasOwnerMultipleTypes()
+
+
+ /**
+ * Test correctly determining whether the last set of parenthesis around an arbitrary token
+ * has an owner of a certain type.
+ *
+ * @param string $testName The name of this test as set in the cached $testTokens array.
+ * @param array $validOwners Valid owners to test against.
+ * @param int|false $expected Expected function output
+ *
+ * @dataProvider datalastOwnerIn
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::lastOwnerIn
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getOwner
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::getLastOpener
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Parentheses::nestedParensWalker
+ *
+ * @return void
+ */
+ public function testLastOwnerIn($testName, $validOwners, $expected)
+ {
+ $stackPtr = $this->testTokens[$testName];
+ if ($expected !== false) {
+ $expected += $stackPtr;
+ }
+
+ $result = Parentheses::lastOwnerIn(self::$phpcsFile, $stackPtr, $validOwners);
+ $this->assertSame($expected, $result);
+
+ }//end testLastOwnerIn()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testlastOwnerIn()
+ *
+ * @return array
+ */
+ public function dataLastOwnerIn()
+ {
+ return [
+ 'testElseIfWithClosure-$a' => [
+ 'testElseIfWithClosure-$a',
+ [T_FUNCTION],
+ -3,
+ ],
+ 'testElseIfWithClosure-$a' => [
+ 'testElseIfWithClosure-$a',
+ [T_ARRAY],
+ false,
+ ],
+ 'testForeach-45' => [
+ 'testForeach-45',
+ [T_ARRAY],
+ -2,
+ ],
+ 'testForeach-45' => [
+ 'testForeach-45',
+ [
+ T_FOREACH,
+ T_FOR,
+ ],
+ false,
+ ],
+ 'testForeach-$a' => [
+ 'testForeach-$a',
+ [
+ T_FOREACH,
+ T_FOR,
+ ],
+ false,
+ ],
+ 'testFunctionwithArray-$param' => [
+ 'testFunctionwithArray-$param',
+ [
+ T_FUNCTION,
+ T_CLOSURE,
+ ],
+ -4,
+ ],
+ 'testFunctionwithArray-$param' => [
+ 'testFunctionwithArray-$param',
+ [
+ T_IF,
+ T_ELSEIF,
+ T_ELSE,
+ ],
+ false,
+ ],
+ 'testAnonClass-$e' => [
+ 'testAnonClass-$e',
+ [T_FUNCTION],
+ false,
+ ],
+ 'testAnonClass-$e' => [
+ 'testAnonClass-$e',
+ [T_CATCH],
+ -5,
+ ],
+ ];
+
+ }//end dataLastOwnerIn()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/PassedParameters/GetDoubleArrowPositionTest.inc b/tests/Core/Util/Sniffs/PassedParameters/GetDoubleArrowPositionTest.inc
new file mode 100644
index 0000000000..8ced21d9e8
--- /dev/null
+++ b/tests/Core/Util/Sniffs/PassedParameters/GetDoubleArrowPositionTest.inc
@@ -0,0 +1,57 @@
+ 'arrow numeric index',
+
+ /* testArrowStringIndex */
+ 'foo' => 'arrow string index',
+
+ /* testArrowMultiTokenIndex */
+ 'concat' . 'index' => 'arrow multi token index',
+
+ /* testNoArrowValueShortArray */
+ [
+ 'value only' => 'arrow belongs to value',
+ ],
+
+ /* testNoArrowValueLongArray */
+ array(
+ 'value only' => 'arrow belongs to value',
+ ),
+
+ /* testNoArrowValueNestedArrays */
+ array(
+ [
+ array(
+ ['key' => 'arrow belongs to nested array'],
+ ),
+ ],
+ ),
+
+ /* testNoArrowValueClosure */
+ function() {
+ echo 'closure as value arrow belongs to value';
+ return array( $a => $b );
+ },
+
+ /* testArrowValueShortArray */
+ 'index and value short array' => [
+ 'index and value' => '',
+ ],
+
+ /* testArrowValueLongArray */
+ 'index and value long array' => array(
+ 'index and value' => '',
+ ),
+
+ /* testArrowValueClosure */
+ 'index and value closure' => function() {
+ echo 'closure as value arrow belongs to value';
+ return array( $a => $b );
+ },
+];
diff --git a/tests/Core/Util/Sniffs/PassedParameters/GetDoubleArrowPositionTest.php b/tests/Core/Util/Sniffs/PassedParameters/GetDoubleArrowPositionTest.php
new file mode 100644
index 0000000000..cc47e528c8
--- /dev/null
+++ b/tests/Core/Util/Sniffs/PassedParameters/GetDoubleArrowPositionTest.php
@@ -0,0 +1,193 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\PassedParameters;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\PassedParameters;
+
+class GetDoubleArrowPositionTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Cache for the parsed parameters array.
+ *
+ * @var array =>
+ */
+ private $parameters = [];
+
+
+ /**
+ * Set up the parsed parameters cache for the tests.
+ *
+ * Retrieves the parsed parameters array only once and caches
+ * it as it won't change between the tests anyway.
+ *
+ * @return void
+ */
+ protected function setUp()
+ {
+ if (empty($this->parameters) === true) {
+ $target = $this->getTargetToken('/* testGetDoubleArrowPosition */', [T_OPEN_SHORT_ARRAY]);
+ $this->parameters = PassedParameters::getParameters(self::$phpcsFile, $target);
+ }
+
+ }//end setUp()
+
+
+ /**
+ * Test receiving an expected exception when an invalid start position is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage Invalid start and/or end position passed to getDoubleArrowPosition(). Received: $start -10, $end 10
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getDoubleArrowPosition
+ *
+ * @return void
+ */
+ public function testInvalidStartPositionException()
+ {
+ $result = PassedParameters::getDoubleArrowPosition(self::$phpcsFile, -10, 10);
+
+ }//end testInvalidStartPositionException()
+
+
+ /**
+ * Test receiving an expected exception when an invalid end position is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage Invalid start and/or end position passed to getDoubleArrowPosition(). Received: $start 0, $end 100000
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getDoubleArrowPosition
+ *
+ * @return void
+ */
+ public function testInvalidEndPositionException()
+ {
+ $result = PassedParameters::getDoubleArrowPosition(self::$phpcsFile, 0, 100000);
+
+ }//end testInvalidEndPositionException()
+
+
+ /**
+ * Test receiving an expected exception when the start position is after the end position.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage Invalid start and/or end position passed to getDoubleArrowPosition(). Received: $start 10, $end 5
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getDoubleArrowPosition
+ *
+ * @return void
+ */
+ public function testInvalidStartEndPositionException()
+ {
+ $result = PassedParameters::getDoubleArrowPosition(self::$phpcsFile, 10, 5);
+
+ }//end testInvalidStartEndPositionException()
+
+
+ /**
+ * Test retrieving the position of the double arrow for an array parameter.
+ *
+ * @param string $testMarker The comment which is part of the target array item in the test file.
+ * @param array $expected The expected function call result.
+ *
+ * @dataProvider dataGetDoubleArrowPosition
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getDoubleArrowPosition
+ *
+ * @return void
+ */
+ public function testGetDoubleArrowPosition($testMarker, $expected)
+ {
+ foreach ($this->parameters as $index => $values) {
+ if (strpos($values['raw'], $testMarker) !== false) {
+ $start = $values['start'];
+ $end = $values['end'];
+ break;
+ }
+ }
+
+ if (isset($start, $end) === false) {
+ $this->markTestIncomplete('Test case not found for '.$testMarker);
+ }
+
+ // Expected double arrow positions are set as offsets
+ // in relation to the start of the array item.
+ // Change these to exact positions.
+ if ($expected !== false) {
+ $expected = ($start + $expected);
+ }
+
+ $result = PassedParameters::getDoubleArrowPosition(self::$phpcsFile, $start, $end);
+ $this->assertSame($expected, $result);
+
+ }//end testGetDoubleArrowPosition()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testGetDoubleArrowPosition()
+ *
+ * @return array
+ */
+ public function dataGetDoubleArrowPosition()
+ {
+ return [
+ [
+ '/* testValueNoArrow */',
+ false,
+ ],
+ [
+ '/* testArrowNumericIndex */',
+ 8,
+ ],
+ [
+ '/* testArrowStringIndex */',
+ 8,
+ ],
+ [
+ '/* testArrowMultiTokenIndex */',
+ 12,
+ ],
+ [
+ '/* testNoArrowValueShortArray */',
+ false,
+ ],
+ [
+ '/* testNoArrowValueLongArray */',
+ false,
+ ],
+ [
+ '/* testNoArrowValueNestedArrays */',
+ false,
+ ],
+ [
+ '/* testNoArrowValueClosure */',
+ false,
+ ],
+ [
+ '/* testArrowValueShortArray */',
+ 8,
+ ],
+ [
+ '/* testArrowValueLongArray */',
+ 8,
+ ],
+ [
+ '/* testArrowValueClosure */',
+ 8,
+ ],
+ ];
+
+ }//end dataGetDoubleArrowPosition()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/PassedParameters/GetParameterCountTest.inc b/tests/Core/Util/Sniffs/PassedParameters/GetParameterCountTest.inc
new file mode 100644
index 0000000000..781e2c30d0
--- /dev/null
+++ b/tests/Core/Util/Sniffs/PassedParameters/GetParameterCountTest.inc
@@ -0,0 +1,199 @@
+ 'b',]);
+
+/* testFunctionCall27 */
+json_encode(['a' => $a,]);
+
+/* testFunctionCall28 */
+json_encode(['a' => $a,] + (isset($b) ? ['b' => $b,] : []));
+
+/* testFunctionCall29 */
+json_encode(['a' => $a,] + (isset($b) ? ['b' => $b, 'c' => $c,] : []));
+
+/* testFunctionCall30 */
+json_encode(['a' => $a, 'b' => $b] + (isset($c) ? ['c' => $c, 'd' => $d] : []));
+
+/* testFunctionCall31 */
+json_encode(['a' => $a, 'b' => $b] + (isset($c) ? ['c' => $c, 'd' => $d,] : []));
+
+/* testFunctionCall32 */
+json_encode(['a' => $a, 'b' => $b] + (isset($c) ? ['c' => $c, 'd' => $d, $c => 'c'] : []));
+
+/* testFunctionCall33 */
+json_encode(['a' => $a,] + (isset($b) ? ['b' => $b,] : []) + ['c' => $c, 'd' => $d,]);
+
+/* testFunctionCall34 */
+json_encode(['a' => 'b', 'c' => 'd',]);
+
+/* testFunctionCall35 */
+json_encode(['a' => ['b',],]);
+
+/* testFunctionCall36 */
+json_encode(['a' => ['b' => 'c',],]);
+
+/* testFunctionCall37 */
+json_encode(['a' => ['b' => 'c',], 'd' => ['e' => 'f',],]);
+
+/* testFunctionCall38 */
+json_encode(['a' => $a, 'b' => $b,]);
+
+/* testFunctionCall39 */
+json_encode(['a' => $a,] + ['b' => $b,]);
+
+/* testFunctionCall40 */
+json_encode(['a' => $a] + ['b' => $b, 'c' => $c,]);
+
+/* testFunctionCall41 */
+json_encode(['a' => $a, 'b' => $b] + ['c' => $c, 'd' => $d]);
+
+/* testFunctionCall42 */
+json_encode(['a' => $a, 'b' => $b] + ['c' => $c, 'd' => $d,]);
+
+/* testFunctionCall43 */
+json_encode(['a' => $a, 'b' => $b] + ['c' => $c, 'd' => $d, $c => 'c']);
+
+/* testFunctionCall44 */
+json_encode(['a' => $a, 'b' => $b,] + ['c' => $c]);
+
+/* testFunctionCall45 */
+json_encode(['a' => $a, 'b' => $b,] + ['c' => $c,]);
+
+/* testFunctionCall46 */
+json_encode(['a' => $a, 'b' => $b, 'c' => $c]);
+
+/* testFunctionCall47 */
+json_encode(['a' => $a, 'b' => $b, 'c' => $c,] + ['c' => $c, 'd' => $d,]);
+
+/* testLongArray1 */
+$foo = array( 1, 2, 3, 4, 5, 6, true );
+
+/* testLongArray2 */
+$foo = array(str_replace("../", "/", trim($value))); // 1
+
+/* testLongArray3 */
+$foo = array($stHour, 0, 0, $arrStDt[0], $arrStDt[1], $arrStDt[2]); // 6
+
+/* testLongArray4 */
+$foo = array(0, 0, date('s'), date('m'), date('d'), date('Y')); // 6
+
+/* testLongArray5 */
+$foo = array(some_call(5, 1), another(1), why(5, 1, 2), 4, 5, 6); // 6
+
+/* testLongArray6 */
+$foo = array('a' => $a, 'b' => $b, 'c' => $c);
+
+/* testLongArray7 */
+$foo = array('a' => $a, 'b' => $b, 'c' => (isset($c) ? $c : null));
+
+/* testLongArray8 */
+$foo = array(0 => $a, 2 => $b, 6 => (isset($c) ? $c : null));
+
+/* testShortArray1 */
+$bar = [ 1, 2, 3, 4, 5, 6, true ];
+
+/* testShortArray2 */
+$bar = [str_replace("../", "/", trim($value))]; // 1
+
+/* testShortArray3 */
+$bar = [$stHour, 0, 0, $arrStDt[0], $arrStDt[1], $arrStDt[2]]; // 6
+
+/* testShortArray4 */
+$bar = [0, 0, date('s'), date('m'), date('d'), date('Y')]; // 6
+
+/* testShortArray5 */
+$bar = [some_call(5, 1), another(1), why(5, 1, 2), 4, 5, 6]; // 6
+
+/* testShortArray6 */
+$bar = ['a' => $a, 'b' => $b, 'c' => $c];
+
+/* testShortArray7 */
+$bar = ['a' => $a, 'b' => $b, 'c' => (isset($c) ? $c : null)];
+
+/* testShortArray8 */
+$bar = [0 => $a, 2 => $b, 6 => (isset($c) ? $c : null)];
diff --git a/tests/Core/Util/Sniffs/PassedParameters/GetParameterCountTest.php b/tests/Core/Util/Sniffs/PassedParameters/GetParameterCountTest.php
new file mode 100644
index 0000000000..15ad63cde7
--- /dev/null
+++ b/tests/Core/Util/Sniffs/PassedParameters/GetParameterCountTest.php
@@ -0,0 +1,316 @@
+
+ * @copyright 2016-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\PassedParameters;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\PassedParameters;
+
+class GetParameterCountTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test correctly counting the number of passed parameters.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param int $expected The expected parameter count.
+ *
+ * @dataProvider dataGetParameterCount
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getParameterCount
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getParameters
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::hasParameters
+ *
+ * @return void
+ */
+ public function testGetParameterCount($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, [T_STRING, T_ARRAY, T_OPEN_SHORT_ARRAY, T_LIST, T_ISSET, T_UNSET]);
+ $result = PassedParameters::getParameterCount(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testGetParameterCount()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testGetParameterCount()
+ *
+ * @return array
+ */
+ public function dataGetParameterCount()
+ {
+ return [
+ [
+ '/* testFunctionCall0 */',
+ 0,
+ ],
+ [
+ '/* testFunctionCall1 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall2 */',
+ 2,
+ ],
+ [
+ '/* testFunctionCall3 */',
+ 3,
+ ],
+ [
+ '/* testFunctionCall4 */',
+ 4,
+ ],
+ [
+ '/* testFunctionCall5 */',
+ 5,
+ ],
+ [
+ '/* testFunctionCall6 */',
+ 6,
+ ],
+ [
+ '/* testFunctionCall7 */',
+ 7,
+ ],
+ [
+ '/* testFunctionCall8 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall9 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall10 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall11 */',
+ 2,
+ ],
+ [
+ '/* testFunctionCall12 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall13 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall14 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall15 */',
+ 2,
+ ],
+ [
+ '/* testFunctionCall16 */',
+ 6,
+ ],
+ [
+ '/* testFunctionCall17 */',
+ 6,
+ ],
+ [
+ '/* testFunctionCall18 */',
+ 6,
+ ],
+ [
+ '/* testFunctionCall19 */',
+ 6,
+ ],
+ [
+ '/* testFunctionCall20 */',
+ 6,
+ ],
+ [
+ '/* testFunctionCall21 */',
+ 6,
+ ],
+ [
+ '/* testFunctionCall22 */',
+ 6,
+ ],
+ [
+ '/* testFunctionCall23 */',
+ 3,
+ ],
+ [
+ '/* testFunctionCall24 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall25 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall26 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall27 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall28 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall29 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall30 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall31 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall32 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall33 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall34 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall35 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall36 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall37 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall38 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall39 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall40 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall41 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall42 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall43 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall44 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall45 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall46 */',
+ 1,
+ ],
+ [
+ '/* testFunctionCall47 */',
+ 1,
+ ],
+
+ // Long arrays.
+ [
+ '/* testLongArray1 */',
+ 7,
+ ],
+ [
+ '/* testLongArray2 */',
+ 1,
+ ],
+ [
+ '/* testLongArray3 */',
+ 6,
+ ],
+ [
+ '/* testLongArray4 */',
+ 6,
+ ],
+ [
+ '/* testLongArray5 */',
+ 6,
+ ],
+ [
+ '/* testLongArray6 */',
+ 3,
+ ],
+ [
+ '/* testLongArray7 */',
+ 3,
+ ],
+ [
+ '/* testLongArray8 */',
+ 3,
+ ],
+
+ // Short arrays.
+ [
+ '/* testShortArray1 */',
+ 7,
+ ],
+ [
+ '/* testShortArray2 */',
+ 1,
+ ],
+ [
+ '/* testShortArray3 */',
+ 6,
+ ],
+ [
+ '/* testShortArray4 */',
+ 6,
+ ],
+ [
+ '/* testShortArray5 */',
+ 6,
+ ],
+ [
+ '/* testShortArray6 */',
+ 3,
+ ],
+ [
+ '/* testShortArray7 */',
+ 3,
+ ],
+ [
+ '/* testShortArray8 */',
+ 3,
+ ],
+ ];
+
+ }//end dataGetParameterCount()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/PassedParameters/GetParametersTest.inc b/tests/Core/Util/Sniffs/PassedParameters/GetParametersTest.inc
new file mode 100644
index 0000000000..c655863ecb
--- /dev/null
+++ b/tests/Core/Util/Sniffs/PassedParameters/GetParametersTest.inc
@@ -0,0 +1,123 @@
+ $a,] + (isset($b) ? ['b' => $b,] : []));
+
+/* testLongArrayNestedFunctionCalls */
+$foo = array(some_call(5, 1), another(1), why(5, 1, 2), 4, 5, 6); // 6
+
+/* testSimpleLongArray */
+$foo = array( 1, 2, 3, 4, 5, 6, true );
+
+/* testLongArrayWithKeys */
+$foo = array('a' => $a, 'b' => $b, 'c' => $c);
+
+/* testShortArrayNestedFunctionCalls */
+$bar = [0, 0, date('s', $timestamp), date('m'), date('d'), date('Y')]; // 6
+
+/* testShortArrayMoreNestedFunctionCalls */
+$bar = [str_replace("../", "/", trim($value))]; // 1
+
+/* testShortArrayWithKeysAndTernary */
+$bar = [0 => $a, 2 => $b, 6 => (isset($c) ? $c : null)];
+
+/* testNestedArraysToplevel */
+$array = array(
+ '1' => array(
+ 0 => 'more nesting',
+ /* testNestedArraysLevel2 */
+ 1 => array(1,2,3),
+ ),
+ /* testNestedArraysLevel1 */
+ '2' => [
+ 0 => 'more nesting',
+ 1 => [1,2,3],
+ ],
+);
+
+/* testFunctionCallNestedArrayNestedClosureWithCommas */
+preg_replace_callback_array(
+ /* testShortArrayNestedClosureWithCommas */
+ [
+ '~'.$dyn.'~J' => function ($match) {
+ echo strlen($match[0]), ' matches for "a" found', PHP_EOL;
+ },
+ '~'.function_call().'~i' => function ($match) {
+ echo strlen($match[0]), ' matches for "b" found', PHP_EOL;
+ },
+ ],
+ $subject
+);
+
+/* testShortArrayNestedAnonClass */
+$array = [
+ 'class' => new class() {
+ public $prop = [1,2,3];
+ public function test( $foo, $bar ) {
+ echo $foo, $bar;
+ }
+ },
+ 'anotherclass' => new class() {
+ public function test( $foo, $bar ) {
+ echo $foo, $bar;
+ }
+ },
+];
+
+/* testVariableFunctionCall */
+$closure($a, (1 + 20), $a & $b );
+
+/* testStaticVariableFunctionCall */
+self::$closureInStaticProperty($a->property, $b->call() );
+
+/* testSimpleList */
+list($id, $name) = $data;
+
+/* testNestedList */
+list($a, list($b, $c)) = array(1, array(2, 3));
+
+/* testListWithKeys */
+list('name' => $a, 'id' => $b, 'field' => $a) = ['name' => 1, 'id' => 2, 'field' => 3];
+
+/* testListWithEmptyEntries */
+list( , $a, , $b, , $a, ,) = [1, 2, 3, 4, 5, 6, 7, 8];
+
+/* testMultiLineKeyedListWithTrailingComma */
+ list(
+ "name" => $this->name,
+ "colour" => $this->colour,
+ "age" => $this->age,
+ "cuteness" => $this->cuteness,
+ ) = $attributes;
+
+/* testShortList */
+[$a, $b, $c] = [1, 2 => 'x', 'z' => 'c'];
+
+/* testNestedShortList */
+[[$a, $b], [$b, $a]] = array(array(10, 11), array(2, 3));
+
+/* testIsset */
+if ( isset(
+ $variable,
+ $object->property,
+ static::$property,
+ $array[$name][$sub],
+)) {}
+
+/* testUnset */
+unset( $variable, $object->property, static::$property, $array[$name], );
diff --git a/tests/Core/Util/Sniffs/PassedParameters/GetParametersTest.php b/tests/Core/Util/Sniffs/PassedParameters/GetParametersTest.php
new file mode 100644
index 0000000000..32b5d2c082
--- /dev/null
+++ b/tests/Core/Util/Sniffs/PassedParameters/GetParametersTest.php
@@ -0,0 +1,752 @@
+
+ * @copyright 2016-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\PassedParameters;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\PassedParameters;
+
+class GetParametersTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test retrieving the parameter details from a function call without parameters.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getParameters
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getParameter
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::hasParameters
+ *
+ * @return void
+ */
+ public function testGetParametersNoParams()
+ {
+ $stackPtr = $this->getTargetToken('/* testNoParams */', T_STRING);
+
+ $result = PassedParameters::getParameters(self::$phpcsFile, $stackPtr);
+ $this->assertSame([], $result);
+
+ $result = PassedParameters::getParameter(self::$phpcsFile, $stackPtr, 2);
+ $this->assertFalse($result);
+
+ }//end testGetParametersNoParams()
+
+
+ /**
+ * Test retrieving the parameter details from a function call or construct.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param int|string $targetType The type of token to look for.
+ * @param array $expected The expected parameter array.
+ *
+ * @dataProvider dataGetParameters
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getParameters
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::hasParameters
+ *
+ * @return void
+ */
+ public function testGetParameters($testMarker, $targetType, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, [$targetType]);
+
+ // Start/end token position values in the expected array are set as offsets
+ // in relation to the target token.
+ // Change these to exact positions based on the retrieved stackPtr.
+ foreach ($expected as $key => $value) {
+ $expected[$key]['start'] = ($stackPtr + $value['start']);
+ $expected[$key]['end'] = ($stackPtr + $value['end']);
+ }
+
+ $result = PassedParameters::getParameters(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testGetParameters()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testGetParameters()
+ *
+ * @return array
+ */
+ public function dataGetParameters()
+ {
+ return [
+ [
+ '/* testFunctionCall */',
+ T_STRING,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 3,
+ 'raw' => '1',
+ ],
+ 2 => [
+ 'start' => 5,
+ 'end' => 6,
+ 'raw' => '2',
+ ],
+ 3 => [
+ 'start' => 8,
+ 'end' => 9,
+ 'raw' => '3',
+ ],
+ 4 => [
+ 'start' => 11,
+ 'end' => 12,
+ 'raw' => '4',
+ ],
+ 5 => [
+ 'start' => 14,
+ 'end' => 15,
+ 'raw' => '5',
+ ],
+ 6 => [
+ 'start' => 17,
+ 'end' => 18,
+ 'raw' => '6',
+ ],
+ 7 => [
+ 'start' => 20,
+ 'end' => 22,
+ 'raw' => 'true',
+ ],
+ ],
+ ],
+ [
+ '/* testFunctionCallNestedFunctionCall */',
+ T_STRING,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 9,
+ 'raw' => 'dirname( __FILE__ )',
+ ],
+ ],
+ ],
+ [
+ '/* testAnotherFunctionCall */',
+ T_STRING,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 2,
+ 'raw' => '$stHour',
+ ],
+ 2 => [
+ 'start' => 4,
+ 'end' => 5,
+ 'raw' => '0',
+ ],
+ 3 => [
+ 'start' => 7,
+ 'end' => 8,
+ 'raw' => '0',
+ ],
+ 4 => [
+ 'start' => 10,
+ 'end' => 14,
+ 'raw' => '$arrStDt[0]',
+ ],
+ 5 => [
+ 'start' => 16,
+ 'end' => 20,
+ 'raw' => '$arrStDt[1]',
+ ],
+ 6 => [
+ 'start' => 22,
+ 'end' => 26,
+ 'raw' => '$arrStDt[2]',
+ ],
+ ],
+
+ ],
+ [
+ '/* testFunctionCallTrailingComma */',
+ T_STRING,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 5,
+ 'raw' => 'array()',
+ ],
+ ],
+ ],
+ [
+ '/* testFunctionCallNestedShortArray */',
+ T_STRING,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 34,
+ 'raw' => '[\'a\' => $a,] + (isset($b) ? [\'b\' => $b,] : [])',
+ ],
+ ],
+ ],
+ [
+ '/* testFunctionCallNestedArrayNestedClosureWithCommas */',
+ T_STRING,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 90,
+ 'raw' => '/* testShortArrayNestedClosureWithCommas */
+ [
+ \'~\'.$dyn.\'~J\' => function ($match) {
+ echo strlen($match[0]), \' matches for "a" found\', PHP_EOL;
+ },
+ \'~\'.function_call().\'~i\' => function ($match) {
+ echo strlen($match[0]), \' matches for "b" found\', PHP_EOL;
+ },
+ ]',
+ ],
+ 2 => [
+ 'start' => 92,
+ 'end' => 95,
+ 'raw' => '$subject',
+ ],
+ ],
+ ],
+
+ // Long array.
+ [
+ '/* testLongArrayNestedFunctionCalls */',
+ T_ARRAY,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 8,
+ 'raw' => 'some_call(5, 1)',
+ ],
+ 2 => [
+ 'start' => 10,
+ 'end' => 14,
+ 'raw' => 'another(1)',
+ ],
+ 3 => [
+ 'start' => 16,
+ 'end' => 26,
+ 'raw' => 'why(5, 1, 2)',
+ ],
+ 4 => [
+ 'start' => 28,
+ 'end' => 29,
+ 'raw' => '4',
+ ],
+ 5 => [
+ 'start' => 31,
+ 'end' => 32,
+ 'raw' => '5',
+ ],
+ 6 => [
+ 'start' => 34,
+ 'end' => 35,
+ 'raw' => '6',
+ ],
+ ],
+ ],
+
+ // Short array.
+ [
+ '/* testShortArrayNestedFunctionCalls */',
+ T_OPEN_SHORT_ARRAY,
+ [
+ 1 => [
+ 'start' => 1,
+ 'end' => 1,
+ 'raw' => '0',
+ ],
+ 2 => [
+ 'start' => 3,
+ 'end' => 4,
+ 'raw' => '0',
+ ],
+ 3 => [
+ 'start' => 6,
+ 'end' => 13,
+ 'raw' => 'date(\'s\', $timestamp)',
+ ],
+ 4 => [
+ 'start' => 15,
+ 'end' => 19,
+ 'raw' => 'date(\'m\')',
+ ],
+ 5 => [
+ 'start' => 21,
+ 'end' => 25,
+ 'raw' => 'date(\'d\')',
+ ],
+ 6 => [
+ 'start' => 27,
+ 'end' => 31,
+ 'raw' => 'date(\'Y\')',
+ ],
+ ],
+ ],
+
+ // Nested arrays.
+ [
+ '/* testNestedArraysToplevel */',
+ T_ARRAY,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 38,
+ 'raw' => '\'1\' => array(
+ 0 => \'more nesting\',
+ /* testNestedArraysLevel2 */
+ 1 => array(1,2,3),
+ )',
+ ],
+ 2 => [
+ 'start' => 40,
+ 'end' => 74,
+ 'raw' => '/* testNestedArraysLevel1 */
+ \'2\' => [
+ 0 => \'more nesting\',
+ 1 => [1,2,3],
+ ]',
+ ],
+ ],
+ ],
+
+ // Array containing closure.
+ [
+ '/* testShortArrayNestedClosureWithCommas */',
+ T_OPEN_SHORT_ARRAY,
+ [
+ 1 => [
+ 'start' => 1,
+ 'end' => 38,
+ 'raw' => '\'~\'.$dyn.\'~J\' => function ($match) {
+ echo strlen($match[0]), \' matches for "a" found\', PHP_EOL;
+ }',
+ ],
+ 2 => [
+ 'start' => 40,
+ 'end' => 79,
+ 'raw' => '\'~\'.function_call().\'~i\' => function ($match) {
+ echo strlen($match[0]), \' matches for "b" found\', PHP_EOL;
+ }',
+ ],
+ ],
+ ],
+
+ // Array containing anonymous class.
+ [
+ '/* testShortArrayNestedAnonClass */',
+ T_OPEN_SHORT_ARRAY,
+ [
+ 1 => [
+ 'start' => 1,
+ 'end' => 61,
+ 'raw' => '\'class\' => new class() {
+ public $prop = [1,2,3];
+ public function test( $foo, $bar ) {
+ echo $foo, $bar;
+ }
+ }',
+ ],
+ 2 => [
+ 'start' => 63,
+ 'end' => 107,
+ 'raw' => '\'anotherclass\' => new class() {
+ public function test( $foo, $bar ) {
+ echo $foo, $bar;
+ }
+ }',
+ ],
+ ],
+ ],
+
+ // Function calling closure in variable.
+ [
+ '/* testVariableFunctionCall */',
+ T_VARIABLE,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 2,
+ 'raw' => '$a',
+ ],
+ 2 => [
+ 'start' => 4,
+ 'end' => 11,
+ 'raw' => '(1 + 20)',
+ ],
+ 3 => [
+ 'start' => 13,
+ 'end' => 19,
+ 'raw' => '$a & $b',
+ ],
+ ],
+ ],
+ [
+ '/* testStaticVariableFunctionCall */',
+ T_VARIABLE,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 4,
+ 'raw' => '$a->property',
+ ],
+ 2 => [
+ 'start' => 6,
+ 'end' => 12,
+ 'raw' => '$b->call()',
+ ],
+ ],
+ ],
+
+ // Lists.
+ [
+ '/* testNestedList */',
+ T_LIST,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 2,
+ 'raw' => '$a',
+ ],
+ 2 => [
+ 'start' => 4,
+ 'end' => 11,
+ 'raw' => 'list($b, $c)',
+ ],
+ ],
+ ],
+ [
+ '/* testListWithEmptyEntries */',
+ T_LIST,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 2,
+ 'raw' => '',
+ ],
+ 2 => [
+ 'start' => 4,
+ 'end' => 5,
+ 'raw' => '$a',
+ ],
+ 3 => [
+ 'start' => 7,
+ 'end' => 7,
+ 'raw' => '',
+ ],
+ 4 => [
+ 'start' => 9,
+ 'end' => 10,
+ 'raw' => '$b',
+ ],
+ 5 => [
+ 'start' => 12,
+ 'end' => 12,
+ 'raw' => '',
+ ],
+ 6 => [
+ 'start' => 14,
+ 'end' => 15,
+ 'raw' => '$a',
+ ],
+ 7 => [
+ 'start' => 17,
+ 'end' => 17,
+ 'raw' => '',
+ ],
+ ],
+ ],
+ [
+ '/* testMultiLineKeyedListWithTrailingComma */',
+ T_LIST,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 10,
+ 'raw' => '"name" => $this->name',
+ ],
+ 2 => [
+ 'start' => 12,
+ 'end' => 20,
+ 'raw' => '"colour" => $this->colour',
+ ],
+ 3 => [
+ 'start' => 22,
+ 'end' => 30,
+ 'raw' => '"age" => $this->age',
+ ],
+ 4 => [
+ 'start' => 32,
+ 'end' => 40,
+ 'raw' => '"cuteness" => $this->cuteness',
+ ],
+ ],
+ ],
+ [
+ '/* testNestedShortList */',
+ T_OPEN_SHORT_ARRAY,
+ [
+ 1 => [
+ 'start' => 1,
+ 'end' => 6,
+ 'raw' => '[$a, $b]',
+ ],
+ 2 => [
+ 'start' => 8,
+ 'end' => 14,
+ 'raw' => '[$b, $a]',
+ ],
+ ],
+ ],
+ [
+ '/* testIsset */',
+ T_ISSET,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 4,
+ 'raw' => '$variable',
+ ],
+ 2 => [
+ 'start' => 6,
+ 'end' => 10,
+ 'raw' => '$object->property',
+ ],
+ 3 => [
+ 'start' => 12,
+ 'end' => 16,
+ 'raw' => 'static::$property',
+ ],
+ 4 => [
+ 'start' => 18,
+ 'end' => 26,
+ 'raw' => '$array[$name][$sub]',
+ ],
+ ],
+ ],
+ [
+ '/* testUnset */',
+ T_UNSET,
+ [
+ 1 => [
+ 'start' => 2,
+ 'end' => 3,
+ 'raw' => '$variable',
+ ],
+ 2 => [
+ 'start' => 5,
+ 'end' => 8,
+ 'raw' => '$object->property',
+ ],
+ 3 => [
+ 'start' => 10,
+ 'end' => 13,
+ 'raw' => 'static::$property',
+ ],
+ 4 => [
+ 'start' => 15,
+ 'end' => 19,
+ 'raw' => '$array[$name]',
+ ],
+ ],
+ ],
+ ];
+
+ }//end dataGetParameters()
+
+
+ /**
+ * Test retrieving the details for a specific parameter from a function call or construct.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param int|string $targetType The type of token to look for.
+ * @param int $paramPosition The position of the parameter we want to retrieve the details for.
+ * @param array $expected The expected array for the specific parameter.
+ *
+ * @dataProvider dataGetParameter
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getParameter
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::getParameters
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::hasParameters
+ *
+ * @return void
+ */
+ public function testGetParameter($testMarker, $targetType, $paramPosition, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, [$targetType]);
+
+ // Start/end token position values in the expected array are set as offsets
+ // in relation to the target token.
+ // Change these to exact positions based on the retrieved stackPtr.
+ $expected['start'] += $stackPtr;
+ $expected['end'] += $stackPtr;
+
+ $result = PassedParameters::getParameter(self::$phpcsFile, $stackPtr, $paramPosition);
+ $this->assertSame($expected, $result);
+
+ }//end testGetParameter()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testGetParameter()
+ *
+ * @return array
+ */
+ public function dataGetParameter()
+ {
+ return [
+ [
+ '/* testFunctionCall */',
+ T_STRING,
+ 4,
+ [
+ 'start' => 11,
+ 'end' => 12,
+ 'raw' => '4',
+ ],
+ ],
+ [
+ '/* testFunctionCallNestedFunctionCall */',
+ T_STRING,
+ 1,
+ [
+ 'start' => 2,
+ 'end' => 9,
+ 'raw' => 'dirname( __FILE__ )',
+ ],
+ ],
+ [
+ '/* testAnotherFunctionCall */',
+ T_STRING,
+ 1,
+ [
+ 'start' => 2,
+ 'end' => 2,
+ 'raw' => '$stHour',
+ ],
+ ],
+ [
+ '/* testAnotherFunctionCall */',
+ T_STRING,
+ 6,
+ [
+ 'start' => 22,
+ 'end' => 26,
+ 'raw' => '$arrStDt[2]',
+ ],
+ ],
+ [
+ '/* testLongArrayNestedFunctionCalls */',
+ T_ARRAY,
+ 3,
+ [
+ 'start' => 16,
+ 'end' => 26,
+ 'raw' => 'why(5, 1, 2)',
+ ],
+ ],
+ [
+ '/* testSimpleLongArray */',
+ T_ARRAY,
+ 1,
+ [
+ 'start' => 2,
+ 'end' => 3,
+ 'raw' => '1',
+ ],
+ ],
+ [
+ '/* testSimpleLongArray */',
+ T_ARRAY,
+ 7,
+ [
+ 'start' => 20,
+ 'end' => 22,
+ 'raw' => 'true',
+ ],
+ ],
+ [
+ '/* testLongArrayWithKeys */',
+ T_ARRAY,
+ 2,
+ [
+ 'start' => 8,
+ 'end' => 13,
+ 'raw' => '\'b\' => $b',
+ ],
+ ],
+ [
+ '/* testShortArrayMoreNestedFunctionCalls */',
+ T_OPEN_SHORT_ARRAY,
+ 1,
+ [
+ 'start' => 1,
+ 'end' => 13,
+ 'raw' => 'str_replace("../", "/", trim($value))',
+ ],
+ ],
+ [
+ '/* testShortArrayWithKeysAndTernary */',
+ T_OPEN_SHORT_ARRAY,
+ 3,
+ [
+ 'start' => 14,
+ 'end' => 32,
+ 'raw' => '6 => (isset($c) ? $c : null)',
+ ],
+ ],
+ [
+ '/* testNestedArraysLevel2 */',
+ T_ARRAY,
+ 1,
+ [
+ 'start' => 2,
+ 'end' => 2,
+ 'raw' => '1',
+ ],
+ ],
+ [
+ '/* testNestedArraysLevel1 */',
+ T_OPEN_SHORT_ARRAY,
+ 2,
+ [
+ 'start' => 9,
+ 'end' => 21,
+ 'raw' => '1 => [1,2,3]',
+ ],
+ ],
+ [
+ '/* testListWithKeys */',
+ T_LIST,
+ 2,
+ [
+ 'start' => 8,
+ 'end' => 13,
+ 'raw' => '\'id\' => $b',
+ ],
+ ],
+ [
+ '/* testShortList */',
+ T_OPEN_SHORT_ARRAY,
+ 3,
+ [
+ 'start' => 6,
+ 'end' => 7,
+ 'raw' => '$c',
+ ],
+ ],
+ ];
+
+ }//end dataGetParameter()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/PassedParameters/HasParametersTest.inc b/tests/Core/Util/Sniffs/PassedParameters/HasParametersTest.inc
new file mode 100644
index 0000000000..7c2e99dde9
--- /dev/null
+++ b/tests/Core/Util/Sniffs/PassedParameters/HasParametersTest.inc
@@ -0,0 +1,172 @@
+methodName();
+
+// Function calls: no parameters.
+
+/* testNoParamsFunctionCall1 */
+some_function();
+
+/* testNoParamsFunctionCall2 */
+some_function( );
+
+/* testNoParamsFunctionCall3 */
+some_function( /*nothing here*/ );
+
+/* testNoParamsFunctionCall4 */
+$closure(/*nothing here*/);
+
+// Function calls: has parameters.
+
+/* testHasParamsFunctionCall1 */
+some_function( 1 );
+
+/* testHasParamsFunctionCall2 */
+$closure(1,2,3);
+
+class Bar {
+ public static function getInstance() {
+ /* testHasParamsFunctionCall3 */
+ return new self(true);
+ }
+}
+
+/* testHasParamsFunctionCall4 */
+${$functionname}(1,2,3);
+
+/* testHasParamsFunctionCall5 */
+$obj->{$var}('foo');
+
+/* testHasParamsFunctionCall6 */
+$this->{self::$methodname}([]);
+
+/* testHasParamsFunctionCall7 */
+${$this->functionname}(array(1));
+
+/* testHasParamsFunctionCall8 */
+${FUNCTION_NAME}(false);
+
+$closureCall = (function ($a, $b) {
+ /* testHasParamsFunctionCall9 */
+ })('a','b');
+
+/* testNotAClosureDeclarationWithCall2 */
+(function ($a, $b) {
+ /* testNotAClosureDeclarationWithCall3 */
+ return function ($c, $d)
+ /* testNotAClosureDeclarationWithCall4 */
+ use ($a, $b)
+ {
+ echo $a, $b, $c, $d;
+ };
+/* testHasParamsFunctionCall10 */
+})
+ /* testHasParamsFunctionCall11 */
+ ('a','b')
+ /* testNotAClosureDeclarationWithCall5 */
+ ('c','d');
+
+// Arrays: no parameters.
+
+/* testNoParamsLongArray1 */
+$foo = array();
+
+/* testNoParamsLongArray2 */
+$foo = array( );
+
+/* testNoParamsLongArray3 */
+$foo = array( /*nothing here*/ );
+
+/* testNoParamsLongArray4 */
+$foo = array(/*nothing here*/);
+
+/* testNoParamsShortArray1 */
+$bar = [];
+
+/* testNoParamsShortArray2 */
+$bar = [ ];
+
+/* testNoParamsShortArray3 */
+$bar = [ /*nothing here*/ ];
+
+/* testNoParamsShortArray4 */
+$bar = [/*nothing here*/];
+
+// Arrays: has parameters.
+
+/* testHasParamsLongArray1 */
+$foo = array( 1 );
+
+/* testHasParamsLongArray2 */
+$foo = array(1,2,3);
+
+/* testHasParamsLongArray3 */
+$foo = array(true);
+
+/* testHasParamsShortArray1 */
+$bar = [ 1 ];
+
+/* testHasParamsShortArray2 */
+$bar = [1,2,3];
+
+/* testHasParamsShortArray3 */
+$bar = [true];
+
+/* testNoParamsLongList */
+list() = $array; // Intentional fatal error.
+
+/* testHasParamsLongList */
+list($a) = $array;
+
+/* testNoParamsShortList */
+[
+ // phpcs:ignore Standard.Cat.Sniff -- for reasons
+] = $array; // Intentional fatal error.
+
+/* testHasParamsShortList */
+[$this->prop] = $array;
+
+/* testNoParamsIsset */
+$a = isset( /* comment */ ); // Intentional parse error.
+
+/* testHasParamsIsset */
+$a = isset( $array[$key] );
+
+/* testNoParamsUnset */
+unset(
+
+
+); // Intentional parse error.
+
+/* testHasParamsUnset */
+unset(
+
+ $hello,
+
+);
+
+// Intentional parse error.
+/* testNoCloseParenthesis */
+$array = array(1, 2, 3
+
+// Intentional parse error.
+/* testNoOpenParenthesis */
+$array = function_call[];
+
+// Intentional parse error. This has to be the last test in the file without a new line after it.
+/* testLiveCoding */
+$array = array
\ No newline at end of file
diff --git a/tests/Core/Util/Sniffs/PassedParameters/HasParametersTest.php b/tests/Core/Util/Sniffs/PassedParameters/HasParametersTest.php
new file mode 100644
index 0000000000..9213f52def
--- /dev/null
+++ b/tests/Core/Util/Sniffs/PassedParameters/HasParametersTest.php
@@ -0,0 +1,341 @@
+
+ * @copyright 2016-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\PassedParameters;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\PassedParameters;
+
+class HasParametersTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a token which is not supported by
+ * these methods is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage The hasParameters() method expects a function call, array, list, isset or unset token to be passed. Received "T_INTERFACE" instead
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::hasParameters
+ *
+ * @return void
+ */
+ public function testNotAnAcceptedTokenException()
+ {
+ $interface = $this->getTargetToken('/* testNotAnAcceptedToken */', T_INTERFACE);
+ $result = PassedParameters::hasParameters(self::$phpcsFile, $interface);
+
+ }//end testNotAnAcceptedTokenException()
+
+
+ /**
+ * Test receiving an expected exception when T_SELF is passed not preceeded by `new`.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage The hasParameters() method expects a function call, array, list, isset or unset token to be passed. Received "T_SELF" instead
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::hasParameters
+ *
+ * @return void
+ */
+ public function testNotACallToConstructor()
+ {
+ $self = $this->getTargetToken('/* testNotACallToConstructor */', T_SELF);
+ $result = PassedParameters::hasParameters(self::$phpcsFile, $self);
+
+ }//end testNotACallToConstructor()
+
+
+ /**
+ * Test receiving an expected exception when T_CLOSE_CURLY_BRACKET is passed which isn't a function call.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage The hasParameters() method expects a function call, array, list, isset or unset token to be passed. Received a "T_CLOSE_CURLY_BRACKET" which is not function call
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::hasParameters
+ *
+ * @return void
+ */
+ public function testNotAVariableFunctionCall()
+ {
+ $self = $this->getTargetToken('/* testNotAVariableFunctionCall */', T_CLOSE_CURLY_BRACKET);
+ $result = PassedParameters::hasParameters(self::$phpcsFile, $self);
+
+ }//end testNotAVariableFunctionCall()
+
+
+ /**
+ * Test receiving an expected exception when T_CLOSE_PARENTHESIS is passed which isn't a function call.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage The hasParameters() method expects a function call, array, list, isset or unset token to be passed. Received a "T_CLOSE_PARENTHESIS" which is not function call
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::hasParameters
+ *
+ * @return void
+ */
+ public function testNotAClosureDeclarationWithCall()
+ {
+ for ($i = 1; $i <= 5; $i++) {
+ $closeParen = $this->getTargetToken('/* testNotAClosureDeclarationWithCall'.$i.' */', T_CLOSE_PARENTHESIS);
+ $result = PassedParameters::hasParameters(self::$phpcsFile, $closeParen);
+ }
+
+ }//end testNotAClosureDeclarationWithCall()
+
+
+ /**
+ * Test correctly identifying whether parameters were passed to a function call or construct.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param int|string $targetType The type of token to look for.
+ * @param bool $expected Whether or not the function/array has parameters/values.
+ *
+ * @dataProvider dataHasParameters
+ * @covers \PHP_CodeSniffer\Util\Sniffs\PassedParameters::hasParameters
+ *
+ * @return void
+ */
+ public function testHasParameters($testMarker, $targetType, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, [$targetType]);
+ $result = PassedParameters::hasParameters(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testHasParameters()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testHasParameters()
+ *
+ * @return array
+ */
+ public function dataHasParameters()
+ {
+ return [
+ // Function calls.
+ [
+ '/* testNoParamsFunctionCall1 */',
+ T_STRING,
+ false,
+ ],
+ [
+ '/* testNoParamsFunctionCall2 */',
+ T_STRING,
+ false,
+ ],
+ [
+ '/* testNoParamsFunctionCall3 */',
+ T_STRING,
+ false,
+ ],
+ [
+ '/* testNoParamsFunctionCall4 */',
+ T_VARIABLE,
+ false,
+ ],
+ [
+ '/* testHasParamsFunctionCall1 */',
+ T_STRING,
+ true,
+ ],
+ [
+ '/* testHasParamsFunctionCall2 */',
+ T_VARIABLE,
+ true,
+ ],
+ [
+ '/* testHasParamsFunctionCall3 */',
+ T_SELF,
+ true,
+ ],
+ [
+ '/* testHasParamsFunctionCall4 */',
+ T_CLOSE_CURLY_BRACKET,
+ true,
+ ],
+ [
+ '/* testHasParamsFunctionCall5 */',
+ T_CLOSE_CURLY_BRACKET,
+ true,
+ ],
+ [
+ '/* testHasParamsFunctionCall6 */',
+ T_CLOSE_CURLY_BRACKET,
+ true,
+ ],
+ [
+ '/* testHasParamsFunctionCall7 */',
+ T_CLOSE_CURLY_BRACKET,
+ true,
+ ],
+ [
+ '/* testHasParamsFunctionCall8 */',
+ T_CLOSE_CURLY_BRACKET,
+ true,
+ ],
+ [
+ '/* testHasParamsFunctionCall9 */',
+ T_CLOSE_PARENTHESIS,
+ true,
+ ],
+ [
+ '/* testHasParamsFunctionCall10 */',
+ T_CLOSE_PARENTHESIS,
+ true,
+ ],
+ [
+ '/* testHasParamsFunctionCall11 */',
+ T_CLOSE_PARENTHESIS,
+ true,
+ ],
+
+ // Arrays.
+ [
+ '/* testNoParamsLongArray1 */',
+ T_ARRAY,
+ false,
+ ],
+ [
+ '/* testNoParamsLongArray2 */',
+ T_ARRAY,
+ false,
+ ],
+ [
+ '/* testNoParamsLongArray3 */',
+ T_ARRAY,
+ false,
+ ],
+ [
+ '/* testNoParamsLongArray4 */',
+ T_ARRAY,
+ false,
+ ],
+ [
+ '/* testNoParamsShortArray1 */',
+ T_OPEN_SHORT_ARRAY,
+ false,
+ ],
+ [
+ '/* testNoParamsShortArray2 */',
+ T_OPEN_SHORT_ARRAY,
+ false,
+ ],
+ [
+ '/* testNoParamsShortArray3 */',
+ T_OPEN_SHORT_ARRAY,
+ false,
+ ],
+ [
+ '/* testNoParamsShortArray4 */',
+ T_OPEN_SHORT_ARRAY,
+ false,
+ ],
+ [
+ '/* testHasParamsLongArray1 */',
+ T_ARRAY,
+ true,
+ ],
+ [
+ '/* testHasParamsLongArray2 */',
+ T_ARRAY,
+ true,
+ ],
+ [
+ '/* testHasParamsLongArray3 */',
+ T_ARRAY,
+ true,
+ ],
+ [
+ '/* testHasParamsShortArray1 */',
+ T_OPEN_SHORT_ARRAY,
+ true,
+ ],
+ [
+ '/* testHasParamsShortArray2 */',
+ T_OPEN_SHORT_ARRAY,
+ true,
+ ],
+ [
+ '/* testHasParamsShortArray3 */',
+ T_OPEN_SHORT_ARRAY,
+ true,
+ ],
+
+ // Lists.
+ [
+ '/* testNoParamsLongList */',
+ T_LIST,
+ false,
+ ],
+ [
+ '/* testHasParamsLongList */',
+ T_LIST,
+ true,
+ ],
+ [
+ '/* testNoParamsShortList */',
+ T_OPEN_SHORT_ARRAY,
+ false,
+ ],
+ [
+ '/* testHasParamsShortList */',
+ T_OPEN_SHORT_ARRAY,
+ true,
+ ],
+
+ // Isset.
+ [
+ '/* testNoParamsIsset */',
+ T_ISSET,
+ false,
+ ],
+ [
+ '/* testHasParamsIsset */',
+ T_ISSET,
+ true,
+ ],
+
+ // Unset.
+ [
+ '/* testNoParamsUnset */',
+ T_UNSET,
+ false,
+ ],
+ [
+ '/* testHasParamsUnset */',
+ T_UNSET,
+ true,
+ ],
+
+ // Defensive coding against parse errors and live coding.
+ [
+ '/* testNoCloseParenthesis */',
+ T_ARRAY,
+ false,
+ ],
+ [
+ '/* testNoOpenParenthesis */',
+ T_STRING,
+ false,
+ ],
+ [
+ '/* testLiveCoding */',
+ T_ARRAY,
+ false,
+ ],
+ ];
+
+ }//end dataHasParameters()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/TextStrings/GetCompleteTextStringTest.inc b/tests/Core/Util/Sniffs/TextStrings/GetCompleteTextStringTest.inc
new file mode 100644
index 0000000000..fe0df4fb67
--- /dev/null
+++ b/tests/Core/Util/Sniffs/TextStrings/GetCompleteTextStringTest.inc
@@ -0,0 +1,44 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\TextStrings;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\TextStrings;
+
+class GetCompleteTextStringTest extends AbstractMethodUnitTest
+{
+
+ /**
+ * Token types to target for these tests.
+ *
+ * @var array
+ */
+ private $targets = [
+ T_START_HEREDOC,
+ T_START_NOWDOC,
+ T_CONSTANT_ENCAPSED_STRING,
+ T_DOUBLE_QUOTED_STRING,
+ ];
+
+
+ /**
+ * Test receiving an expected exception when a non text string is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_START_HEREDOC, T_START_NOWDOC, T_CONSTANT_ENCAPSED_STRING or T_DOUBLE_QUOTED_STRING
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\TextStrings::getCompleteTextString
+ *
+ * @return void
+ */
+ public function testNotATextStringException()
+ {
+ $next = $this->getTargetToken('/* testNotATextString */', T_RETURN);
+ $result = TextStrings::getCompleteTextString(self::$phpcsFile, $next);
+
+ }//end testNotATextStringException()
+
+
+ /**
+ * Test receiving an expected exception when a text string token is not the first token
+ * of a multi-line text string.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be the start of the text string
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\TextStrings::getCompleteTextString
+ *
+ * @return void
+ */
+ public function testNotFirstTextStringException()
+ {
+ $next = $this->getTargetToken(
+ '/* testNotFirstTextStringToken */',
+ T_CONSTANT_ENCAPSED_STRING,
+ 'second line
+'
+ );
+ $result = TextStrings::getCompleteTextString(self::$phpcsFile, $next);
+
+ }//end testNotFirstTextStringException()
+
+
+ /**
+ * Test correctly retrieving the contents of a (potentially) multi-line text string.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param string $expected The expected function return value.
+ * @param string $expectedWithQuotes The expected function return value when $stripQuotes is set to "false".
+ *
+ * @dataProvider dataGetCompleteTextString
+ * @covers \PHP_CodeSniffer\Util\Sniffs\TextStrings::getCompleteTextString
+ *
+ * @return void
+ */
+ public function testGetCompleteTextString($testMarker, $expected, $expectedWithQuotes)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, $this->targets);
+
+ $result = TextStrings::getCompleteTextString(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ $result = TextStrings::getCompleteTextString(self::$phpcsFile, $stackPtr, false);
+ $this->assertSame($expectedWithQuotes, $result);
+
+ }//end testGetCompleteTextString()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testGetCompleteTextString()
+ *
+ * @return array
+ */
+ public function dataGetCompleteTextString()
+ {
+ return [
+ [
+ '/* testSingleLineConstantEncapsedString */',
+ 'single line text string',
+ "'single line text string'",
+ ],
+ [
+ '/* testMultiLineConstantEncapsedString */',
+ 'first line
+second line
+third line
+fourth line',
+ '"first line
+second line
+third line
+fourth line"',
+ ],
+ [
+ '/* testSingleLineDoubleQuotedString */',
+ 'single $line text string',
+ '"single $line text string"',
+ ],
+ [
+ '/* testMultiLineDoubleQuotedString */',
+ 'first line
+second $line
+third line
+fourth line',
+ '"first line
+second $line
+third line
+fourth line"',
+ ],
+ [
+ '/* testHeredocString */',
+ 'first line
+second $line
+third line
+fourth line
+',
+ 'first line
+second $line
+third line
+fourth line
+',
+ ],
+ [
+ '/* testNowdocString */',
+ 'first line
+second line
+third line
+fourth line
+',
+ 'first line
+second line
+third line
+fourth line
+',
+ ],
+ ];
+
+ }//end dataGetCompleteTextString()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/TextStrings/StripQuotesTest.php b/tests/Core/Util/Sniffs/TextStrings/StripQuotesTest.php
new file mode 100644
index 0000000000..f4add2b239
--- /dev/null
+++ b/tests/Core/Util/Sniffs/TextStrings/StripQuotesTest.php
@@ -0,0 +1,84 @@
+
+ * @copyright 2016-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\TextStrings;
+
+use PHPUnit\Framework\TestCase;
+use PHP_CodeSniffer\Util\Sniffs\TextStrings;
+
+class StripQuotesTest extends TestCase
+{
+
+
+ /**
+ * Test correctly stripping quotes surrounding text strings.
+ *
+ * @param string $input The input string.
+ * @param string $expected The expected function output.
+ *
+ * @dataProvider dataStripQuotes
+ * @covers \PHP_CodeSniffer\Util\Sniffs\TextStrings::stripQuotes
+ *
+ * @return void
+ */
+ public function testStripQuotes($input, $expected)
+ {
+ $this->assertSame($expected, TextStrings::stripQuotes($input));
+
+ }//end testStripQuotes()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testStripQuotes()
+ *
+ * @return array
+ */
+ public function dataStripQuotes()
+ {
+ return [
+ [
+ '"dir_name"',
+ 'dir_name',
+ ],
+ [
+ "'soap.wsdl_cache'",
+ "soap.wsdl_cache",
+ ],
+ [
+ '"arbitrary-\'string\" with\' quotes within"',
+ 'arbitrary-\'string\" with\' quotes within',
+ ],
+ [
+ '"\'quoted_name\'"',
+ '\'quoted_name\'',
+ ],
+ [
+ "'\"quoted\" start of string'",
+ '"quoted" start of string',
+ ],
+ [
+ "'no stripping when there is only a start quote",
+ "'no stripping when there is only a start quote",
+ ],
+ [
+ 'no stripping when there is only an end quote"',
+ 'no stripping when there is only an end quote"',
+ ],
+ [
+ "'no stripping when quotes at start/end are mismatched\"",
+ "'no stripping when quotes at start/end are mismatched\"",
+ ],
+ ];
+
+ }//end dataStripQuotes()
+
+
+}//end class
diff --git a/tests/Core/File/IsReferenceTest.inc b/tests/Core/Util/Sniffs/TokenIs/IsReferenceTest.inc
similarity index 56%
rename from tests/Core/File/IsReferenceTest.inc
rename to tests/Core/Util/Sniffs/TokenIs/IsReferenceTest.inc
index c0c867b33e..82cf068b33 100644
--- a/tests/Core/File/IsReferenceTest.inc
+++ b/tests/Core/Util/Sniffs/TokenIs/IsReferenceTest.inc
@@ -1,138 +1,136 @@
$first, 'b' => $something & $somethingElse ];
-/* bitwiseAndF */
+/* testBitwiseAndF */
$a = array( 'a' => $first, 'b' => $something & \MyClass::$somethingElse );
-/* bitwiseAndG */
+/* testBitwiseAndG */
$a = $something & $somethingElse;
-/* bitwiseAndH */
+/* testBitwiseAndH */
function myFunction($a = 10 & 20) {}
-/* bitwiseAndI */
+/* testBitwiseAndI */
$closure = function ($a = MY_CONSTANT & parent::OTHER_CONSTANT) {};
-/* functionReturnByReference */
+/* testFunctionReturnByReference */
function &myFunction() {}
-/* functionPassByReferenceA */
+/* testFunctionPassByReferenceA */
function myFunction( &$a ) {}
-/* functionPassByReferenceB */
+/* testFunctionPassByReferenceB */
function myFunction( $a, &$b ) {}
-/* functionPassByReferenceC */
+/* testFunctionPassByReferenceC */
$closure = function ( &$a ) {};
-/* functionPassByReferenceD */
+/* testFunctionPassByReferenceD */
$closure = function ( $a, &$b ) {};
-/* functionPassByReferenceE */
+/* testFunctionPassByReferenceE */
function myFunction(array &$one) {}
-/* functionPassByReferenceF */
+/* testFunctionPassByReferenceF */
$closure = function (\MyClass &$one) {};
-/* functionPassByReferenceG */
-$closure = function myFunc($param, &...$moreParams) {};
+/* testFunctionPassByReferenceG */
+$closure = function ($param, &...$moreParams) {};
-/* foreachValueByReference */
+/* testForeachValueByReference */
foreach( $array as $key => &$value ) {}
-/* foreachKeyByReference */
+/* testForeachKeyByReference */
foreach( $array as &$key => $value ) {}
-/* arrayValueByReferenceA */
+/* testArrayValueByReferenceA */
$a = [ 'a' => &$something ];
-/* arrayValueByReferenceB */
+/* testArrayValueByReferenceB */
$a = [ 'a' => $something, 'b' => &$somethingElse ];
-/* arrayValueByReferenceC */
+/* testArrayValueByReferenceC */
$a = [ &$something ];
-/* arrayValueByReferenceD */
+/* testArrayValueByReferenceD */
$a = [ $something, &$somethingElse ];
-/* arrayValueByReferenceE */
+/* testArrayValueByReferenceE */
$a = array( 'a' => &$something );
-/* arrayValueByReferenceF */
+/* testArrayValueByReferenceF */
$a = array( 'a' => $something, 'b' => &$somethingElse );
-/* arrayValueByReferenceG */
+/* testArrayValueByReferenceG */
$a = array( &$something );
-/* arrayValueByReferenceH */
+/* testArrayValueByReferenceH */
$a = array( $something, &$somethingElse );
-/* assignByReferenceA */
+/* testAssignByReferenceA */
$b = &$something;
-/* assignByReferenceB */
+/* testAssignByReferenceB */
$b =& $something;
-/* assignByReferenceC */
+/* testAssignByReferenceC */
$b .= &$something;
-/* assignByReferenceD */
+/* testAssignByReferenceD */
$myValue = &$obj->getValue();
-/* assignByReferenceE */
+/* testAssignByReferenceE */
$collection = &collector();
-/* passByReferenceA */
+/* testPassByReferenceA */
functionCall(&$something, $somethingElse);
-/* passByReferenceB */
+/* testPassByReferenceB */
functionCall($something, &$somethingElse);
-/* passByReferenceC */
+/* testPassByReferenceC */
functionCall($something, &$this->somethingElse);
-/* passByReferenceD */
+/* testPassByReferenceD */
functionCall($something, &self::$somethingElse);
-/* passByReferenceE */
+/* testPassByReferenceE */
functionCall($something, &parent::$somethingElse);
-/* passByReferenceF */
+/* testPassByReferenceF */
functionCall($something, &static::$somethingElse);
-/* passByReferenceG */
+/* testPassByReferenceG */
functionCall($something, &SomeClass::$somethingElse);
-/* passByReferenceH */
+/* testPassByReferenceH */
functionCall(&\SomeClass::$somethingElse);
-/* passByReferenceI */
+/* testPassByReferenceI */
functionCall($something, &\SomeNS\SomeClass::$somethingElse);
-/* passByReferenceJ */
+/* testPassByReferenceJ */
functionCall($something, &namespace\SomeClass::$somethingElse);
-/* newByReferenceA */
+/* testNewByReferenceA */
$foobar2 = &new Foobar();
-/* newByReferenceB */
+/* testNewByReferenceB */
functionCall( $something , &new Foobar() );
-/* useByReference */
+/* testUseByReference */
$closure = function() use (&$var){};
diff --git a/tests/Core/Util/Sniffs/TokenIs/IsReferenceTest.php b/tests/Core/Util/Sniffs/TokenIs/IsReferenceTest.php
new file mode 100644
index 0000000000..e966ac8a52
--- /dev/null
+++ b/tests/Core/Util/Sniffs/TokenIs/IsReferenceTest.php
@@ -0,0 +1,249 @@
+
+ * @copyright 2018-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\TokenIs;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\TokenIs;
+
+class IsReferenceTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test that false is returned when a non-"bitwise and" token is passed.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\TokenIs::isReference
+ *
+ * @return void
+ */
+ public function testNotBitwiseAndToken()
+ {
+ $target = $this->getTargetToken('/* testBitwiseAndA */', T_STRING);
+ $this->assertFalse(TokenIs::isReference(self::$phpcsFile, $target));
+
+ }//end testNotBitwiseAndToken()
+
+
+ /**
+ * Test whether a bitwise-and token is used as a reference.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expected Expected function output.
+ *
+ * @dataProvider dataIsReference
+ * @covers \PHP_CodeSniffer\Util\Sniffs\TokenIs::isReference
+ *
+ * @return void
+ */
+ public function testIsReference($testMarker, $expected)
+ {
+ $bitwiseAnd = $this->getTargetToken($testMarker, T_BITWISE_AND);
+ $result = TokenIs::isReference(self::$phpcsFile, $bitwiseAnd);
+ $this->assertSame($expected, $result);
+
+ }//end testIsReference()
+
+
+ /**
+ * Data provider for the IsReference test.
+ *
+ * @see testIsReference()
+ *
+ * @return array
+ */
+ public function dataIsReference()
+ {
+ return [
+ [
+ '/* testBitwiseAndA */',
+ false,
+ ],
+ [
+ '/* testBitwiseAndB */',
+ false,
+ ],
+ [
+ '/* testBitwiseAndC */',
+ false,
+ ],
+ [
+ '/* testBitwiseAndD */',
+ false,
+ ],
+ [
+ '/* testBitwiseAndE */',
+ false,
+ ],
+ [
+ '/* testBitwiseAndF */',
+ false,
+ ],
+ [
+ '/* testBitwiseAndG */',
+ false,
+ ],
+ [
+ '/* testBitwiseAndH */',
+ false,
+ ],
+ [
+ '/* testBitwiseAndI */',
+ false,
+ ],
+ [
+ '/* testFunctionReturnByReference */',
+ true,
+ ],
+ [
+ '/* testFunctionPassByReferenceA */',
+ true,
+ ],
+ [
+ '/* testFunctionPassByReferenceB */',
+ true,
+ ],
+ [
+ '/* testFunctionPassByReferenceC */',
+ true,
+ ],
+ [
+ '/* testFunctionPassByReferenceD */',
+ true,
+ ],
+ [
+ '/* testFunctionPassByReferenceE */',
+ true,
+ ],
+ [
+ '/* testFunctionPassByReferenceF */',
+ true,
+ ],
+ [
+ '/* testFunctionPassByReferenceG */',
+ true,
+ ],
+ [
+ '/* testForeachValueByReference */',
+ true,
+ ],
+ [
+ '/* testForeachKeyByReference */',
+ true,
+ ],
+ [
+ '/* testArrayValueByReferenceA */',
+ true,
+ ],
+ [
+ '/* testArrayValueByReferenceB */',
+ true,
+ ],
+ [
+ '/* testArrayValueByReferenceC */',
+ true,
+ ],
+ [
+ '/* testArrayValueByReferenceD */',
+ true,
+ ],
+ [
+ '/* testArrayValueByReferenceE */',
+ true,
+ ],
+ [
+ '/* testArrayValueByReferenceF */',
+ true,
+ ],
+ [
+ '/* testArrayValueByReferenceG */',
+ true,
+ ],
+ [
+ '/* testArrayValueByReferenceH */',
+ true,
+ ],
+ [
+ '/* testAssignByReferenceA */',
+ true,
+ ],
+ [
+ '/* testAssignByReferenceB */',
+ true,
+ ],
+ [
+ '/* testAssignByReferenceC */',
+ true,
+ ],
+ [
+ '/* testAssignByReferenceD */',
+ true,
+ ],
+ [
+ '/* testAssignByReferenceE */',
+ true,
+ ],
+ [
+ '/* testPassByReferenceA */',
+ true,
+ ],
+ [
+ '/* testPassByReferenceB */',
+ true,
+ ],
+ [
+ '/* testPassByReferenceC */',
+ true,
+ ],
+ [
+ '/* testPassByReferenceD */',
+ true,
+ ],
+ [
+ '/* testPassByReferenceE */',
+ true,
+ ],
+ [
+ '/* testPassByReferenceF */',
+ true,
+ ],
+ [
+ '/* testPassByReferenceG */',
+ true,
+ ],
+ [
+ '/* testPassByReferenceH */',
+ true,
+ ],
+ [
+ '/* testPassByReferenceI */',
+ true,
+ ],
+ [
+ '/* testPassByReferenceJ */',
+ true,
+ ],
+ [
+ '/* testNewByReferenceA */',
+ true,
+ ],
+ [
+ '/* testNewByReferenceB */',
+ true,
+ ],
+ [
+ '/* testUseByReference */',
+ true,
+ ],
+ ];
+
+ }//end dataIsReference()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/TokenIs/IsShortListTest.inc b/tests/Core/Util/Sniffs/TokenIs/IsShortListTest.inc
new file mode 100644
index 0000000000..171e1e5927
--- /dev/null
+++ b/tests/Core/Util/Sniffs/TokenIs/IsShortListTest.inc
@@ -0,0 +1,83 @@
+ [$id, $name, $info]) {}
+
+foreach ($array as [$a, /* testShortListInForeachNested */ [$b, $c]]) {}
+
+/* testMultiAssignShortlist */
+$foo = [$baz, $bat] = [$a, $b];
+
+/* testShortListWithKeys */
+["id" => $id1, "name" => $name1] = $data[0];
+
+/* testShortListInForeachWithKeysDetectOnCloseBracket */
+foreach ($data as ["id" => $id, "name" => $name]) {}
+
+// Invalid as empty lists are not allowed, but it is short list syntax.
+[$x, /* testNestedShortListEmpty */ [], $y] = $a;
+
+[$x, [ $y, /* testDeeplyNestedShortList */ [$z]], $q] = $a;
+
+/* testShortListWithNestingAndKeys */
+[
+ /* testNestedShortListWithKeys_1 */
+ ["x" => $x1, "y" => $y1],
+ /* testNestedShortListWithKeys_2 */
+ ["x" => $x2, "y" => $y2],
+ /* testNestedShortListWithKeys_3 */
+ ["x" => $x3, "y" => $y3],
+] = $points;
+
+/* testShortListWithoutVars */
+// Invalid list as it doesn't contain variables, but it is short list syntax.
+[42] = [1];
+
+/* testShortListNestedLongList */
+// Invalid list as mixing short list syntax with list() is not allowed, but it is short list syntax.
+[list($a, $b), list($c, $d)] = [[1, 2], [3, 4]];
+
+/* testNestedAnonClassWithTraitUseAs */
+// Parse error, but not short list syntax.
+array_map(function($a) {
+ return new class() {
+ use MyTrait {
+ MyTrait::functionName as [];
+ }
+ };
+}, $array);
+
+/* testParseError */
+// Parse error, but not short list syntax.
+use Something as [$a];
+
+/* testLiveCoding */
+// This has to be the last test in the file.
+[$a, [$b]
diff --git a/tests/Core/Util/Sniffs/TokenIs/IsShortListTest.php b/tests/Core/Util/Sniffs/TokenIs/IsShortListTest.php
new file mode 100644
index 0000000000..2959db613e
--- /dev/null
+++ b/tests/Core/Util/Sniffs/TokenIs/IsShortListTest.php
@@ -0,0 +1,175 @@
+
+ * @copyright 2018-2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\TokenIs;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\TokenIs;
+
+class IsShortListTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test whether a T_OPEN_SHORT_ARRAY token is a short array or a short list.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expected The expected boolean return value.
+ * @param int|string|array $targetToken The token type(s) to test. Defaults to T_OPEN_SHORT_ARRAY.
+ *
+ * @dataProvider dataIsShortList
+ * @covers \PHP_CodeSniffer\Util\Sniffs\TokenIs::isShortList
+ *
+ * @return void
+ */
+ public function testIsShortList($testMarker, $expected, $targetToken=T_OPEN_SHORT_ARRAY)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, $targetToken);
+ $result = TokenIs::isShortList(self::$phpcsFile, $stackPtr);
+
+ $this->assertSame($expected, $result);
+
+ }//end testIsShortList()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsShortList()
+ *
+ * @return array
+ */
+ public function dataIsShortList()
+ {
+ return [
+ [
+ '/* testLongList */',
+ false,
+ \T_LIST,
+ ],
+ [
+ '/* testArrayAssignment */',
+ false,
+ [
+ \T_OPEN_SHORT_ARRAY,
+ \T_OPEN_SQUARE_BRACKET,
+ ],
+ ],
+ [
+ '/* testNonNestedShortArray */',
+ false,
+ ],
+ [
+ '/* testNoAssignment */',
+ false,
+ ],
+ [
+ '/* testNestedNoAssignment */',
+ false,
+ ],
+ [
+ '/* testShortArrayInForeach */',
+ false,
+ ],
+ [
+ '/* testShortList */',
+ true,
+ ],
+ [
+ '/* testShortListDetectOnCloseBracket */',
+ true,
+ \T_CLOSE_SHORT_ARRAY,
+ ],
+ [
+ '/* testShortListWithNesting */',
+ true,
+ ],
+ [
+ '/* testNestedShortList */',
+ true,
+ ],
+ [
+ '/* testShortListInForeach */',
+ true,
+ ],
+ [
+ '/* testShortListInForeachWithKey */',
+ true,
+ ],
+ [
+ '/* testShortListInForeachNested */',
+ true,
+ ],
+ [
+ '/* testMultiAssignShortlist */',
+ true,
+ ],
+ [
+ '/* testShortListWithKeys */',
+ true,
+ ],
+ [
+ '/* testShortListInForeachWithKeysDetectOnCloseBracket */',
+ true,
+ \T_CLOSE_SHORT_ARRAY,
+ ],
+ [
+ '/* testNestedShortListEmpty */',
+ true,
+ ],
+ [
+ '/* testDeeplyNestedShortList */',
+ true,
+ ],
+ [
+ '/* testShortListWithNestingAndKeys */',
+ true,
+ ],
+ [
+ '/* testNestedShortListWithKeys_1 */',
+ true,
+ ],
+ [
+ '/* testNestedShortListWithKeys_2 */',
+ true,
+ ],
+ [
+ '/* testNestedShortListWithKeys_3 */',
+ true,
+ ],
+ [
+ '/* testShortListWithoutVars */',
+ true,
+ ],
+ [
+ '/* testShortListNestedLongList */',
+ true,
+ ],
+ [
+ '/* testNestedAnonClassWithTraitUseAs */',
+ false,
+ ],
+ [
+ '/* testParseError */',
+ false,
+ ],
+ [
+ '/* testLiveCoding */',
+ false,
+ [
+ \T_OPEN_SHORT_ARRAY,
+ \T_OPEN_SQUARE_BRACKET,
+ ],
+ ],
+ ];
+
+ }//end dataIsShortList()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusJSTest.js b/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusJSTest.js
new file mode 100644
index 0000000000..f67a181ca0
--- /dev/null
+++ b/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusJSTest.js
@@ -0,0 +1,23 @@
+/* testNonUnaryPlus */
+result = 1 + 2;
+
+/* testNonUnaryMinus */
+result = 1-2;
+
+/* testUnaryMinusColon */
+$.localScroll({offset: {top: -32}});
+
+switch (result) {
+ /* testUnaryMinusCase */
+ case -1:
+ break;
+}
+
+/* testUnaryMinusInlineIf */
+result = x?-y:z;
+
+/* testUnaryPlusInlineThen */
+result = x ? y : +z;
+
+/* testUnaryMinusInlineLogical */
+if (true || -1 == b) {}
diff --git a/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusJSTest.php b/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusJSTest.php
new file mode 100644
index 0000000000..327a7af007
--- /dev/null
+++ b/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusJSTest.php
@@ -0,0 +1,66 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\TokenIs;
+
+class IsUnaryPlusMinusJSTest extends IsUnaryPlusMinusTest
+{
+
+ /**
+ * The file extension of the test case file (without leading dot).
+ *
+ * @var string
+ */
+ protected static $fileExtension = 'js';
+
+
+ /**
+ * Data provider.
+ *
+ * @see IsUnaryPlusMinusTest::testIsUnaryPlusMinus()
+ *
+ * @return array
+ */
+ public function dataIsUnaryPlusMinus()
+ {
+ return [
+ [
+ '/* testNonUnaryPlus */',
+ false,
+ ],
+ [
+ '/* testNonUnaryMinus */',
+ false,
+ ],
+ [
+ '/* testUnaryMinusColon */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusCase */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusInlineIf */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusInlineThen */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusInlineLogical */',
+ true,
+ ],
+ ];
+
+ }//end dataIsUnaryPlusMinus()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusTest.inc b/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusTest.inc
new file mode 100644
index 0000000000..514b849259
--- /dev/null
+++ b/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusTest.inc
@@ -0,0 +1,124 @@
+
+ /* testUnaryPlusLongArrayAssignmentValue */
+ +20,
+);
+
+$array = [
+ /* testUnaryPlusShortArrayAssignment */
+ +1 =>
+ /* testNonUnaryMinusShortArrayAssignment */
+ 5-20,
+];
+
+/* testUnaryMinusCast */
+$a = (bool) -2;
+
+functionCall(
+ /* testUnaryPlusFunctionCallParam */
+ +2,
+ /* testUnaryMinusFunctionCallParam */
+ - 123.456,
+);
+
+switch ($a) {
+ /* testUnaryPlusCase */
+ case +20:
+ // Something.
+ break;
+
+ /* testUnaryMinusCase */
+ case -1.23:
+ // Something.
+ break;
+}
+
+// Testing `$a = -+-+10`;
+$a =
+ /* testSequenceNonUnary1 */
+ -
+ /* testSequenceNonUnary2 */
+ +
+ /* testSequenceNonUnary3 */
+ -
+ /* testSequenceUnaryEnd */
+ + /*comment*/ 10;
+
+// Intentional parse error. This has to be the last test in the file.
+/* testParseError */
+$a = -
diff --git a/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusTest.php b/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusTest.php
new file mode 100644
index 0000000000..bfe27ff2e3
--- /dev/null
+++ b/tests/Core/Util/Sniffs/TokenIs/IsUnaryPlusMinusTest.php
@@ -0,0 +1,222 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\TokenIs;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\TokenIs;
+
+class IsUnaryPlusMinusTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test that false is returned when a non-plus/minus token is passed.
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\TokenIs::isUnaryPlusMinus
+ *
+ * @return void
+ */
+ public function testNotPlusMinusToken()
+ {
+ $target = $this->getTargetToken('/* testNonUnaryPlus */', T_LNUMBER);
+ $this->assertFalse(TokenIs::isUnaryPlusMinus(self::$phpcsFile, $target));
+
+ }//end testNotPlusMinusToken()
+
+
+ /**
+ * Test whether a T_PLUS or T_MINUS token is a unary operator.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expected The expected boolean return value.
+ *
+ * @dataProvider dataIsUnaryPlusMinus
+ * @covers \PHP_CodeSniffer\Util\Sniffs\TokenIs::isUnaryPlusMinus
+ *
+ * @return void
+ */
+ public function testIsUnaryPlusMinus($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, [T_PLUS, T_MINUS]);
+ $result = TokenIs::isUnaryPlusMinus(self::$phpcsFile, $stackPtr);
+
+ $this->assertSame($expected, $result);
+
+ }//end testIsUnaryPlusMinus()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsUnaryPlusMinus()
+ *
+ * @return array
+ */
+ public function dataIsUnaryPlusMinus()
+ {
+ return [
+ [
+ '/* testNonUnaryPlus */',
+ false,
+ ],
+ [
+ '/* testNonUnaryMinus */',
+ false,
+ ],
+ [
+ '/* testNonUnaryPlusArrays */',
+ false,
+ ],
+ [
+ '/* testUnaryPlusIntAssignment */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusVariableAssignment */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusFloatAssignment */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusBoolAssignment */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusStringAssignmentWithComment */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusStringAssignment */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusNullAssignment */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusVariableVariableAssignment */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusIntComparison */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusIntComparisonYoda */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusFloatComparison */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusStringComparisonYoda */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusVariableLogical */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusVariableLogical */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusInlineIf */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusInlineThen */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusIntReturn */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusFloatReturn */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusArrayAccess */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusStringArrayAccess */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusLongArrayAssignment */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusLongArrayAssignmentKey */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusLongArrayAssignmentValue */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusShortArrayAssignment */',
+ true,
+ ],
+ [
+ '/* testNonUnaryMinusShortArrayAssignment */',
+ false,
+ ],
+ [
+ '/* testUnaryMinusCast */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusFunctionCallParam */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusFunctionCallParam */',
+ true,
+ ],
+ [
+ '/* testUnaryPlusCase */',
+ true,
+ ],
+ [
+ '/* testUnaryMinusCase */',
+ true,
+ ],
+ [
+ '/* testSequenceNonUnary1 */',
+ false,
+ ],
+ [
+ '/* testSequenceNonUnary2 */',
+ false,
+ ],
+ [
+ '/* testSequenceNonUnary3 */',
+ false,
+ ],
+ [
+ '/* testSequenceUnaryEnd */',
+ true,
+ ],
+ [
+ '/* testParseError */',
+ false,
+ ],
+ ];
+
+ }//end dataIsUnaryPlusMinus()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/UseStatements/SplitImportUseStatementTest.inc b/tests/Core/Util/Sniffs/UseStatements/SplitImportUseStatementTest.inc
new file mode 100644
index 0000000000..f09fc342f2
--- /dev/null
+++ b/tests/Core/Util/Sniffs/UseStatements/SplitImportUseStatementTest.inc
@@ -0,0 +1,76 @@
+
+
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\UseStatements;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\UseStatements;
+
+class SplitImportUseStatementTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a non-supported token is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_USE
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::splitImportUseStatement
+ *
+ * @return void
+ */
+ public function testInvalidTokenPassed()
+ {
+ // 0 = PHP open tag.
+ $result = UseStatements::splitImportUseStatement(self::$phpcsFile, 0);
+
+ }//end testInvalidTokenPassed()
+
+
+ /**
+ * Test receiving an expected exception when a non-import use statement token is passed.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be an import use statement
+ *
+ * @dataProvider dataNonImportUseTokenPassed
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::splitImportUseStatement
+ *
+ * @return void
+ */
+ public function testNonImportUseTokenPassed($testMarker)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_USE);
+ $result = UseStatements::splitImportUseStatement(self::$phpcsFile, $stackPtr);
+
+ }//end testNonImportUseTokenPassed()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testSplitImportUseStatement()
+ *
+ * @return array
+ */
+ public function dataNonImportUseTokenPassed()
+ {
+ return [
+ ['/* testClosureUse */'],
+ ['/* testTraitUse */'],
+ ];
+
+ }//end dataNonImportUseTokenPassed()
+
+
+ /**
+ * Test correctly splitting a T_USE statement into individual statements.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected return value of the function.
+ *
+ * @dataProvider dataSplitImportUseStatement
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::splitImportUseStatement
+ *
+ * @return void
+ */
+ public function testSplitImportUseStatement($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_USE);
+ $result = UseStatements::splitImportUseStatement(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testSplitImportUseStatement()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testSplitImportUseStatement()
+ *
+ * @return array
+ */
+ public function dataSplitImportUseStatement()
+ {
+ return [
+ [
+ '/* testUsePlain */',
+ [
+ 'name' => ['MyClass' => 'MyNamespace\MyClass'],
+ 'function' => [],
+ 'const' => [],
+ ],
+ ],
+ [
+ '/* testUsePlainAliased */',
+ [
+ 'name' => ['ClassAlias' => 'MyNamespace\YourClass'],
+ 'function' => [],
+ 'const' => [],
+ ],
+ ],
+ [
+ '/* testUseMultiple */',
+ [
+ 'name' => [
+ 'ClassABC' => 'Vendor\Foo\ClassA',
+ 'InterfaceB' => 'Vendor\Bar\InterfaceB',
+ 'ClassC' => 'Vendor\Baz\ClassC',
+ ],
+ 'function' => [],
+ 'const' => [],
+ ],
+ ],
+ [
+ '/* testUseFunctionPlainEndsOnCloseTag */',
+ [
+ 'name' => [],
+ 'function' => ['myFunction' => 'MyNamespace\myFunction'],
+ 'const' => [],
+ ],
+ ],
+ [
+ '/* testUseFunctionPlainAliased */',
+ [
+ 'name' => [],
+ 'function' => ['FunctionAlias' => 'Vendor\YourNamespace\const\yourFunction'],
+ 'const' => [],
+ ],
+ ],
+ [
+ '/* testUseFunctionMultiple */',
+ [
+ 'name' => [],
+ 'function' => [
+ 'sin' => 'foo\math\sin',
+ 'FooCos' => 'foo\math\cos',
+ 'cosh' => 'foo\math\cosh',
+ ],
+ 'const' => [],
+ ],
+ ],
+ [
+ '/* testUseConstPlainUppercaseConstKeyword */',
+ [
+ 'name' => [],
+ 'function' => [],
+ 'const' => ['MY_CONST' => 'MyNamespace\MY_CONST'],
+ ],
+ ],
+ [
+ '/* testUseConstPlainAliased */',
+ [
+ 'name' => [],
+ 'function' => [],
+ 'const' => ['CONST_ALIAS' => 'MyNamespace\YOUR_CONST'],
+ ],
+ ],
+ [
+ '/* testUseConstMultiple */',
+ [
+ 'name' => [],
+ 'function' => [],
+ 'const' => [
+ 'PI' => 'foo\math\PI',
+ 'MATH_GOLDEN' => 'foo\math\GOLDEN_RATIO',
+ ],
+ ],
+ ],
+ [
+ '/* testGroupUse */',
+ [
+ 'name' => [
+ 'SomeClassA' => 'some\namespacing\SomeClassA',
+ 'SomeClassB' => 'some\namespacing\deeper\level\SomeClassB',
+ 'C' => 'some\namespacing\another\level\SomeClassC',
+ ],
+ 'function' => [],
+ 'const' => [],
+ ],
+ ],
+ [
+ '/* testGroupUseFunctionTrailingComma */',
+ [
+ 'name' => [],
+ 'function' => [
+ 'Msin' => 'bar\math\Msin',
+ 'BarCos' => 'bar\math\level\Mcos',
+ 'Mcosh' => 'bar\math\Mcosh',
+ ],
+ 'const' => [],
+ ],
+ ],
+ [
+ '/* testGroupUseConst */',
+ [
+ 'name' => [],
+ 'function' => [],
+ 'const' => [
+ 'BAR_GAMMA' => 'bar\math\BGAMMA',
+ 'BGOLDEN_RATIO' => 'bar\math\BGOLDEN_RATIO',
+ ],
+ ],
+ ],
+ [
+ '/* testGroupUseMixed */',
+ [
+ 'name' => [
+ 'ClassName' => 'Some\NS\ClassName',
+ 'AnotherLevel' => 'Some\NS\AnotherLevel',
+ ],
+ 'function' => [
+ 'functionName' => 'Some\NS\SubLevel\functionName',
+ 'AnotherName' => 'Some\NS\SubLevel\AnotherName',
+ ],
+ 'const' => ['SOME_CONSTANT' => 'Some\NS\Constants\CONSTANT_NAME'],
+ ],
+ ],
+ [
+ '/* testParseError */',
+ [
+ 'name' => [],
+ 'function' => [],
+ 'const' => [],
+ ],
+ ],
+ ];
+
+ }//end dataSplitImportUseStatement()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/UseStatements/UseTypeTest.inc b/tests/Core/Util/Sniffs/UseStatements/UseTypeTest.inc
new file mode 100644
index 0000000000..cd0582ed00
--- /dev/null
+++ b/tests/Core/Util/Sniffs/UseStatements/UseTypeTest.inc
@@ -0,0 +1,53 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\UseStatements;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\UseStatements;
+
+class UseTypeTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when passing a non-existent token pointer.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_USE
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::getType
+ *
+ * @return void
+ */
+ public function testNonExistentToken()
+ {
+ $result = UseStatements::getType(self::$phpcsFile, 100000);
+
+ }//end testNonExistentToken()
+
+
+ /**
+ * Test receiving an expected exception when passing a non T_USE token.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_USE
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::getType
+ *
+ * @return void
+ */
+ public function testNonUseToken()
+ {
+ $result = UseStatements::getType(self::$phpcsFile, 0);
+
+ }//end testNonUseToken()
+
+
+ /**
+ * Test correctly identifying whether a T_USE token is used as a closure use statement.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected return values for the various functions.
+ *
+ * @dataProvider dataUseType
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::isClosureUse
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::getType
+ *
+ * @return void
+ */
+ public function testIsClosureUse($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_USE);
+
+ $result = UseStatements::isClosureUse(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected['closure'], $result);
+
+ }//end testIsClosureUse()
+
+
+ /**
+ * Test correctly identifying whether a T_USE token is used as an import use statement.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected return values for the various functions.
+ *
+ * @dataProvider dataUseType
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::isImportUse
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::getType
+ *
+ * @return void
+ */
+ public function testIsImportUse($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_USE);
+
+ $result = UseStatements::isImportUse(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected['import'], $result);
+
+ }//end testIsImportUse()
+
+
+ /**
+ * Test correctly identifying whether a T_USE token is used as a trait import use statement.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected The expected return values for the various functions.
+ *
+ * @dataProvider dataUseType
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::isTraitUse
+ * @covers \PHP_CodeSniffer\Util\Sniffs\UseStatements::getType
+ *
+ * @return void
+ */
+ public function testIsTraitUse($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_USE);
+
+ $result = UseStatements::isTraitUse(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected['trait'], $result, 'isTraitUseStatement() test failed');
+
+ }//end testIsTraitUse()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsClosureUse()
+ * @see testIsImportUse()
+ * @see testIsTraitUse()
+ *
+ * @return array
+ */
+ public function dataUseType()
+ {
+ return [
+ [
+ '/* testUseImport1 */',
+ [
+ 'closure' => false,
+ 'import' => true,
+ 'trait' => false,
+ ],
+ ],
+ [
+ '/* testUseImport2 */',
+ [
+ 'closure' => false,
+ 'import' => true,
+ 'trait' => false,
+ ],
+ ],
+ [
+ '/* testUseImport3 */',
+ [
+ 'closure' => false,
+ 'import' => true,
+ 'trait' => false,
+ ],
+ ],
+ [
+ '/* testUseImport4 */',
+ [
+ 'closure' => false,
+ 'import' => true,
+ 'trait' => false,
+ ],
+ ],
+ [
+ '/* testClosureUse */',
+ [
+ 'closure' => true,
+ 'import' => false,
+ 'trait' => false,
+ ],
+ ],
+ [
+ '/* testUseTrait */',
+ [
+ 'closure' => false,
+ 'import' => false,
+ 'trait' => true,
+ ],
+ ],
+ [
+ '/* testClosureUseNestedInClass */',
+ [
+ 'closure' => true,
+ 'import' => false,
+ 'trait' => false,
+ ],
+ ],
+ [
+ '/* testUseTraitInNestedAnonClass */',
+ [
+ 'closure' => false,
+ 'import' => false,
+ 'trait' => true,
+ ],
+ ],
+ [
+ '/* testUseTraitInTrait */',
+ [
+ 'closure' => false,
+ 'import' => false,
+ 'trait' => true,
+ ],
+ ],
+ [
+ '/* testClosureUseNestedInTrait */',
+ [
+ 'closure' => true,
+ 'import' => false,
+ 'trait' => false,
+ ],
+ ],
+ [
+ '/* testUseTraitInInterface */',
+ [
+ 'closure' => false,
+ 'import' => false,
+ 'trait' => false,
+ ],
+ ],
+ [
+ '/* testLiveCoding */',
+ [
+ 'closure' => false,
+ 'import' => false,
+ 'trait' => false,
+ ],
+ ],
+ ];
+
+ }//end dataUseType()
+
+
+}//end class
diff --git a/tests/Core/File/GetMemberPropertiesTest.inc b/tests/Core/Util/Sniffs/Variables/GetMemberPropertiesTest.inc
similarity index 98%
rename from tests/Core/File/GetMemberPropertiesTest.inc
rename to tests/Core/Util/Sniffs/Variables/GetMemberPropertiesTest.inc
index 614e3743da..4fee3b37bd 100644
--- a/tests/Core/File/GetMemberPropertiesTest.inc
+++ b/tests/Core/Util/Sniffs/Variables/GetMemberPropertiesTest.inc
@@ -94,7 +94,7 @@ class TestMemberProperties
/* testMethodParam */
public function methodName($param) {
/* testImportedGlobal */
- global $importedGlobal = true;
+ global $importedGlobal;
/* testLocalVariable */
$localVariable = true;
diff --git a/tests/Core/File/GetMemberPropertiesTest.php b/tests/Core/Util/Sniffs/Variables/GetMemberPropertiesTest.php
similarity index 74%
rename from tests/Core/File/GetMemberPropertiesTest.php
rename to tests/Core/Util/Sniffs/Variables/GetMemberPropertiesTest.php
index 597e96c306..226cf7576c 100644
--- a/tests/Core/File/GetMemberPropertiesTest.php
+++ b/tests/Core/Util/Sniffs/Variables/GetMemberPropertiesTest.php
@@ -1,87 +1,36 @@
- * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
+ * @copyright 2006-2019 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
-namespace PHP_CodeSniffer\Tests\Core\File;
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Variables;
-use PHP_CodeSniffer\Config;
-use PHP_CodeSniffer\Ruleset;
-use PHP_CodeSniffer\Files\DummyFile;
-use PHPUnit\Framework\TestCase;
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Variables;
-class GetMemberPropertiesTest extends TestCase
+class GetMemberPropertiesTest extends AbstractMethodUnitTest
{
- /**
- * The PHP_CodeSniffer_File object containing parsed contents of the test case file.
- *
- * @var \PHP_CodeSniffer\Files\File
- */
- private $phpcsFile;
-
-
- /**
- * Initialize & tokenize PHP_CodeSniffer_File with code from the test case file.
- *
- * Methods used for these tests can be found in a test case file in the same
- * directory and with the same name, using the .inc extension.
- *
- * @return void
- */
- public function setUp()
- {
- $config = new Config();
- $config->standards = ['Generic'];
-
- $ruleset = new Ruleset($config);
-
- $pathToTestFile = dirname(__FILE__).'/'.basename(__FILE__, '.php').'.inc';
- $this->phpcsFile = new DummyFile(file_get_contents($pathToTestFile), $ruleset, $config);
- $this->phpcsFile->process();
-
- }//end setUp()
-
-
- /**
- * Clean up after finished test.
- *
- * @return void
- */
- public function tearDown()
- {
- unset($this->phpcsFile);
-
- }//end tearDown()
-
/**
* Test the getMemberProperties() method.
*
- * @param string $identifier Comment which precedes the test case.
- * @param bool $expected Expected function output.
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param array $expected Expected function output.
*
* @dataProvider dataGetMemberProperties
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Variables::getMemberProperties
*
* @return void
*/
- public function testGetMemberProperties($identifier, $expected)
+ public function testGetMemberProperties($testMarker, $expected)
{
- $start = ($this->phpcsFile->numTokens - 1);
- $delim = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- $identifier
- );
- $variable = $this->phpcsFile->findNext(T_VARIABLE, ($delim + 1));
-
- $result = $this->phpcsFile->getMemberProperties($variable);
+ $variable = $this->getTargetToken($testMarker, T_VARIABLE);
+ $result = Variables::getMemberProperties(self::$phpcsFile, $variable);
$this->assertSame($expected, $result);
}//end testGetMemberProperties()
@@ -325,28 +274,20 @@ public function dataGetMemberProperties()
/**
* Test receiving an expected exception when a non property is passed.
*
- * @param string $identifier Comment which precedes the test case.
+ * @param string $testMarker The comment which prefaces the target token in the test file.
*
- * @expectedException PHP_CodeSniffer\Exceptions\TokenizerException
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
* @expectedExceptionMessage $stackPtr is not a class member var
*
* @dataProvider dataNotClassProperty
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Variables::getMemberProperties
*
* @return void
*/
- public function testNotClassPropertyException($identifier)
+ public function testNotClassPropertyException($testMarker)
{
- $start = ($this->phpcsFile->numTokens - 1);
- $delim = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- $identifier
- );
- $variable = $this->phpcsFile->findNext(T_VARIABLE, ($delim + 1));
-
- $result = $this->phpcsFile->getMemberProperties($variable);
+ $variable = $this->getTargetToken($testMarker, T_VARIABLE);
+ $result = Variables::getMemberProperties(self::$phpcsFile, $variable);
}//end testNotClassPropertyException()
@@ -375,24 +316,17 @@ public function dataNotClassProperty()
/**
* Test receiving an expected exception when a non variable is passed.
*
- * @expectedException PHP_CodeSniffer\Exceptions\TokenizerException
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
* @expectedExceptionMessage $stackPtr must be of type T_VARIABLE
*
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Variables::getMemberProperties
+ *
* @return void
*/
public function testNotAVariableException()
{
- $start = ($this->phpcsFile->numTokens - 1);
- $delim = $this->phpcsFile->findPrevious(
- T_COMMENT,
- $start,
- null,
- false,
- '/* testNotAVariable */'
- );
- $next = $this->phpcsFile->findNext(T_WHITESPACE, ($delim + 1), null, true);
-
- $result = $this->phpcsFile->getMemberProperties($next);
+ $next = $this->getTargetToken('/* testNotAVariable */', T_RETURN);
+ $result = Variables::getMemberProperties(self::$phpcsFile, $next);
}//end testNotAVariableException()
diff --git a/tests/Core/Util/Sniffs/Variables/IsForeachAsTest.inc b/tests/Core/Util/Sniffs/Variables/IsForeachAsTest.inc
new file mode 100644
index 0000000000..90f72cd53f
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Variables/IsForeachAsTest.inc
@@ -0,0 +1,41 @@
+ 10) {}
+
+/* testForeachWithoutAs */
+foreach($something) {} // Intentional parse error.
+
+/* testForeachVarBeforeAs */
+foreach($something as $k => $v) {}
+
+/* testForeachVarAfterAs */
+foreach($array as $something) {}
+
+/* testForeachVarAfterAsKey */
+foreach($array as $something => $value) {}
+
+/* testForeachVarAfterAsValue */
+foreach($array as $key => $something) {}
+
+/* testForeachVarAfterAsList */
+foreach($array as [$something, $else]) {}
+
+/* testNestedForeachVarAfterAs */
+add_action('something', function($array) {
+ foreach($array as $something) {}
+ return;
+});
+
+// Intentional parse error. This has to be the last test in the file.
+/* testParseError */
+foreach ($array as $something {}
diff --git a/tests/Core/Util/Sniffs/Variables/IsForeachAsTest.php b/tests/Core/Util/Sniffs/Variables/IsForeachAsTest.php
new file mode 100644
index 0000000000..35a101466d
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Variables/IsForeachAsTest.php
@@ -0,0 +1,116 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Variables;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Variables;
+
+class IsForeachAsTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test receiving an expected exception when a non variable is passed.
+ *
+ * @expectedException PHP_CodeSniffer\Exceptions\RuntimeException
+ * @expectedExceptionMessage $stackPtr must be of type T_VARIABLE
+ *
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Variables::isForeachAs
+ *
+ * @return void
+ */
+ public function testNotAVariableException()
+ {
+ $next = $this->getTargetToken('/* testNotAVariable */', T_RETURN);
+ $result = Variables::isForeachAs(self::$phpcsFile, $next);
+
+ }//end testNotAVariableException()
+
+
+ /**
+ * Test correctly identifying whether a T_VARIABLE token in the `as ...` part of a foreach statement.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expected The expected function return value.
+ *
+ * @dataProvider dataIsForeachAs
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Variables::isForeachAs
+ *
+ * @return void
+ */
+ public function testIsForeachAs($testMarker, $expected)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, T_VARIABLE, '$something');
+ $result = Variables::isForeachAs(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testIsForeachAs()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsForeachAs()
+ *
+ * @return array
+ */
+ public function dataIsForeachAs()
+ {
+ return [
+ [
+ '/* testNoParenthesis */',
+ false,
+ ],
+ [
+ '/* testNoParenthesisOwner */',
+ false,
+ ],
+ [
+ '/* testOwnerNotForeach */',
+ false,
+ ],
+ [
+ '/* testForeachWithoutAs */',
+ false,
+ ],
+ [
+ '/* testForeachVarBeforeAs */',
+ false,
+ ],
+ [
+ '/* testForeachVarAfterAs */',
+ true,
+ ],
+ [
+ '/* testForeachVarAfterAsKey */',
+ true,
+ ],
+ [
+ '/* testForeachVarAfterAsValue */',
+ true,
+ ],
+ [
+ '/* testForeachVarAfterAsList */',
+ true,
+ ],
+ [
+ '/* testNestedForeachVarAfterAs */',
+ true,
+ ],
+ [
+ '/* testParseError */',
+ false,
+ ],
+ ];
+
+ }//end dataIsForeachAs()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Variables/IsPHPReservedVarNameTest.php b/tests/Core/Util/Sniffs/Variables/IsPHPReservedVarNameTest.php
new file mode 100644
index 0000000000..11a183a562
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Variables/IsPHPReservedVarNameTest.php
@@ -0,0 +1,153 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Variables;
+
+use PHP_CodeSniffer\Util\Sniffs\Variables;
+use PHPUnit\Framework\TestCase;
+
+class IsPHPReservedVarNameTest extends TestCase
+{
+
+
+ /**
+ * Test valid PHP reserved variable names.
+ *
+ * @param string $name The variable name to test.
+ *
+ * @dataProvider dataIsPHPReservedVarName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Variables::isPHPReservedVarName
+ *
+ * @return void
+ */
+ public function testIsPHPReservedVarName($name)
+ {
+ $this->assertTrue(Variables::isPHPReservedVarName($name));
+
+ }//end testIsPHPReservedVarName()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsPHPReservedVarName()
+ *
+ * @return array
+ */
+ public function dataIsPHPReservedVarName()
+ {
+ return [
+ // With dollar sign.
+ ['$_SERVER'],
+ ['$_GET'],
+ ['$_POST'],
+ ['$_REQUEST'],
+ ['$_SESSION'],
+ ['$_ENV'],
+ ['$_COOKIE'],
+ ['$_FILES'],
+ ['$GLOBALS'],
+ ['$http_response_header'],
+ ['$argc'],
+ ['$argv'],
+ ['$HTTP_RAW_POST_DATA'],
+ ['$php_errormsg'],
+ ['$HTTP_SERVER_VARS'],
+ ['$HTTP_GET_VARS'],
+ ['$HTTP_POST_VARS'],
+ ['$HTTP_SESSION_VARS'],
+ ['$HTTP_ENV_VARS'],
+ ['$HTTP_COOKIE_VARS'],
+ ['$HTTP_POST_FILES'],
+
+ // Without dollar sign.
+ ['_SERVER'],
+ ['_GET'],
+ ['_POST'],
+ ['_REQUEST'],
+ ['_SESSION'],
+ ['_ENV'],
+ ['_COOKIE'],
+ ['_FILES'],
+ ['GLOBALS'],
+ ['http_response_header'],
+ ['argc'],
+ ['argv'],
+ ['HTTP_RAW_POST_DATA'],
+ ['php_errormsg'],
+ ['HTTP_SERVER_VARS'],
+ ['HTTP_GET_VARS'],
+ ['HTTP_POST_VARS'],
+ ['HTTP_SESSION_VARS'],
+ ['HTTP_ENV_VARS'],
+ ['HTTP_COOKIE_VARS'],
+ ['HTTP_POST_FILES'],
+ ];
+
+ }//end dataIsPHPReservedVarName()
+
+
+ /**
+ * Test non-reserved variable names.
+ *
+ * @param string $name The variable name to test.
+ *
+ * @dataProvider dataIsPHPReservedVarNameFalse
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Variables::isPHPReservedVarName
+ *
+ * @return void
+ */
+ public function testIsPHPReservedVarNameFalse($name)
+ {
+ $this->assertFalse(Variables::isPHPReservedVarName($name));
+
+ }//end testIsPHPReservedVarNameFalse()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsPHPReservedVarNameFalse()
+ *
+ * @return array
+ */
+ public function dataIsPHPReservedVarNameFalse()
+ {
+ return [
+ // Different case.
+ ['$_Server'],
+ ['$_get'],
+ ['$_pOST'],
+ ['$HTTP_RESPONSE_HEADER'],
+ ['_EnV'],
+ ['PHP_errormsg'],
+
+ // Shouldn't be possible, but all the same: double dollar.
+ ['$$_REQUEST'],
+
+ // No underscore.
+ ['$SERVER'],
+ ['SERVER'],
+
+ // Double underscore.
+ ['$__SERVER'],
+ ['__SERVER'],
+
+ // Globals with underscore.
+ ['$_GLOBALS'],
+ ['_GLOBALS'],
+
+ // Some completely different variable name.
+ ['my_php_errormsg'],
+ ];
+
+ }//end dataIsPHPReservedVarNameFalse()
+
+
+}//end class
diff --git a/tests/Core/Util/Sniffs/Variables/IsSuperglobalTest.inc b/tests/Core/Util/Sniffs/Variables/IsSuperglobalTest.inc
new file mode 100644
index 0000000000..9186bd658a
--- /dev/null
+++ b/tests/Core/Util/Sniffs/Variables/IsSuperglobalTest.inc
@@ -0,0 +1,41 @@
+
+ * @copyright 2019 Juliette Reinders Folmer. All rights reserved.
+ * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Util\Sniffs\Variables;
+
+use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
+use PHP_CodeSniffer\Util\Sniffs\Variables;
+
+class IsSuperglobalTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test correctly detecting superglobal variables.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param bool $expected The expected function return value.
+ * @param int|string $testTargetType Optional. The token type for the target token in the test file.
+ * @param string $testTargetValue Optional. The token content for the target token in the test file.
+ *
+ * @dataProvider dataIsSuperglobal
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Variables::isSuperglobal
+ *
+ * @return void
+ */
+ public function testIsSuperglobal($testMarker, $expected, $testTargetType=T_VARIABLE, $testTargetValue=null)
+ {
+ $stackPtr = $this->getTargetToken($testMarker, $testTargetType, $testTargetValue);
+ $result = Variables::isSuperglobal(self::$phpcsFile, $stackPtr);
+ $this->assertSame($expected, $result);
+
+ }//end testIsSuperglobal()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsSuperglobal()
+ *
+ * @return array
+ */
+ public function dataIsSuperglobal()
+ {
+ return [
+ [
+ '/* testNotAVariable */',
+ false,
+ T_RETURN,
+ ],
+ [
+ '/* testNotAReservedVar */',
+ false,
+ ],
+ [
+ '/* testReservedVarNotSuperglobal */',
+ false,
+ ],
+ [
+ '/* testReservedVarIsSuperglobal */',
+ true,
+ ],
+ [
+ '/* testGLOBALSArrayKeyNotAReservedVar */',
+ false,
+ T_CONSTANT_ENCAPSED_STRING,
+ ],
+ [
+ '/* testGLOBALSArrayKeyVar */',
+ false,
+ T_VARIABLE,
+ '$something',
+ ],
+ [
+ '/* testGLOBALSArrayKeyReservedVar */',
+ false,
+ T_VARIABLE,
+ '$php_errormsg',
+ ],
+ [
+ '/* testGLOBALSArrayKeySuperglobal */',
+ true,
+ T_VARIABLE,
+ '$_COOKIE',
+ ],
+ [
+ '/* testGLOBALSArrayKeyNotSingleString */',
+ false,
+ T_CONSTANT_ENCAPSED_STRING,
+ ],
+ [
+ '/* testGLOBALSArrayKeyInterpolatedVar */',
+ false,
+ T_DOUBLE_QUOTED_STRING,
+ ],
+ [
+ '/* testGLOBALSArrayKeySingleStringSuperglobal */',
+ true,
+ T_CONSTANT_ENCAPSED_STRING,
+ ],
+ [
+ '/* testGLOBALSArrayKeySuperglobalWithKey */',
+ true,
+ T_VARIABLE,
+ '$_GET',
+ ],
+ [
+ '/* testSuperglobalKeyNotGLOBALSArray */',
+ false,
+ T_CONSTANT_ENCAPSED_STRING,
+ ],
+ ];
+
+ }//end dataIsSuperglobal()
+
+
+ /**
+ * Test valid PHP superglobal names.
+ *
+ * @param string $name The variable name to test.
+ *
+ * @dataProvider dataIsSuperglobalName
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Variables::isSuperglobalName
+ *
+ * @return void
+ */
+ public function testIsSuperglobalName($name)
+ {
+ $this->assertTrue(Variables::isSuperglobalName($name));
+
+ }//end testIsSuperglobalName()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsSuperglobalName()
+ *
+ * @return array
+ */
+ public function dataIsSuperglobalName()
+ {
+ return [
+ ['$_SERVER'],
+ ['$_GET'],
+ ['$_POST'],
+ ['$_REQUEST'],
+ ['_SESSION'],
+ ['_ENV'],
+ ['_COOKIE'],
+ ['_FILES'],
+ ['GLOBALS'],
+ ];
+
+ }//end dataIsSuperglobalName()
+
+
+ /**
+ * Test non-superglobal variable names.
+ *
+ * @param string $name The variable name to test.
+ *
+ * @dataProvider dataIsSuperglobalNameFalse
+ * @covers \PHP_CodeSniffer\Util\Sniffs\Variables::isSuperglobalName
+ *
+ * @return void
+ */
+ public function testIsSuperglobalNameFalse($name)
+ {
+ $this->assertFalse(Variables::isSuperglobalName($name));
+
+ }//end testIsSuperglobalNameFalse()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testIsSuperglobalNameFalse()
+ *
+ * @return array
+ */
+ public function dataIsSuperglobalNameFalse()
+ {
+ return [
+ ['$not_a_superglobal'],
+ ['$http_response_header'],
+ ['$argc'],
+ ['$argv'],
+ ['$HTTP_RAW_POST_DATA'],
+ ['$php_errormsg'],
+ ['HTTP_SERVER_VARS'],
+ ['HTTP_GET_VARS'],
+ ['HTTP_POST_VARS'],
+ ['HTTP_SESSION_VARS'],
+ ['HTTP_ENV_VARS'],
+ ['HTTP_COOKIE_VARS'],
+ ['HTTP_POST_FILES'],
+ ];
+
+ }//end dataIsSuperglobalNameFalse()
+
+
+}//end class