MDL-19247 Added the PHP CodeSniffer tool and lib/thirdpartylibs.xml

This commit is contained in:
nicolasconnault 2009-05-19 15:22:43 +00:00
parent 1905b5f68d
commit d35df1b54a
67 changed files with 16067 additions and 0 deletions

1665
lib/pear/PHP/CodeSniffer.php Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,473 @@
<?php
/**
* A class to process command line phpcs scripts.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (is_file(dirname(__FILE__).'/../CodeSniffer.php') === true) {
include_once dirname(__FILE__).'/../CodeSniffer.php';
} else {
include_once 'PHP/CodeSniffer.php';
}
/**
* A class to process command line phpcs scripts.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_CLI
{
/**
* Exits if the minimum requirements of PHP_CodSniffer are not met.
*
* @return array
*/
public function checkRequirements()
{
// Check the PHP version.
if (version_compare(PHP_VERSION, '5.1.2') === -1) {
echo 'ERROR: PHP_CodeSniffer requires PHP version 5.1.2 or greater.'.PHP_EOL;
exit(2);
}
if (extension_loaded('tokenizer') === false) {
echo 'ERROR: PHP_CodeSniffer requires the tokenizer extension to be enabled.'.PHP_EOL;
exit(2);
}
}//end checkRequirements()
/**
* Get a list of default values for all possible command line arguments.
*
* @return array
*/
public function getDefaults()
{
// The default values for config settings.
$defaults['files'] = array();
$defaults['standard'] = null;
$defaults['verbosity'] = 0;
$defaults['local'] = false;
$defaults['extensions'] = array();
$defaults['ignored'] = array();
$defaults['generator'] = '';
$defaults['report'] = PHP_CodeSniffer::getConfigData('report_format');
if ($defaults['report'] === null) {
$defaults['report'] = 'full';
}
$showWarnings = PHP_CodeSniffer::getConfigData('show_warnings');
if ($showWarnings === null) {
$defaults['showWarnings'] = true;
} else {
$defaults['showWarnings'] = (bool) $showWarnings;
}
$tabWidth = PHP_CodeSniffer::getConfigData('tab_width');
if ($tabWidth === null) {
$defaults['tabWidth'] = 0;
} else {
$defaults['tabWidth'] = (int) $tabWidth;
}
return $defaults;
}//end getDefaults()
/**
* Process the command line arguments and returns the values.
*
* @return array
*/
public function getCommandLineValues()
{
$values = $this->getDefaults();
for ($i = 1; $i < $_SERVER['argc']; $i++) {
$arg = $_SERVER['argv'][$i];
if ($arg{0} === '-') {
if ($arg{1} === '-') {
$values = $this->processLongArgument(substr($arg, 2), $i, $values);
} else {
$switches = str_split($arg);
foreach ($switches as $switch) {
if ($switch === '-') {
continue;
}
$values = $this->processShortArgument($switch, $values);
}
}
} else {
$values = $this->processUnknownArgument($arg, $i, $values);
}
}//end for
return $values;
}//end getCommandLineValues()
/**
* Processes a sort (-e) command line argument.
*
* @param string $arg The command line argument.
* @param array $values An array of values determined from CLI args.
*
* @return array The updated CLI values.
* @see getCommandLineValues()
*/
public function processShortArgument($arg, $values)
{
switch ($arg) {
case 'h':
case '?':
$this->printUsage();
exit(0);
break;
case 'i' :
$this->printInstalledStandards();
exit(0);
break;
case 'v' :
$values['verbosity']++;
break;
case 'l' :
$values['local'] = true;
break;
case 'n' :
$values['showWarnings'] = false;
break;
case 'w' :
$values['showWarnings'] = true;
break;
default:
$values = $this->processUnknownArgument('-'.$arg, $values);
}//end switch
return $values;
}//end processShortArgument()
/**
* Processes a long (--example) command line argument.
*
* @param string $arg The command line argument.
* @param int $pos The position of the argument on the command line.
* @param array $values An array of values determined from CLI args.
*
* @return array The updated CLI values.
* @see getCommandLineValues()
*/
public function processLongArgument($arg, $pos, $values)
{
switch ($arg) {
case 'help':
$this->printUsage();
exit(0);
break;
case 'version':
echo 'PHP_CodeSniffer version 1.1.0 (stable) ';
echo 'by Squiz Pty Ltd. (http://www.squiz.net)'.PHP_EOL;
exit(0);
break;
case 'config-set':
$key = $_SERVER['argv'][($pos + 1)];
$value = $_SERVER['argv'][($pos + 2)];
PHP_CodeSniffer::setConfigData($key, $value);
exit(0);
break;
case 'config-delete':
$key = $_SERVER['argv'][($pos + 1)];
PHP_CodeSniffer::setConfigData($key, null);
exit(0);
break;
case 'config-show':
$data = PHP_CodeSniffer::getAllConfigData();
print_r($data);
exit(0);
break;
default:
if (substr($arg, 0, 7) === 'report=') {
$values['report'] = substr($arg, 7);
$validReports = array(
'full',
'xml',
'checkstyle',
'csv',
'summary',
);
if (in_array($values['report'], $validReports) === false) {
echo 'ERROR: Report type "'.$report.'" not known.'.PHP_EOL;
exit(2);
}
} else if (substr($arg, 0, 9) === 'standard=') {
$values['standard'] = substr($arg, 9);
} else if (substr($arg, 0, 11) === 'extensions=') {
$values['extensions'] = explode(',', substr($arg, 11));
} else if (substr($arg, 0, 7) === 'ignore=') {
// Split the ignore string on commas, unless the comma is escaped
// using 1 or 3 slashes (\, or \\\,).
$values['ignored'] = preg_split('/(?<=(?<!\\\\)\\\\\\\\),|(?<!\\\\),/', substr($arg, 7));
} else if (substr($arg, 0, 10) === 'generator=') {
$values['generator'] = substr($arg, 10);
} else if (substr($arg, 0, 10) === 'tab-width=') {
$values['tabWidth'] = (int) substr($arg, 10);
} else {
$values = $this->processUnknownArgument('--'.$arg, $values);
}//end if
break;
}//end switch
return $values;
}//end processLongArgument()
/**
* Processes an unknown command line argument.
*
* Assumes all unknown arguments are files and folders to check.
*
* @param string $arg The command line argument.
* @param int $pos The position of the argument on the command line.
* @param array $values An array of values determined from CLI args.
*
* @return array The updated CLI values.
* @see getCommandLineValues()
*/
public function processUnknownArgument($arg, $pos, $values)
{
// We don't know about any additional switches; just files.
if ($arg{0} === '-') {
echo 'ERROR: option "'.$arg.'" not known.'.PHP_EOL.PHP_EOL;
$this->printUsage();
exit(2);
}
$file = realpath($arg);
if (file_exists($file) === false) {
echo 'ERROR: The file "'.$arg.'" does not exist.'.PHP_EOL.PHP_EOL;
$this->printUsage();
exit(2);
} else {
$values['files'][] = $file;
}
return $values;
}//end processUnknownArgument()
/**
* Runs PHP_CodeSniffer over files are directories.
*
* @param array $values An array of values determined from CLI args.
*
* @return int The number of error and warning messages shown.
* @see getCommandLineValues()
*/
public function process($values=array())
{
if (empty($values) === true) {
$values = $this->getCommandLineValues();
}
if ($values['generator'] !== '') {
$phpcs = new PHP_CodeSniffer($values['verbosity']);
$phpcs->generateDocs($values['standard'], $values['files'], $values['generator']);
exit(0);
}
if (empty($values['files']) === true) {
echo 'ERROR: You must supply at least one file or directory to process.'.PHP_EOL.PHP_EOL;
$this->printUsage();
exit(2);
}
$values['standard'] = $this->validateStandard($values['standard']);
if (PHP_CodeSniffer::isInstalledStandard($values['standard']) === false) {
// They didn't select a valid coding standard, so help them
// out by letting them know which standards are installed.
echo 'ERROR: the "'.$values['standard'].'" coding standard is not installed. ';
$this->printInstalledStandards();
exit(2);
}
$phpcs = new PHP_CodeSniffer($values['verbosity'], $values['tabWidth']);
// Set file extensions if they were specified. Otherwise,
// let PHP_CodeSniffer decide on the defaults.
if (empty($values['extensions']) === false) {
$phpcs->setAllowedFileExtensions($values['extensions']);
}
// Set ignore patterns if they were specified.
if (empty($values['ignored']) === false) {
$phpcs->setIgnorePatterns($values['ignored']);
}
$phpcs->process($values['files'], $values['standard'], array(), $values['local']);
return $this->printErrorReport($phpcs, $values['report'], $values['showWarnings']);
}//end process()
/**
* Prints the error report.
*
* @param PHP_CodeSniffer $phpcs The PHP_CodeSniffer object containing
* the errors.
* @param string $report The type of report to print.
* @param bool $showWarnings TRUE if warnings should also be printed.
*
* @return int The number of error and warning messages shown.
*/
public function printErrorReport($phpcs, $report, $showWarnings)
{
switch ($report) {
case 'xml':
$numErrors = $phpcs->printXMLErrorReport($showWarnings);
break;
case 'checkstyle':
$numErrors = $phpcs->printCheckstyleErrorReport($showWarnings);
break;
case 'csv':
$numErrors = $phpcs->printCSVErrorReport($showWarnings);
break;
case 'summary':
$numErrors = $phpcs->printErrorReportSummary($showWarnings);
break;
default:
$numErrors = $phpcs->printErrorReport($showWarnings);
break;
}
return $numErrors;
}//end printErrorReport()
/**
* Convert the passed standard into a valid standard.
*
* Checks things like default values and case.
*
* @param string $standard The standard to validate.
*
* @return string
*/
public function validateStandard($standard)
{
if ($standard === null) {
// They did not supply a standard to use.
// Try to get the default from the config system.
$standard = PHP_CodeSniffer::getConfigData('default_standard');
if ($standard === null) {
$standard = 'Moodle';
}
}
// Check if the standard name is valid. If not, check that the case
// was not entered incorrectly.
if (PHP_CodeSniffer::isInstalledStandard($standard) === false) {
$installedStandards = PHP_CodeSniffer::getInstalledStandards();
foreach ($installedStandards as $validStandard) {
if (strtolower($standard) === strtolower($validStandard)) {
$standard = $validStandard;
break;
}
}
}
return $standard;
}//end validateStandard()
/**
* Prints out the usage information for this script.
*
* @return void
*/
public function printUsage()
{
echo 'Usage: phpcs [-nwlvi] [--report=<report>]'.PHP_EOL;
echo ' [--config-set key value] [--config-delete key] [--config-show]'.PHP_EOL;
echo ' [--generator=<generator>] [--extensions=<extensions>]'.PHP_EOL;
echo ' [--ignore=<patterns>] [--tab-width=<width>] <file> ...'.PHP_EOL;
echo ' -n Do not print warnings'.PHP_EOL;
echo ' -w Print both warnings and errors (on by default)'.PHP_EOL;
echo ' -l Local directory only, no recursion'.PHP_EOL;
echo ' -v[v][v] Print verbose output'.PHP_EOL;
echo ' -i Show a list of installed coding standards'.PHP_EOL;
echo ' --help Print this help message'.PHP_EOL;
echo ' --version Print version information'.PHP_EOL;
echo ' <file> One or more files and/or directories to check'.PHP_EOL;
echo ' <extensions> A comma separated list of file extensions to check'.PHP_EOL;
echo ' (only valid if checking a directory)'.PHP_EOL;
echo ' <patterns> A comma separated list of patterns that are used'.PHP_EOL;
echo ' to ignore directories and files'.PHP_EOL;
echo ' <width> The number of spaces each tab represents'.PHP_EOL;
echo ' <generator> The name of a doc generator to use'.PHP_EOL;
echo ' (forces doc generation instead of checking)'.PHP_EOL;
echo ' <report> Print either the "full", "xml", "checkstyle",'.PHP_EOL;
echo ' "csv" or "summary" report'.PHP_EOL;
echo ' (the "full" report is printed by default)'.PHP_EOL;
}//end printUsage()
/**
* Prints out a list of installed coding standards.
*
* @return void
*/
public function printInstalledStandards()
{
$installedStandards = PHP_CodeSniffer::getInstalledStandards();
$numStandards = count($installedStandards);
if ($numStandards === 0) {
echo 'No coding standards are installed.'.PHP_EOL;
} else {
$lastStandard = array_pop($installedStandards);
if ($numStandards === 1) {
echo "The only coding standard installed is $lastStandard".PHP_EOL;
} else {
$standardList = implode(', ', $installedStandards);
$standardList .= ' and '.$lastStandard;
echo 'The installed coding standards are '.$standardList.PHP_EOL;
}
}
}//end printInstalledStandards()
}//end class
?>

View file

@ -0,0 +1,327 @@
<?php
/**
* A class to handle most of the parsing operations of a doc comment element.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (interface_exists('PHP_CodeSniffer_CommentParser_DocElement', true) === false) {
$error = 'Interface PHP_CodeSniffer_CommentParser_DocElement not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* A class to handle most of the parsing operations of a doc comment element.
*
* Extending classes should implement the getSubElements method to return
* a list of elements that the doc comment element contains, in the order that
* they appear in the element. For example a function parameter element has a
* type, a variable name and a comment. It should therefore implement the method
* as follows:
*
* <code>
* protected function getSubElements()
* {
* return array(
* 'type',
* 'variable',
* 'comment',
* );
* }
* </code>
*
* The processSubElement will be called for each of the sub elements to allow
* the extending class to process them. So for the parameter element we would
* have:
*
* <code>
* protected function processSubElement($name, $content, $whitespaceBefore)
* {
* if ($name === 'type') {
* echo 'The name of the variable was '.$content;
* }
* // Process other tags.
* }
* </code>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
abstract class PHP_CodeSniffer_CommentParser_AbstractDocElement implements PHP_CodeSniffer_CommentParser_DocElement
{
/**
* The element previous to this element.
*
* @var PHP_CodeSniffer_CommentParser_DocElement
*/
protected $previousElement = null;
/**
* The element proceeding this element.
*
* @var PHP_CodeSniffer_CommentParser_DocElement
*/
protected $nextElement = null;
/**
* The whitespace the occurs after this element and its sub elements.
*
* @var string
*/
protected $afterWhitespace = '';
/**
* The tokens that comprise this element.
*
* @var array(string)
*/
protected $tokens = array();
/**
* The file this element is in.
*
* @var array(string)
*/
protected $phpcsFile = null;
/**
* The tag that this element represents (omiting the @ symbol).
*
* @var string
*/
protected $tag = '';
/**
* Constructs a Doc Element.
*
* @param PHP_CodeSniffer_CommentParser_DocElement $previousElement The element
* that ocurred
* before this.
* @param array $tokens The tokens of
* this element.
* @param string $tag The doc
* element tag
* this element
* represents.
* @param PHP_CodeSniffer_File $phpcsFile The file that
* this element
* is in.
*
* @throws Exception If $previousElement in not a DocElement or if
* getSubElements() does not return an array.
*/
public function __construct($previousElement, array $tokens, $tag, PHP_CodeSniffer_File $phpcsFile)
{
if ($previousElement !== null && ($previousElement instanceof PHP_CodeSniffer_CommentParser_DocElement) === false) {
$error = '$previousElement must be an instance of DocElement';
throw new Exception($error);
}
$this->phpcsFile = $phpcsFile;
$this->previousElement = $previousElement;
if ($previousElement !== null) {
$this->previousElement->nextElement = $this;
}
$this->tag = $tag;
$this->tokens = $tokens;
$subElements = $this->getSubElements();
if (is_array($subElements) === false) {
throw new Exception('getSubElements() must return an array');
}
$whitespace = '';
$currElem = 0;
$lastElement = '';
$lastElementWhitespace = null;
$numSubElements = count($subElements);
foreach ($this->tokens as $token) {
if (trim($token) === '') {
$whitespace .= $token;
} else {
if ($currElem < ($numSubElements - 1)) {
$element = $subElements[$currElem];
$this->processSubElement($element, $token, $whitespace);
$whitespace = '';
$currElem++;
} else {
if ($lastElementWhitespace === null) {
$lastElementWhitespace = $whitespace;
}
$lastElement .= $whitespace.$token;
$whitespace = '';
}
}
}//end foreach
$lastElement = ltrim($lastElement);
$lastElementName = $subElements[($numSubElements - 1)];
// Process the last element in this tag.
$this->processSubElement($lastElementName, $lastElement, $lastElementWhitespace);
$this->afterWhitespace = $whitespace;
}//end __construct()
/**
* Returns the element that exists before this.
*
* @return PHP_CodeSniffer_CommentParser_DocElement
*/
public function getPreviousElement()
{
return $this->previousElement;
}//end getPreviousElement()
/**
* Returns the element that exists after this.
*
* @return PHP_CodeSniffer_CommentParser_DocElement
*/
public function getNextElement()
{
return $this->nextElement;
}//end getNextElement()
/**
* Returns the whitespace that exists before this element.
*
* @return string
*/
public function getWhitespaceBefore()
{
if ($this->previousElement !== null) {
return $this->previousElement->getWhitespaceAfter();
}
return '';
}//end getWhitespaceBefore()
/**
* Returns the whitespace that exists after this element.
*
* @return string
*/
public function getWhitespaceAfter()
{
return $this->afterWhitespace;
}//end getWhitespaceAfter()
/**
* Returns the order that this element appears in the comment.
*
* @return int
*/
public function getOrder()
{
if ($this->previousElement === null) {
return 1;
} else {
return ($this->previousElement->getOrder() + 1);
}
}//end getOrder()
/**
* Returns the tag that this element represents, ommiting the @ symbol.
*
* @return string
*/
public function getTag()
{
return $this->tag;
}//end getTag()
/**
* Returns the raw content of this element, ommiting the tag.
*
* @return string
*/
public function getRawContent()
{
return implode('', $this->tokens);
}//end getRawContent()
/**
* Returns the line in which this element first occured.
*
* @return int
*/
public function getLine()
{
if ($this->previousElement === null) {
// First element is on line one.
return 1;
} else {
$previousContent = $this->previousElement->getRawContent();
$previousLine = $this->previousElement->getLine();
return ($previousLine + substr_count($previousContent, $this->phpcsFile->eolChar));
}
}//end getLine()
/**
* Returns the sub element names that make up this element in the order they
* appear in the element.
*
* @return array(string)
* @see processSubElement()
*/
abstract protected function getSubElements();
/**
* Called to process each sub element as sepcified in the return value
* of getSubElements().
*
* @param string $name The name of the element to process.
* @param string $content The content of the the element.
* @param string $whitespaceBefore The whitespace found before this element.
*
* @return void
* @see getSubElements()
*/
abstract protected function processSubElement($name, $content, $whitespaceBefore);
}//end class
?>

View file

@ -0,0 +1,586 @@
<?php
/**
* Parses doc comments.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_CommentParser_SingleElement', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_SingleElement not found';
throw new PHP_CodeSniffer_Exception($error);
}
if (class_exists('PHP_CodeSniffer_CommentParser_CommentElement', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_CommentElement not found';
throw new PHP_CodeSniffer_Exception($error);
}
if (class_exists('PHP_CodeSniffer_CommentParser_ParserException', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_ParserException not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* Parses doc comments.
*
* This abstract parser handles the following tags:
*
* <ul>
* <li>The short description and the long description</li>
* <li>@see</li>
* <li>@link</li>
* <li>@deprecated</li>
* <li>@since</li>
* </ul>
*
* Extending classes should implement the getAllowedTags() method to return the
* tags that they wish to process, ommiting the tags that this base class
* processes. When one of these tags in encountered, the process&lt;tag_name&gt;
* method is called on that class. For example, if a parser's getAllowedTags()
* method returns \@param as one of its tags, the processParam method will be
* called so that the parser can process such a tag.
*
* The method is passed the tokens that comprise this tag. The tokens array
* includes the whitespace that exists between the tokens, as seperate tokens.
* It's up to the method to create a element that implements the DocElement
* interface, which should be returned. The AbstractDocElement class is a helper
* class that can be used to handle most of the parsing of the tokens into their
* individual sub elements. It requires that you construct it with the element
* previous to the element currently being processed, which can be acquired
* with the protected $previousElement class member of this class.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
abstract class PHP_CodeSniffer_CommentParser_AbstractParser
{
/**
* The comment element that appears in the doc comment.
*
* @var PHP_CodeSniffer_CommentParser_CommentElement
*/
protected $comment = null;
/**
* The string content of the comment.
*
* @var string
*/
protected $commentString = '';
/**
* The file that the comment exists in.
*
* @var PHP_CodeSniffer_File
*/
protected $phpcsFile = null;
/**
* The word tokens that appear in the comment.
*
* Whitespace tokens also appear in this stack, but are separate tokens
* from words.
*
* @var array(string)
*/
protected $words = array();
/**
* The previous doc element that was processed.
*
* null if the current element being processed is the first element in the
* doc comment.
*
* @var PHP_CodeSniffer_CommentParser_DocElement
*/
protected $previousElement = null;
/**
* A list of see elements that appear in this doc comment.
*
* @var array(PHP_CodeSniffer_CommentParser_SingleElement)
*/
protected $sees = array();
/**
* A list of see elements that appear in this doc comment.
*
* @var array(PHP_CodeSniffer_CommentParser_SingleElement)
*/
protected $deprecated = null;
/**
* A list of see elements that appear in this doc comment.
*
* @var array(PHP_CodeSniffer_CommentParser_SingleElement)
*/
protected $links = array();
/**
* A element to represent \@since tags.
*
* @var PHP_CodeSniffer_CommentParser_SingleElement
*/
protected $since = null;
/**
* True if the comment has been parsed.
*
* @var boolean
*/
private $_hasParsed = false;
/**
* The tags that this class can process.
*
* @var array(string)
*/
private static $_tags = array(
'see' => false,
'link' => false,
'deprecated' => true,
'since' => true,
);
/**
* An array of unknown tags.
*
* @var array(string)
*/
public $unknown = array();
/**
* The order of tags.
*
* @var array(string)
*/
public $orders = array();
/**
* Constructs a Doc Comment Parser.
*
* @param string $comment The comment to parse.
* @param PHP_CodeSniffer_File $phpcsFile The file that this comment is in.
*/
public function __construct($comment, PHP_CodeSniffer_File $phpcsFile)
{
$this->commentString = $comment;
$this->phpcsFile = $phpcsFile;
}//end __construct()
/**
* Initiates the parsing of the doc comment.
*
* @return void
* @throws PHP_CodeSniffer_CommentParser_ParserException If the parser finds a
* problem with the
* comment.
*/
public function parse()
{
if ($this->_hasParsed === false) {
$this->_parse($this->commentString);
}
}//end parse()
/**
* Parse the comment.
*
* @param string $comment The doc comment to parse.
*
* @return void
* @see _parseWords()
*/
private function _parse($comment)
{
// Firstly, remove the comment tags and any stars from the left side.
$lines = split($this->phpcsFile->eolChar, $comment);
foreach ($lines as &$line) {
$line = trim($line);
if ($line !== '') {
if (substr($line, 0, 3) === '/**') {
$line = substr($line, 3);
} else if (substr($line, -2, 2) === '*/') {
$line = substr($line, 0, -2);
} else if ($line{0} === '*') {
$line = substr($line, 1);
}
// Add the words to the stack, preserving newlines. Other parsers
// might be interested in the spaces between words, so tokenize
// spaces as well as separate tokens.
$flags = (PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$words = preg_split('|(\s+)|', $line.$this->phpcsFile->eolChar, -1, $flags);
$this->words = array_merge($this->words, $words);
}
}//end foreach
$this->_parseWords();
}//end _parse()
/**
* Parses each word within the doc comment.
*
* @return void
* @see _parse()
* @throws PHP_CodeSniffer_CommentParser_ParserException If more than the allowed
* number of occurances of
* a tag is found.
*/
private function _parseWords()
{
$allowedTags = (self::$_tags + $this->getAllowedTags());
$allowedTagNames = array_keys($allowedTags);
$foundTags = array();
$prevTagPos = false;
$wordWasEmpty = true;
foreach ($this->words as $wordPos => $word) {
if (trim($word) !== '') {
$wordWasEmpty = false;
}
if ($word{0} === '@') {
$tag = substr($word, 1);
// Filter out @ tags in the comment description.
// A real comment tag should have whitespace and a newline before it.
if (isset($this->words[($wordPos - 1)]) === false || trim($this->words[($wordPos - 1)]) !== '') {
continue;
}
if (isset($this->words[($wordPos - 2)]) === false || $this->words[($wordPos - 2)] !== $this->phpcsFile->eolChar) {
continue;
}
$foundTags[] = $tag;
if ($prevTagPos !== false) {
// There was a tag before this so let's process it.
$prevTag = substr($this->words[$prevTagPos], 1);
$this->parseTag($prevTag, $prevTagPos, ($wordPos - 1));
} else {
// There must have been a comment before this tag, so
// let's process that.
$this->parseTag('comment', 0, ($wordPos - 1));
}
$prevTagPos = $wordPos;
if (in_array($tag, $allowedTagNames) === false) {
// This is not a tag that we process, but let's check to
// see if it is a tag we know about. If we don't know about it,
// we add it to a list of unknown tags.
$knownTags = array(
'abstract',
'access',
'example',
'filesource',
'global',
'ignore',
'internal',
'name',
'static',
'staticvar',
'todo',
'tutorial',
'uses',
'package_version@',
);
if (in_array($tag, $knownTags) === false) {
$this->unknown[] = array(
'tag' => $tag,
'line' => $this->getLine($wordPos),
);
}
}//end if
}//end if
}//end foreach
// Only process this tag if there was something to process.
if ($wordWasEmpty === false) {
if ($prevTagPos === false) {
// There must only be a comment in this doc comment.
$this->parseTag('comment', 0, count($this->words));
} else {
// Process the last tag element.
$prevTag = substr($this->words[$prevTagPos], 1);
$this->parseTag($prevTag, $prevTagPos, count($this->words));
}
}
}//end _parseWords()
/**
* Returns the line that the token exists on in the doc comment.
*
* @param int $tokenPos The position in the words stack to find the line
* number for.
*
* @return int
*/
protected function getLine($tokenPos)
{
$newlines = 0;
for ($i = 0; $i < $tokenPos; $i++) {
$newlines += substr_count($this->phpcsFile->eolChar, $this->words[$i]);
}
return $newlines;
}//end getLine()
/**
* Parses see tag element within the doc comment.
*
* @param array(string) $tokens The word tokens that comprise this element.
*
* @return DocElement The element that represents this see comment.
*/
protected function parseSee($tokens)
{
$see = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'see', $this->phpcsFile);
$this->sees[] = $see;
return $see;
}//end parseSee()
/**
* Parses the comment element that appears at the top of the doc comment.
*
* @param array(string) $tokens The word tokens that comprise tihs element.
*
* @return DocElement The element that represents this comment element.
*/
protected function parseComment($tokens)
{
$this->comment = new PHP_CodeSniffer_CommentParser_CommentElement($this->previousElement, $tokens, $this->phpcsFile);
return $this->comment;
}//end parseComment()
/**
* Parses \@deprecated tags.
*
* @param array(string) $tokens The word tokens that comprise tihs element.
*
* @return DocElement The element that represents this deprecated tag.
*/
protected function parseDeprecated($tokens)
{
$this->deprecated = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'deprecated', $this->phpcsFile);
return $this->deprecated;
}//end parseDeprecated()
/**
* Parses \@since tags.
*
* @param array(string) $tokens The word tokens that comprise this element.
*
* @return SingleElement The element that represents this since tag.
*/
protected function parseSince($tokens)
{
$this->since = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'since', $this->phpcsFile);
return $this->since;
}//end parseSince()
/**
* Parses \@link tags.
*
* @param array(string) $tokens The word tokens that comprise this element.
*
* @return SingleElement The element that represents this link tag.
*/
protected function parseLink($tokens)
{
$link = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'link', $this->phpcsFile);
$this->links[] = $link;
return $link;
}//end parseLink()
/**
* Returns the see elements that appear in this doc comment.
*
* @return array(SingleElement)
*/
public function getSees()
{
return $this->sees;
}//end getSees()
/**
* Returns the comment element that appears at the top of this doc comment.
*
* @return CommentElement
*/
public function getComment()
{
return $this->comment;
}//end getComment()
/**
* Returns the link elements found in this comment.
*
* Returns an empty array if no links are found in the comment.
*
* @return array(SingleElement)
*/
public function getLinks()
{
return $this->links;
}//end getLinks()
/**
* Returns the deprecated element found in this comment.
*
* Returns null if no element exists in the comment.
*
* @return SingleElement
*/
public function getDeprecated()
{
return $this->deprecated;
}//end getDeprecated()
/**
* Returns the since element found in this comment.
*
* Returns null if no element exists in the comment.
*
* @return SingleElement
*/
public function getSince()
{
return $this->since;
}//end getSince()
/**
* Parses the specified tag.
*
* @param string $tag The tag name to parse (omitting the @ sybmol from
* the tag)
* @param int $start The position in the word tokens where this element
* started.
* @param int $end The position in the word tokens where this element
* ended.
*
* @return void
* @throws Exception If the process method for the tag cannot be found.
*/
protected function parseTag($tag, $start, $end)
{
$tokens = array_slice($this->words, ($start + 1), ($end - $start));
$allowedTags = (self::$_tags + $this->getAllowedTags());
$allowedTagNames = array_keys($allowedTags);
if ($tag === 'comment' || in_array($tag, $allowedTagNames) === true) {
$method = 'parse'.$tag;
if (method_exists($this, $method) === false) {
$error = 'Method '.$method.' must be implemented to process '.$tag.' tags';
throw new Exception($error);
}
$this->previousElement = $this->$method($tokens);
} else {
$this->previousElement = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, $tag, $this->phpcsFile);
}
$this->orders[] = $tag;
if ($this->previousElement === null || ($this->previousElement instanceof PHP_CodeSniffer_CommentParser_DocElement) === false) {
throw new Exception('Parse method must return a DocElement');
}
}//end parseTag()
/**
* Returns a list of tags that this comment parser allows for it's comment.
*
* Each tag should indicate if only one entry of this tag can exist in the
* comment by specifying true as the array value, or false if more than one
* is allowed. Each tag should ommit the @ symbol. Only tags other than
* the standard tags should be returned.
*
* @return array(string => boolean)
*/
protected abstract function getAllowedTags();
/**
* Returns the tag orders (index => tagName).
*
* @return array
*/
public function getTagOrders()
{
return $this->orders;
}//end getTagOrders()
/**
* Returns the unknown tags.
*
* @return array
*/
public function getUnknown()
{
return $this->unknown;
}//end getUnknown()
}//end class
?>

