Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,24 @@
<documentation title="Including Code">
<standard>
<![CDATA[
Anywhere you are unconditionally including a class file, use <em>require_once</em>. Anywhere you are conditionally including a class file (for example, factory methods), use <em>include_once</em>. Either of these will ensure that class files are included only once. They share the same file list, so you don't need to worry about mixing them - a file included with <em>require_once</em> will not be included again by <em>include_once</em>.
]]>
</standard>
<standard>
<![CDATA[
Note that <em>include_once</em> and <em>require_once</em> are statements, not functions. Parentheses should not surround the subject filename.
]]>
</standard>
<code_comparison>
<code title="Valid: used as statement">
<![CDATA[
require_once 'PHP/CodeSniffer.php';
]]>
</code>
<code title="Invalid: used as function">
<![CDATA[
require_once<em>(</em>'PHP/CodeSniffer.php'<em>)</em>;
]]>
</code>
</code_comparison>
</documentation>

View File

@@ -0,0 +1,7 @@
<documentation title="Line Length">
<standard>
<![CDATA[
It is recommended to keep lines at approximately 85 characters long for better code readability.
]]>
</standard>
</documentation>

View File

@@ -0,0 +1,19 @@
<documentation title="Function Calls">
<standard>
<![CDATA[
Functions should be called with no spaces between the function name, the opening parenthesis, and the first parameter; and no space between the last parameter, the closing parenthesis, and the semicolon.
]]>
</standard>
<code_comparison>
<code title="Valid: spaces between parameters">
<![CDATA[
$var = foo($bar, $baz, $quux);
]]>
</code>
<code title="Invalid: additional spaces used">
<![CDATA[
$var = foo<em> </em>(<em> </em>$bar, $baz, $quux<em> </em>)<em> </em>;
]]>
</code>
</code_comparison>
</documentation>

View File

@@ -0,0 +1,25 @@
<documentation title="Default Values in Function Declarations">
<standard>
<![CDATA[
Arguments with default values go at the end of the argument list.
]]>
</standard>
<code_comparison>
<code title="Valid: argument with default value at end of declaration">
<![CDATA[
function connect($dsn, <em>$persistent = false</em>)
{
...
}
]]>
</code>
<code title="Invalid: argument with default value at start of declaration">
<![CDATA[
function connect(<em>$persistent = false</em>, $dsn)
{
...
}
]]>
</code>
</code_comparison>
</documentation>

View File

@@ -0,0 +1,23 @@
<documentation title="Class Names">
<standard>
<![CDATA[
Classes should be given descriptive names. Avoid using abbreviations where possible. Class names should always begin with an uppercase letter. The PEAR class hierarchy is also reflected in the class name, each level of the hierarchy separated with a single underscore.
]]>
</standard>
<code_comparison>
<code title="Examples of valid class names">
<![CDATA[
Log
Net_Finger
HTML_Upload_Error
]]>
</code>
<code title="Examples of invalid class names">
<![CDATA[
log
NetFinger
HTML-Upload-Error
]]>
</code>
</code_comparison>
</documentation>

View File

@@ -0,0 +1,23 @@
<documentation title="Function and Method Names">
<standard>
<![CDATA[
Functions and methods should be named using the "studly caps" style (also referred to as "bumpy case" or "camel caps"). Functions should in addition have the package name as a prefix, to avoid name collisions between packages. The initial letter of the name (after the prefix) is lowercase, and each letter that starts a new "word" is capitalized.
]]>
</standard>
<code_comparison>
<code title="Examples of valid function names">
<![CDATA[
connect()
getData()
buildSomeWidget()
XML_RPC_serializeData()
]]>
</code>
<code title="Examples of invalid function names">
<![CDATA[
Connect()
get_data()
]]>
</code>
</code_comparison>
</documentation>

View File

@@ -0,0 +1,125 @@
<?php
/**
* Class Declaration Test.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Class Declaration Test.
*
* Checks the declaration of the class is correct.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_Classes_ClassDeclarationSniff implements PHP_CodeSniffer_Sniff
{
/**
* The number of spaces code should be indented.
*
* @var int
*/
public $indent = 4;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_CLASS,
T_INTERFACE,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param integer $stackPtr The position of the current token in the
* stack passed in $tokens.
*
* @return void
*/
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$errorData = array($tokens[$stackPtr]['content']);
if (isset($tokens[$stackPtr]['scope_opener']) === false) {
$error = 'Possible parse error: %s missing opening or closing brace';
$phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $errorData);
return;
}
$curlyBrace = $tokens[$stackPtr]['scope_opener'];
$lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($curlyBrace - 1), $stackPtr, true);
$classLine = $tokens[$lastContent]['line'];
$braceLine = $tokens[$curlyBrace]['line'];
if ($braceLine === $classLine) {
$error = 'Opening brace of a %s must be on the line after the definition';
$phpcsFile->addError($error, $curlyBrace, 'OpenBraceNewLine', $errorData);
return;
} else if ($braceLine > ($classLine + 1)) {
$error = 'Opening brace of a %s must be on the line following the %s declaration; found %s line(s)';
$data = array(
$tokens[$stackPtr]['content'],
$tokens[$stackPtr]['content'],
($braceLine - $classLine - 1),
);
$phpcsFile->addError($error, $curlyBrace, 'OpenBraceWrongLine', $data);
return;
}
if ($tokens[($curlyBrace + 1)]['content'] !== $phpcsFile->eolChar) {
$error = 'Opening %s brace must be on a line by itself';
$phpcsFile->addError($error, $curlyBrace, 'OpenBraceNotAlone', $errorData);
}
if ($tokens[($curlyBrace - 1)]['code'] === T_WHITESPACE) {
$prevContent = $tokens[($curlyBrace - 1)]['content'];
if ($prevContent === $phpcsFile->eolChar) {
$spaces = 0;
} else {
$blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar));
$spaces = strlen($blankSpace);
}
$expected = ($tokens[$stackPtr]['level'] * $this->indent);
if ($spaces !== $expected) {
$error = 'Expected %s spaces before opening brace; %s found';
$data = array(
$expected,
$spaces,
);
$phpcsFile->addError($error, $curlyBrace, 'SpaceBeforeBrace', $data);
}
}
}//end process()
}//end class
?>

View File

@@ -0,0 +1,233 @@
<?php
/**
* Parses and verifies the doc comments for classes.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_CommentParser_ClassCommentParser', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_ClassCommentParser not found';
throw new PHP_CodeSniffer_Exception($error);
}
if (class_exists('PEAR_Sniffs_Commenting_FileCommentSniff', true) === false) {
$error = 'Class PEAR_Sniffs_Commenting_FileCommentSniff not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* Parses and verifies the doc comments for classes.
*
* Verifies that :
* <ul>
* <li>A doc comment exists.</li>
* <li>There is a blank newline after the short description.</li>
* <li>There is a blank newline between the long and short description.</li>
* <li>There is a blank newline between the long description and tags.</li>
* <li>Check the order of the tags.</li>
* <li>Check the indentation of each tag.</li>
* <li>Check required and optional tags and the format of their content.</li>
* </ul>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_Commenting_ClassCommentSniff extends PEAR_Sniffs_Commenting_FileCommentSniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_CLASS,
T_INTERFACE,
);
}//end register()
/**
* 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;
$tokens = $phpcsFile->getTokens();
$type = strtolower($tokens[$stackPtr]['content']);
$errorData = array($type);
$find = array(
T_ABSTRACT,
T_WHITESPACE,
T_FINAL,
);
// Extract the class comment docblock.
$commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
if ($commentEnd !== false && $tokens[$commentEnd]['code'] === T_COMMENT) {
$error = 'You must use "/**" style comments for a %s comment';
$phpcsFile->addError($error, $stackPtr, 'WrongStyle', $errorData);
return;
} else if ($commentEnd === false
|| $tokens[$commentEnd]['code'] !== T_DOC_COMMENT
) {
$phpcsFile->addError('Missing %s doc comment', $stackPtr, 'Missing', $errorData);
return;
}
$commentStart = ($phpcsFile->findPrevious(T_DOC_COMMENT, ($commentEnd - 1), null, true) + 1);
$commentNext = $phpcsFile->findPrevious(T_WHITESPACE, ($commentEnd + 1), $stackPtr, false, $phpcsFile->eolChar);
// Distinguish file and class comment.
$prevClassToken = $phpcsFile->findPrevious(T_CLASS, ($stackPtr - 1));
if ($prevClassToken === false) {
// This is the first class token in this file, need extra checks.
$prevNonComment = $phpcsFile->findPrevious(T_DOC_COMMENT, ($commentStart - 1), null, true);
if ($prevNonComment !== false) {
$prevComment = $phpcsFile->findPrevious(T_DOC_COMMENT, ($prevNonComment - 1));
if ($prevComment === false) {
// There is only 1 doc comment between open tag and class token.
$newlineToken = $phpcsFile->findNext(T_WHITESPACE, ($commentEnd + 1), $stackPtr, false, $phpcsFile->eolChar);
if ($newlineToken !== false) {
$newlineToken = $phpcsFile->findNext(
T_WHITESPACE,
($newlineToken + 1),
$stackPtr,
false,
$phpcsFile->eolChar
);
if ($newlineToken !== false) {
// Blank line between the class and the doc block.
// The doc block is most likely a file comment.
$error = 'Missing %s doc comment';
$phpcsFile->addError($error, ($stackPtr + 1), 'Missing', $errorData);
return;
}
}//end if
}//end if
}//end if
}//end if
$comment = $phpcsFile->getTokensAsString(
$commentStart,
($commentEnd - $commentStart + 1)
);
// Parse the class 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, 'FailedParse');
return;
}
$comment = $this->commentParser->getComment();
if (is_null($comment) === true) {
$error = 'Doc comment is empty for %s';
$phpcsFile->addError($error, $commentStart, 'Empty', $errorData);
return;
}
// No extra newline before short description.
$short = $comment->getShortComment();
$newlineCount = 0;
$newlineSpan = strspn($short, $phpcsFile->eolChar);
if ($short !== '' && $newlineSpan > 0) {
$error = 'Extra newline(s) found before %s comment short description';
$phpcsFile->addError($error, ($commentStart + 1), 'SpacingBeforeShort', $errorData);
}
$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 %s comments';
$phpcsFile->addError($error, ($commentStart + $newlineCount + 1), 'SpacingAfterShort', $errorData);
}
$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 %s comments';
if ($long !== '') {
$newlineCount += (substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1);
}
$phpcsFile->addError($error, ($commentStart + $newlineCount), 'SpacingBeforeTags', $errorData);
$short = rtrim($short, $phpcsFile->eolChar.' ');
}
}
// Check each tag.
$this->processTags($commentStart, $commentEnd);
}//end process()
/**
* Process the version tag.
*
* @param int $errorPos The line number where the error occurs.
*
* @return void
*/
protected function processVersion($errorPos)
{
$version = $this->commentParser->getVersion();
if ($version !== null) {
$content = $version->getContent();
$matches = array();
if (empty($content) === true) {
$error = 'Content missing for @version tag in doc comment';
$this->currentFile->addError($error, $errorPos, 'EmptyVersion');
} else if ((strstr($content, 'Release:') === false)) {
$error = 'Invalid version "%s" in doc comment; consider "Release: <package_version>" instead';
$data = array($content);
$this->currentFile->addWarning($error, $errorPos, 'InvalidVersion', $data);
}
}
}//end processVersion()
}//end class
?>

