]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-19247 Added the PHP CodeSniffer tool and lib/thirdpartylibs.xml
authornicolasconnault <nicolasconnault>
Tue, 19 May 2009 15:22:43 +0000 (15:22 +0000)
committernicolasconnault <nicolasconnault>
Tue, 19 May 2009 15:22:43 +0000 (15:22 +0000)
67 files changed:
lib/pear/PHP/CodeSniffer.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CLI.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/AbstractDocElement.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/AbstractParser.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/ClassCommentParser.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/CommentElement.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/DocElement.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/FunctionCommentParser.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/MemberCommentParser.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/PairElement.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/ParameterElement.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/ParserException.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/CommentParser/SingleElement.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/DocGenerators/Generator.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/DocGenerators/HTML.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/DocGenerators/Text.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Exception.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/File.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Sniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/AbstractPatternSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/AbstractScopeSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/AbstractVariableSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/CodingStandard.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/IncorrectPatternException.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/MoodleCodingStandard.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Classes/ClassDeclarationSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/EmptyStatementSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/ForLoopShouldBeWhileLoopSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/ForLoopWithTestFunctionCallSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/JumbledIncrementerSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UnconditionalIfStatementSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UnnecessaryFinalModifierSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UnusedFunctionParameterSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UselessOverridingMethodSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/ClassCommentSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/FileCommentSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/FunctionCommentSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/InlineCommentSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/ControlStructures/ControlSignatureSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/ControlStructures/InlineControlStructureSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Files/IncludingFileSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Files/LineEndingsSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Files/LineLengthSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Formatting/SpaceAfterCastSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/FunctionCallSignatureSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/FunctionDeclarationSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/LowercaseFunctionKeywordsSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/ValidDefaultValueSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/UpperCaseConstantNameSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/ValidClassNameSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/ValidFunctionNameSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/ValidVariableNameSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/PHP/DisallowShortOpenTagSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/PHP/LowerCaseConstantSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/PHP/LowercasePHPFunctionsSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Strings/DoubleQuoteUsageSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Strings/EchoedStringsSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/DisallowTabIndentSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/MemberVarSpacingSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/ScopeClosingBraceSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/ScopeIndentSniff.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Tokenizers/JS.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Tokenizers/PHP.php [new file with mode: 0644]
lib/pear/PHP/CodeSniffer/Tokens.php [new file with mode: 0644]
lib/thirdpartylibs.xml [new file with mode: 0644]
phpcs [new file with mode: 0755]