View file

@ -0,0 +1,300 @@
<?php
/**
* Parses Class doc comments.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_CommentParser_AbstractParser', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_AbstractParser not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* Parses Class doc comments.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_CommentParser_ClassCommentParser extends PHP_CodeSniffer_CommentParser_AbstractParser
{
/**
* The package element of this class.
*
* @var SingleElement
*/
private $_package = null;
/**
* The subpackage element of this class.
*
* @var SingleElement
*/
private $_subpackage = null;
/**
* The version element of this class.
*
* @var SingleElement
*/
private $_version = null;
/**
* The category element of this class.
*
* @var SingleElement
*/
private $_category = null;
/**
* The copyright elements of this class.
*
* @var array(SingleElement)
*/
private $_copyrights = array();
/**
* The licence element of this class.
*
* @var PairElement
*/
private $_license = null;
/**
* The author elements of this class.
*
* @var array(SingleElement)
*/
private $_authors = array();
/**
* Returns the allowed tags withing a class comment.
*
* @return array(string => int)
*/
protected function getAllowedTags()
{
return array(
'category' => false,
'package' => true,
'subpackage' => true,
'author' => false,
'copyright' => true,
'license' => false,
'version' => true,
);
}//end getAllowedTags()
/**
* Parses the license tag of this class comment.
*
* @param array $tokens The tokens that comprise this tag.
*
* @return PHP_CodeSniffer_CommentParser_PairElement
*/
protected function parseLicense($tokens)
{
$this->_license = new PHP_CodeSniffer_CommentParser_PairElement($this->previousElement, $tokens, 'license', $this->phpcsFile);
return $this->_license;
}//end parseLicense()
/**
* Parses the copyright tags of this class comment.
*
* @param array $tokens The tokens that comprise this tag.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
protected function parseCopyright($tokens)
{
$copyright = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'copyright', $this->phpcsFile);
$this->_copyrights[] = $copyright;
return $copyright;
}//end parseCopyright()
/**
* Parses the category tag of this class comment.
*
* @param array $tokens The tokens that comprise this tag.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
protected function parseCategory($tokens)
{
$this->_category = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'category', $this->phpcsFile);
return $this->_category;
}//end parseCategory()
/**
* Parses the author tag of this class comment.
*
* @param array $tokens The tokens that comprise this tag.
*
* @return array(PHP_CodeSniffer_CommentParser_SingleElement)
*/
protected function parseAuthor($tokens)
{
$author = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'author', $this->phpcsFile);
$this->_authors[] = $author;
return $author;
}//end parseAuthor()
/**
* Parses the version tag of this class comment.
*
* @param array $tokens The tokens that comprise this tag.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
protected function parseVersion($tokens)
{
$this->_version = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'version', $this->phpcsFile);
return $this->_version;
}//end parseVersion()
/**
* Parses the package tag found in this test.
*
* @param array $tokens The tokens that comprise this var.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
protected function parsePackage($tokens)
{
$this->_package = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'package', $this->phpcsFile);
return $this->_package;
}//end parsePackage()
/**
* Parses the package tag found in this test.
*
* @param array $tokens The tokens that comprise this var.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
protected function parseSubpackage($tokens)
{
$this->_subpackage = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'subpackage', $this->phpcsFile);
return $this->_subpackage;
}//end parseSubpackage()
/**
* Returns the authors of this class comment.
*
* @return array(PHP_CodeSniffer_CommentParser_SingleElement)
*/
public function getAuthors()
{
return $this->_authors;
}//end getAuthors()
/**
* Returns the version of this class comment.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
public function getVersion()
{
return $this->_version;
}//end getVersion()
/**
* Returns the license of this class comment.
*
* @return PHP_CodeSniffer_CommentParser_PairElement
*/
public function getLicense()
{
return $this->_license;
}//end getLicense()
/**
* Returns the copyrights of this class comment.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
public function getCopyrights()
{
return $this->_copyrights;
}//end getCopyrights()
/**
* Returns the category of this class comment.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
public function getCategory()
{
return $this->_category;
}//end getCategory()
/**
* Returns the package that this class belongs to.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
public function getPackage()
{
return $this->_package;
}//end getPackage()
/**
* Returns the subpackage that this class belongs to.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
public function getSubpackage()
{
return $this->_subpackage;
}//end getSubpackage()
}//end class
?>

View file

@ -0,0 +1,240 @@
<?php
/**
* A class to represent Comments of a doc comment.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_CommentParser_SingleElement', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_SingleElement not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* A class to represent Comments of a doc comment.
*
* Comments are in the following format.
* <code>
* /** <--this is the start of the comment.
* * This is a short comment description
* *
* * This is a long comment description
* * <-- this is the end of the comment
* * @return something
* {@/}
* </code>
*
* Note that the sentence before two newlines is assumed
* the short comment description.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_CommentParser_CommentElement extends PHP_CodeSniffer_CommentParser_SingleElement
{
/**
* Constructs a PHP_CodeSniffer_CommentParser_CommentElement.
*
* @param PHP_CodeSniffer_CommentParser_DocElemement $previousElement The element
* that
* appears
* before this
* element.
* @param array $tokens The tokens
* that make
* up this
* element.
* @param PHP_CodeSniffer_File $phpcsFile The file
* that this
* element is
* in.
*/
public function __construct($previousElement, $tokens, PHP_CodeSniffer_File $phpcsFile)
{
parent::__construct($previousElement, $tokens, 'comment', $phpcsFile);
}//end __construct()
/**
* Returns the short comment description.
*
* @return string
* @see getLongComment()
*/
public function getShortComment()
{
$pos = $this->_getShortCommentEndPos();
if ($pos === -1) {
return '';
}
return implode('', array_slice($this->tokens, 0, ($pos + 1)));
}//end getShortComment()
/**
* Returns the last token position of the short comment description.
*
* @return int The last token position of the short comment description
* @see _getLongCommentStartPos()
*/
private function _getShortCommentEndPos()
{
$found = false;
$whiteSpace = array(
' ',
"\t",
);
foreach ($this->tokens as $pos => $token) {
$token = str_replace($whiteSpace, '', $token);
if ($token === $this->phpcsFile->eolChar) {
if ($found === false) {
// Include newlines before short description.
continue;
} else {
if (isset($this->tokens[($pos + 1)]) === true) {
if ($this->tokens[($pos + 1)] === $this->phpcsFile->eolChar) {
return ($pos - 1);
}
} else {
return $pos;
}
}
} else {
$found = true;
}
}//end foreach
return (count($this->tokens) - 1);
}//end _getShortCommentEndPos()
/**
* Returns the long comment description.
*
* @return string
* @see getShortComment
*/
public function getLongComment()
{
$start = $this->_getLongCommentStartPos();
if ($start === -1) {
return '';
}
return implode('', array_slice($this->tokens, $start));
}//end getLongComment()
/**
* Returns the start position of the long comment description.
*
* Returns -1 if there is no long comment.
*
* @return int The start position of the long comment description.
* @see _getShortCommentEndPos()
*/
private function _getLongCommentStartPos()
{
$pos = ($this->_getShortCommentEndPos() + 1);
if ($pos === (count($this->tokens) - 1)) {
return -1;
}
$count = count($this->tokens);
for ($i = $pos; $i < $count; $i++) {
$content = trim($this->tokens[$i]);
if ($content !== '') {
if ($content{0} === '@') {
return -1;
}
return $i;
}
}
return -1;
}//end _getLongCommentStartPos()
/**
* Returns the whitespace that exists between
* the short and the long comment description.
*
* @return string
*/
public function getWhiteSpaceBetween()
{
$endShort = ($this->_getShortCommentEndPos() + 1);
$startLong = ($this->_getLongCommentStartPos() - 1);
if ($startLong === -1) {
return '';
}
return implode('', array_slice($this->tokens, $endShort, ($startLong - $endShort)));
}//end getWhiteSpaceBetween()
/**
* Returns the number of newlines that exist before the tags.
*
* @return int
*/
public function getNewlineAfter()
{
$long = $this->getLongComment();
if ($long !== '') {
$long = rtrim($long, ' ');
$long = strrev($long);
$newlines = strspn($long, $this->phpcsFile->eolChar);
} else {
$endShort = ($this->_getShortCommentEndPos() + 1);
$after = implode('', array_slice($this->tokens, $endShort));
$after = trim($after, ' ');
$newlines = strspn($after, $this->phpcsFile->eolChar);
}
return ($newlines / strlen($this->phpcsFile->eolChar));
}//end getNewlineAfter()
/**
* Returns true if there is no comment.
*
* @return boolean
*/
public function isEmpty()
{
return (trim($this->getContent()) === '');
}//end isEmpty()
}//end class
?>

View file

@ -0,0 +1,105 @@
<?php
/**
* A DocElement represents a logical element within a Doc Comment.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* A DocElement represents a logical element within a Doc Comment.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
interface PHP_CodeSniffer_CommentParser_DocElement
{
/**
* Returns the name of the tag this element represents, omitting the @ symbol.
*
* @return string
*/
public function getTag();
/**
* Returns the whitespace that exists before this element.
*
* @return string
* @see getWhitespaceAfter()
*/
public function getWhitespaceBefore();
/**
* Returns the whitespace that exists after this element.
*
* @return string
* @see getWhitespaceBefore()
*/
public function getWhitespaceAfter();
/**
* Returns the order that this element appears in the doc comment.
*
* The first element in the comment should have an order of 1.
*
* @return int
*/
public function getOrder();
/**
* Returns the element that appears before this element.
*
* @return PHP_CodeSniffer_CommentParser_DocElement
* @see getNextElement()
*/
public function getPreviousElement();
/**
* Returns the element that appears after this element.
*
* @return PHP_CodeSniffer_CommentParser_DocElement
* @see getPreviousElement()
*/
public function getNextElement();
/**
* Returns the line that this element started on.
*
* @return int
*/
public function getLine();
/**
* Returns the raw content of this element, ommiting the tag.
*
* @return string
*/
public function getRawContent();
}//end interface
?>

View file

@ -0,0 +1,196 @@
<?php
/**
* Parses function doc comments.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_CommentParser_AbstractParser', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_AbstractParser not found';
throw new PHP_CodeSniffer_Exception($error);
}
if (class_exists('PHP_CodeSniffer_CommentParser_ParameterElement', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_ParameterElement not found';
throw new PHP_CodeSniffer_Exception($error);
}
if (class_exists('PHP_CodeSniffer_CommentParser_PairElement', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_PairElement not found';
throw new PHP_CodeSniffer_Exception($error);
}
if (class_exists('PHP_CodeSniffer_CommentParser_SingleElement', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_SingleElement not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* Parses function doc comments.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_CommentParser_FunctionCommentParser extends PHP_CodeSniffer_CommentParser_AbstractParser
{
/**
* The parameter elements within this function comment.
*
* @var array(PHP_CodeSniffer_CommentParser_ParameterElement)
*/
private $_params = array();
/**
* The return element in this function comment.
*
* @var PHP_CodeSniffer_CommentParser_PairElement.
*/
private $_return = null;
/**
* The throws element list for this function comment.
*
* @var array(PHP_CodeSniffer_CommentParser_PairElement)
*/
private $_throws = array();
/**
* Constructs a PHP_CodeSniffer_CommentParser_FunctionCommentParser.
*
* @param string $comment The comment to parse.
* @param PHP_CodeSniffer_File $phpcsFile The file that this comment is in.
*/
public function __construct($comment, PHP_CodeSniffer_File $phpcsFile)
{
parent::__construct($comment, $phpcsFile);
}//end __construct()
/**
* Parses parameter elements.
*
* @param array(string) $tokens The tokens that conmpise this sub element.
*
* @return PHP_CodeSniffer_CommentParser_ParameterElement
*/
protected function parseParam($tokens)
{
$param = new PHP_CodeSniffer_CommentParser_ParameterElement($this->previousElement, $tokens, $this->phpcsFile);
$this->_params[] = $param;
return $param;
}//end parseParam()
/**
* Parses return elements.
*
* @param array(string) $tokens The tokens that conmpise this sub element.
*
* @return PHP_CodeSniffer_CommentParser_PairElement
*/
protected function parseReturn($tokens)
{
$return = new PHP_CodeSniffer_CommentParser_PairElement($this->previousElement, $tokens, 'return', $this->phpcsFile);
$this->_return = $return;
return $return;
}//end parseReturn()
/**
* Parses throws elements.
*
* @param array(string) $tokens The tokens that conmpise this sub element.
*
* @return PHP_CodeSniffer_CommentParser_PairElement
*/
protected function parseThrows($tokens)
{
$throws = new PHP_CodeSniffer_CommentParser_PairElement($this->previousElement, $tokens, 'throws', $this->phpcsFile);
$this->_throws[] = $throws;
return $throws;
}//end parseThrows()
/**
* Returns the parameter elements that this function comment contains.
*
* Returns an empty array if no parameter elements are contained within
* this function comment.
*
* @return array(PHP_CodeSniffer_CommentParser_ParameterElement)
*/
public function getParams()
{
return $this->_params;
}//end getParams()
/**
* Returns the return element in this fucntion comment.
*
* Returns null if no return element exists in the comment.
*
* @return PHP_CodeSniffer_CommentParser_PairElement
*/
public function getReturn()
{
return $this->_return;
}//end getReturn()
/**
* Returns the throws elements in this fucntion comment.
*
* Returns empty array if no throws elements in the comment.
*
* @return array(PHP_CodeSniffer_CommentParser_PairElement)
*/
public function getThrows()
{
return $this->_throws;
}//end getThrows()
/**
* Returns the allowed tags that can exist in a function comment.
*
* @return array(string => boolean)
*/
protected function getAllowedTags()
{
return array(
'param' => false,
'return' => true,
'throws' => false,
);
}//end getAllowedTags()
}//end class
?>

View file

@ -0,0 +1,86 @@
<?php
/**
* Parses class member comments.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @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);
}
/**
* Parses class member comments.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_CommentParser_MemberCommentParser extends PHP_CodeSniffer_CommentParser_ClassCommentParser
{
/**
* Represents a \@var tag in a member comment.
*
* @var PHP_CodeSniffer_CommentParser_SingleElement
*/
private $_var = null;
/**
* Parses Var tags.
*
* @param array $tokens The tokens that represent this tag.
*
* @return PHP_CodeSniffer_CommentParser_SingleElement
*/
protected function parseVar($tokens)
{
$this->_var = new PHP_CodeSniffer_CommentParser_SingleElement($this->previousElement, $tokens, 'var', $this->phpcsFile);
return $this->_var;
}//end parseVar()
/**
* Returns the var tag found in the member comment.
*
* @return PHP_CodeSniffer_CommentParser_PairElement
*/
public function getVar()
{
return $this->_var;
}//end getVar()
/**
* Returns the allowed tags for this parser.
*
* @return array
*/
protected function getAllowedTags()
{
return array('var' => true);
}//end getAllowedTags()
}//end class
?>

View file

@ -0,0 +1,168 @@
<?php
/**
* A class to represent elements that have a value => comment format.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_CommentParser_AbstractDocElement', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_AbstractDocElement not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* A class to represent elements that have a value => comment format.
*
* An example of a pair element tag is the \@throws as it has an exception type
* and a comment on the circumstance of when the exception is thrown.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_CommentParser_PairElement extends PHP_CodeSniffer_CommentParser_AbstractDocElement
{
/**
* The value of the tag.
*
* @var string
*/
private $_value = '';
/**
* The comment of the tag.
*
* @var string
*/
private $_comment = '';
/**
* The whitespace that exists before the value elem.
*
* @var string
*/
private $_valueWhitespace = '';
/**
* The whitespace that exists before the comment elem.
*
* @var string
*/
private $_commentWhitespace = '';
/**
* Constructs a PHP_CodeSniffer_CommentParser_PairElement doc tag.
*
* @param PHP_CodeSniffer_CommentParser_DocElement $previousElement The element
* before this
* one.
* @param array $tokens The tokens
* that comprise
* this element.
* @param string $tag The tag that
* this element
* represents.
* @param PHP_CodeSniffer_File $phpcsFile The file that
* this element
* is in.
*/
public function __construct($previousElement, $tokens, $tag, PHP_CodeSniffer_File $phpcsFile)
{
parent::__construct($previousElement, $tokens, $tag, $phpcsFile);
}//end __construct()
/**
* Returns the element names that this tag is comprised of, in the order
* that they appear in the tag.
*
* @return array(string)
* @see processSubElement()
*/
protected function getSubElements()
{
return array(
'value',
'comment',
);
}//end getSubElements()
/**
* Processes the sub element with the specified name.
*
* @param string $name The name of the sub element to process.
* @param string $content The content of this sub element.
* @param string $whitespaceBefore The whitespace that exists before the
* sub element.
*
* @return void
* @see getSubElements()
*/
protected function processSubElement($name, $content, $whitespaceBefore)
{
$element = '_'.$name;
$whitespace = $element.'Whitespace';
$this->$element = $content;
$this->$whitespace = $whitespaceBefore;
}//end processSubElement()
/**
* Returns the value of the tag.
*
* @return string
*/
public function getValue()
{
return $this->_value;
}//end getValue()
/**
* Returns the comment associated with the value of this tag.
*
* @return string
*/
public function getComment()
{
return $this->_comment;
}//end getComment()
/**
* Returns the witespace before the content of this tag.
*
* @return string
*/
public function getWhitespaceBeforeValue()
{
return $this->_valueWhitespace;
}//end getWhitespaceBeforeValue()
}//end class
?>

View file

@ -0,0 +1,326 @@
<?php
/**
* A class to represent param tags within a function comment.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_CommentParser_AbstractDocElement', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_AbstractDocElement not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* A class to represent param tags within a function comment.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_CommentParser_ParameterElement extends PHP_CodeSniffer_CommentParser_AbstractDocElement
{
/**
* The variable name of this parameter name, including the $ sign.
*
* @var string
*/
private $_varName = '';
/**
* The comment of this parameter tag.
*
* @var string
*/
private $_comment = '';
/**
* The variable type of this parameter tag.
*
* @var string
*/
private $_type = '';
/**
* The whitespace that exists before the variable name.
*
* @var string
*/
private $_varNameWhitespace = '';
/**
* The whitespace that exists before the comment.
*
* @var string
*/
private $_commentWhitespace = null;
/**
* The whitespace that exists before the variable type.
*
* @var string
*/
private $_typeWhitespace = '';
/**
* Constructs a PHP_CodeSniffer_CommentParser_ParameterElement.
*
* @param PHP_CodeSniffer_CommentParser_DocElement $previousElement The element
* previous to
* this one.
* @param array $tokens The tokens
* that make up
* this element.
* @param PHP_CodeSniffer_File $phpcsFile The file that
* this element
* is in.
*/
public function __construct($previousElement, $tokens, PHP_CodeSniffer_File $phpcsFile)
{
parent::__construct($previousElement, $tokens, 'param', $phpcsFile);
// Handle special variable type: array(x => y).
$type = strtolower($this->_type);
if ($this->_varName === '=>' && strpos($type, 'array(') !== false) {
$rawContent = $this->getRawContent();
$matches = array();
if (preg_match('/^(\s+)(array\(.*\))(\s+)(\$\S*)(\s+)(.*)/i', $rawContent, $matches) !== 0) {
// Process the sub elements correctly for this special case.
if (count($matches) === 7) {
$this->processSubElement('type', $matches[2], $matches[1]);
$this->processSubElement('varName', $matches[4], $matches[3]);
$this->processSubElement('comment', $matches[6], $matches[5]);
}
}
}
}//end __construct()
/**
* Returns the element names that this tag is comprised of, in the order
* that they appear in the tag.
*
* @return array(string)
* @see processSubElement()
*/
protected function getSubElements()
{
return array(
'type',
'varName',
'comment',
);
}//end getSubElements()
/**
* Processes the sub element with the specified name.
*
* @param string $name The name of the sub element to process.
* @param string $content The content of this sub element.
* @param string $beforeWhitespace The whitespace that exists before the
* sub element.
*
* @return void
* @see getSubElements()
*/
protected function processSubElement($name, $content, $beforeWhitespace)
{
$element = '_'.$name;
$whitespace = $element.'Whitespace';
$this->$element = $content;
$this->$whitespace = $beforeWhitespace;
}//end processSubElement()
/**
* Returns the variable name that this parameter tag represents.
*
* @return string
*/
public function getVarName()
{
return $this->_varName;
}//end getVarName()
/**
* Returns the variable type that this string represents.
*
* @return string
*/
public function getType()
{
return $this->_type;
}//end getType()
/**
* Returns the comment of this comment for this parameter.
*
* @return string
*/
public function getComment()
{
return $this->_comment;
}//end getComment()
/**
* Returns the whitespace before the variable type.
*
* @return stirng
* @see getWhiteSpaceBeforeVarName()
* @see getWhiteSpaceBeforeComment()
*/
public function getWhiteSpaceBeforeType()
{
return $this->_typeWhitespace;
}//end getWhiteSpaceBeforeType()
/**
* Returns the whitespace before the variable name.
*
* @return string
* @see getWhiteSpaceBeforeComment()
* @see getWhiteSpaceBeforeType()
*/
public function getWhiteSpaceBeforeVarName()
{
return $this->_varNameWhitespace;
}//end getWhiteSpaceBeforeVarName()
/**
* Returns the whitespace before the comment.
*
* @return string
* @see getWhiteSpaceBeforeVarName()
* @see getWhiteSpaceBeforeType()
*/
public function getWhiteSpaceBeforeComment()
{
return $this->_commentWhitespace;
}//end getWhiteSpaceBeforeComment()
/**
* Returns the postition of this parameter are it appears in the comment.
*
* This method differs from getOrder as it is only relative to method
* parameters.
*
* @return int
*/
public function getPosition()
{
if (($this->getPreviousElement() instanceof PHP_CodeSniffer_CommentParser_ParameterElement) === false) {
return 1;
} else {
return ($this->getPreviousElement()->getPosition() + 1);
}
}//end getPosition()
/**
* Returns true if this parameter's variable aligns with the other's.
*
* @param PHP_CodeSniffer_CommentParser_ParameterElement $other The other param
* to check
* alignment with.
*
* @return boolean
*/
public function alignsVariableWith(PHP_CodeSniffer_CommentParser_ParameterElement $other)
{
// Format is:
// @param type $variable Comment.
// @param <-a-><---b---->
// Compares the index before param variable.
$otherVar = (strlen($other->_type) + strlen($other->_varNameWhitespace));
$thisVar = (strlen($this->_type) + strlen($this->_varNameWhitespace));
if ($otherVar !== $thisVar) {
return false;
}
return true;
}//end alignsVariableWith()
/**
* Returns true if this parameter's comment aligns with the other's.
*
* @param PHP_CodeSniffer_CommentParser_ParameterElement $other The other param
* to check
* alignment with.
*
* @return boolean
*/
public function alignsCommentWith(PHP_CodeSniffer_CommentParser_ParameterElement $other)
{
// Compares the index before param comment.
$otherComment = (strlen($other->_varName) + strlen($other->_commentWhitespace));
$thisComment = (strlen($this->_varName) + strlen($this->_commentWhitespace));
if ($otherComment !== $thisComment) {
return false;
}
return true;
}//end alignsCommentWith()
/**
* Returns true if this parameter aligns with the other paramter.
*
* @param PHP_CodeSniffer_CommentParser_ParameterElement $other The other param
* to check
* alignment with.
*
* @return boolean
*/
public function alignsWith(PHP_CodeSniffer_CommentParser_ParameterElement $other)
{
if ($this->alignsVariableWith($other) === false) {
return false;
}
if ($this->alignsCommentWith($other) === false) {
return false;
}
return true;
}//end alignsWith()
}//end class
?>

View file

