West/Sniffs/Commenting/FunctionCommentSniff.php
protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
    {
        if ($this->phpVersion === null) {
            $this->phpVersion = Config::getConfigData('php_version');
            if ($this->phpVersion === null) {
                $this->phpVersion = PHP_VERSION_ID;
            }
        }

        $tokens = $phpcsFile->getTokens();

        // if the comment contains a single tag; if it's @see or @inheritdoc
        // we can skip the rest of the comment validation
        if (count($tokens[$commentStart]['comment_tags']) === 1) {
            $allowedTokens = ['@see', '@inheritdoc'];
            $commentToken = $tokens[$tokens[$commentStart]['comment_tags'][0]];
            if (in_array(strtolower($commentToken['content']), $allowedTokens)) {
                return;
            }
        }

        $params  = [];
        $maxType = 0;
        $maxVar  = 0;
        foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) {
            if ($tokens[$tag]['content'] !== '@param') {
                continue;
            }

            $type         = '';
            $typeSpace    = 0;
            $var          = '';
            $varSpace     = 0;
            $comment      = '';
            $commentLines = [];
            if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) {
                $matches = [];
                preg_match('/([^$&.]+)(?:((?:\.\.\.)?(?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/', $tokens[($tag + 2)]['content'], $matches);

                if (empty($matches) === false) {
                    $typeLen   = strlen($matches[1]);
                    $type      = trim($matches[1]);
                    $typeSpace = ($typeLen - strlen($type));
                    $typeLen   = strlen($type);
                    if ($typeLen > $maxType) {
                        $maxType = $typeLen;
                    }
                }

                if (isset($matches[2]) === true) {
                    $var    = $matches[2];
                    $varLen = strlen($var);
                    if ($varLen > $maxVar) {
                        $maxVar = $varLen;
                    }

                    if (isset($matches[4]) === true) {
                        $varSpace       = strlen($matches[3]);
                        $comment        = $matches[4];
                        $commentLines[] = [
                            'comment' => $comment,
                            'token'   => ($tag + 2),
                            'indent'  => $varSpace,
                        ];

                        // Any strings until the next tag belong to this comment.
                        if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) {
                            $end = $tokens[$commentStart]['comment_tags'][($pos + 1)];
                        } else {
                            $end = $tokens[$commentStart]['comment_closer'];
                        }

                        for ($i = ($tag + 3); $i < $end; $i++) {
                            if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
                                $indent = 0;
                                if ($tokens[($i - 1)]['code'] === T_DOC_COMMENT_WHITESPACE) {
                                    $indent = strlen($tokens[($i - 1)]['content']);
                                }

                                $comment       .= ' '.$tokens[$i]['content'];
                                $commentLines[] = [
                                    'comment' => $tokens[$i]['content'],
                                    'token'   => $i,
                                    'indent'  => $indent,
                                ];
                            }
                        }
                    } else {
                        $error = 'Missing parameter comment';
                        $phpcsFile->addError($error, $tag, 'MissingParamComment');
                        $commentLines[] = ['comment' => ''];
                    }
                } else {
                    $error = 'Missing parameter name';
                    $phpcsFile->addError($error, $tag, 'MissingParamName');
                }
            } else {
                $error = 'Missing parameter type';
                $phpcsFile->addError($error, $tag, 'MissingParamType');
            }

            $params[] = [
                'tag'          => $tag,
                'type'         => $type,
                'var'          => $var,
                'comment'      => $comment,
                'commentLines' => $commentLines,
                'type_space'   => $typeSpace,
                'var_space'    => $varSpace,
            ];
        }

        $realParams  = $phpcsFile->getMethodParameters($stackPtr);
        $foundParams = [];

        // We want to use ... for all variable length arguments, so added
        // this prefix to the variable name so comparisons are easier.
        foreach ($realParams as $pos => $param) {
            if ($param['variable_length'] === true) {
                $realParams[$pos]['name'] = '...'.$realParams[$pos]['name'];
            }
        }

        foreach ($params as $pos => $param) {
            // If the type is empty, the whole line is empty.
            if ($param['type'] === '') {
                continue;
            }

            // Check the param type value.
            $typeNames          = explode('|', $param['type']);
            $suggestedTypeNames = [];

            foreach ($typeNames as $typeName) {
                // Strip nullable operator.
                if ($typeName[0] === '?') {
                    $typeName = substr($typeName, 1);
                }

                $suggestedName        = self::suggestType($typeName);
                $suggestedTypeNames[] = $suggestedName;

                if (count($typeNames) > 1) {
                    continue;
                }

                // Check type hint for array and custom type.
                $suggestedTypeHint = '';
                if (strpos($suggestedName, 'array') !== false || substr($suggestedName, -2) === '[]') {
                    $suggestedTypeHint = 'array';
                } else if (strpos($suggestedName, 'callable') !== false) {
                    $suggestedTypeHint = 'callable';
                } else if (strpos($suggestedName, 'callback') !== false) {
                    $suggestedTypeHint = 'callable';
                } else if (in_array($suggestedName, self::$allowedTypes) === false) {
                    $suggestedTypeHint = $suggestedName;
                }

                if ($this->phpVersion >= 70000) {
                    if ($suggestedName === 'string') {
                        $suggestedTypeHint = 'string';
                    } else if ($suggestedName === 'int' || $suggestedName === 'integer') {
                        $suggestedTypeHint = 'int';
                    } else if ($suggestedName === 'float') {
                        $suggestedTypeHint = 'float';
                    } else if ($suggestedName === 'bool' || $suggestedName === 'boolean') {
                        $suggestedTypeHint = 'bool';
                    }
                }

                if ($this->phpVersion >= 70200) {
                    if ($suggestedName === 'object') {
                        $suggestedTypeHint = 'object';
                    }
                }

                if ($suggestedTypeHint === '' && isset($realParams[$pos]) === true) {
                    $typeHint = $realParams[$pos]['type_hint'];
                    if ($typeHint !== '') {
                        $error = 'Unknown type hint "%s" found for %s';
                        $data  = [
                            $typeHint,
                            $param['var'],
                        ];
                        $phpcsFile->addError($error, $stackPtr, 'InvalidTypeHint', $data);
                    }
                }
            }

            $suggestedType = implode($suggestedTypeNames, '|');
            if ($param['type'] !== $suggestedType) {
                $error = 'Expected "%s" but found "%s" for parameter type';
                $data  = [
                    $suggestedType,
                    $param['type'],
                ];

                $fix = $phpcsFile->addFixableError($error, $param['tag'], 'IncorrectParamVarName', $data);
                if ($fix === true) {
                    $phpcsFile->fixer->beginChangeset();

                    $content  = $suggestedType;
                    $content .= str_repeat(' ', $param['type_space']);
                    $content .= $param['var'];
                    $content .= str_repeat(' ', $param['var_space']);
                    if (isset($param['commentLines'][0]) === true) {
                        $content .= $param['commentLines'][0]['comment'];
                    }

                    $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content);

                    // Fix up the indent of additional comment lines.
                    foreach ($param['commentLines'] as $lineNum => $line) {
                        if ($lineNum === 0
                            || $param['commentLines'][$lineNum]['indent'] === 0
                        ) {
                            continue;
                        }

                        $diff      = (strlen($param['type']) - strlen($suggestedType));
                        $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff);
                        $phpcsFile->fixer->replaceToken(
                            ($param['commentLines'][$lineNum]['token'] - 1),
                            str_repeat(' ', $newIndent)
                        );
                    }

                    $phpcsFile->fixer->endChangeset();
                }
            }

            if ($param['var'] === '') {
                continue;
            }

            $foundParams[] = $param['var'];

            // Check number of spaces after the type.
            $this->checkSpacingAfterParamType($phpcsFile, $param, $maxType);

            // Make sure the param name is correct.
            if (isset($realParams[$pos]) === true) {
                $realName = $realParams[$pos]['name'];
                if ($realName !== $param['var']) {
                    $code = 'ParamNameNoMatch';
                    $data = [
                        $param['var'],
                        $realName,
                    ];

                    $error = 'Doc comment for parameter %s does not match ';
                    if (strtolower($param['var']) === strtolower($realName)) {
                        $error .= 'case of ';
                        $code   = 'ParamNameNoCaseMatch';
                    }

                    $error .= 'actual variable name %s';

                    $phpcsFile->addError($error, $param['tag'], $code, $data);
                }
            } else if (substr($param['var'], -4) !== ',...') {
                // We must have an extra parameter comment.
                $error = 'Superfluous parameter comment';
                $phpcsFile->addError($error, $param['tag'], 'ExtraParamComment');
            }

            if ($param['comment'] === '') {
                continue;
            }

            // Check number of spaces after the var name.
            $this->checkSpacingAfterParamName($phpcsFile, $param, $maxVar);

            // Param comments must start with a capital letter and end with the full stop.
            if (preg_match('/^(\p{Ll}|\P{L})/u', $param['comment']) === 1) {
                $error = 'Parameter comment must start with a capital letter';
                $phpcsFile->addError($error, $param['tag'], 'ParamCommentNotCapital');
            }

            $lastChar = substr($param['comment'], -1);
            if ($lastChar !== '.') {
                $error = 'Parameter comment must end with a full stop';
                $phpcsFile->addError($error, $param['tag'], 'ParamCommentFullStop');
            }
        }

        $realNames = [];
        foreach ($realParams as $realParam) {
            $realNames[] = $realParam['name'];
        }

        // Report missing comments.
        $diff = array_diff($realNames, $foundParams);
        foreach ($diff as $neededParam) {
            $error = 'Doc comment for parameter "%s" missing';
            $data  = [$neededParam];
            $phpcsFile->addError($error, $commentStart, 'MissingParamTag', $data);
        }
    }