View File

@@ -0,0 +1,791 @@
<?php
/**
* Parses and verifies the doc comments for files.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_CommentParser_ClassCommentParser', true) === false) {
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_CommentParser_ClassCommentParser not found');
}
/**
* Parses and verifies the doc comments for files.
*
* Verifies that :
* <ul>
* <li>A doc comment exists.</li>
* <li>There is a blank newline after the short description.</li>
* <li>There is a blank newline between the long and short description.</li>
* <li>There is a blank newline between the long description and tags.</li>
* <li>A PHP version is specified.</li>
* <li>Check the order of the tags.</li>
* <li>Check the indentation of each tag.</li>
* <li>Check required and optional tags and the format of their content.</li>
* </ul>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_Commenting_FileCommentSniff implements PHP_CodeSniffer_Sniff
{
/**
* The header comment parser for the current file.
*
* @var PHP_CodeSniffer_Comment_Parser_ClassCommentParser
*/
protected $commentParser = null;
/**
* The current PHP_CodeSniffer_File object we are processing.
*
* @var PHP_CodeSniffer_File
*/
protected $currentFile = null;
/**
* Tags in correct order and related info.
*
* @var array
*/
protected $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' => true,
'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' => true,
'allow_multiple' => false,
'order_text' => 'follows @copyright (if used) or @author',
),
'version' => array(
'required' => false,
'allow_multiple' => false,
'order_text' => 'follows @license',
),
'link' => array(
'required' => true,
'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',
),
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_OPEN_TAG);
}//end register()
/**
* 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);
// Allow declare() statements at the top of the file.
if ($tokens[$commentStart]['code'] === T_DECLARE) {
$semicolon = $phpcsFile->findNext(T_SEMICOLON, ($commentStart + 1));
$commentStart
= $phpcsFile->findNext(T_WHITESPACE, ($semicolon + 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
);
}
}
$errorToken = ($stackPtr + 1);
if (isset($tokens[$errorToken]) === false) {
$errorToken--;
}
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) {
$error = 'You must use "/**" style comments for a file comment';
$phpcsFile->addError($error, $errorToken, 'WrongStyle');
return;
} else if ($commentStart === false
|| $tokens[$commentStart]['code'] !== T_DOC_COMMENT
) {
$phpcsFile->addError('Missing file doc comment', $errorToken, 'Missing');
return;
} else {
// Extract the header comment docblock.
$commentEnd = $phpcsFile->findNext(
T_DOC_COMMENT,
($commentStart + 1),
null,
true
);
$commentEnd--;
// 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.
$error = 'Missing file doc comment';
$phpcsFile->addError($error, $errorToken, 'Missing');
return;
}
}
}//end if
$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, 'FailedParse');
return;
}
$comment = $this->commentParser->getComment();
if (is_null($comment) === true) {
$error = 'File doc comment is empty';
$phpcsFile->addError($error, $commentStart, 'Empty');
return;
}
// No extra newline before short description.
$short = $comment->getShortComment();
$newlineCount = 0;
$newlineSpan = strspn($short, $phpcsFile->eolChar);
if ($short !== '' && $newlineSpan > 0) {
$error = 'Extra newline(s) found before file comment short description';
$phpcsFile->addError($error, ($commentStart + 1), 'SpacingBefore');
}
$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), 'DescriptionSpacing');
}
$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), 'SpacingBeforeTags');
$short = rtrim($short, $phpcsFile->eolChar.' ');
}
}
// Check the PHP Version.
$this->processPHPVersion($commentStart, $commentEnd, $long);
// Check each tag.
$this->processTags($commentStart, $commentEnd);
}//end if
}//end process()
/**
* Check that the PHP version is specified.
*
* @param int $commentStart Position in the stack where the comment started.
* @param int $commentEnd Position in the stack where the comment ended.
* @param string $commentText The text of the function comment.
*
* @return void
*/
protected function processPHPVersion($commentStart, $commentEnd, $commentText)
{
if (strstr(strtolower($commentText), 'php version') === false) {
$error = 'PHP version not specified';
$this->currentFile->addWarning($error, $commentEnd, 'MissingVersion');
}
}//end processPHPVersion()
/**
* 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 @%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 + 1)
) {
$expected = (($longestTag - strlen($indentInfo['tag'])) + 1);
$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);
}
}
}//end processTags()
/**
* Get the indentation information of each tag.
*
* @param string $tagName The name of the
* doc comment
* element.
* @param PHP_CodeSniffer_CommentParser_DocElement $tagElement The doc comment
* element.
*
* @return void
*/
protected function getIndentation($tagName, $tagElement)
{
if ($tagElement instanceof PHP_CodeSniffer_CommentParser_SingleElement) {
if ($tagElement->getContent() !== '') {
return (strlen($tagName) + substr_count($tagElement->getWhitespaceBeforeContent(), ' '));
}
} else if ($tagElement instanceof PHP_CodeSniffer_CommentParser_PairElement) {
if ($tagElement->getValue() !== '') {
return (strlen($tagName) + substr_count($tagElement->getWhitespaceBeforeValue(), ' '));
}
}
return 0;
}//end getIndentation()
/**
* Process the category tag.
*
* @param int $errorPos The line number where the error occurs.
*
* @return void
*/
protected function processCategory($errorPos)
{
$category = $this->commentParser->getCategory();
if ($category !== null) {
$content = $category->getContent();
if ($content !== '') {
if (PHP_CodeSniffer::isUnderscoreName($content) !== true) {
$newContent = str_replace(' ', '_', $content);
$nameBits = explode('_', $newContent);
$firstBit = array_shift($nameBits);
$newName = ucfirst($firstBit).'_';
foreach ($nameBits as $bit) {
$newName .= ucfirst($bit).'_';
}
$error = 'Category name "%s" is not valid; consider "%s" instead';
$validName = trim($newName, '_');
$data = array(
$content,
$validName,
);
$this->currentFile->addError($error, $errorPos, 'InvalidCategory', $data);
}
} else {
$error = '@category tag must contain a name';
$this->currentFile->addError($error, $errorPos, 'EmptyCategory');
}
}
}//end processCategory()
/**
* Process the package tag.
*
* @param int $errorPos The line number where the error occurs.
*
* @return void
*/
protected function processPackage($errorPos)
{
$package = $this->commentParser->getPackage();
if ($package !== null) {
$content = $package->getContent();
if ($content !== '') {
if (PHP_CodeSniffer::isUnderscoreName($content) !== true) {
$newContent = str_replace(' ', '_', $content);
$nameBits = explode('_', $newContent);
$firstBit = array_shift($nameBits);
$newName = strtoupper($firstBit{0}).substr($firstBit, 1).'_';
foreach ($nameBits as $bit) {
$newName .= strtoupper($bit{0}).substr($bit, 1).'_';
}
$error = 'Package name "%s" is not valid; consider "%s" instead';
$validName = trim($newName, '_');
$data = array(
$content,
$validName,
);
$this->currentFile->addError($error, $errorPos, 'InvalidPackage', $data);
}
} else {
$error = '@package tag must contain a name';
$this->currentFile->addError($error, $errorPos, 'EmptyPackage');
}
}
}//end processPackage()
/**
* Process the subpackage tag.
*
* @param int $errorPos The line number where the error occurs.
*
* @return void
*/
protected function processSubpackage($errorPos)
{
$package = $this->commentParser->getSubpackage();
if ($package !== null) {
$content = $package->getContent();
if ($content !== '') {
if (PHP_CodeSniffer::isUnderscoreName($content) !== true) {
$newContent = str_replace(' ', '_', $content);
$nameBits = explode('_', $newContent);
$firstBit = array_shift($nameBits);
$newName = strtoupper($firstBit{0}).substr($firstBit, 1).'_';
foreach ($nameBits as $bit) {
$newName .= strtoupper($bit{0}).substr($bit, 1).'_';
}
$error = 'Subpackage name "%s" is not valid; consider "%s" instead';
$validName = trim($newName, '_');
$data = array(
$content,
$validName,
);
$this->currentFile->addError($error, $errorPos, 'InvalidSubpackage', $data);
}
} else {
$error = '@subpackage tag must contain a name';
$this->currentFile->addError($error, $errorPos, 'EmptySubpackage');
}
}
}//end processSubpackage()
/**
* Process the author tag(s) that this header comment has.
*
* This function is different from other _process functions
* as $authors is an array of SingleElements, so we work out
* the errorPos for each element separately
*
* @param int $commentStart The position in the stack where
* the comment started.
*
* @return void
*/
protected function processAuthors($commentStart)
{
$authors = $this->commentParser->getAuthors();
// Report missing return.
if (empty($authors) === false) {
foreach ($authors as $author) {
$errorPos = ($commentStart + $author->getLine());
$content = $author->getContent();
if ($content !== '') {
$local = '\da-zA-Z-_+';
// Dot character cannot be the first or last character
// in the local-part.
$localMiddle = $local.'.\w';
if (preg_match('/^([^<]*)\s+<(['.$local.']['.$localMiddle.']*['.$local.']@[\da-zA-Z][-.\w]*[\da-zA-Z]\.[a-zA-Z]{2,7})>$/', $content) === 0) {
$error = 'Content of the @author tag must be in the form "Display Name <username@example.com>"';
$this->currentFile->addError($error, $errorPos, 'InvalidAuthors');
}
} else {
$error = 'Content missing for @author tag in %s comment';
$docBlock = (get_class($this) === 'PEAR_Sniffs_Commenting_FileCommentSniff') ? 'file' : 'class';
$data = array($docBlock);
$this->currentFile->addError($error, $errorPos, 'EmptyAuthors', $data);
}
}
}
}//end processAuthors()
/**
* Process the copyright tags.
*
* @param int $commentStart The position in the stack where
* the comment started.
*
* @return void
*/
protected function processCopyrights($commentStart)
{
$copyrights = $this->commentParser->getCopyrights();
foreach ($copyrights as $copyright) {
$errorPos = ($commentStart + $copyright->getLine());
$content = $copyright->getContent();
if ($content !== '') {
$matches = array();
if (preg_match('/^([0-9]{4})((.{1})([0-9]{4}))? (.+)$/', $content, $matches) !== 0) {
// Check earliest-latest year order.
if ($matches[3] !== '') {
if ($matches[3] !== '-') {
$error = 'A hyphen must be used between the earliest and latest year';
$this->currentFile->addError($error, $errorPos, 'CopyrightHyphen');
}
if ($matches[4] !== '' && $matches[4] < $matches[1]) {
$error = "Invalid year span \"$matches[1]$matches[3]$matches[4]\" found; consider \"$matches[4]-$matches[1]\" instead";
$this->currentFile->addWarning($error, $errorPos, 'InvalidCopyright');
}
}
} else {
$error = '@copyright tag must contain a year and the name of the copyright holder';
$this->currentFile->addError($error, $errorPos, 'EmptyCopyright');
}
} else {
$error = '@copyright tag must contain a year and the name of the copyright holder';
$this->currentFile->addError($error, $errorPos, 'EmptyCopyright');
}//end if
}//end if
}//end processCopyrights()
/**
* Process the license tag.
*
* @param int $errorPos The line number where the error occurs.
*
* @return void
*/
protected function processLicense($errorPos)
{
$license = $this->commentParser->getLicense();
if ($license !== null) {
$value = $license->getValue();
$comment = $license->getComment();
if ($value === '' || $comment === '') {
$error = '@license tag must contain a URL and a license name';
$this->currentFile->addError($error, $errorPos, 'EmptyLicense');
}
}
}//end processLicense()
/**
* Process the version tag.
*
* @param int $errorPos The line number where the error occurs.
*
* @return void
*/
protected function processVersion($errorPos)
{
$version = $this->commentParser->getVersion();
if ($version !== null) {
$content = $version->getContent();
$matches = array();
if (empty($content) === true) {
$error = 'Content missing for @version tag in file comment';
$this->currentFile->addError($error, $errorPos, 'EmptyVersion');
} else if (strstr($content, 'CVS:') === false
&& strstr($content, 'SVN:') === false
&& strstr($content, 'GIT:') === false
) {
$error = 'Invalid version "%s" in file comment; consider "CVS: <cvs_id>" or "SVN: <svn_id>" or "GIT: <git_id>" instead';
$data = array($content);
$this->currentFile->addWarning($error, $errorPos, 'InvalidVersion', $data);
}
}
}//end processVersion()
}//end class
?>