@ -0,0 +1,72 @@
<?php
/**
* An exception to be thrown when a DocCommentParser finds an anomilty in a
* doc comment.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* An exception to be thrown when a DocCommentParser finds an anomilty in a
* doc comment.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_CommentParser_ParserException extends Exception
{
/**
* The line where the exception occured, in relation to the doc comment.
*
* @var int
*/
private $_line = 0;
/**
* Constructs a DocCommentParserException.
*
* @param string $message The message of the exception.
* @param int $line The position in comment where the error occured.
* A position of 0 indicates that the error occured
* at the opening line of the doc comment.
*/
public function __construct($message, $line)
{
parent::__construct($message);
$this->_line = $line;
}//end __construct()
/**
* Returns the line number within the comment where the exception occured.
*
* @return int
*/
public function getLineWithinComment()
{
return $this->_line;
}//end getLineWithinComment()
}//end class
?>

View file

@ -0,0 +1,161 @@
<?php
/**
* A class to represent single element doc tags.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_CommentParser_AbstractDocElement', true) === false) {
$error = 'Class PHP_CodeSniffer_CommentParser_AbstractDocElement not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* A class to represent single element doc tags.
*
* A single element doc tag contains only one value after the tag itself. An
* example would be the \@package tag.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_CommentParser_SingleElement extends PHP_CodeSniffer_CommentParser_AbstractDocElement
{
/**
* The content that exists after the tag.
*
* @var string
* @see getContent()
*/
protected $content = '';
/**
* The whitespace that exists before the content.
*
* @var string
* @see getWhitespaceBeforeContent()
*/
protected $contentWhitespace = '';
/**
* Constructs a SingleElement doc tag.
*
* @param PHP_CodeSniffer_CommentParser_DocElement $previousElement The element
* before this
* one.
* @param array $tokens The tokens
* that comprise
* this element.
* @param string $tag The tag that
* this element
* represents.
* @param PHP_CodeSniffer_File $phpcsFile The file that
* this element
* is in.
*/
public function __construct($previousElement, $tokens, $tag, PHP_CodeSniffer_File $phpcsFile)
{
parent::__construct($previousElement, $tokens, $tag, $phpcsFile);
}//end __construct()
/**
* Returns the element names that this tag is comprised of, in the order
* that they appear in the tag.
*
* @return array(string)
* @see processSubElement()
*/
protected function getSubElements()
{
return array('content');
}//end getSubElements()
/**
* Processes the sub element with the specified name.
*
* @param string $name The name of the sub element to process.
* @param string $content The content of this sub element.
* @param string $whitespaceBefore The whitespace that exists before the
* sub element.
*
* @return void
* @see getSubElements()
*/
protected function processSubElement($name, $content, $whitespaceBefore)
{
$this->content = $content;
$this->contentWhitespace = $whitespaceBefore;
}//end processSubElement()
/**
* Returns the content of this tag.
*
* @return string
*/
public function getContent()
{
return $this->content;
}//end getContent()
/**
* Returns the witespace before the content of this tag.
*
* @return string
*/
public function getWhitespaceBeforeContent()
{
return $this->contentWhitespace;
}//end getWhitespaceBeforeContent()
/**
* Processes a content check for single doc element.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param int $commentStart The line number where
* the error occurs.
* @param string $docBlock Whether this is a file or
* class comment doc.
*
* @return void
*/
public function process(PHP_CodeSniffer_File $phpcsFile, $commentStart, $docBlock)
{
if ($this->content === '') {
$errorPos = ($commentStart + $this->getLine());
$error = "Content missing for $this->tag tag in $docBlock comment";
$phpcsFile->addError($error, $errorPos);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,191 @@
<?php
/**
* The base class for all PHP_CodeSniffer documentation generators.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* The base class for all PHP_CodeSniffer documentation generators.
*
* Documentation generators are used to print documentation about code sniffs
* in a standard.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_DocGenerators_Generator
{
/**
* The name of the coding standard we are generating docs for.
*
* @var string
*/
private $_standard = '';
/**
* An array of sniffs that we are limiting the generated docs to.
*
* If this array is empty, docs are generated for all sniffs in the
* supplied coding standard.
*
* @var string
*/
private $_sniffs = array();
/**
* Constructs a PHP_CodeSniffer_DocGenerators_Generator object.
*
* @param string $standard The name of the coding standard to generate
* docs for.
* @param array $sniffs An array of sniffs that we are limiting the
* generated docs to.
*
* @see generate()
*/
public function __construct($standard, array $sniffs=array())
{
$this->_standard = $standard;
$this->_sniffs = $sniffs;
}//end __construct()
/**
* Retrieves the title of the sniff from the DOMNode supplied.
*
* @param DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return string
*/
protected function getTitle(DOMNode $doc)
{
return $doc->getAttribute('title');
}//end getTitle()
/**
* Retrieves the name of the standard we are generating docs for.
*
* @return string
*/
protected function getStandard()
{
return $this->_standard;
}//end getStandard()
/**
* Generates the documentation for a standard.
*
* It's probably wise for doc generators to override this method so they
* have control over how the docs are produced. Otherwise, the processSniff
* method should be overridden to output content for each sniff.
*
* @return void
* @see processSniff()
*/
public function generate()
{
$standardFiles = $this->getStandardFiles();
foreach ($standardFiles as $standard) {
$doc = new DOMDocument();
$doc->load($standard);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$this->processSniff($documentation);
}
}//end generate()
/**
* Returns a list of paths to XML standard files for all sniffs in a standard.
*
* Any sniffs that do not have an XML standard file are obviously not included
* in the returned array. If documentation is only being generated for some
* sniffs (ie. $this->_sniffs is not empty) then all others sniffs will
* be filtered from the results as well.
*
* @return array(string)
*/
protected function getStandardFiles()
{
if (is_dir($this->_standard) === true) {
// This is a custom standard.
$standardDir = $this->_standard;
$standard = basename($this->_standard);
} else {
$standardDir = realpath(dirname(__FILE__).'/../Standards/'.$this->_standard);
$standard = $this->_standard;
}
$sniffs = PHP_CodeSniffer::getSniffFiles($standardDir, $standard);
$standardFiles = array();
foreach ($sniffs as $sniff) {
if (empty($this->_sniffs) === false) {
// We are limiting the docs to certain sniffs only, so filter
// out any unwanted sniffs.
$sniffName = substr($sniff, (strrpos($sniff, '/') + 1));
$sniffName = substr($sniffName, 0, -9);
if (in_array($sniffName, $this->_sniffs) === false) {
continue;
}
}
$standardFile = str_replace(DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR.'Docs'.DIRECTORY_SEPARATOR, $sniff);
$standardFile = str_replace('Sniff.php', 'Standard.xml', $standardFile);
if (is_file($standardFile) === true) {
$standardFiles[] = $standardFile;
}
}
return $standardFiles;
}//end getStandardFiles()
/**
* Process the documentation for a single sniff.
*
* Doc generators should override this function to produce output.
*
* @param DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return void
* @see generate()
*/
protected function processSniff(DOMNode $doc)
{
}//end processSniff()
}//end class
?>

View file

@ -0,0 +1,291 @@
<?php
/**
* A doc generator that outputs documentation in one big HTML file.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
require_once 'PHP/CodeSniffer/DocGenerators/Generator.php';
/**
* A doc generator that outputs documentation in one big HTML file.
*
* Output is in one large HTML file and is designed for you to style with
* your own stylesheet. It contains a table of contents at the top with anchors
* to each sniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_DocGenerators_HTML extends PHP_CodeSniffer_DocGenerators_Generator
{
/**
* Generates the documentation for a standard.
*
* @return void
* @see processSniff()
*/
public function generate()
{
ob_start();
$this->printHeader();
$standardFiles = $this->getStandardFiles();
$this->printToc($standardFiles);
foreach ($standardFiles as $standard) {
$doc = new DOMDocument();
$doc->load($standard);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$this->processSniff($documentation);
}
$this->printFooter();
$content = ob_get_contents();
ob_end_clean();
echo $content;
}//end generate()
/**
* Print the header of the HTML page.
*
* @return void
*/
protected function printHeader()
{
$standard = $this->getStandard();
echo '<html>'.PHP_EOL;
echo ' <head>'.PHP_EOL;
echo " <title>$standard Coding Standards</title>".PHP_EOL;
echo ' <style>
body {
background-color: #FFFFFF;
font-size: 14px;
font-family: Arial, Helvetica, sans-serif;
color: #000000;
}
h1 {
color: #666666;
font-size: 20px;
font-weight: bold;
margin-top: 0px;
background-color: #E6E7E8;
padding: 20px;
border: 1px solid #BBBBBB;
}
h2 {
color: #00A5E3;
font-size: 16px;
font-weight: normal;
margin-top: 50px;
}
.code-comparison {
width: 100%;
}
.code-comparison td {
border: 1px solid #CCCCCC;
}
.code-comparison-title, .code-comparison-code {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
color: #000000;
vertical-align: top;
padding: 4px;
width: 50%;
background-color: #F1F1F1;
line-height: 15px;
}
.code-comparison-code {
font-family: Courier;
background-color: #F9F9F9;
}
.code-comparison-highlight {
background-color: #DDF1F7;
border: 1px solid #00A5E3;
line-height: 15px;
}
.tag-line {
text-align: center;
width: 100%;
margin-top: 30px;
font-size: 12px;
}
.tag-line a {
color: #000000;
}
</style>'.PHP_EOL;
echo ' </head>'.PHP_EOL;
echo ' <body>'.PHP_EOL;
echo " <h1>$standard Coding Standards</h1>".PHP_EOL;
}//end printHeader()
/**
* Print the table of contents for the standard.
*
* The TOC is just an unordered list of bookmarks to sniffs on the page.
*
* @param array $standardFiles An array of paths to the XML standard files.
*
* @return void
*/
protected function printToc($standardFiles)
{
echo ' <h2>Table of Contents</h2>'.PHP_EOL;
echo ' <ul class="toc">'.PHP_EOL;
foreach ($standardFiles as $standard) {
$doc = new DOMDocument();
$doc->load($standard);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$title = $this->getTitle($documentation);
echo ' <li><a href="#'.str_replace(' ', '-', $title)."\">$title</a></li>".PHP_EOL;
}
echo ' </ul>'.PHP_EOL;
}//end printToc()
/**
* Print the footer of the HTML page.
*
* @return void
*/
protected function printFooter()
{
// Turn off strict errors so we don't get timezone warnings if people
// don't have their timezone set.
error_reporting(E_ALL);
echo ' <div class="tag-line">';
echo 'Documentation generated on '.date('r');
echo ' by <a href="http://pear.php.net/package/PHP_CodeSniffer">PHP_CodeSniffer 1.1.0</a>';
echo '</div>'.PHP_EOL;
error_reporting(E_ALL | E_STRICT);
echo ' </body>'.PHP_EOL;
echo '</html>'.PHP_EOL;
}//end printFooter()
/**
* Process the documentation for a single sniff.
*
* @param DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return void
*/
public function processSniff(DOMNode $doc)
{
$title = $this->getTitle($doc);
echo ' <a name="'.str_replace(' ', '-', $title).'" />'.PHP_EOL;
echo " <h2>$title</h2>".PHP_EOL;
foreach ($doc->childNodes as $node) {
if ($node->nodeName === 'standard') {
$this->printTextBlock($node);
} else if ($node->nodeName === 'code_comparison') {
$this->printCodeComparisonBlock($node);
}
}
}//end processSniff()
/**
* Print a text block found in a standard.
*
* @param DOMNode $node The DOMNode object for the text block.
*
* @return void
*/
protected function printTextBlock($node)
{
$content = trim($node->nodeValue);
$content = htmlspecialchars($content);
// Allow em tags only.
$content = str_replace('&lt;em&gt;', '<em>', $content);
$content = str_replace('&lt;/em&gt;', '</em>', $content);
echo " <p class=\"text\">$content</p>".PHP_EOL;
}//end printTextBlock()
/**
* Print a code comparison block found in a standard.
*
* @param DOMNode $node The DOMNode object for the code comparison block.
*
* @return void
*/
protected function printCodeComparisonBlock($node)
{
$codeBlocks = $node->getElementsByTagName('code');
$firstTitle = $codeBlocks->item(0)->getAttribute('title');
$first = trim($codeBlocks->item(0)->nodeValue);
$first = str_replace("\n", '</br>', $first);
$first = str_replace(' ', '&nbsp;', $first);
$first = str_replace('<em>', '<span class="code-comparison-highlight">', $first);
$first = str_replace('</em>', '</span>', $first);
$secondTitle = $codeBlocks->item(1)->getAttribute('title');
$second = trim($codeBlocks->item(1)->nodeValue);
$second = str_replace("\n", '</br>', $second);
$second = str_replace(' ', '&nbsp;', $second);
$second = str_replace('<em>', '<span class="code-comparison-highlight">', $second);
$second = str_replace('</em>', '</span>', $second);
echo ' <table class="code-comparison">'.PHP_EOL;
echo ' <tr>'.PHP_EOL;
echo " <td class=\"code-comparison-title\">$firstTitle</td>".PHP_EOL;
echo " <td class=\"code-comparison-title\">$secondTitle</td>".PHP_EOL;
echo ' </tr>'.PHP_EOL;
echo ' <tr>'.PHP_EOL;
echo " <td class=\"code-comparison-code\">$first</td>".PHP_EOL;
echo " <td class=\"code-comparison-code\">$second</td>".PHP_EOL;
echo ' </tr>'.PHP_EOL;
echo ' </table>'.PHP_EOL;
}//end printCodeComparisonBlock()
}//end class
?>

View file

@ -0,0 +1,266 @@
<?php
/**
* A doc generator that outputs text-based documentation.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
require_once 'PHP/CodeSniffer/DocGenerators/Generator.php';
/**
* A doc generator that outputs text-based documentation.
*
* Output is designed to be displayed in a terminal and is wrapped to 100 characters.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_DocGenerators_Text extends PHP_CodeSniffer_DocGenerators_Generator
{
/**
* Process the documentation for a single sniff.
*
* @param DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return void
*/
public function processSniff(DOMNode $doc)
{
$this->printTitle($doc);
foreach ($doc->childNodes as $node) {
if ($node->nodeName === 'standard') {
$this->printTextBlock($node);
} else if ($node->nodeName === 'code_comparison') {
$this->printCodeComparisonBlock($node);
}
}
}//end processSniff()
/**
* Prints the title area for a single sniff.
*
* @param DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return void
*/
protected function printTitle(DOMNode $doc)
{
$title = $this->getTitle($doc);
$standard = $this->getStandard();
echo PHP_EOL;
echo str_repeat('-', (strlen("$standard CODING STANDARD: $title") + 4));
echo strtoupper(PHP_EOL."| $standard CODING STANDARD: $title |".PHP_EOL);
echo str_repeat('-', (strlen("$standard CODING STANDARD: $title") + 4));
echo PHP_EOL.PHP_EOL;
}//end printTitle()
/**
* Print a text block found in a standard.
*
* @param DOMNode $node The DOMNode object for the text block.
*
* @return void
*/
protected function printTextBlock($node)
{
$text = trim($node->nodeValue);
$text = str_replace('<em>', '*', $text);
$text = str_replace('</em>', '*', $text);
$lines = array();
$tempLine = '';
$words = explode(' ', $text);
foreach ($words as $word) {
if (strlen($tempLine.$word) >= 99) {
if (strlen($tempLine.$word) === 99) {
// Adding the extra space will push us to the edge
// so we are done.
$lines[] = $tempLine.$word;
$tempLine = '';
} else if (strlen($tempLine.$word) === 100) {
// We are already at the edge, so we are done.
$lines[] = $tempLine.$word;
$tempLine = '';
} else {
$lines[] = rtrim($tempLine);
$tempLine = $word.' ';
}
} else {
$tempLine .= $word.' ';
}
}//end foreach
if ($tempLine !== '') {
$lines[] = rtrim($tempLine);
}
echo implode(PHP_EOL, $lines).PHP_EOL.PHP_EOL;
}//end printTextBlock()
/**
* Print a code comparison block found in a standard.
*
* @param DOMNode $node The DOMNode object for the code comparison block.
*
* @return void
*/
protected function printCodeComparisonBlock($node)
{
$codeBlocks = $node->getElementsByTagName('code');
$first = trim($codeBlocks->item(0)->nodeValue);
$firstTitle = $codeBlocks->item(0)->getAttribute('title');
$firstTitleLines = array();
$tempTitle = '';
$words = explode(' ', $firstTitle);
foreach ($words as $word) {
if (strlen($tempTitle.$word) >= 45) {
if (strlen($tempTitle.$word) === 45) {
// Adding the extra space will push us to the edge
// so we are done.
$firstTitleLines[] = $tempTitle.$word;
$tempTitle = '';
} else if (strlen($tempTitle.$word) === 46) {
// We are already at the edge, so we are done.
$firstTitleLines[] = $tempTitle.$word;
$tempTitle = '';
} else {
$firstTitleLines[] = $tempTitle;
$tempTitle = $word;
}
} else {
$tempTitle .= $word.' ';
}
}//end foreach
if ($tempTitle !== '') {
$firstTitleLines[] = $tempTitle;
}
$first = str_replace('<em>', '', $first);
$first = str_replace('</em>', '', $first);
$firstLines = explode("\n", $first);
$second = trim($codeBlocks->item(1)->nodeValue);
$secondTitle = $codeBlocks->item(1)->getAttribute('title');
$secondTitleLines = array();
$tempTitle = '';
$words = explode(' ', $secondTitle);
foreach ($words as $word) {
if (strlen($tempTitle.$word) >= 45) {
if (strlen($tempTitle.$word) === 45) {
// Adding the extra space will push us to the edge
// so we are done.
$secondTitleLines[] = $tempTitle.$word;
$tempTitle = '';
} else if (strlen($tempTitle.$word) === 46) {
// We are already at the edge, so we are done.
$secondTitleLines[] = $tempTitle.$word;
$tempTitle = '';
} else {
$secondTitleLines[] = $tempTitle;
$tempTitle = $word;
}
} else {
$tempTitle .= $word.' ';
}
}//end foreach
if ($tempTitle !== '') {
$secondTitleLines[] = $tempTitle;
}
$second = str_replace('<em>', '', $second);
$second = str_replace('</em>', '', $second);
$secondLines = explode("\n", $second);
$maxCodeLines = max(count($firstLines), count($secondLines));
$maxTitleLines = max(count($firstTitleLines), count($secondTitleLines));
echo str_repeat('-', 41);
echo ' CODE COMPARISON ';
echo str_repeat('-', 42).PHP_EOL;
for ($i = 0; $i < $maxTitleLines; $i++) {
if (isset($firstTitleLines[$i]) === true) {
$firstLineText = $firstTitleLines[$i];
} else {
$firstLineText = '';
}
if (isset($secondTitleLines[$i]) === true) {
$secondLineText = $secondTitleLines[$i];
} else {
$secondLineText = '';
}
echo '| ';
echo $firstLineText.str_repeat(' ', (46 - strlen($firstLineText)));
echo ' | ';
echo $secondLineText.str_repeat(' ', (47 - strlen($secondLineText)));
echo ' |'.PHP_EOL;
}//end for
echo str_repeat('-', 100).PHP_EOL;
for ($i = 0; $i < $maxCodeLines; $i++) {
if (isset($firstLines[$i]) === true) {
$firstLineText = $firstLines[$i];
} else {
$firstLineText = '';
}
if (isset($secondLines[$i]) === true) {
$secondLineText = $secondLines[$i];
} else {
$secondLineText = '';
}
echo '| ';
echo $firstLineText.str_repeat(' ', (47 - strlen($firstLineText)));
echo '| ';
echo $secondLineText.str_repeat(' ', (48 - strlen($secondLineText)));
echo '|'.PHP_EOL;
}//end for
echo str_repeat('-', 100).PHP_EOL.PHP_EOL;
}//end printCodeComparisonBlock()
}//end class
?>

View file

@ -0,0 +1,36 @@
<?php
/**
* An exception thrown by PHP_CodeSniffer when it encounters an unrecoverable error.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
require_once 'PEAR/Exception.php';
/**
* An exception thrown by PHP_CodeSniffer when it encounters an unrecoverable error.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_Exception extends PEAR_Exception
{
}//end class
?>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,94 @@
<?php
/**
* Represents a PHP_CodeSniffer sniff for sniffing coding standards.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Represents a PHP_CodeSniffer sniff for sniffing coding standards.
*
* A sniff registers what token types it wishes to listen for, then, when
* PHP_CodeSniffer encounters that token, the sniff is invoked and passed
* information about where the token was found in the stack, and the
* PHP_CodeSniffer file in which the token was found.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
interface PHP_CodeSniffer_Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* An example return value for a sniff that wants to listen for whitespace
* and any comments would be:
*
* <code>
* return array(
* T_WHITESPACE,
* T_DOC_COMMENT,
* T_COMMENT,
* );
* </code>
*
* @return array(int)
* @see Tokens.php
*/
public function register();
/**
* Called when one of the token types that this sniff is listening for
* is found.
*
* The stackPtr variable indicates where in the stack the token was found.
* A sniff can acquire information this token, along with all the other
* tokens within the stack by first acquiring the token stack:
*
* <code>
* $tokens = $phpcsFile->getTokens();
* echo 'Encountered a '.$tokens[$stackPtr]['type'].' token';
* echo 'token information: ';
* print_r($tokens[$stackPtr]);
* </code>
*
* If the sniff discovers an anomilty in the code, they can raise an error
* by calling addError() on the PHP_CodeSniffer_File object, specifying an error
* message and the position of the offending token:
*
* <code>
* $phpcsFile->addError('Encountered an error', $stackPtr);
* </code>
*
* @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
* @param int $stackPtr The position in the PHP_CodeSniffer
* file's token stack where the token
* was found.
*
* @return void
*/
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr);
}//end interface
?>

View file