protected function processParams(File $phpcsFile, $stackPtr, $commentStart)
    {
        if ($this->phpVersion === null) {
            $this->phpVersion = Config::getConfigData('php_version');
            if ($this->phpVersion === null) {
                $this->phpVersion = PHP_VERSION_ID;
            }
        }

        $tokens = $phpcsFile->getTokens();

        // if the comment contains a single tag; if it's @see or @inheritdoc
        // we can skip the rest of the comment validation
        if (count($tokens[$commentStart]['comment_tags']) === 1) {
            $allowedTokens = ['@see', '@inheritdoc'];
            $commentToken = $tokens[$tokens[$commentStart]['comment_tags'][0]];
            if (in_array(strtolower($commentToken['content']), $allowedTokens)) {
                return;
            }
        }

        $params  = [];
        $maxType = 0;
        $maxVar  = 0;
        foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) {
            if ($tokens[$tag]['content'] !== '@param') {
                continue;
            }

            $type         = '';
            $typeSpace    = 0;
            $var          = '';
            $varSpace     = 0;
            $comment      = '';
            $commentLines = [];
            if ($tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING) {
                $matches = [];
                preg_match('/([^$&.]+)(?:((?:\.\.\.)?(?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/', $tokens[($tag + 2)]['content'], $matches);

                if (empty($matches) === false) {
                    $typeLen   = strlen($matches[1]);
                    $type      = trim($matches[1]);
                    $typeSpace = ($typeLen - strlen($type));
                    $typeLen   = strlen($type);
                    if ($typeLen > $maxType) {
                        $maxType = $typeLen;
                    }
                }

                if (isset($matches[2]) === true) {
                    $var    = $matches[2];
                    $varLen = strlen($var);
                    if ($varLen > $maxVar) {
                        $maxVar = $varLen;
                    }

                    if (isset($matches[4]) === true) {
                        $varSpace       = strlen($matches[3]);
                        $comment        = $matches[4];
                        $commentLines[] = [
                            'comment' => $comment,
                            'token'   => ($tag + 2),
                            'indent'  => $varSpace,
                        ];

                        // Any strings until the next tag belong to this comment.
                        if (isset($tokens[$commentStart]['comment_tags'][($pos + 1)]) === true) {
                            $end = $tokens[$commentStart]['comment_tags'][($pos + 1)];
                        } else {
                            $end = $tokens[$commentStart]['comment_closer'];
                        }

                        for ($i = ($tag + 3); $i < $end; $i++) {
                            if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
                                $indent = 0;
                                if ($tokens[($i - 1)]['code'] === T_DOC_COMMENT_WHITESPACE) {
                                    $indent = strlen($tokens[($i - 1)]['content']);
                                }

                                $comment       .= ' '.$tokens[$i]['content'];
                                $commentLines[] = [
                                    'comment' => $tokens[$i]['content'],
                                    'token'   => $i,
                                    'indent'  => $indent,
                                ];
                            }
                        }
                    } else {
                        $error = 'Missing parameter comment';
                        $phpcsFile->addError($error, $tag, 'MissingParamComment');
                        $commentLines[] = ['comment' => ''];
                    }
                } else {
                    $error = 'Missing parameter name';
                    $phpcsFile->addError($error, $tag, 'MissingParamName');
                }
            } else {
                $error = 'Missing parameter type';
                $phpcsFile->addError($error, $tag, 'MissingParamType');
            }

            $params[] = [
                'tag'          => $tag,
                'type'         => $type,
                'var'          => $var,
                'comment'      => $comment,
                'commentLines' => $commentLines,
                'type_space'   => $typeSpace,
                'var_space'    => $varSpace,
            ];
        }

        $realParams  = $phpcsFile->getMethodParameters($stackPtr);
        $foundParams = [];

        // We want to use ... for all variable length arguments, so added
        // this prefix to the variable name so comparisons are easier.
        foreach ($realParams as $pos => $param) {
            if ($param['variable_length'] === true) {
                $realParams[$pos]['name'] = '...'.$realParams[$pos]['name'];
            }
        }

        foreach ($params as $pos => $param) {
            // If the type is empty, the whole line is empty.
            if ($param['type'] === '') {
                continue;
            }

            // Check the param type value.
            $typeNames          = explode('|', $param['type']);
            $suggestedTypeNames = [];

            foreach ($typeNames as $typeName) {
                // Strip nullable operator.
                if ($typeName[0] === '?') {
                    $typeName = substr($typeName, 1);
                }

                $suggestedName        = self::suggestType($typeName);
                $suggestedTypeNames[] = $suggestedName;

                if (count($typeNames) > 1) {
                    continue;
                }

                // Check type hint for array and custom type.
                $suggestedTypeHint = '';
                if (strpos($suggestedName, 'array') !== false || substr($suggestedName, -2) === '[]') {
                    $suggestedTypeHint = 'array';
                } else if (strpos($suggestedName, 'callable') !== false) {
                    $suggestedTypeHint = 'callable';
                } else if (strpos($suggestedName, 'callback') !== false) {
                    $suggestedTypeHint = 'callable';
                } else if (in_array($suggestedName, self::$allowedTypes) === false) {
                    $suggestedTypeHint = $suggestedName;
                }

                if ($this->phpVersion >= 70000) {
                    if ($suggestedName === 'string') {
                        $suggestedTypeHint = 'string';
                    } else if ($suggestedName === 'int' || $suggestedName === 'integer') {
                        $suggestedTypeHint = 'int';
                    } else if ($suggestedName === 'float') {
                        $suggestedTypeHint = 'float';
                    } else if ($suggestedName === 'bool' || $suggestedName === 'boolean') {
                        $suggestedTypeHint = 'bool';
                    }
                }

                if ($this->phpVersion >= 70200) {
                    if ($suggestedName === 'object') {
                        $suggestedTypeHint = 'object';
                    }
                }

                if ($suggestedTypeHint === '' && isset($realParams[$pos]) === true) {
                    $typeHint = $realParams[$pos]['type_hint'];
                    if ($typeHint !== '') {
                        $error = 'Unknown type hint "%s" found for %s';
                        $data  = [
                            $typeHint,
                            $param['var'],
                        ];
                        $phpcsFile->addError($error, $stackPtr, 'InvalidTypeHint', $data);
                    }
                }
            }

            $suggestedType = implode($suggestedTypeNames, '|');
            if ($param['type'] !== $suggestedType) {
                $error = 'Expected "%s" but found "%s" for parameter type';
                $data  = [
                    $suggestedType,
                    $param['type'],
                ];

                $fix = $phpcsFile->addFixableError($error, $param['tag'], 'IncorrectParamVarName', $data);
                if ($fix === true) {
                    $phpcsFile->fixer->beginChangeset();

                    $content  = $suggestedType;
                    $content .= str_repeat(' ', $param['type_space']);
                    $content .= $param['var'];
                    $content .= str_repeat(' ', $param['var_space']);
                    if (isset($param['commentLines'][0]) === true) {
                        $content .= $param['commentLines'][0]['comment'];
                    }

                    $phpcsFile->fixer->replaceToken(($param['tag'] + 2), $content);

                    // Fix up the indent of additional comment lines.
                    foreach ($param['commentLines'] as $lineNum => $line) {
                        if ($lineNum === 0
                            || $param['commentLines'][$lineNum]['indent'] === 0
                        ) {
                            continue;
                        }

                        $diff      = (strlen($param['type']) - strlen($suggestedType));
                        $newIndent = ($param['commentLines'][$lineNum]['indent'] - $diff);
                        $phpcsFile->fixer->replaceToken(
                            ($param['commentLines'][$lineNum]['token'] - 1),
                            str_repeat(' ', $newIndent)
                        );
                    }

                    $phpcsFile->fixer->endChangeset();
                }
            }

            if ($param['var'] === '') {
                continue;
            }

            $foundParams[] = $param['var'];

            // Check number of spaces after the type.
            $this->checkSpacingAfterParamType($phpcsFile, $param, $maxType);

            // Make sure the param name is correct.
            if (isset($realParams[$pos]) === true) {
                $realName = $realParams[$pos]['name'];
                if ($realName !== $param['var']) {
                    $code = 'ParamNameNoMatch';
                    $data = [
                        $param['var'],
                        $realName,
                    ];

                    $error = 'Doc comment for parameter %s does not match ';
                    if (strtolower($param['var']) === strtolower($realName)) {
                        $error .= 'case of ';
                        $code   = 'ParamNameNoCaseMatch';
                    }

                    $error .= 'actual variable name %s';

                    $phpcsFile->addError($error, $param['tag'], $code, $data);
                }
            } else if (substr($param['var'], -4) !== ',...') {
                // We must have an extra parameter comment.
                $error = 'Superfluous parameter comment';
                $phpcsFile->addError($error, $param['tag'], 'ExtraParamComment');
            }

            if ($param['comment'] === '') {
                continue;
            }

            // Check number of spaces after the var name.
            $this->checkSpacingAfterParamName($phpcsFile, $param, $maxVar);

            // Param comments must start with a capital letter and end with the full stop.
            if (preg_match('/^(\p{Ll}|\P{L})/u', $param['comment']) === 1) {
                $error = 'Parameter comment must start with a capital letter';
                $phpcsFile->addError($error, $param['tag'], 'ParamCommentNotCapital');
            }

            $lastChar = substr($param['comment'], -1);
            if ($lastChar !== '.') {
                $error = 'Parameter comment must end with a full stop';
                $phpcsFile->addError($error, $param['tag'], 'ParamCommentFullStop');
            }
        }

        $realNames = [];
        foreach ($realParams as $realParam) {
            $realNames[] = $realParam['name'];
        }

        // Report missing comments.
        $diff = array_diff($realNames, $foundParams);
        foreach ($diff as $neededParam) {
            $error = 'Doc comment for parameter "%s" missing';
            $data  = [$neededParam];
            $phpcsFile->addError($error, $commentStart, 'MissingParamTag', $data);
        }
    }