View File

@@ -0,0 +1,490 @@
<?php
/**
* Parses and verifies the doc comments for functions.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_CommentParser_FunctionCommentParser', true) === false) {
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_CommentParser_FunctionCommentParser not found');
}
/**
* 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>There is a blank newline between the long and short description.</li>
* <li>There is a blank newline between the long description and tags.</li>
* <li>Parameter names represent those in the method.</li>
* <li>Parameter comments are in the correct order</li>
* <li>Parameter comments are complete</li>
* <li>A space is present before the first and after the last parameter</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>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_Commenting_FunctionCommentSniff implements PHP_CodeSniffer_Sniff
{
/**
* The name of the method that we are currently processing.
*
* @var string
*/
private $_methodName = '';
/**
* The position in the stack where the fucntion token was found.
*
* @var int
*/
private $_functionToken = null;
/**
* The position in the stack where the class token was found.
*
* @var int
*/
private $_classToken = null;
/**
* The function comment parser for the current method.
*
* @var PHP_CodeSniffer_Comment_Parser_FunctionCommentParser
*/
protected $commentParser = null;
/**
* The current PHP_CodeSniffer_File object we are processing.
*
* @var PHP_CodeSniffer_File
*/
protected $currentFile = null;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_FUNCTION);
}//end register()
/**
* 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)
{
$find = array(
T_COMMENT,
T_DOC_COMMENT,
T_CLASS,
T_FUNCTION,
T_OPEN_TAG,
);
$commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1));
if ($commentEnd === false) {
return;
}
$this->currentFile = $phpcsFile;
$tokens = $phpcsFile->getTokens();
// If the token that we found was a class or a function, then this
// function has no doc comment.
$code = $tokens[$commentEnd]['code'];
if ($code === T_COMMENT) {
$error = 'You must use "/**" style comments for a function comment';
$phpcsFile->addError($error, $stackPtr, 'WrongStyle');
return;
} else if ($code !== T_DOC_COMMENT) {
$phpcsFile->addError('Missing function doc comment', $stackPtr, 'Missing');
return;
}
// If there is any code between the function keyword and the doc block
// then the doc block is not for us.
$ignore = PHP_CodeSniffer_Tokens::$scopeModifiers;
$ignore[] = T_STATIC;
$ignore[] = T_WHITESPACE;
$ignore[] = T_ABSTRACT;
$ignore[] = T_FINAL;
$prevToken = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true);
if ($prevToken !== $commentEnd) {
$phpcsFile->addError('Missing function doc comment', $stackPtr, 'Missing');
return;
}
$this->_functionToken = $stackPtr;
$this->_classToken = null;
foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condition) {
if ($condition === T_CLASS || $condition === T_INTERFACE) {
$this->_classToken = $condPtr;
break;
}
}
// If the first T_OPEN_TAG is right before the comment, it is probably
// a file comment.
$commentStart = ($phpcsFile->findPrevious(T_DOC_COMMENT, ($commentEnd - 1), null, true) + 1);
$prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($commentStart - 1), null, true);
if ($tokens[$prevToken]['code'] === T_OPEN_TAG) {
// Is this the first open tag?
if ($stackPtr === 0 || $phpcsFile->findPrevious(T_OPEN_TAG, ($prevToken - 1)) === false) {
$phpcsFile->addError('Missing function doc comment', $stackPtr, 'Missing');
return;
}
}
$comment = $phpcsFile->getTokensAsString($commentStart, ($commentEnd - $commentStart + 1));
$this->_methodName = $phpcsFile->getDeclarationName($stackPtr);
try {
$this->commentParser = new PHP_CodeSniffer_CommentParser_FunctionCommentParser($comment, $phpcsFile);
$this->commentParser->parse();
} catch (PHP_CodeSniffer_CommentParser_ParserException $e) {
$line = ($e->getLineWithinComment() + $commentStart);
$phpcsFile->addError($e->getMessage(), $line, 'FailedParse');
return;
}
$comment = $this->commentParser->getComment();
if (is_null($comment) === true) {
$error = 'Function doc comment is empty';
$phpcsFile->addError($error, $commentStart, 'Empty');
return;
}
$this->processParams($commentStart);
$this->processReturn($commentStart, $commentEnd);
$this->processThrows($commentStart);
// No extra newline before short description.
$short = $comment->getShortComment();
$newlineCount = 0;
$newlineSpan = strspn($short, $phpcsFile->eolChar);
if ($short !== '' && $newlineSpan > 0) {
$error = 'Extra newline(s) found before function comment short description';
$phpcsFile->addError($error, ($commentStart + 1), 'SpacingBeforeShort');
}
$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 function comment';
$phpcsFile->addError($error, ($commentStart + $newlineCount + 1), 'SpacingAfterShort');
}
$newlineCount += $newlineBetween;
}
// Exactly one blank line before tags.
$params = $this->commentParser->getTagOrders();
if (count($params) > 1) {
$newlineSpan = $comment->getNewlineAfter();
if ($newlineSpan !== 2) {
$error = 'There must be exactly one blank line before the tags in function comment';
if ($long !== '') {
$newlineCount += (substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1);
}
$phpcsFile->addError($error, ($commentStart + $newlineCount), 'SpacingBeforeTags');
$short = rtrim($short, $phpcsFile->eolChar.' ');
}
}
}//end process()
/**
* Process any throw tags that this function comment has.
*
* @param int $commentStart The position in the stack where the
* comment started.
*
* @return void
*/
protected function processThrows($commentStart)
{
if (count($this->commentParser->getThrows()) === 0) {
return;
}
foreach ($this->commentParser->getThrows() as $throw) {
$exception = $throw->getValue();
$errorPos = ($commentStart + $throw->getLine());
if ($exception === '') {
$error = '@throws tag must contain the exception class name';
$this->currentFile->addError($error, $errorPos, 'EmptyThrows');
}
}
}//end processThrows()
/**
* Process the return comment of this function comment.
*
* @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 processReturn($commentStart, $commentEnd)
{
// Skip constructor and destructor.
$className = '';
if ($this->_classToken !== null) {
$className = $this->currentFile->getDeclarationName($this->_classToken);
$className = strtolower(ltrim($className, '_'));
}
$methodName = strtolower(ltrim($this->_methodName, '_'));
$isSpecialMethod = ($this->_methodName === '__construct' || $this->_methodName === '__destruct');
if ($isSpecialMethod === false && $methodName !== $className) {
// Report missing return tag.
if ($this->commentParser->getReturn() === null) {
$error = 'Missing @return tag in function comment';
$this->currentFile->addError($error, $commentEnd, 'MissingReturn');
} else if (trim($this->commentParser->getReturn()->getRawContent()) === '') {
$error = '@return tag is empty in function comment';
$errorPos = ($commentStart + $this->commentParser->getReturn()->getLine());
$this->currentFile->addError($error, $errorPos, 'EmptyReturn');
}
}
}//end processReturn()
/**
* Process the function parameter comments.
*
* @param int $commentStart The position in the stack where
* the comment started.
*
* @return void
*/
protected function processParams($commentStart)
{
$realParams = $this->currentFile->getMethodParameters($this->_functionToken);
$params = $this->commentParser->getParams();
$foundParams = array();
if (empty($params) === false) {
$lastParm = (count($params) - 1);
if (substr_count($params[$lastParm]->getWhitespaceAfter(), $this->currentFile->eolChar) !== 2) {
$error = 'Last parameter comment requires a blank newline after it';
$errorPos = ($params[$lastParm]->getLine() + $commentStart);
$this->currentFile->addError($error, $errorPos, 'SpacingAfterParams');
}
// Parameters must appear immediately after the comment.
if ($params[0]->getOrder() !== 2) {
$error = 'Parameters must appear immediately after the comment';
$errorPos = ($params[0]->getLine() + $commentStart);
$this->currentFile->addError($error, $errorPos, 'SpacingBeforeParams');
}
$previousParam = null;
$spaceBeforeVar = 10000;
$spaceBeforeComment = 10000;
$longestType = 0;
$longestVar = 0;
foreach ($params as $param) {
$paramComment = trim($param->getComment());
$errorPos = ($param->getLine() + $commentStart);
// Make sure that there is only one space before the var type.
if ($param->getWhitespaceBeforeType() !== ' ') {
$error = 'Expected 1 space before variable type';
$this->currentFile->addError($error, $errorPos, 'SpacingBeforeParamType');
}
$spaceCount = substr_count($param->getWhitespaceBeforeVarName(), ' ');
if ($spaceCount < $spaceBeforeVar) {
$spaceBeforeVar = $spaceCount;
$longestType = $errorPos;
}
$spaceCount = substr_count($param->getWhitespaceBeforeComment(), ' ');
if ($spaceCount < $spaceBeforeComment && $paramComment !== '') {
$spaceBeforeComment = $spaceCount;
$longestVar = $errorPos;
}
// Make sure they are in the correct order,
// and have the correct name.
$pos = $param->getPosition();
$paramName = ($param->getVarName() !== '') ? $param->getVarName() : '[ UNKNOWN ]';
if ($previousParam !== null) {
$previousName = ($previousParam->getVarName() !== '') ? $previousParam->getVarName() : 'UNKNOWN';
// Check to see if the parameters align properly.
if ($param->alignsVariableWith($previousParam) === false) {
$error = 'The variable names for parameters %s (%s) and %s (%s) do not align';
$data = array(
$previousName,
($pos - 1),
$paramName,
$pos,
);
$this->currentFile->addError($error, $errorPos, 'ParameterNamesNotAligned', $data);
}
if ($param->alignsCommentWith($previousParam) === false) {
$error = 'The comments for parameters %s (%s) and %s (%s) do not align';
$data = array(
$previousName,
($pos - 1),
$paramName,
$pos,
);
$this->currentFile->addError($error, $errorPos, 'ParameterCommentsNotAligned', $data);
}
}//end if
// Make sure the names of the parameter comment matches the
// actual parameter.
if (isset($realParams[($pos - 1)]) === true) {
$realName = $realParams[($pos - 1)]['name'];
$foundParams[] = $realName;
// Append ampersand to name if passing by reference.
if ($realParams[($pos - 1)]['pass_by_reference'] === true) {
$realName = '&'.$realName;
}
if ($realName !== $paramName) {
$code = 'ParamNameNoMatch';
$data = array(
$paramName,
$realName,
$pos,
);
$error = 'Doc comment for var %s does not match ';
if (strtolower($paramName) === strtolower($realName)) {
$error .= 'case of ';
$code = 'ParamNameNoCaseMatch';
}
$error .= 'actual variable name %s at position %s';
$this->currentFile->addError($error, $errorPos, $code, $data);
}
} else {
// We must have an extra parameter comment.
$error = 'Superfluous doc comment at position '.$pos;
$this->currentFile->addError($error, $errorPos, 'ExtraParamComment');
}
if ($param->getVarName() === '') {
$error = 'Missing parameter name at position '.$pos;
$this->currentFile->addError($error, $errorPos, 'MissingParamName');
}
if ($param->getType() === '') {
$error = 'Missing type at position '.$pos;
$this->currentFile->addError($error, $errorPos, 'MissingParamType');
}
if ($paramComment === '') {
$error = 'Missing comment for param "%s" at position %s';
$data = array(
$paramName,
$pos,
);
$this->currentFile->addError($error, $errorPos, 'MissingParamComment', $data);
}
$previousParam = $param;
}//end foreach
if ($spaceBeforeVar !== 1 && $spaceBeforeVar !== 10000 && $spaceBeforeComment !== 10000) {
$error = 'Expected 1 space after the longest type';
$this->currentFile->addError($error, $longestType, 'SpacingAfterLongType');
}
if ($spaceBeforeComment !== 1 && $spaceBeforeComment !== 10000) {
$error = 'Expected 1 space after the longest variable name';
$this->currentFile->addError($error, $longestVar, 'SpacingAfterLongName');
}
}//end if
$realNames = array();
foreach ($realParams as $realParam) {
$realNames[] = $realParam['name'];
}
// Report and missing comments.
$diff = array_diff($realNames, $foundParams);
foreach ($diff as $neededParam) {
if (count($params) !== 0) {
$errorPos = ($params[(count($params) - 1)]->getLine() + $commentStart);
} else {
$errorPos = $commentStart;
}
$error = 'Doc comment for "%s" missing';
$data = array($neededParam);
$this->currentFile->addError($error, $errorPos, 'MissingParamTag', $data);
}
}//end processParams()
}//end class
?>