@ -0,0 +1,779 @@
<?php
/**
* Processes pattern strings and checks that the code conforms to the pattern.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_Standards_IncorrectPatternException', true) === false) {
$error = 'Class PHP_CodeSniffer_Standards_IncorrectPatternException not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* Processes pattern strings and checks that the code conforms to the pattern.
*
* This test essentially checks that code is correctly formatted with whitespace.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
abstract class PHP_CodeSniffer_Standards_AbstractPatternSniff implements PHP_CodeSniffer_Sniff
{
/**
* The parsed patterns array.
*
* @var array
*/
private $_parsedPatterns = array();
/**
* Tokens that wish this sniff wishes to process outside of the patterns.
*
* @var array(int)
* @see registerSupplementary()
* @see processSupplementary()
*/
private $_supplementaryTokens = array();
/**
* If true, comments will be ignored if they are found in the code.
*
* @var boolean
*/
private $_ignoreComments = false;
/**
* Positions in the stack where errors have occured.
*
* @var array()
*/
private $_errorPos = array();
/**
* Constructs a PHP_CodeSniffer_Standards_AbstractPatternSniff.
*
* @param boolean $ignoreComments If true, comments will be ignored.
*/
public function __construct($ignoreComments=false)
{
$this->_ignoreComments = $ignoreComments;
$this->_supplementaryTokens = $this->registerSupplementary();
}//end __construct()
/**
* Registers the tokens to listen to.
*
* Classes extending <i>AbstractPatternTest</i> should implement the
* <i>getPatterns()</i> method to register the patterns they wish to test.
*
* @return array(int)
* @see process()
*/
public final function register()
{
$listenTypes = array();
$patterns = $this->getPatterns();
foreach ($patterns as $pattern) {
$parsedPattern = $this->_parse($pattern);
// Find a token position in the pattern that we can use for a listener
// token.
$pos = $this->_getListenerTokenPos($parsedPattern);
$tokenType = $parsedPattern[$pos]['token'];
$listenTypes[] = $tokenType;
$patternArray = array(
'listen_pos' => $pos,
'pattern' => $parsedPattern,
'pattern_code' => $pattern,
);
if (isset($this->_parsedPatterns[$tokenType]) === false) {
$this->_parsedPatterns[$tokenType] = array();
}
$this->_parsedPatterns[$tokenType][] = $patternArray;
}//end foreach
return array_unique(array_merge($listenTypes, $this->_supplementaryTokens));
}//end register()
/**
* Returns the token types that the specified pattern is checking for.
*
* Returned array is in the format:
* <code>
* array(
* T_WHITESPACE => 0, // 0 is the position where the T_WHITESPACE token
* // should occur in the pattern.
* );
* </code>
*
* @param array $pattern The parsed pattern to find the acquire the token
* types from.
*
* @return array(int => int)
*/
private function _getPatternTokenTypes($pattern)
{
$tokenTypes = array();
foreach ($pattern as $pos => $patternInfo) {
if ($patternInfo['type'] === 'token') {
if (isset($tokenTypes[$patternInfo['token']]) === false) {
$tokenTypes[$patternInfo['token']] = $pos;
}
}
}
return $tokenTypes;
}//end _getPatternTokenTypes()
/**
* Returns the position in the pattern that this test should register as
* a listener for the pattern.
*
* @param array $pattern The pattern to acquire the listener for.
*
* @return int The postition in the pattern that this test should register
* as the listener.
* @throws PHP_CodeSniffer_Exception If we could not determine a token
* to listen for.
*/
private function _getListenerTokenPos($pattern)
{
$tokenTypes = $this->_getPatternTokenTypes($pattern);
$tokenCodes = array_keys($tokenTypes);
$token = PHP_CodeSniffer_Tokens::getHighestWeightedToken($tokenCodes);
// If we could not get a token.
if ($token === false) {
$error = 'Could not determine a token to listen for';
throw new PHP_CodeSniffer_Exception($error);
}
return $tokenTypes[$token];
}//end _getListenerTokenPos()
/**
* Processes the test.
*
* @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where the
* token occured.
* @param int $stackPtr The postion in the tokens stack
* where the listening token type was
* found.
*
* @return void
* @see register()
*/
public final function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if (in_array($tokens[$stackPtr]['code'], $this->_supplementaryTokens) === true) {
$this->processSupplementary($phpcsFile, $stackPtr);
}
$type = $tokens[$stackPtr]['code'];
// If the type is not set, then it must have been a token registered
// with registerSupplementary().
if (isset($this->_parsedPatterns[$type]) === false) {
return;
}
$allErrors = array();
// Loop over each pattern that is listening to the current token type
// that we are processing.
foreach ($this->_parsedPatterns[$type] as $patternInfo) {
// If processPattern returns false, then the pattern that we are
// checking the code with must not be design to check that code.
$errors = $this->processPattern($patternInfo, $phpcsFile, $stackPtr);
if ($errors === false) {
// The pattern didn't match.
continue;
} else if (empty($errors) === true) {
// The pattern matched, but there were no errors.
break;
}
foreach ($errors as $stackPtr => $error) {
if (isset($this->_errorPos[$stackPtr]) === false) {
$this->_errorPos[$stackPtr] = true;
$allErrors[$stackPtr] = $error;
}
}
}
foreach ($allErrors as $stackPtr => $error) {
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
/**
* Processes the pattern and verifies the code at $stackPtr.
*
* @param array $patternInfo Information about the pattern used
* for checking, which includes are
* parsed token representation of the
* pattern.
* @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where the
* token occured.
* @param int $stackPtr The postion in the tokens stack where
* the listening token type was found.
*
* @return array(errors)
*/
protected function processPattern($patternInfo, PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$pattern = $patternInfo['pattern'];
$patternCode = $patternInfo['pattern_code'];
$errors = array();
$found = '';
$ignoreTokens = array(T_WHITESPACE);
if ($this->_ignoreComments === true) {
$ignoreTokens = array_merge($ignoreTokens, PHP_CodeSniffer_Tokens::$commentTokens);
}
$origStackPtr = $stackPtr;
$hasError = false;
if ($patternInfo['listen_pos'] > 0) {
$stackPtr--;
for ($i = ($patternInfo['listen_pos'] - 1); $i >= 0; $i--) {
if ($pattern[$i]['type'] === 'token') {
if ($pattern[$i]['token'] === T_WHITESPACE) {
if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
$found = $tokens[$stackPtr]['content'].$found;
}
// Only check the size of the whitespace if this is not
// not the first token. We don't care about the size of
// leading whitespace, just that there is some.
if ($i !== 0) {
if ($tokens[$stackPtr]['content'] !== $pattern[$i]['value']) {
$hasError = true;
}
}
} else {
// Check to see if this important token is the same as the
// previous important token in the pattern. If it is not,
// then the pattern cannot be for this piece of code.
$prev = $phpcsFile->findPrevious($ignoreTokens, $stackPtr, null, true);
if ($prev === false || $tokens[$prev]['code'] !== $pattern[$i]['token']) {
return false;
}
// If we skipped past some whitespace tokens, then add them
// to the found string.
if (($stackPtr - $prev) > 1) {
for ($j = ($stackPtr - 1); $j > $prev; $j--) {
$found = $tokens[$j]['content'].$found;
}
}
$found = $tokens[$prev]['content'].$found;
if (isset($pattern[($i - 1)]) === true && $pattern[($i - 1)]['type'] === 'skip') {
$stackPtr = $prev;
} else {
$stackPtr = ($prev - 1);
}
}//end if
} else if ($pattern[$i]['type'] === 'skip') {
// Skip to next piece of relevant code.
if ($pattern[$i]['to'] === 'parenthesis_closer') {
$to = 'parenthesis_opener';
} else {
$to = 'scope_opener';
}
// Find the previous opener.
$next = $phpcsFile->findPrevious($ignoreTokens, $stackPtr, null, true);
if ($next === false || isset($tokens[$next][$to]) === false) {
// If there was not opener, then we must be
// using the wrong pattern.
return false;
}
if ($to === 'parenthesis_opener') {
$found = '{'.$found;
} else {
$found = '('.$found;
}
$found = '...'.$found;
// Skip to the opening token.
$stackPtr = ($tokens[$next][$to] - 1);
} else if ($pattern[$i]['type'] === 'string') {
$found = 'abc';
} else if ($pattern[$i]['type'] === 'newline') {
$found = 'EOL';
}//end if
}//end for
}//end if
$stackPtr = $origStackPtr;
$lastAddedStackPtr = null;
$patternLen = count($pattern);
for ($i = $patternInfo['listen_pos']; $i < $patternLen; $i++) {
if ($pattern[$i]['type'] === 'token') {
if ($pattern[$i]['token'] === T_WHITESPACE) {
if ($this->_ignoreComments === true) {
// If we are ignoring comments, check to see if this current
// token is a comment. If so skip it.
if (in_array($tokens[$stackPtr]['code'], PHP_CodeSniffer_Tokens::$commentTokens) === true) {
continue;
}
// If the next token is a comment, the we need to skip the
// current token as we should allow a space before a
// comment for readability.
if (in_array($tokens[($stackPtr + 1)]['code'], PHP_CodeSniffer_Tokens::$commentTokens) === true) {
continue;
}
}
$tokenContent = '';
if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
if (isset($pattern[($i + 1)]) === false) {
// This is the last token in the pattern, so just compare
// the next token of content.
$tokenContent = $tokens[$stackPtr]['content'];
} else {
// Get all the whitespace to the next token.
$next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr, null, true);
$tokenContent = $phpcsFile->getTokensAsString($stackPtr, ($next - $stackPtr));
$lastAddedStackPtr = $stackPtr;
$stackPtr = $next;
}
if ($stackPtr !== $lastAddedStackPtr) {
$found .= $tokenContent;
}
} else {
if ($stackPtr !== $lastAddedStackPtr) {
$found .= $tokens[$stackPtr]['content'];
$lastAddedStackPtr = $stackPtr;
}
}//end if
if (isset($pattern[($i + 1)]) === true && $pattern[($i + 1)]['type'] === 'skip') {
// The next token is a skip token, so we just need to make
// sure the whitespace we found has *at least* the
// whitespace required.
if (strpos($tokenContent, $pattern[$i]['value']) !== 0) {
$hasError = true;
}
} else {
if ($tokenContent !== $pattern[$i]['value']) {
$hasError = true;
}
}
} else {
// Check to see if this important token is the same as the
// next important token in the pattern. If it is not, then
// the pattern cannot be for this piece of code.
$next = $phpcsFile->findNext($ignoreTokens, $stackPtr, null, true);
if ($next === false || $tokens[$next]['code'] !== $pattern[$i]['token']) {
return false;
}
// If we skipped past some whitespace tokens, then add them
// to the found string.
if (($next - $stackPtr) > 0) {
$hasComment = false;
for ($j = $stackPtr; $j < $next; $j++) {
$found .= $tokens[$j]['content'];
if (in_array($tokens[$j]['code'], PHP_CodeSniffer_Tokens::$commentTokens) === true) {
$hasComment = true;
}
}
// If we are not ignoring comments, this additional
// whitespace or comment is not allowed. If we are
// ignoring comments, there needs to be at least one
// comment for this to be allowed.
if ($this->_ignoreComments === false || ($this->_ignoreComments === true && $hasComment === false)) {
$hasError = true;
}
// Even when ignoring comments, we are not allowed to include
// newlines without the pattern specifying them, so
// everything should be on the same line.
if ($tokens[$next]['line'] !== $tokens[$stackPtr]['line']) {
$hasError = true;
}
}//end if
if ($next !== $lastAddedStackPtr) {
$found .= $tokens[$next]['content'];
$lastAddedStackPtr = $next;
}
if (isset($pattern[($i + 1)]) === true && $pattern[($i + 1)]['type'] === 'skip') {
$stackPtr = $next;
} else {
$stackPtr = ($next + 1);
}
}//end if
} else if ($pattern[$i]['type'] === 'skip') {
// Find the previous opener.
$next = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$blockOpeners, $stackPtr, null);
if ($next === false || isset($tokens[$next][$pattern[$i]['to']]) === false) {
// If there was not opener, then we must
// be using the wrong pattern.
return false;
}
$found .= '...';
if ($pattern[$i]['to'] === 'parenthesis_closer') {
$found .= ')';
} else {
$found .= '}';
}
// Skip to the closing token.
$stackPtr = ($tokens[$next][$pattern[$i]['to']] + 1);
} else if ($pattern[$i]['type'] === 'string') {
if ($tokens[$stackPtr]['code'] !== T_STRING) {
$hasError = true;
}
if ($stackPtr !== $lastAddedStackPtr) {
$found .= 'abc';
$lastAddedStackPtr = $stackPtr;
}
$stackPtr++;
} else if ($pattern[$i]['type'] === 'newline') {
// Find the next token that contains a newline character.
$newline = 0;
for ($j = $stackPtr; $j < $phpcsFile->numTokens; $j++) {
if (strpos($tokens[$j]['content'], $phpcsFile->eolChar) !== false) {
$newline = $j;
break;
}
}
if ($newline === 0) {
// We didn't find a newline character in the rest of the file.
$next = ($phpcsFile->numTokens - 1);
$hasError = true;
} else {
if ($this->_ignoreComments === false) {
// The newline character cannot be part of a comment.
if (in_array($tokens[$newline]['code'], PHP_CodeSniffer_Tokens::$commentTokens) === true) {
$hasError = true;
}
}
if ($newline === $stackPtr) {
$next = ($stackPtr + 1);
} else {
// Check that there were no significant tokens that we
// skipped over to find our newline character.
$next = $phpcsFile->findNext($ignoreTokens, $stackPtr, null, true);
if ($next < $newline) {
// We skipped a non-ignored token.
$hasError = true;
} else {
$next = ($newline + 1);
}
}
}//end if
if ($stackPtr !== $lastAddedStackPtr) {
$found .= $phpcsFile->getTokensAsString($stackPtr, ($next - $stackPtr));
$diff = ($next - $stackPtr);
$lastAddedStackPtr = ($next - 1);
}
$stackPtr = $next;
}//end if
}//end for
if ($hasError === true) {
$error = $this->prepareError($found, $patternCode);
$errors[$origStackPtr] = $error;
}
return $errors;
}//end processPattern()
/**
* Prepares an error for the specified patternCode.
*
* @param string $found The actual found string in the code.
* @param string $patternCode The expected pattern code.
*
* @return string The error message.
*/
protected function prepareError($found, $patternCode)
{
$found = str_replace("\r\n", '\n', $found);
$found = str_replace("\n", '\n', $found);
$found = str_replace("\r", '\n', $found);
$found = str_replace('EOL', '\n', $found);
$expected = str_replace('EOL', '\n', $patternCode);
$error = "Expected \"$expected\"; found \"$found\"";
return $error;
}//end prepareError()
/**
* Returns the patterns that should be checked.
*
* @return array(string)
*/
protected abstract function getPatterns();
/**
* Registers any supplementary tokens that this test might wish to process.
*
* A sniff may wish to register supplementary tests when it wishes to group
* an arbitary validation that cannot be performed using a pattern, with
* other pattern tests.
*
* @return array(int)
* @see processSupplementary()
*/
protected function registerSupplementary()
{
return array();
}//end registerSupplementary()
/**
* Processes any tokens registered with registerSupplementary().
*
* @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where to
* process the skip.
* @param int $stackPtr The position in the tokens stack to
* process.
*
* @return void
* @see registerSupplementary()
*/
protected function processSupplementary(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
return;
}//end processSupplementary()
/**
* Parses a pattern string into an array of pattern steps.
*
* @param string $pattern The pattern to parse.
*
* @return array The parsed pattern array.
* @see _createSkipPattern()
* @see _createTokenPattern()
*/
private function _parse($pattern)
{
$patterns = array();
$length = strlen($pattern);
$lastToken = 0;
$firstToken = 0;
for ($i = 0; $i < $length; $i++) {
$specialPattern = false;
$isLastChar = ($i === ($length - 1));
$oldFirstToken = $firstToken;
if (substr($pattern, $i, 3) === '...') {
// It's a skip pattern. The skip pattern requires the
// content of the token in the "from" position and the token
// to skip to.
$specialPattern = $this->_createSkipPattern($pattern, ($i - 1));
$lastToken = ($i - $firstToken);
$firstToken = ($i + 4);
$i = ($i + 3);
} else if (substr($pattern, $i, 3) === 'abc') {
$specialPattern = array('type' => 'string');
$lastToken = ($i - $firstToken);
$firstToken = ($i + 3);
$i = ($i + 2);
} else if (substr($pattern, $i, 3) === 'EOL') {
$specialPattern = array('type' => 'newline');
$lastToken = ($i - $firstToken);
$firstToken = ($i + 3);
$i = ($i + 2);
}
if ($specialPattern !== false || $isLastChar === true) {
// If we are at the end of the string, don't worry about a limit.
if ($isLastChar === true) {
// Get the string from the end of the last skip pattern, if any,
// to the end of the pattern string.
$str = substr($pattern, $oldFirstToken);
} else {
// Get the string from the end of the last special pattern,
// if any, to the start of this special pattern.
$str = substr($pattern, $oldFirstToken, $lastToken);
}
$tokenPatterns = $this->_createTokenPattern($str);
// Make sure we don't skip the last token.
if ($isLastChar === false && $i === ($length - 1)) {
$i--;
}
foreach ($tokenPatterns as $tokenPattern) {
$patterns[] = $tokenPattern;
}
}//end if
// Add the skip pattern *after* we have processed
// all the tokens from the end of the last skip pattern
// to the start of this skip pattern.
if ($specialPattern !== false) {
$patterns[] = $specialPattern;
}
}//end for
return $patterns;
}//end _parse()
/**
* Creates a skip pattern.
*
* @param string $pattern The pattern being parsed.
* @param string $from The token content that the skip pattern starts from.
*
* @return array The pattern step.
* @see _createTokenPattern()
* @see _parse()
*/
private function _createSkipPattern($pattern, $from)
{
$skip = array('type' => 'skip');
for ($from; $from >= 0; $from--) {
switch ($pattern[$from]) {
case '(':
$skip['to'] = 'parenthesis_closer';
break;
case '{':
$skip['to'] = 'scope_closer';
break;
}
if (isset($skip['to']) === true) {
break;
}
}
if (isset($skip['to']) === false) {
$skip['to'] = 'unknown';
}
return $skip;
}//end _createSkipPattern()
/**
* Creates a token pattern.
*
* @param string $str The tokens string that the pattern should match.
*
* @return array The pattern step.
* @see _createSkipPattern()
* @see _parse()
*/
private function _createTokenPattern($str)
{
// Don't add a space after the closing php tag as it will add a new
// whitespace token.
$tokens = token_get_all('<?php '.$str.'?>');
// Remove the <?php tag from the front and the end php tag from the back.
$tokens = array_slice($tokens, 1, (count($tokens) - 2));
foreach ($tokens as &$token) {
$token = PHP_CodeSniffer::standardiseToken($token);
}
$patterns = array();
foreach ($tokens as $patternInfo) {
$patterns[] = array(
'type' => 'token',
'token' => $patternInfo['code'],
'value' => $patternInfo['content'],
);
}
return $patterns;
}//end _createTokenPattern()
}//end class
?>

View file

@ -0,0 +1,213 @@
<?php
/**
* An AbstractScopeTest allows for tests that extend from this class to
* listen for tokens within a particluar scope.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* An AbstractScopeTest allows for tests that extend from this class to
* listen for tokens within a particluar scope.
*
* Below is a test that listens to methods that exist only within classes:
* <code>
* class ClassScopeTest extends PHP_CodeSniffer_Standards_AbstractScopeSniff
* {
* public function __construct()
* {
* parent::__construct(array(T_CLASS), array(T_FUNCTION));
* }
*
* protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $)
* {
* $className = $phpcsFile->getDeclarationName($currScope);
* echo 'encountered a method within class '.$className;
* }
* }
* </code>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
abstract class PHP_CodeSniffer_Standards_AbstractScopeSniff implements PHP_CodeSniffer_Sniff
{
/**
* The token types that this test wishes to listen to within the scope.
*
* @var array()
*/
private $_tokens = array();
/**
* The type of scope opener tokens that this test wishes to listen to.
*
* @var string
*/
private $_scopeTokens = array();
/**
* The position in the tokens array that opened the current scope.
*
* @var array()
*/
protected $currScope = null;
/**
* True if this test should fire on tokens outside of the scope.
*
* @var boolean
*/
private $_listenOutside = false;
/**
* Constructs a new AbstractScopeTest.
*
* @param array $scopeTokens The type of scope the test wishes to listen to.
* @param array $tokens The tokens that the test wishes to listen to
* within the scope.
* @param boolean $listenOutside If true this test will also alert the
* extending class when a token is found outside
* the scope, by calling the
* processTokenOutideScope method.
*
* @see PHP_CodeSniffer.getValidScopeTokeners()
* @throws PHP_CodeSniffer_Test_Exception If the specified tokens array is empty.
*/
public function __construct(array $scopeTokens, array $tokens, $listenOutside=false)
{
if (empty($scopeTokens) === true) {
$error = 'The scope tokens list cannot be empty';
throw new PHP_CodeSniffer_Test_Exception($error);
}
if (empty($tokens) === true) {
$error = 'The tokens list cannot be empty';
throw new PHP_CodeSniffer_Test_Exception($error);
}
$invalidScopeTokens = array_intersect($scopeTokens, $tokens);
if (empty($invalidScopeTokens) === false) {
$invalid = implode(', ', $invalidScopeTokens);
$error = "Scope tokens [$invalid] cant be in the tokens array";
throw new PHP_CodeSniffer_Test_Exception($error);
}
$this->_listenOutside = $listenOutside;
$this->_scopeTokens = $scopeTokens;
$this->_tokens = $tokens;
}//end __construct()
/**
* The method that is called to register the tokens this test wishes to
* listen to.
*
* DO NOT OVERRIDE THIS METHOD. Use the constructor of this class to register
* for the desired tokens and scope.
*
* @return array(int)
* @see __constructor()
*/
public final function register()
{
if ($this->_listenOutside === false) {
return $this->_scopeTokens;
} else {
return array_merge($this->_scopeTokens, $this->_tokens);
}
}//end register()
/**
* Processes the tokens that this test is listening for.
*
* @param PHP_CodeSniffer_File $phpcsFile The file where this token was found.
* @param int $stackPtr The position in the stack where this
* token was found.
*
* @return void
* @see processTokenWithinScope()
*/
public final function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if (in_array($tokens[$stackPtr]['code'], $this->_scopeTokens) === true) {
$this->currScope = $stackPtr;
$phpcsFile->addTokenListener($this, $this->_tokens);
} else if ($this->currScope !== null && isset($tokens[$this->currScope]['scope_closer']) === true && $stackPtr > $tokens[$this->currScope]['scope_closer']) {
$this->currScope = null;
if ($this->_listenOutside === true) {
// This is a token outside the current scope, so notify the
// extender as they wish to know about this.
$this->processTokenOutsideScope($phpcsFile, $stackPtr);
} else {
// Don't remove the listener if the extender wants to know about
// tokens that live outside the current scope.
$phpcsFile->removeTokenListener($this, $this->_tokens);
}
} else if ($this->currScope !== null) {
$this->processTokenWithinScope($phpcsFile, $stackPtr, $this->currScope);
} else {
$this->processTokenOutsideScope($phpcsFile, $stackPtr);
}
}//end process()
/**
* Processes a token that is found within the scope that this test is
* listening to.
*
* @param PHP_CodeSniffer_File $phpcsFile The file where this token was found.
* @param int $stackPtr The position in the stack where this
* token was found.
* @param int $currScope The position in the tokens array that
* opened the scope that this test is
* listening for.
*
* @return void
*/
protected abstract function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope);
/**
* Processes a token that is found within the scope that this test is
* listening to.
*
* @param PHP_CodeSniffer_File $phpcsFile The file where this token was found.
* @param int $stackPtr The position in the stack where this
* token was found.
*
* @return void
*/
protected function processTokenOutsideScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
return;
}//end processTokenOutsideScope()
}//end class
?>

View file

@ -0,0 +1,217 @@
<?php
/**
* A class to find T_VARIABLE tokens.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_Standards_AbstractScopeSniff', true) === false) {
$error = 'Class PHP_CodeSniffer_Standards_AbstractScopeSniff not found';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* A class to find T_VARIABLE tokens.
*
* This class can distingush between normal T_VARIABLE tokens, and those tokens
* that represent class members. If a class member is encountered, then then
* processMemberVar method is called so the extending class can process it. If
* the token is found to be a normal T_VARIABLE token, then processVariable is
* called.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
abstract class PHP_CodeSniffer_Standards_AbstractVariableSniff extends PHP_CodeSniffer_Standards_AbstractScopeSniff
{
/**
* The end token of the current function that we are in.
*
* @var int
*/
private $_endFunction = -1;
/**
* true if a function is currently open.
*
* @var boolean
*/
private $_functionOpen = false;
/**
* The current PHP_CodeSniffer file that we are processing.
*
* @var PHP_CodeSniffer_File
*/
protected $currentFile = null;
/**
* Constructs an AbstractVariableTest.
*/
public function __construct()
{
$listen = array(
T_CLASS,
T_INTERFACE,
);
$scopes = array(
T_FUNCTION,
T_VARIABLE,
T_DOUBLE_QUOTED_STRING,
);
parent::__construct($listen, $scopes, true);
}//end __construct()
/**
* Processes the token in the specified PHP_CodeSniffer_File.
*
* @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the token was found.
* @param array $currScope The current scope opener token.
*
* @return void
*/
protected final function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope)
{
if ($this->currentFile !== $phpcsFile) {
$this->currentFile = $phpcsFile;
$this->_functionOpen = false;
$this->_endFunction = -1;
}
$tokens = $phpcsFile->getTokens();
if ($stackPtr > $this->_endFunction) {
$this->_functionOpen = false;
}
if ($tokens[$stackPtr]['code'] === T_FUNCTION && $this->_functionOpen === false) {
$this->_functionOpen = true;
$methodProps = $phpcsFile->getMethodProperties($stackPtr);
// If the function is abstract, or is in an interface,
// then set the end of the function to it's closing semicolon.
if ($methodProps['is_abstract'] === true || $tokens[$currScope]['code'] === T_INTERFACE) {
$this->_endFunction = $phpcsFile->findNext(array(T_SEMICOLON), $stackPtr);
} else {
if (isset($tokens[$stackPtr]['scope_closer']) === false) {
$error = 'Possible parse error: non-abstract method defined as abstract';
$phpcsFile->addWarning($error, $stackPtr);
return;
}
$this->_endFunction = $tokens[$stackPtr]['scope_closer'];
}
}
if ($this->_functionOpen === true) {
if ($tokens[$stackPtr]['code'] === T_VARIABLE) {
$this->processVariable($phpcsFile, $stackPtr);
} else if ($tokens[$stackPtr]['code'] === T_DOUBLE_QUOTED_STRING) {
// Check to see if this string has a variable in it.
$pattern = '|[^\\\]\$[a-zA-Z0-9_]+|';
if (preg_match($pattern, $tokens[$stackPtr]['content']) !== 0) {
$this->processVariableInString($phpcsFile, $stackPtr);
}
}
return;
} else {
// What if we assign a member variable to another?
// ie. private $_count = $this->_otherCount + 1;.
$this->processMemberVar($phpcsFile, $stackPtr);
}
}//end processTokenWithinScope()
/**
* Processes the token outside the scope in the file.
*
* @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
protected final function processTokenOutsideScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// These variables are not member vars.
if ($tokens[$stackPtr]['code'] === T_VARIABLE) {
$this->processVariable($phpcsFile, $stackPtr);
} else {
$this->processVariableInString($phpcsFile, $stackPtr);
}
}//end processTokenOutsideScope()
/**
* Called to process class member vars.
*
* @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
abstract protected function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr);
/**
* Called to process normal member vars.
*
* @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
abstract protected function processVariable(PHP_CodeSniffer_File $phpcsFile, $stackPtr);
/**
* Called to process variables found in duoble quoted strings.
*
* Note that there may be more than one variable in the string, which will
* result only in one call for the string.
*
* @param PHP_CodeSniffer_File $phpcsFile The PHP_CodeSniffer file where this
* token was found.
* @param int $stackPtr The position where the double quoted
* string was found.
*
* @return void
*/
abstract protected function processVariableInString(PHP_CodeSniffer_File $phpcsFile, $stackPtr);
}//end class
?>

View file

@ -0,0 +1,73 @@
<?php
/**
* Bass Coding Standard class.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Base Coding Standard class.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_Standards_CodingStandard
{
/**
* Return a list of external sniffs to include with this standard.
*
* External locations can be single sniffs, a whole directory of sniffs, or
* an entire coding standard. Locations start with the standard name. For
* example:
* PEAR => include all sniffs in this standard
* PEAR/Sniffs/Files => include all sniffs in this dir
* PEAR/Sniffs/Files/LineLengthSniff => include this single sniff
*
* @return array
*/
public function getIncludedSniffs()
{
return array();
}//end getIncludedSniffs()
/**
* Return a list of external sniffs to exclude from this standard.
*
* External locations can be single sniffs, a whole directory of sniffs, or
* an entire coding standard. Locations start with the standard name. For
* example:
* PEAR => exclude all sniffs in this standard
* PEAR/Sniffs/Files => exclude all sniffs in this dir
* PEAR/Sniffs/Files/LineLengthSniff => exclude this single sniff
*
* @return array
*/
public function getExcludedSniffs()
{
return array();
}//end getExcludedSniffs()
}//end class
?>

View file