protected function processParams ( File $ phpcsFile , $ stackPtr , $ commentStart ) { if ( $ this -> phpVersion === null ) { $ this -> phpVersion = Config :: getConfigData ( 'php_version' ) ; if ( $ this -> phpVersion === null ) { $ this -> phpVersion = PHP_VERSION_ID ; } } $ tokens = $ phpcsFile -> getTokens ( ) ; // if the comment contains a single tag; if it's @see or @inheritdoc // we can skip the rest of the comment validation if ( count ( $ tokens [ $ commentStart ] [ 'comment_tags' ] ) === 1 ) { $ allowedTokens = [ '@see' , '@inheritdoc' ] ; $ commentToken = $ tokens [ $ tokens [ $ commentStart ] [ 'comment_tags' ] [ 0 ] ] ; if ( in_array ( strtolower ( $ commentToken [ 'content' ] ) , $ allowedTokens ) ) { return ; } } $ params = [ ] ; $ maxType = 0 ; $ maxVar = 0 ; foreach ( $ tokens [ $ commentStart ] [ 'comment_tags' ] as $ pos => $ tag ) { if ( $ tokens [ $ tag ] [ 'content' ] !== '@param' ) { continue ; } $ type = '' ; $ typeSpace = 0 ; $ var = '' ; $ varSpace = 0 ; $ comment = '' ; $ commentLines = [ ] ; if ( $ tokens [ ( $ tag + 2 ) ] [ 'code' ] === T_DOC_COMMENT_STRING ) { $ matches = [ ] ; preg_match ( '/([^$&.]+)(?:((?:\.\.\.)?(?:\$|&)[^\s]+)(?:(\s+)(.*))?)?/' , $ tokens [ ( $ tag + 2 ) ] [ 'content' ] , $ matches ) ; if ( empty ( $ matches ) === false ) { $ typeLen = strlen ( $ matches [ 1 ] ) ; $ type = trim ( $ matches [ 1 ] ) ; $ typeSpace = ( $ typeLen - strlen ( $ type ) ) ; $ typeLen = strlen ( $ type ) ; if ( $ typeLen > $ maxType ) { $ maxType = $ typeLen ; } } if ( isset ( $ matches [ 2 ] ) === true ) { $ var = $ matches [ 2 ] ; $ varLen = strlen ( $ var ) ; if ( $ varLen > $ maxVar ) { $ maxVar = $ varLen ; } if ( isset ( $ matches [ 4 ] ) === true ) { $ varSpace = strlen ( $ matches [ 3 ] ) ; $ comment = $ matches [ 4 ] ; $ commentLines [ ] = [ 'comment' => $ comment , 'token' => ( $ tag + 2 ) , 'indent' => $ varSpace , ] ; // Any strings until the next tag belong to this comment. if ( isset ( $ tokens [ $ commentStart ] [ 'comment_tags' ] [ ( $ pos + 1 ) ] ) === true ) { $ end = $ tokens [ $ commentStart ] [ 'comment_tags' ] [ ( $ pos + 1 ) ] ; } else { $ end = $ tokens [ $ commentStart ] [ 'comment_closer' ] ; } for ( $ i = ( $ tag + 3 ) ; $ i < $ end ; $ i ++ ) { if ( $ tokens [ $ i ] [ 'code' ] === T_DOC_COMMENT_STRING ) { $ indent = 0 ; if ( $ tokens [ ( $ i - 1 ) ] [ 'code' ] === T_DOC_COMMENT_WHITESPACE ) { $ indent = strlen ( $ tokens [ ( $ i - 1 ) ] [ 'content' ] ) ; } $ comment .= ' ' . $ tokens [ $ i ] [ 'content' ] ; $ commentLines [ ] = [ 'comment' => $ tokens [ $ i ] [ 'content' ] , 'token' => $ i , 'indent' => $ indent , ] ; } } } else { $ error = 'Missing parameter comment' ; $ phpcsFile -> addError ( $ error , $ tag , 'MissingParamComment' ) ; $ commentLines [ ] = [ 'comment' => '' ] ; } } else { $ error = 'Missing parameter name' ; $ phpcsFile -> addError ( $ error , $ tag , 'MissingParamName' ) ; } } else { $ error = 'Missing parameter type' ; $ phpcsFile -> addError ( $ error , $ tag , 'MissingParamType' ) ; } $ params [ ] = [ 'tag' => $ tag , 'type' => $ type , 'var' => $ var , 'comment' => $ comment , 'commentLines' => $ commentLines , 'type_space' => $ typeSpace , 'var_space' => $ varSpace , ] ; } $ realParams = $ phpcsFile -> getMethodParameters ( $ stackPtr ) ; $ foundParams = [ ] ; // We want to use ... for all variable length arguments, so added // this prefix to the variable name so comparisons are easier. foreach ( $ realParams as $ pos => $ param ) { if ( $ param [ 'variable_length' ] === true ) { $ realParams [ $ pos ] [ 'name' ] = '...' . $ realParams [ $ pos ] [ 'name' ] ; } } foreach ( $ params as $ pos => $ param ) { // If the type is empty, the whole line is empty. if ( $ param [ 'type' ] === '' ) { continue ; } // Check the param type value. $ typeNames = explode ( '|' , $ param [ 'type' ] ) ; $ suggestedTypeNames = [ ] ; foreach ( $ typeNames as $ typeName ) { // Strip nullable operator. if ( $ typeName [ 0 ] === '?' ) { $ typeName = substr ( $ typeName , 1 ) ; } $ suggestedName = self :: suggestType ( $ typeName ) ; $ suggestedTypeNames [ ] = $ suggestedName ; if ( count ( $ typeNames ) > 1 ) { continue ; } // Check type hint for array and custom type. $ suggestedTypeHint = '' ; if ( strpos ( $ suggestedName , 'array' ) !== false || substr ( $ suggestedName , - 2 ) === '[]' ) { $ suggestedTypeHint = 'array' ; } else if ( strpos ( $ suggestedName , 'callable' ) !== false ) { $ suggestedTypeHint = 'callable' ; } else if ( strpos ( $ suggestedName , 'callback' ) !== false ) { $ suggestedTypeHint = 'callable' ; } else if ( in_array ( $ suggestedName , self :: $ allowedTypes ) === false ) { $ suggestedTypeHint = $ suggestedName ; } if ( $ this -> phpVersion >= 70000 ) { if ( $ suggestedName === 'string' ) { $ suggestedTypeHint = 'string' ; } else if ( $ suggestedName === 'int' || $ suggestedName === 'integer' ) { $ suggestedTypeHint = 'int' ; } else if ( $ suggestedName === 'float' ) { $ suggestedTypeHint = 'float' ; } else if ( $ suggestedName === 'bool' || $ suggestedName === 'boolean' ) { $ suggestedTypeHint = 'bool' ; } } if ( $ this -> phpVersion >= 70200 ) { if ( $ suggestedName === 'object' ) { $ suggestedTypeHint = 'object' ; } } if ( $ suggestedTypeHint === '' && isset ( $ realParams [ $ pos ] ) === true ) { $ typeHint = $ realParams [ $ pos ] [ 'type_hint' ] ; if ( $ typeHint !== '' ) { $ error = 'Unknown type hint "%s" found for %s' ; $ data = [ $ typeHint , $ param [ 'var' ] , ] ; $ phpcsFile -> addError ( $ error , $ stackPtr , 'InvalidTypeHint' , $ data ) ; } } } $ suggestedType = implode ( $ suggestedTypeNames , '|' ) ; if ( $ param [ 'type' ] !== $ suggestedType ) { $ error = 'Expected "%s" but found "%s" for parameter type' ; $ data = [ $ suggestedType , $ param [ 'type' ] , ] ; $ fix = $ phpcsFile -> addFixableError ( $ error , $ param [ 'tag' ] , 'IncorrectParamVarName' , $ data ) ; if ( $ fix === true ) { $ phpcsFile -> fixer -> beginChangeset ( ) ; $ content = $ suggestedType ; $ content .= str_repeat ( ' ' , $ param [ 'type_space' ] ) ; $ content .= $ param [ 'var' ] ; $ content .= str_repeat ( ' ' , $ param [ 'var_space' ] ) ; if ( isset ( $ param [ 'commentLines' ] [ 0 ] ) === true ) { $ content .= $ param [ 'commentLines' ] [ 0 ] [ 'comment' ] ; } $ phpcsFile -> fixer -> replaceToken ( ( $ param [ 'tag' ] + 2 ) , $ content ) ; // Fix up the indent of additional comment lines. foreach ( $ param [ 'commentLines' ] as $ lineNum => $ line ) { if ( $ lineNum === 0 || $ param [ 'commentLines' ] [ $ lineNum ] [ 'indent' ] === 0 ) { continue ; } $ diff = ( strlen ( $ param [ 'type' ] ) - strlen ( $ suggestedType ) ) ; $ newIndent = ( $ param [ 'commentLines' ] [ $ lineNum ] [ 'indent' ] - $ diff ) ; $ phpcsFile -> fixer -> replaceToken ( ( $ param [ 'commentLines' ] [ $ lineNum ] [ 'token' ] - 1 ) , str_repeat ( ' ' , $ newIndent ) ) ; } $ phpcsFile -> fixer -> endChangeset ( ) ; } } if ( $ param [ 'var' ] === '' ) { continue ; } $ foundParams [ ] = $ param [ 'var' ] ; // Check number of spaces after the type. $ this -> checkSpacingAfterParamType ( $ phpcsFile , $ param , $ maxType ) ; // Make sure the param name is correct. if ( isset ( $ realParams [ $ pos ] ) === true ) { $ realName = $ realParams [ $ pos ] [ 'name' ] ; if ( $ realName !== $ param [ 'var' ] ) { $ code = 'ParamNameNoMatch' ; $ data = [ $ param [ 'var' ] , $ realName , ] ; $ error = 'Doc comment for parameter %s does not match ' ; if ( strtolower ( $ param [ 'var' ] ) === strtolower ( $ realName ) ) { $ error .= 'case of ' ; $ code = 'ParamNameNoCaseMatch' ; } $ error .= 'actual variable name %s' ; $ phpcsFile -> addError ( $ error , $ param [ 'tag' ] , $ code , $ data ) ; } } else if ( substr ( $ param [ 'var' ] , - 4 ) !== ',...' ) { // We must have an extra parameter comment. $ error = 'Superfluous parameter comment' ; $ phpcsFile -> addError ( $ error , $ param [ 'tag' ] , 'ExtraParamComment' ) ; } if ( $ param [ 'comment' ] === '' ) { continue ; } // Check number of spaces after the var name. $ this -> checkSpacingAfterParamName ( $ phpcsFile , $ param , $ maxVar ) ; // Param comments must start with a capital letter and end with the full stop. if ( preg_match ( '/^(\p{Ll}|\P{L})/u' , $ param [ 'comment' ] ) === 1 ) { $ error = 'Parameter comment must start with a capital letter' ; $ phpcsFile -> addError ( $ error , $ param [ 'tag' ] , 'ParamCommentNotCapital' ) ; } $ lastChar = substr ( $ param [ 'comment' ] , - 1 ) ; if ( $ lastChar !== '.' ) { $ error = 'Parameter comment must end with a full stop' ; $ phpcsFile -> addError ( $ error , $ param [ 'tag' ] , 'ParamCommentFullStop' ) ; } } $ realNames = [ ] ; foreach ( $ realParams as $ realParam ) { $ realNames [ ] = $ realParam [ 'name' ] ; } // Report missing comments. $ diff = array_diff ( $ realNames , $ foundParams ) ; foreach ( $ diff as $ neededParam ) { $ error = 'Doc comment for parameter "%s" missing' ; $ data = [ $ neededParam ] ; $ phpcsFile -> addError ( $ error , $ commentStart , 'MissingParamTag' , $ data ) ; } }
{@inheritdoc}
{