View File

@@ -0,0 +1,70 @@
<?php
/**
* PHP_CodeSniffer_Sniffs_PEAR_Commenting_InlineCommentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PHP_CodeSniffer_Sniffs_PEAR_Commenting_InlineCommentSniff.
*
* Checks that no perl-style comments are used.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_Commenting_InlineCommentSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_COMMENT);
}//end register()
/**
* 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)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['content']{0} === '#') {
$error = 'Perl-style comments are not allowed. Use "// Comment."';
$error .= ' or "/* comment */" instead.';
$phpcsFile->addError($error, $stackPtr, 'WrongStyle');
}
}//end process()
}//end class
?>

View File

@@ -0,0 +1,70 @@
<?php
/**
* Verifies that control statements conform to their coding standards.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_Standards_AbstractPatternSniff', true) === false) {
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Standards_AbstractPatternSniff not found');
}
/**
* Verifies that control statements conform to their coding standards.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_ControlStructures_ControlSignatureSniff extends PHP_CodeSniffer_Standards_AbstractPatternSniff
{
/**
* Constructs a PEAR_Sniffs_ControlStructures_ControlSignatureSniff.
*/
public function __construct()
{
parent::__construct(true);
}//end __construct()
/**
* Returns the patterns that this test wishes to verify.
*
* @return array(string)
*/
protected function getPatterns()
{
return array(
'do {EOL...} while (...);EOL',
'while (...) {EOL',
'for (...) {EOL',
'if (...) {EOL',
'foreach (...) {EOL',
'} else if (...) {EOL',
'} elseif (...) {EOL',
'} else {EOL',
'do {EOL',
);
}//end getPatterns()
}//end class
?>

View File