@ -0,0 +1,36 @@
<?php
/**
* An exception thrown if the pattern being processed is not supposed to be
* validating the code in question.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* An exception thrown if the pattern being processed is not supposed to be
* validating the code in question.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_Standards_IncorrectPatternException extends Exception
{
}//end class
?>

View file

@ -0,0 +1,38 @@
<?php
/**
* Moodle Coding Standard.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_Standards_CodingStandard', true) === false) {
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Standards_CodingStandard not found');
}
/**
* Moodle Coding Standard.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: @package_version@
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_Standards_Moodle_MoodleCodingStandard extends PHP_CodeSniffer_Standards_CodingStandard {
public function getIncludedSniffs() {
return array();
}
public function getExcludedSniffs() {
return array('Moodle/Sniffs/CodeAnalysis');
}
}//end class
?>

View file

@ -0,0 +1,100 @@
<?php
/**
* Class Declaration Test.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @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 Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_Classes_ClassDeclarationSniff 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 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 (isset($tokens[$stackPtr]['scope_opener']) === false) {
$error = 'Possible parse error: ';
$error .= $tokens[$stackPtr]['content'];
$error .= ' missing opening or closing brace';
$phpcsFile->addWarning($error, $stackPtr);
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 ';
$error .= $tokens[$stackPtr]['content'];
$error .= ' must be on the same line as the definition';
$phpcsFile->addError($error, $curlyBrace);
return;
}
if ($tokens[($curlyBrace - 1)]['code'] === T_WHITESPACE) {
$prevContent = $tokens[($curlyBrace - 1)]['content'];
if ($prevContent !== $phpcsFile->eolChar) {
$blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar));
$spaces = strlen($blankSpace);
if ($spaces !== 1) {
$error = "Expected 1 space before opening brace; $spaces found";
$phpcsFile->addError($error, $curlyBrace);
}
}
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,129 @@
<?php
/**
* This file is part of the CodeAnalysis addon for PHP_CodeSniffer.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* This sniff class detected empty statement.
*
* This sniff implements the common algorithm for empty statement body detection.
* A body is considered as empty if it is completely empty or it only contains
* whitespace characters and|or comments.
*
* <code>
* stmt {
* // foo
* }
* stmt (conditions) {
* // foo
* }
* </code>
*
* Statements covered by this sniff are <b>catch</b>, <b>do</b>, <b>else</b>,
* <b>elsif</b>, <b>for</b>, <b>foreach<b>, <b>if</b>, <b>switch</b>, <b>try</b>
* and <b>while</b>.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_CodeAnalysis_EmptyStatementSniff implements PHP_CodeSniffer_Sniff
{
/**
* List of block tokens that this sniff covers.
*
* The key of this hash identifies the required token while the boolean
* value says mark an error or mark a warning.
*
* @type array<boolean>
* @var array(integer=>boolean) $_tokens
*/
private $_tokens = array(
T_CATCH => true,
T_DO => false,
T_ELSE => false,
T_ELSEIF => false,
T_FOR => false,
T_FOREACH => false,
T_IF => false,
T_SWITCH => false,
T_TRY => false,
T_WHILE => false,
);
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return array(integer)
*/
public function register()
{
return array_keys($this->_tokens);
}//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();
$token = $tokens[$stackPtr];
// Skip for-statements without body.
if (isset($token['scope_opener']) === false) {
return;
}
$next = ++$token['scope_opener'];
$end = --$token['scope_closer'];
$emptyBody = true;
for (; $next <= $end; ++$next) {
if (in_array($tokens[$next]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
$emptyBody = false;
break;
}
}
if ($emptyBody === true) {
// Get token identifier.
$name = $phpcsFile->getTokensAsString($stackPtr, 1);
$error = sprintf('Empty %s statement detected', strtoupper($name));
if ($this->_tokens[$token['code']] === true) {
$phpcsFile->addError($error, $stackPtr);
} else {
$phpcsFile->addWarning($error, $stackPtr);
}
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,101 @@
<?php
/**
* This file is part of the CodeAnalysis addon for PHP_CodeSniffer.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Detects for-loops that can be simplified to a while-loop.
*
* This rule is based on the PMD rule catalog. Detects for-loops that can be
* simplified as a while-loop.
*
* <code>
* class Foo
* {
* public function bar($x)
* {
* for (;true;) true; // No Init or Update part, may as well be: while (true)
* }
* }
* </code>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_CodeAnalysis_ForLoopShouldBeWhileLoopSniff implements PHP_CodeSniffer_Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return array(integer)
*/
public function register()
{
return array(T_FOR);
}//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();
$token = $tokens[$stackPtr];
// Skip invalid statement.
if (isset($token['parenthesis_opener']) === false) {
return;
}
$next = ++$token['parenthesis_opener'];
$end = --$token['parenthesis_closer'];
$parts = array(0, 0, 0);
$index = 0;
for (; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
if ($code === T_SEMICOLON) {
++$index;
} else if (in_array($code, PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
++$parts[$index];
}
}
if ($parts[0] === 0 && $parts[2] === 0 && $parts[1] > 0) {
$error = 'This FOR loop can be simplified to a WHILE loop';
$phpcsFile->addWarning($error, $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,114 @@
<?php
/**
* This file is part of the CodeAnalysis addon for PHP_CodeSniffer.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Detects for-loops that use a function call in the test expression.
*
* This rule is based on the PMD rule catalog. Detects for-loops that use a
* function call in the test expression.
*
* <code>
* class Foo
* {
* public function bar($x)
* {
* $a = array(1, 2, 3, 4);
* for ($i = 0; $i < count($a); $i++) {
* $a[$i] *= $i;
* }
* }
* }
* </code>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_CodeAnalysis_ForLoopWithTestFunctionCallSniff implements PHP_CodeSniffer_Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return array(integer)
*/
public function register()
{
return array(T_FOR);
}//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();
$token = $tokens[$stackPtr];
// Skip invalid statement.
if (isset($token['parenthesis_opener']) === false) {
return;
}
$next = ++$token['parenthesis_opener'];
$end = --$token['parenthesis_closer'];
$position = 0;
for (; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
if ($code === T_SEMICOLON) {
++$position;
}
if ($position < 1) {
continue;
} else if ($position > 1) {
break;
} else if ($code !== T_VARIABLE && $code !== T_STRING) {
continue;
}
// Find next non empty token, if it is a open curly brace we have a
// function call.
$index = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true);
if ($tokens[$index]['code'] === T_OPEN_PARENTHESIS) {
$error = 'Avoid function calls in a FOR loop test part';
$phpcsFile->addWarning($error, $stackPtr);
break;
}
}//end for
}//end process()
}//end class
?>

View file

@ -0,0 +1,148 @@
<?php
/**
* This file is part of the CodeAnalysis addon for PHP_CodeSniffer.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Detects incrementer jumbling in for loops.
*
* This rule is based on the PMD rule catalog. The jumbling incrementer sniff
* detects the usage of one and the same incrementer into an outer and an inner
* loop. Even it is intended this is confusing code.
*
* <code>
* class Foo
* {
* public function bar($x)
* {
* for ($i = 0; $i < 10; $i++)
* {
* for ($k = 0; $k < 20; $i++)
* {
* echo 'Hello';
* }
* }
* }
* }
* </code>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_CodeAnalysis_JumbledIncrementerSniff implements PHP_CodeSniffer_Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return array(integer)
*/
public function register()
{
return array(T_FOR);
}//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();
$token = $tokens[$stackPtr];
// Skip for-loop without body.
if (isset($token['scope_opener']) === false) {
return;
}
// Find incrementors for outer loop.
$outer = $this->findIncrementers($tokens, $token);
// Skip if empty.
if (count($outer) === 0) {
return;
}
// Find nested for loops.
$start = ++$token['scope_opener'];
$end = --$token['scope_closer'];
for (; $start <= $end; ++$start) {
if ($tokens[$start]['code'] !== T_FOR) {
continue;
}
$inner = $this->findIncrementers($tokens, $tokens[$start]);
$diff = array_intersect($outer, $inner);
if (count($diff) !== 0) {
$error = sprintf('Loop incrementor (%s) jumbling with inner loop', join(', ', $diff));
$phpcsFile->addWarning($error, $stackPtr);
}
}
}//end process()
/**
* Get all used variables in the incrementer part of a for statement.
*
* @param array(integer=>array) $tokens Array with all code sniffer tokens.
* @param array(string=>mixed) $token Current for loop token
*
* @return array(string) List of all found incrementer variables.
*/
protected function findIncrementers(array $tokens, array $token)
{
// Skip invalid statement.
if (isset($token['parenthesis_opener']) === false) {
return array();
}
$start = ++$token['parenthesis_opener'];
$end = --$token['parenthesis_closer'];
$incrementers = array();
$semicolons = 0;
for ($next = $start; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
if ($code === T_SEMICOLON) {
++$semicolons;
} else if ($semicolons === 2 && $code === T_VARIABLE) {
$incrementers[] = $tokens[$next]['content'];
}
}
return $incrementers;
}//end findIncrementers()
}//end class
?>

View file

@ -0,0 +1,107 @@
<?php
/**
* This file is part of the CodeAnalysis addon for PHP_CodeSniffer.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Detects unconditional if- and elseif-statements.
*
* This rule is based on the PMD rule catalog. The Unconditional If Statment
* sniff detects statement conditions that are only set to one of the constant
* values <b>true</b> or <b>false</b>
*
* <code>
* class Foo
* {
* public function close()
* {
* if (true)
* {
* // ...
* }
* }
* }
* </code>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_CodeAnalysis_UnconditionalIfStatementSniff implements PHP_CodeSniffer_Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return array(integer)
*/
public function register()
{
return array(
T_IF,
T_ELSEIF,
);
}//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();
$token = $tokens[$stackPtr];
// Skip for-loop without body.
if (isset($token['parenthesis_opener']) === false) {
return;
}
$next = ++$token['parenthesis_opener'];
$end = --$token['parenthesis_closer'];
$goodCondition = false;
for (; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
if (in_array($code, PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
continue;
} else if ($code !== T_TRUE && $code !== T_FALSE) {
$goodCondition = true;
}
}
if ($goodCondition === false) {
$error = 'Avoid IF statements that are always true or false';
$phpcsFile->addWarning($error, $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,99 @@
<?php
/**
* This file is part of the CodeAnalysis addon for PHP_CodeSniffer.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Detects unnecessary final modifiers inside of final classes.
*
* This rule is based on the PMD rule catalog. The Unnecessary Final Modifier
* sniff detects the use of the final modifier inside of a final class which
* is unnecessary.
*
* <code>
* final class Foo
* {
* public final function bar()
* {
* }
* }
* </code>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_CodeAnalysis_UnnecessaryFinalModifierSniff implements PHP_CodeSniffer_Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return array(integer)
*/
public function register()
{
return array(T_CLASS);
}//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();
$token = $tokens[$stackPtr];
// Skip for-statements without body.
if (isset($token['scope_opener']) === false) {
return;
}
// Fetch previous token.
$prev = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
// Skip for non final class.
if ($prev === false || $tokens[$prev]['code'] !== T_FINAL) {
return;
}
$next = ++$token['scope_opener'];
$end = --$token['scope_closer'];
for (; $next <= $end; ++$next) {
if ($tokens[$next]['code'] === T_FINAL) {
$error = 'Unnecessary FINAL modifier in FINAL class';
$phpcsFile->addWarning($error, $next);
}
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,141 @@
<?php
/**
* This file is part of the CodeAnalysis addon for PHP_CodeSniffer.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2009 Nicolas Connault
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Checks the for unused function parameters.
*
* This sniff checks that all function parameters are used in the function body.
* One exception is made for empty function bodies or function bodies that only
* contain comments. This could be usefull for the classes that implement an
* interface that defines multiple methods but the implementation only needs some
* of them.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_CodeAnalysis_UnusedFunctionParameterSniff 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();
$token = $tokens[$stackPtr];
// Skip broken function declarations.
if (isset($token['scope_opener']) === false || isset($token['parenthesis_opener']) === false) {
return;
}
$params = array();
foreach ($phpcsFile->getMethodParameters($stackPtr) as $param) {
$params[$param['name']] = $stackPtr;
}
$next = ++$token['scope_opener'];
$end = --$token['scope_closer'];
$emptyBody = true;
for (; $next <= $end; ++$next) {
$token = $tokens[$next];
$code = $token['code'];
// Ingorable tokens.
if (in_array($code, PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
continue;
} else if ($code === T_THROW && $emptyBody === true) {
// Throw statement and an empty body indicate an interface method.
return;
} else if ($code === T_RETURN && $emptyBody === true) {
// Return statement and an empty body indicate an interface method.
$tmp = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true);
if ($tmp === false) {
return;
}
// There is a return.
if ($tokens[$tmp] === T_SEMICOLON) {
return;
}
$tmp = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($tmp + 1), null, true);
// There is a return <token>.
if ($tmp !== false && $tokens[$tmp] === T_SEMICOLON) {
return;
}
}//end if
$emptyBody = false;
if ($code === T_VARIABLE && isset($params[$token['content']]) === true) {
unset($params[$token['content']]);
} else if ($code === T_DOUBLE_QUOTED_STRING) {
// Tokenize double quote string.
$strTokens = token_get_all(sprintf('<?php %s;?>', $token['content']));
foreach ($strTokens as $tok) {
if (is_array($tok) === false || $tok[0] !== T_VARIABLE ) {
continue;
}
if (isset($params[$tok[1]]) === true) {
unset($params[$tok[1]]);
}
}
}//end if
}//end for
if ($emptyBody === false && count($params) > 0) {
foreach ($params as $paramName => $position) {
$error = 'The method parameter '.$paramName.' is never used';
$phpcsFile->addWarning($error, $position);
}
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,182 @@
<?php
/**
* This file is part of the CodeAnalysis addon for PHP_CodeSniffer.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Detects unnecessary final modifiers inside of final classes.
*
* This rule is based on the PMD rule catalog. The Unnecessary Final Modifier
* sniff detects the use of the final modifier inside of a final class which
* is unnecessary.
*
* <code>
* final class Foo
* {
* public final function bar()
* {
* }
* }
* </code>
*
* @category PHP
* @package PHP_CodeSniffer
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2008 Manuel Pichler. All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_CodeAnalysis_UselessOverridingMethodSniff implements PHP_CodeSniffer_Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return array(integer)
*/
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();
$token = $tokens[$stackPtr];
// Skip function without body.
if (isset($token['scope_opener']) === false) {
return;
}
// Get function name.
$methodName = $phpcsFile->getDeclarationName($stackPtr);
// Get all parameters from method signature.
$signature = array();
foreach ($phpcsFile->getMethodParameters($stackPtr) as $param) {
$signature[] = $param['name'];
}
$next = ++$token['scope_opener'];
$end = --$token['scope_closer'];
for (; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
if (in_array($code, PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
continue;
} else if ($code === T_RETURN) {
continue;
}
break;
}
// Any token except 'parent' indicates correct code.
if ($tokens[$next]['code'] !== T_PARENT) {
return;
}
// Find next non empty token index, should be double colon.
$next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true);
// Skip for invalid code.
if ($next === false || $tokens[$next]['code'] !== T_DOUBLE_COLON) {
return;
}
// Find next non empty token index, should be the function name.
$next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true);
// Skip for invalid code or other method.
if ($next === false || $tokens[$next]['content'] !== $methodName) {
return;
}
// Find next non empty token index, should be the open parenthesis.
$next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true);
// Skip for invalid code.
if ($next === false || $tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
return;
}
$validParameterTypes = array(
T_VARIABLE,
T_LNUMBER,
T_CONSTANT_ENCAPSED_STRING,
);
$parameters = array('');
$parenthesisCount = 1;
$count = count($tokens);
for (++$next; $next < $count; ++$next) {
$code = $tokens[$next]['code'];
if ($code === T_OPEN_PARENTHESIS) {
++$parenthesisCount;
} else if ($code === T_CLOSE_PARENTHESIS) {
--$parenthesisCount;
} else if ($parenthesisCount === 1 && $code === T_COMMA) {
$parameters[] = '';
} else if (in_array($code, PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
$parameters[(count($parameters) - 1)] .= $tokens[$next]['content'];
}
if ($parenthesisCount === 0) {
break;
}
}//end for
$next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true);
if ($next === false || $tokens[$next]['code'] !== T_SEMICOLON) {
return;
}
// Check rest of the scope.
for (++$next; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
// Skip for any other content.
if (in_array($code, PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
return;
}
}
$parameters = array_map('trim', $parameters);
$parameters = array_filter($parameters);
if (count($parameters) === count($signature) && $parameters === $signature) {
$phpcsFile->addWarning('Useless method overriding detected', $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,224 @@
<?php
/**
* Parses and verifies the doc comments for classes.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @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('Moodle_Sniffs_Commenting_FileCommentSniff', true) === false) {
$error = 'Class Moodle_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 Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_Commenting_ClassCommentSniff extends Moodle_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)
{
// Modify array of required tags
$this->tags['package']['required'] = false;
$this->tags['copyright']['required'] = false;
$this->tags['author']['required'] = true;
$this->currentFile = $phpcsFile;
$tokens = $phpcsFile->getTokens();
$type = strtolower($tokens[$stackPtr]['content']);
$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) {
$phpcsFile->addError("You must use \"/**\" style comments for a $type comment", $stackPtr);
return;
} else if ($commentEnd === false || $tokens[$commentEnd]['code'] !== T_DOC_COMMENT) {
$phpcsFile->addError("Missing $type doc comment", $stackPtr);
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.
$phpcsFile->addError("Missing $type doc comment", ($stackPtr + 1));
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);
return;
}
$comment = $this->commentParser->getComment();
if (is_null($comment) === true) {
$error = ucfirst($type).' doc comment is empty';
$phpcsFile->addError($error, $commentStart);
return;
}
// No extra newline before short description.
$short = $comment->getShortComment();
$newlineCount = 0;
$newlineSpan = strspn($short, $phpcsFile->eolChar);
if ($short !== '' && $newlineSpan > 0) {
$line = ($newlineSpan > 1) ? 'newlines' : 'newline';
$error = "Extra $line found before $type comment short description";
$phpcsFile->addError($error, ($commentStart + 1));
}
$newlineCount = (substr_count($short, $phpcsFile->eolChar) + 1);
// Exactly one blank line between short and long description.
$long = $comment->getLongComment();
if (empty($long) === false) {
$between = $comment->getWhiteSpaceBetween();
$newlineBetween = substr_count($between, $phpcsFile->eolChar);
if ($newlineBetween !== 2) {
$error = "There must be exactly one blank line between descriptions in $type comments";
$phpcsFile->addError($error, ($commentStart + $newlineCount + 1));
}
$newlineCount += $newlineBetween;
}
// Exactly one blank line before tags.
$tags = $this->commentParser->getTagOrders();
if (count($tags) > 1) {
$newlineSpan = $comment->getNewlineAfter();
if ($newlineSpan !== 2) {
$error = "There must be exactly one blank line before the tags in $type comments";
if ($long !== '') {
$newlineCount += (substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1);
}
$phpcsFile->addError($error, ($commentStart + $newlineCount));
$short = rtrim($short, $phpcsFile->eolChar.' ');
}
}
// Check each tag.
$this->processTags($commentStart, $commentEnd);
}//end 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);
} else if ((strstr($content, 'Release:') === false)) {
$error = "Invalid version \"$content\" in doc comment; consider \"Release: <package_version>\" instead";
$this->currentFile->addWarning($error, $errorPos);
}
}
}//end processVersion()
}//end class
?>

View file

@ -0,0 +1,716 @@
<?php
/**
* Parses and verifies the doc comments for files.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @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 Nicolas Connault <nicolasconnault@gmail.com>
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_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' => false,
'allow_multiple' => false,
'order_text' => 'precedes @package',
),
'package' => array(
'required' => true,
'allow_multiple' => false,
'order_text' => 'follows @category',
),
'subpackage' => array(
'required' => false,
'allow_multiple' => false,
'order_text' => 'follows @package',
),
'author' => array(
'required' => false,
'allow_multiple' => true,
'order_text' => 'follows @subpackage (if used) or @package',
),
'copyright' => array(
'required' => true,
'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 @licence',
),
'link' => array(
'required' => false,
'allow_multiple' => true,
'order_text' => 'follows @version',
),
'see' => array(
'required' => false,
'allow_multiple' => true,
'order_text' => 'follows @link',
),
'since' => array(
'required' => false,
'allow_multiple' => false,
'order_text' => 'follows @see (if used) or @link',
),
'deprecated' => array(
'required' => false,
'allow_multiple' => false,
'order_text' => 'follows @since (if used) or @see (if used) or @link',
),
);
/**
* 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);
// Look for $Id$ and boilerplate
if ($tokens[$commentStart]['code'] != T_COMMENT) {
$phpcsFile->addError('File must begin with License boilerplate', ($stackPtr + 1));
return;
} else if (preg_match('|\$Id\$|i', $tokens[$commentStart]['content'])) {
$phpcsFile->addWarning('$Id$ tag is no longer required, please remove.', ($stackPtr + 1));
return;
}
// now look for boilerplate, must be immediately after the first line
$boilerplate = '// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.';
$nextToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
$boilerplate_lines = preg_split('/[\n\r]+/', $boilerplate);
if (rtrim($tokens[$nextToken]['content']) != $boilerplate_lines[0]) {
$phpcsFile->addError('You must include the Moodle boilerplate at the top of the file', ($nextToken));
return;
}
$boilerplate_index = 0;
foreach ($boilerplate_lines as $line) {
$nextToken = $phpcsFile->findNext(T_COMMENT, ($nextToken));
if (rtrim($tokens[$nextToken]['content']) != $boilerplate_lines[$boilerplate_index]) {
$phpcsFile->addError('Badly formatted boilerplate. Please copy-paste exactly', ($nextToken));
return;
}
$nextToken++;
$boilerplate_index++;
}
$filedocToken = $phpcsFile->findNext(T_WHITESPACE, ($nextToken + 1), null, true);
if ($tokens[$filedocToken]['code'] === T_CLOSE_TAG) {
// We are only interested if this is the first open tag.
return;
} else if ($tokens[$filedocToken]['code'] === T_COMMENT) {
$phpcsFile->addError('You must use "/**" style comments for a file comment', ($filedocToken + 1));
return;
} else if ($filedocToken === false || $tokens[$filedocToken]['code'] !== T_DOC_COMMENT) {
$phpcsFile->addError('Missing file doc comment', ($filedocToken + 1));
return;
} else {
// Extract the header comment docblock.
$commentEnd = ($phpcsFile->findNext(T_DOC_COMMENT, ($filedocToken + 1), null, true) - 1);
// Check if there is only 1 doc comment between the open tag and class token.
$nextToken = array(
T_ABSTRACT,
T_CLASS,
T_FUNCTION,
T_DOC_COMMENT,
);
$commentNext = $phpcsFile->findNext($nextToken, ($commentEnd + 1));
if ($commentNext !== false && $tokens[$commentNext]['code'] !== T_DOC_COMMENT) {
// Found a class token right after comment doc block.
$newlineToken = $phpcsFile->findNext(T_WHITESPACE, ($commentEnd + 1), $commentNext, false, $phpcsFile->eolChar);
if ($newlineToken !== false) {
$newlineToken = $phpcsFile->findNext(T_WHITESPACE, ($newlineToken + 1), $commentNext, false, $phpcsFile->eolChar);
if ($newlineToken === false) {
// No blank line between the class token and the doc block.
// The doc block is most likely a class comment.
$phpcsFile->addError('Missing file doc comment', ($stackPtr + 1));
return;
}
}
}
$comment = $phpcsFile->getTokensAsString($filedocToken, ($commentEnd - $filedocToken + 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() + $filedocToken);
$phpcsFile->addError($e->getMessage(), $line);
return;
}
$comment = $this->commentParser->getComment();
if (is_null($comment) === true) {
$error = 'File doc comment is empty';
$phpcsFile->addError($error, $filedocToken);
return;
}
// No extra newline before short description.
$short = $comment->getShortComment();
$newlineCount = 0;
$newlineSpan = strspn($short, $phpcsFile->eolChar);
if ($short !== '' && $newlineSpan > 0) {
$line = ($newlineSpan > 1) ? 'newlines' : 'newline';
$error = "Extra $line found before file comment short description";
$phpcsFile->addError($error, ($filedocToken + 1));
}
$newlineCount = (substr_count($short, $phpcsFile->eolChar) + 1);
// Exactly one blank line between short and long description.
$long = $comment->getLongComment();
if (empty($long) === false) {
$between = $comment->getWhiteSpaceBetween();
$newlineBetween = substr_count($between, $phpcsFile->eolChar);
if ($newlineBetween !== 2) {
$error = 'There must be exactly one blank line between descriptions in file comment';
$phpcsFile->addError($error, ($filedocToken + $newlineCount + 1));
}
$newlineCount += $newlineBetween;
}
// Exactly one blank line before tags.
$tags = $this->commentParser->getTagOrders();
if (count($tags) > 1) {
$newlineSpan = $comment->getNewlineAfter();
if ($newlineSpan !== 2) {
$error = 'There must be exactly one blank line before the tags in file comment';
if ($long !== '') {
$newlineCount += (substr_count($long, $phpcsFile->eolChar) - $newlineSpan + 1);
}
$phpcsFile->addError($error, ($filedocToken + $newlineCount));
$short = rtrim($short, $phpcsFile->eolChar.' ');
}
}
// Check the PHP Version.
/*
if (strstr(strtolower($long), 'php version') === false) {
$error = 'PHP version not specified';
$phpcsFile->addWarning($error, $commentEnd);
}
*/
// Check each tag.
$this->processTags($filedocToken, $commentEnd);
}//end if
}//end process()
/**
* Processes each required or optional tag.
*
* @param int $commentStart The position in the stack where the comment started.
* @param int $commentEnd The position in the stack where the comment ended.
*
* @return void
*/
protected function processTags($commentStart, $commentEnd)
{
$docBlock = (get_class($this) === 'Moodle_Sniffs_Commenting_FileCommentSniff') ? 'file' : 'class';
$foundTags = $this->commentParser->getTagOrders();
$orderIndex = 0;
$indentation = array();
$longestTag = 0;
$errorPos = 0;
foreach ($this->tags as $tag => $info) {
// Required tag missing.
if ($info['required'] === true && in_array($tag, $foundTags) === false) {
$error = "Missing @$tag tag in $docBlock comment";
$this->currentFile->addError($error, $commentEnd);
continue;
}
// Get the line number for current tag.
$tagName = ucfirst($tag);
if ($info['allow_multiple'] === true) {
$tagName .= 's';
}
$getMethod = 'get'.$tagName;
$tagElement = $this->commentParser->$getMethod();
if (is_null($tagElement) === true || empty($tagElement) === true) {
continue;
}
$errorPos = $commentStart;
if (is_array($tagElement) === false) {
$errorPos = ($commentStart + $tagElement->getLine());
}
// Get the tag order.
$foundIndexes = array_keys($foundTags, $tag);
if (count($foundIndexes) > 1) {
// Multiple occurance not allowed.
if ($info['allow_multiple'] === false) {
$error = "Only 1 @$tag tag is allowed in a $docBlock comment";
$this->currentFile->addError($error, $errorPos);
} else {
// Make sure same tags are grouped together.
$i = 0;
$count = $foundIndexes[0];
foreach ($foundIndexes as $index) {
if ($index !== $count) {
$errorPosIndex = ($errorPos + $tagElement[$i]->getLine());
$error = "@$tag tags must be grouped together";
$this->currentFile->addError($error, $errorPosIndex);
}
$i++;
$count++;
}
}
}//end if
// Check tag order.
if ($foundIndexes[0] > $orderIndex) {
$orderIndex = $foundIndexes[0];
} else {
if (is_array($tagElement) === true && empty($tagElement) === false) {
$errorPos += $tagElement[0]->getLine();
}
$orderText = $info['order_text'];
$error = "The @$tag tag is in the wrong order; the tag $orderText";
$this->currentFile->addError($error, $errorPos);
}
// Store the indentation for checking.
$len = strlen($tag);
if ($len > $longestTag) {
$longestTag = $len;
}
if (is_array($tagElement) === true) {
foreach ($tagElement as $key => $element) {
$indentation[] = array(
'tag' => $tag,
'space' => $this->getIndentation($tag, $element),
'line' => $element->getLine(),
);
}
} else {
$indentation[] = array(
'tag' => $tag,
'space' => $this->getIndentation($tag, $tagElement),
);
}
$method = 'process'.$tagName;
if (method_exists($this, $method) === true) {
// Process each tag if a method is defined.
call_user_func(array($this, $method), $errorPos);
} else {
if (is_array($tagElement) === true) {
foreach ($tagElement as $key => $element) {
$element->process($this->currentFile, $commentStart, $docBlock);
}
} else {
$tagElement->process($this->currentFile, $commentStart, $docBlock);
}
}
}//end foreach
foreach ($indentation as $indentInfo) {
if ($indentInfo['space'] !== 0 && $indentInfo['space'] !== ($longestTag + 1)) {
$expected = (($longestTag - strlen($indentInfo['tag'])) + 1);
$space = ($indentInfo['space'] - strlen($indentInfo['tag']));
$error = "@$indentInfo[tag] tag comment indented incorrectly. ";
$error .= "Expected $expected spaces but found $space.";
$getTagMethod = 'get'.ucfirst($indentInfo['tag']);
if ($this->tags[$indentInfo['tag']]['allow_multiple'] === true) {
$line = $indentInfo['line'];
} else {
$tagElem = $this->commentParser->$getTagMethod();
$line = $tagElem->getLine();
}
$this->currentFile->addError($error, ($commentStart + $line));
}
}
}//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).'_';
}
$validName = trim($newName, '_');
$error = "Category name \"$content\" is not valid; consider \"$validName\" instead";
$this->currentFile->addError($error, $errorPos);
}
} else {
$error = '@category tag must contain a name';
$this->currentFile->addError($error, $errorPos);
}
}
}//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 (!preg_match('/^[a-z\-]*$/', $content)) {
$error = "Package name \"$content\" is not valid; must be lower-case with optional hyphens.";
$this->currentFile->addError($error, $errorPos);
}
} else {
$error = '@package tag must contain a name';
$this->currentFile->addError($error, $errorPos);
}
}
}//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).'_';
}
$validName = trim($newName, '_');
$error = "Subpackage name \"$content\" is not valid; consider \"$validName\" instead";
$this->currentFile->addError($error, $errorPos);
}
} else {
$error = '@subpackage tag must contain a name';
$this->currentFile->addError($error, $errorPos);
}
}
}//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);
}
} else {
$docBlock = (get_class($this) === 'Moodle_Sniffs_Commenting_FileCommentSniff') ? 'file' : 'class';
$error = "Content missing for @author tag in $docBlock comment";
$this->currentFile->addError($error, $errorPos);
}
}
}
}//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);
}
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);
}
}
} else {
$error = '@copyright tag must contain a year and the name of the copyright holder';
$this->currentFile->addError($error, $errorPos);
}
} else {
$error = '@copyright tag must contain a year and the name of the copyright holder';
$this->currentFile->addError($error, $errorPos);
}//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);
}
if ($comment != 'GNU GPL v3 or later') {
$this->currentFile->addError('License must be "GNU GPL v3 or later", found "'.$comment.'"', $errorPos);
}
if ($value != 'http://www.gnu.org/copyleft/gpl.html') {
$this->currentFile->addError('License must be "GNU GPL v3 or later"', $errorPos);
}
}
}//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);
} else if (strstr($content, 'CVS:') === false && strstr($content, 'SVN:') === false) {
$error = "Invalid version \"$content\" in file comment; consider \"CVS: <cvs_id>\" or \"SVN: <svn_id>\" instead";
$this->currentFile->addWarning($error, $errorPos);
}
}
}//end processVersion()
}//end class
?>

