Skip to content

Commit

Permalink
Added support for multi-file sniffs, which get a list of all sniffed …
Browse files Browse the repository at this point in the history
…files in a run for checking. Using these sniffs dramatically increases the memory requirements of the scirpt as all the token arrays are left around in memory for later use. There may be an alternative way of doing this, but I also don't want to slow the multi-file sniffs down too much by re-parsing everything; but I may have to. I've also added a sample multi-file sniff and its new unit tests (a specific strucutre is required for these) that checks if the same class name is used multiple times throughout your files. Could be useful if you check your whole project, but mostly just a sample.

git-svn-id: http://svn.php.net/repository/pear/packages/PHP_CodeSniffer/trunk@263478 c90b9560-bf6c-de11-be94-00142212c4b1
  • Loading branch information
gsherwood committed Jul 25, 2008
1 parent c05c30d commit 3a742c0
Show file tree
Hide file tree
Showing 9 changed files with 474 additions and 128 deletions.
75 changes: 72 additions & 3 deletions CodeSniffer.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
throw new PHP_CodeSniffer_Exception('Interface PHP_CodeSniffer_Sniff not found');
}

if (interface_exists('PHP_CodeSniffer_MultiFileSniff', true) === false) {
throw new PHP_CodeSniffer_Exception('Interface PHP_CodeSniffer_MultiFileSniff not found');
}

/**
* PHP_CodeSniffer tokenises PHP code and detects violations of a
* defined set of coding standards.
Expand Down Expand Up @@ -89,6 +93,16 @@ class PHP_CodeSniffer
*/
protected $listeners = array();

/**
* The listeners array, indexed by token type.
*
* @var array()
*/
private $_tokenListeners = array(
'file' => array(),
'multifile' => array(),
);

/**
* An array of patterns to use for skipping files.
*
Expand Down Expand Up @@ -267,8 +281,12 @@ public function process($files, $standard, array $sniffs=array(), $local=false)
}

// Reset the members.
$this->listeners = array();
$this->files = array();
$this->listeners = array();
$this->files = array();
$this->_tokenListeners = array(
'file' => array(),
'multifile' => array(),
);

if (PHP_CODESNIFFER_VERBOSITY > 0) {
echo "Registering sniffs in $standard standard... ";
Expand All @@ -283,6 +301,31 @@ public function process($files, $standard, array $sniffs=array(), $local=false)
echo "DONE ($numSniffs sniffs registered)".PHP_EOL;
}

// Construct a list of listeners indexed by token being listened for.
foreach ($this->listeners as $listenerClass) {
$listener = new $listenerClass();

if (($listener instanceof PHP_CodeSniffer_Sniff) === true) {
$tokens = $listener->register();
if (is_array($tokens) === false) {
$msg = "Sniff $listenerClass register() method must return an array";
throw new PHP_CodeSniffer_Exception($msg);
}

foreach ($tokens as $token) {
if (isset($this->_tokenListeners['file'][$token]) === false) {
$this->_tokenListeners['file'][$token] = array();
}

if (in_array($listener, $this->_tokenListeners['file'][$token], true) === false) {
$this->_tokenListeners['file'][$token][] = $listener;
}
}
} else if (($listener instanceof PHP_CodeSniffer_MultiFileSniff) === true) {
$this->_tokenListeners['multifile'][] = $listener;
}
}//end foreach

foreach ($files as $file) {
$this->file = $file;
if (is_dir($this->file) === true) {
Expand All @@ -292,6 +335,14 @@ public function process($files, $standard, array $sniffs=array(), $local=false)
}
}

// Now process the multi-file sniffs, assuming there are
// multiple files being sniffed.
if (count($files) > 1) {
foreach ($this->_tokenListeners['multifile'] as $listener) {
$listener->process($this->files);
}
}

}//end process()


Expand Down Expand Up @@ -619,10 +670,16 @@ public function processFile($file, $contents=null)
}
}

$phpcsFile = new PHP_CodeSniffer_File($file, $this->listeners, $this->allowedFileExtensions);
$phpcsFile = new PHP_CodeSniffer_File($file, $this->_tokenListeners['file'], $this->allowedFileExtensions);
$this->addFile($phpcsFile);
$phpcsFile->start($contents);

// Clean up the test if we can to save memory. This can't be done if
// we need to leave the files around for multi-file sniffs.
if (empty($this->_tokenListeners['multifile']) === true) {
$phpcsFile->cleanUp();
}

if (PHP_CODESNIFFER_VERBOSITY > 0) {
$timeTaken = (time() - $startTime);
if ($timeTaken === 0) {
Expand Down Expand Up @@ -1099,6 +1156,18 @@ public function getSniffs()
}//end getSniffs()


/**
* Gets the array of PHP_CodeSniffer_Sniff's indexed by token type.
*
* @return array()
*/
public function getTokenSniffs()
{
return $this->_tokenListeners;

}//end getTokenSniffs()


