<?php PHP_CodeSniffer::$allowedTypes = array_merge(PHP_CodeSniffer::$allowedTypes, array('bool', 'int')); /** * Parses and verifies the doc comments for functions. * * Verifies that : * <ul> * <li>A comment exists</li> * <li>There is a blank newline after the short description.</li> * <li>Parameter names represent those in the method.</li> * <li>Parameter comments are in the correct order</li> * <li>A type hint is provided for array and custom class</li> * <li>Type hint matches the actual variable/class type</li> * <li>A return type exists</li> * <li>There must be one blank line between body and headline comments.</li> * <li>Any throw tag must have an exception class.</li> * </ul> * * @author Jan Prachař <*****@*****.**> */ class wikidi_sniffs_commenting_FunctionCommentSniff extends PEAR_Sniffs_Commenting_FunctionCommentSniff { /** * The name of the method that we are currently processing. * * @var string */ private $_methodName = ''; /** * The position in the stack where the function token was found.
public function __construct() { PHP_CodeSniffer::$allowedTypes = array_merge(PHP_CodeSniffer::$allowedTypes, $this->allowedTypes); }
/** * Process the function parameter comments. * * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token * in the stack passed in $tokens. * @param int $commentStart The position in the stack where the comment started. * * @return void */ protected function processParams(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $commentStart) { // Accept inheriting of comments to be sufficient. if ($this->isInherited($phpcsFile, $commentStart)) { return; } $previous = PHP_CodeSniffer::$allowedTypes; PHP_CodeSniffer::$allowedTypes[] = 'int'; PHP_CodeSniffer::$allowedTypes[] = 'bool'; parent::processParams($phpcsFile, $stackPtr, $commentStart); PHP_CodeSniffer::$allowedTypes = $previous; }
/** * Called to process class member vars. * * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. * * @param int $stackPtr The position of the current token in the stack passed in $tokens. * * @return void * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $commentToken = array(T_COMMENT, T_DOC_COMMENT_CLOSE_TAG); $commentEnd = $phpcsFile->findPrevious($commentToken, $stackPtr); if ($commentEnd === false) { $phpcsFile->addError('Missing member variable doc comment', $stackPtr, 'Missing'); return; } // Make sure the comment we have found belongs to us. $commentFor = $phpcsFile->findNext(array(T_VARIABLE, T_CLASS, T_INTERFACE, T_FUNCTION), $commentEnd + 1); if ($commentFor !== $stackPtr) { $phpcsFile->addError('Missing member variable doc comment', $stackPtr, 'Missing'); return; } if ($tokens[$commentEnd]['code'] === T_COMMENT) { $phpcsFile->addError('Member variable doc comment must be doc block', $commentEnd, 'NotDocBlock'); return; } elseif ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG) { return; } $commentStart = $tokens[$commentEnd]['comment_opener']; $comment = strtolower($phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart)); // Accept inheriting of comments to be sufficient. if (strpos($comment, '@inheritdoc') !== false) { return; } // Add well known types phpcs does not know about. $previous = PHP_CodeSniffer::$allowedTypes; PHP_CodeSniffer::$allowedTypes[] = 'int'; PHP_CodeSniffer::$allowedTypes[] = 'bool'; $foundVar = null; foreach ($tokens[$commentStart]['comment_tags'] as $tag) { if ($tokens[$tag]['content'] === '@var') { if ($foundVar !== null) { $phpcsFile->addError('Only one @var tag is allowed in a member variable comment', $tag, 'DuplicateVar'); } else { $foundVar = $tag; } } elseif ($tokens[$tag]['content'] === '@see') { // Make sure the tag isn't empty. $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd); if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) { $error = 'Content missing for @see tag in member variable comment'; $phpcsFile->addError($error, $tag, 'EmptySees'); } } elseif ($tokens[$tag]['content'] === '@deprecated') { $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd); if ($string === false || $tokens[$string]['line'] !== $tokens[$tag]['line']) { $error = 'Content missing for @deprecated tag in member variable comment'; $phpcsFile->addError($error, $tag, 'EmptyDeprecated'); } } } // The @var tag is the only one we require. if ($foundVar === null) { $error = 'Missing @var tag in member variable comment'; $phpcsFile->addError($error, $commentEnd, 'MissingVar'); return; } $firstTag = $tokens[$commentStart]['comment_tags'][0]; if ($foundVar !== null && $tokens[$firstTag]['content'] !== '@var') { $error = 'The @var tag must be the first tag in a member variable comment'; $phpcsFile->addError($error, $foundVar, 'VarOrder'); } // Make sure the tag isn't empty and has the correct padding. $string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $foundVar, $commentEnd); if ($string === false || $tokens[$string]['line'] !== $tokens[$foundVar]['line']) { $error = 'Content missing for @var tag in member variable comment'; $phpcsFile->addError($error, $foundVar, 'EmptyVar'); return; } $varType = $tokens[$foundVar + 2]['content']; $suggestedType = PHP_CodeSniffer::suggestType($varType); if ($varType !== $suggestedType) { $error = 'Expected "%s" but found "%s" for @var tag in member variable comment'; $data = array($suggestedType, $varType); $phpcsFile->addError($error, $foundVar + 2, 'IncorrectVarType', $data); } PHP_CodeSniffer::$allowedTypes = $previous; $this->checkShortComment($phpcsFile, $commentStart, $commentEnd); }