View file

@ -0,0 +1,462 @@
<?php
/**
* Parses and verifies the doc comments for functions.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @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 Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_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);
return;
} else if ($code !== T_DOC_COMMENT) {
$phpcsFile->addError('Missing function doc comment', $stackPtr);
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);
return;
}
$this->_functionToken = $stackPtr;
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);
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);
return;
}
$comment = $this->commentParser->getComment();
if (is_null($comment) === true) {
$error = 'Function doc comment is empty';
$phpcsFile->addError($error, $commentStart);
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) {
$line = ($newlineSpan > 1) ? 'newlines' : 'newline';
$error = "Extra $line found before function comment short description";
$phpcsFile->addError($error, ($commentStart + 1));
}
$newlineCount = (substr_count($short, $phpcsFile->eolChar) + 1);
// Exactly one blank line between short and long description.
$long = $comment->getLongComment();
if (empty($long) === false) {
$between = $comment->getWhiteSpaceBetween();
$newlineBetween = substr_count($between, $phpcsFile->eolChar);
if ($newlineBetween !== 2) {
$error = 'There must be exactly one blank line between descriptions in function comment';
$phpcsFile->addError($error, ($commentStart + $newlineCount + 1));
}
$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));
$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);
}
}
}//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);
} 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);
}
}
}//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);
}
// 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);
}
$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);
}
$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 '.$previousName.' ('.($pos - 1).') and '.$paramName.' ('.$pos.') do not align';
$this->currentFile->addError($error, $errorPos);
}
if ($param->alignsCommentWith($previousParam) === false) {
$error = 'The comments for parameters '.$previousName.' ('.($pos - 1).') and '.$paramName.' ('.$pos.') do not align';
$this->currentFile->addError($error, $errorPos);
}
}//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 !== $param->getVarName()) {
$error = 'Doc comment var "'.$paramName;
$error .= '" does not match actual variable name "'.$realName;
$error .= '" at position '.$pos;
$this->currentFile->addError($error, $errorPos);
}
} else {
// We must have an extra parameter comment.
$error = 'Superfluous doc comment at position '.$pos;
$this->currentFile->addError($error, $errorPos);
}
if ($param->getVarName() === '') {
$error = 'Missing parameter name at position '.$pos;
$this->currentFile->addError($error, $errorPos);
}
if ($param->getType() === '') {
$error = 'Missing type at position '.$pos;
$this->currentFile->addError($error, $errorPos);
}
if ($paramComment === '') {
$error = 'Missing comment for param "'.$paramName.'" at position '.$pos;
$this->currentFile->addError($error, $errorPos);
}
$previousParam = $param;
}//end foreach
if ($spaceBeforeVar !== 1 && $spaceBeforeVar !== 10000 && $spaceBeforeComment !== 10000) {
$error = 'Expected 1 space after the longest type';
$this->currentFile->addError($error, $longestType);
}
if ($spaceBeforeComment !== 1 && $spaceBeforeComment !== 10000) {
$error = 'Expected 1 space after the longest variable name';
$this->currentFile->addError($error, $longestVar);
}
}//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 "'.$neededParam.'" missing';
$this->currentFile->addError($error, $errorPos);
}
}//end processParams()
}//end class
?>

View file

@ -0,0 +1,71 @@
<?php
/**
* PHP_CodeSniffer_Sniffs_Moodle_Commenting_InlineCommentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* PHP_CodeSniffer_Sniffs_Moodle_Commenting_InlineCommentSniff.
*
* Checks that no perl-style comments are used.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_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);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,71 @@
<?php
/**
* Verifies that control statements conform to their coding standards.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @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 Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_ControlStructures_ControlSignatureSniff extends PHP_CodeSniffer_Standards_AbstractPatternSniff
{
/**
* Constructs a Moodle_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,120 @@
<?php
/**
* Moodle_Sniffs_ControlStructures_InlineControlStructureSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_ControlStructures_InlineControlStructureSniff.
*
* Verifies that inline control statements are not present.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_ControlStructures_InlineControlStructureSniff implements PHP_CodeSniffer_Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = array(
'PHP',
'JS',
);
/**
* If true, an error will be thrown; otherwise a warning.
*
* @var bool
*/
protected $error = true;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_IF,
T_ELSE,
T_FOREACH,
T_WHILE,
T_DO,
T_SWITCH,
T_FOR,
);
}//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 (isset($tokens[$stackPtr]['scope_opener']) === false) {
// Ignore the ELSE in ELSE IF. We'll process the IF part later.
if (($tokens[$stackPtr]['code'] === T_ELSE) && ($tokens[($stackPtr + 2)]['code'] === T_IF)) {
return;
}
if ($tokens[$stackPtr]['code'] === T_WHILE) {
// This could be from a DO WHILE, which doesn't have an opening brace.
$lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) {
$brace = $tokens[$lastContent];
if (isset($brace['scope_condition']) === true) {
$condition = $tokens[$brace['scope_condition']];
if ($condition['code'] === T_DO) {
return;
}
}
}
}
// This is a control structure without an opening brace,
// so it is an inline statement.
if ($this->error === true) {
$phpcsFile->addError('Inline control structures are not allowed', $stackPtr);
} else {
$phpcsFile->addWarning('Inline control structures are discouraged', $stackPtr);
}
return;
}//end if
}//end process()
}//end class
?>

View file

@ -0,0 +1,136 @@
<?php
/**
* Moodle_Sniffs_Files_IncludingFileSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_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 Nicolas Connault <nicolasconnault@gmail.com>
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_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 = '"'.$tokens[$stackPtr]['content'].'"';
$error .= ' is a statement, not a function; ';
$error .= 'no parentheses are required';
$phpcsFile->addError($error, $stackPtr);
}
$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 its 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);
} else if ($tokenCode === T_REQUIRE) {
$error = 'File is being conditionally included; ';
$error .= 'use "include" instead';
$phpcsFile->addError($error, $stackPtr);
}
} 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);
} else if ($tokenCode === T_INCLUDE) {
$error = 'File is being unconditionally included; ';
$error .= 'use "require" instead';
$phpcsFile->addError($error, $stackPtr);
}
}//end if
}//end process()
}//end class
?>

View file

@ -0,0 +1,88 @@
<?php
/**
* Moodle_Sniffs_Files_LineEndingsSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_Files_LineEndingsSniff.
*
* Checks that end of line characters are correct.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_Files_LineEndingsSniff implements PHP_CodeSniffer_Sniff
{
/**
* The valid EOL character.
*
* @var string
*/
protected $eolChar = "\n";
/**
* 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 sniff, 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)
{
// We are only interested if this is the first open tag.
if ($stackPtr !== 0) {
if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
return;
}
}
if ($phpcsFile->eolChar !== $this->eolChar) {
$expected = $this->eolChar;
$expected = str_replace("\n", '\n', $expected);
$expected = str_replace("\r", '\r', $expected);
$found = $phpcsFile->eolChar;
$found = str_replace("\n", '\n', $found);
$found = str_replace("\r", '\r', $found);
$error = "End of line character is invalid; expected \"$expected\" but found \"$found\"";
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,134 @@
<?php
/**
* Moodle_Sniffs_Files_LineLengthSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_Files_LineLengthSniff.
*
* Checks all lines in the file, and throws warnings if they are over 80
* characters in length and errors if they are over 100. Both these
* figures can be changed by extending this sniff in your own standard.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_Files_LineLengthSniff implements PHP_CodeSniffer_Sniff
{
/**
* The limit that the length of a line should not exceed.
*
* @var int
*/
protected $lineLimit = 80;
/**
* The limit that the length of a line must not exceed.
*
* Set to zero (0) to disable.
*
* @var int
*/
protected $absoluteLineLimit = 120;
/**
* 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)
{
$tokens = $phpcsFile->getTokens();
// Make sure this is the first open tag.
$previousOpenTag = $phpcsFile->findPrevious(array(T_OPEN_TAG), ($stackPtr - 1));
if ($previousOpenTag !== false) {
return;
}
$tokenCount = 0;
$currentLineContent = '';
$currentLine = 1;
for (; $tokenCount < $phpcsFile->numTokens; $tokenCount++) {
if ($tokens[$tokenCount]['line'] === $currentLine) {
$currentLineContent .= $tokens[$tokenCount]['content'];
} else {
$currentLineContent = trim($currentLineContent, $phpcsFile->eolChar);
$this->checkLineLength($phpcsFile, ($tokenCount - 1), $currentLineContent);
$currentLineContent = $tokens[$tokenCount]['content'];
$currentLine++;
}
}
$this->checkLineLength($phpcsFile, ($tokenCount - 1), $currentLineContent);
}//end process()
/**
* Checks if a line is too long.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param int $stackPtr The token at the end of the line.
* @param string $lineContent The content of the line.
*
* @return void
*/
protected function checkLineLength(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $lineContent)
{
// If the content is a CVS or SVN id in a version tag, or it is
// a license tag with a name and URL, there is nothing the
// developer can do to shorten the line, so don't throw errors.
if (preg_match('|@version[^\$]+\$Id|', $lineContent) === 0 && preg_match('|@license|', $lineContent) === 0) {
$lineLength = strlen($lineContent);
if ($this->absoluteLineLimit > 0 && $lineLength > $this->absoluteLineLimit) {
$error = 'Line exceeds maximum limit of '.$this->absoluteLineLimit." characters; contains $lineLength characters";
$phpcsFile->addError($error, $stackPtr);
} else if ($lineLength > $this->lineLimit) {
$warning = 'Line exceeds '.$this->lineLimit." characters; contains $lineLength characters";
$phpcsFile->addWarning($warning, $stackPtr);
}
}
}//end checkLineLength()
}//end class
?>

View file

@ -0,0 +1,76 @@
<?php
/**
* Moodle_Sniffs_Formatting_SpaceAfterCastSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_Formatting_SpaceAfterCastSniff.
*
* Ensures there is a single space after cast tokens.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_Formatting_SpaceAfterCastSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return PHP_CodeSniffer_Tokens::$castTokens;
}//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 + 1)]['code'] !== T_WHITESPACE) {
$error = 'A cast statement must be followed by a single space';
$phpcsFile->addError($error, $stackPtr);
return;
}
if ($tokens[($stackPtr + 1)]['content'] !== ' ') {
$error = 'A cast statement must be followed by a single space';
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,135 @@
<?php
/**
* Moodle_Sniffs_Functions_FunctionCallArgumentSpacingSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_Functions_FunctionCallArgumentSpacingSniff.
*
* Checks that calls to methods and functions are spaced correctly.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_Functions_FunctionCallArgumentSpacingSniff 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();
// Skip tokens that are the names of functions or classes
// within their definitions. For example:
// function myFunction...
// "myFunction" is T_STRING but we should skip because it is not a
// function or method *call*.
$functionName = $stackPtr;
$functionKeyword = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($tokens[$functionKeyword]['code'] === T_FUNCTION || $tokens[$functionKeyword]['code'] === T_CLASS) {
return;
}
// If the next non-whitespace token after the function or method call
// is not an opening parenthesis then it cant really be a *call*.
$openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($functionName + 1), null, true);
if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
return;
}
$closeBracket = $tokens[$openBracket]['parenthesis_closer'];
$nextSeperator = $openBracket;
while (($nextSeperator = $phpcsFile->findNext(array(T_COMMA, T_VARIABLE), ($nextSeperator + 1), $closeBracket)) !== false) {
// Make sure the comma or variable belongs directly to this function call,
// and is not inside a nested function call or array.
$brackets = $tokens[$nextSeperator]['nested_parenthesis'];
$lastBracket = array_pop($brackets);
if ($lastBracket !== $closeBracket) {
continue;
}
if ($tokens[$nextSeperator]['code'] === T_COMMA) {
if ($tokens[($nextSeperator - 1)]['code'] === T_WHITESPACE) {
$error = 'Space found before comma in function call';
$phpcsFile->addWarning($error, $stackPtr);
}
if ($tokens[($nextSeperator + 1)]['code'] !== T_WHITESPACE) {
$error = 'No space found after comma in function call';
$phpcsFile->addWarning($error, $stackPtr);
} else {
// If there is a newline in the space, then the must be formatting
// each argument on a newline, which is valid, so ignore it.
if (strpos($tokens[($nextSeperator + 1)]['content'], $phpcsFile->eolChar) === false) {
$space = strlen($tokens[($nextSeperator + 1)]['content']);
if ($space > 1) {
$error = 'Expected 1 space after comma in function call; ';
$error .= $space.' found';
$phpcsFile->addWarning($error, $stackPtr);
}
}
}
} else {
// Token is a variable.
$nextToken = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($nextSeperator + 1), $closeBracket, true);
if ($nextToken !== false) {
if ($tokens[$nextToken]['code'] === T_EQUAL) {
if (($tokens[($nextToken - 1)]['code']) !== T_WHITESPACE) {
$error = 'Expected 1 space before = sign of default value';
$phpcsFile->addWarning($error, $stackPtr);
}
if ($tokens[($nextToken + 1)]['code'] !== T_WHITESPACE) {
$error = 'Expected 1 space after = sign of default value';
$phpcsFile->addWarning($error, $stackPtr);
}
}
}
}//end if
}//end while
}//end process()
}//end class
?>

View file

@ -0,0 +1,125 @@
<?php
/**
* Moodle_Sniffs_Functions_FunctionCallSignatureSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_Functions_FunctionCallSignatureSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_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.
$next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
// Not a function call.
return;
}
if (isset($tokens[$next]['parenthesis_closer']) === false) {
// Not a function call.
return;
}
// Find the previous non-empty token.
$previous = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($tokens[$previous]['code'] === T_FUNCTION) {
// It's a function definition, not a function call.
return;
}
if ($tokens[$previous]['code'] === T_NEW) {
// We are creating an object, not calling a function.
return;
}
if (($stackPtr + 1) !== $next) {
// Checking this: $value = my_function[*](...).
$error = 'Space before opening parenthesis of function call prohibited';
$phpcsFile->addError($error, $stackPtr);
}
if ($tokens[($next + 1)]['code'] === T_WHITESPACE) {
// Checking this: $value = my_function([*]...).
$error = 'Space after opening parenthesis of function call prohibited';
$phpcsFile->addError($error, $stackPtr);
}
$closer = $tokens[$next]['parenthesis_closer'];
if ($tokens[($closer - 1)]['code'] === T_WHITESPACE) {
// Checking this: $value = my_function(...[*]).
$between = $phpcsFile->findNext(T_WHITESPACE, ($next + 1), null, true);
// Only throw an error if there is some content between the parenthesis.
// IE. 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);
}
}
$next = $phpcsFile->findNext(T_WHITESPACE, ($closer + 1), null, true);
if ($tokens[$next]['code'] === T_SEMICOLON) {
if (in_array($tokens[($closer + 1)]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
$error = 'Space after closing parenthesis of function call prohibited';
$phpcsFile->addError($error, $closer);
}
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,54 @@
<?php
/**
* Moodle_Sniffs_Functions_FunctionDeclarationSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @copyright 2006 Moodle Pty Ltd (ABN 77 084 670 600)
* @license http://www.gnu.org/copyleft/gpl.html GPL
* @version CVS: $Id:
* @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');
}
/**
* Moodle_Sniffs_Functions_FunctionDeclarationSniff.
*
* Checks the function declaration is correct.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @copyright 2006 Moodle Pty Ltd (ABN 77 084 670 600)
* @license http://www.gnu.org/copyleft/gpl.html GPL
* @version CVS: $Id:
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_Functions_FunctionDeclarationSniff extends PHP_CodeSniffer_Standards_AbstractPatternSniff
{
/**
* Returns an array of patterns to check are correct.
*
* @return array
*/
protected function getPatterns()
{
return array(
'function abc(...) {',
'abstract function abc(...);'
);
}//end getPatterns()
}//end class
?>

View file

@ -0,0 +1,77 @@
<?php
/**
* Moodle_Sniffs_Functions_LowercaseFunctionKeywordsSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @copyright 2006 Moodle Pty Ltd (ABN 77 084 670 600)
* @license http://www.gnu.org/copyleft/gpl.html GPL
* @version CVS: $Id:
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_Functions_LowercaseFunctionKeywordsSniff.
*
* Ensures all class keywords are lowercase.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @copyright 2006 Moodle Pty Ltd (ABN 77 084 670 600)
* @license http://www.gnu.org/copyleft/gpl.html GPL
* @version CVS: $Id:
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_Functions_LowercaseFunctionKeywordsSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_FUNCTION,
T_PUBLIC,
T_PRIVATE,
T_PROTECTED,
T_STATIC,
);
}//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();
$content = $tokens[$stackPtr]['content'];
if ($content !== strtolower($content)) {
$type = strtoupper($content);
$expected = strtolower($content);
$error = "$type keyword must be lowercase; expected \"$expected\" but found \"$content\"";
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,110 @@
<?php
/**
* Moodle_Sniffs_Functions_ValidDefaultValueSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_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 Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_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';
$error .= ' of the argument list';
$phpcsFile->addError($error, $nextArg);
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,151 @@
<?php
/**
* Moodle_Sniffs_NamingConventions_UpperCaseConstantNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_NamingConventions_UpperCaseConstantNameSniff.
*
* Ensures that constant names are all uppercase.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_NamingConventions_UpperCaseConstantNameSniff 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();
$constName = $tokens[$stackPtr]['content'];
// If this token is in a heredoc, ignore it.
if ($phpcsFile->hasCondition($stackPtr, T_START_HEREDOC) === true) {
return;
}
// If the next non-whitespace token after this token
// is not an opening parenthesis then it is not a function call.
$openBracket = $phpcsFile->findNext(array(T_WHITESPACE), ($stackPtr + 1), null, true);
if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
$functionKeyword = $phpcsFile->findPrevious(array(T_WHITESPACE, T_COMMA, T_COMMENT, T_STRING), ($stackPtr - 1), null, true);
$declarations = array(
T_FUNCTION,
T_CLASS,
T_INTERFACE,
T_IMPLEMENTS,
T_EXTENDS,
T_INSTANCEOF,
T_NEW,
);
if (in_array($tokens[$functionKeyword]['code'], $declarations) === true) {
// This is just a declaration; no constants here.
return;
}
if ($tokens[$functionKeyword]['code'] === T_CONST) {
// This is a class constant.
if (strtoupper($constName) !== $constName) {
$error = 'Class constants must be uppercase; expected '.strtoupper($constName)." but found $constName";
$phpcsFile->addError($error, $stackPtr);
}
return;
}
// Is this a class name?
$nextPtr = $phpcsFile->findNext(array(T_WHITESPACE), ($stackPtr + 1), null, true);
if ($tokens[$nextPtr]['code'] === T_DOUBLE_COLON) {
return;
}
// Is this a type hint?
if ($tokens[$nextPtr]['code'] === T_VARIABLE) {
return;
} else if ($phpcsFile->isReference($nextPtr) === true) {
return;
}
// Is this a member var name?
$prevPtr = $phpcsFile->findPrevious(array(T_WHITESPACE), ($stackPtr - 1), null, true);
if ($tokens[$prevPtr]['code'] === T_OBJECT_OPERATOR) {
return;
}
// Is this an instance of declare()
$prevPtr = $phpcsFile->findPrevious(array(T_WHITESPACE, T_OPEN_PARENTHESIS), ($stackPtr - 1), null, true);
if ($tokens[$prevPtr]['code'] === T_DECLARE) {
return;
}
// This is a real constant.
if (strtoupper($constName) !== $constName) {
$error = 'Constants must be uppercase; expected '.strtoupper($constName)." but found $constName";
$phpcsFile->addError($error, $stackPtr);
}
} else if (strtolower($constName) === 'define' || strtolower($constName) === 'constant') {
/*
This may be a "define" or "constant" function call.
*/
// The next non-whitespace token must be the constant name.
$constPtr = $phpcsFile->findNext(array(T_WHITESPACE), ($openBracket + 1), null, true);
if ($tokens[$constPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
return;
}
$constName = $tokens[$constPtr]['content'];
if (strtoupper($constName) !== $constName) {
$error = 'Constants must be uppercase; expected '.strtoupper($constName)." but found $constName";
$phpcsFile->addError($error, $stackPtr);
}
}//end if
}//end process()
}//end class
?>

View file