/**
* Takes a token produced from <code>token_get_all()</code> and produces a
* more uniform token.
Expand Down
27 changes: 12 additions & 15 deletions CodeSniffer/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,19 +243,8 @@ class PHP_CodeSniffer_File
*/
public function __construct($file, array $listeners, array $tokenizers)
{
foreach ($listeners as $listenerClass) {
$listener = new $listenerClass();
$tokens = $listener->register();

if (is_array($tokens) === false) {
$msg = "Sniff $listenerClass register() method must return an array";
throw new PHP_CodeSniffer_Exception($msg);
}

$this->addTokenListener($listener, $tokens);
}

$this->_file = trim($file);
$this->_listeners = $listeners;
$this->tokenizers = $tokenizers;

}//end __construct()
Expand Down Expand Up @@ -391,12 +380,20 @@ public function start($contents=null)
echo "\t*** END TOKEN PROCESSING ***".PHP_EOL;
}

// We don't need the tokens any more, so get rid of them
// to save some memory.
}//end start()


/**
* Remove vars stored in this sniff that are no longer required.
*
* @return void
*/
public function cleanUp()
{
$this->_tokens = null;
$this->_listeners = null;

}//end start()
}//end cleanUp()


/**
Expand Down
48 changes: 48 additions & 0 deletions CodeSniffer/MultiFileSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
* Represents a PHP_CodeSniffer sniff for sniffing coding standards.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <[email protected]>
* @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 multi-file sniff for sniffing coding standards.
*
* A multi-file sniff is called after all files have been checked using the
* regular sniffs. The process() method is passed an array of PHP_CodeSniffer_File
* objects, one for each file checked during the script run.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <[email protected]>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: @package_version@
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
interface PHP_CodeSniffer_MultiFileSniff
{


/**
* Called once per script run to allow for processing of this sniff.
*
* @param array(PHP_CodeSniffer_File) $files The PHP_CodeSniffer files processed
* during the script run.
*
* @return void
*/
public function process(array $files);


}//end interface

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
/**
* Reports errors if the same class or interface name is used in multiple files.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <[email protected]>
* @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
*/

/**
* Reports errors if the same class or interface name is used in multiple files.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <[email protected]>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: @package_version@
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Generic_Sniffs_Classes_DuplicateClassNameSniff implements PHP_CodeSniffer_MultiFileSniff
{


/**
* Called once per script run to allow for processing of this sniff.
*
* @param array(PHP_CodeSniffer_File) $files The PHP_CodeSniffer files processed
* during the script run.
*
* @return void
*/
public function process(array $files)
{
$foundClasses = array();

foreach ($files as $phpcsFile) {
$tokens = $phpcsFile->getTokens();

$stackPtr = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE), 0);
while ($stackPtr !== false) {
$nameToken = $phpcsFile->findNext(T_STRING, $stackPtr);
$name = $tokens[$nameToken]['content'];
$compareName = strtolower($name);
if (isset($foundClasses[$compareName]) === true) {
$type = strtolower($tokens[$stackPtr]['content']);
$file = $foundClasses[$compareName]['file'];
$line = $foundClasses[$compareName]['line'];
$error = "Duplicate $type name \"$name\" found; first defined in $file on line $line";
$phpcsFile->addWarning($error, $stackPtr);
} else {
$foundClasses[$compareName] = array(
'file' => $phpcsFile->getFilename(),
'line' => $tokens[$stackPtr]['line'],
);
}

$stackPtr = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE), ($stackPtr + 1));
}//end while

}//end foreach

}//end process()


}//end class

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
class MyClass {}
class YourClass {}
interface MyInterface {}
interface YourInterface {}
class MyClass {}
interface MyInterface {}
?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
class MyClass {}
interface MyInterface {}
?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php
/**
* Unit test class for the DuplicateClassName multi-file sniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <[email protected]>
* @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
*/

/**
* Unit test class for the DuplicateClassName multi-file sniff.
*
* A multi-file sniff unit test checks a .1.inc and a .2.inc file for expected violations
* of a single coding standard. Expected errors and warnings are stored in this class.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Greg Sherwood <[email protected]>
* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
* @version Release: @package_version@
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class Generic_Tests_Classes_DuplicateClassNameUnitTest extends AbstractSniffUnitTest
{


/**
* Returns the lines where errors should occur.
*
* The key of the array should represent the line number and the value
* should represent the number of errors that should occur on that line.
*
* @return array(int => int)
*/
public function getErrorList()
{
return array();

}//end getErrorList()


/**
* Returns the lines where warnings should occur.
*
* The key of the array should represent the line number and the value
* should represent the number of warnings that should occur on that line.
*
* @return array(int => int)
*/
public function getWarningList($testFile='')
{
switch ($testFile) {
case 'DuplicateClassNameUnitTest.1.inc':
return array(
6 => 1,
7 => 1,
);
break;
case 'DuplicateClassNameUnitTest.2.inc':
return array(
2 => 1,
3 => 1,
);
break;
default:
return array();
break;
}//end switch

}//end getWarningList()


}//end class

?>
Loading

0 comments on commit 3a742c0

Please sign in to comment.