updated streamline-setup v2

This commit is contained in:
2025-01-15 08:53:49 -08:00
committed by alec.turner
parent a2ce9248f0
commit 4b569f81b0
20228 changed files with 2932048 additions and 63204 deletions
@@ -12,9 +12,13 @@
namespace PHP_CodeSniffer;
use Exception;
use Phar;
use PHP_CodeSniffer\Exceptions\DeepExitException;
use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Util\Help;
use PHP_CodeSniffer\Util\Standards;
/**
* Stores the configuration used to run PHPCS and PHPCBF.
@@ -81,7 +85,7 @@ class Config
*
* @var string
*/
const VERSION = '3.9.0';
const VERSION = '3.11.2';
/**
* Package stability; either stable, beta or alpha.
@@ -265,7 +269,7 @@ class Config
$cleaned = [];
// Check if the standard name is valid, or if the case is invalid.
$installedStandards = Util\Standards::getInstalledStandards();
$installedStandards = Standards::getInstalledStandards();
foreach ($value as $standard) {
foreach ($installedStandards as $validStandard) {
if (strtolower($standard) === strtolower($validStandard)) {
@@ -420,7 +424,7 @@ class Config
// Check for content on STDIN.
if ($this->stdin === true
|| (Util\Common::isStdinATTY() === false
|| (Common::isStdinATTY() === false
&& feof($handle) === false)
) {
$readStreams = [$handle];
@@ -649,7 +653,7 @@ class Config
throw new DeepExitException($output, 0);
case 'i' :
ob_start();
Util\Standards::printInstalledStandards();
Standards::printInstalledStandards();
$output = ob_get_contents();
ob_end_clean();
throw new DeepExitException($output, 0);
@@ -812,7 +816,7 @@ class Config
try {
$this->setConfigData($key, $value);
} catch (\Exception $e) {
} catch (Exception $e) {
throw new DeepExitException($e->getMessage().PHP_EOL, 3);
}
@@ -840,7 +844,7 @@ class Config
} else {
try {
$this->setConfigData($key, null);
} catch (\Exception $e) {
} catch (Exception $e) {
throw new DeepExitException($e->getMessage().PHP_EOL, 3);
}
@@ -922,7 +926,7 @@ class Config
$this->cache = true;
self::$overriddenDefaults['cache'] = true;
$this->cacheFile = Util\Common::realpath(substr($arg, 6));
$this->cacheFile = Common::realpath(substr($arg, 6));
// It may not exist and return false instead.
if ($this->cacheFile === false) {
@@ -941,9 +945,9 @@ class Config
} else {
if ($dir[0] === '/') {
// An absolute path.
$dir = Util\Common::realpath($dir);
$dir = Common::realpath($dir);
} else {
$dir = Util\Common::realpath(getcwd().'/'.$dir);
$dir = Common::realpath(getcwd().'/'.$dir);
}
if ($dir !== false) {
@@ -964,7 +968,7 @@ class Config
$files = explode(',', substr($arg, 10));
$bootstrap = [];
foreach ($files as $file) {
$path = Util\Common::realpath($file);
$path = Common::realpath($file);
if ($path === false) {
$error = 'ERROR: The specified bootstrap file "'.$file.'" does not exist'.PHP_EOL.PHP_EOL;
$error .= $this->printShortUsage(true);
@@ -978,7 +982,7 @@ class Config
self::$overriddenDefaults['bootstrap'] = true;
} else if (substr($arg, 0, 10) === 'file-list=') {
$fileList = substr($arg, 10);
$path = Util\Common::realpath($fileList);
$path = Common::realpath($fileList);
if ($path === false) {
$error = 'ERROR: The specified file list "'.$fileList.'" does not exist'.PHP_EOL.PHP_EOL;
$error .= $this->printShortUsage(true);
@@ -1001,7 +1005,7 @@ class Config
break;
}
$this->stdinPath = Util\Common::realpath(substr($arg, 11));
$this->stdinPath = Common::realpath(substr($arg, 11));
// It may not exist and return false instead, so use whatever they gave us.
if ($this->stdinPath === false) {
@@ -1014,13 +1018,13 @@ class Config
break;
}
$this->reportFile = Util\Common::realpath(substr($arg, 12));
$this->reportFile = Common::realpath(substr($arg, 12));
// It may not exist and return false instead.
if ($this->reportFile === false) {
$this->reportFile = substr($arg, 12);
$dir = Util\Common::realpath(dirname($this->reportFile));
$dir = Common::realpath(dirname($this->reportFile));
if (is_dir($dir) === false) {
$error = 'ERROR: The specified report file path "'.$this->reportFile.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
$error .= $this->printShortUsage(true);
@@ -1056,7 +1060,7 @@ class Config
break;
}
$this->basepath = Util\Common::realpath(substr($arg, 9));
$this->basepath = Common::realpath(substr($arg, 9));
// It may not exist and return false instead.
if ($this->basepath === false) {
@@ -1083,7 +1087,7 @@ class Config
if ($output === false) {
$output = null;
} else {
$dir = Util\Common::realpath(dirname($output));
$dir = Common::realpath(dirname($output));
if (is_dir($dir) === false) {
$error = 'ERROR: The specified '.$report.' report file path "'.$output.'" points to a non-existent directory'.PHP_EOL.PHP_EOL;
$error .= $this->printShortUsage(true);
@@ -1317,7 +1321,7 @@ class Config
return;
}
$file = Util\Common::realpath($path);
$file = Common::realpath($path);
if (file_exists($file) === false) {
if ($this->dieOnUnknownArg === false) {
return;
@@ -1392,71 +1396,21 @@ class Config
*/
public function printPHPCSUsage()
{
echo 'Usage: phpcs [-nwlsaepqvi] [-d key[=value]] [--colors] [--no-colors]'.PHP_EOL;
echo ' [--cache[=<cacheFile>]] [--no-cache] [--tab-width=<tabWidth>]'.PHP_EOL;
echo ' [--report=<report>] [--report-file=<reportFile>] [--report-<report>=<reportFile>]'.PHP_EOL;
echo ' [--report-width=<reportWidth>] [--basepath=<basepath>] [--bootstrap=<bootstrap>]'.PHP_EOL;
echo ' [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
echo ' [--runtime-set key value] [--config-set key value] [--config-delete key] [--config-show]'.PHP_EOL;
echo ' [--standard=<standard>] [--sniffs=<sniffs>] [--exclude=<sniffs>]'.PHP_EOL;
echo ' [--encoding=<encoding>] [--parallel=<processes>] [--generator=<generator>]'.PHP_EOL;
echo ' [--extensions=<extensions>] [--ignore=<patterns>] [--ignore-annotations]'.PHP_EOL;
echo ' [--stdin-path=<stdinPath>] [--file-list=<fileList>] [--filter=<filter>] <file> - ...'.PHP_EOL;
echo PHP_EOL;
echo ' - Check STDIN instead of local files and directories'.PHP_EOL;
echo ' -n Do not print warnings (shortcut for --warning-severity=0)'.PHP_EOL;
echo ' -w Print both warnings and errors (this is the default)'.PHP_EOL;
echo ' -l Local directory only, no recursion'.PHP_EOL;
echo ' -s Show error codes in all reports'.PHP_EOL;
echo ' -a Run interactively'.PHP_EOL;
echo ' -e Explain a standard by showing the sniffs it includes'.PHP_EOL;
echo ' -p Show progress of the run'.PHP_EOL;
echo ' -q Quiet mode; disables progress and verbose output'.PHP_EOL;
echo ' -m Stop error messages from being recorded'.PHP_EOL;
echo ' (saves a lot of memory, but stops many reports from being used)'.PHP_EOL;
echo ' -v Print processed files'.PHP_EOL;
echo ' -vv Print ruleset and token output'.PHP_EOL;
echo ' -vvv Print sniff processing information'.PHP_EOL;
echo ' -i Show a list of installed coding standards'.PHP_EOL;
echo ' -d Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
echo PHP_EOL;
echo ' --help Print this help message'.PHP_EOL;
echo ' --version Print version information'.PHP_EOL;
echo ' --colors Use colors in output'.PHP_EOL;
echo ' --no-colors Do not use colors in output (this is the default)'.PHP_EOL;
echo ' --cache Cache results between runs'.PHP_EOL;
echo ' --no-cache Do not cache results between runs (this is the default)'.PHP_EOL;
echo ' --ignore-annotations Ignore all phpcs: annotations in code comments'.PHP_EOL;
echo PHP_EOL;
echo ' <cacheFile> Use a specific file for caching (uses a temporary file by default)'.PHP_EOL;
echo ' <basepath> A path to strip from the front of file paths inside reports'.PHP_EOL;
echo ' <bootstrap> A comma separated list of files to run before processing begins'.PHP_EOL;
echo ' <encoding> The encoding of the files being checked (default is utf-8)'.PHP_EOL;
echo ' <extensions> A comma separated list of file extensions to check'.PHP_EOL;
echo ' The type of the file can be specified using: ext/type'.PHP_EOL;
echo ' e.g., module/php,es/js'.PHP_EOL;
echo ' <file> One or more files and/or directories to check'.PHP_EOL;
echo ' <fileList> A file containing a list of files and/or directories to check (one per line)'.PHP_EOL;
echo ' <filter> Use either the "GitModified" or "GitStaged" filter,'.PHP_EOL;
echo ' or specify the path to a custom filter class'.PHP_EOL;
echo ' <generator> Use either the "HTML", "Markdown" or "Text" generator'.PHP_EOL;
echo ' (forces documentation generation instead of checking)'.PHP_EOL;
echo ' <patterns> A comma separated list of patterns to ignore files and directories'.PHP_EOL;
echo ' <processes> How many files should be checked simultaneously (default is 1)'.PHP_EOL;
echo ' <report> Print either the "full", "xml", "checkstyle", "csv"'.PHP_EOL;
echo ' "json", "junit", "emacs", "source", "summary", "diff"'.PHP_EOL;
echo ' "svnblame", "gitblame", "hgblame", "notifysend" or "performance",'.PHP_EOL;
echo ' report or specify the path to a custom report class'.PHP_EOL;
echo ' (the "full" report is printed by default)'.PHP_EOL;
echo ' <reportFile> Write the report to the specified file path'.PHP_EOL;
echo ' <reportWidth> How many columns wide screen reports should be printed'.PHP_EOL;
echo ' or set to "auto" to use current screen width, where supported'.PHP_EOL;
echo ' <severity> The minimum severity required to display an error or warning'.PHP_EOL;
echo ' <sniffs> A comma separated list of sniff codes to include or exclude from checking'.PHP_EOL;
echo ' (all sniffs must be part of the specified standard)'.PHP_EOL;
echo ' <standard> The name or path of the coding standard to use'.PHP_EOL;
echo ' <stdinPath> If processing STDIN, the file path that STDIN will be processed as'.PHP_EOL;
echo ' <tabWidth> The number of spaces each tab represents'.PHP_EOL;
$longOptions = explode(',', Help::DEFAULT_LONG_OPTIONS);
$longOptions[] = 'cache';
$longOptions[] = 'no-cache';
$longOptions[] = 'report';
$longOptions[] = 'report-file';
$longOptions[] = 'report-report';
$longOptions[] = 'config-explain';
$longOptions[] = 'config-set';
$longOptions[] = 'config-delete';
$longOptions[] = 'config-show';
$longOptions[] = 'generator';
$shortOptions = Help::DEFAULT_SHORT_OPTIONS.'aems';
(new Help($this, $longOptions, $shortOptions))->display();
}//end printPHPCSUsage()
@@ -1468,49 +1422,11 @@ class Config
*/
public function printPHPCBFUsage()
{
echo 'Usage: phpcbf [-nwli] [-d key[=value]] [--ignore-annotations] [--bootstrap=<bootstrap>]'.PHP_EOL;
echo ' [--standard=<standard>] [--sniffs=<sniffs>] [--exclude=<sniffs>] [--suffix=<suffix>]'.PHP_EOL;
echo ' [--severity=<severity>] [--error-severity=<severity>] [--warning-severity=<severity>]'.PHP_EOL;
echo ' [--tab-width=<tabWidth>] [--encoding=<encoding>] [--parallel=<processes>]'.PHP_EOL;
echo ' [--basepath=<basepath>] [--extensions=<extensions>] [--ignore=<patterns>]'.PHP_EOL;
echo ' [--stdin-path=<stdinPath>] [--file-list=<fileList>] [--filter=<filter>] <file> - ...'.PHP_EOL;
echo PHP_EOL;
echo ' - Fix STDIN instead of local files and directories'.PHP_EOL;
echo ' -n Do not fix warnings (shortcut for --warning-severity=0)'.PHP_EOL;
echo ' -w Fix both warnings and errors (on by default)'.PHP_EOL;
echo ' -l Local directory only, no recursion'.PHP_EOL;
echo ' -p Show progress of the run'.PHP_EOL;
echo ' -q Quiet mode; disables progress and verbose output'.PHP_EOL;
echo ' -v Print processed files'.PHP_EOL;
echo ' -vv Print ruleset and token output'.PHP_EOL;
echo ' -vvv Print sniff processing information'.PHP_EOL;
echo ' -i Show a list of installed coding standards'.PHP_EOL;
echo ' -d Set the [key] php.ini value to [value] or [true] if value is omitted'.PHP_EOL;
echo PHP_EOL;
echo ' --help Print this help message'.PHP_EOL;
echo ' --version Print version information'.PHP_EOL;
echo ' --ignore-annotations Ignore all phpcs: annotations in code comments'.PHP_EOL;
echo PHP_EOL;
echo ' <basepath> A path to strip from the front of file paths inside reports'.PHP_EOL;
echo ' <bootstrap> A comma separated list of files to run before processing begins'.PHP_EOL;
echo ' <encoding> The encoding of the files being fixed (default is utf-8)'.PHP_EOL;
echo ' <extensions> A comma separated list of file extensions to fix'.PHP_EOL;
echo ' The type of the file can be specified using: ext/type'.PHP_EOL;
echo ' e.g., module/php,es/js'.PHP_EOL;
echo ' <file> One or more files and/or directories to fix'.PHP_EOL;
echo ' <fileList> A file containing a list of files and/or directories to fix (one per line)'.PHP_EOL;
echo ' <filter> Use either the "GitModified" or "GitStaged" filter,'.PHP_EOL;
echo ' or specify the path to a custom filter class'.PHP_EOL;
echo ' <patterns> A comma separated list of patterns to ignore files and directories'.PHP_EOL;
echo ' <processes> How many files should be fixed simultaneously (default is 1)'.PHP_EOL;
echo ' <severity> The minimum severity required to fix an error or warning'.PHP_EOL;
echo ' <sniffs> A comma separated list of sniff codes to include or exclude from fixing'.PHP_EOL;
echo ' (all sniffs must be part of the specified standard)'.PHP_EOL;
echo ' <standard> The name or path of the coding standard to use'.PHP_EOL;
echo ' <stdinPath> If processing STDIN, the file path that STDIN will be processed as'.PHP_EOL;
echo ' <suffix> Write modified files to a filename using this suffix'.PHP_EOL;
echo ' ("diff" and "patch" are not used in this mode)'.PHP_EOL;
echo ' <tabWidth> The number of spaces each tab represents'.PHP_EOL;
$longOptions = explode(',', Help::DEFAULT_LONG_OPTIONS);
$longOptions[] = 'suffix';
$shortOptions = Help::DEFAULT_SHORT_OPTIONS;
(new Help($this, $longOptions, $shortOptions))->display();
}//end printPHPCBFUsage()
@@ -1608,7 +1524,7 @@ class Config
if ($temp === false) {
$path = '';
if (is_callable('\Phar::running') === true) {
$path = \Phar::running(false);
$path = Phar::running(false);
}
if ($path !== '') {
@@ -1653,8 +1569,8 @@ class Config
// If the installed paths are being set, make sure all known
// standards paths are added to the autoloader.
if ($key === 'installed_paths') {
$installedStandards = Util\Standards::getInstalledStandardDetails();
foreach ($installedStandards as $name => $details) {
$installedStandards = Standards::getInstalledStandardDetails();
foreach ($installedStandards as $details) {
Autoload::addSearchPath($details['path'], $details['namespace']);
}
}
@@ -1679,7 +1595,7 @@ class Config
$path = '';
if (is_callable('\Phar::running') === true) {
$path = \Phar::running(false);
$path = Phar::running(false);
}
if ($path !== '') {
@@ -12,7 +12,9 @@
namespace PHP_CodeSniffer\Exceptions;
class DeepExitException extends \Exception
use Exception;
class DeepExitException extends Exception
{
}//end class
@@ -9,7 +9,9 @@
namespace PHP_CodeSniffer\Exceptions;
class RuntimeException extends \RuntimeException
use RuntimeException as PHPRuntimeException;
class RuntimeException extends PHPRuntimeException
{
}//end class
@@ -9,7 +9,9 @@
namespace PHP_CodeSniffer\Exceptions;
class TokenizerException extends \Exception
use Exception;
class TokenizerException extends Exception
{
}//end class
@@ -14,8 +14,8 @@
namespace PHP_CodeSniffer\Files;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Ruleset;
class DummyFile extends File
{
@@ -9,12 +9,13 @@
namespace PHP_CodeSniffer\Files;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Fixer;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Exceptions\TokenizerException;
use PHP_CodeSniffer\Fixer;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Util\Tokens;
class File
{
@@ -276,7 +277,7 @@ class File
$this->tokens = [];
try {
$this->eolChar = Util\Common::detectLineEndings($content);
$this->eolChar = Common::detectLineEndings($content);
} catch (RuntimeException $e) {
$this->addWarningOnLine($e->getMessage(), 1, 'Internal.DetectLineEndings');
return;
@@ -430,7 +431,7 @@ class File
if (PHP_CODESNIFFER_VERBOSITY > 2) {
$type = $token['type'];
$content = Util\Common::prepareForOutput($token['content']);
$content = Common::prepareForOutput($token['content']);
echo "\t\tProcess token $stackPtr: $type => $content".PHP_EOL;
}
@@ -872,16 +873,20 @@ class File
$parts = explode('.', $code);
if ($parts[0] === 'Internal') {
// An internal message.
$listenerCode = Util\Common::getSniffCode($this->activeListener);
$sniffCode = $code;
$checkCodes = [$sniffCode];
$listenerCode = '';
if ($this->activeListener !== '') {
$listenerCode = Common::getSniffCode($this->activeListener);
}
$sniffCode = $code;
$checkCodes = [$sniffCode];
} else {
if ($parts[0] !== $code) {
// The full message code has been passed in.
$sniffCode = $code;
$listenerCode = substr($sniffCode, 0, strrpos($sniffCode, '.'));
} else {
$listenerCode = Util\Common::getSniffCode($this->activeListener);
$listenerCode = Common::getSniffCode($this->activeListener);
$sniffCode = $listenerCode.'.'.$code;
$parts = explode('.', $sniffCode);
}
@@ -1418,7 +1423,9 @@ class File
// it's likely to be an array which might have arguments in it. This
// could cause problems in our parsing below, so lets just skip to the
// end of it.
if (isset($this->tokens[$i]['parenthesis_opener']) === true) {
if ($this->tokens[$i]['code'] !== T_TYPE_OPEN_PARENTHESIS
&& isset($this->tokens[$i]['parenthesis_opener']) === true
) {
// Don't do this if it's the close parenthesis for the method.
if ($i !== $this->tokens[$i]['parenthesis_closer']) {
$i = $this->tokens[$i]['parenthesis_closer'];
@@ -1512,6 +1519,8 @@ class File
case T_NS_SEPARATOR:
case T_TYPE_UNION:
case T_TYPE_INTERSECTION:
case T_TYPE_OPEN_PARENTHESIS:
case T_TYPE_CLOSE_PARENTHESIS:
case T_FALSE:
case T_TRUE:
case T_NULL:
@@ -1615,7 +1624,7 @@ class File
$paramCount++;
break;
case T_EQUAL:
$defaultStart = $this->findNext(Util\Tokens::$emptyTokens, ($i + 1), null, true);
$defaultStart = $this->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
$equalToken = $i;
break;
}//end switch
@@ -1734,18 +1743,20 @@ class File
}
$valid = [
T_STRING => T_STRING,
T_CALLABLE => T_CALLABLE,
T_SELF => T_SELF,
T_PARENT => T_PARENT,
T_STATIC => T_STATIC,
T_FALSE => T_FALSE,
T_TRUE => T_TRUE,
T_NULL => T_NULL,
T_NAMESPACE => T_NAMESPACE,
T_NS_SEPARATOR => T_NS_SEPARATOR,
T_TYPE_UNION => T_TYPE_UNION,
T_TYPE_INTERSECTION => T_TYPE_INTERSECTION,
T_STRING => T_STRING,
T_CALLABLE => T_CALLABLE,
T_SELF => T_SELF,
T_PARENT => T_PARENT,
T_STATIC => T_STATIC,
T_FALSE => T_FALSE,
T_TRUE => T_TRUE,
T_NULL => T_NULL,
T_NAMESPACE => T_NAMESPACE,
T_NS_SEPARATOR => T_NS_SEPARATOR,
T_TYPE_UNION => T_TYPE_UNION,
T_TYPE_INTERSECTION => T_TYPE_INTERSECTION,
T_TYPE_OPEN_PARENTHESIS => T_TYPE_OPEN_PARENTHESIS,
T_TYPE_CLOSE_PARENTHESIS => T_TYPE_CLOSE_PARENTHESIS,
];
for ($i = $this->tokens[$stackPtr]['parenthesis_closer']; $i < $this->numTokens; $i++) {
@@ -1756,6 +1767,20 @@ class File
break;
}
if ($this->tokens[$i]['code'] === T_USE) {
// Skip over closure use statements.
for ($j = ($i + 1); $j < $this->numTokens && isset(Tokens::$emptyTokens[$this->tokens[$j]['code']]) === true; $j++);
if ($this->tokens[$j]['code'] === T_OPEN_PARENTHESIS) {
if (isset($this->tokens[$j]['parenthesis_closer']) === false) {
// Live coding/parse error, stop parsing.
break;
}
$i = $this->tokens[$j]['parenthesis_closer'];
continue;
}
}
if ($this->tokens[$i]['code'] === T_NULLABLE) {
$nullableReturnType = true;
}
@@ -1885,7 +1910,7 @@ class File
T_READONLY => T_READONLY,
];
$valid += Util\Tokens::$emptyTokens;
$valid += Tokens::$emptyTokens;
$scope = 'public';
$scopeSpecified = false;
@@ -1937,17 +1962,19 @@ class File
if ($i < $stackPtr) {
// We've found a type.
$valid = [
T_STRING => T_STRING,
T_CALLABLE => T_CALLABLE,
T_SELF => T_SELF,
T_PARENT => T_PARENT,
T_FALSE => T_FALSE,
T_TRUE => T_TRUE,
T_NULL => T_NULL,
T_NAMESPACE => T_NAMESPACE,
T_NS_SEPARATOR => T_NS_SEPARATOR,
T_TYPE_UNION => T_TYPE_UNION,
T_TYPE_INTERSECTION => T_TYPE_INTERSECTION,
T_STRING => T_STRING,
T_CALLABLE => T_CALLABLE,
T_SELF => T_SELF,
T_PARENT => T_PARENT,
T_FALSE => T_FALSE,
T_TRUE => T_TRUE,
T_NULL => T_NULL,
T_NAMESPACE => T_NAMESPACE,
T_NS_SEPARATOR => T_NS_SEPARATOR,
T_TYPE_UNION => T_TYPE_UNION,
T_TYPE_INTERSECTION => T_TYPE_INTERSECTION,
T_TYPE_OPEN_PARENTHESIS => T_TYPE_OPEN_PARENTHESIS,
T_TYPE_CLOSE_PARENTHESIS => T_TYPE_CLOSE_PARENTHESIS,
];
for ($i; $i < $stackPtr; $i++) {
@@ -2073,7 +2100,7 @@ class File
}
$tokenBefore = $this->findPrevious(
Util\Tokens::$emptyTokens,
Tokens::$emptyTokens,
($stackPtr - 1),
null,
true
@@ -2097,14 +2124,14 @@ class File
return true;
}
if (isset(Util\Tokens::$assignmentTokens[$this->tokens[$tokenBefore]['code']]) === true) {
if (isset(Tokens::$assignmentTokens[$this->tokens[$tokenBefore]['code']]) === 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;
}
$tokenAfter = $this->findNext(
Util\Tokens::$emptyTokens,
Tokens::$emptyTokens,
($stackPtr + 1),
null,
true
@@ -2155,7 +2182,7 @@ class File
if ($this->tokens[$tokenAfter]['code'] === T_VARIABLE) {
return true;
} else {
$skip = Util\Tokens::$emptyTokens;
$skip = Tokens::$emptyTokens;
$skip[] = T_NS_SEPARATOR;
$skip[] = T_SELF;
$skip[] = T_PARENT;
@@ -2245,7 +2272,7 @@ class File
* be returned.
* @param bool $local If true, tokens outside the current statement
* will not be checked. IE. checking will stop
* at the previous semi-colon found.
* at the previous semicolon found.
*
* @return int|false
* @see findNext()
@@ -2326,7 +2353,7 @@ class File
* be returned.
* @param bool $local If true, tokens outside the current statement
* will not be checked. i.e., checking will stop
* at the next semi-colon found.
* at the next semicolon found.
*
* @return int|false
* @see findPrevious()
@@ -2382,7 +2409,7 @@ class File
*/
public function findStartOfStatement($start, $ignore=null)
{
$startTokens = Util\Tokens::$blockOpeners;
$startTokens = Tokens::$blockOpeners;
$startTokens[T_OPEN_SHORT_ARRAY] = true;
$startTokens[T_OPEN_TAG] = true;
$startTokens[T_OPEN_TAG_WITH_ECHO] = true;
@@ -2412,51 +2439,88 @@ class File
// If the start token is inside the case part of a match expression,
// find the start of the condition. If it's in the statement part, find
// the token that comes after the match arrow.
$matchExpression = $this->getCondition($start, T_MATCH);
if ($matchExpression !== false) {
for ($prevMatch = $start; $prevMatch > $this->tokens[$matchExpression]['scope_opener']; $prevMatch--) {
if ($prevMatch !== $start
&& ($this->tokens[$prevMatch]['code'] === T_MATCH_ARROW
|| $this->tokens[$prevMatch]['code'] === T_COMMA)
) {
break;
}
if (empty($this->tokens[$start]['conditions']) === false) {
$conditions = $this->tokens[$start]['conditions'];
$lastConditionOwner = end($conditions);
$matchExpression = key($conditions);
// Skip nested statements.
if (isset($this->tokens[$prevMatch]['bracket_opener']) === true
&& $prevMatch === $this->tokens[$prevMatch]['bracket_closer']
) {
$prevMatch = $this->tokens[$prevMatch]['bracket_opener'];
} else if (isset($this->tokens[$prevMatch]['parenthesis_opener']) === true
&& $prevMatch === $this->tokens[$prevMatch]['parenthesis_closer']
) {
$prevMatch = $this->tokens[$prevMatch]['parenthesis_opener'];
}
}
if ($lastConditionOwner === T_MATCH
// Check if the $start token is at the same parentheses nesting level as the match token.
&& ((empty($this->tokens[$matchExpression]['nested_parenthesis']) === true
&& empty($this->tokens[$start]['nested_parenthesis']) === true)
|| ((empty($this->tokens[$matchExpression]['nested_parenthesis']) === false
&& empty($this->tokens[$start]['nested_parenthesis']) === false)
&& $this->tokens[$matchExpression]['nested_parenthesis'] === $this->tokens[$start]['nested_parenthesis']))
) {
// Walk back to the previous match arrow (if it exists).
$lastComma = null;
$inNestedExpression = false;
for ($prevMatch = $start; $prevMatch > $this->tokens[$matchExpression]['scope_opener']; $prevMatch--) {
if ($prevMatch !== $start && $this->tokens[$prevMatch]['code'] === T_MATCH_ARROW) {
break;
}
if ($prevMatch <= $this->tokens[$matchExpression]['scope_opener']) {
// We're before the arrow in the first case.
$next = $this->findNext(Util\Tokens::$emptyTokens, ($this->tokens[$matchExpression]['scope_opener'] + 1), null, true);
if ($next === false) {
return $start;
}
if ($prevMatch !== $start && $this->tokens[$prevMatch]['code'] === T_COMMA) {
$lastComma = $prevMatch;
continue;
}
return $next;
}
// Skip nested statements.
if (isset($this->tokens[$prevMatch]['bracket_opener']) === true
&& $prevMatch === $this->tokens[$prevMatch]['bracket_closer']
) {
$prevMatch = $this->tokens[$prevMatch]['bracket_opener'];
continue;
}
if (isset($this->tokens[$prevMatch]['parenthesis_opener']) === true
&& $prevMatch === $this->tokens[$prevMatch]['parenthesis_closer']
) {
$prevMatch = $this->tokens[$prevMatch]['parenthesis_opener'];
continue;
}
// Stop if we're _within_ a nested short array statement, which may contain comma's too.
// No need to deal with parentheses, those are handled above via the `nested_parenthesis` checks.
if (isset($this->tokens[$prevMatch]['bracket_opener']) === true
&& $this->tokens[$prevMatch]['bracket_closer'] > $start
) {
$inNestedExpression = true;
break;
}
}//end for
if ($inNestedExpression === false) {
// $prevMatch will now either be the scope opener or a match arrow.
// If it is the scope opener, go the first non-empty token after. $start will have been part of the first condition.
if ($prevMatch <= $this->tokens[$matchExpression]['scope_opener']) {
// We're before the arrow in the first case.
$next = $this->findNext(Tokens::$emptyTokens, ($this->tokens[$matchExpression]['scope_opener'] + 1), null, true);
if ($next === false) {
// Shouldn't be possible.
return $start;
}
return $next;
}
// Okay, so we found a match arrow.
// If $start was part of the "next" condition, the last comma will be set.
// Otherwise, $start must have been part of a return expression.
if (isset($lastComma) === true && $lastComma > $prevMatch) {
$prevMatch = $lastComma;
}
// In both cases, go to the first non-empty token after.
$next = $this->findNext(Tokens::$emptyTokens, ($prevMatch + 1), null, true);
if ($next === false) {
// Shouldn't be possible.
return $start;
}
if ($this->tokens[$prevMatch]['code'] === T_COMMA) {
// We're before the arrow, but not in the first case.
$prevMatchArrow = $this->findPrevious(T_MATCH_ARROW, ($prevMatch - 1), $this->tokens[$matchExpression]['scope_opener']);
if ($prevMatchArrow === false) {
// We're before the arrow in the first case.
$next = $this->findNext(Util\Tokens::$emptyTokens, ($this->tokens[$matchExpression]['scope_opener'] + 1), null, true);
return $next;
}
$end = $this->findEndOfStatement($prevMatchArrow);
$next = $this->findNext(Util\Tokens::$emptyTokens, ($end + 1), null, true);
return $next;
}
}//end if
}//end if
}//end if
$lastNotEmpty = $start;
@@ -2515,7 +2579,7 @@ class File
}
}//end if
if (isset(Util\Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
if (isset(Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
$lastNotEmpty = $i;
}
}//end for
@@ -2610,7 +2674,7 @@ class File
continue;
}
if ($i === $start && isset(Util\Tokens::$scopeOpeners[$this->tokens[$i]['code']]) === true) {
if ($i === $start && isset(Tokens::$scopeOpeners[$this->tokens[$i]['code']]) === true) {
return $this->tokens[$i]['scope_closer'];
}
@@ -2630,7 +2694,7 @@ class File
}
}//end if
if (isset(Util\Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
if (isset(Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
$lastNotEmpty = $i;
}
}//end for
@@ -11,14 +11,20 @@
namespace PHP_CodeSniffer\Files;
use Countable;
use FilesystemIterator;
use Iterator;
use PHP_CodeSniffer\Autoload;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Exceptions\DeepExitException;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Util\Common;
use RecursiveArrayIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ReturnTypeWillChange;
class FileList implements \Iterator, \Countable
class FileList implements Iterator, Countable
{
/**
@@ -72,7 +78,7 @@ class FileList implements \Iterator, \Countable
$paths = $config->files;
foreach ($paths as $path) {
$isPharFile = Util\Common::isPharFile($path);
$isPharFile = Common::isPharFile($path);
if (is_dir($path) === true || $isPharFile === true) {
if ($isPharFile === true) {
$path = 'phar://'.$path;
@@ -80,9 +86,9 @@ class FileList implements \Iterator, \Countable
$filterClass = $this->getFilterClass();
$di = new \RecursiveDirectoryIterator($path, (\RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS));
$di = new RecursiveDirectoryIterator($path, (RecursiveDirectoryIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS));
$filter = new $filterClass($di, $path, $config, $ruleset);
$iterator = new \RecursiveIteratorIterator($filter);
$iterator = new RecursiveIteratorIterator($filter);
foreach ($iterator as $file) {
$this->files[$file->getPathname()] = null;
@@ -121,9 +127,9 @@ class FileList implements \Iterator, \Countable
$filterClass = $this->getFilterClass();
$di = new \RecursiveArrayIterator([$path]);
$di = new RecursiveArrayIterator([$path]);
$filter = new $filterClass($di, $path, $this->config, $this->ruleset);
$iterator = new \RecursiveIteratorIterator($filter);
$iterator = new RecursiveIteratorIterator($filter);
foreach ($iterator as $path) {
$this->files[$path] = $file;
@@ -9,8 +9,8 @@
namespace PHP_CodeSniffer\Files;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Util\Cache;
use PHP_CodeSniffer\Util\Common;
@@ -11,7 +11,7 @@
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Util\Common;
abstract class ExactMatch extends Filter
{
@@ -64,7 +64,7 @@ abstract class ExactMatch extends Filter
}
}
$filePath = Util\Common::realpath($this->current());
$filePath = Common::realpath($this->current());
// If a file is both disallowed and allowed, the disallowed files list takes precedence.
if (isset($this->disallowedFiles[$filePath]) === true) {
@@ -9,12 +9,15 @@
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Ruleset;
use FilesystemIterator;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Util\Common;
use RecursiveDirectoryIterator;
use RecursiveFilterIterator;
use ReturnTypeWillChange;
class Filter extends \RecursiveFilterIterator
class Filter extends RecursiveFilterIterator
{
/**
@@ -94,7 +97,7 @@ class Filter extends \RecursiveFilterIterator
public function accept()
{
$filePath = $this->current();
$realPath = Util\Common::realpath($filePath);
$realPath = Common::realpath($filePath);
if ($realPath !== false) {
// It's a real path somewhere, so record it
@@ -137,7 +140,7 @@ class Filter extends \RecursiveFilterIterator
{
$filterClass = get_called_class();
$children = new $filterClass(
new \RecursiveDirectoryIterator($this->current(), (\RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS)),
new RecursiveDirectoryIterator($this->current(), (RecursiveDirectoryIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS)),
$this->basedir,
$this->config,
$this->ruleset
@@ -176,7 +179,7 @@ class Filter extends \RecursiveFilterIterator
// complete extension list and make sure one is allowed.
$extensions = [];
array_shift($fileParts);
foreach ($fileParts as $part) {
while (empty($fileParts) === false) {
$extensions[implode('.', $fileParts)] = 1;
array_shift($fileParts);
}
@@ -9,7 +9,7 @@
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Util\Common;
class GitModified extends ExactMatch
{
@@ -65,7 +65,7 @@ class GitModified extends ExactMatch
}
foreach ($output as $path) {
$path = Util\Common::realpath($path);
$path = Common::realpath($path);
if ($path === false) {
continue;
@@ -11,7 +11,7 @@
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Util\Common;
class GitStaged extends ExactMatch
{
@@ -67,7 +67,7 @@ class GitStaged extends ExactMatch
}
foreach ($output as $path) {
$path = Util\Common::realpath($path);
$path = Common::realpath($path);
if ($path === false) {
// Skip deleted files.
continue;
@@ -12,6 +12,7 @@
namespace PHP_CodeSniffer;
use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Common;
@@ -226,6 +227,8 @@ class Fixer
* @param boolean $colors Print coloured output or not.
*
* @return string
*
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException When the diff command fails.
*/
public function generateDiff($filePath=null, $colors=true)
{
@@ -246,19 +249,56 @@ class Fixer
$fixedFile = fopen($tempName, 'w');
fwrite($fixedFile, $contents);
// We must use something like shell_exec() because whitespace at the end
// We must use something like shell_exec() or proc_open() because whitespace at the end
// of lines is critical to diff files.
// Using proc_open() instead of shell_exec improves performance on Windows significantly,
// while the results are the same (though more code is needed to get the results).
// This is specifically due to proc_open allowing to set the "bypass_shell" option.
$filename = escapeshellarg($filename);
$cmd = "diff -u -L$filename -LPHP_CodeSniffer $filename \"$tempName\"";
$diff = shell_exec($cmd);
// Stream 0 = STDIN, 1 = STDOUT, 2 = STDERR.
$descriptorspec = [
0 => [
'pipe',
'r',
],
1 => [
'pipe',
'w',
],
2 => [
'pipe',
'w',
],
];
$options = null;
if (stripos(PHP_OS, 'WIN') === 0) {
$options = ['bypass_shell' => true];
}
$process = proc_open($cmd, $descriptorspec, $pipes, $cwd, null, $options);
if (is_resource($process) === false) {
throw new RuntimeException('Could not obtain a resource to execute the diff command.');
}
// We don't need these.
fclose($pipes[0]);
fclose($pipes[2]);
// Stdout will contain the actual diff.
$diff = stream_get_contents($pipes[1]);
fclose($pipes[1]);
proc_close($process);
fclose($fixedFile);
if (is_file($tempName) === true) {
unlink($tempName);
}
if ($diff === null) {
if ($diff === false || $diff === '') {
return '';
}
@@ -362,7 +402,7 @@ class Fixer
if ($bt[1]['class'] === __CLASS__) {
$sniff = 'Fixer';
} else {
$sniff = Util\Common::getSniffCode($bt[1]['class']);
$sniff = Common::getSniffCode($bt[1]['class']);
}
$line = $bt[0]['line'];
@@ -447,7 +487,7 @@ class Fixer
$line = $bt[0]['line'];
}
$sniff = Util\Common::getSniffCode($sniff);
$sniff = Common::getSniffCode($sniff);
$numChanges = count($this->changeset);
@@ -504,7 +544,7 @@ class Fixer
$line = $bt[0]['line'];
}
$sniff = Util\Common::getSniffCode($sniff);
$sniff = Common::getSniffCode($sniff);
$tokens = $this->currentFile->getTokens();
$type = $tokens[$stackPtr]['type'];
@@ -619,7 +659,7 @@ class Fixer
$line = $bt[0]['line'];
}
$sniff = Util\Common::getSniffCode($sniff);
$sniff = Common::getSniffCode($sniff);
$tokens = $this->currentFile->getTokens();
$type = $tokens[$stackPtr]['type'];
@@ -6,14 +6,18 @@
* in a standard.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @copyright 2024 PHPCSStandards and contributors
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Generators;
use PHP_CodeSniffer\Ruleset;
use DOMDocument;
use DOMNode;
use PHP_CodeSniffer\Autoload;
use PHP_CodeSniffer\Ruleset;
abstract class Generator
{
@@ -44,20 +48,27 @@ abstract class Generator
{
$this->ruleset = $ruleset;
$find = [
DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR,
'Sniff.php',
];
$replace = [
DIRECTORY_SEPARATOR.'Docs'.DIRECTORY_SEPARATOR,
'Standard.xml',
];
foreach ($ruleset->sniffs as $className => $sniffClass) {
$file = Autoload::getLoadedFileName($className);
$docFile = str_replace(
DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR,
DIRECTORY_SEPARATOR.'Docs'.DIRECTORY_SEPARATOR,
$file
);
$docFile = str_replace('Sniff.php', 'Standard.xml', $docFile);
$docFile = str_replace($find, $replace, $file);
if (is_file($docFile) === true) {
$this->docFiles[] = $docFile;
}
}
// Always present the docs in a consistent alphabetical order.
sort($this->docFiles, (SORT_NATURAL | SORT_FLAG_CASE));
}//end __construct()
@@ -70,7 +81,7 @@ abstract class Generator
*
* @return string
*/
protected function getTitle(\DOMNode $doc)
protected function getTitle(DOMNode $doc)
{
return $doc->getAttribute('title');
@@ -90,7 +101,7 @@ abstract class Generator
public function generate()
{
foreach ($this->docFiles as $file) {
$doc = new \DOMDocument();
$doc = new DOMDocument();
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$this->processSniff($documentation);
@@ -111,7 +122,7 @@ abstract class Generator
* @return void
* @see generate()
*/
abstract protected function processSniff(\DOMNode $doc);
abstract protected function processSniff(DOMNode $doc);
}//end class
@@ -7,17 +7,93 @@
* to each sniff.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @copyright 2024 PHPCSStandards and contributors
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Generators;
use DOMDocument;
use DOMNode;
use PHP_CodeSniffer\Config;
class HTML extends Generator
{
/**
* Stylesheet for the HTML output.
*
* @var string
*/
const STYLESHEET = '<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>';
/**
* Generates the documentation for a standard.
@@ -27,12 +103,16 @@ class HTML extends Generator
*/
public function generate()
{
if (empty($this->docFiles) === true) {
return;
}
ob_start();
$this->printHeader();
$this->printToc();
foreach ($this->docFiles as $file) {
$doc = new \DOMDocument();
$doc = new DOMDocument();
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$this->processSniff($documentation);
@@ -59,72 +139,7 @@ class HTML extends Generator
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 ' '.str_replace("\n", PHP_EOL, self::STYLESHEET).PHP_EOL;
echo ' </head>'.PHP_EOL;
echo ' <body>'.PHP_EOL;
echo " <h1>$standard Coding Standards</h1>".PHP_EOL;
@@ -141,11 +156,16 @@ class HTML extends Generator
*/
protected function printToc()
{
// Only show a TOC when there are two or more docs to display.
if (count($this->docFiles) < 2) {
return;
}
echo ' <h2>Table of Contents</h2>'.PHP_EOL;
echo ' <ul class="toc">'.PHP_EOL;
foreach ($this->docFiles as $file) {
$doc = new \DOMDocument();
$doc = new DOMDocument();
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$title = $this->getTitle($documentation);
@@ -188,7 +208,7 @@ class HTML extends Generator
*
* @return void
*/
public function processSniff(\DOMNode $doc)
public function processSniff(DOMNode $doc)
{
$title = $this->getTitle($doc);
echo ' <a name="'.str_replace(' ', '-', $title).'" />'.PHP_EOL;
@@ -212,16 +232,40 @@ class HTML extends Generator
*
* @return void
*/
protected function printTextBlock(\DOMNode $node)
protected function printTextBlock(DOMNode $node)
{
$content = trim($node->nodeValue);
$content = htmlspecialchars($content);
$content = htmlspecialchars($content, (ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401));
// Allow em tags only.
// Allow only em tags.
$content = str_replace('&lt;em&gt;', '<em>', $content);
$content = str_replace('&lt;/em&gt;', '</em>', $content);
echo " <p class=\"text\">$content</p>".PHP_EOL;
$nodeLines = explode("\n", $content);
$lineCount = count($nodeLines);
$lines = [];
for ($i = 0; $i < $lineCount; $i++) {
$currentLine = trim($nodeLines[$i]);
if (isset($nodeLines[($i + 1)]) === false) {
// We're at the end of the text, just add the line.
$lines[] = $currentLine;
} else {
$nextLine = trim($nodeLines[($i + 1)]);
if ($nextLine === '') {
// Next line is a blank line, end the paragraph and start a new one.
// Also skip over the blank line.
$lines[] = $currentLine.'</p>'.PHP_EOL.' <p class="text">';
++$i;
} else {
// Next line is not blank, so just add a line break.
$lines[] = $currentLine.'<br/>'.PHP_EOL;
}
}
}
echo ' <p class="text">'.implode('', $lines).'</p>'.PHP_EOL;
}//end printTextBlock()
@@ -233,11 +277,12 @@ class HTML extends Generator
*
* @return void
*/
protected function printCodeComparisonBlock(\DOMNode $node)
protected function printCodeComparisonBlock(DOMNode $node)
{
$codeBlocks = $node->getElementsByTagName('code');
$firstTitle = $codeBlocks->item(0)->getAttribute('title');
$firstTitle = trim($codeBlocks->item(0)->getAttribute('title'));
$firstTitle = str_replace(' ', '&nbsp;&nbsp;', $firstTitle);
$first = trim($codeBlocks->item(0)->nodeValue);
$first = str_replace('<?php', '&lt;?php', $first);
$first = str_replace("\n", '</br>', $first);
@@ -245,7 +290,8 @@ class HTML extends Generator
$first = str_replace('<em>', '<span class="code-comparison-highlight">', $first);
$first = str_replace('</em>', '</span>', $first);
$secondTitle = $codeBlocks->item(1)->getAttribute('title');
$secondTitle = trim($codeBlocks->item(1)->getAttribute('title'));
$secondTitle = str_replace(' ', '&nbsp;&nbsp;', $secondTitle);
$second = trim($codeBlocks->item(1)->nodeValue);
$second = str_replace('<?php', '&lt;?php', $second);
$second = str_replace("\n", '</br>', $second);
@@ -3,12 +3,16 @@
* A doc generator that outputs documentation in Markdown format.
*
* @author Stefano Kowalke <blueduck@gmx.net>
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
* @copyright 2014 Arroba IT
* @copyright 2024 PHPCSStandards and contributors
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Generators;
use DOMDocument;
use DOMNode;
use PHP_CodeSniffer\Config;
class Markdown extends Generator
@@ -23,11 +27,15 @@ class Markdown extends Generator
*/
public function generate()
{
if (empty($this->docFiles) === true) {
return;
}
ob_start();
$this->printHeader();
foreach ($this->docFiles as $file) {
$doc = new \DOMDocument();
$doc = new DOMDocument();
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$this->processSniff($documentation);
@@ -65,9 +73,10 @@ class Markdown extends Generator
{
// Turn off errors so we don't get timezone warnings if people
// don't have their timezone set.
error_reporting(0);
echo 'Documentation generated on '.date('r');
$errorLevel = error_reporting(0);
echo PHP_EOL.'Documentation generated on '.date('r');
echo ' by [PHP_CodeSniffer '.Config::VERSION.'](https://github.com/PHPCSStandards/PHP_CodeSniffer)'.PHP_EOL;
error_reporting($errorLevel);
}//end printFooter()
@@ -81,10 +90,10 @@ class Markdown extends Generator
*
* @return void
*/
protected function processSniff(\DOMNode $doc)
protected function processSniff(DOMNode $doc)
{
$title = $this->getTitle($doc);
echo PHP_EOL."## $title".PHP_EOL;
echo PHP_EOL."## $title".PHP_EOL.PHP_EOL;
foreach ($doc->childNodes as $node) {
if ($node->nodeName === 'standard') {
@@ -104,15 +113,38 @@ class Markdown extends Generator
*
* @return void
*/
protected function printTextBlock(\DOMNode $node)
protected function printTextBlock(DOMNode $node)
{
$content = trim($node->nodeValue);
$content = htmlspecialchars($content);
$content = htmlspecialchars($content, (ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401));
$content = str_replace('&lt;em&gt;', '*', $content);
$content = str_replace('&lt;/em&gt;', '*', $content);
echo $content.PHP_EOL;
$nodeLines = explode("\n", $content);
$lineCount = count($nodeLines);
$lines = [];
for ($i = 0; $i < $lineCount; $i++) {
$currentLine = trim($nodeLines[$i]);
if ($currentLine === '') {
// The text contained a blank line. Respect this.
$lines[] = '';
continue;
}
// Check if the _next_ line is blank.
if (isset($nodeLines[($i + 1)]) === false
|| trim($nodeLines[($i + 1)]) === ''
) {
// Next line is blank, just add the line.
$lines[] = $currentLine;
} else {
// Ensure that line breaks are respected in markdown.
$lines[] = $currentLine.' ';
}
}
echo implode(PHP_EOL, $lines).PHP_EOL;
}//end printTextBlock()
@@ -124,19 +156,21 @@ class Markdown extends Generator
*
* @return void
*/
protected function printCodeComparisonBlock(\DOMNode $node)
protected function printCodeComparisonBlock(DOMNode $node)
{
$codeBlocks = $node->getElementsByTagName('code');
$firstTitle = $codeBlocks->item(0)->getAttribute('title');
$firstTitle = trim($codeBlocks->item(0)->getAttribute('title'));
$firstTitle = str_replace(' ', '&nbsp;&nbsp;', $firstTitle);
$first = trim($codeBlocks->item(0)->nodeValue);
$first = str_replace("\n", "\n ", $first);
$first = str_replace("\n", PHP_EOL.' ', $first);
$first = str_replace('<em>', '', $first);
$first = str_replace('</em>', '', $first);
$secondTitle = $codeBlocks->item(1)->getAttribute('title');
$secondTitle = trim($codeBlocks->item(1)->getAttribute('title'));
$secondTitle = str_replace(' ', '&nbsp;&nbsp;', $secondTitle);
$second = trim($codeBlocks->item(1)->nodeValue);
$second = str_replace("\n", "\n ", $second);
$second = str_replace("\n", PHP_EOL.' ', $second);
$second = str_replace('<em>', '', $second);
$second = str_replace('</em>', '', $second);
@@ -5,12 +5,16 @@
* Output is designed to be displayed in a terminal and is wrapped to 100 characters.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @copyright 2024 PHPCSStandards and contributors
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Generators;
use DOMNode;
class Text extends Generator
{
@@ -24,7 +28,7 @@ class Text extends Generator
*
* @return void
*/
public function processSniff(\DOMNode $doc)
public function processSniff(DOMNode $doc)
{
$this->printTitle($doc);
@@ -48,15 +52,17 @@ class Text extends Generator
*
* @return void
*/
protected function printTitle(\DOMNode $doc)
protected function printTitle(DOMNode $doc)
{
$title = $this->getTitle($doc);
$standard = $this->ruleset->name;
$title = $this->getTitle($doc);
$standard = $this->ruleset->name;
$displayTitle = "$standard CODING STANDARD: $title";
$titleLength = strlen($displayTitle);
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 str_repeat('-', ($titleLength + 4));
echo strtoupper(PHP_EOL."| $displayTitle |".PHP_EOL);
echo str_repeat('-', ($titleLength + 4));
echo PHP_EOL.PHP_EOL;
}//end printTitle()
@@ -69,7 +75,7 @@ class Text extends Generator
*
* @return void
*/
protected function printTextBlock(\DOMNode $node)
protected function printTextBlock(DOMNode $node)
{
$text = trim($node->nodeValue);
$text = str_replace('<em>', '*', $text);
@@ -123,11 +129,11 @@ class Text extends Generator
*
* @return void
*/
protected function printCodeComparisonBlock(\DOMNode $node)
protected function printCodeComparisonBlock(DOMNode $node)
{
$codeBlocks = $node->getElementsByTagName('code');
$first = trim($codeBlocks->item(0)->nodeValue);
$firstTitle = $codeBlocks->item(0)->getAttribute('title');
$firstTitle = trim($codeBlocks->item(0)->getAttribute('title'));
$firstTitleLines = [];
$tempTitle = '';
@@ -162,7 +168,7 @@ class Text extends Generator
$firstLines = explode("\n", $first);
$second = trim($codeBlocks->item(1)->nodeValue);
$secondTitle = $codeBlocks->item(1)->getAttribute('title');
$secondTitle = trim($codeBlocks->item(1)->getAttribute('title'));
$secondTitleLines = [];
$tempTitle = '';
@@ -236,7 +236,7 @@ class Reporter
ob_end_clean();
if ($this->config->colors !== true || $reportFile !== null) {
$generatedReport = preg_replace('`\033\[[0-9;]+m`', '', $generatedReport);
$generatedReport = Common::stripColors($generatedReport);
}
if ($reportFile !== null) {
@@ -329,7 +329,29 @@ class Reporter
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file that has been processed.
*
* @return array
* @return array<string, string|int|array> Prepared report data.
* The format of prepared data is as follows:
* ```
* array(
* 'filename' => string The name of the current file.
* 'errors' => int The number of errors seen in the current file.
* 'warnings' => int The number of warnings seen in the current file.
* 'fixable' => int The number of fixable issues seen in the current file.
* 'messages' => array(
* int <Line number> => array(
* int <Column number> => array(
* int <Message index> => array(
* 'message' => string The error/warning message.
* 'source' => string The full error code for the message.
* 'severity' => int The severity of the message.
* 'fixable' => bool Whether this error/warning is auto-fixable.
* 'type' => string The type of message. Either 'ERROR' or 'WARNING'.
* )
* )
* )
* )
* )
* ```
*/
public function prepareFileReport(File $phpcsFile)
{
@@ -342,7 +364,7 @@ class Reporter
];
if ($report['errors'] === 0 && $report['warnings'] === 0) {
// Prefect score!
// Perfect score!
return $report;
}
@@ -15,7 +15,7 @@ namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Exceptions\DeepExitException;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Util\Timing;
class Cbf implements Report
{
@@ -28,10 +28,11 @@ class Cbf implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
* @throws \PHP_CodeSniffer\Exceptions\DeepExitException
@@ -244,7 +245,7 @@ class Cbf implements Report
echo PHP_EOL.str_repeat('-', $width).PHP_EOL.PHP_EOL;
if ($toScreen === true && $interactive === false) {
Util\Timing::printRunTime();
Timing::printRunTime();
}
}//end generate()
@@ -11,6 +11,7 @@ namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
use XMLWriter;
class Checkstyle implements Report
{
@@ -23,16 +24,17 @@ class Checkstyle implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$out = new \XMLWriter;
$out = new XMLWriter;
$out->openMemory();
$out->setIndent(true);
@@ -9,8 +9,10 @@
namespace PHP_CodeSniffer\Reports;
use Exception;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Util\Timing;
class Code implements Report
{
@@ -23,10 +25,11 @@ class Code implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -37,7 +40,7 @@ class Code implements Report
return false;
}
// How many lines to show about and below the error line.
// How many lines to show above and below the error line.
$surroundingLines = 2;
$file = $report['filename'];
@@ -52,7 +55,7 @@ class Code implements Report
try {
$phpcsFile->parse();
} catch (\Exception $e) {
} catch (Exception $e) {
// This is a second parse, so ignore exceptions.
// They would have been added to the file's error list already.
}
@@ -119,8 +122,8 @@ class Code implements Report
// Determine the longest error message we will be showing.
$maxErrorLength = 0;
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($report['messages'] as $lineErrors) {
foreach ($lineErrors as $colErrors) {
foreach ($colErrors as $error) {
$length = strlen($error['message']);
if ($showSources === true) {
@@ -235,7 +238,7 @@ class Code implements Report
$tokenContent = $token['content'];
}
$tokenContent = Util\Common::prepareForOutput($tokenContent, ["\r", "\n", "\t"]);
$tokenContent = Common::prepareForOutput($tokenContent, ["\r", "\n", "\t"]);
$tokenContent = str_replace("\000", ' ', $tokenContent);
$underline = false;
@@ -262,7 +265,7 @@ class Code implements Report
echo str_repeat('-', $width).PHP_EOL;
foreach ($lineErrors as $column => $colErrors) {
foreach ($lineErrors as $colErrors) {
foreach ($colErrors as $error) {
$padding = ($maxLineNumLength - strlen($line));
echo 'LINE '.str_repeat(' ', $padding).$line.': ';
@@ -353,7 +356,7 @@ class Code implements Report
echo $cachedData;
if ($toScreen === true && $interactive === false) {
Util\Timing::printRunTime();
Timing::printRunTime();
}
}//end generate()
@@ -22,10 +22,11 @@ class Csv implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -22,10 +22,11 @@ class Diff implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -22,10 +22,11 @@ class Emacs implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -10,7 +10,7 @@
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Util\Timing;
class Full implements Report
{
@@ -23,10 +23,11 @@ class Full implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -61,8 +62,8 @@ class Full implements Report
// Make sure the report width isn't too big.
$maxErrorLength = 0;
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($report['messages'] as $lineErrors) {
foreach ($lineErrors as $colErrors) {
foreach ($colErrors as $error) {
// Start with the presumption of a single line error message.
$length = strlen($error['message']);
@@ -138,7 +139,7 @@ class Full implements Report
$beforeAfterLength = strlen($beforeMsg.$afterMsg);
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($lineErrors as $colErrors) {
foreach ($colErrors as $error) {
$errorMsg = wordwrap(
$error['message'],
@@ -250,7 +251,7 @@ class Full implements Report
echo $cachedData;
if ($toScreen === true && $interactive === false) {
Util\Timing::printRunTime();
Timing::printRunTime();
}
}//end generate()
@@ -23,10 +23,11 @@ class Info implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -23,10 +23,11 @@ class Json implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -12,6 +12,7 @@ namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
use XMLWriter;
class Junit implements Report
{
@@ -24,16 +25,17 @@ class Junit implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$out = new \XMLWriter;
$out = new XMLWriter;
$out->openMemory();
$out->setIndent(true);
@@ -88,10 +88,11 @@ class Notifysend implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -24,10 +24,11 @@ class Performance implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -22,10 +22,33 @@ interface Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* The format of the `$report` parameter the function receives is as follows:
* ```
* array(
* 'filename' => string The name of the current file.
* 'errors' => int The number of errors seen in the current file.
* 'warnings' => int The number of warnings seen in the current file.
* 'fixable' => int The number of fixable issues seen in the current file.
* 'messages' => array(
* int <Line number> => array(
* int <Column number> => array(
* int <Message index> => array(
* 'message' => string The error/warning message.
* 'source' => string The full error code for the message.
* 'severity' => int The severity of the message.
* 'fixable' => bool Whether this error/warning is auto-fixable.
* 'type' => string The type of message. Either 'ERROR' or 'WARNING'.
* )
* )
* )
* )
* )
* ```
*
* @param array<string, string|int|array> $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -23,10 +23,11 @@ class Source implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -39,8 +40,8 @@ class Source implements Report
$sources = [];
foreach ($report['messages'] as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($report['messages'] as $lineErrors) {
foreach ($lineErrors as $colErrors) {
foreach ($colErrors as $error) {
$src = $error['source'];
if (isset($sources[$src]) === false) {
@@ -10,7 +10,7 @@
namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Util\Timing;
class Summary implements Report
{
@@ -23,10 +23,11 @@ class Summary implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -174,7 +175,7 @@ class Summary implements Report
echo PHP_EOL.str_repeat('-', $width).PHP_EOL.PHP_EOL;
if ($toScreen === true && $interactive === false) {
Util\Timing::printRunTime();
Timing::printRunTime();
}
}//end generate()
@@ -31,10 +31,11 @@ abstract class VersionControl implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
@@ -65,7 +66,7 @@ abstract class VersionControl implements Report
$praiseCache[$author]['bad']++;
foreach ($lineErrors as $column => $colErrors) {
foreach ($lineErrors as $colErrors) {
foreach ($colErrors as $error) {
$authorCache[$author]++;
@@ -11,6 +11,7 @@ namespace PHP_CodeSniffer\Reports;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
use XMLWriter;
class Xml implements Report
{
@@ -23,16 +24,17 @@ class Xml implements Report
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
* its data should be counted in the grand totals.
*
* @param array $report Prepared report data.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
* @param array<string, string|int|array> $report Prepared report data.
* See the {@see Report} interface for a detailed specification.
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being reported on.
* @param bool $showSources Show sources?
* @param int $width Maximum allowed line width.
*
* @return bool
*/
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
{
$out = new \XMLWriter;
$out = new XMLWriter;
$out->openMemory();
$out->setIndent(true);
$out->setIndentString(' ');
@@ -13,7 +13,11 @@ namespace PHP_CodeSniffer;
use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Sniffs\DeprecatedSniff;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Util\Standards;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ReflectionClass;
use stdClass;
class Ruleset
@@ -145,13 +149,13 @@ class Ruleset
$standardPaths = [];
foreach ($config->standards as $standard) {
$installed = Util\Standards::getInstalledStandardPath($standard);
$installed = Standards::getInstalledStandardPath($standard);
if ($installed === null) {
$standard = Util\Common::realpath($standard);
$standard = Common::realpath($standard);
if (is_dir($standard) === true
&& is_file(Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml')) === true
&& is_file(Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml')) === true
) {
$standard = Util\Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml');
$standard = Common::realpath($standard.DIRECTORY_SEPARATOR.'ruleset.xml');
}
} else {
$standard = $installed;
@@ -410,11 +414,7 @@ class Ruleset
$sniffCode = substr($sniffCode, 0, ($maxMessageWidth - 3)).'...';
}
$message = '- '.$sniffCode.PHP_EOL;
if ($this->config->colors === true) {
$message = '- '."\033[36m".$sniffCode."\033[0m".PHP_EOL;
}
$message = '- '."\033[36m".$sniffCode."\033[0m".PHP_EOL;
$maxActualWidth = max($maxActualWidth, strlen($sniffCode));
// Normalize new line characters in custom message.
@@ -447,8 +447,13 @@ class Ruleset
echo $summaryLine.PHP_EOL;
}
$messages = implode(PHP_EOL, $messages);
if ($this->config->colors === false) {
$messages = Common::stripColors($messages);
}
echo str_repeat('-', min(($maxActualWidth + 4), $reportWidth)).PHP_EOL;
echo implode(PHP_EOL, $messages);
echo $messages;
$closer = wordwrap('Deprecated sniffs are still run, but will stop working at some point in the future.', $reportWidth, PHP_EOL);
echo PHP_EOL.PHP_EOL.$closer.PHP_EOL.PHP_EOL;
@@ -472,10 +477,10 @@ class Ruleset
*/
public function processRuleset($rulesetPath, $depth=0)
{
$rulesetPath = Util\Common::realpath($rulesetPath);
$rulesetPath = Common::realpath($rulesetPath);
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo 'Processing ruleset '.Util\Common::stripBasepath($rulesetPath, $this->config->basepath).PHP_EOL;
echo 'Processing ruleset '.Common::stripBasepath($rulesetPath, $this->config->basepath).PHP_EOL;
}
libxml_use_internal_errors(true);
@@ -505,7 +510,7 @@ class Ruleset
if (is_dir($sniffDir) === true) {
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\tAdding sniff files from ".Util\Common::stripBasepath($sniffDir, $this->config->basepath).' directory'.PHP_EOL;
echo "\tAdding sniff files from ".Common::stripBasepath($sniffDir, $this->config->basepath).' directory'.PHP_EOL;
}
$ownSniffs = $this->expandSniffDirectory($sniffDir, $depth);
@@ -520,7 +525,7 @@ class Ruleset
$autoloadPath = (string) $autoload;
// Try relative autoload paths first.
$relativePath = Util\Common::realPath(dirname($rulesetPath).DIRECTORY_SEPARATOR.$autoloadPath);
$relativePath = Common::realPath(dirname($rulesetPath).DIRECTORY_SEPARATOR.$autoloadPath);
if ($relativePath !== false && is_file($relativePath) === true) {
$autoloadPath = $relativePath;
@@ -710,7 +715,7 @@ class Ruleset
// Change the directory so all relative paths are worked
// out based on the location of the ruleset instead of
// the location of the user.
$inPhar = Util\Common::isPharFile($rulesetDir);
$inPhar = Common::isPharFile($rulesetDir);
if ($inPhar === false) {
$currentDir = getcwd();
chdir($rulesetDir);
@@ -757,7 +762,7 @@ class Ruleset
if (in_array($sniff, $excludedSniffs, true) === true) {
continue;
} else {
$files[] = Util\Common::realpath($sniff);
$files[] = Common::realpath($sniff);
}
}
@@ -779,8 +784,8 @@ class Ruleset
{
$sniffs = [];
$rdi = new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
$di = new \RecursiveIteratorIterator($rdi, 0, \RecursiveIteratorIterator::CATCH_GET_CHILD);
$rdi = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::FOLLOW_SYMLINKS);
$di = new RecursiveIteratorIterator($rdi, 0, RecursiveIteratorIterator::CATCH_GET_CHILD);
$dirLen = strlen($directory);
@@ -815,7 +820,7 @@ class Ruleset
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\t\t=> ".Util\Common::stripBasepath($path, $this->config->basepath).PHP_EOL;
echo "\t\t=> ".Common::stripBasepath($path, $this->config->basepath).PHP_EOL;
}
$sniffs[] = $path;
@@ -856,12 +861,12 @@ class Ruleset
// to absolute paths. If this fails, let the reference run through
// the normal checks and have it fail as normal.
if (substr($ref, 0, 1) === '.') {
$realpath = Util\Common::realpath($rulesetDir.'/'.$ref);
$realpath = Common::realpath($rulesetDir.'/'.$ref);
if ($realpath !== false) {
$ref = $realpath;
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
echo "\t\t=> ".Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
}
}
}
@@ -869,12 +874,12 @@ class Ruleset
// As sniffs can't begin with a tilde, assume references in
// this format are relative to the user's home directory.
if (substr($ref, 0, 2) === '~/') {
$realpath = Util\Common::realpath($ref);
$realpath = Common::realpath($ref);
if ($realpath !== false) {
$ref = $realpath;
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
echo "\t\t=> ".Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
}
}
}
@@ -887,8 +892,8 @@ class Ruleset
}
} else {
// See if this is a whole standard being referenced.
$path = Util\Standards::getInstalledStandardPath($ref);
if ($path !== null && Util\Common::isPharFile($path) === true && strpos($path, 'ruleset.xml') === false) {
$path = Standards::getInstalledStandardPath($ref);
if ($path !== null && Common::isPharFile($path) === true && strpos($path, 'ruleset.xml') === false) {
// If the ruleset exists inside the phar file, use it.
if (file_exists($path.DIRECTORY_SEPARATOR.'ruleset.xml') === true) {
$path .= DIRECTORY_SEPARATOR.'ruleset.xml';
@@ -901,7 +906,7 @@ class Ruleset
$ref = $path;
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
echo "\t\t=> ".Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
}
} else if (is_dir($ref) === false) {
// Work out the sniff path.
@@ -925,16 +930,16 @@ class Ruleset
}
$newRef = false;
$stdPath = Util\Standards::getInstalledStandardPath($stdName);
$stdPath = Standards::getInstalledStandardPath($stdName);
if ($stdPath !== null && $path !== '') {
if (Util\Common::isPharFile($stdPath) === true
if (Common::isPharFile($stdPath) === true
&& strpos($stdPath, 'ruleset.xml') === false
) {
// Phar files can only return the directory,
// since ruleset can be omitted if building one standard.
$newRef = Util\Common::realpath($stdPath.$path);
$newRef = Common::realpath($stdPath.$path);
} else {
$newRef = Util\Common::realpath(dirname($stdPath).$path);
$newRef = Common::realpath(dirname($stdPath).$path);
}
}
@@ -949,7 +954,7 @@ class Ruleset
continue;
}
$newRef = Util\Common::realpath($dir.$path);
$newRef = Common::realpath($dir.$path);
if ($newRef !== false) {
$ref = $newRef;
@@ -961,7 +966,7 @@ class Ruleset
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\t\t=> ".Util\Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
echo "\t\t=> ".Common::stripBasepath($ref, $this->config->basepath).PHP_EOL;
}
}//end if
}//end if
@@ -1330,7 +1335,7 @@ class Ruleset
}
$className = Autoload::loadFile($file);
$compareName = Util\Common::cleanSniffClass($className);
$compareName = Common::cleanSniffClass($className);
// If they have specified a list of sniffs to restrict to, check
// to see if this sniff is allowed.
@@ -1349,7 +1354,7 @@ class Ruleset
}
// Skip abstract classes.
$reflection = new \ReflectionClass($className);
$reflection = new ReflectionClass($className);
if ($reflection->isAbstract() === true) {
continue;
}
@@ -1381,7 +1386,7 @@ class Ruleset
$this->sniffs[$sniffClass] = null;
$this->sniffs[$sniffClass] = new $sniffClass();
$sniffCode = Util\Common::getSniffCode($sniffClass);
$sniffCode = Common::getSniffCode($sniffClass);
$this->sniffCodes[$sniffCode] = $sniffClass;
if ($this->sniffs[$sniffClass] instanceof DeprecatedSniff) {
@@ -12,6 +12,8 @@
namespace PHP_CodeSniffer;
use Exception;
use InvalidArgumentException;
use PHP_CodeSniffer\Exceptions\DeepExitException;
use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Files\DummyFile;
@@ -20,6 +22,8 @@ use PHP_CodeSniffer\Files\FileList;
use PHP_CodeSniffer\Util\Cache;
use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Util\Standards;
use PHP_CodeSniffer\Util\Timing;
use PHP_CodeSniffer\Util\Tokens;
class Runner
{
@@ -56,7 +60,7 @@ class Runner
$this->registerOutOfMemoryShutdownMessage('phpcs');
try {
Util\Timing::startTiming();
Timing::startTiming();
Runner::checkRequirements();
if (defined('PHP_CODESNIFFER_CBF') === false) {
@@ -127,7 +131,7 @@ class Runner
&& ($toScreen === false
|| (($this->reporter->totalErrors + $this->reporter->totalWarnings) === 0 && $this->config->showProgress === true))
) {
Util\Timing::printRunTime();
Timing::printRunTime();
}
} catch (DeepExitException $e) {
echo $e->getMessage();
@@ -162,7 +166,7 @@ class Runner
}
try {
Util\Timing::startTiming();
Timing::startTiming();
Runner::checkRequirements();
// Creating the Config object populates it with all required settings
@@ -213,7 +217,7 @@ class Runner
$this->reporter->printReports();
echo PHP_EOL;
Util\Timing::printRunTime();
Timing::printRunTime();
} catch (DeepExitException $e) {
echo $e->getMessage();
return $e->getCode();
@@ -310,12 +314,12 @@ class Runner
// Check that the standards are valid.
foreach ($this->config->standards as $standard) {
if (Util\Standards::isInstalledStandard($standard) === false) {
if (Standards::isInstalledStandard($standard) === false) {
// They didn't select a valid coding standard, so help them
// out by letting them know which standards are installed.
$error = 'ERROR: the "'.$standard.'" coding standard is not installed. ';
ob_start();
Util\Standards::printInstalledStandards();
Standards::printInstalledStandards();
$error .= ob_get_contents();
ob_end_clean();
throw new DeepExitException($error, 3);
@@ -330,11 +334,11 @@ class Runner
// Create this class so it is autoloaded and sets up a bunch
// of PHP_CodeSniffer-specific token type constants.
$tokens = new Util\Tokens();
new Tokens();
// Allow autoloading of custom files inside installed standards.
$installedStandards = Standards::getInstalledStandardDetails();
foreach ($installedStandards as $name => $details) {
foreach ($installedStandards as $details) {
Autoload::addSearchPath($details['path'], $details['namespace']);
}
@@ -662,7 +666,7 @@ class Runner
echo " ($errors errors, $warnings warnings)".PHP_EOL;
}
}
} catch (\Exception $e) {
} catch (Exception $e) {
$error = 'An error occurred during processing; checking has been aborted. The error message was: '.$e->getMessage();
// Determine which sniff caused the error.
@@ -685,16 +689,23 @@ class Runner
}
if (empty($sniffStack) === false) {
if (empty($nextStack) === false
&& isset($nextStack['class']) === true
&& substr($nextStack['class'], -5) === 'Sniff'
) {
$sniffCode = Common::getSniffCode($nextStack['class']);
} else {
$sniffCode = '';
try {
if (empty($nextStack) === false
&& isset($nextStack['class']) === true
&& substr($nextStack['class'], -5) === 'Sniff'
) {
$sniffCode = 'the '.Common::getSniffCode($nextStack['class']).' sniff';
}
} catch (InvalidArgumentException $e) {
// Sniff code could not be determined. This may be an abstract sniff class.
}
if ($sniffCode === '') {
$sniffCode = substr(strrchr(str_replace('\\', '/', $sniffStack['file']), '/'), 1);
}
$error .= sprintf(PHP_EOL.'The error originated in the %s sniff on line %s.', $sniffCode, $sniffStack['line']);
$error .= sprintf(PHP_EOL.'The error originated in %s on line %s.', $sniffCode, $sniffStack['line']);
}
$file->addErrorOnLine($error, 1, 'Internal.Exception');
@@ -9,10 +9,10 @@
namespace PHP_CodeSniffer\Sniffs;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
use PHP_CodeSniffer\Tokenizers\PHP;
use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Tokenizers\PHP;
use PHP_CodeSniffer\Util\Tokens;
abstract class AbstractPatternSniff implements Sniff
{
@@ -416,6 +416,11 @@ abstract class AbstractPatternSniff implements Sniff
$lastAddedStackPtr = null;
$patternLen = count($pattern);
if (($stackPtr + $patternLen - $patternInfo['listen_pos']) > $phpcsFile->numTokens) {
// Pattern can never match as there are not enough tokens left in the file.
return false;
}
for ($i = $patternInfo['listen_pos']; $i < $patternLen; $i++) {
if (isset($tokens[$stackPtr]) === false) {
break;
@@ -26,8 +26,8 @@
namespace PHP_CodeSniffer\Sniffs;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Files\File;
abstract class AbstractScopeSniff implements Sniff
{
@@ -123,7 +123,7 @@ abstract class AbstractScopeSniff implements Sniff
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
* pointer is reached. Return `$phpcsFile->numTokens` to skip
* the rest of the file.
* @see processTokenWithinScope()
*/
@@ -164,7 +164,7 @@ abstract class AbstractScopeSniff implements Sniff
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
* pointer is reached. Return `$phpcsFile->numTokens` to skip
* the rest of the file.
*/
abstract protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope);
@@ -180,7 +180,7 @@ abstract class AbstractScopeSniff implements Sniff
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return (count($tokens) + 1) to skip
* pointer is reached. Return `$phpcsFile->numTokens` to skip
* the rest of the file.
*/
abstract protected function processTokenOutsideScope(File $phpcsFile, $stackPtr);
@@ -72,7 +72,7 @@ abstract class AbstractVariableSniff extends AbstractScopeSniff
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
* pointer is reached. Return `$phpcsFile->numTokens` to skip
* the rest of the file.
*/
final protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
@@ -156,7 +156,7 @@ abstract class AbstractVariableSniff extends AbstractScopeSniff
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
* pointer is reached. Return `$phpcsFile->numTokens` to skip
* the rest of the file.
*/
final protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
@@ -187,7 +187,7 @@ abstract class AbstractVariableSniff extends AbstractScopeSniff
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
* pointer is reached. Return `$phpcsFile->numTokens` to skip
* the rest of the file.
*/
abstract protected function processMemberVar(File $phpcsFile, $stackPtr);
@@ -202,7 +202,7 @@ abstract class AbstractVariableSniff extends AbstractScopeSniff
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
* pointer is reached. Return `$phpcsFile->numTokens` to skip
* the rest of the file.
*/
abstract protected function processVariable(File $phpcsFile, $stackPtr);
@@ -221,7 +221,7 @@ abstract class AbstractVariableSniff extends AbstractScopeSniff
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return ($phpcsFile->numTokens + 1) to skip
* pointer is reached. Return `$phpcsFile->numTokens` to skip
* the rest of the file.
*/
abstract protected function processVariableInString(File $phpcsFile, $stackPtr);
@@ -71,7 +71,7 @@ interface Sniff
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return (count($tokens) + 1) to skip
* pointer is reached. Return `$phpcsFile->numTokens` to skip
* the rest of the file.
*/
public function process(File $phpcsFile, $stackPtr);
@@ -0,0 +1,107 @@
<documentation title="Array Indent">
<standard>
<![CDATA[
The opening brace of a multi-line array must be indented at least to the same level as the start of the statement.
]]>
</standard>
<code_comparison>
<code title="Valid: Opening brace of a multi-line array indented to the same level as the start of the statement.">
<![CDATA[
$b = <em>[</em>
1,
2,
];
if ($condition) {
$a =
<em> [</em>
1,
2,
];
}
]]>
</code>
<code title="Invalid: Opening brace of a multi-line array not indented to the same level as the start of the statement.">
<![CDATA[
if ($condition) {
$a =
<em>[</em>
1,
2,
];
}
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
Each array element must be indented exactly four spaces from the start of the statement.
]]>
</standard>
<code_comparison>
<code title="Valid: Each array element is indented by exactly four spaces.">
<![CDATA[
$a = array(
<em> </em>1,
<em> </em>2,
<em> </em>3,
);
]]>
</code>
<code title="Invalid: Array elements not indented by four spaces.">
<![CDATA[
$a = array(
<em> </em>1,
<em> </em>2,
<em> </em>3,
);
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
The array closing brace must be on a new line.
]]>
</standard>
<code_comparison>
<code title="Valid: Array closing brace on its own line.">
<![CDATA[
$a = [
1,
2,<em>
]</em>;
]]>
</code>
<code title="Invalid: Array closing brace not on its own line.">
<![CDATA[
$a = [
1,
2,<em>]</em>;
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
The closing brace must be aligned with the start of the statement containing the array opener.
]]>
</standard>
<code_comparison>
<code title="Valid: Closing brace aligned with the start of the statement containing the array opener.">
<![CDATA[
$a = array(
1,
2,
<em>)</em>;
]]>
</code>
<code title="Invalid: Closing brace not aligned with the start of the statement containing the array opener.">
<![CDATA[
$a = array(
1,
2,
<em> )</em>;
]]>
</code>
</code_comparison>
</documentation>
@@ -20,11 +20,11 @@
</code_comparison>
<standard>
<![CDATA[
Superfluous semi-colons are not allowed.
Superfluous semicolons are not allowed.
]]>
</standard>
<code_comparison>
<code title="Valid: There is no superfluous semi-colon after a PHP statement.">
<code title="Valid: There is no superfluous semicolon after a PHP statement.">
<![CDATA[
function_call()<em>;</em>
if (true) {
@@ -32,7 +32,7 @@ if (true) {
}
]]>
</code>
<code title="Invalid: There are one or more superfluous semi-colons after a PHP statement.">
<code title="Invalid: There are one or more superfluous semicolons after a PHP statement.">
<![CDATA[
function_call()<em>;;;</em>
if (true) {
@@ -0,0 +1,269 @@
<documentation title="Doc Comment">
<standard>
<![CDATA[
Enforces rules related to the formatting of DocBlocks ("Doc Comments") in PHP code.
DocBlocks are a special type of comment that can provide information about a structural element. In the context of DocBlocks, the following are considered structural elements:
class, interface, trait, enum, function, property, constant, variable declarations and require/include[_once] statements.
DocBlocks start with a `/**` marker and end on `*/`. This sniff will check the formatting of all DocBlocks, independently of whether or not they are attached to a structural element.
]]>
</standard>
<standard>
<![CDATA[
A DocBlock must not be empty.
]]>
</standard>
<code_comparison>
<code title="Valid: DocBlock with some content.">
<![CDATA[
/**
* <em>Some content.</em>
*/
]]>
</code>
<code title="Invalid: Empty DocBlock.">
<![CDATA[
/**
* <em></em>
*/
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
The opening and closing DocBlock tags must be the only content on the line.
]]>
</standard>
<code_comparison>
<code title="Valid: The opening and closing DocBlock tags have to be on a line by themselves.">
<![CDATA[
<em>/**</em>
* Short description.
<em>*/</em>
]]>
</code>
<code title="Invalid: The opening and closing DocBlock tags are not on a line by themselves.">
<![CDATA[
<em>/** Short description. */</em>
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
The DocBlock must have a short description, and it must be on the first line.
]]>
</standard>
<code_comparison>
<code title="Valid: DocBlock with a short description on the first line.">
<![CDATA[
/**
* <em>Short description.</em>
*/
]]>
</code>
<code title="Invalid: DocBlock without a short description or short description not on the first line.">
<![CDATA[
/**
* <em></em>@return int
*/
/**
<em> *</em>
* Short description.
*/
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
Both the short description, as well as the long description, must start with a capital letter.
]]>
</standard>
<code_comparison>
<code title="Valid: Both the short and long description start with a capital letter.">
<![CDATA[
/**
* <em>S</em>hort description.
*
* <em>L</em>ong description.
*/
]]>
</code>
<code title="Invalid: Neither short nor long description starts with a capital letter.">
<![CDATA[
/**
* <em>s</em>hort description.
*
* <em>l</em>ong description.
*/
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
There must be exactly one blank line separating the short description, the long description and tag groups.
]]>
</standard>
<code_comparison>
<code title="Valid: One blank line separating the short description, the long description and tag groups.">
<![CDATA[
/**
* Short description.
<em> *<em>
* Long description.
<em> *</em>
* @param int $foo
*/
]]>
</code>
<code title="Invalid: More than one or no blank line separating the short description, the long description and tag groups.">
<![CDATA[
/**
* Short description.
<em> *
*
</em>
* Long description.<em>
</em> * @param int $foo
*/
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
Parameter tags must be grouped together.
]]>
</standard>
<code_comparison>
<code title="Valid: Parameter tags grouped together.">
<![CDATA[
/**
* Short description.
*
<em> * @param int $foo
* @param string $bar</em>
*/
]]>
</code>
<code title="Invalid: Parameter tags not grouped together.">
<![CDATA[
/**
* Short description.
*
* @param int $foo
<em> *</em>
* @param string $bar
*/
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
Parameter tags must not be grouped together with other tags.
]]>
</standard>
<code_comparison>
<code title="Valid: Parameter tags are not grouped together with other tags.">
<![CDATA[
/**
* Short description.
*
<em> * @param int $foo</em>
*
* @since 3.4.8
* @deprecated 6.0.0
*/
]]>
</code>
<code title="Invalid: Parameter tags grouped together with other tags.">
<![CDATA[
/**
* Short description.
*
<em> * @param int $foo
* @since 3.4.8
* @deprecated 6.0.0</em>
*/
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
Tag values for different tags in the same group must be aligned with each other.
]]>
</standard>
<code_comparison>
<code title="Valid: Tag values for different tags in the same tag group are aligned with each other.">
<![CDATA[
/**
* Short description.
*
* @since<em> 0.5.0</em>
* @deprecated<em> 1.0.0</em>
*/
]]>
</code>
<code title="Invalid: Tag values for different tags in the same tag group are not aligned with each other.">
<![CDATA[
/**
* Short description.
*
* @since<em> 0.5.0</em>
* @deprecated<em> 1.0.0</em>
*/
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
Parameter tags must be defined before other tags in a DocBlock.
]]>
</standard>
<code_comparison>
<code title="Valid: Parameter tags are defined first.">
<![CDATA[
/**
* Short description.
*
* <em>@param string $foo</em>
*
* @return void
*/
]]>
</code>
<code title="Invalid: Parameter tags are not defined first.">
<![CDATA[
/**
* Short description.
*
* @return void
*
* <em>@param string $bar</em>
*/
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
There must be no additional blank (comment) lines before the closing DocBlock tag.
]]>
</standard>
<code_comparison>
<code title="Valid: No additional blank lines before the closing DocBlock tag.">
<![CDATA[
/**
* Short description.<em>
</em> */
]]>
</code>
<code title="Invalid: Additional blank lines before the closing DocBlock tag.">
<![CDATA[
/**
* Short description.
<em> *</em>
*/
]]>
</code>
</code_comparison>
</documentation>
@@ -5,14 +5,14 @@
]]>
</standard>
<code_comparison>
<code title="Valid: value to be asserted must go on the right side of the comparison.">
<code title="Valid: Value to be asserted must go on the right side of the comparison.">
<![CDATA[
if ($test === null) <em>{</em>
$var = 1;
<em>}</em>
]]>
</code>
<code title="Invalid: value to be asserted must not be on the left.">
<code title="Invalid: Value to be asserted must not be on the left.">
<![CDATA[
if (null === $test) <em>{</em>
$var = 1;
@@ -1,18 +1,18 @@
<documentation title="Inline HTML">
<standard>
<![CDATA[
Files that contain php code should only have php code and should not have any "inline html".
Files that contain PHP code should only have PHP code and should not have any "inline html".
]]>
</standard>
<code_comparison>
<code title="Valid: A php file with only php code in it.">
<code title="Valid: A PHP file with only PHP code in it.">
<![CDATA[
<?php
$foo = 'bar';
echo $foo . 'baz';
]]>
</code>
<code title="Invalid: A php file with html in it outside of the php tags.">
<code title="Invalid: A PHP file with html in it outside of the PHP tags.">
<![CDATA[
<em>some string here</em>
<?php
@@ -1,18 +1,18 @@
<documentation title="Aligning Blocks of Assignments">
<standard>
<![CDATA[
There should be one space on either side of an equals sign used to assign a value to a variable. In the case of a block of related assignments, more space may be inserted to promote readability.
There should be one space on either side of an equals sign used to assign a value to a variable. In the case of a block of related assignments, more space may be inserted to promote readability.
]]>
</standard>
<code_comparison>
<code title="Equals signs aligned">
<code title="Valid: Equals signs aligned.">
<![CDATA[
$shortVar <em>=</em> (1 + 2);
$veryLongVarName <em>=</em> 'string';
$var <em>=</em> foo($bar, $baz);
]]>
</code>
<code title="Not aligned; harder to read">
<code title="Invalid: Not aligned; harder to read.">
<![CDATA[
$shortVar <em>=</em> (1 + 2);
$veryLongVarName <em>=</em> 'string';
@@ -22,17 +22,17 @@ $var <em>=</em> foo($bar, $baz);
</code_comparison>
<standard>
<![CDATA[
When using plus-equals, minus-equals etc. still ensure the equals signs are aligned to one space after the longest variable name.
When using plus-equals, minus-equals etc. still ensure the equals signs are aligned to one space after the longest variable name.
]]>
</standard>
<code_comparison>
<code title="Equals signs aligned; only one space after longest var name">
<code title="Valid: Equals signs aligned; only one space after longest var name.">
<![CDATA[
$shortVar <em>+= </em>1;
$veryLongVarName<em> = </em>1;
]]>
</code>
<code title="Two spaces after longest var name">
<code title="Invalid: Two spaces after longest var name.">
<![CDATA[
$shortVar <em> += </em>1;
$veryLongVarName<em> = </em>1;
@@ -40,13 +40,13 @@ $veryLongVarName<em> = </em>1;
</code>
</code_comparison>
<code_comparison>
<code title="Equals signs aligned">
<code title="Valid: Equals signs aligned.">
<![CDATA[
$shortVar <em> = </em>1;
$veryLongVarName<em> -= </em>1;
]]>
</code>
<code title="Equals signs not aligned">
<code title="Invalid: Equals signs not aligned.">
<![CDATA[
$shortVar <em> = </em>1;
$veryLongVarName<em> -= </em>1;
@@ -1,4 +1,4 @@
<documentation title="Space After Casts">
<documentation title="No Space After Cast">
<standard>
<![CDATA[
Spaces are not allowed after casting operators.
@@ -1,4 +1,4 @@
<documentation title="Space After Casts">
<documentation title="Space After Cast">
<standard>
<![CDATA[
Exactly one space is allowed after a cast.
@@ -5,7 +5,7 @@
]]>
</standard>
<code_comparison>
<code title="Valid: brace on next line">
<code title="Valid: Brace on next line.">
<![CDATA[
function fooFunction($arg1, $arg2 = '')
<em>{</em>
@@ -13,7 +13,7 @@ function fooFunction($arg1, $arg2 = '')
}
]]>
</code>
<code title="Invalid: brace on same line">
<code title="Invalid: Brace on same line.">
<![CDATA[
function fooFunction($arg1, $arg2 = '') <em>{</em>
...
@@ -5,14 +5,14 @@
]]>
</standard>
<code_comparison>
<code title="Valid: brace on same line">
<code title="Valid: Brace on same line.">
<![CDATA[
function fooFunction($arg1, $arg2 = '')<em> {</em>
...
}
]]>
</code>
<code title="Invalid: brace on next line">
<code title="Invalid: Brace on next line.">
<![CDATA[
function fooFunction($arg1, $arg2 = '')
<em>{</em>
@@ -5,14 +5,14 @@
]]>
</standard>
<code_comparison>
<code title="Valid: ">
<code title="Valid: Class name starts with 'Abstract'.">
<![CDATA[
abstract class <em>AbstractBar</em>
{
}
]]>
</code>
<code title="Invalid: ">
<code title="Invalid: Class name does not start with 'Abstract'.">
<![CDATA[
abstract class <em>Bar</em>
{
@@ -5,14 +5,14 @@
]]>
</standard>
<code_comparison>
<code title="Valid: ">
<code title="Valid: Interface name ends on 'Interface'.">
<![CDATA[
interface <em>BarInterface</em>
{
}
]]>
</code>
<code title="Invalid: ">
<code title="Invalid: Interface name does not end on 'Interface'.">
<![CDATA[
interface <em>Bar</em>
{
@@ -5,14 +5,14 @@
]]>
</standard>
<code_comparison>
<code title="Valid: ">
<code title="Valid: Trait name ends on 'Trait'.">
<![CDATA[
trait <em>BarTrait</em>
{
}
]]>
</code>
<code title="Invalid: ">
<code title="Invalid: Trait name does not end on 'Trait'.">
<![CDATA[
trait <em>Bar</em>
{
@@ -1,11 +1,11 @@
<documentation title="Constant Names">
<standard>
<![CDATA[
Constants should always be all-uppercase, with underscores to separate words.
Constants should always be all-uppercase, with underscores to separate words.
]]>
</standard>
<code_comparison>
<code title="Valid: all uppercase">
<code title="Valid: All uppercase constant name.">
<![CDATA[
define('<em>FOO_CONSTANT</em>', 'foo');
@@ -15,7 +15,7 @@ class FooClass
}
]]>
</code>
<code title="Invalid: mixed case">
<code title="Invalid: Mixed case or lowercase constant name.">
<![CDATA[
define('<em>Foo_Constant</em>', 'foo');
@@ -1,17 +1,17 @@
<documentation title="Opening Tag at Start of File">
<standard>
<![CDATA[
The opening php tag should be the first item in the file.
The opening PHP tag should be the first item in the file.
]]>
</standard>
<code_comparison>
<code title="Valid: A file starting with an opening php tag.">
<code title="Valid: A file starting with an opening PHP tag.">
<![CDATA[
<em></em><?php
echo 'Foo';
]]>
</code>
<code title="Invalid: A file with content before the opening php tag.">
<code title="Invalid: A file with content before the opening PHP tag.">
<![CDATA[
<em>Beginning content</em>
<?php
@@ -1,11 +1,11 @@
<documentation title="Closing PHP Tags">
<standard>
<![CDATA[
All opening php tags should have a corresponding closing tag.
All opening PHP tags should have a corresponding closing tag.
]]>
</standard>
<code_comparison>
<code title="Valid: A closing tag paired with it's opening tag.">
<code title="Valid: A closing tag paired with its opening tag.">
<![CDATA[
<em><?php</em>
echo 'Foo';
@@ -1,6 +1,6 @@
<documentation title="$_REQUEST Superglobal">
<standard>
<![CDATA[
<![CDATA[
$_REQUEST should never be used due to the ambiguity created as to where the data is coming from. Use $_POST, $_GET, or $_COOKIE instead.
]]>
</standard>
@@ -5,14 +5,14 @@
]]>
</standard>
<code_comparison>
<code title="Valid: lowercase constants">
<code title="Valid: Lowercase constants.">
<![CDATA[
if ($var === <em>false</em> || $var === <em>null</em>) {
$var = <em>true</em>;
}
]]>
</code>
<code title="Invalid: uppercase constants">
<code title="Invalid: Uppercase constants.">
<![CDATA[
if ($var === <em>FALSE</em> || $var === <em>NULL</em>) {
$var = <em>TRUE</em>;
@@ -0,0 +1,38 @@
<documentation title="Require Strict Types">
<standard>
<![CDATA[
The strict_types declaration must be present.
]]>
</standard>
<code_comparison>
<code title="Valid: `strict_types` declaration is present.">
<![CDATA[
declare(<em>strict_types=1</em>);
declare(encoding='UTF-8', <em>strict_types=0</em>);
]]>
</code>
<code title="Invalid: Missing `strict_types` declaration.">
<![CDATA[
declare(encoding='ISO-8859-1'<em></em>);
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
The strict_types declaration must be enabled.
]]>
</standard>
<code_comparison>
<code title="Valid: `strict_types` declaration is enabled.">
<![CDATA[
declare(strict_types=<em>1</em>);
]]>
</code>
<code title="Invalid: `strict_types` declaration is disabled.">
<![CDATA[
declare(strict_types=<em>0</em>);
]]>
</code>
</code_comparison>
</documentation>
@@ -12,7 +12,7 @@ if (<em>PHP_SAPI</em> === 'cli') {
}
]]>
</code>
<code title="Invalid: php_sapi_name() is used.">
<code title="Invalid: Function call to php_sapi_name() is used.">
<![CDATA[
if (<em>php_sapi_name()</em> === 'cli') {
echo "Hello, CLI user.";
@@ -5,14 +5,14 @@
]]>
</standard>
<code_comparison>
<code title="Valid: uppercase constants">
<code title="Valid: Uppercase constants.">
<![CDATA[
if ($var === <em>FALSE</em> || $var === <em>NULL</em>) {
$var = <em>TRUE</em>;
}
]]>
</code>
<code title="Invalid: lowercase constants">
<code title="Invalid: Lowercase constants.">
<![CDATA[
if ($var === <em>false</em> || $var === <em>null</em>) {
$var = <em>true</em>;
@@ -0,0 +1,39 @@
<documentation title="Unnecessary Heredoc">
<standard>
<![CDATA[
If no interpolation or expressions are used in the body of a heredoc, nowdoc syntax should be used instead.
]]>
</standard>
<code_comparison>
<code title="Valid: Using nowdoc syntax for a text string without any interpolation or expressions.">
<![CDATA[
$nowdoc = <em><<<'EOD'</em>
some text
EOD;
]]>
</code>
<code title="Invalid: Using heredoc syntax for a text string without any interpolation or expressions.">
<![CDATA[
$heredoc = <em><<<EOD</em>
some text
EOD;
]]>
</code>
</code_comparison>
<code_comparison>
<code title="Valid: Using heredoc syntax for a text string containing interpolation or expressions.">
<![CDATA[
$heredoc = <em><<<"EOD"</em>
some $text
EOD;
]]>
</code>
<code title="Invalid: Using heredoc syntax for a text string without any interpolation or expressions.">
<![CDATA[
$heredoc = <em><<<"EOD"</em>
some text
EOD;
]]>
</code>
</code_comparison>
</documentation>
@@ -1,7 +1,7 @@
<documentation title="Subversion Properties">
<standard>
<![CDATA[
All php files in a subversion repository should have the svn:keywords property set to 'Author Id Revision' and the svn:eol-style property set to 'native'.
All PHP files in a subversion repository should have the svn:keywords property set to 'Author Id Revision' and the svn:eol-style property set to 'native'.
]]>
</standard>
</documentation>
@@ -5,12 +5,12 @@
]]>
</standard>
<code_comparison>
<code title="Valid: no spaces on the inside of a set of arbitrary parentheses.">
<code title="Valid: No spaces on the inside of a set of arbitrary parentheses.">
<![CDATA[
$a = (null !== $extra);
]]>
</code>
<code title="Invalid: spaces or new lines on the inside of a set of arbitrary parentheses.">
<code title="Invalid: Spaces or new lines on the inside of a set of arbitrary parentheses.">
<![CDATA[
$a = ( null !== $extra );
@@ -0,0 +1,23 @@
<documentation title="Heredoc Nowdoc Identifier Spacing">
<standard>
<![CDATA[
There should be no space between the <<< and the heredoc/nowdoc identifier string.
]]>
</standard>
<code_comparison>
<code title="Valid: No space between the &lt;&lt;&lt; and the identifier string.">
<![CDATA[
$heredoc = <em><<<EOD</em>
some text
EOD;
]]>
</code>
<code title="Invalid: Whitespace between the &lt;&lt;&lt; and the identifier string.">
<![CDATA[
$heredoc = <em><<< END</em>
some text
END;
]]>
</code>
</code_comparison>
</documentation>
@@ -17,7 +17,7 @@ function foo(<em>&...$spread</em>) {
}
]]>
</code>
<code title="Invalid: space found between the spread operator and the variable/function call it applies to.">
<code title="Invalid: Space found between the spread operator and the variable/function call it applies to.">
<![CDATA[
function bar(<em>... </em>$spread) {
bar(<em>...
@@ -25,7 +25,7 @@ function bar(<em>... </em>$spread) {
);
bar(
[<em>... </em>$foo ],<em>.../*comment*/</em>array_values($keyedArray)
[<em>... </em>$foo ],<em>.../*@*/</em>array_values($keyed)
);
}
]]>
@@ -62,7 +62,6 @@ class ArrayIndentSniff extends AbstractArraySniff
// Determine how far indented the entire array declaration should be.
$ignore = Tokens::$emptyTokens;
$ignore[] = T_DOUBLE_ARROW;
$ignore[] = T_COMMA;
$prev = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true);
$start = $phpcsFile->findStartOfStatement($prev);
$first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $start, true);
@@ -152,7 +151,7 @@ class ArrayIndentSniff extends AbstractArraySniff
$error = 'Closing brace of array declaration must be on a new line';
$fix = $phpcsFile->addFixableError($error, $arrayEnd, 'CloseBraceNotNewLine');
if ($fix === true) {
$padding = $phpcsFile->eolChar.str_repeat(' ', $expectedIndent);
$padding = $phpcsFile->eolChar.str_repeat(' ', $startIndent);
$phpcsFile->fixer->addContentBefore($arrayEnd, $padding);
}
@@ -160,20 +159,19 @@ class ArrayIndentSniff extends AbstractArraySniff
}
// The close brace must be indented one stop less.
$expectedIndent -= $this->indent;
$foundIndent = ($tokens[$arrayEnd]['column'] - 1);
if ($foundIndent === $expectedIndent) {
$foundIndent = ($tokens[$arrayEnd]['column'] - 1);
if ($foundIndent === $startIndent) {
return;
}
$pluralizeSpace = 's';
if ($expectedIndent === 1) {
if ($startIndent === 1) {
$pluralizeSpace = '';
}
$error = 'Array close brace not indented correctly; expected %s space%s but found %s';
$data = [
$expectedIndent,
$startIndent,
$pluralizeSpace,
$foundIndent,
];
@@ -182,7 +180,7 @@ class ArrayIndentSniff extends AbstractArraySniff
return;
}
$padding = str_repeat(' ', $expectedIndent);
$padding = str_repeat(' ', $startIndent);
if ($foundIndent === 0) {
$phpcsFile->fixer->addContentBefore($arrayEnd, $padding);
} else {
@@ -45,9 +45,7 @@ class DisallowLongArraySyntaxSniff implements Sniff
$error = 'Short array syntax must be used to define arrays';
if (isset($tokens[$stackPtr]['parenthesis_opener']) === false
|| isset($tokens[$stackPtr]['parenthesis_closer']) === false
) {
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
// Live coding/parse error, just show the error, don't try and fix it.
$phpcsFile->addError($error, $stackPtr, 'Found');
return;
@@ -61,13 +59,9 @@ class DisallowLongArraySyntaxSniff implements Sniff
$phpcsFile->fixer->beginChangeset();
if ($opener === null) {
$phpcsFile->fixer->replaceToken($stackPtr, '[]');
} else {
$phpcsFile->fixer->replaceToken($stackPtr, '');
$phpcsFile->fixer->replaceToken($opener, '[');
$phpcsFile->fixer->replaceToken($closer, ']');
}
$phpcsFile->fixer->replaceToken($stackPtr, '');
$phpcsFile->fixer->replaceToken($opener, '[');
$phpcsFile->fixer->replaceToken($closer, ']');
$phpcsFile->fixer->endChangeset();
}
@@ -11,6 +11,7 @@ namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Classes;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class DuplicateClassNameSniff implements Sniff
{
@@ -55,63 +56,70 @@ class DuplicateClassNameSniff implements Sniff
T_TRAIT,
T_ENUM,
T_NAMESPACE,
T_CLOSE_TAG,
];
$stackPtr = $phpcsFile->findNext($findTokens, ($stackPtr + 1));
while ($stackPtr !== false) {
if ($tokens[$stackPtr]['code'] === T_CLOSE_TAG) {
// We can stop here. The sniff will continue from the next open
// tag when PHPCS reaches that token, if there is one.
return;
}
// Keep track of what namespace we are in.
if ($tokens[$stackPtr]['code'] === T_NAMESPACE) {
$nsEnd = $phpcsFile->findNext(
[
T_NS_SEPARATOR,
T_STRING,
T_WHITESPACE,
],
($stackPtr + 1),
null,
true
);
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextNonEmpty !== false
// Ignore namespace keyword used as operator.
&& $tokens[$nextNonEmpty]['code'] !== T_NS_SEPARATOR
) {
$namespace = '';
for ($i = $nextNonEmpty; $i < $phpcsFile->numTokens; $i++) {
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
continue;
}
$namespace = trim($phpcsFile->getTokensAsString(($stackPtr + 1), ($nsEnd - $stackPtr - 1)));
$stackPtr = $nsEnd;
} else {
$nameToken = $phpcsFile->findNext(T_STRING, $stackPtr);
$name = $tokens[$nameToken]['content'];
if ($namespace !== '') {
$name = $namespace.'\\'.$name;
if ($tokens[$i]['code'] !== T_STRING && $tokens[$i]['code'] !== T_NS_SEPARATOR) {
break;
}
$namespace .= $tokens[$i]['content'];
}
$stackPtr = $i;
}
} else {
$name = $phpcsFile->getDeclarationName($stackPtr);
if (empty($name) === false) {
if ($namespace !== '') {
$name = $namespace.'\\'.$name;
}
$compareName = strtolower($name);
if (isset($this->foundClasses[$compareName]) === true) {
$type = strtolower($tokens[$stackPtr]['content']);
$file = $this->foundClasses[$compareName]['file'];
$line = $this->foundClasses[$compareName]['line'];
$error = 'Duplicate %s name "%s" found; first defined in %s on line %s';
$data = [
$type,
$name,
$file,
$line,
];
$phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
} else {
$this->foundClasses[$compareName] = [
'file' => $phpcsFile->getFilename(),
'line' => $tokens[$stackPtr]['line'],
];
$compareName = strtolower($name);
if (isset($this->foundClasses[$compareName]) === true) {
$type = strtolower($tokens[$stackPtr]['content']);
$file = $this->foundClasses[$compareName]['file'];
$line = $this->foundClasses[$compareName]['line'];
$error = 'Duplicate %s name "%s" found; first defined in %s on line %s';
$data = [
$type,
$name,
$file,
$line,
];
$phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
} else {
$this->foundClasses[$compareName] = [
'file' => $phpcsFile->getFilename(),
'line' => $tokens[$stackPtr]['line'],
];
}
}//end if
if (isset($tokens[$stackPtr]['scope_closer']) === true) {
$stackPtr = $tokens[$stackPtr]['scope_closer'];
}
}//end if
$stackPtr = $phpcsFile->findNext($findTokens, ($stackPtr + 1));
}//end while
return $phpcsFile->numTokens;
}//end process()
@@ -2,7 +2,7 @@
/**
* Checks against empty PHP statements.
*
* - Check against two semi-colons with no executable code in between.
* - Check against two semicolons with no executable code in between.
* - Check against an empty PHP open - close tag combination.
*
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
@@ -48,115 +48,136 @@ class EmptyPHPStatementSniff implements Sniff
{
$tokens = $phpcsFile->getTokens();
switch ($tokens[$stackPtr]['type']) {
// Detect `something();;`.
case 'T_SEMICOLON':
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($prevNonEmpty === false) {
return;
}
if ($tokens[$prevNonEmpty]['code'] !== T_SEMICOLON
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
) {
if (isset($tokens[$prevNonEmpty]['scope_condition']) === false) {
return;
}
if ($tokens[$prevNonEmpty]['scope_opener'] !== $prevNonEmpty
&& $tokens[$prevNonEmpty]['code'] !== T_CLOSE_CURLY_BRACKET
) {
return;
}
$scopeOwner = $tokens[$tokens[$prevNonEmpty]['scope_condition']]['code'];
if ($scopeOwner === T_CLOSURE || $scopeOwner === T_ANON_CLASS || $scopeOwner === T_MATCH) {
return;
}
// Else, it's something like `if (foo) {};` and the semi-colon is not needed.
}
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
$nested = $tokens[$stackPtr]['nested_parenthesis'];
$lastCloser = array_pop($nested);
if (isset($tokens[$lastCloser]['parenthesis_owner']) === true
&& $tokens[$tokens[$lastCloser]['parenthesis_owner']]['code'] === T_FOR
) {
// Empty for() condition.
return;
}
}
$fix = $phpcsFile->addFixableWarning(
'Empty PHP statement detected: superfluous semi-colon.',
$stackPtr,
'SemicolonWithoutCodeDetected'
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
if ($tokens[$prevNonEmpty]['code'] === T_OPEN_TAG
|| $tokens[$prevNonEmpty]['code'] === T_OPEN_TAG_WITH_ECHO
) {
// Check for superfluous whitespace after the semi-colon which will be
// removed as the `<?php ` open tag token already contains whitespace,
// either a space or a new line.
if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
$replacement = str_replace(' ', '', $tokens[($stackPtr + 1)]['content']);
$phpcsFile->fixer->replaceToken(($stackPtr + 1), $replacement);
}
}
for ($i = $stackPtr; $i > $prevNonEmpty; $i--) {
if ($tokens[$i]['code'] !== T_SEMICOLON
&& $tokens[$i]['code'] !== T_WHITESPACE
) {
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}//end if
break;
// Detect `<?php ? >`.
case 'T_CLOSE_TAG':
$prevNonEmpty = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($prevNonEmpty === false
|| ($tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO)
) {
return;
}
$fix = $phpcsFile->addFixableWarning(
'Empty PHP open/close tag combination detected.',
$prevNonEmpty,
'EmptyPHPOpenCloseTagsDetected'
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = $prevNonEmpty; $i <= $stackPtr; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
break;
default:
// Deliberately left empty.
break;
}//end switch
if ($tokens[$stackPtr]['code'] === T_SEMICOLON) {
$this->processSemicolon($phpcsFile, $stackPtr);
} else {
$this->processCloseTag($phpcsFile, $stackPtr);
}
}//end process()
/**
* Detect `something();;`.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
private function processSemicolon(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($tokens[$prevNonEmpty]['code'] !== T_SEMICOLON
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
) {
if (isset($tokens[$prevNonEmpty]['scope_condition']) === false) {
return;
}
if ($tokens[$prevNonEmpty]['scope_opener'] !== $prevNonEmpty
&& $tokens[$prevNonEmpty]['code'] !== T_CLOSE_CURLY_BRACKET
) {
return;
}
$scopeOwner = $tokens[$tokens[$prevNonEmpty]['scope_condition']]['code'];
if ($scopeOwner === T_CLOSURE || $scopeOwner === T_ANON_CLASS || $scopeOwner === T_MATCH) {
return;
}
// Else, it's something like `if (foo) {};` and the semicolon is not needed.
}
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
$nested = $tokens[$stackPtr]['nested_parenthesis'];
$lastCloser = array_pop($nested);
if (isset($tokens[$lastCloser]['parenthesis_owner']) === true
&& $tokens[$tokens[$lastCloser]['parenthesis_owner']]['code'] === T_FOR
) {
// Empty for() condition.
return;
}
}
$fix = $phpcsFile->addFixableWarning(
'Empty PHP statement detected: superfluous semicolon.',
$stackPtr,
'SemicolonWithoutCodeDetected'
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
if ($tokens[$prevNonEmpty]['code'] === T_OPEN_TAG
|| $tokens[$prevNonEmpty]['code'] === T_OPEN_TAG_WITH_ECHO
) {
// Check for superfluous whitespace after the semicolon which should be
// removed as the `<?php ` open tag token already contains whitespace,
// either a space or a new line.
if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
$replacement = str_replace(' ', '', $tokens[($stackPtr + 1)]['content']);
$phpcsFile->fixer->replaceToken(($stackPtr + 1), $replacement);
}
}
for ($i = $stackPtr; $i > $prevNonEmpty; $i--) {
if ($tokens[$i]['code'] !== T_SEMICOLON
&& $tokens[$i]['code'] !== T_WHITESPACE
) {
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}//end if
}//end processSemicolon()
/**
* Detect `<?php ? >`.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
private function processCloseTag(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$prevNonEmpty = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
) {
return;
}
$fix = $phpcsFile->addFixableWarning(
'Empty PHP open/close tag combination detected.',
$prevNonEmpty,
'EmptyPHPOpenCloseTagsDetected'
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = $prevNonEmpty; $i <= $stackPtr; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
}//end processCloseTag()
}//end class
@@ -67,7 +67,7 @@ class JumbledIncrementerSniff implements Sniff
return;
}
// Find incrementors for outer loop.
// Find incrementers for outer loop.
$outer = $this->findIncrementers($tokens, $token);
// Skip if empty.
@@ -88,8 +88,8 @@ class JumbledIncrementerSniff implements Sniff
$diff = array_intersect($outer, $inner);
if (count($diff) !== 0) {
$error = 'Loop incrementor (%s) jumbling with inner loop';
$data = [join(', ', $diff)];
$error = 'Loop incrementer (%s) jumbling with inner loop';
$data = [implode(', ', $diff)];
$phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
}
}
@@ -101,14 +101,14 @@ class JumbledIncrementerSniff implements Sniff
* Get all used variables in the incrementer part of a for statement.
*
* @param array<int, array> $tokens Array with all code sniffer tokens.
* @param array<string, mixed> $token Current for loop token
* @param array<string, mixed> $token Current for loop token.
*
* @return string[] List of all found incrementer variables.
*/
protected function findIncrementers(array $tokens, array $token)
{
// Skip invalid statement.
if (isset($token['parenthesis_opener']) === false) {
if (isset($token['parenthesis_opener'], $token['parenthesis_closer']) === false) {
return [];
}
@@ -53,8 +53,7 @@ class RequireExplicitBooleanOperatorPrecedenceSniff implements Sniff
*/
public function register()
{
$this->searchTargets = Tokens::$booleanOperators;
$this->searchTargets += Tokens::$blockOpeners;
$this->searchTargets = Tokens::$booleanOperators;
$this->searchTargets[T_INLINE_THEN] = T_INLINE_THEN;
$this->searchTargets[T_INLINE_ELSE] = T_INLINE_ELSE;
@@ -102,12 +101,6 @@ class RequireExplicitBooleanOperatorPrecedenceSniff implements Sniff
return;
}
if (isset(Tokens::$blockOpeners[$tokens[$previous]['code']]) === true) {
// Beginning of the expression found for a block opener. Needed to
// correctly handle match arms.
return;
}
// We found a mismatching operator, thus we must report the error.
$error = 'Mixing different binary boolean operators within an expression';
$error .= ' without using parentheses to clarify precedence is not allowed.';
@@ -28,6 +28,17 @@ use PHP_CodeSniffer\Util\Tokens;
class UselessOverridingMethodSniff implements Sniff
{
/**
* Object-Oriented scopes in which a call to parent::method() can exist.
*
* @var array<int|string, bool> Keys are the token constants, value is irrelevant.
*/
private $validOOScopes = [
T_CLASS => true,
T_ANON_CLASS => true,
T_TRAIT => true,
];
/**
* Registers the tokens that this sniff wants to listen for.
@@ -56,7 +67,15 @@ class UselessOverridingMethodSniff implements Sniff
$token = $tokens[$stackPtr];
// Skip function without body.
if (isset($token['scope_opener']) === false) {
if (isset($token['scope_opener'], $token['scope_closer']) === false) {
return;
}
$conditions = $token['conditions'];
$lastCondition = end($conditions);
// Skip functions that are not a method part of a class, anon class or trait.
if (isset($this->validOOScopes[$lastCondition]) === false) {
return;
}
@@ -93,15 +112,15 @@ class UselessOverridingMethodSniff implements Sniff
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
// Skip for invalid code.
if ($next === false || $tokens[$next]['code'] !== T_DOUBLE_COLON) {
if ($tokens[$next]['code'] !== T_DOUBLE_COLON) {
return;
}
// Find next non empty token index, should be the function name.
// Find next non empty token index, should be the name of the method being called.
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
// Skip for invalid code or other method.
if ($next === false || $tokens[$next]['content'] !== $methodName) {
if (strcasecmp($tokens[$next]['content'], $methodName) !== 0) {
return;
}
@@ -109,14 +128,13 @@ class UselessOverridingMethodSniff implements Sniff
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
// Skip for invalid code.
if ($next === false || $tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
if ($tokens[$next]['code'] !== T_OPEN_PARENTHESIS || isset($tokens[$next]['parenthesis_closer']) === false) {
return;
}
$parameters = [''];
$parenthesisCount = 1;
$count = count($tokens);
for (++$next; $next < $count; ++$next) {
for (++$next; $next < $phpcsFile->numTokens; ++$next) {
$code = $tokens[$next]['code'];
if ($code === T_OPEN_PARENTHESIS) {
@@ -135,15 +153,20 @@ class UselessOverridingMethodSniff implements Sniff
}//end for
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
if ($next === false || $tokens[$next]['code'] !== T_SEMICOLON) {
if ($tokens[$next]['code'] !== T_SEMICOLON && $tokens[$next]['code'] !== T_CLOSE_TAG) {
return;
}
// This list deliberately does not include the `T_OPEN_TAG_WITH_ECHO` as that token implicitly is an echo statement, i.e. content.
$nonContent = Tokens::$emptyTokens;
$nonContent[T_OPEN_TAG] = T_OPEN_TAG;
$nonContent[T_CLOSE_TAG] = T_CLOSE_TAG;
// Check rest of the scope.
for (++$next; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
// Skip for any other content.
if (isset(Tokens::$emptyTokens[$code]) === false) {
if (isset($nonContent[$code]) === false) {
return;
}
}
@@ -25,7 +25,10 @@ class DisallowYodaConditionsSniff implements Sniff
*/
public function register()
{
return Tokens::$comparisonTokens;
$tokens = Tokens::$comparisonTokens;
unset($tokens[T_COALESCE]);
return $tokens;
}//end register()
@@ -54,9 +57,7 @@ class DisallowYodaConditionsSniff implements Sniff
T_CONSTANT_ENCAPSED_STRING,
];
if ($previousIndex === false
|| in_array($tokens[$previousIndex]['code'], $relevantTokens, true) === false
) {
if (in_array($tokens[$previousIndex]['code'], $relevantTokens, true) === false) {
return;
}
@@ -68,9 +69,6 @@ class DisallowYodaConditionsSniff implements Sniff
}
$prevIndex = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($previousIndex - 1), null, true);
if ($prevIndex === false) {
return;
}
if (in_array($tokens[$prevIndex]['code'], Tokens::$arithmeticTokens, true) === true) {
return;
@@ -82,16 +80,15 @@ class DisallowYodaConditionsSniff implements Sniff
// Is it a parenthesis.
if ($tokens[$previousIndex]['code'] === T_CLOSE_PARENTHESIS) {
// Check what exists inside the parenthesis.
$closeParenthesisIndex = $phpcsFile->findPrevious(
$beforeOpeningParenthesisIndex = $phpcsFile->findPrevious(
Tokens::$emptyTokens,
($tokens[$previousIndex]['parenthesis_opener'] - 1),
null,
true
);
if ($closeParenthesisIndex === false || $tokens[$closeParenthesisIndex]['code'] !== T_ARRAY) {
if ($tokens[$closeParenthesisIndex]['code'] === T_STRING) {
if ($beforeOpeningParenthesisIndex === false || $tokens[$beforeOpeningParenthesisIndex]['code'] !== T_ARRAY) {
if ($tokens[$beforeOpeningParenthesisIndex]['code'] === T_STRING) {
return;
}
@@ -107,14 +104,14 @@ class DisallowYodaConditionsSniff implements Sniff
return;
}
// If there is nothing inside the parenthesis, it it not a Yoda.
// If there is nothing inside the parenthesis, it is not a Yoda condition.
$opener = $tokens[$previousIndex]['parenthesis_opener'];
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($previousIndex - 1), ($opener + 1), true);
if ($prev === false) {
return;
}
} else if ($tokens[$closeParenthesisIndex]['code'] === T_ARRAY
&& $this->isArrayStatic($phpcsFile, $closeParenthesisIndex) === false
} else if ($tokens[$beforeOpeningParenthesisIndex]['code'] === T_ARRAY
&& $this->isArrayStatic($phpcsFile, $beforeOpeningParenthesisIndex) === false
) {
return;
}//end if
@@ -141,7 +138,6 @@ class DisallowYodaConditionsSniff implements Sniff
{
$tokens = $phpcsFile->getTokens();
$arrayEnd = null;
if ($tokens[$arrayToken]['code'] === T_OPEN_SHORT_ARRAY) {
$start = $arrayToken;
$end = $tokens[$arrayToken]['bracket_closer'];
@@ -149,7 +145,8 @@ class DisallowYodaConditionsSniff implements Sniff
$start = $tokens[$arrayToken]['parenthesis_opener'];
$end = $tokens[$arrayToken]['parenthesis_closer'];
} else {
return true;
// Shouldn't be possible but may happen if external sniffs are using this method.
return true; // @codeCoverageIgnore
}
$staticTokens = Tokens::$emptyTokens;
@@ -75,7 +75,7 @@ class InlineControlStructureSniff implements Sniff
// Ignore the ELSE in ELSE IF. We'll process the IF part later.
if ($tokens[$stackPtr]['code'] === T_ELSE) {
$next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($tokens[$next]['code'] === T_IF) {
return;
}
@@ -95,21 +95,6 @@ class InlineControlStructureSniff implements Sniff
return;
}
}
// In Javascript DO WHILE loops without curly braces are legal. This
// is only valid if a single statement is present between the DO and
// the WHILE. We can detect this by checking only a single semicolon
// is present between them.
if ($tokens[$stackPtr]['code'] === T_WHILE && $phpcsFile->tokenizerType === 'JS') {
$lastDo = $phpcsFile->findPrevious(T_DO, ($stackPtr - 1));
$lastSemicolon = $phpcsFile->findPrevious(T_SEMICOLON, ($stackPtr - 1));
if ($lastDo !== false && $lastSemicolon !== false && $lastDo < $lastSemicolon) {
$precedingSemicolon = $phpcsFile->findPrevious(T_SEMICOLON, ($lastSemicolon - 1));
if ($precedingSemicolon === false || $precedingSemicolon < $lastDo) {
return;
}
}
}
}//end if
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false
@@ -150,7 +135,7 @@ class InlineControlStructureSniff implements Sniff
// tag in short open tags and scan run with short_open_tag=Off.
// Bow out completely as any further detection will be unreliable
// and create incorrect fixes or cause fixer conflicts.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
unset($nextNonEmpty, $start);
@@ -52,7 +52,7 @@ class CSSLintSniff implements Sniff
{
$csslintPath = Config::getExecutablePath('csslint');
if ($csslintPath === null) {
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
$fileName = $phpcsFile->getFilename();
@@ -61,7 +61,7 @@ class CSSLintSniff implements Sniff
exec($cmd, $output, $retval);
if (is_array($output) === false) {
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
$count = count($output);
@@ -90,7 +90,7 @@ class CSSLintSniff implements Sniff
}//end for
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}//end process()
@@ -63,13 +63,13 @@ class ClosureLinterSniff implements Sniff
* the token was found.
*
* @return int
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jslint.js could not be run
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jslint.js could not be run.
*/
public function process(File $phpcsFile, $stackPtr)
{
$lintPath = Config::getExecutablePath('gjslint');
if ($lintPath === null) {
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
$fileName = $phpcsFile->getFilename();
@@ -79,7 +79,7 @@ class ClosureLinterSniff implements Sniff
exec($cmd, $output, $retval);
if (is_array($output) === false) {
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
foreach ($output as $finding) {
@@ -111,7 +111,7 @@ class ClosureLinterSniff implements Sniff
}//end foreach
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}//end process()
@@ -54,13 +54,13 @@ class ESLintSniff implements Sniff
* the token was found.
*
* @return int
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jshint.js could not be run
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jshint.js could not be run.
*/
public function process(File $phpcsFile, $stackPtr)
{
$eslintPath = Config::getExecutablePath('eslint');
if ($eslintPath === null) {
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
$filename = $phpcsFile->getFilename();
@@ -86,13 +86,13 @@ class ESLintSniff implements Sniff
if ($code <= 0) {
// No errors, continue.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
$data = json_decode(implode("\n", $stdout));
if (json_last_error() !== JSON_ERROR_NONE) {
// Ignore any errors.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
// Data is a list of files, but we only pass a single one.
@@ -107,7 +107,7 @@ class ESLintSniff implements Sniff
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}//end process()
@@ -48,14 +48,14 @@ class JSHintSniff implements Sniff
* the token was found.
*
* @return int
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jshint.js could not be run
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jshint.js could not be run.
*/
public function process(File $phpcsFile, $stackPtr)
{
$rhinoPath = Config::getExecutablePath('rhino');
$jshintPath = Config::getExecutablePath('jshint');
if ($jshintPath === null) {
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
$fileName = $phpcsFile->getFilename();
@@ -89,7 +89,7 @@ class JSHintSniff implements Sniff
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}//end process()
@@ -49,13 +49,13 @@ class ByteOrderMarkSniff implements Sniff
* @param int $stackPtr The position of the current token in
* the stack passed in $tokens.
*
* @return void
* @return int
*/
public function process(File $phpcsFile, $stackPtr)
{
// The BOM will be the very first token in the file.
if ($stackPtr !== 0) {
return;
return $phpcsFile->numTokens;
}
$tokens = $phpcsFile->getTokens();
@@ -68,12 +68,14 @@ class ByteOrderMarkSniff implements Sniff
$error = 'File contains %s byte order mark, which may corrupt your application';
$phpcsFile->addError($error, $stackPtr, 'Found', $errorData);
$phpcsFile->recordMetric($stackPtr, 'Using byte order mark', 'yes');
return;
return $phpcsFile->numTokens;
}
}
$phpcsFile->recordMetric($stackPtr, 'Using byte order mark', 'no');
return $phpcsFile->numTokens;
}//end process()
@@ -76,7 +76,7 @@ class EndFileNewlineSniff implements Sniff
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}//end process()
@@ -83,7 +83,7 @@ class EndFileNoNewlineSniff implements Sniff
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}//end process()
@@ -54,7 +54,7 @@ class ExecutableFileSniff implements Sniff
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}//end process()
@@ -54,7 +54,7 @@ class InlineHTMLSniff implements Sniff
{
// Allow a byte-order mark.
$tokens = $phpcsFile->getTokens();
foreach ($this->bomDefinitions as $bomName => $expectedBomHex) {
foreach ($this->bomDefinitions as $expectedBomHex) {
$bomByteLength = (strlen($expectedBomHex) / 2);
$htmlBomHex = bin2hex(substr($tokens[0]['content'], 0, $bomByteLength));
if ($htmlBomHex === $expectedBomHex && strlen($tokens[0]['content']) === $bomByteLength) {
@@ -68,7 +68,7 @@ class LineEndingsSniff implements Sniff
if ($found === $this->eolChar) {
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
// Check for single line files without an EOL. This is a very special
@@ -79,7 +79,7 @@ class LineEndingsSniff implements Sniff
if ($tokens[$lastToken]['line'] === 1
&& $tokens[$lastToken]['content'] !== "\n"
) {
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
}
@@ -140,7 +140,7 @@ class LineEndingsSniff implements Sniff
}//end if
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}//end process()
@@ -80,7 +80,7 @@ class LineLengthSniff implements Sniff
$this->checkLineLength($phpcsFile, $tokens, $i);
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}//end process()
@@ -44,7 +44,7 @@ class LowercasedFilenameSniff implements Sniff
{
$filename = $phpcsFile->getFilename();
if ($filename === 'STDIN') {
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}
$filename = basename($filename);
@@ -62,7 +62,7 @@ class LowercasedFilenameSniff implements Sniff
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
return $phpcsFile->numTokens;
}//end process()
@@ -27,6 +27,10 @@ class CallTimePassByReferenceSniff implements Sniff
return [
T_STRING,
T_VARIABLE,
T_ANON_CLASS,
T_PARENT,
T_SELF,
T_STATIC,
];
}//end register()
@@ -50,12 +54,12 @@ class CallTimePassByReferenceSniff implements Sniff
$prev = $phpcsFile->findPrevious($findTokens, ($stackPtr - 1), null, true);
// Skip tokens that are the names of functions or classes
// Skip tokens that are the names of functions
// within their definitions. For example: function myFunction...
// "myFunction" is T_STRING but we should skip because it is not a
// function or method *call*.
$prevCode = $tokens[$prev]['code'];
if ($prevCode === T_FUNCTION || $prevCode === T_CLASS) {
if ($prevCode === T_FUNCTION) {
return;
}
@@ -69,7 +73,7 @@ class CallTimePassByReferenceSniff implements Sniff
true
);
if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
if ($openBracket === false || $tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
return;
}
@@ -86,10 +90,6 @@ class CallTimePassByReferenceSniff implements Sniff
];
while (($nextSeparator = $phpcsFile->findNext($find, ($nextSeparator + 1), $closeBracket)) !== false) {
if (isset($tokens[$nextSeparator]['nested_parenthesis']) === false) {
continue;
}
if ($tokens[$nextSeparator]['code'] === T_OPEN_SHORT_ARRAY) {
$nextSeparator = $tokens[$nextSeparator]['bracket_closer'];
continue;
@@ -109,17 +109,25 @@ class FunctionCallArgumentSpacingSniff implements Sniff
$find = [
T_COMMA,
T_CLOSURE,
T_FN,
T_ANON_CLASS,
T_OPEN_SHORT_ARRAY,
T_MATCH,
];
while (($nextSeparator = $phpcsFile->findNext($find, ($nextSeparator + 1), $closeBracket)) !== false) {
if ($tokens[$nextSeparator]['code'] === T_CLOSURE
|| $tokens[$nextSeparator]['code'] === T_ANON_CLASS
|| $tokens[$nextSeparator]['code'] === T_MATCH
) {
// Skip closures.
// Skip closures, anon class declarations and match control structures.
$nextSeparator = $tokens[$nextSeparator]['scope_closer'];
continue;
} else if ($tokens[$nextSeparator]['code'] === T_FN) {
// Skip arrow functions, but don't skip the arrow function closer as it is likely to
// be the comma separating it from the next function call argument (or the parenthesis closer).
$nextSeparator = ($tokens[$nextSeparator]['scope_closer'] - 1);
continue;
} else if ($tokens[$nextSeparator]['code'] === T_OPEN_SHORT_ARRAY) {
// Skips arrays using short notation.
$nextSeparator = $tokens[$nextSeparator]['bracket_closer'];
@@ -72,17 +72,9 @@ class OpeningFunctionBraceKernighanRitchieSniff implements Sniff
}
$openingBrace = $tokens[$stackPtr]['scope_opener'];
$closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
$use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']);
if ($use !== false) {
$openBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1));
$closeBracket = $tokens[$openBracket]['parenthesis_closer'];
}
}
// Find the end of the function declaration.
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($openingBrace - 1), $closeBracket, true);
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($openingBrace - 1), null, true);
$functionLine = $tokens[$prev]['line'];
$braceLine = $tokens[$openingBrace]['line'];
@@ -99,7 +91,6 @@ class OpeningFunctionBraceKernighanRitchieSniff implements Sniff
$error = 'Opening brace should be on the same line as the declaration';
$fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceOnNewLine');
if ($fix === true) {
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($openingBrace - 1), $closeBracket, true);
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->addContent($prev, ' {');
$phpcsFile->fixer->replaceToken($openingBrace, '');
@@ -147,28 +138,32 @@ class OpeningFunctionBraceKernighanRitchieSniff implements Sniff
return;
}
// We are looking for tabs, even if they have been replaced, because
// we enforce a space here.
if (isset($tokens[($openingBrace - 1)]['orig_content']) === true) {
$spacing = $tokens[($openingBrace - 1)]['orig_content'];
} else {
$spacing = $tokens[($openingBrace - 1)]['content'];
}
// Enforce a single space. Tabs not allowed.
$spacing = $tokens[($openingBrace - 1)]['content'];
if ($tokens[($openingBrace - 1)]['code'] !== T_WHITESPACE) {
$length = 0;
} else if ($spacing === "\t") {
// Tab without tab-width set, so no tab replacement has taken place.
$length = '\t';
} else {
$length = strlen($spacing);
}
// If tab replacement is on, avoid confusing the user with a "expected 1 space, found 1"
// message when the "1" found is actually a tab, not a space.
if ($length === 1
&& isset($tokens[($openingBrace - 1)]['orig_content']) === true
&& $tokens[($openingBrace - 1)]['orig_content'] === "\t"
) {
$length = '\t';
}
if ($length !== 1) {
$error = 'Expected 1 space before opening brace; found %s';
$data = [$length];
$fix = $phpcsFile->addFixableError($error, $openingBrace, 'SpaceBeforeBrace', $data);
if ($fix === true) {
if ($length === 0 || $length === '\t') {
if ($length === 0) {
$phpcsFile->fixer->addContentBefore($openingBrace, ' ');
} else {
$phpcsFile->fixer->replaceToken(($openingBrace - 1), ' ');
@@ -60,8 +60,8 @@ class CyclomaticComplexitySniff implements Sniff
{
$tokens = $phpcsFile->getTokens();
// Ignore abstract methods.
if (isset($tokens[$stackPtr]['scope_opener']) === false) {
// Ignore abstract and interface methods. Bail early when live coding.
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
return;
}
@@ -56,8 +56,8 @@ class NestingLevelSniff implements Sniff
{
$tokens = $phpcsFile->getTokens();
// Ignore abstract methods.
if (isset($tokens[$stackPtr]['scope_opener']) === false) {
// Ignore abstract and interface methods. Bail early when live coding.
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
return;
}
@@ -45,7 +45,7 @@ class AbstractClassNamePrefixSniff implements Sniff
$className = $phpcsFile->getDeclarationName($stackPtr);
if ($className === null) {
// We are not interested in anonymous classes.
// Live coding or parse error.
return;
}
@@ -113,7 +113,7 @@ class CamelCapsFunctionNameSniff extends AbstractScopeSniff
$methodName = $phpcsFile->getDeclarationName($stackPtr);
if ($methodName === null) {
// Ignore closures.
// Live coding or parse error. Bow out.
return;
}
@@ -150,7 +150,7 @@ class CamelCapsFunctionNameSniff extends AbstractScopeSniff
return;
}
// Ignore first underscore in methods prefixed with "_".
// Ignore leading underscores in the method name.
$methodName = ltrim($methodName, '_');
$methodProps = $phpcsFile->getMethodProperties($stackPtr);
@@ -168,7 +168,6 @@ class CamelCapsFunctionNameSniff extends AbstractScopeSniff
}
$phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'no');
return;
} else {
$phpcsFile->recordMetric($stackPtr, 'CamelCase method name', 'yes');
}
@@ -189,7 +188,7 @@ class CamelCapsFunctionNameSniff extends AbstractScopeSniff
{
$functionName = $phpcsFile->getDeclarationName($stackPtr);
if ($functionName === null) {
// Ignore closures.
// Live coding or parse error. Bow out.
return;
}
@@ -206,7 +205,7 @@ class CamelCapsFunctionNameSniff extends AbstractScopeSniff
$phpcsFile->addError($error, $stackPtr, 'FunctionDoubleUnderscore', $errorData);
}
// Ignore first underscore in functions prefixed with "_".
// Ignore leading underscores in the method name.
$functionName = ltrim($functionName, '_');
if (Common::isCamelCaps($functionName, false, true, $this->strict) === false) {
@@ -15,6 +15,7 @@ namespace PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
use PHP_CodeSniffer\Util\Tokens;
class ConstructorNameSniff extends AbstractScopeSniff
{
@@ -90,27 +91,41 @@ class ConstructorNameSniff extends AbstractScopeSniff
}
// Stop if the constructor doesn't have a body, like when it is abstract.
if (isset($tokens[$stackPtr]['scope_closer']) === false) {
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
return;
}
$parentClassName = strtolower($phpcsFile->findExtendedClassName($currScope));
$parentClassName = $phpcsFile->findExtendedClassName($currScope);
if ($parentClassName === false) {
return;
}
$parentClassNameLc = strtolower($parentClassName);
$endFunctionIndex = $tokens[$stackPtr]['scope_closer'];
$startIndex = $stackPtr;
while (($doubleColonIndex = $phpcsFile->findNext(T_DOUBLE_COLON, $startIndex, $endFunctionIndex)) !== false) {
if ($tokens[($doubleColonIndex + 1)]['code'] === T_STRING
&& strtolower($tokens[($doubleColonIndex + 1)]['content']) === $parentClassName
$startIndex = $tokens[$stackPtr]['scope_opener'];
while (($doubleColonIndex = $phpcsFile->findNext(T_DOUBLE_COLON, ($startIndex + 1), $endFunctionIndex)) !== false) {
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($doubleColonIndex + 1), null, true);
if ($tokens[$nextNonEmpty]['code'] !== T_STRING
|| strtolower($tokens[$nextNonEmpty]['content']) !== $parentClassNameLc
) {
$error = 'PHP4 style calls to parent constructors are not allowed; use "parent::__construct()" instead';
$phpcsFile->addError($error, ($doubleColonIndex + 1), 'OldStyleCall');
$startIndex = $nextNonEmpty;
continue;
}
$startIndex = ($doubleColonIndex + 1);
}
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($doubleColonIndex - 1), null, true);
if ($tokens[$prevNonEmpty]['code'] === T_PARENT
|| $tokens[$prevNonEmpty]['code'] === T_SELF
|| $tokens[$prevNonEmpty]['code'] === T_STATIC
|| ($tokens[$prevNonEmpty]['code'] === T_STRING
&& strtolower($tokens[$prevNonEmpty]['content']) === $parentClassNameLc)
) {
$error = 'PHP4 style calls to parent constructors are not allowed; use "parent::__construct()" instead';
$phpcsFile->addError($error, $nextNonEmpty, 'OldStyleCall');
}
$startIndex = $nextNonEmpty;
}//end while
}//end processTokenWithinScope()
@@ -40,6 +40,7 @@ class InterfaceNameSuffixSniff implements Sniff
{
$interfaceName = $phpcsFile->getDeclarationName($stackPtr);
if ($interfaceName === null) {
// Live coding or parse error. Bow out.
return;
}
@@ -40,6 +40,7 @@ class TraitNameSuffixSniff implements Sniff
{
$traitName = $phpcsFile->getDeclarationName($stackPtr);
if ($traitName === null) {
// Live coding or parse error. Bow out.
return;
}
@@ -87,37 +87,39 @@ class UpperCaseConstantNameSniff implements Sniff
return;
}
// Make sure this is not a method call.
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
// Make sure this is not a method call or class instantiation.
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($tokens[$prev]['code'] === T_OBJECT_OPERATOR
|| $tokens[$prev]['code'] === T_DOUBLE_COLON
|| $tokens[$prev]['code'] === T_NULLSAFE_OBJECT_OPERATOR
|| $tokens[$prev]['code'] === T_NEW
) {
return;
}
// Make sure this is not an attribute.
if (empty($tokens[$stackPtr]['nested_attributes']) === false) {
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(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($openBracket === false) {
if ($openBracket === false || $tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
return;
}
// The next non-whitespace token must be the constant name.
$constPtr = $phpcsFile->findNext(T_WHITESPACE, ($openBracket + 1), null, true);
if ($tokens[$constPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
// Bow out if next non-empty token after the opening parenthesis is not a string (the
// constant name). This could happen when live coding, if the constant is a variable or an
// expression, or if handling a first-class callable or a function definition outside the
// global scope.
$constPtr = $phpcsFile->findNext(Tokens::$emptyTokens, ($openBracket + 1), null, true);
if ($constPtr === false || $tokens[$constPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
return;
}
$constName = $tokens[$constPtr]['content'];
// Check for constants like self::CONSTANT.
$prefix = '';
$splitPos = strpos($constName, '::');
if ($splitPos !== false) {
$prefix = substr($constName, 0, ($splitPos + 2));
$constName = substr($constName, ($splitPos + 2));
}
$prefix = '';
// Strip namespace from constant like /foo/bar/CONSTANT.
$splitPos = strrpos($constName, '\\');
@@ -128,9 +130,9 @@ class UpperCaseConstantNameSniff implements Sniff
if (strtoupper($constName) !== $constName) {
if (strtolower($constName) === $constName) {
$phpcsFile->recordMetric($stackPtr, 'Constant name case', 'lower');
$phpcsFile->recordMetric($constPtr, 'Constant name case', 'lower');
} else {
$phpcsFile->recordMetric($stackPtr, 'Constant name case', 'mixed');
$phpcsFile->recordMetric($constPtr, 'Constant name case', 'mixed');
}
$error = 'Constants must be uppercase; expected %s but found %s';
@@ -138,9 +140,9 @@ class UpperCaseConstantNameSniff implements Sniff
$prefix.strtoupper($constName),
$prefix.$constName,
];
$phpcsFile->addError($error, $stackPtr, 'ConstantNotUpperCase', $data);
$phpcsFile->addError($error, $constPtr, 'ConstantNotUpperCase', $data);
} else {
$phpcsFile->recordMetric($stackPtr, 'Constant name case', 'upper');
$phpcsFile->recordMetric($constPtr, 'Constant name case', 'upper');
}
}//end process()
@@ -56,7 +56,7 @@ class CharacterBeforePHPOpeningTagSniff implements Sniff
if ($stackPtr > 0) {
// Allow a byte-order mark.
$tokens = $phpcsFile->getTokens();
foreach ($this->bomDefinitions as $bomName => $expectedBomHex) {
foreach ($this->bomDefinitions as $expectedBomHex) {
$bomByteLength = (strlen($expectedBomHex) / 2);
$htmlBomHex = bin2hex(substr($tokens[0]['content'], 0, $bomByteLength));
if ($htmlBomHex === $expectedBomHex) {

Some files were not shown because too many files have changed in this diff Show More