/** * Process the var tag * * @param integer $commentStart The position in the stack where the comment started * @param integer $commentEnd The position in the stack where the comment ended * @return void */ public function processVar($commentStart, $commentEnd) { $var = $this->commentParser->getVar(); if ($var !== null) { $errorPos = $commentStart + $var->getLine(); $index = array_keys($this->commentParser->getTagOrders(), 'var'); if (count($index) > 1) { $this->currentFile->addEvent('ONE_VERSION_TAG_VARIABLE_COMMENT', array(), $errorPos); return; } if ($index[0] !== 1) { $this->currentFile->addEvent('ONE_VERSION_TAG_VARIABLE_COMMENT', array(), $errorPos); } $content = $var->getContent(); if (empty($content) === true) { $this->currentFile->addEvent('TYPE_MISSING_VAR_TAG_VARIABLE_COMMENT', array(), $errorPos); return; } else { $suggestedType = PHP_CodeSniffer::suggestType($content); if ($content !== $suggestedType) { $this->currentFile->addEvent('EXPECTED_FOUND_VAR_TAG_VARIABLE_COMMENT', array('suggestedyype' => $suggestedType, 'content' => $content), $errorPos); } } $spacing = substr_count($var->getWhitespaceBeforeContent(), ' '); if ($spacing !== $this->space) { $this->currentFile->addEvent('EXPECTED_SPACES_FOUND_VAR_TAG_VARIABLE_COMMENT', array('space' => $this->space, 'spacing' => $spacing), $errorPos); } } else { $this->currentFile->addEvent('MISSING_VAR_TAG_VARIABLE_COMMENT', array(), $commentEnd); } }
/** * Process the var tag * * @param integer $commentStart The position in the stack where the comment started * @param integer $commentEnd The position in the stack where the comment ended * @return void */ public function processVar($commentStart, $commentEnd) { $var = $this->commentParser->getVar(); if ($var !== null) { $errorPos = $commentStart + $var->getLine(); $index = array_keys($this->commentParser->getTagOrders(), 'var'); if (count($index) > 1) { $this->currentFile->addError("Le tag @version doit être présent une seule fois dans le commentaire de variable", $errorPos, 'OneVersionTagVariableComment'); return; } if ($index[0] !== 1) { $this->currentFile->addError("Le tag @version doit être présent une seule fois dans le commentaire de variable", $errorPos, 'OneVersionTagVariableComment'); } $content = $var->getContent(); if (empty($content) === true) { $this->currentFile->addError("Le tag @type doit être suivi du type de variable", $errorPos, 'TypeMissingVarTagVariableComment'); return; } else { $suggestedType = PHP_CodeSniffer::suggestType($content); if ($content !== $suggestedType) { $this->currentFile->addError('Le tag @type devrait être suivi de "' . $suggestedType . '", "' . $content . '" trouvé', $errorPos, 'ExpectedFoundVarTagVariableComment'); } } $spacing = substr_count($var->getWhitespaceBeforeContent(), ' '); if ($spacing !== $this->space) { $this->currentFile->addError($this->space . ' espace(s) attendu(s), ' . $spacing . ' trouvé(s)', $errorPos, 'ExpectedSpacesFoundVarTagVariableComment'); } } else { $this->currentFile->addError('Le tag @var est manquant dans le commentaire de variable', $commentEnd, 'MissingVarTagVariableComment'); } }
/** * Process the var tag. * * @param int $commentStart The position in the stack where the comment started. * @param int $commentEnd The position in the stack where the comment ended. * * @return void */ protected function processVar($commentStart, $commentEnd) { $var = $this->commentParser->getVar(); if ($var !== null) { $errorPos = $commentStart + $var->getLine(); $index = array_keys($this->commentParser->getTagOrders(), 'var'); if (count($index) > 1) { $error = 'Only 1 @var tag is allowed in variable comment'; $this->currentFile->addError($error, $errorPos, 'DuplicateVar'); return; } if ($index[0] !== 1) { $error = 'The @var tag must be the first tag in a variable comment'; $this->currentFile->addError($error, $errorPos, 'VarOrder'); } $content = $var->getContent(); if (empty($content) === true) { $error = 'Var type missing for @var tag in variable comment'; $this->currentFile->addError($error, $errorPos, 'MissingVarType'); return; } else { $suggestedType = PHP_CodeSniffer::suggestType($content); if ($suggestedType !== $content) { // hotfix - somehow they do not like "int" and "bool". switch ($content) { case 'int': $suggestedType = 'int'; break; case 'bool': $suggestedType = 'bool'; break; default: } } if ($content !== $suggestedType) { $error = 'Expected "%s"; found "%s" for @var tag in variable comment'; $data = array($suggestedType, $content); $this->currentFile->addError($error, $errorPos, 'IncorrectVarType', $data); } } $spacing = substr_count($var->getWhitespaceBeforeContent(), ' '); if ($spacing !== 1) { $error = '@var tag indented incorrectly; expected 1 space but found %s'; $data = array($spacing); $this->currentFile->addError($error, $errorPos, 'VarIndent', $data); } } else { $error = 'Missing @var tag in variable comment'; $this->currentFile->addError($error, $commentEnd, 'MissingVar'); } //end if }
/** * Process the since tag. * * @param int $commentStart The position in the stack where the comment started. * @param int $commentEnd The position in the stack where the comment ended. * * @return void */ protected function processSince($commentStart, $commentEnd) { $since = $this->commentParser->getSince(); if ($since !== null) { $errorPos = $commentStart + $since->getLine(); $foundTags = $this->commentParser->getTagOrders(); $index = array_keys($foundTags, 'since'); $var = array_keys($foundTags, 'var'); if (count($index) > 1) { $error = 'Only 1 @since tag is allowed in variable comment'; $this->currentFile->addError($error, $errorPos); return; } // Only check order if there is one var tag in variable comment. if (count($var) === 1 && $index[0] !== 2) { $error = 'The order of @since tag is wrong in variable comment'; $this->currentFile->addError($error, $errorPos); } $content = $since->getContent(); if (empty($content) === true) { $error = 'Version number missing for @since tag in variable comment'; $this->currentFile->addError($error, $errorPos); return; } else { if ($content !== '%release_version%') { if (preg_match('/^([0-9]+)\\.([0-9]+)\\.([0-9]+)/', $content) === 0) { $error = 'Expected version number to be in the form x.x.x in @since tag'; $this->currentFile->addError($error, $errorPos); } } } $spacing = substr_count($since->getWhitespaceBeforeContent(), ' '); if ($spacing !== 1) { $error = '@since tag indented incorrectly. '; $error .= "Expected 1 space but found {$spacing}."; $this->currentFile->addError($error, $errorPos); } } else { $error = 'Missing @since tag in variable comment'; $this->currentFile->addError($error, $commentEnd); } //end if }
/** * Processes each required or optional tag. * * @param int $commentStart Position in the stack where the comment started. * @param int $commentEnd Position in the stack where the comment ended. * * @return void */ protected function processTags($commentStart, $commentEnd) { $docBlock = get_class($this) === 'PEAR_Sniffs_Commenting_FileCommentSniff' ? 'file' : 'class'; $foundTags = $this->commentParser->getTagOrders(); $orderIndex = 0; $indentation = array(); $longestTag = 0; $errorPos = 0; foreach ($this->tags as $tag => $info) { // Required tag missing. if ($info['required'] === true && in_array($tag, $foundTags) === false) { $error = "Missing @{$tag} tag in {$docBlock} comment"; $this->currentFile->addError($error, $commentEnd); continue; } // Get the line number for current tag. $tagName = ucfirst($tag); if ($info['allow_multiple'] === true) { $tagName .= 's'; } $getMethod = 'get' . $tagName; $tagElement = $this->commentParser->{$getMethod}(); if (is_null($tagElement) === true || empty($tagElement) === true) { continue; } $errorPos = $commentStart; if (is_array($tagElement) === false) { $errorPos = $commentStart + $tagElement->getLine(); } // Get the tag order. $foundIndexes = array_keys($foundTags, $tag); if (count($foundIndexes) > 1) { // Multiple occurance not allowed. if ($info['allow_multiple'] === false) { $error = "Only 1 @{$tag} tag is allowed in a {$docBlock} comment"; $this->currentFile->addError($error, $errorPos); } else { // Make sure same tags are grouped together. $i = 0; $count = $foundIndexes[0]; foreach ($foundIndexes as $index) { if ($index !== $count) { $errorPosIndex = $errorPos + $tagElement[$i]->getLine(); $error = "@{$tag} tags must be grouped together"; $this->currentFile->addError($error, $errorPosIndex); } $i++; $count++; } } } //end if // Check tag order. if ($foundIndexes[0] > $orderIndex) { $orderIndex = $foundIndexes[0]; } else { if (is_array($tagElement) === true && empty($tagElement) === false) { $errorPos += $tagElement[0]->getLine(); } $orderText = $info['order_text']; $error = "The @{$tag} tag is in the wrong order; the tag {$orderText}"; $this->currentFile->addError($error, $errorPos); } // Store the indentation for checking. $len = strlen($tag); if ($len > $longestTag) { $longestTag = $len; } if (is_array($tagElement) === true) { foreach ($tagElement as $key => $element) { $indentation[] = array('tag' => $tag, 'space' => $this->getIndentation($tag, $element), 'line' => $element->getLine()); } } else { $indentation[] = array('tag' => $tag, 'space' => $this->getIndentation($tag, $tagElement)); } $method = 'process' . $tagName; if (method_exists($this, $method) === true) { // Process each tag if a method is defined. call_user_func(array($this, $method), $errorPos); } else { if (is_array($tagElement) === true) { foreach ($tagElement as $key => $element) { $element->process($this->currentFile, $commentStart, $docBlock); } } else { $tagElement->process($this->currentFile, $commentStart, $docBlock); } } } //end foreach foreach ($indentation as $indentInfo) { if ($indentInfo['space'] !== 0 && $indentInfo['space'] !== $longestTag + 1) { $expected = $longestTag - strlen($indentInfo['tag']) + 1; $space = $indentInfo['space'] - strlen($indentInfo['tag']); $error = "@{$indentInfo['tag']} tag comment indented incorrectly. "; $error .= "Expected {$expected} spaces but found {$space}."; $getTagMethod = 'get' . ucfirst($indentInfo['tag']); if ($this->tags[$indentInfo['tag']]['allow_multiple'] === true) { $line = $indentInfo['line']; } else { $tagElem = $this->commentParser->{$getTagMethod}(); $line = $tagElem->getLine(); } $this->currentFile->addError($error, $commentStart + $line); } } }
/** * Processes each required or optional tag. * * @param int $commentStart The position in the stack where the comment started. * @param int $commentEnd The position in the stack where the comment ended. * * @return void */ protected function processTags($commentStart, $commentEnd) { // Required tags in correct order. $tags = array('version' => 'precedes @package', 'package' => 'follows @version', 'subpackage' => 'follows @package', 'author' => 'follows @subpackage', 'copyright' => 'follows @author', 'license' => 'follows @copyright'); $foundTags = $this->commentParser->getTagOrders(); $errorPos = 0; $orderIndex = 0; $longestTag = 0; $indentation = array(); foreach ($tags as $tag => $orderText) { // Required tag missing. if (in_array($tag, $foundTags) === false) { $error = "Missing @{$tag} tag in file comment"; $this->currentFile->addError($error, $commentEnd); continue; } // Get the line number for current tag. $tagName = ucfirst($tag); if ($tagName === 'Author' || $tagName === 'Copyright') { // These tags are different because they return an array. $tagName .= 's'; } // Work out the line number for this tag. $getMethod = 'get' . $tagName; $tagElement = $this->commentParser->{$getMethod}(); if (is_null($tagElement) === true || empty($tagElement) === true) { continue; } else { if (is_array($tagElement) === true && empty($tagElement) === false) { $tagElement = $tagElement[0]; } } $errorPos = $commentStart + $tagElement->getLine(); // Make sure there is no duplicate tag. $foundIndexes = array_keys($foundTags, $tag); if (count($foundIndexes) > 1) { $error = "Only 1 @{$tag} tag is allowed in file comment"; $this->currentFile->addError($error, $errorPos); } // Check tag order. if ($foundIndexes[0] > $orderIndex) { $orderIndex = $foundIndexes[0]; } else { $error = "The @{$tag} tag is in the wrong order; the tag {$orderText}"; $this->currentFile->addError($error, $errorPos); } // Store the indentation of each tag. $len = strlen($tag); if ($len > $longestTag) { $longestTag = $len; } $indentation[] = array('tag' => $tag, 'errorPos' => $errorPos, 'space' => $this->getIndentation($tag, $tagElement)); $method = 'process' . $tagName; if (method_exists($this, $method) === true) { // Process each tag if a method is defined. call_user_func(array($this, $method), $errorPos); } else { $tagElement->process($this->currentFile, $commentStart, 'file'); } } //end foreach // Check tag indentation. foreach ($indentation as $indentInfo) { $tagName = ucfirst($indentInfo['tag']); if ($tagName === 'Author') { $tagName .= 's'; } if ($indentInfo['space'] !== 0 && $indentInfo['space'] !== $longestTag + 1) { $expected = $longestTag - strlen($indentInfo['tag']) + 1; $space = $indentInfo['space'] - strlen($indentInfo['tag']); $error = "@{$indentInfo['tag']} tag comment indented incorrectly. "; $error .= "Expected {$expected} spaces but found {$space}."; $this->currentFile->addError($error, $indentInfo['errorPos']); } } }
/** * Processes each required or optional tag * * @param integer $commentStart The position in the stack where the comment started * @param integer $commentEnd The position in the stack where the comment ended * @return void */ protected function _processTags($commentStart, $commentEnd) { // Tags in correct order and related info $tags = array('category' => array('required' => true, 'allow_multiple' => false, 'order_text' => 'precedes @package'), 'package' => array('required' => true, 'allow_multiple' => false, 'order_text' => 'follows @category'), 'subpackage' => array('required' => false, 'allow_multiple' => false, 'order_text' => 'follows @package'), 'copyright' => array('required' => true, 'allow_multiple' => false, 'order_text' => 'follows @subpackage (if used) or @package'), 'license' => array('required' => true, 'allow_multiple' => false, 'order_text' => 'follows @copyright'), 'version' => array('required' => true, 'allow_multiple' => false, 'order_text' => 'follows @license'), 'deprecated' => array('required' => false, 'allow_multiple' => false, 'order_text' => 'follows @version')); $foundTags = $this->_commentParser->getTagOrders(); $orderIndex = 0; $indentation = array(); $longestTag = 0; $errorPos = 0; foreach ($foundTags as $tag => $info) { if (array_key_exists($info, $tags) === false and $info !== 'comment') { $error = "Tag @{$info} is not allowed"; $this->_currentFile->addError($error, $commentStart + $tag + 13, 'TagNotAllowedFileComment'); } } foreach ($tags as $tag => $info) { // Required tag missing if ($info['required'] === true and in_array($tag, $foundTags) === false) { $error = "Missing @{$tag} tag in file comment"; $this->_currentFile->addError($error, $commentEnd, 'TagRequiredFileComment'); continue; } // Get the line number for current tag $tagName = ucfirst($tag); if ($info['allow_multiple'] === true or $tag === 'copyright') { $tagName .= 's'; } $getMethod = 'get' . $tagName; $tagElement = $this->_commentParser->{$getMethod}(); if (is_null($tagElement) === true or empty($tagElement) === true) { continue; } $errorPos = $commentStart; if (is_array($tagElement) === false) { $errorPos = $commentStart + $tagElement->getLine(); } // Get the tag order $foundIndexes = array_keys($foundTags, $tag); if (count($foundIndexes) > 1) { // Multiple occurance not allowed if ($info['allow_multiple'] === false) { $error = "Only 1 @{$tag} tag is allowed in a file comment"; $this->_currentFile->addError($error, $errorPos, 'NoDuplicateTagFileComment'); } else { // Make sure same tags are grouped together $i = 0; $count = $foundIndexes[0]; foreach ($foundIndexes as $index) { if ($index !== $count) { $errorPosIndex = $errorPos + $tagElement[$i]->getLine(); $error = "@{$tag} tags must be grouped together"; $this->_currentFile->addError($error, $errorPosIndex, 'TagsMustBeGroupedFileComment'); } $i++; $count++; } } } // Check tag order if ($foundIndexes[0] > $orderIndex) { $orderIndex = $foundIndexes[0]; } else { if (is_array($tagElement) === true and empty($tagElement) === false) { $errorPos += $tagElement[0]->getLine(); } $orderText = $info['order_text']; $error = "The @{$tag} tag is in the wrong order; the tag {$orderText}"; $this->_currentFile->addError($error, $errorPos, 'TagsOrderFileComment'); } // Store the indentation for checking $len = strlen($tag); if ($len > $longestTag) { $longestTag = $len; } if (is_array($tagElement) === true) { foreach ($tagElement as $key => $element) { $indentation[] = array('tag' => $tag, 'space' => $this->_getIndentation($tag, $element), 'line' => $element->getLine()); } } else { $indentation[] = array('tag' => $tag, 'space' => $this->_getIndentation($tag, $tagElement)); } $method = '_process' . $tagName; if (method_exists($this, $method) === true) { // Process each tag if a method is defined call_user_func(array($this, $method), $errorPos); } else { if (is_array($tagElement) === true) { foreach ($tagElement as $key => $element) { $element->process($this->_currentFile, $commentStart, 'file'); } } else { $tagElement->process($this->_currentFile, $commentStart, 'file'); } } } foreach ($indentation as $indentInfo) { if ($indentInfo['space'] !== 0 and $indentInfo['space'] !== $longestTag + 1) { $expected = $longestTag - strlen($indentInfo['tag']) + 1; $space = $indentInfo['space'] - strlen($indentInfo['tag']); $error = "@{$indentInfo['tag']} tag comment indented incorrectly. "; $error .= "Expected {$expected} spaces but found {$space}."; $getTagMethod = 'get' . ucfirst($indentInfo['tag']); if ($tags[$indentInfo['tag']]['allow_multiple'] === true or $indentInfo['tag'] === 'copyright') { $line = $indentInfo['line']; } else { $tagElem = $this->_commentParser->{$getTagMethod}(); $line = $tagElem->getLine(); } $this->_currentFile->addError($error, $commentStart + $line, 'TagIndentationFileComment'); } } }
/** * Processes each required or optional tag. * * @param int $commentStart Position in the stack where the comment started. * @param int $commentEnd Position in the stack where the comment ended. * * @return void */ protected function processTags($commentStart, $commentEnd) { $docBlock = get_class($this) === 'Joomla_Sniffs_Commenting_FileCommentSniff' ? 'file' : 'class'; $foundTags = $this->commentParser->getTagOrders(); $orderIndex = 0; $indentation = array(); $longestTag = 0; $errorPos = 0; foreach ($this->tags as $tag => $info) { // Required tag missing. if ($info['required'] === true && in_array($tag, $foundTags) === false) { // We don't use package tags in namespaced code or the bootstrap file if ($tag == 'package') { // this should return 0 if there is no namespaced tokens $namespaced = $this->currentFile->findNext(T_NAMESPACE, 0); if ($namespaced !== 0 || strpos($this->currentFile->getFilename(), '/libraries/bootstrap.php')) { continue; } } $error = 'Missing @%s tag in %s comment'; $data = array($tag, $docBlock); $this->currentFile->addError($error, $commentEnd, 'MissingTag', $data); continue; } // Get the line number for current tag. $tagName = ucfirst($tag); if ($info['allow_multiple'] === true) { $tagName .= 's'; } $getMethod = 'get' . $tagName; $tagElement = $this->commentParser->{$getMethod}(); if (is_null($tagElement) === true || empty($tagElement) === true) { continue; } $errorPos = $commentStart; if (is_array($tagElement) === false) { $errorPos = $commentStart + $tagElement->getLine(); } // Get the tag order. $foundIndexes = array_keys($foundTags, $tag); if (count($foundIndexes) > 1) { // Multiple occurance not allowed. if ($info['allow_multiple'] === false) { $error = 'Only 1 @%s tag is allowed in a %s comment'; $data = array($tag, $docBlock); $this->currentFile->addError($error, $errorPos, 'DuplicateTag', $data); } else { // Make sure same tags are grouped together. $i = 0; $count = $foundIndexes[0]; foreach ($foundIndexes as $index) { if ($index !== $count) { $errorPosIndex = $errorPos + $tagElement[$i]->getLine(); $error = '@%s tags must be grouped together'; $data = array($tag); $this->currentFile->addError($error, $errorPosIndex, 'TagsNotGrouped', $data); } $i++; $count++; } } } //end if // Check tag order. if ($foundIndexes[0] > $orderIndex) { $orderIndex = $foundIndexes[0]; } else { if (is_array($tagElement) === true && empty($tagElement) === false) { $errorPos += $tagElement[0]->getLine(); } $error = 'The @%s tag is in the wrong order; the tag %s'; $data = array($tag, $info['order_text']); $this->currentFile->addError($error, $errorPos, 'WrongTagOrder', $data); } // Store the indentation for checking. $len = strlen($tag); if ($len > $longestTag) { $longestTag = $len; } if (is_array($tagElement) === true) { foreach ($tagElement as $key => $element) { $indentation[] = array('tag' => $tag, 'space' => $this->getIndentation($tag, $element), 'line' => $element->getLine()); } } else { $indentation[] = array('tag' => $tag, 'space' => $this->getIndentation($tag, $tagElement)); } $method = 'process' . $tagName; if (method_exists($this, $method) === true) { // Process each tag if a method is defined. call_user_func(array($this, $method), $errorPos); } else { if (is_array($tagElement) === true) { foreach ($tagElement as $key => $element) { $element->process($this->currentFile, $commentStart, $docBlock); } } else { $tagElement->process($this->currentFile, $commentStart, $docBlock); } } } //end foreach foreach ($indentation as $indentInfo) { if ($indentInfo['space'] !== 0 && $indentInfo['space'] !== $longestTag + 2) { $expected = $longestTag - strlen($indentInfo['tag']) + 2; $space = $indentInfo['space'] - strlen($indentInfo['tag']); $error = '@%s tag comment indented incorrectly; expected %s spaces but found %s'; $data = array($indentInfo['tag'], $expected, $space); $getTagMethod = 'get' . ucfirst($indentInfo['tag']); if ($this->tags[$indentInfo['tag']]['allow_multiple'] === true) { $line = $indentInfo['line']; } else { $tagElem = $this->commentParser->{$getTagMethod}(); $line = $tagElem->getLine(); } $this->currentFile->addError($error, $commentStart + $line, 'TagIndent', $data); } } }
/** * Processes each required or optional tag. * * @param int $commentStart The position in the stack where the comment started. * @param int $commentEnd The position in the stack where the comment ended. * * @return void */ protected function processTags($commentStart, $commentEnd) { // Tags in correct order and related info. $tags = array( 'category' => array( 'required' => true, 'allow_multiple' => false, 'order_text' => 'precedes @package', ), 'package' => array( 'required' => true, 'allow_multiple' => false, 'order_text' => 'follows @category', ), 'subpackage' => array( 'required' => false, 'allow_multiple' => false, 'order_text' => 'follows @package', ), 'author' => array( 'required' => false, 'allow_multiple' => true, 'order_text' => 'follows @subpackage (if used) or @package', ), 'copyright' => array( 'required' => false, 'allow_multiple' => true, 'order_text' => 'follows @author', ), 'license' => array( 'required' => false, 'allow_multiple' => false, 'order_text' => 'follows @copyright (if used) or @author', ), 'version' => array( 'required' => false, 'allow_multiple' => false, 'order_text' => 'follows @licence', ), 'link' => array( 'required' => false, 'allow_multiple' => true, 'order_text' => 'follows @version', ), 'see' => array( 'required' => false, 'allow_multiple' => true, 'order_text' => 'follows @link', ), 'since' => array( 'required' => false, 'allow_multiple' => false, 'order_text' => 'follows @see (if used) or @link', ), 'deprecated' => array( 'required' => false, 'allow_multiple' => false, 'order_text' => 'follows @since (if used) or @see (if used) or @link', ), ); $docBlock = (get_class($this) === 'PEAR_Sniffs_Commenting_FileCommentSniff') ? 'file' : 'class'; $foundTags = $this->commentParser->getTagOrders(); $orderIndex = 0; $indentation = array(); $longestTag = 0; $errorPos = 0; foreach ($tags as $tag => $info) { // Required tag missing. if ($info['required'] === true && in_array($tag, $foundTags) === false) { $error = "Missing @$tag tag in $docBlock comment"; $this->currentFile->addError($error, $commentEnd); continue; } // Get the line number for current tag. $tagName = ucfirst($tag); if ($info['allow_multiple'] === true) { $tagName .= 's'; } $getMethod = 'get'.$tagName; $tagElement = $this->commentParser->$getMethod(); if (is_null($tagElement) === true || empty($tagElement) === true) { continue; } $errorPos = $commentStart; if (is_array($tagElement) === false) { $errorPos = ($commentStart + $tagElement->getLine()); } // Get the tag order. $foundIndexes = array_keys($foundTags, $tag); if (count($foundIndexes) > 1) { // Multiple occurance not allowed. if ($info['allow_multiple'] === false) { $error = "Only 1 @$tag tag is allowed in a $docBlock comment"; $this->currentFile->addError($error, $errorPos); } else { // Make sure same tags are grouped together. $i = 0; $count = $foundIndexes[0]; foreach ($foundIndexes as $index) { if ($index !== $count) { $errorPosIndex = ($errorPos + $tagElement[$i]->getLine()); $error = "@$tag tags must be grouped together"; $this->currentFile->addError($error, $errorPosIndex); } $i++; $count++; } } }//end if // Check tag order. if ($foundIndexes[0] > $orderIndex) { $orderIndex = $foundIndexes[0]; } else { if (is_array($tagElement) === true && empty($tagElement) === false) { $errorPos += $tagElement[0]->getLine(); } $orderText = $info['order_text']; $error = "The @$tag tag is in the wrong order; the tag $orderText"; $this->currentFile->addError($error, $errorPos); } // Store the indentation for checking. $len = strlen($tag); if ($len > $longestTag) { $longestTag = $len; } if (is_array($tagElement) === true) { foreach ($tagElement as $key => $element) { $indentation[] = array( 'tag' => $tag, 'space' => $this->getIndentation($tag, $element), 'line' => $element->getLine(), ); } } else { $indentation[] = array( 'tag' => $tag, 'space' => $this->getIndentation($tag, $tagElement), ); } $method = 'process'.$tagName; if (method_exists($this, $method) === true) { // Process each tag if a method is defined. call_user_func(array($this, $method), $errorPos); } else { if (is_array($tagElement) === true) { foreach ($tagElement as $key => $element) { $element->process($this->currentFile, $commentStart, $docBlock); } } else { $tagElement->process($this->currentFile, $commentStart, $docBlock); } } }//end foreach }//end processTags()
/** * Processes each required or optional tag. * * @param int $commentStart Position in the stack where the comment started. * @param int $commentEnd Position in the stack where the comment ended. * * @return void */ protected function processTags($commentStart, $commentEnd) { $foundTags = $this->commentParser->getTagOrders(); $orderIndex = 0; $indentation = array(); $longestTag = 0; $errorPos = 0; $this->checkGotchas($commentStart, $commentEnd); $diff = array_diff($foundTags, array_keys($this->tags), array('comment')); if (count($diff) > 0 && $this->reqCodeForbidden) { foreach ($diff as $tag) { $error = "Forbidden @{$tag} tag in " . $this->docBlock . " comment"; $this->currentFile->addError($this->getReqPrefix($this->reqCodeForbidden) . $error, $commentEnd); } } foreach ($this->tags as $tag => $info) { // Required tag missing. if ($this->getTagRule($info, 'required') && in_array($tag, $foundTags) === false && $this->reqCodeRequire) { $error = "Missing @{$tag} tag in " . $this->docBlock . " comment"; $this->currentFile->addError($this->getReqPrefix($this->reqCodeRequire) . $error, $commentEnd); continue; } // Get the line number for current tag. $tagName = ucfirst($tag); if ($info['allow_multiple'] === true) { $tagName .= 's'; } $getMethod = 'get' . $tagName; if (!method_exists($this->commentParser, $getMethod) && $info['allow_multiple'] !== true) { $getMethod .= 's'; } if (!method_exists($this->commentParser, $getMethod)) { continue; } $tagElement = $this->commentParser->{$getMethod}(); if (is_null($tagElement) === true || empty($tagElement) === true) { continue; } $tagElements = is_array($tagElement) ? $tagElement : array($tagElement); $errorPos = $commentStart; if (is_array($tagElement) === false) { $errorPos = $commentStart + $tagElement->getLine(); } // Get the tag order. $foundIndexes = array_keys($foundTags, $tag); if (count($foundIndexes) > 1) { // Multiple occurance not allowed. if (!$this->getTagRule($info, 'allow_multiple')) { if ($this->reqCodeOnlyOne) { $error = "Only 1 @{$tag} tag is allowed in a " . $this->docBlock . " comment"; $this->currentFile->addError($this->getReqPrefix($this->reqCodeOnlyOne) . $error, $errorPos); } } else { // Make sure same tags are grouped together. $i = 0; $count = $foundIndexes[0]; foreach ($foundIndexes as $index) { if ($index !== $count) { $errorPosIndex = $errorPos + $tagElement[$i]->getLine(); $error = "@{$tag} tags must be grouped together"; $this->currentFile->addError($this->getReqPrefix($this->reqCodeUngroup) . $error, $errorPosIndex); } $i++; $count++; } } } //end if // Check tag order. if ($foundIndexes[0] > $orderIndex) { $orderIndex = $foundIndexes[0]; } else { if (is_array($tagElement) === true && empty($tagElement) === false) { $errorPos += $tagElement[0]->getLine(); } $orderText = $info['order_text']; $error = "The @{$tag} tag is in the wrong order; the tag {$orderText}"; $this->currentFile->addError($this->getReqPrefix($this->reqCodeWrongOrder) . $error, $errorPos); } // Store the indentation for checking. $len = strlen($tag); if ($len > $longestTag) { $longestTag = $len; } foreach ($tagElements as $key => $element) { $indentation[] = array('tag' => $tag, 'space' => $this->getIndentation($tag, $element), 'line' => $element->getLine(), 'value' => $this->getTagValue($element)); } $method1 = 'process' . ucfirst($tag); $method2 = 'process' . ucfirst($tag) . 's'; if (method_exists($this, $method1) === true) { // Process each tag if a method is defined. call_user_func(array($this, $method1), $errorPos, $commentEnd, $tagElements); } elseif (method_exists($this, $method2) === true) { // Process each tag if a method is defined. call_user_func(array($this, $method2), $errorPos, $commentEnd, $tagElements); } else { foreach ($tagElements as $key => $element) { if (method_exists($element, 'process')) { $element->process($this->currentFile, $commentStart, $this->docBlock); } } } } //end foreach foreach ($indentation as $indentInfo) { $this->checkForDefaultValue($indentInfo['value'], $indentInfo['tag'], $commentStart + $indentInfo['line']); if ($indentInfo['space'] !== 0 && $indentInfo['space'] !== $longestTag + 1) { $expected = $longestTag - strlen($indentInfo['tag']) + 1; $space = $indentInfo['space'] - strlen($indentInfo['tag']); $error = "@{$indentInfo['tag']} tag comment indented incorrectly. "; $error .= "Expected {$expected} spaces but found {$space}."; $getTagMethod = isset($this->reqCodesWrongFormat[$indentInfo['tag']]) ? $this->reqCodesWrongFormat[$indentInfo['tag']]['function'] : false; $line = $indentInfo['line']; if ($this->tags[$indentInfo['tag']]['allow_multiple'] === true) { $line = $indentInfo['line']; } elseif ($getTagMethod && method_exists($this->commentParser, $getTagMethod)) { $tagElem = $this->commentParser->{$getTagMethod}(); if ('array' === $this->reqCodesWrongFormat[$indentInfo['tag']]['type']) { $tagElem = array_pop($tagElem); } $line = $tagElem->getLine(); } $this->currentFile->addError($this->getReqPrefix($this->reqCodeIndent) . $error, $commentStart + $line); } } }
/** * Processes this test, when one of its tokens is encountered. * * @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 */ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) { $this->currentFile = $phpcsFile; // We are only interested if this is the first open tag. if ($stackPtr !== 0) { if ($phpcsFile->findPrevious(T_OPEN_TAG, $stackPtr - 1) !== false) { return; } } $tokens = $phpcsFile->getTokens(); // Find the next non whitespace token. $commentStart = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); // Ignore vim header. if ($tokens[$commentStart]['code'] === T_COMMENT) { if (strstr($tokens[$commentStart]['content'], 'vim:') !== false) { $commentStart = $phpcsFile->findNext(T_WHITESPACE, $commentStart + 1, null, true); } } if ($tokens[$commentStart]['code'] === T_CLOSE_TAG) { // We are only interested if this is the first open tag. return; } else { if ($tokens[$commentStart]['code'] === T_COMMENT) { $phpcsFile->addError('You must use "/**" style comments for a file comment', $stackPtr + 1); return; } else { if ($commentStart === false || $tokens[$commentStart]['code'] !== T_DOC_COMMENT) { $phpcsFile->addError('Missing file doc comment', $stackPtr + 1); return; } else { // Extract the header comment docblock. $commentEnd = $phpcsFile->findNext(T_DOC_COMMENT, $commentStart + 1, null, true) - 1; // Check if there is only 1 doc comment between the open tag and class token. $nextToken = array(T_ABSTRACT, T_CLASS, T_FUNCTION, T_DOC_COMMENT); $commentNext = $phpcsFile->findNext($nextToken, $commentEnd + 1); if ($commentNext !== false && $tokens[$commentNext]['code'] !== T_DOC_COMMENT) { // Found a class token right after comment doc block. $newlineToken = $phpcsFile->findNext(T_WHITESPACE, $commentEnd + 1, $commentNext, false, $phpcsFile->eolChar); if ($newlineToken !== false) { $newlineToken = $phpcsFile->findNext(T_WHITESPACE, $newlineToken + 1, $commentNext, false, $phpcsFile->eolChar); if ($newlineToken === false) { // No blank line between the class token and the doc block. // The doc block is most likely a class comment. $phpcsFile->addError('Missing file doc comment', $stackPtr + 1); return; } } } $comment = $phpcsFile->getTokensAsString($commentStart, $commentEnd - $commentStart + 1); // Parse the header comment docblock. try { $this->commentParser = new PHP_CodeSniffer_CommentParser_ClassCommentParser($comment, $phpcsFile); $this->commentParser->parse(); } catch (PHP_CodeSniffer_CommentParser_ParserException $e) { $line = $e->getLineWithinComment() + $commentStart; $phpcsFile->addError($e->getMessage(), $line); return; } $comment = $this->commentParser->getComment(); if (is_null($comment) === true) { $error = 'File doc comment is empty'; $phpcsFile->addError($error, $commentStart); return; } // No extra newline before short description. $short = $comment->getShortComment(); $newlineCount = 0; $newlineSpan = strspn($short, $phpcsFile->eolChar); if ($short !== '' && $newlineSpan > 0) { $line = $newlineSpan > 1 ? 'newlines' : 'newline'; $error = "Extra {$line} found before file comment short description"; $phpcsFile->addError($error, $commentStart + 1); } $newlineCount = substr_count($short, $phpcsFile->eolChar) + 1; // Exactly one blank line between short and long description. $long = $comment->getLongComment(); if (empty($long) === false) { $between = $comment->getWhiteSpaceBetween(); $newlineBetween = substr_count($between, $phpcsFile->eolChar); if ($newlineBetween !== 2) { $error = 'There must be exactly one blank line between descriptions in file comment'; $phpcsFile->addError($error, $commentStart + $newlineCount + 1); } $newlineCount += $newlineBetween; } // Exactly one blank line before tags. $tags = $this->commentParser->getTagOrders(); if (count($tags) > 1) { $newlineSpan = $comment->getNewlineAfter(); if ($newlineSpan !== 2) { $error = 'There must be exactly one blank line before the tags in file comment'; if ($long !== '') { $newlineCount += substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1; } $phpcsFile->addError($error, $commentStart + $newlineCount); $short = rtrim($short, $phpcsFile->eolChar . ' '); } } // Check each tag. $this->processTags($commentStart, $commentEnd); } } } //end if }