diff --git a/PhpCollective/Sniffs/Classes/Psr4Sniff.php b/PhpCollective/Sniffs/Classes/Psr4Sniff.php index 850dfd9..1624d15 100644 --- a/PhpCollective/Sniffs/Classes/Psr4Sniff.php +++ b/PhpCollective/Sniffs/Classes/Psr4Sniff.php @@ -142,8 +142,8 @@ protected function addError( $phpcsFile->addError( sprintf( - 'Class name is not compliant with PSR-4 configuration. ' . - 'It should be `%s` instead of `%s`.', + 'Class name is not compliant with PSR-4 configuration. ' + . 'It should be `%s` instead of `%s`.', $result->getExpectedClassName(), $result->getActualClassName(), ), diff --git a/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php b/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php index b3e4c66..48c229d 100644 --- a/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php +++ b/PhpCollective/Sniffs/Commenting/DocBlockVarSniff.php @@ -11,6 +11,7 @@ use PHP_CodeSniffer\Util\Tokens; use PhpCollective\Sniffs\AbstractSniffs\AbstractSniff; use PhpCollective\Traits\CommentingTrait; +use PhpCollective\Traits\UseStatementsTrait; /** * Ensures Doc Blocks for variables exist and are correct. @@ -21,6 +22,7 @@ class DocBlockVarSniff extends AbstractSniff { use CommentingTrait; + use UseStatementsTrait; /** * @inheritDoc @@ -377,7 +379,7 @@ protected function handleDefaultValue( protected function handleTypes(File $phpCsFile, int $stackPointer, array $types, mixed $content, string $appendix, int $classNameIndex): void { foreach ($types as $type) { - if (str_contains($content, $type)) { + if ($this->typesMatch($phpCsFile, $content, $type)) { continue; } @@ -387,4 +389,40 @@ protected function handleTypes(File $phpCsFile, int $stackPointer, array $types, } } } + + /** + * Check if two types match, considering use statement aliases. + * + * @param \PHP_CodeSniffer\Files\File $phpCsFile + * @param string $docBlockType + * @param string $propertyType + * + * @return bool + */ + protected function typesMatch(File $phpCsFile, string $docBlockType, string $propertyType): bool + { + // Direct match + if (str_contains($docBlockType, $propertyType)) { + return true; + } + + // Check if property type is 'array' and doc block contains array syntax + if ($propertyType === 'array' && $this->containsTypeArray([$docBlockType])) { + return true; + } + + // Get use statements + $useStatements = $this->getUseStatements($phpCsFile); + + // Check if the property type is an alias + if (isset($useStatements[$propertyType])) { + $fullClassName = $useStatements[$propertyType]['fullName']; + // Check if doc block contains the full class name (with or without leading backslash) + if (str_contains($docBlockType, '\\' . $fullClassName) || str_contains($docBlockType, $fullClassName)) { + return true; + } + } + + return false; + } } diff --git a/PhpCollective/Sniffs/Commenting/FullyQualifiedClassNameInDocBlockSniff.php b/PhpCollective/Sniffs/Commenting/FullyQualifiedClassNameInDocBlockSniff.php index b67d381..dbad8e5 100644 --- a/PhpCollective/Sniffs/Commenting/FullyQualifiedClassNameInDocBlockSniff.php +++ b/PhpCollective/Sniffs/Commenting/FullyQualifiedClassNameInDocBlockSniff.php @@ -27,8 +27,25 @@ class FullyQualifiedClassNameInDocBlockSniff implements Sniff * @var array */ public static array $whitelistedTypes = [ - 'string', 'int', 'integer', 'float', 'bool', 'boolean', 'resource', 'null', 'void', 'callable', - 'array', 'iterable', 'mixed', 'object', 'false', 'true', 'self', 'static', '$this', + 'string', + 'int', + 'integer', + 'float', + 'bool', + 'boolean', + 'resource', + 'null', + 'void', + 'callable', + 'array', + 'iterable', + 'mixed', + 'object', + 'false', + 'true', + 'self', + 'static', + '$this', ]; /** diff --git a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php index 05eafce..5279e96 100644 --- a/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php +++ b/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniff.php @@ -24,6 +24,18 @@ */ class ArrayDeclarationSniff implements Sniff { + /** + * Controls when multi-line indentation rules are applied. + * + * Options: + * - 'assoc' (default): Only enforce one item per line for associative arrays + * - 'all': Enforce one item per line for all arrays (both associative and indexed) + * - 'none': Disable multi-line indentation checks + * + * @var string + */ + public string $multiLineIndentationMode = 'assoc'; + /** * @inheritDoc */ @@ -54,6 +66,7 @@ public function process(File $phpcsFile, $stackPtr): void $this->processSingleLineArray($phpcsFile, $arrayStart, $arrayEnd); } else { $this->processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd); + $this->processMultiLineIndentation($phpcsFile, $arrayStart, $arrayEnd); } } @@ -293,33 +306,6 @@ public function processMultiLineArray(File $phpcsFile, int $stackPtr, int $array } } - /* - Below the actual indentation of the array is checked. - Errors will be thrown when a key is not aligned, when - a double arrow is not aligned, and when a value is not - aligned correctly. - If an error is found in one of the above areas, then errors - are not reported for the rest of the line to avoid reporting - spaces and columns incorrectly. Often fixing the first - problem will fix the other 2 anyway. - - For example: - - $a = array( - 'index' => '2', - ); - - or - - $a = [ - 'index' => '2', - ]; - - In this array, the double arrow is indented too far, but this - will also cause an error in the value's alignment. If the arrow were - to be moved back one space however, then both errors would be fixed. - */ - $numValues = count($indices); foreach ($indices as $index) { @@ -356,4 +342,342 @@ public function processMultiLineArray(File $phpcsFile, int $stackPtr, int $array } } } + + protected function processMultiLineIndentation(File $phpcsFile, int $arrayStart, int $arrayEnd): void + { + // Early return if mode is 'none' + if ($this->multiLineIndentationMode === 'none') { + return; + } + + $tokens = $phpcsFile->getTokens(); + $pairs = []; + + $i = $arrayStart + 1; + while ($i < $arrayEnd) { + $token = $tokens[$i]; + + if (in_array($token['code'], Tokens::$emptyTokens, true)) { + $i++; + + continue; + } + + // Skip over nested structures (function calls, arrays, etc.) + if ($token['code'] === T_OPEN_PARENTHESIS && isset($token['parenthesis_closer'])) { + $i = $token['parenthesis_closer'] + 1; + + continue; + } + // Note: Nested arrays are processed separately by their own process() call, + // so we should skip them here to avoid double-processing + if ($token['code'] === T_OPEN_SHORT_ARRAY && isset($token['bracket_closer'])) { + $i = $token['bracket_closer'] + 1; + + continue; + } + if ($token['code'] === T_ARRAY && isset($token['parenthesis_closer'])) { + $i = $token['parenthesis_closer'] + 1; + + continue; + } + + // Handle key => value + if ($token['code'] === T_DOUBLE_ARROW) { + $keyEnd = $phpcsFile->findPrevious(T_WHITESPACE, $i - 1, $arrayStart, true); + if ($keyEnd === false) { + break; + } + $keyStart = $phpcsFile->findStartOfStatement($keyEnd); + + $valueStart = $phpcsFile->findNext(Tokens::$emptyTokens, $i + 1, $arrayEnd, true); + if ($valueStart === false) { + break; + } + + // Find the end of the value expression (handles function calls, etc.) + $valueEnd = $valueStart; + $depth = 0; + + for ($j = $valueStart; $j < $arrayEnd; $j++) { + $currentToken = $tokens[$j]; + + // Handle string literals + if (in_array($currentToken['code'], [T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_QUOTED_STRING], true)) { + $valueEnd = $j; + + continue; + } + + // Handle nested arrays - if we hit an array opener, skip to its closer + if ($currentToken['code'] === T_OPEN_SHORT_ARRAY && isset($currentToken['bracket_closer'])) { + $valueEnd = $currentToken['bracket_closer']; + $j = $valueEnd; + + continue; + } + if ($currentToken['code'] === T_ARRAY && isset($currentToken['parenthesis_closer'])) { + $valueEnd = $currentToken['parenthesis_closer']; + $j = $valueEnd; + + continue; + } + + // Track parentheses depth + if ($currentToken['code'] === T_OPEN_PARENTHESIS) { + $depth++; + } elseif ($currentToken['code'] === T_CLOSE_PARENTHESIS) { + $depth--; + } + + // Stop at comma when we're at depth 0 (not inside function call) + if ($currentToken['code'] === T_COMMA && $depth === 0) { + break; + } + + // Skip whitespace and comments when determining end + if (!in_array($currentToken['code'], Tokens::$emptyTokens, true)) { + $valueEnd = $j; + } + } + + $pairs[] = [ + 'key' => $keyStart, + 'arrow' => $i, + 'value' => $valueStart, + 'value_end' => $valueEnd, + 'line' => $tokens[$keyStart]['line'], + 'is_associative' => true, + ]; + + $i = $phpcsFile->findNext([T_COMMA], $valueEnd + 1, $arrayEnd); + if ($i === false) { + break; + } + + $i++; + + continue; + } + + // Handle single value (non-associative) + if ($token['code'] !== T_COMMA) { + // Check if this might be a key by looking ahead for => + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $i + 1, $arrayEnd, true); + if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === T_DOUBLE_ARROW) { + // This is actually a key, not a standalone value + // The T_DOUBLE_ARROW will be handled in the next iteration + $i++; + + continue; + } + // Find the end of this value expression (handles function calls, etc.) + $valueEnd = $i; + $depth = 0; + + for ($j = $i; $j < $arrayEnd; $j++) { + $currentToken = $tokens[$j]; + + // Handle string literals + if (in_array($currentToken['code'], [T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_QUOTED_STRING], true)) { + $valueEnd = $j; + + continue; + } + + // Handle nested arrays - if we hit an array opener, skip to its closer + if ($currentToken['code'] === T_OPEN_SHORT_ARRAY && isset($currentToken['bracket_closer'])) { + $valueEnd = $currentToken['bracket_closer']; + $j = $valueEnd; + + continue; + } + if ($currentToken['code'] === T_ARRAY && isset($currentToken['parenthesis_closer'])) { + $valueEnd = $currentToken['parenthesis_closer']; + $j = $valueEnd; + + continue; + } + + // Track parentheses depth + if ($currentToken['code'] === T_OPEN_PARENTHESIS) { + $depth++; + } elseif ($currentToken['code'] === T_CLOSE_PARENTHESIS) { + $depth--; + } + + // Stop at comma when we're at depth 0 (not inside function call) + if ($currentToken['code'] === T_COMMA && $depth === 0) { + break; + } + + // Skip whitespace and comments when determining end + if (!in_array($currentToken['code'], Tokens::$emptyTokens, true)) { + $valueEnd = $j; + } + } + + $pairs[] = [ + 'key' => null, + 'arrow' => null, + 'value' => $i, + 'value_end' => $valueEnd, + 'line' => $tokens[$i]['line'], + 'is_associative' => false, + ]; + + $i = $phpcsFile->findNext([T_COMMA], $valueEnd + 1, $arrayEnd); + if ($i === false) { + break; + } + $i++; + } else { + // Skip the comma and continue + $i++; + } + } + + // Group by line, but only if fully single-line expressions + $lineCounts = []; + foreach ($pairs as $pair) { + $startLine = $tokens[$pair['key'] ?? $pair['value']]['line']; + $endLine = $tokens[$pair['value_end'] ?: $pair['value']]['line']; + + if ($startLine === $endLine) { + $lineCounts[$startLine][] = $pair; + } + } + + foreach ($lineCounts as $line => $items) { + if (count($items) < 2) { + continue; + } + + // Check if we should process these items based on configuration + $shouldProcess = false; + if ($this->multiLineIndentationMode === 'all') { + $shouldProcess = true; + } else { + // In 'assoc' mode, only process if at least one item on this line is associative + foreach ($items as $item) { + if ($item['is_associative']) { + $shouldProcess = true; + + break; + } + } + } + + if (!$shouldProcess) { + continue; + } + + foreach ($items as $i => $pair) { + // In 'assoc' mode, only flag associative items + if ($this->multiLineIndentationMode === 'assoc' && !$pair['is_associative']) { + continue; + } + + $ptr = $pair['key'] ?? $pair['value']; + $error = 'Each array item must be on its own line in a multi-line array'; + $fix = $phpcsFile->addFixableError($error, $ptr, 'MultipleItemsPerLine'); + + if ($fix) { + // Calculate proper indentation by looking at existing properly indented items in this array + $baseIndent = ''; + + // Look for the first properly indented item in this array to match its indentation + for ($searchIdx = $arrayStart + 1; $searchIdx < $arrayEnd; $searchIdx++) { + if ( + $tokens[$searchIdx]['line'] > $tokens[$arrayStart]['line'] && + !in_array($tokens[$searchIdx]['code'], Tokens::$emptyTokens, true) + ) { + // Extract actual indentation from the line (only leading whitespace) + $lineStart = $phpcsFile->findFirstOnLine([], $searchIdx); + if ($lineStart !== false && $lineStart < $searchIdx) { + $indentContent = $phpcsFile->getTokensAsString($lineStart, $searchIdx - $lineStart); + // Only keep leading whitespace (tabs and spaces), remove any other characters + if (preg_match('/^[\t ]*/', $indentContent, $matches)) { + $baseIndent = $matches[0]; + } + } else { + // Fallback to column-based calculation + $indentLevel = $tokens[$searchIdx]['column'] - 1; + $baseIndent = str_repeat(' ', $indentLevel); + } + + break; + } + } + + // Fallback: detect indentation style and calculate based on array position + if ($baseIndent === '') { + // Detect if file uses tabs or spaces by looking at existing indentation + $usesTabs = false; + $indentSize = 4; // Default to 4 spaces + + // Scan the file to detect indentation style + $count = count($tokens); + for ($detectIdx = 0; $detectIdx < $count; $detectIdx++) { + if ( + $tokens[$detectIdx]['code'] === T_WHITESPACE && + $tokens[$detectIdx]['line'] !== $tokens[$detectIdx - 1]['line'] + ) { + $whitespace = $tokens[$detectIdx]['content']; + if (str_contains($whitespace, "\t")) { + $usesTabs = true; + + break; + } elseif (strlen($whitespace) > 0) { + // Count spaces to determine indent size + $spaceCount = strlen(str_replace(["\n", "\r"], '', $whitespace)); + if ($spaceCount > 0 && $spaceCount % 4 === 0) { + $indentSize = 4; + } elseif ($spaceCount > 0 && $spaceCount % 2 === 0) { + $indentSize = 2; + } + } + } + } + + // Calculate indentation based on array nesting + $arrayColumn = $tokens[$arrayStart]['column']; + $indentLevel = (int)(($arrayColumn + 3) / ($usesTabs ? 1 : $indentSize)); + + if ($usesTabs) { + $baseIndent = str_repeat("\t", $indentLevel); + } else { + $baseIndent = str_repeat(' ', $arrayColumn + 3); + } + } + + $phpcsFile->fixer->beginChangeset(); + foreach ($items as $j => $p) { + if ($j === 0) { + continue; + } + + // In 'assoc' mode, when we have mixed items on a line, we need to fix all of them + // Don't skip non-associative items when they're on the same line as associative ones + + $targetPtr = $p['key'] ?? $p['value']; + + // Find any whitespace before the target token and remove it + $prevToken = $targetPtr - 1; + while ($prevToken >= $arrayStart && in_array($tokens[$prevToken]['code'], [T_WHITESPACE, T_COMMA], true)) { + if ($tokens[$prevToken]['code'] === T_WHITESPACE) { + $phpcsFile->fixer->replaceToken($prevToken, ''); + } + $prevToken--; + } + + $phpcsFile->fixer->addContentBefore($targetPtr, "\n" . $baseIndent); + } + $phpcsFile->fixer->endChangeset(); + } + + break; // Only one error per line + } + } + } } diff --git a/PhpCollective/Sniffs/Internal/DisallowFunctionsSniff.php b/PhpCollective/Sniffs/Internal/DisallowFunctionsSniff.php index a0712ca..1fcdae3 100644 --- a/PhpCollective/Sniffs/Internal/DisallowFunctionsSniff.php +++ b/PhpCollective/Sniffs/Internal/DisallowFunctionsSniff.php @@ -42,8 +42,7 @@ class DisallowFunctionsSniff extends AbstractSniff /** * @var array> */ - protected static array $methods = [ - ]; + protected static array $methods = []; /** * @var bool|null diff --git a/PhpCollective/Sniffs/PHP/DeclareStrictTypesSniff.php b/PhpCollective/Sniffs/PHP/DeclareStrictTypesSniff.php index fcba959..aa21bc0 100644 --- a/PhpCollective/Sniffs/PHP/DeclareStrictTypesSniff.php +++ b/PhpCollective/Sniffs/PHP/DeclareStrictTypesSniff.php @@ -18,7 +18,13 @@ class DeclareStrictTypesSniff implements Sniff * @var array */ protected const TOKEN_CODES_TO_CHECK = [ - T_OPEN_PARENTHESIS, T_STRING, T_EQUAL, T_LNUMBER, T_CLOSE_PARENTHESIS, T_SEMICOLON, T_COMMENT, + T_OPEN_PARENTHESIS, + T_STRING, + T_EQUAL, + T_LNUMBER, + T_CLOSE_PARENTHESIS, + T_SEMICOLON, + T_COMMENT, ]; /** diff --git a/PhpCollective/Tools/Tokenizer.php b/PhpCollective/Tools/Tokenizer.php index 25008d1..3d27480 100644 --- a/PhpCollective/Tools/Tokenizer.php +++ b/PhpCollective/Tools/Tokenizer.php @@ -52,7 +52,7 @@ public function __construct(array $argv) throw new Exception('Please provide a valid file.'); } - $this->root = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR; + $this->root = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR; $this->path = $file; $this->verbose = !empty($argv[2]) && in_array($argv[2], ['--verbose', '-v']); } diff --git a/PhpCollective/ruleset.xml b/PhpCollective/ruleset.xml index 1530cbe..a45bd66 100644 --- a/PhpCollective/ruleset.xml +++ b/PhpCollective/ruleset.xml @@ -18,7 +18,7 @@ *\.txt *\.json - + @@ -250,6 +250,15 @@ + + + + + + + + + diff --git a/composer.json b/composer.json index a9f15ff..44713cf 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ ], "require": { "php": ">=8.1", + "phpcsstandards/phpcsextra": "^1.2.0", "slevomat/coding-standard": "^8.16.0", "squizlabs/php_codesniffer": "^3.11.3" }, diff --git a/docs/sniffs.md b/docs/sniffs.md index 4c782a0..8073708 100644 --- a/docs/sniffs.md +++ b/docs/sniffs.md @@ -1,10 +1,11 @@ # PhpCollective Code Sniffer -The PhpCollectiveStrict standard contains 225 sniffs +The PhpCollectiveStrict standard contains 233 sniffs -Generic (26 sniffs) +Generic (27 sniffs) ------------------- +- Generic.Arrays.ArrayIndent - Generic.Arrays.DisallowLongArraySyntax - Generic.CodeAnalysis.ForLoopShouldBeWhileLoop - Generic.CodeAnalysis.ForLoopWithTestFunctionCall @@ -32,6 +33,14 @@ Generic (26 sniffs) - Generic.WhiteSpace.LanguageConstructSpacing - Generic.WhiteSpace.ScopeIndent +Modernize (1 sniff) +------------------- +- Modernize.FunctionCalls.Dirname + +NormalizedArrays (1 sniff) +-------------------------- +- NormalizedArrays.Arrays.ArrayBraceSpacing + PEAR (4 sniffs) --------------- - PEAR.Classes.ClassDeclaration @@ -254,6 +263,14 @@ Squiz (27 sniffs) - Squiz.WhiteSpace.SemicolonSpacing - Squiz.WhiteSpace.SuperfluousWhitespace +Universal (5 sniffs) +-------------------- +- Universal.Constants.LowercaseClassResolutionKeyword +- Universal.Constants.UppercaseMagicConstants +- Universal.Operators.ConcatPosition +- Universal.UseStatements.NoUselessAliases +- Universal.WhiteSpace.PrecisionAlignment + Zend (1 sniff) -------------- - Zend.Files.ClosingTag diff --git a/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php b/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php new file mode 100644 index 0000000..ccd6e18 --- /dev/null +++ b/tests/PhpCollective/Sniffs/Formatting/ArrayDeclarationSniffTest.php @@ -0,0 +1,30 @@ +assertSnifferFindsErrors(new ArrayDeclarationSniff(), 4); + } + + /** + * @return void + */ + public function testDocBlockConstFixer(): void + { + $this->assertSnifferCanFixErrors(new ArrayDeclarationSniff()); + } +} diff --git a/tests/_data/ArrayDeclaration/after.php b/tests/_data/ArrayDeclaration/after.php new file mode 100644 index 0000000..b5b9646 --- /dev/null +++ b/tests/_data/ArrayDeclaration/after.php @@ -0,0 +1,40 @@ + 'b' . 'c', + 'x' => [ + 'y' => 'z', + 'z' => 'a', + ], + 'c' => __FILE__, + 'd' => Xyz::class, + 'content' => $this->getContent($tokens, $i, $tagEnd), + ]; + + // Test case for nested arrays - should NOT be flagged + $config = [ + 'levels' => ['notice', 'info', 'debug'], + 'other' => 'value', + ]; + + // Multiple items with nested arrays - SHOULD be flagged + $multi = [ + 'first' => ['a', 'b'], + 'second' => ['c', 'd'], + ]; + + // Mixed associative and non-associative items - SHOULD be flagged + $url = [ + 'controller' => 'ControllerName', + 'action' => 'view', + $uuid, + '?' => ['pdf' => 1], + ]; + } +} diff --git a/tests/_data/ArrayDeclaration/before.php b/tests/_data/ArrayDeclaration/before.php new file mode 100644 index 0000000..295d88b --- /dev/null +++ b/tests/_data/ArrayDeclaration/before.php @@ -0,0 +1,35 @@ + 'b' . 'c', + 'x' => [ + 'y' => 'z', 'z' => 'a', + ], + 'c' => __FILE__, 'd' => Xyz::class, 'content' => $this->getContent($tokens, $i, $tagEnd), + ]; + + // Test case for nested arrays - should NOT be flagged + $config = [ + 'levels' => ['notice', 'info', 'debug'], + 'other' => 'value', + ]; + + // Multiple items with nested arrays - SHOULD be flagged + $multi = [ + 'first' => ['a', 'b'], 'second' => ['c', 'd'], + ]; + + // Mixed associative and non-associative items - SHOULD be flagged + $url = [ + 'controller' => 'ControllerName', + 'action' => 'view', $uuid, + '?' => ['pdf' => 1], + ]; + } +} diff --git a/tests/_data/DocBlockVar/after.php b/tests/_data/DocBlockVar/after.php index 481acd3..341ff37 100644 --- a/tests/_data/DocBlockVar/after.php +++ b/tests/_data/DocBlockVar/after.php @@ -2,6 +2,10 @@ namespace PhpCollective; +use Tools\Mailer\Message as MailerMessage; +use Some\Other\Class; +use Yet\Another\Thing as AnotherAlias; + class FixMe { protected $x = []; @@ -28,5 +32,42 @@ class FixMe * * @var list */ - protected $left = []; + protected array $left = []; + + /** + * @var \Tools\Mailer\Message + */ + protected MailerMessage $message; + + /** + * @var \Some\Other\MyClass|null + */ + protected ?MyClass $class; + + /** + * @var \Yet\Another\Thing + */ + protected AnotherAlias $another; + + /** + * @var string[] + */ + protected array $fixtures = [ + 'plugin.QueueScheduler.SchedulerRows', + ]; + + /** + * @var int[] + */ + protected array $numbers = [1, 2, 3]; + + /** + * @var array + */ + protected array $config = []; + + /** + * @var MyClass[] + */ + protected array $objects; } diff --git a/tests/_data/DocBlockVar/before.php b/tests/_data/DocBlockVar/before.php index 481acd3..341ff37 100644 --- a/tests/_data/DocBlockVar/before.php +++ b/tests/_data/DocBlockVar/before.php @@ -2,6 +2,10 @@ namespace PhpCollective; +use Tools\Mailer\Message as MailerMessage; +use Some\Other\Class; +use Yet\Another\Thing as AnotherAlias; + class FixMe { protected $x = []; @@ -28,5 +32,42 @@ class FixMe * * @var list */ - protected $left = []; + protected array $left = []; + + /** + * @var \Tools\Mailer\Message + */ + protected MailerMessage $message; + + /** + * @var \Some\Other\MyClass|null + */ + protected ?MyClass $class; + + /** + * @var \Yet\Another\Thing + */ + protected AnotherAlias $another; + + /** + * @var string[] + */ + protected array $fixtures = [ + 'plugin.QueueScheduler.SchedulerRows', + ]; + + /** + * @var int[] + */ + protected array $numbers = [1, 2, 3]; + + /** + * @var array + */ + protected array $config = []; + + /** + * @var MyClass[] + */ + protected array $objects; }