@@ -0,0 +1,176 @@
<?php
/**
* PEAR_Sniffs_ControlStructures_MultiLineConditionSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PEAR_Sniffs_ControlStructures_MultiLineConditionSniff.
*
* Ensure multi-line IF conditions are defined correctly.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_ControlStructures_MultiLineConditionSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_IF);
}//end register()
/**
* 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)
{
$tokens = $phpcsFile->getTokens();
// We need to work out how far indented the if statement
// itself is, so we can work out how far to indent conditions.
$statementIndent = 0;
for ($i = ($stackPtr - 1); $i >= 0; $i--) {
if ($tokens[$i]['line'] !== $tokens[$stackPtr]['line']) {
$i++;
break;
}
}
if ($i >= 0 && $tokens[$i]['code'] === T_WHITESPACE) {
$statementIndent = strlen($tokens[$i]['content']);
}
// Each line between the parenthesis should be indented 4 spaces
// and start with an operator, unless the line is inside a
// function call, in which case it is ignored.
$openBracket = $tokens[$stackPtr]['parenthesis_opener'];
$closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
$lastLine = $tokens[$openBracket]['line'];
for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
if ($tokens[$i]['line'] !== $lastLine) {
if ($tokens[$i]['line'] === $tokens[$closeBracket]['line']) {
$next = $phpcsFile->findNext(T_WHITESPACE, $i, null, true);
if ($next !== $closeBracket) {
// Closing bracket is on the same line as a condition.
$error = 'Closing parenthesis of a multi-line IF statement must be on a new line';
$phpcsFile->addError($error, $i, 'CloseBracketNewLine');
$expectedIndent = ($statementIndent + 4);
} else {
// Closing brace needs to be indented to the same level
// as the function.
$expectedIndent = $statementIndent;
}
} else {
$expectedIndent = ($statementIndent + 4);
}
// We changed lines, so this should be a whitespace indent token.
if ($tokens[$i]['code'] !== T_WHITESPACE) {
$foundIndent = 0;
} else {
$foundIndent = strlen($tokens[$i]['content']);
}
if ($expectedIndent !== $foundIndent) {
$error = 'Multi-line IF statement not indented correctly; expected %s spaces but found %s';
$data = array(
$expectedIndent,
$foundIndent,
);
$phpcsFile->addError($error, $i, 'Alignment', $data);
}
if ($tokens[$i]['line'] !== $tokens[$closeBracket]['line']) {
$next = $phpcsFile->findNext(T_WHITESPACE, $i, null, true);
if (in_array($tokens[$next]['code'], PHP_CodeSniffer_Tokens::$booleanOperators) === false) {
$error = 'Each line in a multi-line IF statement must begin with a boolean operator';
$phpcsFile->addError($error, $i, 'StartWithBoolean');
}
}
$lastLine = $tokens[$i]['line'];
}//end if
if ($tokens[$i]['code'] === T_STRING) {
$next = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), null, true);
if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS) {
// This is a function call, so skip to the end as they
// have their own indentation rules.
$i = $tokens[$next]['parenthesis_closer'];
$lastLine = $tokens[$i]['line'];
continue;
}
}
}//end for
// From here on, we are checking the spacing of the opening and closing
// braces. If this IF statement does not use braces, we end here.
if (isset($tokens[$stackPtr]['scope_opener']) === false) {
return;
}
// The opening brace needs to be one space away from the closing parenthesis.
if ($tokens[($closeBracket + 1)]['code'] !== T_WHITESPACE) {
$length = 0;
} else if ($tokens[($closeBracket + 1)]['content'] === $phpcsFile->eolChar) {
$length = -1;
} else {
$length = strlen($tokens[($closeBracket + 1)]['content']);
}
if ($length !== 1) {
$data = array($length);
$code = 'SpaceBeforeOpenBrace';
$error = 'There must be a single space between the closing parenthesis and the opening brace of a multi-line IF statement; found ';
if ($length === -1) {
$error .= 'newline';
$code = 'NewlineBeforeOpenBrace';
} else {
$error .= '%s spaces';
}
$phpcsFile->addError($error, ($closeBracket + 1), $code, $data);
}
// And just in case they do something funny before the brace...
$next = $phpcsFile->findNext(T_WHITESPACE, ($closeBracket + 1), null, true);
if ($next !== false && $tokens[$next]['code'] !== T_OPEN_CURLY_BRACKET) {
$error = 'There must be a single space between the closing parenthesis and the opening brace of a multi-line IF statement';
$phpcsFile->addError($error, $next, 'NoSpaceBeforeOpenBrace');
}
}//end process()
}//end class
?>

View File

@@ -0,0 +1,137 @@
<?php
/**
* PEAR_Sniffs_Files_IncludingFileSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PEAR_Sniffs_Files_IncludingFileSniff.
*
* Checks that the include_once is used in conditional situations, and
* require_once is used elsewhere. Also checks that brackets do not surround
* the file being included.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_Files_IncludingFileSniff implements PHP_CodeSniffer_Sniff
{
/**
* Conditions that should use include_once
*
* @var array(int)
*/
private static $_conditions = array(
T_IF,
T_ELSE,
T_ELSEIF,
T_SWITCH,
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_INCLUDE_ONCE,
T_REQUIRE_ONCE,
T_REQUIRE,
T_INCLUDE,
);
}//end register()
/**
* 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)
{
$tokens = $phpcsFile->getTokens();
$nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS) {
$error = '"%s" is a statement not a function; no parentheses are required';
$data = array($tokens[$stackPtr]['content']);
$phpcsFile->addError($error, $stackPtr, 'BracketsNotRequired', $data);
}
$inCondition = (count($tokens[$stackPtr]['conditions']) !== 0) ? true : false;
// Check to see if this including statement is within the parenthesis
// of a condition. If that's the case then we need to process it as being
// within a condition, as they are checking the return value.
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
foreach ($tokens[$stackPtr]['nested_parenthesis'] as $left => $right) {
if (isset($tokens[$left]['parenthesis_owner']) === true) {
$inCondition = true;
}
}
}
// Check to see if they are assigning the return value of this
// including call. If they are then they are probably checking it, so
// it's conditional.
$previous = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if (in_array($tokens[$previous]['code'], PHP_CodeSniffer_Tokens::$assignmentTokens) === true) {
// The have assigned the return value to it, so its conditional.
$inCondition = true;
}
$tokenCode = $tokens[$stackPtr]['code'];
if ($inCondition === true) {
// We are inside a conditional statement. We need an include_once.
if ($tokenCode === T_REQUIRE_ONCE) {
$error = 'File is being conditionally included; ';
$error .= 'use "include_once" instead';
$phpcsFile->addError($error, $stackPtr, 'UseIncludeOnce');
} else if ($tokenCode === T_REQUIRE) {
$error = 'File is being conditionally included; ';
$error .= 'use "include" instead';
$phpcsFile->addError($error, $stackPtr, 'UseInclude');
}
} else {
// We are unconditionally including, we need a require_once.
if ($tokenCode === T_INCLUDE_ONCE) {
$error = 'File is being unconditionally included; ';
$error .= 'use "require_once" instead';
$phpcsFile->addError($error, $stackPtr, 'UseRequireOnce');
} else if ($tokenCode === T_INCLUDE) {
$error = 'File is being unconditionally included; ';
$error .= 'use "require" instead';
$phpcsFile->addError($error, $stackPtr, 'UseRequire');
}
}//end if
}//end process()
}//end class
?>

View File

@@ -0,0 +1,113 @@
<?php
/**
* PEAR_Sniffs_Functions_FunctionDeclarationSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PEAR_Sniffs_Formatting_MultiLineAssignmentSniff.
*
* If an assignment goes over two lines, ensure the equal sign is indented.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_Formatting_MultiLineAssignmentSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_EQUAL);
}//end register()
/**
* 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)
{
$tokens = $phpcsFile->getTokens();
// Equal sign can't be the last thing on the line.
$next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
if ($next === false) {
// Bad assignment.
return;
}
if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
$error = 'Multi-line assignments must have the equal sign on the second line';
$phpcsFile->addError($error, $stackPtr, 'EqualSignLine');
return;
}
// Make sure it is the first thing on the line, otherwise we ignore it.
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), false, true);
if ($prev === false) {
// Bad assignment.
return;
}
if ($tokens[$prev]['line'] === $tokens[$stackPtr]['line']) {
return;
}
// Find the required indent based on the ident of the previous line.
$assignmentIndent = 0;
$prevLine = $tokens[$prev]['line'];
for ($i = ($prev - 1); $i >= 0; $i--) {
if ($tokens[$i]['line'] !== $prevLine) {
$i++;
break;
}
}
if ($tokens[$i]['code'] === T_WHITESPACE) {
$assignmentIndent = strlen($tokens[$i]['content']);
}
// Find the actual indent.
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1));
$expectedIndent = ($assignmentIndent + 4);
$foundIndent = strlen($tokens[$prev]['content']);
if ($foundIndent !== $expectedIndent) {
$error = 'Multi-line assignment not indented correctly; expected %s spaces but found %s';
$data = array(
$expectedIndent,
$foundIndent,
);
$phpcsFile->addError($error, $stackPtr, 'Indent', $data);
}
}//end process()
}//end class
?>

View File

@@ -0,0 +1,259 @@
<?php
/**
* PEAR_Sniffs_Functions_FunctionCallSignatureSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PEAR_Sniffs_Functions_FunctionCallSignatureSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_Functions_FunctionCallSignatureSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_STRING);
}//end register()
/**
* 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)
{
$tokens = $phpcsFile->getTokens();
// Find the next non-empty token.
$openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
// Not a function call.
return;
}
if (isset($tokens[$openBracket]['parenthesis_closer']) === false) {
// Not a function call.
return;
}
// Find the previous non-empty token.
$search = PHP_CodeSniffer_Tokens::$emptyTokens;
$search[] = T_BITWISE_AND;
$previous = $phpcsFile->findPrevious($search, ($stackPtr - 1), null, true);
if ($tokens[$previous]['code'] === T_FUNCTION) {
// It's a function definition, not a function call.
return;
}
$closeBracket = $tokens[$openBracket]['parenthesis_closer'];
if (($stackPtr + 1) !== $openBracket) {
// Checking this: $value = my_function[*](...).
$error = 'Space before opening parenthesis of function call prohibited';
$phpcsFile->addError($error, $stackPtr, 'SpaceBeforeOpenBracket');
}
$next = $phpcsFile->findNext(T_WHITESPACE, ($closeBracket + 1), null, true);
if ($tokens[$next]['code'] === T_SEMICOLON) {
if (in_array($tokens[($closeBracket + 1)]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
$error = 'Space after closing parenthesis of function call prohibited';
$phpcsFile->addError($error, $closeBracket, 'SpaceAfterCloseBracket');
}
}
// Check if this is a single line or multi-line function call.
if ($tokens[$openBracket]['line'] === $tokens[$closeBracket]['line']) {
$this->processSingleLineCall($phpcsFile, $stackPtr, $openBracket, $tokens);
} else {
$this->processMultiLineCall($phpcsFile, $stackPtr, $openBracket, $tokens);
}
}//end process()
/**
* Processes single-line calls.
*
* @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 $openBracket The position of the openning bracket
* in the stack passed in $tokens.
* @param array $tokens The stack of tokens that make up
* the file.
*
* @return void
*/
public function processSingleLineCall(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $openBracket, $tokens)
{
if ($tokens[($openBracket + 1)]['code'] === T_WHITESPACE) {
// Checking this: $value = my_function([*]...).
$error = 'Space after opening parenthesis of function call prohibited';
$phpcsFile->addError($error, $stackPtr, 'SpaceAfterOpenBracket');
}
$closer = $tokens[$openBracket]['parenthesis_closer'];
if ($tokens[($closer - 1)]['code'] === T_WHITESPACE) {
// Checking this: $value = my_function(...[*]).
$between = $phpcsFile->findNext(T_WHITESPACE, ($openBracket + 1), null, true);
// Only throw an error if there is some content between the parenthesis.
// i.e., Checking for this: $value = my_function().
// If there is no content, then we would have thrown an error in the
// previous IF statement because it would look like this:
// $value = my_function( ).
if ($between !== $closer) {
$error = 'Space before closing parenthesis of function call prohibited';
$phpcsFile->addError($error, $closer, 'SpaceBeforeCloseBracket');
}
}
}//end processSingleLineCall()
/**
* Processes multi-line calls.
*
* @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 $openBracket The position of the openning bracket
* in the stack passed in $tokens.
* @param array $tokens The stack of tokens that make up
* the file.
*
* @return void
*/
public function processMultiLineCall(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $openBracket, $tokens)
{
// We need to work out how far indented the function
// call itself is, so we can work out how far to
// indent the arguments.
$functionIndent = 0;
for ($i = ($stackPtr - 1); $i >= 0; $i--) {
if ($tokens[$i]['line'] !== $tokens[$stackPtr]['line']) {
$i++;
break;
}
}
if ($tokens[$i]['code'] === T_WHITESPACE) {
$functionIndent = strlen($tokens[$i]['content']);
}
// Each line between the parenthesis should be indented 4 spaces.
$closeBracket = $tokens[$openBracket]['parenthesis_closer'];
$lastLine = $tokens[$openBracket]['line'];
for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
// Skip nested function calls.
if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
$i = $tokens[$i]['parenthesis_closer'];
$lastLine = $tokens[$i]['line'];
continue;
}
if ($tokens[$i]['line'] !== $lastLine) {
$lastLine = $tokens[$i]['line'];
// Ignore heredoc indentation.
if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$heredocTokens) === true) {
continue;
}
// Ignore multi-line string indentation.
if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) {
if ($tokens[$i]['code'] === $tokens[($i - 1)]['code']) {
continue;
}
}
// We changed lines, so this should be a whitespace indent token, but first make
// sure it isn't a blank line because we don't need to check indent unless there
// is actually some code to indent.
$nextCode = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), ($closeBracket + 1), true);
if ($tokens[$nextCode]['line'] !== $lastLine) {
$error = 'Empty lines are not allowed in multi-line function calls';
$phpcsFile->addError($error, $i, 'EmptyLine');
continue;
}
if ($nextCode === $closeBracket) {
// Closing brace needs to be indented to the same level
// as the function call.
$expectedIndent = $functionIndent;
} else {
$expectedIndent = ($functionIndent + 4);
}
if ($tokens[$i]['code'] !== T_WHITESPACE) {
$foundIndent = 0;
} else {
$foundIndent = strlen($tokens[$i]['content']);
}
if ($expectedIndent !== $foundIndent) {
$error = 'Multi-line function call not indented correctly; expected %s spaces but found %s';
$data = array(
$expectedIndent,
$foundIndent,
);
$phpcsFile->addError($error, $i, 'Indent', $data);
}
}//end if
// Skip the rest of a closure.
if ($tokens[$i]['code'] === T_CLOSURE) {
$i = $tokens[$i]['scope_closer'];
$lastLine = $tokens[$i]['line'];
continue;
}
}//end for
if ($tokens[($openBracket + 1)]['content'] !== $phpcsFile->eolChar) {
$error = 'Opening parenthesis of a multi-line function call must be the last content on the line';
$phpcsFile->addError($error, $stackPtr, 'ContentAfterOpenBracket');
}
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBracket - 1), null, true);
if ($tokens[$prev]['line'] === $tokens[$closeBracket]['line']) {
$error = 'Closing parenthesis of a multi-line function call must be on a line by itself';
$phpcsFile->addError($error, $closeBracket, 'CloseBracketLine');
}
}//end processMultiLineCall()
}//end class
?>