diff --git a/lib/pear/PHP/CodeSniffer.php b/lib/pear/PHP/CodeSniffer.php
new file mode 100644 (file)
index 0000000..2c1efaf
--- /dev/null
@@ -0,0 +1,1665 @@
+<?php
+/**
+ * PHP_CodeSniffer tokenises PHP code and detects violations of a
+ * defined set of 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
+ */
+
+spl_autoload_register(array('PHP_CodeSniffer', 'autoload'));
+
+if (class_exists('PHP_CodeSniffer_Exception', true) === false) {
+    throw new Exception('Class PHP_CodeSniffer_Exception not found');
+}
+
+if (class_exists('PHP_CodeSniffer_File', true) === false) {
+    throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_File not found');
+}
+
+if (class_exists('PHP_CodeSniffer_Tokens', true) === false) {
+    throw new PHP_CodeSniffer_Exception('Class PHP_CodeSniffer_Tokens not found');
+}
+
+if (interface_exists('PHP_CodeSniffer_Sniff', true) === false) {
+    throw new PHP_CodeSniffer_Exception('Interface PHP_CodeSniffer_Sniff not found');
+}
+
+/**
+ * PHP_CodeSniffer tokenises PHP code and detects violations of a
+ * defined set of coding standards.
+ *
+ * Standards are specified by classes that implement the PHP_CodeSniffer_Sniff
+ * interface. A sniff registers what token types it wishes to listen for, then
+ * PHP_CodeSniffer encounters that token, the sniff is invoked and passed
+ * information about where the token was found in the stack, and the token stack
+ * itself.
+ *
+ * Sniff files and their containing class must be prefixed with Sniff, and
+ * have an extension of .php.
+ *
+ * Multiple PHP_CodeSniffer operations can be performed by re-calling the
+ * process function with different parameters.
+ *
+ * @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
+{
+
+    /**
+     * The file or directory that is currently being processed.
+     *
+     * @var string
+     */
+    protected $file = array();
+
+    /**
+     * The directory to search for sniffs in.
+     *
+     * @var string
+     */
+    protected $standardDir = '';
+
+    /**
+     * The files that have been processed.
+     *
+     * @var array(PHP_CodeSniffer_File)
+     */
+    protected $files = array();
+
+    /**
+     * The listeners array.
+     *
+     * @var array(PHP_CodeSniffer_Sniff)
+     */
+    protected $listeners = array();
+
+    /**
+     * An array of patterns to use for skipping files.
+     *
+     * @var array()
+     */
+    protected $ignorePatterns = array();
+
+    /**
+     * An array of extensions for files we will check.
+     *
+     * @var array
+     */
+    public $allowedFileExtensions = array(
+                                     'php' => 'PHP',
+                                     'inc' => 'PHP',
+                                     'js'  => 'JS',
+                                    );
+
+    /**
+     * An array of variable types for param/var we will check.
+     *
+     * @var array(string)
+     */
+    public static $allowedTypes = array(
+                                   'array',
+                                   'boolean',
+                                   'float',
+                                   'integer',
+                                   'mixed',
+                                   'object',
+                                   'string',
+                                  );
+
+
+    /**
+     * Constructs a PHP_CodeSniffer object.
+     *
+     * @param int $verbosity The verbosity level.
+     *                       1: Print progress information.
+     *                       2: Print developer debug information.
+     * @param int $tabWidth  The number of spaces each tab represents.
+     *                       If greater than zero, tabs will be replaced
+     *                       by spaces before testing each file.
+     *
+     * @see process()
+     */
+    public function __construct($verbosity=0, $tabWidth=0)
+    {
+        define('PHP_CODESNIFFER_VERBOSITY', $verbosity);
+        define('PHP_CODESNIFFER_TAB_WIDTH', $tabWidth);
+
+        // Change into a directory that we know about to stop any
+        // relative path conflicts.
+        chdir(dirname(__FILE__).'/CodeSniffer/');
+
+    }//end __construct()
+
+
+    /**
+     * Autoload static method for loading classes and interfaces.
+     *
+     * @param string $className The name of the class or interface.
+     *
+     * @return void
+     */
+    public static function autoload($className)
+    {
+        if (substr($className, 0, 4) === 'PHP_') {
+            $newClassName = substr($className, 4);
+        } else {
+            $newClassName = $className;
+        }
+
+        $path = str_replace('_', '/', $newClassName).'.php';
+
+        if (is_file(dirname(__FILE__).'/'.$path) === true) {
+            // Check standard file locations based on class name.
+            include dirname(__FILE__).'/'.$path;
+        } else if (is_file(dirname(__FILE__).'/CodeSniffer/Standards/'.$path) === true) {
+            // Check for included sniffs.
+            include dirname(__FILE__).'/CodeSniffer/Standards/'.$path;
+        } else {
+            // Everything else.
+            @include $path;
+        }
+
+    }//end autoload()
+
+
+    /**
+     * Sets an array of file extensions that we will allow checking of.
+     *
+     * If the extension is one of the defaults, a specific tokenizer
+     * will be used. Otherwise, the PHP tokenizer will be used for
+     * all extensions passed.
+     *
+     * @param array $extensions An array of file extensions.
+     *
+     * @return void
+     */
+    public function setAllowedFileExtensions(array $extensions)
+    {
+        $newExtensions = array();
+        foreach ($extensions as $ext) {
+            if (isset($this->allowedFileExtensions[$ext]) === true) {
+                $newExtensions[$ext] = $this->allowedFileExtensions[$ext];
+            } else {
+                $newExtensions[$ext] = 'PHP';
+            }
+        }
+
+        $this->allowedFileExtensions = $newExtensions;
+
+    }//end setAllowedFileExtensions()
+
+
+    /**
+     * Sets an array of ignore patterns that we use to skip files and folders.
+     *
+     * Patterns are not case sensitive.
+     *
+     * @param array $patterns An array of ignore patterns.
+     *
+     * @return void
+     */
+    public function setIgnorePatterns(array $patterns)
+    {
+        $this->ignorePatterns = $patterns;
+
+    }//end setIgnorePatterns()
+
+
+    /**
+     * Adds a file to the list of checked files.
+     *
+     * Checked files are used to generate error reports after the run.
+     *
+     * @param PHP_CodeSniffer_File $phpcsFile The file to add.
+     *
+     * @return void
+     */
+    public function addFile(PHP_CodeSniffer_File $phpcsFile)
+    {
+        $this->files[] = $phpcsFile;
+
+    }//end addFile()
+
+
+    /**
+     * Processes the files/directories that PHP_CodeSniffer was constructed with.
+     *
+     * @param string|array $files    The files and directories to process. For
+     *                               directories, each sub directory will also
+     *                               be traversed for source files.
+     * @param string       $standard The set of code sniffs we are testing
+     *                               against.
+     * @param array        $sniffs   The sniff names to restrict the allowed
+     *                               listeners to.
+     * @param boolean      $local    If true, don't recurse into directories.
+     *
+     * @return void
+     * @throws PHP_CodeSniffer_Exception If files or standard are invalid.
+     */
+    public function process($files, $standard, array $sniffs=array(), $local=false)
+    {
+        if (is_array($files) === false) {
+            if (is_string($files) === false || $files === null) {
+                throw new PHP_CodeSniffer_Exception('$file must be a string');
+            }
+
+            $files = array($files);
+        }
+
+        if (is_string($standard) === false || $standard === null) {
+            throw new PHP_CodeSniffer_Exception('$standard must be a string');
+        }
+
+        // Reset the members.
+        $this->listeners = array();
+        $this->files     = array();
+
+        if (PHP_CODESNIFFER_VERBOSITY > 0) {
+            echo "Registering sniffs in $standard standard... ";
+            if (PHP_CODESNIFFER_VERBOSITY > 2) {
+                echo PHP_EOL;
+            }
+        }
+
+        $this->setTokenListeners($standard, $sniffs);
+        if (PHP_CODESNIFFER_VERBOSITY > 0) {
+            $numSniffs = count($this->listeners);
+            echo "DONE ($numSniffs sniffs registered)".PHP_EOL;
+        }
+
+        foreach ($files as $file) {
+            $this->file = $file;
+            if (is_dir($this->file) === true) {
+                $this->processFiles($this->file, $local);
+            } else {
+                $this->processFile($this->file);
+            }
+        }
+
+    }//end process()
+
+
+    /**
+     * Gets installed sniffs in the coding standard being used.
+     *
+     * Traverses the standard directory for classes that implement the
+     * PHP_CodeSniffer_Sniff interface asks them to register. Each of the
+     * sniff's class names must be exact as the basename of the sniff file.
+     *
+     * Returns an array of sniff class names.
+     *
+     * @param string $standard The name of the coding standard we are checking.
+     * @param array  $sniffs   The sniff names to restrict the allowed
+     *                         listeners to.
+     *
+     * @return array
+     * @throws PHP_CodeSniffer_Exception If any of the tests failed in the
+     *                                   registration process.
+     */
+    public function getTokenListeners($standard, array $sniffs=array())
+    {
+        if (is_dir($standard) === true) {
+            // This is a custom standard.
+            $this->standardDir = $standard;
+            $standard          = basename($standard);
+        } else {
+            $this->standardDir = realpath(dirname(__FILE__).'/CodeSniffer/Standards/'.$standard);
+        }
+
+        $files = self::getSniffFiles($this->standardDir, $standard);
+
+        if (empty($sniffs) === false) {
+            // Convert the allowed sniffs to lower case so
+            // they are easier to check.
+            foreach ($sniffs as &$sniff) {
+                $sniff = strtolower($sniff);
+            }
+        }
+
+        $listeners = array();
+
+        foreach ($files as $file) {
+            // Work out where the position of /StandardName/Sniffs/... is
+            // so we can determine what the class will be called.
+            $sniffPos = strrpos($file, DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR);
+            if ($sniffPos === false) {
+                continue;
+            }
+
+            $slashPos = strrpos(substr($file, 0, $sniffPos), DIRECTORY_SEPARATOR);
+            if ($slashPos === false) {
+                continue;
+            }
+
+            $className = substr($file, ($slashPos + 1));
+            $className = substr($className, 0, -4);
+            $className = str_replace(DIRECTORY_SEPARATOR, '_', $className);
+
+            include_once $file;
+
+            // If they have specified a list of sniffs to restrict to, check
+            // to see if this sniff is allowed.
+            $allowed = in_array(strtolower($className), $sniffs);
+            if (empty($sniffs) === false && $allowed === false) {
+                continue;
+            }
+
+            $listeners[] = $className;
+
+            if (PHP_CODESNIFFER_VERBOSITY > 2) {
+                echo "\tRegistered $className".PHP_EOL;
+            }
+        }//end foreach
+
+        return $listeners;
+
+    }//end getTokenListeners()
+
+
+    /**
+     * Sets installed sniffs in the coding standard being used.
+     *
+     * @param string $standard The name of the coding standard we are checking.
+     * @param array  $sniffs   The sniff names to restrict the allowed
+     *                         listeners to.
+     *
+     * @return null
+     */
+    public function setTokenListeners($standard, array $sniffs=array())
+    {
+        $this->listeners = $this->getTokenListeners($standard, $sniffs);
+
+    }//end setTokenListeners
+
+
+    /**
+     * Return a list of sniffs that a coding standard has defined.
+     *
+     * Sniffs are found by recursing the standard directory and also by
+     * asking the standard for included sniffs.
+     *
+     * @param string $dir      The directory where to look for the files.
+     * @param string $standard The name of the coding standard. If NULL, no
+     *                         included sniffs will be checked for.
+     *
+     * @return array
+     * @throws PHP_CodeSniffer_Exception If an included or excluded sniff does
+     *                                   not exist.
+     */
+    public static function getSniffFiles($dir, $standard=null)
+    {
+        $di = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
+
+        $ownSniffs      = array();
+        $includedSniffs = array();
+        $excludedSniffs = array();
+
+        foreach ($di as $file) {
+            // Skip hidden files.
+            if (substr($file->getFilename(), 0, 1) === '.') {
+                continue;
+            }
+
+            // We are only interested in PHP and sniff files.
+            $fileParts = explode('.', $file);
+            if (array_pop($fileParts) !== 'php') {
+                continue;
+            }
+
+            $basename = basename($file, '.php');
+            if (substr($basename, -5) !== 'Sniff') {
+                continue;
+            }
+
+            $ownSniffs[] = $file->getPathname();
+        }//end foreach
+
+        // Load the standard class and ask it for a list of external
+        // sniffs to include in the standard.
+        if ($standard !== null && is_file("$dir/{$standard}CodingStandard.php") === true) {
+            include_once "$dir/{$standard}CodingStandard.php";
+            $standardClassName = "PHP_CodeSniffer_Standards_{$standard}_{$standard}CodingStandard";
+            $standardClass     = new $standardClassName;
+
+            $included = $standardClass->getIncludedSniffs();
+            foreach ($included as $sniff) {
+                if (is_dir($sniff) === true) {
+                    // Trying to include from a custom standard.
+                    $sniffDir = $sniff;
+                    $sniff    = basename($sniff);
+                } else if (is_file($sniff) === true) {
+                    // Trying to include a custom sniff.
+                    $sniffDir = $sniff;
+                } else {
+                    $sniffDir = realpath(dirname(__FILE__)."/CodeSniffer/Standards/$sniff");
+                    if ($sniffDir === false) {
+                        throw new PHP_CodeSniffer_Exception("Included sniff $sniff does not exist");
+                    }
+                }
+
+                if (is_dir($sniffDir) === true) {
+                    if (self::isInstalledStandard($sniff) === true) {
+                        // We are including a whole coding standard.
+                        $includedSniffs = array_merge($includedSniffs, self::getSniffFiles($sniffDir, $sniff));
+                    } else {
+                        // We are including a whole directory of sniffs.
+                        $includedSniffs = array_merge($includedSniffs, self::getSniffFiles($sniffDir));
+                    }
+                } else {
+                    if (substr($sniffDir, -9) !== 'Sniff.php') {
+                        throw new PHP_CodeSniffer_Exception("Included sniff $sniff does not exist");
+                    }
+
+                    $includedSniffs[] = $sniffDir;
+                }
+            }//end foreach
+
+            $excluded = $standardClass->getExcludedSniffs();
+            foreach ($excluded as $sniff) {
+                if (is_dir($sniff) === true) {
+                    // Trying to exclude from a custom standard.
+                    $sniffDir = $sniff;
+                    $sniff    = basename($sniff);
+                } else if (is_file($sniff) === true) {
+                    // Trying to exclude a custom sniff.
+                    $sniffDir = $sniff;
+                } else {
+                    $sniffDir = realpath(dirname(__FILE__)."/CodeSniffer/Standards/$sniff");
+                    if ($sniffDir === false) {
+                        throw new PHP_CodeSniffer_Exception("Excluded sniff $sniff does not exist");
+                    }
+                }
+
+                if (is_dir($sniffDir) === true) {
+                    if (self::isInstalledStandard($sniff) === true) {
+                        // We are excluding a whole coding standard.
+                        $excludedSniffs = array_merge($excludedSniffs, self::getSniffFiles($sniffDir, $sniff));
+                    } else {
+                        // We are excluding a whole directory of sniffs.
+                        $excludedSniffs = array_merge($excludedSniffs, self::getSniffFiles($sniffDir));
+                    }
+                } else {
+                    if (substr($sniffDir, -9) !== 'Sniff.php') {
+                        throw new PHP_CodeSniffer_Exception("Excluded sniff $sniff does not exist");
+                    }
+
+                    $excludedSniffs[] = $sniffDir;
+                }
+            }//end foreach
+        }//end if
+
+        // Merge our own sniff list with our exnternally included
+        // sniff list, but filter out any excluded sniffs.
+        $files = array();
+        foreach (array_merge($ownSniffs, $includedSniffs) as $sniff) {
+            if (in_array($sniff, $excludedSniffs) === true) {
+                continue;
+            } else {
+                $files[] = $sniff;
+            }
+        }
+
+        return $files;
+
+    }//end getSniffFiles()
+
+
+    /**
+     * Run the code sniffs over each file in a given directory.
+     *
+     * Recusively reads the specified directory and performs the PHP_CodeSniffer
+     * sniffs on each source file found within the directories.
+     *
+     * @param string  $dir   The directory to process.
+     * @param boolean $local If true, only process files in this directory, not
+     *                       sub directories.
+     *
+     * @return void
+     * @throws Exception If there was an error opening the directory.
+     */
+    public function processFiles($dir, $local=false)
+    {
+        try {
+            if ($local === true) {
+                $di = new DirectoryIterator($dir);
+            } else {
+                $di = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
+            }
+
+            // MOODLE CODE: If thirdpartylibs.xml is found, add these values to the ignored array
+            // first iteration to find thirdpartylibs.xml
+            foreach ($di as $file) {
+                if ($file->getFileName() == 'thirdpartylibs.xml') {
+                    $xml = simplexml_load_file($file->getPathName());
+                    foreach ($xml->library as $libobject) {
+                        $this->ignorePatterns[] = (string) $libobject->location;
+                    }
+                }
+            }
+
+            foreach ($di as $file) {
+                $filePath = realpath($file->getPathname());
+
+                if (is_dir($filePath) === true) {
+                    continue;
+                }
+
+                // Check that the file's extension is one we are checking.
+                // Note that because we are doing a whole directory, we
+                // are strick about checking the extension and we don't
+                // let files with no extension through.
+                $fileParts = explode('.', $file);
+                $extension = array_pop($fileParts);
+                if ($extension === $file) {
+                    continue;
+                }
+
+                if (isset($this->allowedFileExtensions[$extension]) === false) {
+                    continue;
+                }
+
+                $this->processFile($filePath);
+            }//end foreach
+        } catch (Exception $e) {
+            $trace    = $e->getTrace();
+            $filename = $trace[0]['args'][0];
+            $error    = 'An error occurred during processing; checking has been aborted. The error message was: '.$e->getMessage();
+
+            $phpcsFile = new PHP_CodeSniffer_File($filename, $this->listeners, $this->allowedFileExtensions);
+            $this->addFile($phpcsFile);
+            $phpcsFile->addError($error, null);
+            return;
+        }
+
+    }//end processFiles()
+
+
+    /**
+     * Run the code sniffs over a single given file.
+     *
+     * Processes the file and runs the PHP_CodeSniffer sniffs to verify that it
+     * conforms with the standard.
+     *
+     * @param string $file     The file to process.
+     * @param string $contents The contents to parse. If NULL, the content
+     *                         is taken from the file system.
+     *
+     * @return void
+     * @throws PHP_CodeSniffer_Exception If the file could not be processed.
+     */
+    public function processFile($file, $contents=null)
+    {
+        if (is_null($contents) === true && file_exists($file) === false) {
+            throw new PHP_CodeSniffer_Exception("Source file $file does not exist");
+        }
+
+        // If the file's path matches one of our ignore patterns, skip it.
+        foreach ($this->ignorePatterns as $pattern) {
+            $replacements = array(
+                             '\\,' => ',',
+                             '*'   => '.*',
+                            );
+
+            $pattern = strtr($pattern, $replacements);
+            if (preg_match("|{$pattern}|i", $file) === 1) {
+                return;
+            }
+        }
+
+        if (PHP_CODESNIFFER_VERBOSITY > 0) {
+            $startTime = time();
+            echo 'Processing '.basename($file).' ';
+            if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                echo PHP_EOL;
+            }
+        }
+
+        $phpcsFile = new PHP_CodeSniffer_File($file, $this->listeners, $this->allowedFileExtensions);
+        $this->addFile($phpcsFile);
+        $phpcsFile->start($contents);
+
+        if (PHP_CODESNIFFER_VERBOSITY > 0) {
+            $timeTaken = (time() - $startTime);
+            if ($timeTaken === 0) {
+                echo 'DONE in < 1 second';
+            } else if ($timeTaken === 1) {
+                echo 'DONE in 1 second';
+            } else {
+                echo "DONE in $timeTaken seconds";
+            }
+
+            $errors   = $phpcsFile->getErrorCount();
+            $warnings = $phpcsFile->getWarningCount();
+            echo " ($errors errors, $warnings warnings)".PHP_EOL;
+        }
+
+    }//end processFile()
+
+
+    /**
+     * Pre-process and package errors and warnings for all files.
+     *
+     * Used by error reports to get a packaged list of all errors and
+     * warnings in each file.
+     *
+     * @param boolean $showWarnings Show warnings as well as errors.
+     *
+     * @return array
+     */
+    public function prepareErrorReport($showWarnings=true)
+    {
+        $report = array(
+                   'totals' => array(
+                                'warnings' => 0,
+                                'errors'   => 0,
+                               ),
+                   'files'  => array(),
+                  );
+
+        foreach ($this->files as $file) {
+            $warnings    = $file->getWarnings();
+            $errors      = $file->getErrors();
+            $numWarnings = $file->getWarningCount();
+            $numErrors   = $file->getErrorCount();
+            $filename    = $file->getFilename();
+
+            $report['files'][$filename] = array(
+                                           'errors'   => 0,
+                                           'warnings' => 0,
+                                           'messages' => array(),
+                                          );
+
+            if ($numErrors === 0 && $numWarnings === 0) {
+                // Prefect score!
+                continue;
+            }
+
+            if ($numErrors === 0 && $showWarnings === false) {
+                // Prefect score (sort of).
+                continue;
+            }
+
+            $report['files'][$filename]['errors'] = $numErrors;
+            if ($showWarnings === true) {
+                $report['files'][$filename]['warnings'] = $numWarnings;
+            } else {
+                $report['files'][$filename]['warnings'] = 0;
+            }
+
+            $report['totals']['errors'] += $numErrors;
+            if ($showWarnings === true) {
+                $report['totals']['warnings'] += $numWarnings;
+            }
+
+            // Merge errors and warnings.
+            foreach ($errors as $line => $lineErrors) {
+                foreach ($lineErrors as $column => $colErrors) {
+                    $newErrors = array();
+                    foreach ($colErrors as $message) {
+                        $newErrors[] = array(
+                                        'message' => $message,
+                                        'type'    => 'ERROR',
+                                       );
+                    }
+
+                    $errors[$line][$column] = $newErrors;
+                }
+            }//end foreach
+
+            if ($showWarnings === true) {
+                foreach ($warnings as $line => $lineWarnings) {
+                    foreach ($lineWarnings as $column => $colWarnings) {
+                        $newWarnings = array();
+                        foreach ($colWarnings as $message) {
+                            $newWarnings[] = array(
+                                              'message' => $message,
+                                              'type'    => 'WARNING',
+                                             );
+                        }
+
+                        if (isset($errors[$line]) === false) {
+                            $errors[$line] = array();
+                        }
+
+                        if (isset($errors[$line][$column]) === true) {
+                            $errors[$line][$column] = array_merge($newWarnings, $errors[$line][$column]);
+                        } else {
+                            $errors[$line][$column] = $newWarnings;
+                        }
+                    }
+                }//end foreach
+            }//end if
+
+            ksort($errors);
+
+            $report['files'][$filename]['messages'] = $errors;
+
+        }//end foreach
+
+        return $report;
+
+    }//end prepareErrorReport()
+
+
+    /**
+     * Prints all errors and warnings for each file processed, in an XML format.
+     *
+     * Errors and warnings are displayed together, grouped by file.
+     *
+     * @param boolean $showWarnings Show warnings as well as errors.
+     *
+     * @return int The number of error and warning messages shown.
+     */
+    public function printXMLErrorReport($showWarnings=true)
+    {
+        echo '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
+        echo '<phpcs version="1.1.0">'.PHP_EOL;
+
+        $errorsShown = 0;
+
+        $report = $this->prepareErrorReport($showWarnings);
+        foreach ($report['files'] as $filename => $file) {
+            if (empty($file['messages']) === true) {
+                continue;
+            }
+
+            echo ' <file name="'.$filename.'" errors="'.$file['errors'].'" warnings="'.$file['warnings'].'">'.PHP_EOL;
+
+            foreach ($file['messages'] as $line => $lineErrors) {
+                foreach ($lineErrors as $column => $colErrors) {
+                    foreach ($colErrors as $error) {
+                        $error['type'] = strtolower($error['type']);
+                        echo '  <'.$error['type'].' line="'.$line.'" column="'.$column.'">';
+                        echo htmlspecialchars($error['message']).'</'.$error['type'].'>'.PHP_EOL;
+                        $errorsShown++;
+                    }
+                }
+            }//end foreach
+
+            echo ' </file>'.PHP_EOL;
+
+        }//end foreach
+
+        echo '</phpcs>'.PHP_EOL;
+
+        return $errorsShown;
+
+    }//end printXMLErrorReport()
+
+
+    /**
+     * Prints all errors and warnings for each file processed, in a Checkstyle XML format.
+     *
+     * Errors and warnings are displayed together, grouped by file.
+     *
+     * @param boolean $showWarnings Show warnings as well as errors.
+     *
+     * @return int The number of error and warning messages shown.
+     */
+    public function printCheckstyleErrorReport($showWarnings=true)
+    {
+        echo '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
+        echo '<checkstyle version="1.1.0">'.PHP_EOL;
+
+        $errorsShown = 0;
+
+        $report = $this->prepareErrorReport($showWarnings);
+        foreach ($report['files'] as $filename => $file) {
+            echo ' <file name="'.$filename.'">'.PHP_EOL;
+
+            foreach ($file['messages'] as $line => $lineErrors) {
+                foreach ($lineErrors as $column => $colErrors) {
+                    foreach ($colErrors as $error) {
+                        $error['type'] = strtolower($error['type']);
+                        echo '  <error';
+                        echo ' line="'.$line.'" column="'.$column.'"';
+                        echo ' severity="'.$error['type'].'"';
+                        $message = utf8_encode(htmlspecialchars($error['message']));
+                        echo ' message="'.$message.'"';
+                        echo '/>'.PHP_EOL;
+                        $errorsShown++;
+                    }
+                }
+            }//end foreach
+
+            echo ' </file>'.PHP_EOL;
+
+        }//end foreach
+
+        echo '</checkstyle>'.PHP_EOL;
+
+        return $errorsShown;
+
+    }//end printCheckstyleErrorReport()
+
+
+    /**
+     * Prints all errors and warnings for each file processed, in a CSV format.
+     *
+     * @param boolean $showWarnings Show warnings as well as errors.
+     *
+     * @return int The number of error and warning messages shown.
+     */
+    public function printCSVErrorReport($showWarnings=true)
+    {
+        echo 'File,Line,Column,Severity,Message'.PHP_EOL;
+
+        $errorsShown = 0;
+
+        $report = $this->prepareErrorReport($showWarnings);
+        foreach ($report['files'] as $filename => $file) {
+            foreach ($file['messages'] as $line => $lineErrors) {
+                foreach ($lineErrors as $column => $colErrors) {
+                    foreach ($colErrors as $error) {
+                        $filename = str_replace('"', '\"', $filename);
+                        $message  = str_replace('"', '\"', $error['message']);
+                        $type     = strtolower($error['type']);
+                        echo "\"$filename\",$line,$column,$type,\"$message\"".PHP_EOL;
+                        $errorsShown++;
+                    }
+                }
+            }//end foreach
+        }//end foreach
+
+        return $errorsShown;
+
+    }//end printCSVErrorReport()
+
+
+    /**
+     * Prints all errors and warnings for each file processed.
+     *
+     * Errors and warnings are displayed together, grouped by file.
+     *
+     * @param boolean $showWarnings Show warnings as well as errors.
+     *
+     * @return int The number of error and warning messages shown.
+     */
+    public function printErrorReport($showWarnings=true)
+    {
+        $errorsShown = 0;
+
+        $report = $this->prepareErrorReport($showWarnings);
+        foreach ($report['files'] as $filename => $file) {
+            if (empty($file['messages']) === true) {
+                continue;
+            }
+
+            echo PHP_EOL.'FILE: ';
+            if (strlen($filename) <= 71) {
+                echo $filename;
+            } else {
+                echo '...'.substr($filename, (strlen($filename) - 71));
+            }
+
+            echo PHP_EOL;
+            echo str_repeat('-', 80).PHP_EOL;
+
+            echo 'FOUND '.$file['errors'].' ERROR(S) ';
+
+            if ($showWarnings === true) {
+                echo 'AND '.$file['warnings'].' WARNING(S) ';
+            }
+
+            echo 'AFFECTING '.count($file['messages']).' LINE(S)'.PHP_EOL;
+            echo str_repeat('-', 80).PHP_EOL;
+
+            // Work out the max line number for formatting.
+            $maxLine = 0;
+            foreach ($file['messages'] as $line => $lineErrors) {
+                if ($line > $maxLine) {
+                    $maxLine = $line;
+                }
+            }
+
+            $maxLineLength = strlen($maxLine);
+
+            // The length of the word ERROR or WARNING; used for padding.
+            if ($showWarnings === true && $file['warnings'] > 0) {
+                $typeLength = 7;
+            } else {
+                $typeLength = 5;
+            }
+
+            // The padding that all lines will require that are
+            // printing an error message overflow.
+            $paddingLine2  = str_repeat(' ', ($maxLineLength + 1));
+            $paddingLine2 .= ' | ';
+            $paddingLine2 .= str_repeat(' ', $typeLength);
+            $paddingLine2 .= ' | ';
+
+            // The maxium amount of space an error message can use.
+            $maxErrorSpace = (80 - strlen($paddingLine2));
+
+            foreach ($file['messages'] as $line => $lineErrors) {
+                foreach ($lineErrors as $column => $colErrors) {
+                    foreach ($colErrors as $error) {
+                        // The padding that goes on the front of the line.
+                        $padding  = ($maxLineLength - strlen($line));
+                        $errorMsg = wordwrap($error['message'], $maxErrorSpace, PHP_EOL."$paddingLine2");
+
+                        echo ' '.str_repeat(' ', $padding).$line.' | '.$error['type'];
+                        if ($error['type'] === 'ERROR') {
+                            if ($showWarnings === true && $file['warnings'] > 0) {
+                                echo '  ';
+                            }
+                        }
+
+                        echo ' | '.$errorMsg.PHP_EOL;
+                        $errorsShown++;
+                    }//end foreach
+                }//end foreach
+            }//end foreach
+
+            echo str_repeat('-', 80).PHP_EOL.PHP_EOL;
+
+        }//end foreach
+
+        return $errorsShown;
+
+    }//end printErrorReport()
+
+
+    /**
+     * Prints a summary of errors and warnings for each file processed.
+     *
+     * If verbose output is enabled, results are shown for all files, even if
+     * they have no errors or warnings. If verbose output is disabled, we only
+     * show files that have at least one warning or error.
+     *
+     * @param boolean $showWarnings Show warnings as well as errors.
+     *
+     * @return int The number of error and warning messages shown.
+     */
+    public function printErrorReportSummary($showWarnings=true)
+    {
+        $errorFiles = array();
+
+        foreach ($this->files as $file) {
+            $numWarnings = $file->getWarningCount();
+            $numErrors   = $file->getErrorCount();
+            $filename    = $file->getFilename();
+
+            // If verbose output is enabled, we show the results for all files,
+            // but if not, we only show files that had errors or warnings.
+            if (PHP_CODESNIFFER_VERBOSITY > 0 || $numErrors > 0 || ($numWarnings > 0 && $showWarnings === true)) {
+                $errorFiles[$filename] = array(
+                                          'warnings' => $numWarnings,
+                                          'errors'   => $numErrors,
+                                         );
+            }
+        }
+
+        if (empty($errorFiles) === true) {
+            // Nothing to print.
+            return 0;
+        }
+
+        echo PHP_EOL.'PHP CODE SNIFFER REPORT SUMMARY'.PHP_EOL;
+        echo str_repeat('-', 80).PHP_EOL;
+        if ($showWarnings === true) {
+            echo 'FILE'.str_repeat(' ', 60).'ERRORS  WARNINGS'.PHP_EOL;
+        } else {
+            echo 'FILE'.str_repeat(' ', 70).'ERRORS'.PHP_EOL;
+        }
+
+        echo str_repeat('-', 80).PHP_EOL;
+
+        $totalErrors   = 0;
+        $totalWarnings = 0;
+        $totalFiles    = 0;
+
+        foreach ($errorFiles as $file => $errors) {
+            if ($showWarnings === true) {
+                $padding = (62 - strlen($file));
+            } else {
+                $padding = (72 - strlen($file));
+            }
+
+            if ($padding < 0) {
+                $file    = '...'.substr($file, (($padding * -1) + 3));
+                $padding = 0;
+            }
+
+            echo $file.str_repeat(' ', $padding).'  ';
+            echo $errors['errors'];
+            if ($showWarnings === true) {
+                echo str_repeat(' ', (8 - strlen((string) $errors['errors'])));
+                echo $errors['warnings'];
+            }
+
+            echo PHP_EOL;
+
+            $totalErrors   += $errors['errors'];
+            $totalWarnings += $errors['warnings'];
+            $totalFiles++;
+        }//end foreach
+
+        echo str_repeat('-', 80).PHP_EOL;
+        echo "A TOTAL OF $totalErrors ERROR(S) ";
+        if ($showWarnings === true) {
+            echo "AND $totalWarnings WARNING(S) ";
+        }
+
+        echo "WERE FOUND IN $totalFiles FILE(S)".PHP_EOL;
+        echo str_repeat('-', 80).PHP_EOL.PHP_EOL;
+
+        return ($totalErrors + $totalWarnings);
+
+    }//end printErrorReportSummary()
+
+
+    /**
+     * Generates documentation for a coding standard.
+     *
+     * @param string $standard  The standard to generate docs for
+     * @param array  $sniffs    A list of sniffs to limit the docs to.
+     * @param string $generator The name of the generator class to use.
+     *
+     * @return void
+     */
+    public function generateDocs($standard, array $sniffs=array(), $generator='Text')
+    {
+        include_once 'PHP/CodeSniffer/DocGenerators/'.$generator.'.php';
+
+        $class     = "PHP_CodeSniffer_DocGenerators_$generator";
+        $generator = new $class($standard, $sniffs);
+
+        $generator->generate();
+
+    }//end generateDocs()
+
+
+    /**
+     * Returns the PHP_CodeSniffer file objects.
+     *
+     * @return array(PHP_CodeSniffer_File)
+     */
+    public function getFiles()
+    {
+        return $this->files;
+
+    }//end getFiles()
+
+
+    /**
+     * Gets the array of PHP_CodeSniffer_Sniff's.
+     *
+     * @return array(PHP_CodeSniffer_Sniff)
+     */
+    public function getSniffs()
+    {
+        return $this->listeners;
+
+    }//end getSniffs()
+
+
+    /**
+     * Takes a token produced from <code>token_get_all()</code> and produces a
+     * more uniform token.
+     *
+     * Note that this method also resolves T_STRING tokens into more descrete
+     * types, therefore there is no need to call resolveTstringToken()
+     *
+     * @param string|array $token The token to convert.
+     *
+     * @return array The new token.
+     */
+    public static function standardiseToken($token)
+    {
+        if (is_array($token) === false) {
+            $newToken = self::resolveSimpleToken($token);
+        } else {
+            // Some T_STRING tokens can be more specific.
+            if ($token[0] === T_STRING) {
+                $newToken = self::resolveTstringToken($token);
+            } else {
+                $newToken            = array();
+                $newToken['code']    = $token[0];
+                $newToken['content'] = $token[1];
+                $newToken['type']    = token_name($token[0]);
+            }
+        }
+
+        return $newToken;
+
+    }//end standardiseToken()
+
+
+    /**
+     * Converts T_STRING tokens into more usable token names.
+     *
+     * The token should be produced using the token_get_all() function.
+     * Currently, not all T_STRING tokens are converted.
+     *
+     * @param string|array $token The T_STRING token to convert as constructed
+     *                            by token_get_all().
+     *
+     * @return array The new token.
+     */
+    public static function resolveTstringToken(array $token)
+    {
+        $newToken = array();
+        switch (strtolower($token[1])) {
+        case 'false':
+            $newToken['type'] = 'T_FALSE';
+            break;
+        case 'true':
+            $newToken['type'] = 'T_TRUE';
+            break;
+        case 'null':
+            $newToken['type'] = 'T_NULL';
+            break;
+        case 'self':
+            $newToken['type'] = 'T_SELF';
+            break;
+        case 'parent':
+            $newToken['type'] = 'T_PARENT';
+            break;
+        default:
+            $newToken['type'] = 'T_STRING';
+            break;
+        }
+
+        $newToken['code']    = constant($newToken['type']);
+        $newToken['content'] = $token[1];
+
+        return $newToken;
+
+    }//end resolveTstringToken()
+
+
+    /**
+     * Converts simple tokens into a format that conforms to complex tokens
+     * produced by token_get_all().
+     *
+     * Simple tokens are tokens that are not in array form when produced from
+     * token_get_all().
+     *
+     * @param string $token The simple token to convert.
+     *
+     * @return array The new token in array format.
+     */
+    public static function resolveSimpleToken($token)
+    {
+        $newToken = array();
+
+        switch ($token) {
+        case '{':
+            $newToken['type'] = 'T_OPEN_CURLY_BRACKET';
+            break;
+        case '}':
+            $newToken['type'] = 'T_CLOSE_CURLY_BRACKET';
+            break;
+        case '[':
+            $newToken['type'] = 'T_OPEN_SQUARE_BRACKET';
+            break;
+        case ']':
+            $newToken['type'] = 'T_CLOSE_SQUARE_BRACKET';
+            break;
+        case '(':
+            $newToken['type'] = 'T_OPEN_PARENTHESIS';
+            break;
+        case ')':
+            $newToken['type'] = 'T_CLOSE_PARENTHESIS';
+            break;
+        case ':':
+            $newToken['type'] = 'T_COLON';
+            break;
+        case '.':
+            $newToken['type'] = 'T_STRING_CONCAT';
+            break;
+        case '?':
+            $newToken['type'] = 'T_INLINE_THEN';
+            break;
+        case ';':
+            $newToken['type'] = 'T_SEMICOLON';
+            break;
+        case '=':
+            $newToken['type'] = 'T_EQUAL';
+            break;
+        case '*':
+            $newToken['type'] = 'T_MULTIPLY';
+            break;
+        case '/':
+            $newToken['type'] = 'T_DIVIDE';
+            break;
+        case '+':
+            $newToken['type'] = 'T_PLUS';
+            break;
+        case '-':
+            $newToken['type'] = 'T_MINUS';
+            break;
+        case '%':
+            $newToken['type'] = 'T_MODULUS';
+            break;
+        case '^':
+            $newToken['type'] = 'T_POWER';
+            break;
+        case '&':
+            $newToken['type'] = 'T_BITWISE_AND';
+            break;
+        case '|':
+            $newToken['type'] = 'T_BITWISE_OR';
+            break;
+        case '<':
+            $newToken['type'] = 'T_LESS_THAN';
+            break;
+        case '>':
+            $newToken['type'] = 'T_GREATER_THAN';
+            break;
+        case '!':
+            $newToken['type'] = 'T_BOOLEAN_NOT';
+            break;
+        case ',':
+            $newToken['type'] = 'T_COMMA';
+            break;
+        default:
+            $newToken['type'] = 'T_NONE';
+            break;
+
+        }//end switch
+
+        $newToken['code']    = constant($newToken['type']);
+        $newToken['content'] = $token;
+
+        return $newToken;
+
+    }//end resolveSimpleToken()
+
+
+    /**
+     * Returns true if the specified string is in the camel caps format.
+     *
+     * @param string  $string      The string the verify.
+     * @param boolean $classFormat If true, check to see if the string is in the
+     *                             class format. Class format strings must start
+     *                             with a capital letter and contain no
+     *                             underscores.
+     * @param boolean $public      If true, the first character in the string
+     *                             must be an a-z character. If false, the
+     *                             character must be an underscore. This
+     *                             argument is only applicable if $classFormat
+     *                             is false.
+     * @param boolean $strict      If true, the string must not have two captial
+     *                             letters next to each other. If false, a
+     *                             relaxed camel caps policy is used to allow
+     *                             for acronyms.
+     *
+     * @return boolean
+     */
+    public static function isCamelCaps($string, $classFormat=false, $public=true, $strict=true)
+    {
+        // Check the first character first.
+        if ($classFormat === false) {
+            if ($public === false) {
+                $legalFirstChar = '[_][a-z]';
+            } else {
+                $legalFirstChar = '[a-z]';
+            }
+        } else {
+            $legalFirstChar = '[A-Z]';
+        }
+
+        if (preg_match("|^$legalFirstChar|", $string) === 0) {
+            return false;
+        }
+
+        // Check that the name only contains legal characters.
+        if ($classFormat === false) {
+            $legalChars = 'a-zA-Z0-9';
+        } else {
+            $legalChars = 'a-zA-Z';
+        }
+
+        if (preg_match("|[^$legalChars]|", substr($string, 1)) > 0) {
+            return false;
+        }
+
+        if ($strict === true) {
+            // Check that there are not two captial letters next to each other.
+            $length          = strlen($string);
+            $lastCharWasCaps = $classFormat;
+
+            for ($i = 1; $i < $length; $i++) {
+                $ascii = ord($string{$i});
+                if ($ascii >= 48 && $ascii <= 57) {
+                    // The character is a number, so it cant be a captial.
+                    $isCaps = false;
+                } else {
+                    if (strtoupper($string{$i}) === $string{$i}) {
+                        $isCaps = true;
+                    } else {
+                        $isCaps = false;
+                    }
+                }
+
+                if ($isCaps === true && $lastCharWasCaps === true) {
+                    return false;
+                }
+
+                $lastCharWasCaps = $isCaps;
+            }
+        }//end if
+
+        return true;
+
+    }//end isCamelCaps()
+
+
+    /**
+     * Returns true if the specified string is in the underscore caps format.
+     *
+     * @param string $string The string to verify.
+     *
+     * @return boolean
+     */
+    public static function isUnderscoreName($string)
+    {
+        // If there are space in the name, it can't be valid.
+        if (strpos($string, ' ') !== false) {
+            return false;
+        }
+
+        $validName = true;
+        $nameBits  = explode('_', $string);
+
+        if (preg_match('|^[A-Z]|', $string) === 0) {
+            // Name does not begin with a capital letter.
+            $validName = false;
+        } else {
+            foreach ($nameBits as $bit) {
+                if ($bit{0} !== strtoupper($bit{0})) {
+                    $validName = false;
+                    break;
+                }
+            }
+        }
+
+        return $validName;
+
+    }//end isUnderscoreName()
+
+
+    /**
+     * Returns a valid variable type for param/var tag.
+     *
+     * If type is not one of the standard type, it must be a custom type.
+     * Returns the correct type name suggestion if type name is invalid.
+     *
+     * @param string $varType The variable type to process.
+     *
+     * @return string
+     */
+    public static function suggestType($varType)
+    {
+        if ($varType === '') {
+            return '';
+        }
+
+        if (in_array($varType, self::$allowedTypes) === true) {
+            return $varType;
+        } else {
+            $lowerVarType = strtolower($varType);
+            switch ($lowerVarType) {
+            case 'bool':
+                return 'boolean';
+            case 'double':
+            case 'real':
+                return 'float';
+            case 'int':
+                return 'integer';
+            case 'array()':
+                return 'array';
+            }//end switch
+
+            if (strpos($lowerVarType, 'array(') !== false) {
+                // Valid array declaration:
+                // array, array(type), array(type1 => type2).
+                $matches = array();
+                $pattern = '/^array\(\s*([^\s^=^>]*)(\s*=>\s*(.*))?\s*\)/i';
+                if (preg_match($pattern, $varType, $matches) !== 0) {
+                    $type1 = '';
+                    if (isset($matches[1]) === true) {
+                        $type1 = $matches[1];
+                    }
+
+                    $type2 = '';
+                    if (isset($matches[3]) === true) {
+                        $type2 = $matches[3];
+                    }
+
+                    $type1 = self::suggestType($type1);
+                    $type2 = self::suggestType($type2);
+                    if ($type2 !== '') {
+                        $type2 = ' => '.$type2;
+                    }
+
+                    return "array($type1$type2)";
+                } else {
+                    return 'array';
+                }//end if
+            } else if (in_array($lowerVarType, self::$allowedTypes) === true) {
+                // A valid type, but not lower cased.
+                return $lowerVarType;
+            } else {
+                // Must be a custom type name.
+                return $varType;
+            }//end if
+        }//end if
+
+    }//end suggestType()
+
+
+    /**
+     * Get a list of all coding standards installed.
+     *
+     * Coding standards are directories located in the
+     * CodeSniffer/Standards directory. Valid coding standards
+     * include a Sniffs subdirectory.
+     *
+     * @param boolean $includeGeneric If true, the special "Generic"
+     *                                coding standard will be included
+     *                                if installed.
+     * @param string  $standardsDir   A specific directory to look for standards
+     *                                in. If not specified, PHP_CodeSniffer will
+     *                                look in its default location.
+     *
+     * @return array
+     * @see isInstalledStandard()
+     */
+    public static function getInstalledStandards($includeGeneric=false, $standardsDir='')
+    {
+        $installedStandards = array();
+
+        if ($standardsDir === '') {
+            $standardsDir = dirname(__FILE__).'/CodeSniffer/Standards';
+        }
+
+        $di = new DirectoryIterator($standardsDir);
+        foreach ($di as $file) {
+            if ($file->isDir() === true && $file->isDot() === false) {
+                $filename = $file->getFilename();
+
+                // Ignore the special "Generic" standard.
+                if ($includeGeneric === false && $filename === 'Generic') {
+                    continue;
+                }
+
+                // Valid coding standard dirs include a standard class.
+                $csFile = $file->getPathname()."/{$filename}CodingStandard.php";
+                if (is_file($csFile) === true) {
+                    // We found a coding standard directory.
+                    $installedStandards[] = $filename;
+                }
+            }
+        }
+
+        return $installedStandards;
+
+    }//end getInstalledStandards()
+
+
+    /**
+     * Determine if a standard is installed.
+     *
+     * Coding standards are directories located in the
+     * CodeSniffer/Standards directory. Valid coding standards
+     * include a Sniffs subdirectory.
+     *
+     * @param string $standard The name of the coding standard.
+     *
+     * @return boolean
+     * @see getInstalledStandards()
+     */
+    public static function isInstalledStandard($standard)
+    {
+        $standardDir  = dirname(__FILE__);
+        $standardDir .= '/CodeSniffer/Standards/'.$standard;
+        if (is_file("$standardDir/{$standard}CodingStandard.php") === true) {
+            return true;
+        } else {
+            // This could be a custom standard, installed outside our
+            // standards directory.
+            $standardFile = rtrim($standard, ' /\\').DIRECTORY_SEPARATOR.basename($standard).'CodingStandard.php';
+            return (is_file($standardFile) === true);
+        }
+
+    }//end isInstalledStandard()
+
+
+    /**
+     * Get a single config value.
+     *
+     * Config data is stored in the data dir, in a file called
+     * CodeSniffer.conf. It is a simple PHP array.
+     *
+     * @param string $key The name of the config value.
+     *
+     * @return string
+     * @see setConfigData()
+     * @see getAllConfigData()
+     */
+    public static function getConfigData($key)
+    {
+        $phpCodeSnifferConfig = self::getAllConfigData();
+
+        if ($phpCodeSnifferConfig === null) {
+            return null;
+        }
+
+        if (isset($phpCodeSnifferConfig[$key]) === false) {
+            return null;
+        }
+
+        return $phpCodeSnifferConfig[$key];
+
+    }//end getConfigData()
+
+
+    /**
+     * Set a single config value.
+     *
+     * Config data is stored in the data dir, in a file called
+     * CodeSniffer.conf. It is a simple PHP array.
+     *
+     * @param string      $key   The name of the config value.
+     * @param string|null $value The value to set. If null, the config
+     *                           entry is deleted, reverting it to the
+     *                           default value.
+     * @param boolean     $temp  Set this config data temporarily for this
+     *                           script run. This will not write the config
+     *                           data to the config file.
+     *
+     * @return boolean
+     * @see getConfigData()
+     * @throws PHP_CodeSniffer_Exception If the config file can not be written.
+     */
+    public static function setConfigData($key, $value, $temp=false)
+    {
+        if ($temp === false) {
+            $configFile = dirname(__FILE__).'/CodeSniffer.conf';
+            if (is_file($configFile) === false) {
+                $configFile = '/usr/lib/php/data/PHP_CodeSniffer/CodeSniffer.conf';
+            }
+
+            if (is_file($configFile) === true && is_writable($configFile) === false) {
+                $error = "Config file $configFile is not writable";
+                throw new PHP_CodeSniffer_Exception($error);
+            }
+        }
+
+        $phpCodeSnifferConfig = self::getAllConfigData();
+
+        if ($value === null) {
+            if (isset($phpCodeSnifferConfig[$key]) === true) {
+                unset($phpCodeSnifferConfig[$key]);
+            }
+        } else {
+            $phpCodeSnifferConfig[$key] = $value;
+        }
+
+        if ($temp === false) {
+            $output  = '<'.'?php'."\n".' $phpCodeSnifferConfig = ';
+            $output .= var_export($phpCodeSnifferConfig, true);
+            $output .= "\n?".'>';
+
+            if (file_put_contents($configFile, $output) === false) {
+                return false;
+            }
+        }
+
+        $GLOBALS['PHP_CODESNIFFER_CONFIG_DATA'] = $phpCodeSnifferConfig;
+
+        return true;
+
+    }//end setConfigData()
+
+
+    /**
+     * Get all config data in an array.
+     *
+     * @return string
+     * @see getConfigData()
+     */
+    public static function getAllConfigData()
+    {
+        if (isset($GLOBALS['PHP_CODESNIFFER_CONFIG_DATA']) === true) {
+            return $GLOBALS['PHP_CODESNIFFER_CONFIG_DATA'];
+        }
+
+        $configFile = dirname(__FILE__).'/CodeSniffer.conf';
+        if (is_file($configFile) === false) {
+            $configFile = '/usr/lib/php/data/PHP_CodeSniffer/CodeSniffer.conf';
+        }
+
+        if (is_file($configFile) === false) {
+            return null;
+        }
+
+        include $configFile;
+        $GLOBALS['PHP_CODESNIFFER_CONFIG_DATA'] = $phpCodeSnifferConfig;
+        return $GLOBALS['PHP_CODESNIFFER_CONFIG_DATA'];
+
+    }//end getAllConfigData()
+
+
+}//end class
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CLI.php b/lib/pear/PHP/CodeSniffer/CLI.php
new file mode 100644 (file)
index 0000000..b13bd63
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/AbstractDocElement.php b/lib/pear/PHP/CodeSniffer/CommentParser/AbstractDocElement.php
new file mode 100644 (file)
index 0000000..d508dea
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/AbstractParser.php b/lib/pear/PHP/CodeSniffer/CommentParser/AbstractParser.php
new file mode 100644 (file)
index 0000000..53a4024
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/ClassCommentParser.php b/lib/pear/PHP/CodeSniffer/CommentParser/ClassCommentParser.php
new file mode 100644 (file)
index 0000000..50e784b
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/CommentElement.php b/lib/pear/PHP/CodeSniffer/CommentParser/CommentElement.php
new file mode 100644 (file)
index 0000000..8ee3832
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/DocElement.php b/lib/pear/PHP/CodeSniffer/CommentParser/DocElement.php
new file mode 100644 (file)
index 0000000..2648b8a
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/FunctionCommentParser.php b/lib/pear/PHP/CodeSniffer/CommentParser/FunctionCommentParser.php
new file mode 100644 (file)
index 0000000..a8616cc
--- /dev/null
@@ -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
+
+?>
\ No newline at end of file
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/MemberCommentParser.php b/lib/pear/PHP/CodeSniffer/CommentParser/MemberCommentParser.php
new file mode 100644 (file)
index 0000000..24825e2
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/PairElement.php b/lib/pear/PHP/CodeSniffer/CommentParser/PairElement.php
new file mode 100644 (file)
index 0000000..7917f7c
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/ParameterElement.php b/lib/pear/PHP/CodeSniffer/CommentParser/ParameterElement.php
new file mode 100644 (file)
index 0000000..a9e4491
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/ParserException.php b/lib/pear/PHP/CodeSniffer/CommentParser/ParserException.php
new file mode 100644 (file)
index 0000000..a690a6b
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/CommentParser/SingleElement.php b/lib/pear/PHP/CodeSniffer/CommentParser/SingleElement.php
new file mode 100644 (file)
index 0000000..519acd6
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/DocGenerators/Generator.php b/lib/pear/PHP/CodeSniffer/DocGenerators/Generator.php
new file mode 100644 (file)
index 0000000..d295846
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/DocGenerators/HTML.php b/lib/pear/PHP/CodeSniffer/DocGenerators/HTML.php
new file mode 100644 (file)
index 0000000..a5f844c
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/DocGenerators/Text.php b/lib/pear/PHP/CodeSniffer/DocGenerators/Text.php
new file mode 100644 (file)
index 0000000..36c7018
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Exception.php b/lib/pear/PHP/CodeSniffer/Exception.php
new file mode 100644 (file)
index 0000000..5828ba2
--- /dev/null
@@ -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
+
+?>
\ No newline at end of file
diff --git a/lib/pear/PHP/CodeSniffer/File.php b/lib/pear/PHP/CodeSniffer/File.php
new file mode 100644 (file)
index 0000000..9611d40
--- /dev/null
@@ -0,0 +1,2000 @@
+<?php
+/**
+ * A PHP_CodeSniffer_File object represents a PHP source file and the tokens
+ * associated with it.
+ *
+ * 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 PHP_CodeSniffer_File object represents a PHP source file and the tokens
+ * associated with it.
+ *
+ * It provides a means for traversing the token stack, along with
+ * other token related operations. If a PHP_CodeSniffer_Sniff finds and error or
+ *  warning within a PHP_CodeSniffer_File, you can raise an error using the
+ *  addError() or addWarning() methods.
+ *
+ * <b>Token Information</b>
+ *
+ * Each token within the stack contains information about itself:
+ *
+ * <code>
+ *   array(
+ *    'code'       => 301,       // the token type code (see token_get_all())
+ *    'content'    => 'if',      // the token content
+ *    'type'       => 'T_IF',    // the token name
+ *    'line'       => 56,        // the line number when the token is located
+ *    'column'     => 12,        // the column in the line where this token
+ *                               // starts (starts from 1)
+ *    'level'      => 2          // the depth a token is within the scopes open
+ *    'conditions' => array(     // a list of scope condition token
+ *                               // positions => codes that
+ *                     2 => 50,  // openened the scopes that this token exists
+ *                     9 => 353, // in (see conditional tokens section below)
+ *                    ),
+ *   );
+ * </code>
+ *
+ * <b>Conditional Tokens</b>
+ *
+ * In addition to the standard token fields, conditions contain information to
+ * determine where their scope begins and ends:
+ *
+ * <code>
+ *   array(
+ *    'scope_condition' => 38, // the token position of the condition
+ *    'scope_opener'    => 41, // the token position that started the scope
+ *    'scope_closer'    => 70, // the token position that ended the scope
+ *   );
+ * </code>
+ *
+ * The condition, the scope opener and the scope closer each contain this
+ * information.
+ *
+ * <b>Parenthesis Tokens</b>
+ *
+ * Each parenthesis token (T_OPEN_PARENTHESIS and T_CLOSE_PARENTHESIS) has a
+ * reference to their opening and closing parenthesis, one being itself, the
+ * other being its oposite.
+ *
+ * <code>
+ *   array(
+ *    'parenthesis_opener' => 34,
+ *    'parenthesis_closer' => 40,
+ *   );
+ * </code>
+ *
+ * Some tokens can "own" a set of parethesis. For example a T_FUNCTION token
+ * has parenthesis around its argument list. These tokens also have the
+ * parenthesis_opener and and parenthesis_closer indicies. Not all parethesis
+ * have owners, for example parenthesis used for arithmetic operations and
+ * function calls. The parenthesis tokens that have an owner have the following
+ * auxilery array indicies.
+ *
+ * <code>
+ *   array(
+ *    'parenthesis_opener' => 34,
+ *    'parenthesis_closer' => 40,
+ *    'parenthesis_owner'  => 33,
+ *   );
+ * </code>
+ *
+ * Each token within a set of parenthesis also has an array indicy
+ * 'nested_parenthesis' which is an array of the
+ * left parenthesis => right parenthesis token positions.
+ *
+ * <code>
+ *   'nested_parentheisis' => array(
+ *                             12 => 15
+ *                             11 => 14
+ *                            );
+ * </code>
+ *
+ * <b>Extended Tokens</b>
+ *
+ * PHP_CodeSniffer extends and augments some of the tokens created by
+ * <i>token_get_all()</i>. A full list of these tokens can be seen in the
+ * <i>Tokens.php</i> file.
+ *
+ * @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_File
+{
+
+    /**
+     * The absolute path to the file associated with this object.
+     *
+     * @var string
+     */
+    private $_file = '';
+
+    /**
+     * The EOL character this file uses.
+     *
+     * @var string
+     */
+    public $eolChar = '';
+
+    /**
+     * The tokenizer being used for this file.
+     *
+     * @var object
+     */
+    public $tokenizer = null;
+
+    /**
+     * The tokenizer being used for this file.
+     *
+     * @var string
+     */
+    public $tokenizerType = 'PHP';
+
+    /**
+     * The number of tokens in this file.
+     *
+     * Stored here to save calling count() everywhere.
+     *
+     * @var int
+     */
+    public $numTokens = 0;
+
+    /**
+     * The tokens stack map.
+     *
+     * Note that the tokens in this array differ in format to the tokens
+     * produced by token_get_all(). Tokens are initially produced with
+     * token_get_all(), then augmented so that it's easier to process them.
+     *
+     * @var array()
+     * @see Tokens.php
+     */
+    private $_tokens = array();
+
+    /**
+     * The errors raised from PHP_CodeSniffer_Sniffs.
+     *
+     * @var array()
+     * @see getErrors()
+     */
+    private $_errors = array();
+
+    /**
+     * The warnings raised form PHP_CodeSniffer_Sniffs.
+     *
+     * @var array()
+     * @see getWarnings()
+     */
+    private $_warnings = array();
+
+    /**
+     * The total number of errors raised.
+     *
+     * @var int
+     */
+    private $_errorCount = 0;
+
+    /**
+     * The total number of warnings raised.
+     *
+     * @var int
+     */
+    private $_warningCount = 0;
+
+    /**
+     * An array of sniffs listening to this file's processing.
+     *
+     * @var array(PHP_CodeSniffer_Sniff)
+     */
+    private $_listeners = array();
+
+    /**
+     * A constant to represent an error in PHP_CodeSniffer.
+     *
+     * @var int
+     */
+    const ERROR = 0;
+
+    /**
+     * A constant to represent a warning in PHP_CodeSniffer.
+     *
+     * @var int
+     */
+    const WARNING = 1;
+
+    /**
+     * An array of extensions mapping to the tokenizer to use.
+     *
+     * This value gets set by PHP_CodeSniffer when the object is created.
+     *
+     * @var array
+     */
+    protected $tokenizers = array();
+
+
+    /**
+     * Constructs a PHP_CodeSniffer_File.
+     *
+     * @param string        $file       The absolute path to the file
+     *                                  to process.
+     * @param array(string) $listeners  The initial listeners listening
+     *                                  to processing of this file.
+     * @param array         $tokenizers An array of extensions mapping
+     *                                  to the tokenizer to use.
+     *
+     * @throws PHP_CodeSniffer_Exception If the register() method does
+     *                                   not return an array.
+     */
+    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->tokenizers = $tokenizers;
+
+    }//end __construct()
+
+
+    /**
+     * Adds a listener to the token stack that listens to the specific tokens.
+     *
+     * When PHP_CodeSniffer encounters on the the tokens specified in $tokens, it
+     *  invokes the process method of the sniff.
+     *
+     * @param PHP_CodeSniffer_Sniff $listener The listener to add to the
+     *                                        listener stack.
+     * @param array(int)            $tokens   The token types the listener wishes to
+     *                                        listen to.
+     *
+     * @return void
+     */
+    public function addTokenListener(PHP_CodeSniffer_Sniff $listener, array $tokens)
+    {
+        foreach ($tokens as $token) {
+            if (isset($this->_listeners[$token]) === false) {
+                $this->_listeners[$token] = array();
+            }
+
+            if (in_array($listener, $this->_listeners[$token], true) === false) {
+                $this->_listeners[$token][] = $listener;
+            }
+        }
+
+    }//end addTokenListener()
+
+
+    /**
+     * Removes a listener from listening from the specified tokens.
+     *
+     * @param PHP_CodeSniffer_Sniff $listener The listener to remove from the
+     *                                        listener stack.
+     * @param array(int)            $tokens   The token types the listener wishes to
+     *                                        stop listen to.
+     *
+     * @return void
+     */
+    public function removeTokenListener(PHP_CodeSniffer_Sniff $listener, array $tokens)
+    {
+        foreach ($tokens as $token) {
+            if (isset($this->_listeners[$token]) === false) {
+                continue;
+            }
+
+            if (in_array($listener, $this->_listeners[$token]) === true) {
+                foreach ($this->_listeners[$token] as $pos => $value) {
+                    if ($value === $listener) {
+                        unset($this->_listeners[$token][$pos]);
+                    }
+                }
+            }
+        }
+
+    }//end removeTokenListener()
+
+
+    /**
+     * Returns the token stack for this file.
+     *
+     * @return array()
+     */
+    public function getTokens()
+    {
+        return $this->_tokens;
+
+    }//end getTokens()
+
+
+    /**
+     * Starts the stack traversal and tells listeners when tokens are found.
+     *
+     * @param string $contents The contents to parse. If NULL, the content
+     *                         is taken from the file system.
+     *
+     * @return void
+     */
+    public function start($contents=null)
+    {
+        $this->_parse($contents);
+
+        if (PHP_CODESNIFFER_VERBOSITY > 2) {
+            echo "\t*** START TOKEN PROCESSING ***".PHP_EOL;
+        }
+
+        // Foreach of the listeners that have registed to listen for this
+        // token, get them to process it.
+        foreach ($this->_tokens as $stackPtr => $token) {
+            if (PHP_CODESNIFFER_VERBOSITY > 2) {
+                $type    = $token['type'];
+                $content = str_replace($this->eolChar, '\n', $token['content']);
+                echo "\t\tProcess token $stackPtr: $type => $content".PHP_EOL;
+            }
+
+            $tokenType = $token['code'];
+            if (isset($this->_listeners[$tokenType]) === true) {
+                foreach ($this->_listeners[$tokenType] as $listener) {
+                    // Make sure this sniff supports the tokenizer
+                    // we are currently using.
+                    $vars = get_class_vars(get_class($listener));
+                    if (isset($vars['supportedTokenizers']) === true) {
+                        if (in_array($this->tokenizerType, $vars['supportedTokenizers']) === false) {
+                            continue;
+                        }
+                    } else {
+                        // The default supported tokenizer is PHP.
+                        if ($this->tokenizerType !== 'PHP') {
+                            continue;
+                        }
+                    }
+
+                    if (PHP_CODESNIFFER_VERBOSITY > 2) {
+                        $startTime = microtime(true);
+                        echo "\t\t\tProcessing ".get_class($listener).'... ';
+                    }
+
+                    $listener->process($this, $stackPtr);
+
+                    if (PHP_CODESNIFFER_VERBOSITY > 2) {
+                        $timeTaken = round((microtime(true) - $startTime), 4);
+                        echo "DONE in $timeTaken seconds".PHP_EOL;
+                    }
+                }//end foreach
+            }//end if
+        }//end foreach
+
+        if (PHP_CODESNIFFER_VERBOSITY > 2) {
+            echo "\t*** END TOKEN PROCESSING ***".PHP_EOL;
+        }
+
+        // We don't need the tokens any more, so get rid of them
+        // to save some memory.
+        $this->_tokens    = null;
+        $this->_listeners = null;
+
+    }//end start()
+
+
+    /**
+     * Tokenizes the file and preapres it for the test run.
+     *
+     * @param string $contents The contents to parse. If NULL, the content
+     *                         is taken from the file system.
+     *
+     * @return void
+     */
+    private function _parse($contents=null)
+    {
+        $this->eolChar = self::detectLineEndings($this->_file, $contents);
+
+        // Determine the tokenizer from the file extension.
+        $fileParts = explode('.', $this->_file);
+        $extension = array_pop($fileParts);
+        if (isset($this->tokenizers[$extension]) === true) {
+            $tokenizerClass      = 'PHP_CodeSniffer_Tokenizers_'.$this->tokenizers[$extension];
+            $this->tokenizerType = $this->tokenizers[$extension];
+        } else {
+            // Revert to default.
+            $tokenizerClass = 'PHP_CodeSniffer_Tokenizers_'.$this->tokenizerType;
+        }
+
+        $tokenizer       = new $tokenizerClass();
+        $this->tokenizer = $tokenizer;
+
+        if ($contents === null) {
+            $contents = file_get_contents($this->_file);
+        }
+
+        $this->_tokens   = self::tokenizeString($contents, $tokenizer, $this->eolChar);
+        $this->numTokens = count($this->_tokens);
+
+        if (PHP_CODESNIFFER_VERBOSITY > 0) {
+            if ($this->numTokens === 0) {
+                $numLines = 0;
+            } else {
+                $numLines = $this->_tokens[($this->numTokens - 1)]['line'];
+            }
+
+            echo "[$this->numTokens tokens in $numLines lines]... ";
+            if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                echo PHP_EOL;
+            }
+        }
+
+    }//end _parse()
+
+
+    /**
+     * Opens a file and detects the EOL character being used.
+     *
+     * @param string $file     The full path to the file.
+     * @param string $contents The contents to parse. If NULL, the content
+     *                         is taken from the file system.
+     *
+     * @return string
+     * @throws PHP_CodeSniffer_Exception If $file could not be opened.
+     */
+    public static function detectLineEndings($file, $contents=null)
+    {
+        if ($contents === null) {
+            // Determine the newline character being used in this file.
+            // Will be either \r, \r\n or \n.
+            $handle = fopen($file, 'r');
+            if ($handle === false) {
+                $error = 'File could not be opened; could not auto-detect line endings';
+                throw new PHP_CodeSniffer_Exception($error);
+            }
+
+            $firstLine = fgets($handle);
+            fclose($handle);
+
+            $eolChar = substr($firstLine, -1);
+            if ($eolChar === "\n") {
+                $secondLastChar = substr($firstLine, -2, 1);
+                if ($secondLastChar === "\r") {
+                    $eolChar = "\r\n";
+                }
+            }
+        } else {
+            if (preg_match("/\r\n?|\n/", $contents, $matches) !== 1) {
+                $error = 'Could not auto-detect line endings from content';
+                throw new PHP_CodeSniffer_Exception($error);
+            }
+
+            $eolChar = $matches[0];
+        }//end if
+
+        return $eolChar;
+
+    }//end detectLineEndings()
+
+
+    /**
+     * Adds an error to the error stack.
+     *
+     * @param string $error    The error message.
+     * @param int    $stackPtr The stack position where the error occured.
+     *
+     * @return void
+     */
+    public function addError($error, $stackPtr)
+    {
+        if ($stackPtr === null) {
+            $lineNum = 1;
+            $column  = 1;
+        } else {
+            $lineNum = $this->_tokens[$stackPtr]['line'];
+            $column  = $this->_tokens[$stackPtr]['column'];
+        }
+
+        if (isset($this->_errors[$lineNum]) === false) {
+            $this->errors[$lineNum] = array();
+        }
+
+        if (isset($this->_errors[$lineNum][$column]) === false) {
+            $this->errors[$lineNum][$column] = array();
+        }
+
+        $this->_errors[$lineNum][$column][] = $error;
+        $this->_errorCount++;
+
+    }//end addError()
+
+
+    /**
+     * Adds an warning to the warning stack.
+     *
+     * @param string $warning  The error message.
+     * @param int    $stackPtr The stack position where the error occured.
+     *
+     * @return void
+     */
+    public function addWarning($warning, $stackPtr)
+    {
+        if ($stackPtr === null) {
+            $lineNum = 1;
+            $column  = 1;
+        } else {
+            $lineNum = $this->_tokens[$stackPtr]['line'];
+            $column  = $this->_tokens[$stackPtr]['column'];
+        }
+
+        if (isset($this->_warnings[$lineNum]) === false) {
+            $this->_warnings[$lineNum] = array();
+        }
+
+        if (isset($this->_warnings[$lineNum][$column]) === false) {
+            $this->_warnings[$lineNum][$column] = array();
+        }
+
+        $this->_warnings[$lineNum][$column][] = $warning;
+        $this->_warningCount++;
+
+    }//end addWarning()
+
+
+    /**
+     * Returns the number of errors raised.
+     *
+     * @return int
+     */
+    public function getErrorCount()
+    {
+        return $this->_errorCount;
+
+    }//end getErrorCount()
+
+
+    /**
+     * Returns the number of warnings raised.
+     *
+     * @return int
+     */
+    public function getWarningCount()
+    {
+        return $this->_warningCount;
+
+    }//end getWarningCount()
+
+
+    /**
+     * Returns the errors raised from processing this file.
+     *
+     * @return array
+     */
+    public function getErrors()
+    {
+        return $this->_errors;
+
+    }//end getErrors()
+
+
+    /**
+     * Returns the warnings raised from processing this file.
+     *
+     * @return array
+     */
+    public function getWarnings()
+    {
+        return $this->_warnings;
+
+    }//end getWarnings()
+
+
+    /**
+     * Returns the absolute filename of this file.
+     *
+     * @return string
+     */
+    public function getFilename()
+    {
+        return $this->_file;
+
+    }//end getFilename()
+
+
+    /**
+     * 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 object $tokenizer A tokenizer class to use to tokenize the string.
+     * @param string $eolChar   The EOL character to use for splitting strings.
+     *
+     * @return array
+     */
+    public static function tokenizeString($string, $tokenizer, $eolChar='\n')
+    {
+        $tokens = $tokenizer->tokenizeString($string, $eolChar);
+
+        self::_createLineMap($tokens, $tokenizer, $eolChar);
+        self::_createBracketMap($tokens, $tokenizer, $eolChar);
+        self::_createParenthesisMap($tokens, $tokenizer, $eolChar);
+        self::_createParenthesisNestingMap($tokens, $tokenizer, $eolChar);
+        self::_createScopeMap($tokens, $tokenizer, $eolChar);
+
+        // If we know the width of each tab, convert tabs
+        // into spaces so sniffs can use one method of checking.
+        if (PHP_CODESNIFFER_TAB_WIDTH > 0) {
+            self::_convertTabs($tokens, $tokenizer, $eolChar);
+        }
+
+        // Column map requires the line map to be complete.
+        self::_createColumnMap($tokens, $tokenizer, $eolChar);
+        self::_createLevelMap($tokens, $tokenizer, $eolChar);
+
+        return $tokens;
+
+    }//end tokenizeString()
+
+
+    /**
+     * Creates a map of tokens => line numbers for each token.
+     *
+     * @param array  &$tokens   The array of tokens to process.
+     * @param object $tokenizer The tokenizer being used to process this file.
+     * @param string $eolChar   The EOL character to use for splitting strings.
+     *
+     * @return void
+     */
+    private static function _createLineMap(&$tokens, $tokenizer, $eolChar)
+    {
+        $lineNumber = 1;
+        $count      = count($tokens);
+
+        for ($i = 0; $i < $count; $i++) {
+            $tokens[$i]['line'] = $lineNumber;
+            $lineNumber        += substr_count($tokens[$i]['content'], $eolChar);
+        }
+
+    }//end _createLineMap()
+
+
+    /**
+     * Converts tabs into spaces.
+     *
+     * Each tab can represent between 1 and $width spaces, so
+     * this cannot be a straight string replace.
+     *
+     * @param array  &$tokens   The array of tokens to process.
+     * @param object $tokenizer The tokenizer being used to process this file.
+     * @param string $eolChar   The EOL character to use for splitting strings.
+     *
+     * @return void
+     */
+    private static function _convertTabs(&$tokens, $tokenizer, $eolChar)
+    {
+        $currColumn = 1;
+        $count      = count($tokens);
+
+        for ($i = 0; $i < $count; $i++) {
+            $tokenContent = $tokens[$i]['content'];
+
+            if (strpos($tokenContent, "\t") === false) {
+                // There are no tabs in this content.
+                $currColumn += (strlen($tokenContent) - 1);
+            } else {
+                // We need to determine the length of each tab.
+                $tabs         = preg_split("|(\t)|", $tokenContent, -1, PREG_SPLIT_DELIM_CAPTURE);
+                $tabNum       = 0;
+                $adjustedTab  = false;
+                $tabsToSpaces = array();
+                $newContent   = '';
+
+                foreach ($tabs as $content) {
+                    if ($content === '') {
+                        continue;
+                    }
+
+                    if (strpos($content, "\t") === false) {
+                        // This piece of content is not a tab.
+                        $currColumn += strlen($content);
+                        $newContent .= $content;
+                    } else {
+                        $lastCurrColumn = $currColumn;
+                        $tabNum++;
+
+                        // Move the pointer to the next tab stop.
+                        if (($currColumn % PHP_CODESNIFFER_TAB_WIDTH) === 0) {
+                            // This is the first tab, and we are already at a
+                            // tab stop, so this tab counts as a single space.
+                            $currColumn++;
+                            $adjustedTab = true;
+                        } else {
+                            $currColumn++;
+                            while (($currColumn % PHP_CODESNIFFER_TAB_WIDTH) != 0) {
+                                $currColumn++;
+                            }
+
+                            $currColumn++;
+                        }
+
+                        $newContent .= str_repeat(' ', ($currColumn - $lastCurrColumn));
+                    }//end if
+                }//end foreach
+
+                if ($tabNum === 1 && $adjustedTab === true) {
+                    $currColumn--;
+                }
+
+                $tokens[$i]['content'] = $newContent;
+            }//end if
+
+            if (isset($tokens[($i + 1)]['line']) === true && $tokens[($i + 1)]['line'] !== $tokens[$i]['line']) {
+                $currColumn = 1;
+            } else {
+                $currColumn++;
+            }
+        }//end for
+
+    }//end _convertTabs()
+
+
+    /**
+     * Creates a column map.
+     *
+     * The column map indicates where the token started on the line where it
+     * exists.
+     *
+     * @param array  &$tokens   The array of tokens to process.
+     * @param object $tokenizer The tokenizer being used to process this file.
+     * @param string $eolChar   The EOL character to use for splitting strings.
+     *
+     * @return void
+     */
+    private static function _createColumnMap(&$tokens, $tokenizer, $eolChar)
+    {
+        $currColumn = 1;
+        $count      = count($tokens);
+
+        for ($i = 0; $i < $count; $i++) {
+            $tokens[$i]['column'] = $currColumn;
+            if (isset($tokens[($i + 1)]['line']) === true && $tokens[($i + 1)]['line'] !== $tokens[$i]['line']) {
+                $currColumn = 1;
+            } else {
+                $currColumn += strlen($tokens[$i]['content']);
+            }
+        }
+
+    }//end _createColumnMap()
+
+
+    /**
+     * Creates a map for opening and closing of square brackets.
+     *
+     * Each bracket token (T_OPEN_SQUARE_BRACKET and T_CLOSE_SQUARE_BRACKET)
+     * has a reference to their opening and closing bracket
+     * (bracket_opener and bracket_closer).
+     *
+     * @param array  &$tokens   The array of tokens to process.
+     * @param object $tokenizer The tokenizer being used to process this file.
+     * @param string $eolChar   The EOL character to use for splitting strings.
+     *
+     * @return void
+     */
+    private static function _createBracketMap(&$tokens, $tokenizer, $eolChar)
+    {
+        $openers   = array();
+        $numTokens = count($tokens);
+        $owners    = array();
+
+        for ($i = 0; $i < $numTokens; $i++) {
+            if ($tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET) {
+                $openers[] = $i;
+            } else if ($tokens[$i]['code'] === T_CLOSE_SQUARE_BRACKET) {
+                if (empty($openers) === false) {
+                    $opener                            = array_pop($openers);
+                    $tokens[$i]['bracket_opener']      = $opener;
+                    $tokens[$i]['bracket_closer']      = $i;
+                    $tokens[$opener]['bracket_opener'] = $opener;
+                    $tokens[$opener]['bracket_closer'] = $i;
+                }
+            }
+        }
+
+    }//end _createBracketMap()
+
+
+    /**
+     * Creates a map for opening and closing of parenthesis.
+     *
+     * Each parenthesis token (T_OPEN_PARENTHESIS and T_CLOSE_PARENTHESIS) has a
+     * reference to their opening and closing parenthesis (parenthesis_opener
+     * and parenthesis_closer).
+     *
+     * @param array  &$tokens   The array of tokens to process.
+     * @param object $tokenizer The tokenizer being used to process this file.
+     * @param string $eolChar   The EOL character to use for splitting strings.
+     *
+     * @return void
+     */
+    private static function _createParenthesisMap(&$tokens, $tokenizer, $eolChar)
+    {
+        $openers   = array();
+        $numTokens = count($tokens);
+        $openOwner = null;
+
+        for ($i = 0; $i < $numTokens; $i++) {
+            if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$parenthesisOpeners) === true) {
+                $tokens[$i]['parenthesis_opener'] = null;
+                $tokens[$i]['parenthesis_closer'] = null;
+                $tokens[$i]['parenthesis_owner']  = $i;
+                $openOwner                        = $i;
+            } else if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
+                $openers[]                        = $i;
+                $tokens[$i]['parenthesis_opener'] = $i;
+                if ($openOwner !== null) {
+                    $tokens[$openOwner]['parenthesis_opener'] = $i;
+                    $tokens[$i]['parenthesis_owner']          = $openOwner;
+                    $openOwner                                = null;
+                }
+            } else if ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
+                // Did we set an owner for this set of parenthesis?
+                $numOpeners = count($openers);
+                if ($numOpeners !== 0) {
+                    $opener = array_pop($openers);
+                    if (isset($tokens[$opener]['parenthesis_owner']) === true) {
+                        $owner = $tokens[$opener]['parenthesis_owner'];
+
+                        $tokens[$owner]['parenthesis_closer'] = $i;
+                        $tokens[$i]['parenthesis_owner']      = $owner;
+                    }
+
+                    $tokens[$i]['parenthesis_opener']      = $opener;
+                    $tokens[$i]['parenthesis_closer']      = $i;
+                    $tokens[$opener]['parenthesis_closer'] = $i;
+                }
+            }//end if
+        }//end for
+
+    }//end _createParenthesisMap()
+
+
+    /**
+     * Creates a map for the parenthesis tokens that surround other tokens.
+     *
+     * @param array  &$tokens   The array of tokens to process.
+     * @param object $tokenizer The tokenizer being used to process this file.
+     * @param string $eolChar   The EOL character to use for splitting strings.
+     *
+     * @return void
+     */
+    private static function _createParenthesisNestingMap(&$tokens, $tokenizer, $eolChar)
+    {
+        $numTokens = count($tokens);
+        $map       = array();
+        for ($i = 0; $i < $numTokens; $i++) {
+            if (isset($tokens[$i]['parenthesis_opener']) === true && $i === $tokens[$i]['parenthesis_opener']) {
+                if (empty($map) === false) {
+                    $tokens[$i]['nested_parenthesis'] = $map;
+                }
+
+                if (isset($tokens[$i]['parenthesis_closer']) === true) {
+                    $map[$tokens[$i]['parenthesis_opener']] = $tokens[$i]['parenthesis_closer'];
+                }
+            } else if (isset($tokens[$i]['parenthesis_closer']) === true && $i === $tokens[$i]['parenthesis_closer']) {
+                array_pop($map);
+                if (empty($map) === false) {
+                    $tokens[$i]['nested_parenthesis'] = $map;
+                }
+            } else {
+                if (empty($map) === false) {
+                    $tokens[$i]['nested_parenthesis'] = $map;
+                }
+            }
+        }
+
+    }//end _createParenthesisNestingMap()
+
+
+    /**
+     * Creates a scope map of tokens that open scopes.
+     *
+     * @param array  &$tokens   The array of tokens to process.
+     * @param object $tokenizer The tokenizer being used to process this file.
+     * @param string $eolChar   The EOL character to use for splitting strings.
+     *
+     * @return void
+     * @see _recurseScopeMap()
+     */
+    private static function _createScopeMap(&$tokens, $tokenizer, $eolChar)
+    {
+        if (PHP_CODESNIFFER_VERBOSITY > 1) {
+            echo "\t*** START SCOPE MAP ***".PHP_EOL;
+        }
+
+        $numTokens = count($tokens);
+        for ($i = 0; $i < $numTokens; $i++) {
+            // Check to see if the current token starts a new scope.
+            if (isset($tokenizer->scopeOpeners[$tokens[$i]['code']]) === true) {
+                if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                    $type    = $tokens[$i]['type'];
+                    $content = str_replace($eolChar, '\n', $tokens[$i]['content']);
+                    echo "\tStart scope map at $i: $type => $content".PHP_EOL;
+                }
+
+                $i = self::_recurseScopeMap($tokens, $numTokens, $tokenizer, $eolChar, $i);
+            }
+        }
+
+        if (PHP_CODESNIFFER_VERBOSITY > 1) {
+            echo "\t*** END SCOPE MAP ***".PHP_EOL;
+        }
+
+    }//end _createScopeMap()
+
+
+    /**
+     * Recurses though the scope openers to build a scope map.
+     *
+     * @param array  &$tokens   The array of tokens to process.
+     * @param int    $numTokens The size of the tokens array.
+     * @param object $tokenizer The tokenizer being used to process this file.
+     * @param string $eolChar   The EOL character to use for splitting strings.
+     * @param int    $stackPtr  The position in the stack of the token that
+     *                          opened the scope (eg. an IF token or FOR token).
+     * @param int    $depth     How many scope levels down we are.
+     *
+     * @return int The position in the stack that closed the scope.
+     */
+    private static function _recurseScopeMap(&$tokens, $numTokens, $tokenizer, $eolChar, $stackPtr, $depth=1)
+    {
+        $opener    = null;
+        $currType  = $tokens[$stackPtr]['code'];
+        $startLine = $tokens[$stackPtr]['line'];
+        $ignore    = false;
+
+        // If the start token for this scope opener is the same as
+        // the scope token, we have already found our opener.
+        if ($currType === $tokenizer->scopeOpeners[$currType]['start']) {
+            $opener = $stackPtr;
+        }
+
+        for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
+            $tokenType = $tokens[$i]['code'];
+
+            if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                $type    = $tokens[$i]['type'];
+                $content = str_replace($eolChar, '\n', $tokens[$i]['content']);
+                echo str_repeat("\t", $depth);
+                echo "Process token $i [";
+                if ($opener !== null) {
+                    echo "opener:$opener;";
+                }
+
+                if ($ignore === true) {
+                    echo 'ignore;';
+                }
+
+                echo "]: $type => $content".PHP_EOL;
+            }
+
+            // Is this an opening condition ?
+            if (isset($tokenizer->scopeOpeners[$tokenType]) === true) {
+                if ($opener === null) {
+                    // Found another opening condition but still haven't
+                    // found our opener, so we are never going to find one.
+                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                        $type = $tokens[$stackPtr]['type'];
+                        echo str_repeat("\t", $depth);
+                        echo "=> Couldn't find scope opener for $stackPtr ($type), bailing".PHP_EOL;
+                    }
+
+                    return $stackPtr;
+                }
+
+                if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                    echo str_repeat("\t", $depth);
+                    echo '* token is an opening condition *'.PHP_EOL;
+                }
+
+                $isShared = ($tokenizer->scopeOpeners[$tokenType]['shared'] === true);
+
+                if (isset($tokens[$i]['scope_condition']) === true) {
+                    // We've been here before.
+                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                        echo str_repeat("\t", $depth);
+                        echo '* already processed, skipping *'.PHP_EOL;
+                    }
+
+                    if ($isShared === false && isset($tokens[$i]['scope_closer']) === true) {
+                        $i = $tokens[$i]['scope_closer'];
+                    }
+
+                    continue;
+                } else if ($currType === $tokenType && $isShared === false && $opener === null) {
+                    // We haven't yet found our opener, but we have found another
+                    // scope opener which is the same type as us, and we don't
+                    // share openers, so we will never find one.
+                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                        echo str_repeat("\t", $depth);
+                        echo '* it was another token\'s opener, bailing *'.PHP_EOL;
+                    }
+
+                    return $stackPtr;
+                } else {
+                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                        echo str_repeat("\t", $depth);
+                        echo '* searching for opener *'.PHP_EOL;
+                    }
+
+                    $i = self::_recurseScopeMap($tokens, $numTokens, $tokenizer, $eolChar, $i, ($depth + 1));
+                }//end if
+            }//end if start scope
+
+            if ($tokenType === $tokenizer->scopeOpeners[$currType]['start'] && $opener === null) {
+                if ($tokenType === T_OPEN_CURLY_BRACKET) {
+                    // Make sure this is actually an opener and not a
+                    // string offset (e.g., $var{0}).
+                    for ($x = ($i - 1); $x > 0; $x--) {
+                        if (in_array($tokens[$x]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === true) {
+                            continue;
+                        } else {
+                            // If the first non-whitespace/comment token is a
+                            // variable or object operator then this is an opener
+                            // for a string offset and not a scope.
+                            if ($tokens[$x]['code'] === T_VARIABLE || $tokens[$x]['code'] === T_OBJECT_OPERATOR) {
+                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                                    echo str_repeat("\t", $depth);
+                                    echo '* ignoring curly brace *'.PHP_EOL;
+                                }
+
+                                $ignore = true;
+                            }//end if
+
+                            break;
+                        }//end if
+                    }//end for
+                }//end if
+
+                if ($ignore === false) {
+                    // We found the opening scope token for $currType.
+                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                        $type = $tokens[$stackPtr]['type'];
+                        echo str_repeat("\t", $depth);
+                        echo "=> Found scope opener for $stackPtr ($type)".PHP_EOL;
+                    }
+
+                    $opener = $i;
+                }
+            } else if ($tokenType === $tokenizer->scopeOpeners[$currType]['end'] && $opener !== null) {
+                if ($ignore === true && $tokenType === T_CLOSE_CURLY_BRACKET) {
+                    // The last opening bracket must have been for a string
+                    // offset or alike, so let's ignore it.
+                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                        echo str_repeat("\t", $depth);
+                        echo '* finished ignoring curly brace *'.PHP_EOL;
+                    }
+
+                    $ignore = false;
+                } else {
+                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                        $type = $tokens[$stackPtr]['type'];
+                        echo str_repeat("\t", $depth);
+                        echo "=> Found scope closer for $stackPtr ($type)".PHP_EOL;
+                    }
+
+                    foreach (array($stackPtr, $opener, $i) as $token) {
+                        $tokens[$token]['scope_condition'] = $stackPtr;
+                        $tokens[$token]['scope_opener']    = $opener;
+                        $tokens[$token]['scope_closer']    = $i;
+                    }
+
+                    if ($tokenizer->scopeOpeners[$tokens[$stackPtr]['code']]['shared'] === true) {
+                        return $opener;
+                    } else {
+                        return $i;
+                    }
+                }//end if
+            } else if ($tokenType === T_OPEN_PARENTHESIS) {
+                if (isset($tokens[$i]['parenthesis_owner']) === true) {
+                    $owner = $tokens[$i]['parenthesis_owner'];
+                    if (in_array($tokens[$owner]['code'], PHP_CodeSniffer_Tokens::$scopeOpeners) === true && isset($tokens[$i]['parenthesis_closer']) === true) {
+                        // If we get into here, then we opened a parenthesis for
+                        // a scope (eg. an if or else if). We can just skip to
+                        // the closing parenthesis.
+                        $i = $tokens[$i]['parenthesis_closer'];
+
+                        // Update the start of the line so that when we check to see
+                        // if the closing parenthesis is more than 3 lines away from
+                        // the statement, we check from the closing parenthesis.
+                        $startLine = $tokens[$tokens[$i]['parenthesis_closer']]['line'];
+
+                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                            echo str_repeat("\t", $depth);
+                            echo '* skipping parenthesis *'.PHP_EOL;
+                        }
+                    }
+                }
+            } else if ($tokenType === T_OPEN_CURLY_BRACKET && $opener !== null) {
+                // We opened something that we don't have a scope opener for.
+                // Examples of this are curly brackets for string offsets etc.
+                // We want to ignore this so that we don't have an invalid scope
+                // map.
+                if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                    echo str_repeat("\t", $depth);
+                    echo '* ignoring curly brace *'.PHP_EOL;
+                }
+
+                $ignore = true;
+            } else if ($opener === null && isset($tokenizer->scopeOpeners[$currType]) === true) {
+                // If we still haven't found the opener after 3 lines,
+                // we're not going to find it, unless we know it requires
+                // an opener, in which case we better keep looking.
+                if ($tokens[$i]['line'] >= ($startLine + 3)) {
+                    if ($tokenizer->scopeOpeners[$currType]['strict'] === true) {
+                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                            $type  = $tokens[$stackPtr]['type'];
+                            $lines = ($tokens[$i]['line'] - $startLine);
+                            echo str_repeat("\t", $depth);
+                            echo "=> Still looking for $stackPtr ($type) scope opener after $lines lines".PHP_EOL;
+                        }
+                    } else {
+                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                            $type = $tokens[$stackPtr]['type'];
+                            echo str_repeat("\t", $depth);
+                            echo "=> Couldn't find scope opener for $stackPtr ($type), bailing".PHP_EOL;
+                        }
+
+                        return $stackPtr;
+                    }
+                }
+            } else if ($opener !== null && $tokenType !== T_BREAK && in_array($tokenType, $tokenizer->endScopeTokens) === true) {
+                if (isset($tokens[$i]['scope_condition']) === false) {
+                    if ($ignore === true) {
+                        // We found the end token for the opener we were ignoring.
+                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                            echo str_repeat("\t", $depth);
+                            echo '* finished ignoring curly brace *'.PHP_EOL;
+                        }
+
+                        $ignore = false;
+                    } else {
+                        // We found a token that closes the scope but it doesn't
+                        // have a condition, so it belongs to another token and
+                        // our token doesn't have a closer, so pretend this is
+                        // the closer.
+                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                            $type = $tokens[$stackPtr]['type'];
+                            echo str_repeat("\t", $depth);
+                            echo "=> Found (unexpected) scope closer for $stackPtr ($type)".PHP_EOL;
+                        }
+
+                        foreach (array($stackPtr, $opener) as $token) {
+                            $tokens[$token]['scope_condition'] = $stackPtr;
+                            $tokens[$token]['scope_opener']    = $opener;
+                            $tokens[$token]['scope_closer']    = $i;
+                        }
+
+                        return ($i - 1);
+                    }//end if
+                }//end if
+            }//end if
+        }//end for
+
+        return $stackPtr;
+
+    }//end _recurseScopeMap()
+
+
+    /**
+     * Constructs the level map.
+     *
+     * The level map adds a 'level' indice to each token which indicates the
+     * depth that a token within a set of scope blocks. It also adds a
+     * 'condition' indice which is an array of the scope conditions that opened
+     * each of the scopes - position 0 being the first scope opener.
+     *
+     * @param array  &$tokens   The array of tokens to process.
+     * @param object $tokenizer The tokenizer being used to process this file.
+     * @param string $eolChar   The EOL character to use for splitting strings.
+     *
+     * @return void
+     */
+    private static function _createLevelMap(&$tokens, $tokenizer, $eolChar)
+    {
+        if (PHP_CODESNIFFER_VERBOSITY > 1) {
+            echo "\t*** START LEVEL MAP ***".PHP_EOL;
+        }
+
+        $numTokens  = count($tokens);
+        $level      = 0;
+        $conditions = array();
+        $lastOpener = null;
+        $openers    = array();
+
+        for ($i = 0; $i < $numTokens; $i++) {
+            if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                $type    = $tokens[$i]['type'];
+                $line    = $tokens[$i]['line'];
+                $content = str_replace($eolChar, '\n', $tokens[$i]['content']);
+                echo str_repeat("\t", ($level + 1));
+                echo "Process token $i on line $line [lvl:$level;";
+                if (empty($conditions) !== true) {
+                    $condString = 'conds;';
+                    foreach ($conditions as $condition) {
+                        $condString .= token_name($condition).',';
+                    }
+
+                    echo rtrim($condString, ',').';';
+                }
+
+                echo "]: $type => $content".PHP_EOL;
+            }
+
+            $tokens[$i]['level']      = $level;
+            $tokens[$i]['conditions'] = $conditions;
+
+            if (isset($tokens[$i]['scope_condition']) === true) {
+                // Check to see if this token opened the scope.
+                if ($tokens[$i]['scope_opener'] === $i) {
+                    $stackPtr = $tokens[$i]['scope_condition'];
+                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                        $type = $tokens[$stackPtr]['type'];
+                        echo str_repeat("\t", ($level + 1));
+                        echo "=> Found scope opener for $stackPtr ($type)".PHP_EOL;
+                    }
+
+                    $stackPtr = $tokens[$i]['scope_condition'];
+
+                    // If we find a scope opener that has a shared closer,
+                    // then we need to go back over the condition map that we
+                    // just created and fix ourselves as we just added some
+                    // conditions where there was none. This happens for T_CASE
+                    // statements that are using the same break statement.
+                    if ($lastOpener !== null && $tokens[$lastOpener]['scope_closer'] === $tokens[$i]['scope_closer']) {
+                        // This opener shares its closer with the previous opener,
+                        // but we still need to check if the two openers share their
+                        // closer with each other directly (like CASE and DEFAULT)
+                        // or if they are just sharing because one doesn't have a
+                        // closer (like CASE with no BREAK using a SWITCHes closer).
+                        $thisType = $tokens[$tokens[$i]['scope_condition']]['code'];
+                        $opener   = $tokens[$lastOpener]['scope_condition'];
+                        if (in_array($tokens[$opener]['code'], $tokenizer->scopeOpeners[$thisType]['with']) === true) {
+                            $badToken = $tokens[$lastOpener]['scope_condition'];
+                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                                $type = $tokens[$badToken]['type'];
+                                echo str_repeat("\t", ($level + 1));
+                                echo "* shared closer, cleaning up $badToken ($type) *".PHP_EOL;
+                            }
+
+                            for ($x = $tokens[$i]['scope_condition']; $x <= $i; $x++) {
+                                $oldConditions = $tokens[$x]['conditions'];
+                                $oldLevel      = $tokens[$x]['level'];
+                                $tokens[$x]['level']--;
+                                unset($tokens[$x]['conditions'][$badToken]);
+                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                                    $type     = $tokens[$x]['type'];
+                                    $oldConds = '';
+                                    foreach ($oldConditions as $condition) {
+                                        $oldConds .= token_name($condition).',';
+                                    }
+
+                                    $oldConds = rtrim($oldConds, ',');
+
+                                    $newConds = '';
+                                    foreach ($tokens[$x]['conditions'] as $condition) {
+                                        $newConds .= token_name($condition).',';
+                                    }
+
+                                    $newConds = rtrim($newConds, ',');
+
+                                    $newLevel = $tokens[$x]['level'];
+                                    echo str_repeat("\t", ($level + 1));
+                                    echo "* cleaned $x ($type) *".PHP_EOL;
+                                    echo str_repeat("\t", ($level + 2));
+                                    echo "=> level changed from $oldLevel to $newLevel".PHP_EOL;
+                                    echo str_repeat("\t", ($level + 2));
+                                    echo "=> conditions changed from $oldConds to $newConds".PHP_EOL;
+                                }//end if
+                            }//end for
+
+                            unset($conditions[$badToken]);
+                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                                $type = $tokens[$badToken]['type'];
+                                echo str_repeat("\t", ($level + 1));
+                                echo "* token $badToken ($type) removed from conditions array *".PHP_EOL;
+                            }
+
+                            unset ($openers[$lastOpener]);
+
+                            $level--;
+                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                                echo str_repeat("\t", ($level + 2));
+                                echo '* level decreased *'.PHP_EOL;
+                            }
+                        }//end if
+                    }//end if
+
+                    $level++;
+                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                        echo str_repeat("\t", ($level + 1));
+                        echo '* level increased *'.PHP_EOL;
+                    }
+
+                    $conditions[$stackPtr] = $tokens[$stackPtr]['code'];
+                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                        $type = $tokens[$stackPtr]['type'];
+                        echo str_repeat("\t", ($level + 1));
+                        echo "* token $stackPtr ($type) added to conditions array *".PHP_EOL;
+                    }
+
+                    $lastOpener = $tokens[$i]['scope_opener'];
+                    if ($lastOpener !== null) {
+                        $openers[$lastOpener] = $lastOpener;
+                    }
+                } else if ($tokens[$i]['scope_closer'] === $i) {
+                    $removedCondition = false;
+                    foreach (array_reverse($openers) as $opener) {
+                        if ($tokens[$opener]['scope_closer'] === $i) {
+                            $oldOpener = array_pop($openers);
+                            if (empty($openers) === false) {
+                                $lastOpener           = array_pop($openers);
+                                $openers[$lastOpener] = $lastOpener;
+                            } else {
+                                $lastOpener = null;
+                            }
+
+                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                                $type = $tokens[$oldOpener]['type'];
+                                echo str_repeat("\t", ($level + 1));
+                                echo "=> Found scope closer for $oldOpener ($type)".PHP_EOL;
+                            }
+
+                            if ($removedCondition === false) {
+                                $oldCondition = array_pop($conditions);
+                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                                    echo str_repeat("\t", ($level + 1));
+                                    echo '* token '.token_name($oldCondition).' removed from conditions array *'.PHP_EOL;
+                                }
+
+                                $level--;
+                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
+                                    echo str_repeat("\t", ($level + 2));
+                                    echo '* level decreased *'.PHP_EOL;
+                                }
+                            }
+
+                            $tokens[$i]['level']      = $level;
+                            $tokens[$i]['conditions'] = $conditions;
+                        }//end if
+                    }//end foreach
+                }//end if
+            }//end if
+        }//end for
+
+        if (PHP_CODESNIFFER_VERBOSITY > 1) {
+            echo "\t*** END LEVEL MAP ***".PHP_EOL;
+        }
+
+    }//end _createLevelMap()
+
+
+    /**
+     * Returns the declaration names for T_CLASS, T_INTERFACE and T_FUNCTION tokens.
+     *
+     * @param int $stackPtr The position of the declaration token which
+     *                      declared the class, interface or function.
+     *
+     * @return string The name of the class, interface or function.
+     * @throws PHP_CodeSniffer_Exception If the specified token is not of type
+     *                                   T_FUNCTION, T_CLASS or T_INTERFACE.
+     */
+    public function getDeclarationName($stackPtr)
+    {
+        $tokenCode = $this->_tokens[$stackPtr]['code'];
+        if ($tokenCode !== T_FUNCTION && $tokenCode !== T_CLASS && $tokenCode !== T_INTERFACE) {
+            throw new PHP_CodeSniffer_Exception('Token type is not T_FUNCTION, T_CLASS OR T_INTERFACE');
+        }
+
+        $token = $this->findNext(T_STRING, $stackPtr);
+        return $this->_tokens[$token]['content'];
+
+    }//end getDeclarationName()
+
+
+    /**
+     * Returns the method parameters for the specified T_FUNCTION token.
+     *
+     * Each parameter is in the following format:
+     *
+     * <code>
+     *   0 => array(
+     *         'name'              => '$var',  // The variable name.
+     *         'pass_by_reference' => false,   // Passed by reference.
+     *         'type_hint'         => string,  // Type hint for array or custom type
+     *        )
+     * </code>
+     *
+     * Parameters with default values have and additional array indice of
+     * 'default' with the value of the default as a string.
+     *
+     * @param int $stackPtr The position in the stack of the T_FUNCTION token
+     *                      to acquire the parameters for.
+     *
+     * @return array()
+     * @throws PHP_CodeSniffer_Exception If the specified $stackPtr is not of
+     *                                   type T_FUNCTION.
+     */
+    public function getMethodParameters($stackPtr)
+    {
+        if ($this->_tokens[$stackPtr]['code'] !== T_FUNCTION) {
+            throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION');
+        }
+
+        $opener = $this->_tokens[$stackPtr]['parenthesis_opener'];
+        $closer = $this->_tokens[$stackPtr]['parenthesis_closer'];
+
+        $vars            = array();
+        $currVar         = null;
+        $defaultStart    = null;
+        $paramCount      = 0;
+        $passByReference = false;
+        $typeHint        = '';
+
+        for ($i = ($opener + 1); $i <= $closer; $i++) {
+            // Check to see if this token has a parenthesis opener. If it does
+            // its likely to be an array, which might have arguments in it, which
+            // we cause problems in our parsing below, so lets just skip to the
+            // end of it.
+            if (isset($this->_tokens[$i]['parenthesis_opener']) === true) {
+                // Don't do this if its the close parenthesis for the method.
+                if ($i !== $this->_tokens[$i]['parenthesis_closer']) {
+                    $i = ($this->_tokens[$i]['parenthesis_closer'] + 1);
+                }
+            }
+
+            switch ($this->_tokens[$i]['code']) {
+            case T_BITWISE_AND:
+                $passByReference = true;
+                break;
+            case T_VARIABLE:
+                $currVar = $i;
+                break;
+            case T_ARRAY_HINT:
+                $typeHint = $this->_tokens[$i]['content'];
+                break;
+            case T_STRING:
+                // This is a string, so it may be a type hint, but it could
+                // also be a constant used as a default value.
+                $prevComma = $this->findPrevious(T_COMMA, $i, $opener);
+                if ($prevComma !== false) {
+                    $nextEquals = $this->findNext(T_EQUAL, $prevComma, $i);
+                    if ($nextEquals !== false) {
+                        break;
+                    }
+                }
+
+                $typeHint = $this->_tokens[$i]['content'];
+                break;
+            case T_CLOSE_PARENTHESIS:
+            case T_COMMA:
+                // If it's null, then there must be no parameters for this
+                // method.
+                if ($currVar === null) {
+                    continue;
+                }
+
+                $vars[$paramCount]         = array();
+                $vars[$paramCount]['name'] = $this->_tokens[$currVar]['content'];
+
+                if ($defaultStart !== null) {
+                    $vars[$paramCount]['default'] = $this->getTokensAsString($defaultStart, ($i - $defaultStart));
+                }
+
+                $vars[$paramCount]['pass_by_reference'] = $passByReference;
+                $vars[$paramCount]['type_hint']         = $typeHint;
+
+                // Reset the vars, as we are about to process the next parameter.
+                $defaultStart    = null;
+                $passByReference = false;
+                $typeHint        = '';
+
+                $paramCount++;
+                break;
+            case T_EQUAL:
+                $defaultStart = ($i + 1);
+                break;
+            }//end switch
+        }//end for
+
+        return $vars;
+
+    }//end getMethodParameters()
+
+
+    /**
+     * Returns the visibility and implementation properies of a method.
+     *
+     * The format of the array is:
+     * <code>
+     *   array(
+     *    'scope'           => 'public', // public private or protected
+     *    'scope_specified' => true,     // true is scope keyword was found.
+     *    'is_abstract'     => false,    // true if the abstract keyword was found.
+     *    'is_final'        => false,    // true if the final keyword was found.
+     *    'is_static'       => false,    // true if the static keyword was found.
+     *   );
+     * </code>
+     *
+     * @param int $stackPtr The position in the stack of the T_FUNCTION token to
+     *                      acquire the properties for.
+     *
+     * @return array
+     * @throws PHP_CodeSniffer_Exception If the specified position is not a
+     *                                   T_FUNCTION token.
+     */
+    public function getMethodProperties($stackPtr)
+    {
+        if ($this->_tokens[$stackPtr]['code'] !== T_FUNCTION) {
+            throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_FUNCTION');
+        }
+
+        $valid = array(
+                  T_PUBLIC,
+                  T_PRIVATE,
+                  T_PROTECTED,
+                  T_STATIC,
+                  T_FINAL,
+                  T_ABSTRACT,
+                  T_WHITESPACE,
+                  T_COMMENT,
+                  T_DOC_COMMENT,
+                 );
+
+        $scope          = 'public';
+        $scopeSpecified = false;
+        $isAbstract     = false;
+        $isFinal        = false;
+        $isStatic       = false;
+
+        for ($i = ($stackPtr - 1); $i > 0; $i--) {
+            if (in_array($this->_tokens[$i]['code'], $valid) === false) {
+                break;
+            }
+
+            switch ($this->_tokens[$i]['code']) {
+            case T_PUBLIC:
+                $scope          = 'public';
+                $scopeSpecified = true;
+                break;
+            case T_PRIVATE:
+                $scope          = 'private';
+                $scopeSpecified = true;
+                break;
+            case T_PROTECTED:
+                $scope          = 'protected';
+                $scopeSpecified = true;
+                break;
+            case T_ABSTRACT:
+                $isAbstract = true;
+                break;
+            case T_FINAL:
+                $isFinal = true;
+                break;
+            case T_STATIC:
+                $isStatic = true;
+                break;
+            }//end switch
+        }//end for
+
+        return array(
+                'scope'           => $scope,
+                'scope_specified' => $scopeSpecified,
+                'is_abstract'     => $isAbstract,
+                'is_final'        => $isFinal,
+                'is_static'       => $isStatic,
+               );
+
+    }//end getMethodProperties()
+
+
+    /**
+     * Returns the visibility and implementation properies of the class member
+     * variable found  at the specified position in the stack.
+     *
+     * The format of the array is:
+     *
+     * <code>
+     *   array(
+     *    'scope'       => 'public', // public private or protected
+     *    'is_static'   => false,    // true if the static keyword was found.
+     *   );
+     * </code>
+     *
+     * @param int $stackPtr The position in the stack of the T_VARIABLE token to
+     *                      acquire the properties for.
+     *
+     * @return array
+     * @throws PHP_CodeSniffer_Exception If the specified position is not a
+     *                                   T_VARIABLE token, or if the position is not
+     *                                   a class member variable.
+     */
+    public function getMemberProperties($stackPtr)
+    {
+        if ($this->_tokens[$stackPtr]['code'] !== T_VARIABLE) {
+            throw new PHP_CodeSniffer_Exception('$stackPtr must be of type T_VARIABLE');
+        }
+
+        end($this->_tokens[$stackPtr]['conditions']);
+        $ptr = key($this->_tokens[$stackPtr]['conditions']);
+        if (isset($this->_tokens[$ptr]) === false || $this->_tokens[$ptr]['code'] !== T_CLASS) {
+            if (isset($this->_tokens[$ptr]) === true && $this->_tokens[$ptr]['code'] === T_INTERFACE) {
+                $error = 'Possible parse error: interfaces may not include member vars';
+                $this->addWarning($error, $stackPtr);
+                return array();
+            } else {
+                throw new PHP_CodeSniffer_Exception('$stackPtr is not a class member var');
+            }
+        }
+
+        $valid = array(
+                  T_PUBLIC,
+                  T_PRIVATE,
+                  T_PROTECTED,
+                  T_STATIC,
+                  T_WHITESPACE,
+                  T_COMMENT,
+                  T_DOC_COMMENT,
+                 );
+
+        $scope          = 'public';
+        $scopeSpecified = false;
+        $isStatic       = false;
+
+        for ($i = ($stackPtr - 1); $i > 0; $i--) {
+            if (in_array($this->_tokens[$i]['code'], $valid) === false) {
+                break;
+            }
+
+            switch ($this->_tokens[$i]['code']) {
+            case T_PUBLIC:
+                $scope          = 'public';
+                $scopeSpecified = true;
+                break;
+            case T_PRIVATE:
+                $scope          = 'private';
+                $scopeSpecified = true;
+                break;
+            case T_PROTECTED:
+                $scope          = 'protected';
+                $scopeSpecified = true;
+                break;
+            case T_STATIC:
+                $isStatic = true;
+                break;
+            }
+        }//end for
+
+        return array(
+                'scope'           => $scope,
+                'scope_specified' => $scopeSpecified,
+                'is_static'       => $isStatic,
+               );
+
+    }//end getMemberProperties()
+
+
+    /**
+     * Determine if the passed token is a reference operator.
+     *
+     * Returns true if the specified token position represents a reference.
+     * Returns false if the token represents a bitwise operator.
+     *
+     * @param int $stackPtr The position of the T_BITWISE_AND token.
+     *
+     * @return boolean
+     */
+    public function isReference($stackPtr)
+    {
+        if ($this->_tokens[$stackPtr]['code'] !== T_BITWISE_AND) {
+            return false;
+        }
+
+        $tokenBefore = $this->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
+
+        if ($this->_tokens[$tokenBefore]['code'] === T_FUNCTION) {
+            // Function returns a reference.
+            return true;
+        }
+
+        if ($this->_tokens[$tokenBefore]['code'] === T_DOUBLE_ARROW) {
+            // Inside a foreach loop, this is a reference.
+            return true;
+        }
+
+        if ($this->_tokens[$tokenBefore]['code'] === T_AS) {
+            // Inside a foreach loop, this is a reference.
+            return true;
+        }
+
+        if (in_array($this->_tokens[$tokenBefore]['code'], PHP_CodeSniffer_Tokens::$assignmentTokens) === true) {
+            // This is directly after an assignment. It's a reference. Even if
+            // it is part of an operation, the other tests will handle it.
+            return true;
+        }
+
+        if (isset($this->_tokens[$stackPtr]['nested_parenthesis']) === true) {
+            $brackets    = $this->_tokens[$stackPtr]['nested_parenthesis'];
+            $lastBracket = array_pop($brackets);
+            if (isset($this->_tokens[$lastBracket]['parenthesis_owner']) === true) {
+                $owner = $this->_tokens[$this->_tokens[$lastBracket]['parenthesis_owner']];
+                if ($owner['code'] === T_FUNCTION) {
+                    // Inside a function declaration, this is a reference.
+                    return true;
+                }
+            }
+        }
+
+        return false;
+
+    }//end isReference()
+
+
+    /**
+     * Returns the content of the tokens from the specified start position in
+     * the token stack for the specified legnth.
+     *
+     * @param int $start  The position to start from in the token stack.
+     * @param int $length The length of tokens to traverse from the start pos.
+     *
+     * @return string The token contents.
+     */
+    public function getTokensAsString($start, $length)
+    {
+        $str = '';
+        $end = ($start + $length);
+        for ($i = $start; $i < $end; $i++) {
+            $str .= $this->_tokens[$i]['content'];
+        }
+
+        return $str;
+
+    }//end getTokensAsString()
+
+
+    /**
+     * Returns the position of the next specified token(s).
+     *
+     * If a value is specified, the next token of the specified type(s)
+     * containing the specified value will be returned.
+     *
+     * Returns false if no token can be found.
+     *
+     * @param int|array $types   The type(s) of tokens to search for.
+     * @param int       $start   The position to start searching from in the
+     *                           token stack.
+     * @param int       $end     The end position to fail if no token is found.
+     *                           if not specified or null, end will default to
+     *                           the start of the token stack.
+     * @param bool      $exclude If true, find the next token that are NOT of
+     *                           the types specified in $types.
+     * @param string    $value   The value that the token(s) must be equal to.
+     *                           If value is ommited, tokens with any value will
+     *                           be returned.
+     * @param bool      $local   If true, tokens outside the current statement
+     *                           will not be cheked. IE. checking will stop
+     *                           at the next semi-colon found.
+     *
+     * @return int | bool
+     * @see findNext()
+     */
+    public function findPrevious($types, $start, $end=null, $exclude=false, $value=null, $local=false)
+    {
+        $types = (array) $types;
+
+        if ($end === null) {
+            $end = 0;
+        }
+
+        for ($i = $start; $i >= $end; $i--) {
+            $found = (bool) $exclude;
+            foreach ($types as $type) {
+                if ($this->_tokens[$i]['code'] === $type) {
+                    $found = !$exclude;
+                    break;
+                }
+            }
+
+            if ($found === true) {
+                if ($value === null) {
+                    return $i;
+                } else if ($this->_tokens[$i]['content'] === $value) {
+                    return $i;
+                }
+            }
+
+            if ($local === true && $this->_tokens[$i]['code'] === T_SEMICOLON) {
+                break;
+            }
+        }//end for
+
+        return false;
+
+    }//end findPrevious()
+
+
+    /**
+     * Returns the position of the next specified token(s).
+     *
+     * If a value is specified, the next token of the specified type(s)
+     * containing the specified value will be returned.
+     *
+     * Returns false if no token can be found.
+     *
+     * @param int|array $types   The type(s) of tokens to search for.
+     * @param int       $start   The position to start searching from in the
+     *                           token stack.
+     * @param int       $end     The end position to fail if no token is found.
+     *                           if not specified or null, end will default to
+     *                           the end of the token stack.
+     * @param bool      $exclude If true, find the next token that is NOT of
+     *                           a type specified in $types.
+     * @param string    $value   The value that the token(s) must be equal to.
+     *                           If value is ommited, tokens with any value will
+     *                           be returned.
+     * @param bool      $local   If true, tokens outside the current statement
+     *                           will not be cheked. IE. checking will stop
+     *                           at the next semi-colon found.
+     *
+     * @return int | bool
+     * @see findPrevious()
+     */
+    public function findNext($types, $start, $end=null, $exclude=false, $value=null, $local=false)
+    {
+        $types = (array) $types;
+
+        if ($end === null || $end > $this->numTokens) {
+            $end = $this->numTokens;
+        }
+
+        for ($i = $start; $i < $end; $i++) {
+            $found = (bool) $exclude;
+            foreach ($types as $type) {
+                if ($this->_tokens[$i]['code'] === $type) {
+                    $found = !$exclude;
+                    break;
+                }
+            }
+
+            if ($found === true) {
+                if ($value === null) {
+                    return $i;
+                } else if ($this->_tokens[$i]['content'] === $value) {
+                    return $i;
+                }
+            }
+
+            if ($local === true && $this->_tokens[$i]['code'] === T_SEMICOLON) {
+                break;
+            }
+        }//end for
+
+        return false;
+
+    }//end findNext()
+
+
+    /**
+     * Returns the position of the first token on a line, matching given type.
+     *
+     * Returns false if no token can be found.
+     *
+     * @param int|array $types   The type(s) of tokens to search for.
+     * @param int       $start   The position to start searching from in the
+     *                           token stack. The first token matching on
+     *                           this line before this token will be returned.
+     * @param bool      $exclude If true, find the token that is NOT of
+     *                           the types specified in $types.
+     * @param string    $value   The value that the token must be equal to.
+     *                           If value is ommited, tokens with any value will
+     *                           be returned.
+     *
+     * @return int | bool
+     */
+    public function findFirstOnLine($types, $start, $exclude=false, $value=null)
+    {
+        if (is_array($types) === false) {
+            $types = array($types);
+        }
+
+        $foundToken = false;
+
+        for ($i = $start; $i >= 0; $i--) {
+            if ($this->_tokens[$i]['line'] < $this->_tokens[$start]['line']) {
+                break;
+            }
+
+            $found = $exclude;
+            foreach ($types as $type) {
+                if ($exclude === false) {
+                    if ($this->_tokens[$i]['code'] === $type) {
+                        $found = true;
+                        break;
+                    }
+                } else {
+                    if ($this->_tokens[$i]['code'] === $type) {
+                        $found = false;
+                        break;
+                    }
+                }
+            }
+
+            if ($found === true) {
+                if ($value === null) {
+                    $foundToken = $i;
+                } else if ($this->_tokens[$i]['content'] === $value) {
+                    $foundToken = $i;
+                }
+            }
+        }//end for
+
+        return $foundToken;
+
+    }//end findFirstOnLine()
+
+
+    /**
+     * Determine if the passed token has a condition of one of the passed types.
+     *
+     * @param int       $stackPtr The position of the token we are checking.
+     * @param int|array $types    The type(s) of tokens to search for.
+     *
+     * @return boolean
+     */
+    public function hasCondition($stackPtr, $types)
+    {
+        // Check for the existence of the token.
+        if (isset($this->_tokens[$stackPtr]) === false) {
+            return false;
+        }
+
+        // Make sure the token has conditions.
+        if (isset($this->_tokens[$stackPtr]['conditions']) === false) {
+            return false;
+        }
+
+        $types      = (array) $types;
+        $conditions = $this->_tokens[$stackPtr]['conditions'];
+
+        foreach ($types as $type) {
+            if (in_array($type, $conditions) === true) {
+                // We found a token with the required type.
+                return true;
+            }
+        }
+
+        return false;
+
+    }//end hasCondition()
+
+
+}//end class
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Sniff.php b/lib/pear/PHP/CodeSniffer/Sniff.php
new file mode 100644 (file)
index 0000000..2c97582
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/AbstractPatternSniff.php b/lib/pear/PHP/CodeSniffer/Standards/AbstractPatternSniff.php
new file mode 100644 (file)
index 0000000..31b7a86
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/AbstractScopeSniff.php b/lib/pear/PHP/CodeSniffer/Standards/AbstractScopeSniff.php
new file mode 100644 (file)
index 0000000..f47c47a
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/AbstractVariableSniff.php b/lib/pear/PHP/CodeSniffer/Standards/AbstractVariableSniff.php
new file mode 100644 (file)
index 0000000..1ad7a6c
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/CodingStandard.php b/lib/pear/PHP/CodeSniffer/Standards/CodingStandard.php
new file mode 100644 (file)
index 0000000..53d8748
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/IncorrectPatternException.php b/lib/pear/PHP/CodeSniffer/Standards/IncorrectPatternException.php
new file mode 100644 (file)
index 0000000..d7f8e45
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/MoodleCodingStandard.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/MoodleCodingStandard.php
new file mode 100644 (file)
index 0000000..51f135c
--- /dev/null
@@ -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
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Classes/ClassDeclarationSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Classes/ClassDeclarationSniff.php
new file mode 100644 (file)
index 0000000..8ddc123
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/EmptyStatementSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/EmptyStatementSniff.php
new file mode 100644 (file)
index 0000000..ad2ada4
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/ForLoopShouldBeWhileLoopSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/ForLoopShouldBeWhileLoopSniff.php
new file mode 100644 (file)
index 0000000..159e7b3
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/ForLoopWithTestFunctionCallSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/ForLoopWithTestFunctionCallSniff.php
new file mode 100644 (file)
index 0000000..7977f55
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/JumbledIncrementerSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/JumbledIncrementerSniff.php
new file mode 100644 (file)
index 0000000..d1c08a5
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UnconditionalIfStatementSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UnconditionalIfStatementSniff.php
new file mode 100644 (file)
index 0000000..8f0b463
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UnnecessaryFinalModifierSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UnnecessaryFinalModifierSniff.php
new file mode 100644 (file)
index 0000000..9ad3fbc
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UnusedFunctionParameterSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UnusedFunctionParameterSniff.php
new file mode 100644 (file)
index 0000000..244aa09
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UselessOverridingMethodSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/CodeAnalysis/UselessOverridingMethodSniff.php
new file mode 100644 (file)
index 0000000..9dd1a55
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/ClassCommentSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/ClassCommentSniff.php
new file mode 100644 (file)
index 0000000..9ac8e14
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/FileCommentSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/FileCommentSniff.php
new file mode 100644 (file)
index 0000000..caf481f
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/FunctionCommentSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/FunctionCommentSniff.php
new file mode 100644 (file)
index 0000000..e4daed6
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/InlineCommentSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Commenting/InlineCommentSniff.php
new file mode 100644 (file)
index 0000000..695a2b0
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/ControlStructures/ControlSignatureSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/ControlStructures/ControlSignatureSniff.php
new file mode 100644 (file)
index 0000000..ae5caf4
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/ControlStructures/InlineControlStructureSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/ControlStructures/InlineControlStructureSniff.php
new file mode 100644 (file)
index 0000000..63803d9
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Files/IncludingFileSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Files/IncludingFileSniff.php
new file mode 100644 (file)
index 0000000..35be2b1
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Files/LineEndingsSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Files/LineEndingsSniff.php
new file mode 100644 (file)
index 0000000..8ab212f
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Files/LineLengthSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Files/LineLengthSniff.php
new file mode 100644 (file)
index 0000000..0e62631
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Formatting/SpaceAfterCastSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Formatting/SpaceAfterCastSniff.php
new file mode 100644 (file)
index 0000000..4dee2b7
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php
new file mode 100644 (file)
index 0000000..d559a7a
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/FunctionCallSignatureSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/FunctionCallSignatureSniff.php
new file mode 100644 (file)
index 0000000..a778096
--- /dev/null
@@ -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
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/FunctionDeclarationSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/FunctionDeclarationSniff.php
new file mode 100644 (file)
index 0000000..76e03a5
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/LowercaseFunctionKeywordsSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/LowercaseFunctionKeywordsSniff.php
new file mode 100644 (file)
index 0000000..afbe506
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/ValidDefaultValueSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Functions/ValidDefaultValueSniff.php
new file mode 100644 (file)
index 0000000..f906472
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/UpperCaseConstantNameSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/UpperCaseConstantNameSniff.php
new file mode 100644 (file)
index 0000000..533ed85
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/ValidClassNameSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/ValidClassNameSniff.php
new file mode 100644 (file)
index 0000000..accc350
--- /dev/null
@@ -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
+
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/ValidFunctionNameSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/ValidFunctionNameSniff.php
new file mode 100644 (file)
index 0000000..1eb1148
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/ValidVariableNameSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/NamingConventions/ValidVariableNameSniff.php
new file mode 100644 (file)
index 0000000..56ae2b4
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/PHP/DisallowShortOpenTagSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/PHP/DisallowShortOpenTagSniff.php
new file mode 100644 (file)
index 0000000..f61266a
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/PHP/LowerCaseConstantSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/PHP/LowerCaseConstantSniff.php
new file mode 100644 (file)
index 0000000..760ad02
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/PHP/LowercasePHPFunctionsSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/PHP/LowercasePHPFunctionsSniff.php
new file mode 100644 (file)
index 0000000..b3d305c
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Strings/DoubleQuoteUsageSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Strings/DoubleQuoteUsageSniff.php
new file mode 100644 (file)
index 0000000..5500830
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Strings/EchoedStringsSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/Strings/EchoedStringsSniff.php
new file mode 100644 (file)
index 0000000..e60a05c
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/DisallowTabIndentSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/DisallowTabIndentSniff.php
new file mode 100644 (file)
index 0000000..678e921
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/MemberVarSpacingSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/MemberVarSpacingSniff.php
new file mode 100644 (file)
index 0000000..d3a2670
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/ScopeClosingBraceSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/ScopeClosingBraceSniff.php
new file mode 100644 (file)
index 0000000..c6144d9
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/ScopeIndentSniff.php b/lib/pear/PHP/CodeSniffer/Standards/Moodle/Sniffs/WhiteSpace/ScopeIndentSniff.php
new file mode 100644 (file)
index 0000000..ef6fa34
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Tokenizers/JS.php b/lib/pear/PHP/CodeSniffer/Tokenizers/JS.php
new file mode 100644 (file)
index 0000000..74b91aa
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Tokenizers/PHP.php b/lib/pear/PHP/CodeSniffer/Tokenizers/PHP.php
new file mode 100644 (file)
index 0000000..c610fda
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/pear/PHP/CodeSniffer/Tokens.php b/lib/pear/PHP/CodeSniffer/Tokens.php
new file mode 100644 (file)
index 0000000..4b96f9f
--- /dev/null
@@ -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
+
+?>
diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml
new file mode 100644 (file)
index 0000000..1eb822b
--- /dev/null
@@ -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>
diff --git a/phpcs b/phpcs
new file mode 100755 (executable)
index 0000000..a1addb1
--- /dev/null
+++ b/phpcs
@@ -0,0 +1,37 @@
+#!/usr/bin/php
+<?php
+/**
+ * PHP_CodeSniffer tokenises PHP code and detects violations of a
+ * defined set of 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
+ */
+
+error_reporting(E_ALL | E_STRICT);
+
+if (is_file(dirname(__FILE__).'/../CodeSniffer/CLI.php') === true) {
+    include_once dirname(__FILE__).'/../CodeSniffer/CLI.php';
+} else {
+    include_once 'lib/pear/PHP/CodeSniffer/CLI.php';
+}
+
+$phpcs = new PHP_CodeSniffer_CLI();
+$phpcs->checkRequirements();
+
+$numErrors = $phpcs->process();
+if ($numErrors === 0) {
+    exit(0);
+} else {
+    exit(1);
+}
+
+?>