@ -0,0 +1,80 @@
<?php
/**
* Moodle_Sniffs_NamingConventions_ValidClassNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_NamingConventions_ValidClassNameSniff.
*
* Ensures class and interface names start with a capital letter
* and use _ separators.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_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']);
// Make sure that the word is all lowercase
if (!preg_match('/[a-z]?/', $name)) {
$error = ucfirst($tokens[$stackPtr]['content']).' name is not valid, must be all lower-case';
$phpcsFile->addError($error, $stackPtr);
}//end if
}//end process()
}//end class
?>

View file

@ -0,0 +1,193 @@
<?php
/**
* Moodle_Sniffs_NamingConventions_ValidFunctionNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @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');
}
/**
* Moodle_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 Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_NamingConventions_ValidFunctionNameSniff extends PHP_CodeSniffer_Standards_AbstractScopeSniff
{
/**
* A list of all PHP magic methods.
*
* @var array
*/
private $_magicMethods = array(
'construct',
'destruct',
'call',
'callStatic',
'get',
'set',
'isset',
'unset',
'sleep',
'wakeup',
'toString',
'set_state',
'clone',
);
/**
* A list of all PHP magic functions.
*
* @var array
*/
private $_magicFunctions = array(
'autoload',
);
/**
* Constructs a Moodle_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)
{
$className = $phpcsFile->getDeclarationName($currScope);
$methodName = $phpcsFile->getDeclarationName($stackPtr);
// 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 \"$className::$methodName\" is invalid; only PHP magic methods should be prefixed with a double underscore";
$phpcsFile->addError($error, $stackPtr);
}
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'];
// Only lower-case accepted
if (preg_match('/[A-Z]+/', $methodName)) {
if ($scopeSpecified === true) {
$error = ucfirst($scope)." method name \"$className::$methodName\" must be in lower-case letters only";
} else {
$error = "Method name \"$className::$methodName\" must be in lower-case letters only";
}
$phpcsFile->addError($error, $stackPtr);
return;
}
// No numbers accepted
if (preg_match('/[0-9]+/', $methodName)) {
if ($scopeSpecified === true) {
$error = ucfirst($scope)." method name \"$className::$methodName\" must only contain letters";
} else {
$error = "Method name \"$className::$methodName\" must only contain letters";
}
$phpcsFile->addError($error, $stackPtr);
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);
// 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 \"$functionName\" is invalid; only PHP magic methods should be prefixed with a double underscore";
$phpcsFile->addError($error, $stackPtr);
}
return;
}
// Only lower-case accepted
if (preg_match('/[A-Z]+/', $functionName)) {
$error = "function name \"$functionName\" must be lower-case letters only";
$phpcsFile->addError($error, $stackPtr);
return;
}
// Only letters accepted
if (preg_match('/[0-9]+/', $functionName)) {
$error = "function name \"$functionName\" must only contain letters";
$phpcsFile->addError($error, $stackPtr);
return;
}
}//end processTokenOutsideScope()
}//end class
?>

View file

@ -0,0 +1,116 @@
<?php
/**
* Moodle_Sniffs_NamingConventions_ValidVariableNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @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);
}
/**
* Moodle_Sniffs_NamingConventions_ValidVariableNameSniff.
*
* Checks the naming of member variables.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_NamingConventions_ValidVariableNameSniff extends PHP_CodeSniffer_Standards_AbstractVariableSniff
{
private $allowed_global_vars = array('CFG', 'SESSION', 'USER', 'COURSE', 'SITE', 'PAGE', 'DB', 'THEME');
/**
* 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();
$memberName = ltrim($tokens[$stackPtr]['content'], '$');
if (preg_match('/[A-Z]+/', $memberName)) {
$error = "Member variable \"$memberName\" must be all lower-case";
$phpcsFile->addError($error, $stackPtr);
return;
}
// Must not be preceded by 'var' keyword
$keyword = $phpcsFile->findPrevious(T_VAR, $stackPtr);
if ($tokens[$keyword]['line'] == $tokens[$stackPtr]['line']) {
$error = "The 'var' keyword is not permitted. Visibility must be explicitly declared with public, private or protected";
$phpcsFile->addError($error, $stackPtr);
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)
{
$tokens = $phpcsFile->getTokens();
$memberName = ltrim($tokens[$stackPtr]['content'], '$');
if (preg_match('/[A-Z]+/', $memberName)) {
if (!in_array($memberName, $this->allowed_global_vars)) {
$error = "Member variable \"$memberName\" must be all lower-case";
$phpcsFile->addError($error, $stackPtr);
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)
{
$tokens = $phpcsFile->getTokens();
$memberName = ltrim($tokens[$stackPtr]['content'], '$');
if (preg_match('/[A-Z]+/', $memberName)) {
$error = "Member variable \"$memberName\" must be all lower-case";
$phpcsFile->addError($error, $stackPtr);
return;
}
return;
}//end processVariableInString()
}//end class
?>

View file

@ -0,0 +1,91 @@
<?php
/**
* Moodle_Sniffs_PHP_DisallowShortOpenTagSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_PHP_DisallowShortOpenTagSniff.
*
* Makes sure that shorthand PHP open tags are not used.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_PHP_DisallowShortOpenTagSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_OPEN_TAG,
T_OPEN_TAG_WITH_ECHO,
);
}//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)
{
// If short open tags are off, then any short open tags will be converted
// to inline_html tags so we can just ignore them.
// If its on, then we want to ban the use of them.
$option = ini_get('short_open_tag');
// Ini_get returns a string "0" if short open tags is off.
if ($option === '0') {
return;
}
$tokens = $phpcsFile->getTokens();
$openTag = $tokens[$stackPtr];
if ($openTag['content'] === '<?') {
$error = 'Short PHP opening tag used. Found "'.$openTag['content'].'" Expected "<?php".';
$phpcsFile->addError($error, $stackPtr);
}
if ($openTag['code'] === T_OPEN_TAG_WITH_ECHO) {
$nextVar = $tokens[$phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true)];
$error = 'Short PHP opening tag used with echo. Found "';
$error .= $openTag['content'].' '.$nextVar['content'].' ..." but expected "<?php echo '.$nextVar['content'].' ...".';
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,84 @@
<?php
/**
* Moodle_Sniffs_PHP_LowerCaseConstantSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_PHP_LowerCaseConstantSniff.
*
* Checks that all uses of true, false and null are lowerrcase.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_PHP_LowerCaseConstantSniff implements PHP_CodeSniffer_Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = array(
'PHP',
'JS',
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_TRUE,
T_FALSE,
T_NULL,
);
}//end register()
/**
* Processes this sniff, 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();
$keyword = $tokens[$stackPtr]['content'];
if (strtolower($keyword) !== $keyword) {
$error = 'TRUE, FALSE and NULL must be lowercase; expected "'.strtolower($keyword).'" but found "'.$keyword.'"';
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,139 @@
<?php
/**
* Moodle_Sniffs_PHP_LowercasePHPFunctionsSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_PHP_LowercasePHPFunctionsSniff.
*
* Ensures all calls to inbuilt PHP functions are lowercase.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_PHP_LowercasePHPFunctionsSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_ISSET,
T_ECHO,
T_PRINT,
T_RETURN,
T_BREAK,
T_CONTINUE,
T_EMPTY,
T_EVAL,
T_EXIT,
T_LIST,
T_UNSET,
T_INCLUDE,
T_INCLUDE_ONCE,
T_REQUIRE,
T_REQUIRE_ONCE,
T_NEW,
T_DECLARE,
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();
if ($tokens[$stackPtr]['code'] !== T_STRING) {
$content = $tokens[$stackPtr]['content'];
if ($content !== strtolower($content)) {
$type = strtoupper($content);
$expected = strtolower($content);
$error = "$type keyword must be lowercase; expected \"$expected\" but found \"$content\"";
$phpcsFile->addError($error, $stackPtr);
}
return;
}
// Make sure this is a function call.
$next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
if ($next === false) {
// Not a function call.
return;
}
if ($tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
// Not a function call.
return;
}
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($tokens[$prev]['code'] === T_FUNCTION) {
// Function declaration, not a function call.
return;
}
if ($tokens[$prev]['code'] === T_OBJECT_OPERATOR) {
// Not an inbuilt function.
return;
}
if ($tokens[$prev]['code'] === T_DOUBLE_COLON) {
// Not an inbuilt function.
return;
}
// Make sure it is an inbuilt PHP function.
// PHP_CodeSniffer doesn't include/require any files, so no
// user defined global functions can exist, except for
// PHP_CodeSniffer ones.
$content = $tokens[$stackPtr]['content'];
if (function_exists($content) === false) {
return;
}
if ($content !== strtolower($content)) {
$expected = strtolower($content);
$error = "Calls to inbuilt PHP functions must be lowercase; expected \"$expected\" but found \"$content\"";
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,109 @@
<?php
/**
* Moodle_Sniffs_Strings_DoubleQuoteUsageSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_Strings_DoubleQuoteUsageSniff.
*
* Makes sure that any use of Double Quotes ("") are warranted.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_Strings_DoubleQuoteUsageSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_CONSTANT_ENCAPSED_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();
$workingString = $tokens[$stackPtr]['content'];
// Check if it's a double quoted string.
if (strpos($workingString, '"') === false) {
return;
}
// Make sure it's not a part of a string started above.
// If it is, then we have already checked it.
if ($workingString[0] !== '"') {
return;
}
// Work through the following tokens, in case this string is stretched
// over multiple Lines.
for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) {
if ($tokens[$i]['type'] !== 'T_CONSTANT_ENCAPSED_STRING') {
break;
}
$workingString .= $tokens[$i]['content'];
}
$allowedChars = array(
'\n',
'\r',
'\f',
'\t',
'\v',
'\x',
'\'',
);
foreach ($allowedChars as $testChar) {
if (strpos($workingString, $testChar) !== false) {
return;
}
}
$error = "String $workingString does not require double quotes; use single quotes instead";
$phpcsFile->addError($error, $stackPtr);
}//end process()
}//end class
?>

View file

@ -0,0 +1,85 @@
<?php
/**
* Moodle_Sniffs_Strings_EchoedStringsSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_Strings_EchoedStringsSniff.
*
* Makes sure that any strings that are "echoed" are not enclosed in brackets
* like a function call.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_Strings_EchoedStringsSniff implements PHP_CodeSniffer_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_ECHO);
}//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();
$firstContent = $phpcsFile->findNext(array(T_WHITESPACE), ($stackPtr + 1), null, true);
// If the first non-whitespace token is not an opening parenthesis, then we are not concerned.
if ($tokens[$firstContent]['code'] !== T_OPEN_PARENTHESIS) {
return;
}
$endOfStatement = $phpcsFile->findNext(array(T_SEMICOLON), $stackPtr, null, false);
// If the token before the semi-colon is not a closing parenthesis, then we are not concerned.
if ($tokens[($endOfStatement - 1)]['code'] !== T_CLOSE_PARENTHESIS) {
return;
}
if (($phpcsFile->findNext(PHP_CodeSniffer_Tokens::$operators, $stackPtr, $endOfStatement, false)) === false) {
// There are no arithmetic operators in this.
$error = 'Echoed strings should not be bracketed';
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,86 @@
<?php
/**
* Moodle_Sniffs_WhiteSpace_DisallowTabIndentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_WhiteSpace_DisallowTabIndentSniff.
*
* Throws errors if tabs are used for indentation.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_WhiteSpace_DisallowTabIndentSniff implements PHP_CodeSniffer_Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = array(
'PHP',
'JS',
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_WHITESPACE);
}//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 whitespace used for indentation.
$line = $tokens[$stackPtr]['line'];
if ($stackPtr > 0 && $tokens[($stackPtr - 1)]['line'] === $line) {
return;
}
if (strpos($tokens[$stackPtr]['content'], "\t") !== false) {
$error = 'Spaces must be used to indent lines; tabs are not allowed';
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,121 @@
<?php
/**
* Verifies that class members are spaced correctly.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
if (class_exists('PHP_CodeSniffer_Standards_AbstractVariableSniff', true) === false) {
throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Standards_AbstractVariableSniff not found');
}
/**
* Verifies that class members are spaced correctly.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_WhiteSpace_MemberVarSpacingSniff extends PHP_CodeSniffer_Standards_AbstractVariableSniff
{
/**
* Processes the function tokens within the class.
*
* @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 processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// There needs to be 1 blank line before the var, not counting comments.
$prevLineToken = null;
for ($i = ($stackPtr - 1); $i > 0; $i--) {
if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$commentTokens) === true) {
// Skip comments.
continue;
} else if (strpos($tokens[$i]['content'], $phpcsFile->eolChar) === false) {
// Not the end of the line.
continue;
} else {
// If this is a WHITESPACE token, and the token right before
// it is a DOC_COMMENT, then it is just the newline after the
// member var's comment, and can be skipped.
if ($tokens[$i]['code'] === T_WHITESPACE && in_array($tokens[($i - 1)]['code'], PHP_CodeSniffer_Tokens::$commentTokens) === true) {
continue;
}
$prevLineToken = $i;
break;
}
}
if (is_null($prevLineToken) === true) {
// Never found the previous line, which means
// there are 0 blank lines before the member var.
$foundLines = 0;
} else {
$prevContent = $phpcsFile->findPrevious(array(T_WHITESPACE, T_DOC_COMMENT), $prevLineToken, null, true);
$foundLines = ($tokens[$prevLineToken]['line'] - $tokens[$prevContent]['line']);
}//end if
if ($foundLines !== 1) {
$phpcsFile->addError("Expected 1 blank line before member var; $foundLines found", $stackPtr);
}
}//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,115 @@
<?php
/**
* Moodle_Sniffs_Whitespace_ScopeClosingBraceSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_Whitespace_ScopeClosingBraceSniff.
*
* Checks that the closing braces of scopes are aligned correctly.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Nicolas Connault <nicolasconnault@gmail.com>
*
* @copyright 2009 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_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;
}
// We need to actually find the first piece of content on this line,
// as 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'];
$scopeStart = $tokens[$stackPtr]['scope_opener'];
$scopeEnd = $tokens[$stackPtr]['scope_closer'];
// Check that the closing brace is on its 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);
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 '.($startColumn + 3).' spaces, found '.($braceIndent - 1);
$phpcsFile->addError($error, $scopeEnd);
}
} else {
if ($braceIndent !== $startColumn) {
$error = 'Closing brace indented incorrectly; expected '.($startColumn - 1).' spaces, found '.($braceIndent - 1);
$phpcsFile->addError($error, $scopeEnd);
}
}
}//end process()
}//end class
?>

View file

@ -0,0 +1,295 @@
<?php
/**
* Moodle_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 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Moodle_Sniffs_Whitespace_ScopeIndentSniff.
*
* Checks that control structures are structured correctly, and their content
* is indented correctly. This sniff will throw errors if tabs are used
* for indentation rather than spaces.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Moodle_Sniffs_WhiteSpace_ScopeIndentSniff implements PHP_CodeSniffer_Sniff
{
/**
* The number of spaces code should be indented.
*
* @var int
*/
protected $indent = 4;
/**
* Does the indent need to be exactly right.
*
* If TRUE, indent needs to be exactly $ident spaces. If FALSE,
* indent needs to be at least $ident spaces (but can be more).
*
* @var bool
*/
protected $exact = false;
/**
* Any scope openers that should not cause an indent.
*
* @var array(int)
*/
protected $nonIndentingScopes = array(T_SWITCH);
/**
* 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_opener']) === false) {
return;
}
if ($tokens[$stackPtr]['code'] === T_ELSE) {
$next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true);
// We will handle the T_IF token in another call to process.
if ($tokens[$next]['code'] === T_IF) {
return;
}
}
// Find the first token on this line.
$firstToken = $stackPtr;
for ($i = $stackPtr; $i >= 0; $i--) {
// Record the first code token on the line.
if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
$firstToken = $i;
}
// It's the start of the line, so we've found our first php token.
if ($tokens[$i]['column'] === 1) {
break;
}
}
// Based on the conditions that surround this token, determine the
// indent that we expect this current content to be.
$expectedIndent = $this->calculateExpectedIndent($tokens, $firstToken);
if ($tokens[$firstToken]['column'] !== $expectedIndent) {
$error = 'Line indented incorrectly; expected ';
$error .= ($expectedIndent - 1).' spaces, found ';
$error .= ($tokens[$firstToken]['column'] - 1);
$phpcsFile->addError($error, $stackPtr);
}
$scopeOpener = $tokens[$stackPtr]['scope_opener'];
$scopeCloser = $tokens[$stackPtr]['scope_closer'];
// Some scopes are expected not to have indents.
if (in_array($tokens[$firstToken]['code'], $this->nonIndentingScopes) === false) {
$indent = ($expectedIndent + $this->indent);
} else {
$indent = $expectedIndent;
}
$newline = false;
$commentOpen = false;
$inHereDoc = false;
// Only loop over the content beween the opening and closing brace, not
// the braces themselves.
for ($i = ($scopeOpener + 1); $i < $scopeCloser; $i++) {
// If this token is another scope, skip it as it will be handled by
// another call to this sniff.
if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$scopeOpeners) === true) {
if (isset($tokens[$i]['scope_opener']) === true) {
$i = $tokens[$i]['scope_closer'];
} else {
// If this token does not have a scope_opener indice, then
// it's probably an inline scope, so let's skip to the next
// semicolon. Inline scopes include inline if's, abstract methods etc.
$nextToken = $phpcsFile->findNext(T_SEMICOLON, $i, $scopeCloser);
if ($nextToken !== false) {
$i = $nextToken;
}
}
continue;
}
// If this is a HEREDOC then we need to ignore it as the whitespace
// before the contents within the HEREDOC are considered part of the content.
if ($tokens[$i]['code'] === T_START_HEREDOC) {
$inHereDoc = true;
continue;
} else if ($inHereDoc === true) {
if ($tokens[$i]['code'] === T_END_HEREDOC) {
$inHereDoc = false;
}
continue;
}
if ($tokens[$i]['column'] === 1) {
// We started a newline.
$newline = true;
}
if ($newline === true && $tokens[$i]['code'] !== T_WHITESPACE) {
// If we started a newline and we find a token that is not
// whitespace, then this must be the first token on the line that
// must be indented.
$newline = false;
$firstToken = $i;
$column = $tokens[$firstToken]['column'];
// Special case for non-PHP code.
if ($tokens[$firstToken]['code'] === T_INLINE_HTML) {
$trimmedContentLength = strlen(ltrim($tokens[$firstToken]['content']));
if ($trimmedContentLength === 0) {
continue;
}
$contentLength = strlen($tokens[$firstToken]['content']);
$column = ($contentLength - $trimmedContentLength + 1);
}
// Check to see if this constant string spans multiple lines.
// If so, then make sure that the strings on lines other than the
// first line are indented appropriately, based on their whitespace.
if (in_array($tokens[$firstToken]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) {
if (in_array($tokens[($firstToken - 1)]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) {
// If we find a string that directly follows another string
// then its just a string that spans multiple lines, so we
// don't need to check for indenting.
continue;
}
}
// This is a special condition for T_DOC_COMMENT and C-style
// comments, which contain whitespace between each line.
if (in_array($tokens[$firstToken]['code'], array(T_COMMENT, T_DOC_COMMENT)) === true) {
$content = trim($tokens[$firstToken]['content']);
if (preg_match('|^/\*|', $content) !== 0) {
// Check to see if the end of the comment is on the same line
// as the start of the comment. If it is, then we don't
// have to worry about opening a comment.
if (preg_match('|\*/$|', $content) === 0) {
// We don't have to calculate the column for the start
// of the comment as there is a whitespace token before it.
$commentOpen = true;
}
} else if ($commentOpen === true) {
if ($content === '') {
// We are in a comment, but this line has nothing on it
// so let's skip it.
continue;
}
$contentLength = strlen($tokens[$firstToken]['content']);
$trimmedContentLength = strlen(ltrim($tokens[$firstToken]['content']));
$column = ($contentLength - $trimmedContentLength + 1);
if (preg_match('|\*/$|', $content) !== 0) {
$commentOpen = false;
}
}//end if
}//end if
// The token at the start of the line, needs to have its' column
// greater than the relative indent we set above. If it is less,
// an error should be shown.
if ($column !== $indent) {
if ($this->exact === true || $column < $indent) {
$error = 'Line indented incorrectly; expected ';
if ($this->exact === false) {
$error .= 'at least ';
}
$error .= ($indent - 1).' spaces, found ';
$error .= ($column - 1);
$phpcsFile->addError($error, $firstToken);
}
}
}//end if
}//end for
}//end process()
/**
* Calculates the expected indent of a token.
*
* @param array $tokens The stack of tokens for this file.
* @param int $stackPtr The position of the token to get indent for.
*
* @return int
*/
protected function calculateExpectedIndent(array $tokens, $stackPtr)
{
$conditionStack = array();
// Empty conditions array (top level structure).
if (empty($tokens[$stackPtr]['conditions']) === true) {
return 1;
}
$tokenConditions = $tokens[$stackPtr]['conditions'];
foreach ($tokenConditions as $id => $condition) {
// If it's an indenting scope ie. it's not in our array of
// scopes that don't indent, add it to our condition stack.
if (in_array($condition, $this->nonIndentingScopes) === false) {
$conditionStack[$id] = $condition;
}
}
return ((count($conditionStack) * $this->indent) + 1);
}//end calculateExpectedIndent()
}//end class
?>

View file

@ -0,0 +1,718 @@
<?php
/**
* Tokenizes JS code.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Tokenizes JS code.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_Tokenizers_JS
{
/**
* A list of tokens that are allowed to open a scope.
*
* This array also contains information about what kind of token the scope
* opener uses to open and close the scope, if the token strictly requires
* an opener, if the token can share a scope closer, and who it can be shared
* with. An example of a token that shares a scope closer is a CASE scope.
*
* @var array
*/
public $scopeOpeners = array(
T_IF => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_TRY => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => true,
'shared' => false,
'with' => array(),
),
T_CATCH => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => true,
'shared' => false,
'with' => array(),
),
T_ELSE => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_FOR => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_FUNCTION => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_WHILE => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_DO => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => true,
'shared' => false,
'with' => array(),
),
T_SWITCH => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => true,
'shared' => false,
'with' => array(),
),
T_CASE => array(
'start' => T_COLON,
'end' => T_BREAK,
'strict' => true,
'shared' => true,
'with' => array(
T_DEFAULT,
T_CASE,
),
),
T_DEFAULT => array(
'start' => T_COLON,
'end' => T_BREAK,
'strict' => true,
'shared' => true,
'with' => array(T_CASE),
),
);
/**
* A list of tokens that end the scope.
*
* This array is just a unique collection of the end tokens
* from the _scopeOpeners array. The data is duplicated here to
* save time during parsing of the file.
*
* @var array
*/
public $endScopeTokens = array(
T_CLOSE_CURLY_BRACKET,
T_BREAK,
);
/**
* A list of special JS tokens and their types.
*
* @var array
*/
protected $tokenValues = array(
'function' => 'T_FUNCTION',
'prototype' => 'T_PROTOTYPE',
'try' => 'T_TRY',
'catch' => 'T_CATCH',
'return' => 'T_RETURN',
'if' => 'T_IF',
'else' => 'T_ELSE',
'do' => 'T_DO',
'while' => 'T_WHILE',
'for' => 'T_FOR',
'var' => 'T_VAR',
'true' => 'T_TRUE',
'false' => 'T_FALSE',
'null' => 'T_NULL',
'this' => 'T_THIS',
'(' => 'T_OPEN_PARENTHESIS',
')' => 'T_CLOSE_PARENTHESIS',
'{' => 'T_OPEN_CURLY_BRACKET',
'}' => 'T_CLOSE_CURLY_BRACKET',
'[' => 'T_OPEN_SQUARE_BRACKET',
']' => 'T_CLOSE_SQUARE_BRACKET',
'?' => 'T_INLINE_THEN',
'.' => 'T_OBJECT_OPERATOR',
'+' => 'T_PLUS',
'-' => 'T_MINUS',
'*' => 'T_MULTIPLY',
'%' => 'T_MODULUS',
'/' => 'T_DIVIDE',
',' => 'T_COMMA',
';' => 'T_SEMICOLON',
':' => 'T_COLON',
'<' => 'T_LESS_THAN',
'>' => 'T_GREATER_THAN',
'<=' => 'T_IS_SMALLER_OR_EQUAL',
'>=' => 'T_IS_GREATER_OR_EQUAL',
'!' => 'T_BOOLEAN_NOT',
'!=' => 'T_IS_NOT_EQUAL',
'!==' => 'T_IS_NOT_IDENTICAL',
'=' => 'T_EQUAL',
'==' => 'T_IS_EQUAL',
'===' => 'T_IS_IDENTICAL',
'-=' => 'T_MINUS_EQUAL',
'+=' => 'T_PLUS_EQUAL',
'++' => 'T_INC',
'--' => 'T_DEC',
'//' => 'T_COMMENT',
'/*' => 'T_COMMENT',
'/**' => 'T_DOC_COMMENT',
'*/' => 'T_COMMENT',
);
/**
* A list string delimiters.
*
* @var array
*/
protected $stringTokens = array(
'\'',
'"',
);
/**
* A list tokens that start and end comments.
*
* @var array
*/
protected $commentTokens = array(
'//' => null,
'/*' => '*/',
'/**' => '*/',
);
/**
* Creates an array of tokens when given some PHP code.
*
* Starts by using token_get_all() but does a lot of extra processing
* to insert information about the context of the token.
*
* @param string $string The string to tokenize.
* @param string $eolChar The EOL character to use for splitting strings.
*
* @return array
*/
public function tokenizeString($string, $eolChar='\n')
{
$tokenTypes = array_keys($this->tokenValues);
$maxTokenLength = 0;
foreach ($tokenTypes as $token) {
if (strlen($token) > $maxTokenLength) {
$maxTokenLength = strlen($token);
}
}
$tokens = array();
$inString = '';
$inComment = '';
$buffer = '';
$cleanBuffer = false;
$tokens[] = array(
'code' => T_OPEN_TAG,
'type' => 'T_OPEN_TAG',
'content' => '',
);
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t*** START TOKENIZING ***".PHP_EOL;
}
$chars = str_split($string);
foreach ($chars as $i => $char) {
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$content = str_replace($eolChar, '\n', $char);
if ($inString !== '') {
echo "\t";
}
if ($inComment !== '') {
echo "\t";
}
echo "Process char $i => $content".PHP_EOL;
}
if ($inString === '' && $inComment === '' && $buffer !== '') {
// If the buffer only has whitespace and we are about to
// add a character, store the whitespace first.
if (trim($char) !== '' && trim($buffer) === '') {
$tokens[] = array(
'code' => T_WHITESPACE,
'type' => 'T_WHITESPACE',
'content' => $buffer,
);
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$content = str_replace($eolChar, '\n', $buffer);
echo "=> Added token T_WHITESPACE ($content)".PHP_EOL;
}
$buffer = '';
}
// If the buffer is not whitespace and we are about to
// add a whitespace character, store the content first.
if ($inString === '' && $inComment === '' && trim($char) === '' && trim($buffer) !== '') {
$tokens[] = array(
'code' => T_STRING,
'type' => 'T_STRING',
'content' => $buffer,
);
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$content = str_replace($eolChar, '\n', $buffer);
echo "=> Added token T_STRING ($content)".PHP_EOL;
}
$buffer = '';
}
}//end if
// Process strings.
if ($inComment === '' && in_array($char, $this->stringTokens) === true) {
if ($inString === $char) {
// This could be the end of the string, but make sure it
// is not escaped first.
$escapes = 0;
for ($x = ($i - 1); $x >= 0; $x--) {
if ($chars[$x] !== '\\') {
break;
}
$escapes++;
}
if ($escapes === 0 || ($escapes % 2) === 0) {
// There is an even number escape chars,
// so this is not escaped, it is the end of the string.
$tokens[] = array(
'code' => T_CONSTANT_ENCAPSED_STRING,
'type' => 'T_CONSTANT_ENCAPSED_STRING',
'content' => $buffer.$char,
);
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t* found end of string *".PHP_EOL;
$content = str_replace($eolChar, '\n', $buffer.$char);
echo "=> Added token T_CONSTANT_ENCAPSED_STRING $content)".PHP_EOL;
}
$buffer = '';
$inString = '';
continue;
}
} else if ($inString === '') {
$inString = $char;
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t* looking for string closer *".PHP_EOL;
}
}//end if
}//end if
$buffer .= $char;
// We don't look for special tokens inside strings,
// so if we are in a string, we can continue here now
// that the current char is in the buffer.
if ($inString !== '') {
continue;
}
// Check for known tokens, but ignore tokens found that are not at
// the end of a string, like FOR and this.FORmat.
if (in_array(strtolower($buffer), $tokenTypes) === true && (preg_match('|[a-zA-z0-9_]|', $char) === 0 || preg_match('|[a-zA-z0-9_]|', $chars[($i + 1)]) === 0)) {
$matchedToken = false;
$lookAheadLength = ($maxTokenLength - strlen($buffer));
if ($lookAheadLength > 0) {
// The buffer contains a token type, but we need
// to look ahead at the next chars to see if this is
// actually part of a larger token. For example,
// FOR and FOREACH.
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t* buffer possibly contains token, looking ahead $lookAheadLength chars *".PHP_EOL;
}
$charBuffer = $buffer;
for ($x = 1; $x <= $lookAheadLength; $x++) {
if (isset($chars[($i + $x)]) === false) {
break;
}
$charBuffer .= $chars[($i + $x)];
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$content = str_replace($eolChar, '\n', $charBuffer);
echo "\t=> Looking ahead $x chars => $content".PHP_EOL;
}
if (in_array(strtolower($charBuffer), $tokenTypes) === true) {
// We've found something larger that matches
// so we can ignore this char.
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$type = $this->tokenValues[strtolower($charBuffer)];
echo "\t* look ahead found more specific token ($type), ignoring $i *".PHP_EOL;
}
$matchedToken = true;
break;
}
}//end for
}//end if
if ($matchedToken === false) {
$value = $this->tokenValues[strtolower($buffer)];
$tokens[] = array(
'code' => constant($value),
'type' => $value,
'content' => $buffer,
);
if (PHP_CODESNIFFER_VERBOSITY > 1) {
if ($lookAheadLength > 0) {
echo "\t* look ahead found nothing *".PHP_EOL;
}
$content = str_replace($eolChar, '\n', $buffer);
echo "=> Added token $value ($content)".PHP_EOL;
}
$cleanBuffer = true;
}
} else if (in_array(strtolower($char), $tokenTypes) === true) {
// No matter what token we end up using, we don't
// need the content in the buffer any more because we have
// found a valid token.
$tokens[] = array(
'code' => T_STRING,
'type' => 'T_STRING',
'content' => substr($buffer, 0, -1),
);
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$content = str_replace($eolChar, '\n', substr($buffer, 0, -1));
echo "=> Added token T_STRING ($content)".PHP_EOL;
}
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t* char is token, looking ahead ".($maxTokenLength - 1).' chars *'.PHP_EOL;
}
// The char is a token type, but we need to look ahead at the
// next chars to see if this is actually part of a larger token.
// For example, = and ===.
$charBuffer = $char;
$matchedToken = false;
for ($x = 1; $x <= $maxTokenLength; $x++) {
if (isset($chars[($i + $x)]) === false) {
break;
}
$charBuffer .= $chars[($i + $x)];
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$content = str_replace($eolChar, '\n', $charBuffer);
echo "\t=> Looking ahead $x chars => $content".PHP_EOL;
}
if (in_array(strtolower($charBuffer), $tokenTypes) === true) {
// We've found something larger that matches
// so we can ignore this char.
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$type = $this->tokenValues[strtolower($charBuffer)];
echo "\t* look ahead found more specific token ($type), ignoring $i *".PHP_EOL;
}
$matchedToken = true;
break;
}
}//end for
if ($matchedToken === false) {
$value = $this->tokenValues[strtolower($char)];
$tokens[] = array(
'code' => constant($value),
'type' => $value,
'content' => $char,
);
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t* look ahead found nothing *".PHP_EOL;
$content = str_replace($eolChar, '\n', $char);
echo "=> Added token $value ($content)".PHP_EOL;
}
$cleanBuffer = true;
} else {
$buffer = $char;
}
}//end if
// Keep track of content inside comments.
if ($inComment === '' && array_key_exists($buffer, $this->commentTokens) === true) {
// We have started a comment.
$inComment = $buffer;
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t* looking for end of comment *".PHP_EOL;
}
} else if ($inComment !== '') {
if ($this->commentTokens[$inComment] === null) {
// Comment ends at the next newline.
if (strpos($buffer, $eolChar) !== false) {
$inComment = '';
}
} else {
if ($this->commentTokens[$inComment] === $buffer) {
$inComment = '';
}
}
if (PHP_CODESNIFFER_VERBOSITY > 1) {
if ($inComment === '') {
echo "\t* found end of comment *".PHP_EOL;
}
}
}//end if
if ($cleanBuffer === true) {
$buffer = '';
$cleanBuffer = false;
}
}//end foreach
$tokens[] = array(
'code' => T_CLOSE_TAG,
'type' => 'T_CLOSE_TAG',
'content' => '',
);
/*
Now that we have done some basic tokenizing, we need to
modify the tokens to join some together and split some apart
so they match what the PHP tokenizer does.
*/
$finalTokens = array();
$newStackPtr = 0;
$numTokens = count($tokens);
for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
$token = $tokens[$stackPtr];
/*
Look for regular expressions and join the tokens together.
*/
if ($token['code'] === T_DIVIDE) {
$beforeTokens = array(
T_EQUAL,
T_OPEN_PARENTHESIS,
);
$afterTokens = array(
T_COMMA,
T_CLOSE_PARENTHESIS,
T_SEMICOLON,
T_WHITESPACE,
);
for ($prev = ($stackPtr - 1); $prev >= 0; $prev--) {
if (in_array($tokens[$prev]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
break;
}
}
// Token needs to be one of the standard allowed or the replace()
// method that can be called on string: string.replace(/abc/...).
if (in_array($tokens[$prev]['code'], $beforeTokens) === true || $tokens[$prev]['content'] === 'replace') {
// This might be a regular expression.
$regexTokens = array(
T_STRING,
T_WHITESPACE,
T_OBJECT_OPERATOR,
);
for ($next = ($stackPtr + 1); $next < $numTokens; $next++) {
if (in_array($tokens[$next]['code'], $regexTokens) === false) {
break;
}
}
if ($tokens[$next]['code'] === T_DIVIDE) {
if ($tokens[($next + 1)]['code'] === T_STRING) {
// The token directly after the end of the regex can
// be modifiers like global and case insensitive
// (.e.g, /pattern/gi).
$next++;
}
$regexEnd = $next;
for ($next = ($next + 1); $next < $numTokens; $next++) {
if (in_array($tokens[$next]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
break;
} else if (strpos($tokens[$next]['content'], $eolChar) !== false) {
// If this is the last token on the line.
break;
}
}
if (in_array($tokens[$next]['code'], $afterTokens) === true) {
// This is a regular expression, so join all the
// tokens together.
for ($i = ($stackPtr + 1); $i <= $regexEnd; $i++) {
$token['content'] .= $tokens[$i]['content'];
}
$token['code'] = T_REGULAR_EXPRESSION;
$token['type'] ='T_REGULAR_EXPRESSION';
$stackPtr = $regexEnd;
}
}
}//end if
}//end if
/*
Look for comments and join the tokens together.
*/
if (array_key_exists($token['content'], $this->commentTokens) === true) {
$newContent = '';
$tokenContent = $token['content'];
$endContent = $this->commentTokens[$tokenContent];
while ($tokenContent !== $endContent) {
if ($endContent === null && strpos($tokenContent, $eolChar) !== false) {
// A null end token means the comment ends at the end of
// the line so we look for newlines and split the token.
$tokens[$stackPtr]['content'] = substr($tokenContent, (strpos($tokenContent, $eolChar) + strlen($eolChar)));
$tokenContent = substr($tokenContent, 0, (strpos($tokenContent, $eolChar) + strlen($eolChar)));
// If the substr failed, skip the token as the content
// will now be blank.
if ($tokens[$stackPtr]['content'] !== false) {
$stackPtr--;
}
break;
}//end if
$stackPtr++;
$newContent .= $tokenContent;
if (isset($tokens[$stackPtr]) === false) {
break;
}
$tokenContent = $tokens[$stackPtr]['content'];
}//end while
// Save the new content in the current token so
// the code below can chop it up on newlines.
$token['content'] = $newContent.$tokenContent;
}//end if
/*
If this token has newlines in its content, split each line up
and create a new token for each line. We do this so it's easier
to asertain where errors occur on a line.
Note that $token[1] is the token's content.
*/
if (strpos($token['content'], $eolChar) !== false) {
$tokenLines = explode($eolChar, $token['content']);
$numLines = count($tokenLines);
for ($i = 0; $i < $numLines; $i++) {
$newToken['content'] = $tokenLines[$i];
if ($i === ($numLines - 1)) {
if ($tokenLines[$i] === '') {
break;
}
} else {
$newToken['content'] .= $eolChar;
}
$newToken['type'] = $token['type'];
$newToken['code'] = $token['code'];
$finalTokens[$newStackPtr] = $newToken;
$newStackPtr++;
}
} else {
$finalTokens[$newStackPtr] = $token;
$newStackPtr++;
}//end if
// Convert numbers, including decimals.
if ($token['code'] === T_STRING || $token['code'] === T_OBJECT_OPERATOR) {
$newContent = '';
$oldStackPtr = $stackPtr;
while (preg_match('|^[0-9\.]+$|', $tokens[$stackPtr]['content']) !== 0) {
$newContent .= $tokens[$stackPtr]['content'];
$stackPtr++;
}
if ($newContent !== '' && $newContent !== '.') {
$finalTokens[($newStackPtr - 1)]['content'] = $newContent;
if (ctype_digit($newContent) === true) {
$finalTokens[($newStackPtr - 1)]['code'] = constant('T_LNUMBER');
$finalTokens[($newStackPtr - 1)]['type'] = 'T_LNUMBER';
} else {
$finalTokens[($newStackPtr - 1)]['code'] = constant('T_DNUMBER');
$finalTokens[($newStackPtr - 1)]['type'] = 'T_DNUMBER';
}
$stackPtr--;
} else {
$stackPtr = $oldStackPtr;
}
}//end if
}//end for
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t*** END TOKENIZING ***".PHP_EOL;
}
return $finalTokens;
}//end tokenizeString()
}//end class
?>