View File

@@ -0,0 +1,224 @@
<?php
/**
* PEAR_Sniffs_Functions_FunctionDeclarationSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PEAR_Sniffs_Functions_FunctionDeclarationSniff.
*
* Ensure single and multi-line function declarations are defined correctly.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_Functions_FunctionDeclarationSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_FUNCTION);
}//end register()
/**
* 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)
{
$tokens = $phpcsFile->getTokens();
// Check if this is a single line or multi-line declaration.
$openBracket = $tokens[$stackPtr]['parenthesis_opener'];
$closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
if ($tokens[$openBracket]['line'] === $tokens[$closeBracket]['line']) {
$this->processSingleLineDeclaration($phpcsFile, $stackPtr, $tokens);
} else {
$this->processMultiLineDeclaration($phpcsFile, $stackPtr, $tokens);
}
}//end process()
/**
* Processes single-line declarations.
*
* Just uses the Generic BSD-Allman brace sniff.
*
* @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 array $tokens The stack of tokens that make up
* the file.
*
* @return void
*/
public function processSingleLineDeclaration(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $tokens)
{
if (class_exists('Generic_Sniffs_Functions_OpeningFunctionBraceBsdAllmanSniff', true) === false) {
throw new PHP_CodeSniffer_Exception('Class Generic_Sniffs_Functions_OpeningFunctionBraceBsdAllmanSniff not found');
}
$sniff = new Generic_Sniffs_Functions_OpeningFunctionBraceBsdAllmanSniff();
$sniff->process($phpcsFile, $stackPtr);
}//end processSingleLineDeclaration()
/**
* Processes mutli-line declarations.
*
* @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 array $tokens The stack of tokens that make up
* the file.
*
* @return void
*/
public function processMultiLineDeclaration(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $tokens)
{
// We need to work out how far indented the function
// declaration itself is, so we can work out how far to
// indent parameters.
$functionIndent = 0;
for ($i = ($stackPtr - 1); $i >= 0; $i--) {
if ($tokens[$i]['line'] !== $tokens[$stackPtr]['line']) {
$i++;
break;
}
}
if ($tokens[$i]['code'] === T_WHITESPACE) {
$functionIndent = strlen($tokens[$i]['content']);
}
// Each line between the parenthesis should be indented 4 spaces.
$openBracket = $tokens[$stackPtr]['parenthesis_opener'];
$closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
$lastLine = $tokens[$openBracket]['line'];
for ($i = ($openBracket + 1); $i < $closeBracket; $i++) {
if ($tokens[$i]['line'] !== $lastLine) {
if ($tokens[$i]['line'] === $tokens[$closeBracket]['line']) {
// Closing brace needs to be indented to the same level
// as the function.
$expectedIndent = $functionIndent;
} else {
$expectedIndent = ($functionIndent + 4);
}
// We changed lines, so this should be a whitespace indent token.
if ($tokens[$i]['code'] !== T_WHITESPACE) {
$foundIndent = 0;
} else {
$foundIndent = strlen($tokens[$i]['content']);
}
if ($expectedIndent !== $foundIndent) {
$error = 'Multi-line function declaration not indented correctly; expected %s spaces but found %s';
$data = array(
$expectedIndent,
$foundIndent,
);
$phpcsFile->addError($error, $i, 'Indent', $data);
}
$lastLine = $tokens[$i]['line'];
}//end if
if ($tokens[$i]['code'] === T_ARRAY) {
// Skip arrays as they have their own indentation rules.
$i = $tokens[$i]['parenthesis_closer'];
$lastLine = $tokens[$i]['line'];
continue;
}
}//end for
if (isset($tokens[$stackPtr]['scope_opener']) === true) {
// The openning brace needs to be one space away
// from the closing parenthesis.
$next = $tokens[($closeBracket + 1)];
if ($next['code'] !== T_WHITESPACE) {
$length = 0;
} else if ($next['content'] === $phpcsFile->eolChar) {
$length = -1;
} else {
$length = strlen($next['content']);
}
if ($length !== 1) {
$data = array($length);
$code = 'SpaceBeforeOpenBrace';
$error = 'There must be a single space between the closing parenthesis and the opening brace of a multi-line function declaration; found ';
if ($length === -1) {
$error .= 'newline';
$code = 'NewlineBeforeOpenBrace';
} else {
$error .= '%s spaces';
}
$phpcsFile->addError($error, ($closeBracket + 1), $code, $data);
return;
}
// And just in case they do something funny before the brace...
$next = $phpcsFile->findNext(
T_WHITESPACE,
($closeBracket + 1),
null,
true
);
if ($next !== false && $tokens[$next]['code'] !== T_OPEN_CURLY_BRACKET) {
$error = 'There must be a single space between the closing parenthesis and the opening brace of a multi-line function declaration';
$phpcsFile->addError($error, $next, 'NoSpaceBeforeOpenBrace');
}
}//end if
// The closing parenthesis must be on a new line, even
// when checking abstract function definitions.
$prev = $phpcsFile->findPrevious(
T_WHITESPACE,
($closeBracket - 1),
null,
true
);
if ($tokens[$prev]['line'] === $tokens[$closeBracket]['line']) {
$error = 'The closing parenthesis of a multi-line function declaration must be on a new line';
$phpcsFile->addError($error, $closeBracket, 'CloseBracketLine');
}
}//end processMultiLineDeclaration()
}//end class
?>

View File

@@ -0,0 +1,108 @@
<?php
/**
* PEAR_Sniffs_Functions_ValidDefaultValueSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PEAR_Sniffs_Functions_ValidDefaultValueSniff.
*
* A Sniff to ensure that parameters defined for a function that have a default
* value come at the end of the function signature.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_Functions_ValidDefaultValueSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_FUNCTION);
}//end register()
/**
* 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)
{
$tokens = $phpcsFile->getTokens();
$argStart = $tokens[$stackPtr]['parenthesis_opener'];
$argEnd = $tokens[$stackPtr]['parenthesis_closer'];
// Flag for when we have found a default in our arg list.
// If there is a value without a default after this, it is an error.
$defaultFound = false;
$nextArg = $argStart;
while (($nextArg = $phpcsFile->findNext(T_VARIABLE, ($nextArg + 1), $argEnd)) !== false) {
$argHasDefault = self::_argHasDefault($phpcsFile, $nextArg);
if (($argHasDefault === false) && ($defaultFound === true)) {
$error = 'Arguments with default values must be at the end of the argument list';
$phpcsFile->addError($error, $nextArg, 'NotAtEnd');
return;
}
if ($argHasDefault === true) {
$defaultFound = true;
}
}
}//end process()
/**
* Returns true if the passed argument has a default value.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param int $argPtr The position of the argument
* in the stack.
*
* @return bool
*/
private static function _argHasDefault(PHP_CodeSniffer_File $phpcsFile, $argPtr)
{
$tokens = $phpcsFile->getTokens();
$nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($argPtr + 1), null, true);
if ($tokens[$nextToken]['code'] !== T_EQUAL) {
return false;
}
return true;
}//end _argHasDefault()
}//end class
?>

