/** * Check for the existence of a docblock for the current token * o go back and find the previous token that is not a whitespace * o if it is a access specifier (private, public etc), then * see if private members are excluded from comment check * (input argument specified this). if we find an access * specifier move on to find the next best token * o if it is ABSTRACT or STATIC specifier move on to find the next best token * o if the found token is a T_DOC_COMMENT, then we have a docblock * * This, of course, assumes that the function/class/interface has to be * immediately preceded by docblock. Even regular comments are not * allowed, which I think is okay. * * Launched when a CLASS / FUNCTION or INTERFACE statement is found. * * @param Integer $token * T_CLASS, T_FUNCTION or T_INTERFACE * @return true is docblock is found */ private function _checkDocExists($token) { // current token = the token after T_CLASS, T_FUNCTION or T_INTERFACE // // token positions: // . curToken - 1 = T_CLASS/T_FUNCTION/T_INTERFACE // . curToken - 2 = whitespace before T_CLASS/T_FUNCTION/T_INTERFACE // . curToken - 3 = T_ABSTRACT/T_PUBLIC/T_PROTECTED/T_PRIVATE/T_STATIC // or T_DOC_COMMENT, if it is present // $isPrivateExcluded = $this->_config->getTestProperty('docBlocks', 'excludePrivateMembers'); // Locate the function, class or interface token $functionTokenPosition = $this->tokenizer->getCurrentPosition(); while (true) { // the type - function, class or interface. (Horribly named). $functionToken = $this->tokenizer->peekTokenAt($functionTokenPosition); $isFunction = $this->tokenizer->checkToken($functionToken, T_FUNCTION); $isClass = $this->tokenizer->checkToken($functionToken, T_CLASS); $isInterface = $this->tokenizer->checkToken($functionToken, T_INTERFACE); if ($isFunction || $isClass || $isInterface) { break; } $functionTokenPosition--; } // Records the type, as well as the type name for more precise error reporting. // Two positions forward from declaration of type. $typeToken = $this->tokenizer->peekTokenAt($functionTokenPosition); $type = $typeToken->text; $nameToken = $this->tokenizer->peekTokenAt($functionTokenPosition + 2); $name = $nameToken->text; $isOldStyleConstructor = strtolower($name) == strtolower($this->_currentClassname); $isNewStyleConstructor = strtolower($name) == '__construct'; if ($isOldStyleConstructor || $isNewStyleConstructor) { $type = "constructor"; } $found = false; $isPrivate = false; $docTokenPosition = $functionTokenPosition - 1; // List of tokens to ignore when looking for the docblock $tokenToIgnoreList = array(T_STATIC, T_FINAL, T_ABSTRACT, T_PROTECTED, T_PUBLIC, T_WHITESPACE, T_TAB, T_COMMENT, T_ML_COMMENT, T_NEW_LINE); // Go backward and look for a T_DOC_COMMENT while (true) { $docToken = $this->tokenizer->peekTokenAt($docTokenPosition); // if the token is in the list above. if ($this->tokenizer->isTokenInList($docToken, $tokenToIgnoreList)) { // All these tokens are ignored } else { if ($this->tokenizer->checkToken($docToken, T_PRIVATE)) { $isPrivate = true; // we are in a private function } else { if ($this->tokenizer->checkToken($docToken, T_DOC_COMMENT)) { // We have found a doc comment $found = true; break; } else { // Any other token found, we stop $found = false; break; } } } $docTokenPosition--; if ($docTokenPosition == 0) { break; // special case for beginning of the file } } if ($found) { // Doc found, look for annotations $this->_processAnnotation($token, $docToken->text); } else { // No documentation found if ($this->_isActive('docBlocks') && !($isPrivateExcluded && $isPrivate)) { $msg = $this->_getMessage('MISSING_DOCBLOCK', $type, $name); $this->_writeError('docBlocks', $msg); } } }