View file

@ -0,0 +1,398 @@
<?php
/**
* Tokenizes PHP code.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* Tokenizes PHP code.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class PHP_CodeSniffer_Tokenizers_PHP
{
/**
* A list of tokens that are allowed to open a scope.
*
* This array also contains information about what kind of token the scope
* opener uses to open and close the scope, if the token strictly requires
* an opener, if the token can share a scope closer, and who it can be shared
* with. An example of a token that shares a scope closer is a CASE scope.
*
* @var array
*/
public $scopeOpeners = array(
T_IF => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_TRY => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => true,
'shared' => false,
'with' => array(),
),
T_CATCH => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => true,
'shared' => false,
'with' => array(),
),
T_ELSE => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_ELSEIF => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_FOR => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_FOREACH => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_INTERFACE => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => true,
'shared' => false,
'with' => array(),
),
T_FUNCTION => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_CLASS => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => true,
'shared' => false,
'with' => array(),
),
T_WHILE => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => false,
'shared' => false,
'with' => array(),
),
T_DO => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => true,
'shared' => false,
'with' => array(),
),
T_SWITCH => array(
'start' => T_OPEN_CURLY_BRACKET,
'end' => T_CLOSE_CURLY_BRACKET,
'strict' => true,
'shared' => false,
'with' => array(),
),
T_CASE => array(
'start' => T_COLON,
'end' => T_BREAK,
'strict' => true,
'shared' => true,
'with' => array(
T_DEFAULT,
T_CASE,
),
),
T_DEFAULT => array(
'start' => T_COLON,
'end' => T_BREAK,
'strict' => true,
'shared' => true,
'with' => array(T_CASE),
),
T_START_HEREDOC => array(
'start' => T_START_HEREDOC,
'end' => T_END_HEREDOC,
'strict' => true,
'shared' => false,
'with' => array(),
),
);
/**
* A list of tokens that end the scope.
*
* This array is just a unique collection of the end tokens
* from the _scopeOpeners array. The data is duplicated here to
* save time during parsing of the file.
*
* @var array
*/
public $endScopeTokens = array(
T_CLOSE_CURLY_BRACKET,
T_BREAK,
T_END_HEREDOC,
);
/**
* Creates an array of tokens when given some PHP code.
*
* Starts by using token_get_all() but does a lot of extra processing
* to insert information about the context of the token.
*
* @param string $string The string to tokenize.
* @param string $eolChar The EOL character to use for splitting strings.
*
* @return array
*/
public function tokenizeString($string, $eolChar='\n')
{
$tokens = @token_get_all($string);
$finalTokens = array();
$newStackPtr = 0;
$numTokens = count($tokens);
for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) {
$token = $tokens[$stackPtr];
$tokenIsArray = is_array($token);
/*
If we are using \r\n newline characters, the \r and \n are sometimes
split over two tokens. This normally occurs after comments. We need
to merge these two characters together so that our line endings are
consistent for all lines.
*/
if ($tokenIsArray === true && substr($token[1], -1) === "\r") {
if (isset($tokens[($stackPtr + 1)]) === true && is_array($tokens[($stackPtr + 1)]) === true && $tokens[($stackPtr + 1)][1][0] === "\n") {
$token[1] .= "\n";
if ($tokens[($stackPtr + 1)][1] === "\n") {
// The next token's content has been merged into this token,
// so we can skip it.
$stackPtr++;
} else {
$tokens[($stackPtr + 1)][1] = substr($tokens[($stackPtr + 1)][1], 1);
}
}
}//end if
/*
If this is a double quoted string, PHP will tokenise the whole
thing which causes problems with the scope map when braces are
within the string. So we need to merge the tokens together to
provide a single string.
*/
if ($tokenIsArray === false && $token === '"') {
$tokenContent = '"';
$nestedVars = array();
for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
$subTokenIsArray = is_array($tokens[$i]);
if ($subTokenIsArray === true) {
$tokenContent .= $tokens[$i][1];
if ($tokens[$i][1] === '{') {
$nestedVars[] = $i;
}
} else {
$tokenContent .= $tokens[$i];
if ($tokens[$i] === '}') {
array_pop($nestedVars);
}
}
if ($subTokenIsArray === false && $tokens[$i] === '"' && empty($nestedVars) === true) {
// We found the other end of the double quoted string.
break;
}
}
$stackPtr = $i;
// Convert each line within the double quoted string to a
// new token, so it conforms with other multiple line tokens.
$tokenLines = explode($eolChar, $tokenContent);
$numLines = count($tokenLines);
$newToken = array();
for ($j = 0; $j < $numLines; $j++) {
$newToken['content'] = $tokenLines[$j];
if ($j === ($numLines - 1)) {
if ($tokenLines[$j] === '') {
break;
}
} else {
$newToken['content'] .= $eolChar;
}
$newToken['code'] = T_DOUBLE_QUOTED_STRING;
$newToken['type'] = 'T_DOUBLE_QUOTED_STRING';
$finalTokens[$newStackPtr] = $newToken;
$newStackPtr++;
}
// Continue, as we're done with this token.
continue;
}//end if
/*
If this is a heredoc, PHP will tokenise the whole
thing which causes problems when heredocs don't
contain real PHP code, which is almost never.
We want to leave the start and end heredoc tokens
alone though.
*/
if ($tokenIsArray === true && $token[0] === T_START_HEREDOC) {
// Add the start heredoc token to the final array.
$finalTokens[$newStackPtr] = PHP_CodeSniffer::standardiseToken($token);
$newStackPtr++;
$tokenContent = '';
for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
$subTokenIsArray = is_array($tokens[$i]);
if ($subTokenIsArray === true && $tokens[$i][0] === T_END_HEREDOC) {
// We found the other end of the heredoc.
break;
}
if ($subTokenIsArray === true) {
$tokenContent .= $tokens[$i][1];
} else {
$tokenContent .= $tokens[$i];
}
}
$stackPtr = $i;
// Convert each line within the heredoc to a
// new token, so it conforms with other multiple line tokens.
$tokenLines = explode($eolChar, $tokenContent);
$numLines = count($tokenLines);
$newToken = array();
for ($j = 0; $j < $numLines; $j++) {
$newToken['content'] = $tokenLines[$j];
if ($j === ($numLines - 1)) {
if ($tokenLines[$j] === '') {
break;
}
} else {
$newToken['content'] .= $eolChar;
}
$newToken['code'] = T_HEREDOC;
$newToken['type'] = 'T_HEREDOC';
$finalTokens[$newStackPtr] = $newToken;
$newStackPtr++;
}
// Add the end heredoc token to the final array.
$finalTokens[$newStackPtr] = PHP_CodeSniffer::standardiseToken($tokens[$stackPtr]);
$newStackPtr++;
// Continue, as we're done with this token.
continue;
}//end if
/*
If this token has newlines in its content, split each line up
and create a new token for each line. We do this so it's easier
to asertain where errors occur on a line.
Note that $token[1] is the token's content.
*/
if ($tokenIsArray === true && strpos($token[1], $eolChar) !== false) {
$tokenLines = explode($eolChar, $token[1]);
$numLines = count($tokenLines);
$tokenName = token_name($token[0]);
for ($i = 0; $i < $numLines; $i++) {
$newToken['content'] = $tokenLines[$i];
if ($i === ($numLines - 1)) {
if ($tokenLines[$i] === '') {
break;
}
} else {
$newToken['content'] .= $eolChar;
}
$newToken['type'] = $tokenName;
$newToken['code'] = $token[0];
$finalTokens[$newStackPtr] = $newToken;
$newStackPtr++;
}
} else {
$newToken = PHP_CodeSniffer::standardiseToken($token);
// This is a special condition for T_ARRAY tokens use to
// type hint function arguments as being arrays. We want to keep
// the parenthsis map clean, so let's tag these tokens as
// T_ARRAY_HINT.
if ($newToken['code'] === T_ARRAY) {
// Recalculate number of tokens.
$numTokens = count($tokens);
for ($i = $stackPtr; $i < $numTokens; $i++) {
if (is_array($tokens[$i]) === false) {
if ($tokens[$i] === '(') {
break;
}
} else if ($tokens[$i][0] === T_VARIABLE) {
$newToken['code'] = T_ARRAY_HINT;
$newToken['type'] = 'T_ARRAY_HINT';
break;
}
}
}
$finalTokens[$newStackPtr] = $newToken;
$newStackPtr++;
}//end if
}//end for
return $finalTokens;
}//end tokenizeString()
}//end class
?>

View file

@ -0,0 +1,389 @@
<?php
/**
* The Tokens class contains weightings for tokens based on their
* probability of occurance in a file.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version CVS: $Id$
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
define('T_NONE', 0);
define('T_OPEN_CURLY_BRACKET', 1000);
define('T_CLOSE_CURLY_BRACKET', 1001);
define('T_OPEN_SQUARE_BRACKET', 1002);
define('T_CLOSE_SQUARE_BRACKET', 1003);
define('T_OPEN_PARENTHESIS', 1004);
define('T_CLOSE_PARENTHESIS', 1005);
define('T_COLON', 1006);
define('T_STRING_CONCAT', 1007);
define('T_INLINE_THEN', 1008);
define('T_NULL', 1009);
define('T_FALSE', 1010);
define('T_TRUE', 1011);
define('T_SEMICOLON', 1012);
define('T_EQUAL', 1013);
define('T_MULTIPLY', 1015);
define('T_DIVIDE', 1016);
define('T_PLUS', 1017);
define('T_MINUS', 1018);
define('T_MODULUS', 1019);
define('T_POWER', 1020);
define('T_BITWISE_AND', 1021);
define('T_BITWISE_OR', 1022);
define('T_ARRAY_HINT', 1023);
define('T_GREATER_THAN', 1024);
define('T_LESS_THAN', 1025);
define('T_BOOLEAN_NOT', 1026);
define('T_SELF', 1027);
define('T_PARENT', 1028);
define('T_DOUBLE_QUOTED_STRING', 1029);
define('T_COMMA', 1030);
define('T_HEREDOC', 1031);
define('T_PROTOTYPE', 1032);
define('T_THIS', 1033);
define('T_REGULAR_EXPRESSION', 1034);
/**
* The Tokens class contains weightings for tokens based on their
* probability of occurance in a file.
*
* The less the chance of a high occurance of an abitrary token, the higher
* the weighting.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Marc McIntyre <mmcintyre@squiz.net>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: 1.1.0
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
final class PHP_CodeSniffer_Tokens
{
/**
* The token weightings.
*
* @var array(int => int)
*/
public static $weightings = array(
T_CLASS => 1000,
T_FUNCTION => 100,
/*
Conditions.
*/
T_WHILE => 50,
T_FOR => 50,
T_FOREACH => 50,
T_IF => 50,
T_ELSE => 50,
T_ELSEIF => 50,
T_WHILE => 50,
T_DO => 50,
T_TRY => 50,
T_CATCH => 50,
T_SWITCH => 50,
T_SELF => 25,
T_PARENT => 25,
/*
Operators and arithmetic.
*/
T_BITWISE_AND => 8,
T_BITWISE_OR => 8,
T_MULTIPLY => 5,
T_DIVIDE => 5,
T_PLUS => 5,
T_MINUS => 5,
T_MODULUS => 5,
T_POWER => 5,
T_EQUAL => 5,
T_AND_EQUAL => 5,
T_CONCAT_EQUAL => 5,
T_DIV_EQUAL => 5,
T_MINUS_EQUAL => 5,
T_MOD_EQUAL => 5,
T_MUL_EQUAL => 5,
T_OR_EQUAL => 5,
T_PLUS_EQUAL => 5,
T_XOR_EQUAL => 5,
T_BOOLEAN_AND => 5,
T_BOOLEAN_OR => 5,
/*
Equality.
*/
T_IS_EQUAL => 5,
T_IS_NOT_EQUAL => 5,
T_IS_IDENTICAL => 5,
T_IS_NOT_IDENTICAL => 5,
T_IS_SMALLER_OR_EQUAL => 5,
T_IS_GREATER_OR_EQUAL => 5,
T_WHITESPACE => 0,
);
/**
* Tokens that represent assignments.
*
* @var array(int)
*/
public static $assignmentTokens = array(
T_EQUAL,
T_AND_EQUAL,
T_CONCAT_EQUAL,
T_DIV_EQUAL,
T_MINUS_EQUAL,
T_MOD_EQUAL,
T_MUL_EQUAL,
T_PLUS_EQUAL,
T_XOR_EQUAL,
);
/**
* Tokens that represent equality comparisons.
*
* @var array(int)
*/
public static $equalityTokens = array(
T_IS_EQUAL,
T_IS_NOT_EQUAL,
T_IS_IDENTICAL,
T_IS_NOT_IDENTICAL,
T_IS_SMALLER_OR_EQUAL,
T_IS_GREATER_OR_EQUAL,
);
/**
* Tokens that represent comparison operator.
*
* @var array(int)
*/
public static $comparisonTokens = array(
T_IS_EQUAL,
T_IS_IDENTICAL,
T_IS_NOT_EQUAL,
T_IS_NOT_IDENTICAL,
T_LESS_THAN,
T_GREATER_THAN,
T_IS_SMALLER_OR_EQUAL,
T_IS_GREATER_OR_EQUAL,
);
/**
* Tokens that represent arithmetic operators.
*
* @var array(int)
*/
public static $arithmeticTokens = array(
T_PLUS,
T_MINUS,
T_MULTIPLY,
T_DIVIDE,
T_MODULUS,
);
/**
* Tokens that represent casting.
*
* @var array(int)
*/
public static $castTokens = array(
T_INT_CAST,
T_STRING_CAST,
T_DOUBLE_CAST,
T_ARRAY_CAST,
T_BOOL_CAST,
T_OBJECT_CAST,
T_UNSET_CAST,
);
/**
* Token types that open parethesis.
*
* @var array(int)
*/
public static $parenthesisOpeners = array(
T_ARRAY,
T_FUNCTION,
T_WHILE,
T_FOR,
T_FOREACH,
T_SWITCH,
T_IF,
T_ELSEIF,
T_CATCH,
);
/**
* Tokens that are allowed to open scopes.
*
* @var array(int)
*/
public static $scopeOpeners = array(
T_CLASS,
T_INTERFACE,
T_FUNCTION,
T_IF,
T_SWITCH,
T_CASE,
T_DEFAULT,
T_WHILE,
T_ELSE,
T_ELSEIF,
T_FOR,
T_FOREACH,
T_DO,
T_TRY,
T_CATCH,
);
/**
* Tokens that represent scope modifiers.
*
* @var array(int)
*/
public static $scopeModifiers = array(
T_PRIVATE,
T_PUBLIC,
T_PROTECTED,
);
/**
* Tokens that perform operations.
*
* @var array(int)
*/
public static $operators = array(
T_MINUS,
T_PLUS,
T_MULTIPLY,
T_DIVIDE,
T_MODULUS,
T_POWER,
T_BITWISE_AND,
T_BITWISE_OR,
);
/**
* Tokens that perform operations.
*
* @var array(int)
*/
public static $blockOpeners = array(
T_OPEN_CURLY_BRACKET,
T_OPEN_SQUARE_BRACKET,
T_OPEN_PARENTHESIS,
);
/**
* Tokens that don't represent code.
*
* @var array(int)
*/
public static $emptyTokens = array(
T_WHITESPACE,
T_COMMENT,
T_DOC_COMMENT,
);
/**
* Tokens that are comments.
*
* @var array(int)
*/
public static $commentTokens = array(
T_COMMENT,
T_DOC_COMMENT,
);
/**
* Tokens that represent strings.
*
* Note that T_STRINGS are NOT represented in this list.
*
* @var array(int)
*/
public static $stringTokens = array(
T_CONSTANT_ENCAPSED_STRING,
T_DOUBLE_QUOTED_STRING,
);
/**
* Tokens that include files.
*
* @var array(int)
*/
public static $includeTokens = array(
T_REQUIRE_ONCE,
T_REQUIRE,
T_INCLUDE_ONCE,
T_INCLUDE,
);
/**
* A PHP_CodeSniffer_Tokens class cannot be constructed.
*
* Only static calls are allowed.
*/
private function __construct()
{
}//end __construct()
/**
* Returns the highest weighted token type.
*
* Tokens are weighted by their approximate frequency of appearance in code
* - the less frequently they appear in the code, the higher the weighting.
* For example T_CLASS tokens apprear very infrequently in a file, and
* therefore have a high weighting.
*
* Returns false if there are no weightings for any of the specified tokens.
*
* @param array(int) $tokens The token types to get the highest weighted
* type for.
*
* @return int The highest weighted token.
*/
public static function getHighestWeightedToken(array $tokens)
{
$highest = -1;
$highestType = false;
$weights = self::$weightings;
foreach ($tokens as $token) {
if (isset($weights[$token]) === true && $weights[$token] > $highest) {
$highest = $weights[$token];
$highestType = $token;
}
}
return $highestType;
}//end getHighestWeightedToken()
}//end class
?>

201
lib/thirdpartylibs.xml Normal file
View file

@ -0,0 +1,201 @@
<?xml version="1.0"?>
<libraries>
<library>
<location>adodb</location>
<name>AdoDB</name>
<license>GPL/BSD</license>
<version>5.08</version>
</library>
<library>
<location>alfresco</location>
<name>Alfresco</name>
<license>GPL</license>
<version></version>
</library>
<library>
<location>bennu</location>
<name>Bennu</name>
<license>LGPL</license>
<version></version>
</library>
<library>
<location>dragmath</location>
<name>DragMath</name>
<license>GPL</license>
<version>0.7.7</version>
</library>
<library>
<location>editor</location>
<name>TinyMCE</name>
<license>LGPL</license>
<version>3.2.3.1</version>
</library>
<library>
<location>evalmath</location>
<name>EvalMath</name>
<license>GPL</license>
<version></version>
</library>
<library>
<location>excel</location>
<name>Spreadsheet WriteExcel</name>
<license>LGPL</license>
<version></version>
</library>
<library>
<location>fpdf</location>
<name>FPDF</name>
<license>Freeware</license>
<version>1.53</version>
</library>
<library>
<location>geoip</location>
<name>GeoIP</name>
<license>LGPL</license>
<version>1.6</version>
</library>
<library>
<location>htmlpurifier</location>
<name>HTML Purifier</name>
<license>LGPL</license>
<version>3.3.0</version>
</library>
<library>
<location>jabber</location>
<name>XMPPHP</name>
<license>GPL</license>
<version></version>
</library>
<library>
<location>magpie</location>
<name>Magpie</name>
<license>GPL</license>
<version>0.72</version>
</library>
<library>
<location>mp3player</location>
<name></name>
<license></license>
<version></version>
</library>
<library>
<location>overlib</location>
<name>Overlib</name>
<license>http://www.bosrup.com/web/overlib/?License</license>
<version>4.21</version>
</library>
<library>
<location>pear</location>
<name>Multiple libraries</name>
<license>LGPL</license>
<version></version>
</library>
<library>
<location>phpmailer</location>
<name>PHPMailer</name>
<license>LGPL</license>
<version></version>
</library>
<library>
<location>phpxml</location>
<name>XML Library</name>
<license>http://keithdevens.com/software/license</license>
<version>1.2b</version>
</library>
<library>
<location>simplepie</location>
<name>SimplePie</name>
<license>BSD</license>
<version>1.1.3</version>
</library>
<library>
<location>simpletestlib</location>
<name>Simpletest</name>
<license>LGPL</license>
<version>1.0.1</version>
</library>
<library>
<location>snoopy</location>
<name>Snoopy</name>
<license>LGPL</license>
<version>1.2.4</version>
</library>
<library>
<location>soap</location>
<name>SOAP wrappers</name>
<license>GPL/LGPL</license>
<version></version>
</library>
<library>
<location>tcpdf</location>
<name>TCPDF</name>
<license>LGPL</license>
<version>4.0.015</version>
</library>
<library>
<location>typo3</location>
<name>Typo3</name>
<license>GPL</license>
<version>4.2.1</version>
</library>
<library>
<location>yui</location>
<name>YUI</name>
<license>BSD</license>
<version>2.7.0</version>
</library>
<library>
<location>zend</location>
<name>Zend Framework</name>
<license>new BSD</license>
<version>1.7.3</version>
</library>
<library>
<location>form</location>
<name>MoodleForms</name>
<license>GPL</license>
<version></version>
</library>
<library>
<location>base32.php</location>
<name>Base32 Library</name>
<license>GPL</license>
<version></version>
</library>
<library>
<location>odbc.php</location>
<name>ODBC server/client</name>
<license>Public Domain</license>
<version></version>
</library>
<library>
<location>html2text.php</location>
<name>HTML2Text</name>
<license>GPL</license>
<version>1.0.0</version>
</library>
<library>
<location>kses.php</location>
<name>KSES</name>
<license>GPL</license>
<version>0.2.2</version>
</library>
<library>
<location>markdown.php</location>
<name>Markdown original+extra</name>
<license></license>
<version>1.1.6</version>
</library>
<library>
<location>recaptchalib.php</location>
<name>ReCAPTCHA</name>
<license></license>
<version></version>
</library>
<library>
<location>xmlize.php</location>
<name>XMLize</name>
<license>GPL</license>
<version>1.0</version>
</library>
</libraries>