View File

@@ -0,0 +1,115 @@
<?php
/**
* PEAR_Sniffs_NamingConventions_ValidClassNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PEAR_Sniffs_NamingConventions_ValidClassNameSniff.
*
* Ensures class and interface names start with a capital letter
* and use _ separators.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_NamingConventions_ValidClassNameSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_CLASS,
T_INTERFACE,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The current file being processed.
* @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)
{
$tokens = $phpcsFile->getTokens();
$className = $phpcsFile->findNext(T_STRING, $stackPtr);
$name = trim($tokens[$className]['content']);
$errorData = array(ucfirst($tokens[$stackPtr]['content']));
// Make sure the first letter is a capital.
if (preg_match('|^[A-Z]|', $name) === 0) {
$error = '%s name must begin with a capital letter';
$phpcsFile->addError($error, $stackPtr, 'StartWithCaptial', $errorData);
}
// Check that each new word starts with a capital as well, but don't
// check the first word, as it is checked above.
$validName = true;
$nameBits = explode('_', $name);
$firstBit = array_shift($nameBits);
foreach ($nameBits as $bit) {
if ($bit === '' || $bit{0} !== strtoupper($bit{0})) {
$validName = false;
break;
}
}
if ($validName === false) {
// Strip underscores because they cause the suggested name
// to be incorrect.
$nameBits = explode('_', trim($name, '_'));
$firstBit = array_shift($nameBits);
if ($firstBit === '') {
$error = '%s name is not valid';
$phpcsFile->addError($error, $stackPtr, 'Invalid', $errorData);
} else {
$newName = strtoupper($firstBit{0}).substr($firstBit, 1).'_';
foreach ($nameBits as $bit) {
if ($bit !== '') {
$newName .= strtoupper($bit{0}).substr($bit, 1).'_';
}
}
$newName = rtrim($newName, '_');
$error = '%s name is not valid; consider %s instead';
$data = $errorData;
$data[] = $newName;
$phpcsFile->addError($error, $stackPtr, 'Invalid', $data);
}
}//end if
}//end process()
}//end class
?>

View File

@@ -0,0 +1,285 @@
<?php
/**
* PEAR_Sniffs_NamingConventions_ValidFunctionNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_Standards_AbstractScopeSniff', true) === false) {
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Standards_AbstractScopeSniff not found');
}
/**
* PEAR_Sniffs_NamingConventions_ValidFunctionNameSniff.
*
* Ensures method names are correct depending on whether they are public
* or private, and that functions are named correctly.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_NamingConventions_ValidFunctionNameSniff extends PHP_CodeSniffer_Standards_AbstractScopeSniff
{
/**
* A list of all PHP magic methods.
*
* @var array
*/
protected $magicMethods = array(
'construct',
'destruct',
'call',
'callStatic',
'get',
'set',
'isset',
'unset',
'sleep',
'wakeup',
'toString',
'set_state',
'clone',
'invoke',
);
/**
* A list of all PHP magic functions.
*
* @var array
*/
protected $magicFunctions = array('autoload');
/**
* Constructs a PEAR_Sniffs_NamingConventions_ValidFunctionNameSniff.
*/
public function __construct()
{
parent::__construct(array(T_CLASS, T_INTERFACE), array(T_FUNCTION), true);
}//end __construct()
/**
* Processes the tokens within the scope.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being processed.
* @param int $stackPtr The position where this token was
* found.
* @param int $currScope The position of the current scope.
*
* @return void
*/
protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope)
{
$methodName = $phpcsFile->getDeclarationName($stackPtr);
if ($methodName === null) {
// Ignore closures.
return;
}
$className = $phpcsFile->getDeclarationName($currScope);
$errorData = array($className.'::'.$methodName);
// Is this a magic method. IE. is prefixed with "__".
if (preg_match('|^__|', $methodName) !== 0) {
$magicPart = substr($methodName, 2);
if (in_array($magicPart, $this->magicMethods) === false) {
$error = 'Method name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
$phpcsFile->addError($error, $stackPtr, 'MethodDoubleUnderscore', $errorData);
}
return;
}
// PHP4 constructors are allowed to break our rules.
if ($methodName === $className) {
return;
}
// PHP4 destructors are allowed to break our rules.
if ($methodName === '_'.$className) {
return;
}
$methodProps = $phpcsFile->getMethodProperties($stackPtr);
$isPublic = ($methodProps['scope'] === 'private') ? false : true;
$scope = $methodProps['scope'];
$scopeSpecified = $methodProps['scope_specified'];
// If it's a private method, it must have an underscore on the front.
if ($isPublic === false && $methodName{0} !== '_') {
$error = 'Private method name "%s" must be prefixed with an underscore';
$phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $errorData);
return;
}
// If it's not a private method, it must not have an underscore on the front.
if ($isPublic === true && $scopeSpecified === true && $methodName{0} === '_') {
$error = '%s method name "%s" must not be prefixed with an underscore';
$data = array(
ucfirst($scope),
$errorData[0],
);
$phpcsFile->addError($error, $stackPtr, 'PublicUnderscore', $data);
return;
}
// If the scope was specified on the method, then the method must be
// camel caps and an underscore should be checked for. If it wasn't
// specified, treat it like a public method and remove the underscore
// prefix if there is one because we cant determine if it is private or
// public.
$testMethodName = $methodName;
if ($scopeSpecified === false && $methodName{0} === '_') {
$testMethodName = substr($methodName, 1);
}
if (PHP_CodeSniffer::isCamelCaps($testMethodName, false, $isPublic, false) === false) {
if ($scopeSpecified === true) {
$error = '%s method name "%s" is not in camel caps format';
$data = array(
ucfirst($scope),
$errorData[0],
);
$phpcsFile->addError($error, $stackPtr, 'ScopeNotCamelCaps', $data);
} else {
$error = 'Method name "%s" is not in camel caps format';
$phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $errorData);
}
return;
}
}//end processTokenWithinScope()
/**
* Processes the tokens outside the scope.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being processed.
* @param int $stackPtr The position where this token was
* found.
*
* @return void
*/
protected function processTokenOutsideScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$functionName = $phpcsFile->getDeclarationName($stackPtr);
if ($functionName === null) {
// Ignore closures.
return;
}
$errorData = array($functionName);
// Is this a magic function. IE. is prefixed with "__".
if (preg_match('|^__|', $functionName) !== 0) {
$magicPart = substr($functionName, 2);
if (in_array($magicPart, $this->magicFunctions) === false) {
$error = 'Function name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore';
$phpcsFile->addError($error, $stackPtr, 'FunctionDoubleUnderscore', $errorData);
}
return;
}
// Function names can be in two parts; the package name and
// the function name.
$packagePart = '';
$camelCapsPart = '';
$underscorePos = strrpos($functionName, '_');
if ($underscorePos === false) {
$camelCapsPart = $functionName;
} else {
$packagePart = substr($functionName, 0, $underscorePos);
$camelCapsPart = substr($functionName, ($underscorePos + 1));
// We don't care about _'s on the front.
$packagePart = ltrim($packagePart, '_');
}
// If it has a package part, make sure the first letter is a capital.
if ($packagePart !== '') {
if ($functionName{0} === '_') {
$error = 'Function name "%s" is invalid; only private methods should be prefixed with an underscore';
$phpcsFile->addError($error, $stackPtr, 'FunctionUnderscore', $errorData);
return;
}
if ($functionName{0} !== strtoupper($functionName{0})) {
$error = 'Function name "%s" is prefixed with a package name but does not begin with a capital letter';
$phpcsFile->addError($error, $stackPtr, 'FunctionNoCaptial', $errorData);
return;
}
}
// If it doesn't have a camel caps part, it's not valid.
if (trim($camelCapsPart) === '') {
$error = 'Function name "%s" is not valid; name appears incomplete';
$phpcsFile->addError($error, $stackPtr, 'FunctionInvalid', $errorData);
return;
}
$validName = true;
$newPackagePart = $packagePart;
$newCamelCapsPart = $camelCapsPart;
// Every function must have a camel caps part, so check that first.
if (PHP_CodeSniffer::isCamelCaps($camelCapsPart, false, true, false) === false) {
$validName = false;
$newCamelCapsPart = strtolower($camelCapsPart{0}).substr($camelCapsPart, 1);
}
if ($packagePart !== '') {
// Check that each new word starts with a capital.
$nameBits = explode('_', $packagePart);
foreach ($nameBits as $bit) {
if ($bit{0} !== strtoupper($bit{0})) {
$newPackagePart = '';
foreach ($nameBits as $bit) {
$newPackagePart .= strtoupper($bit{0}).substr($bit, 1).'_';
}
$validName = false;
break;
}
}
}
if ($validName === false) {
$newName = rtrim($newPackagePart, '_').'_'.$newCamelCapsPart;
if ($newPackagePart === '') {
$newName = $newCamelCapsPart;
} else {
$newName = rtrim($newPackagePart, '_').'_'.$newCamelCapsPart;
}
$error = 'Function name "%s" is invalid; consider "%s" instead';
$data = $errorData;
$data[] = $newName;
$phpcsFile->addError($error, $stackPtr, 'FunctionNameInvalid', $data);
}
}//end processTokenOutsideScope()
}//end class
?>

View File

@@ -0,0 +1,116 @@
<?php
/**
* PEAR_Sniffs_NamingConventions_ValidVariableNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_Standards_AbstractVariableSniff', true) === false) {
$error = 'Class PHP_CodeSniffer_Standards_AbstractVariableSniff not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* PEAR_Sniffs_NamingConventions_ValidVariableNameSniff.
*
* Checks the naming of member variables.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_NamingConventions_ValidVariableNameSniff extends PHP_CodeSniffer_Standards_AbstractVariableSniff
{
/**
* Processes class member variables.
*
* @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
*/
protected function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$memberProps = $phpcsFile->getMemberProperties($stackPtr);
if (empty($memberProps) === true) {
return;
}
$memberName = ltrim($tokens[$stackPtr]['content'], '$');
$isPublic = ($memberProps['scope'] === 'private') ? false : true;
$scope = $memberProps['scope'];
$scopeSpecified = $memberProps['scope_specified'];
// If it's a private member, it must have an underscore on the front.
if ($isPublic === false && $memberName{0} !== '_') {
$error = 'Private member variable "%s" must be prefixed with an underscore';
$data = array($memberName);
$phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $data);
return;
}
// If it's not a private member, it must not have an underscore on the front.
if ($isPublic === true && $scopeSpecified === true && $memberName{0} === '_') {
$error = '%s member variable "%s" must not be prefixed with an underscore';
$data = array(
ucfirst($scope),
$memberName,
);
$phpcsFile->addError($error, $stackPtr, 'PublicUnderscore', $data);
return;
}
}//end processMemberVar()
/**
* Processes normal variables.
*
* @param PHP_CodeSniffer_File $phpcsFile The file where this token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
protected function processVariable(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
// We don't care about normal variables.
return;
}//end processVariable()
/**
* Processes variables in double quoted strings.
*
* @param PHP_CodeSniffer_File $phpcsFile The file where this token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
protected function processVariableInString(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
// We don't care about normal variables.
return;
}//end processVariableInString()
}//end class
?>

View File

@@ -0,0 +1,167 @@
<?php
/**
* PEAR_Sniffs_WhiteSpace_ObjectOperatorIndentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PEAR_Sniffs_WhiteSpace_ObjectOperatorIndentSniff.
*
* Checks that object operators are indented 4 spaces if they are the first
* thing on a line.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_WhiteSpace_ObjectOperatorIndentSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_OBJECT_OPERATOR);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile All the tokens found in the document.
* @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)
{
$tokens = $phpcsFile->getTokens();
// Make sure this is the first object operator in a chain of them.
$varToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($varToken === false || $tokens[$varToken]['code'] !== T_VARIABLE) {
return;
}
// Make sure this is a chained call.
$next = $phpcsFile->findNext(
T_OBJECT_OPERATOR,
($stackPtr + 1),
null,
false,
null,
true
);
if ($next === false) {
// Not a chained call.
return;
}
// Determine correct indent.
for ($i = ($varToken - 1); $i >= 0; $i--) {
if ($tokens[$i]['line'] !== $tokens[$varToken]['line']) {
$i++;
break;
}
}
$requiredIndent = 0;
if ($i >= 0 && $tokens[$i]['code'] === T_WHITESPACE) {
$requiredIndent = strlen($tokens[$i]['content']);
}
$requiredIndent += 4;
// Determine the scope of the original object operator.
$origBrackets = null;
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
$origBrackets = $tokens[$stackPtr]['nested_parenthesis'];
}
$origConditions = null;
if (isset($tokens[$stackPtr]['conditions']) === true) {
$origConditions = $tokens[$stackPtr]['conditions'];
}
// Check indentation of each object operator in the chain.
// If the first object operator is on a different line than
// the variable, make sure we check its indentation too.
if ($tokens[$stackPtr]['line'] > $tokens[$varToken]['line']) {
$next = $stackPtr;
}
while ($next !== false) {
// Make sure it is in the same scope, otherwise dont check indent.
$brackets = null;
if (isset($tokens[$next]['nested_parenthesis']) === true) {
$brackets = $tokens[$next]['nested_parenthesis'];
}
$conditions = null;
if (isset($tokens[$next]['conditions']) === true) {
$conditions = $tokens[$next]['conditions'];
}
if ($origBrackets === $brackets && $origConditions === $conditions) {
// Make sure it starts a line, otherwise dont check indent.
$indent = $tokens[($next - 1)];
if ($indent['code'] === T_WHITESPACE) {
if ($indent['line'] === $tokens[$next]['line']) {
$foundIndent = strlen($indent['content']);
} else {
$foundIndent = 0;
}
if ($foundIndent !== $requiredIndent) {
$error = 'Object operator not indented correctly; expected %s spaces but found %s';
$data = array(
$requiredIndent,
$foundIndent,
);
$phpcsFile->addError($error, $next, 'Incorrect', $data);
}
}
// It cant be the last thing on the line either.
$content = $phpcsFile->findNext(T_WHITESPACE, ($next + 1), null, true);
if ($tokens[$content]['line'] !== $tokens[$next]['line']) {
$error = 'Object operator must be at the start of the line, not the end';
$phpcsFile->addError($error, $next, 'StartOfLine');
}
}//end if
$next = $phpcsFile->findNext(
T_OBJECT_OPERATOR,
($next + 1),
null,
false,
null,
true
);
}//end while
}//end process()
}//end class
?>

View File

@@ -0,0 +1,143 @@
<?php
/**
* PEAR_Sniffs_Whitespace_ScopeClosingBraceSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PEAR_Sniffs_Whitespace_ScopeClosingBraceSniff.
*
* Checks that the closing braces of scopes are aligned correctly.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_WhiteSpace_ScopeClosingBraceSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return PHP_CodeSniffer_Tokens::$scopeOpeners;
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile All the tokens found in the document.
* @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)
{
$tokens = $phpcsFile->getTokens();
// If this is an inline condition (ie. there is no scope opener), then
// return, as this is not a new scope.
if (isset($tokens[$stackPtr]['scope_closer']) === false) {
return;
}
$scopeStart = $tokens[$stackPtr]['scope_opener'];
$scopeEnd = $tokens[$stackPtr]['scope_closer'];
// If the scope closer doesn't think it belongs to this scope opener
// then the opener is sharing its closer ith other tokens. We only
// want to process the closer once, so skip this one.
if ($tokens[$scopeEnd]['scope_condition'] !== $stackPtr) {
return;
}
// We need to actually find the first piece of content on this line,
// because if this is a method with tokens before it (public, static etc)
// or an if with an else before it, then we need to start the scope
// checking from there, rather than the current token.
$lineStart = ($stackPtr - 1);
for ($lineStart; $lineStart > 0; $lineStart--) {
if (strpos($tokens[$lineStart]['content'], $phpcsFile->eolChar) !== false) {
break;
}
}
// We found a new line, now go forward and find the first non-whitespace
// token.
$lineStart= $phpcsFile->findNext(
array(T_WHITESPACE),
($lineStart + 1),
null,
true
);
$startColumn = $tokens[$lineStart]['column'];
// Check that the closing brace is on it's own line.
$lastContent = $phpcsFile->findPrevious(
array(T_WHITESPACE),
($scopeEnd - 1),
$scopeStart,
true
);
if ($tokens[$lastContent]['line'] === $tokens[$scopeEnd]['line']) {
$error = 'Closing brace must be on a line by itself';
$phpcsFile->addError($error, $scopeEnd, 'Line');
return;
}
// Check now that the closing brace is lined up correctly.
$braceIndent = $tokens[$scopeEnd]['column'];
$isBreakCloser = ($tokens[$scopeEnd]['code'] === T_BREAK);
if (in_array($tokens[$stackPtr]['code'], array(T_CASE, T_DEFAULT)) === true
&& $isBreakCloser === true
) {
// BREAK statements should be indented 4 spaces from the
// CASE or DEFAULT statement.
if ($braceIndent !== ($startColumn + 4)) {
$error = 'Break statement indented incorrectly; expected %s spaces, found %s';
$data = array(
($startColumn + 3),
($braceIndent - 1),
);
$phpcsFile->addError($error, $scopeEnd, 'BreakIdent', $data);
}
} else {
if ($braceIndent !== $startColumn) {
$error = 'Closing brace indented incorrectly; expected %s spaces, found %s';
$data = array(
($startColumn - 1),
($braceIndent - 1),
);
$phpcsFile->addError($error, $scopeEnd, 'Indent', $data);
}
}
}//end process()
}//end class
?>

View File

@@ -0,0 +1,48 @@
<?php
/**
* PEAR_Sniffs_Whitespace_ScopeIndentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('Generic_Sniffs_WhiteSpace_ScopeIndentSniff', true) === false) {
$error = 'Class Generic_Sniffs_WhiteSpace_ScopeIndentSniff not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* PEAR_Sniffs_Whitespace_ScopeIndentSniff.
*
* Checks that control structures are structured correctly, and their content
* is indented correctly.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.3.3
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PEAR_Sniffs_WhiteSpace_ScopeIndentSniff extends Generic_Sniffs_WhiteSpace_ScopeIndentSniff
{
/**
* Any scope openers that should not cause an indent.
*
* @var array(int)
*/
protected $nonIndentingScopes = array(T_SWITCH);
}//end class
?>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0"?>
<ruleset name="PEAR">
<description>The PEAR coding standard.</description>
<!-- Include some additional sniffs from the Generic standard -->
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
<rule ref="Generic.PHP.LowerCaseConstant"/>
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
<!-- Lines can be 85 chars long, but never show errors -->
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="85"/>
<property name="absoluteLineLimit" value="0"/>
</properties>
</rule>
<!-- Use Unix newlines -->
<rule ref="Generic.Files.LineEndings">
<properties>
<property name="eolChar" value="\n"/>
</properties>
</rule>
<!-- This messgae is not required as spaces are allowed for alignment -->
<rule ref="Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma">
<severity>0</severity>
</rule>
<!-- Use warnings for inline control structures -->
<rule ref="Generic.ControlStructures.InlineControlStructure">
<properties>
<property name="error" value="false"/>
</properties>
</rule>
</ruleset>