基础代码
This commit is contained in:
+90
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Abstract private methods are not allowed since PHP 5.1.
|
||||
*
|
||||
* Abstract private methods were supported between PHP 5.0.0 and PHP 5.0.4, but
|
||||
* were then disallowed on the grounds that the behaviours of `private` and `abstract`
|
||||
* are mutually exclusive.
|
||||
*
|
||||
* PHP version 5.1
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration51.oop.php#migration51.oop-methods
|
||||
*
|
||||
* @since 9.2.0
|
||||
*/
|
||||
class ForbiddenAbstractPrivateMethodsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Valid scopes to check for abstract private methods.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $ooScopeTokens = array(
|
||||
'T_CLASS' => true,
|
||||
'T_TRAIT' => true,
|
||||
'T_ANON_CLASS' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_FUNCTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('5.1') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) === false) {
|
||||
// Function, not method.
|
||||
return;
|
||||
}
|
||||
|
||||
$properties = $phpcsFile->getMethodProperties($stackPtr);
|
||||
if ($properties['scope'] !== 'private' || $properties['is_abstract'] !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Abstract methods cannot be declared as private since PHP 5.1',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Anonymous classes are supported since PHP 7.0.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/language.oop5.anonymous.php
|
||||
* @link https://wiki.php.net/rfc/anonymous_classes
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
class NewAnonymousClassesSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Tokens which in various PHP versions indicate the `class` keyword.
|
||||
*
|
||||
* The dedicated anonymous class token is added from the `register()`
|
||||
* method if the token is available.
|
||||
*
|
||||
* @since 7.1.2
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $indicators = array(
|
||||
\T_CLASS => \T_CLASS,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if (\defined('T_ANON_CLASS')) {
|
||||
$this->indicators[\T_ANON_CLASS] = \T_ANON_CLASS;
|
||||
}
|
||||
|
||||
return array(\T_NEW);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.6') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
|
||||
if ($nextNonEmpty === false || isset($this->indicators[$tokens[$nextNonEmpty]['code']]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Still here ? In that case, it is an anonymous class.
|
||||
$phpcsFile->addError(
|
||||
'Anonymous classes are not supported in PHP 5.6 or earlier',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+913
@@ -0,0 +1,913 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect use of new PHP native classes.
|
||||
*
|
||||
* The sniff analyses the following constructs to find usage of new classes:
|
||||
* - Class instantiation using the `new` keyword.
|
||||
* - (Anonymous) Class declarations to detect new classes being extended by userland classes.
|
||||
* - Static use of class properties, constants or functions using the double colon.
|
||||
* - Function/closure declarations to detect new classes used as parameter type declarations.
|
||||
* - Function/closure declarations to detect new classes used as return type declarations.
|
||||
* - Try/catch statements to detect new exception classes being caught.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 5.6 Now extends the base `Sniff` class.
|
||||
* @since 7.1.0 Now extends the `AbstractNewFeatureSniff` class.
|
||||
*/
|
||||
class NewClassesSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of new classes, not present in older versions.
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If's sufficient to list the first version where the class appears.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @var array(string => array(string => bool))
|
||||
*/
|
||||
protected $newClasses = array(
|
||||
'ArrayObject' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'ArrayIterator' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'CachingIterator' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'DirectoryIterator' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'RecursiveDirectoryIterator' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'RecursiveIteratorIterator' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'php_user_filter' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'tidy' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
|
||||
'SimpleXMLElement' => array(
|
||||
'5.0.0' => false,
|
||||
'5.0.1' => true,
|
||||
),
|
||||
'tidyNode' => array(
|
||||
'5.0.0' => false,
|
||||
'5.0.1' => true,
|
||||
),
|
||||
|
||||
'libXMLError' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'PDO' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'PDOStatement' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'AppendIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'EmptyIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'FilterIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'InfiniteIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'IteratorIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'LimitIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'NoRewindIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'ParentIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'RecursiveArrayIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'RecursiveCachingIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'RecursiveFilterIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'SimpleXMLIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'SplFileObject' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'XMLReader' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
|
||||
'SplFileInfo' => array(
|
||||
'5.1.1' => false,
|
||||
'5.1.2' => true,
|
||||
),
|
||||
'SplTempFileObject' => array(
|
||||
'5.1.1' => false,
|
||||
'5.1.2' => true,
|
||||
),
|
||||
'XMLWriter' => array(
|
||||
'5.1.1' => false,
|
||||
'5.1.2' => true,
|
||||
),
|
||||
|
||||
'DateTime' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'DateTimeZone' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'RegexIterator' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'RecursiveRegexIterator' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'ReflectionFunctionAbstract' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'ZipArchive' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
|
||||
'Closure' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'DateInterval' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'DatePeriod' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'finfo' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'Collator' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'NumberFormatter' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'Locale' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'Normalizer' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'MessageFormatter' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'IntlDateFormatter' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'Phar' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'PharData' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'PharFileInfo' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'FilesystemIterator' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'GlobIterator' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'MultipleIterator' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'RecursiveTreeIterator' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'SplDoublyLinkedList' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'SplFixedArray' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'SplHeap' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'SplMaxHeap' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'SplMinHeap' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'SplObjectStorage' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'SplPriorityQueue' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'SplQueue' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'SplStack' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
|
||||
'ResourceBundle' => array(
|
||||
'5.3.1' => false,
|
||||
'5.3.2' => true,
|
||||
),
|
||||
|
||||
'CallbackFilterIterator' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'RecursiveCallbackFilterIterator' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'ReflectionZendExtension' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'SessionHandler' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'SNMP' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'Transliterator' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'Spoofchecker' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
|
||||
'Generator' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'CURLFile' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'DateTimeImmutable' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'IntlCalendar' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'IntlGregorianCalendar' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'IntlTimeZone' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'IntlBreakIterator' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'IntlRuleBasedBreakIterator' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'IntlCodePointBreakIterator' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'UConverter' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
|
||||
'GMP' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => true,
|
||||
),
|
||||
|
||||
'IntlChar' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'ReflectionType' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'ReflectionGenerator' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
|
||||
'ReflectionClassConstant' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
|
||||
'FFI' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
'FFI\CData' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
'FFI\CType' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
'ReflectionReference' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
'WeakReference' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* A list of new Exception classes, not present in older versions.
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If's sufficient to list the first version where the class appears.
|
||||
*
|
||||
* {@internal Classes listed here do not need to be added to the $newClasses
|
||||
* property as well.
|
||||
* This list is automatically added to the $newClasses property
|
||||
* in the `register()` method.}
|
||||
*
|
||||
* {@internal Helper to update this list: https://3v4l.org/MhlUp}
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @var array(string => array(string => bool))
|
||||
*/
|
||||
protected $newExceptions = array(
|
||||
'com_exception' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'DOMException' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'Exception' => array(
|
||||
// According to the docs introduced in PHP 5.1, but this appears to be.
|
||||
// an error. Class was introduced with try/catch keywords in PHP 5.0.
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'ReflectionException' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'SoapFault' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'SQLiteException' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
|
||||
'ErrorException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'BadFunctionCallException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'BadMethodCallException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'DomainException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'InvalidArgumentException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'LengthException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'LogicException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'mysqli_sql_exception' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'OutOfBoundsException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'OutOfRangeException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'OverflowException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'PDOException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'RangeException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'RuntimeException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'UnderflowException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'UnexpectedValueException' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
|
||||
'PharException' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
|
||||
'SNMPException' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
|
||||
'IntlException' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
|
||||
'Error' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'ArithmeticError' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'AssertionError' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'DivisionByZeroError' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'ParseError' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'TypeError' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'ClosedGeneratorException' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'UI\Exception\InvalidArgumentException' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'UI\Exception\RuntimeException' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
|
||||
'ArgumentCountError' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
|
||||
'SodiumException' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
|
||||
'CompileError' => array(
|
||||
'7.2' => false,
|
||||
'7.3' => true,
|
||||
),
|
||||
'JsonException' => array(
|
||||
'7.2' => false,
|
||||
'7.3' => true,
|
||||
),
|
||||
|
||||
'FFI\Exception' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
'FFI\ParserException' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 7.0.3 - Now also targets the `class` keyword to detect extended classes.
|
||||
* - Now also targets double colons to detect static class use.
|
||||
* @since 7.1.4 - Now also targets anonymous classes to detect extended classes.
|
||||
* - Now also targets functions/closures to detect new classes used
|
||||
* as parameter type declarations.
|
||||
* - Now also targets the `catch` control structure to detect new
|
||||
* exception classes being caught.
|
||||
* @since 8.2.0 Now also targets the `T_RETURN_TYPE` token to detect new classes used
|
||||
* as return type declarations.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Handle case-insensitivity of class names.
|
||||
$this->newClasses = $this->arrayKeysToLowercase($this->newClasses);
|
||||
$this->newExceptions = $this->arrayKeysToLowercase($this->newExceptions);
|
||||
|
||||
// Add the Exception classes to the Classes list.
|
||||
$this->newClasses = array_merge($this->newClasses, $this->newExceptions);
|
||||
|
||||
$targets = array(
|
||||
\T_NEW,
|
||||
\T_CLASS,
|
||||
\T_DOUBLE_COLON,
|
||||
\T_FUNCTION,
|
||||
\T_CLOSURE,
|
||||
\T_CATCH,
|
||||
);
|
||||
|
||||
if (\defined('T_ANON_CLASS')) {
|
||||
$targets[] = \T_ANON_CLASS;
|
||||
}
|
||||
|
||||
if (\defined('T_RETURN_TYPE')) {
|
||||
$targets[] = \T_RETURN_TYPE;
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
switch ($tokens[$stackPtr]['type']) {
|
||||
case 'T_FUNCTION':
|
||||
case 'T_CLOSURE':
|
||||
$this->processFunctionToken($phpcsFile, $stackPtr);
|
||||
|
||||
// Deal with older PHPCS version which don't recognize return type hints
|
||||
// as well as newer PHPCS versions (3.3.0+) where the tokenization has changed.
|
||||
$returnTypeHint = $this->getReturnTypeHintToken($phpcsFile, $stackPtr);
|
||||
if ($returnTypeHint !== false) {
|
||||
$this->processReturnTypeToken($phpcsFile, $returnTypeHint);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'T_CATCH':
|
||||
$this->processCatchToken($phpcsFile, $stackPtr);
|
||||
break;
|
||||
|
||||
case 'T_RETURN_TYPE':
|
||||
$this->processReturnTypeToken($phpcsFile, $stackPtr);
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->processSingularToken($phpcsFile, $stackPtr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test for when a token resulting in a singular class name is encountered.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processSingularToken(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$FQClassName = '';
|
||||
|
||||
if ($tokens[$stackPtr]['type'] === 'T_NEW') {
|
||||
$FQClassName = $this->getFQClassNameFromNewToken($phpcsFile, $stackPtr);
|
||||
|
||||
} elseif ($tokens[$stackPtr]['type'] === 'T_CLASS' || $tokens[$stackPtr]['type'] === 'T_ANON_CLASS') {
|
||||
$FQClassName = $this->getFQExtendedClassName($phpcsFile, $stackPtr);
|
||||
|
||||
} elseif ($tokens[$stackPtr]['type'] === 'T_DOUBLE_COLON') {
|
||||
$FQClassName = $this->getFQClassNameFromDoubleColonToken($phpcsFile, $stackPtr);
|
||||
}
|
||||
|
||||
if ($FQClassName === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$className = substr($FQClassName, 1); // Remove global namespace indicator.
|
||||
$classNameLc = strtolower($className);
|
||||
|
||||
if (isset($this->newClasses[$classNameLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $className,
|
||||
'nameLc' => $classNameLc,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test for when a function token is encountered.
|
||||
*
|
||||
* - Detect new classes when used as a parameter type declaration.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processFunctionToken(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
// Retrieve typehints stripped of global NS indicator and/or nullable indicator.
|
||||
$typeHints = $this->getTypeHintsFromFunctionDeclaration($phpcsFile, $stackPtr);
|
||||
if (empty($typeHints) || \is_array($typeHints) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($typeHints as $hint) {
|
||||
|
||||
$typeHintLc = strtolower($hint);
|
||||
|
||||
if (isset($this->newClasses[$typeHintLc]) === true) {
|
||||
$itemInfo = array(
|
||||
'name' => $hint,
|
||||
'nameLc' => $typeHintLc,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test for when a catch token is encountered.
|
||||
*
|
||||
* - Detect exceptions when used in a catch statement.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processCatchToken(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
// Bow out during live coding.
|
||||
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$opener = $tokens[$stackPtr]['parenthesis_opener'];
|
||||
$closer = ($tokens[$stackPtr]['parenthesis_closer'] + 1);
|
||||
$name = '';
|
||||
$listen = array(
|
||||
// Parts of a (namespaced) class name.
|
||||
\T_STRING => true,
|
||||
\T_NS_SEPARATOR => true,
|
||||
// End/split tokens.
|
||||
\T_VARIABLE => false,
|
||||
\T_BITWISE_OR => false,
|
||||
\T_CLOSE_CURLY_BRACKET => false, // Shouldn't be needed as we expect a var before this.
|
||||
);
|
||||
|
||||
for ($i = ($opener + 1); $i < $closer; $i++) {
|
||||
if (isset($listen[$tokens[$i]['code']]) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($listen[$tokens[$i]['code']] === true) {
|
||||
$name .= $tokens[$i]['content'];
|
||||
continue;
|
||||
} else {
|
||||
if (empty($name) === true) {
|
||||
// Weird, we should have a name by the time we encounter a variable or |.
|
||||
// So this may be the closer.
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = ltrim($name, '\\');
|
||||
$nameLC = strtolower($name);
|
||||
|
||||
if (isset($this->newExceptions[$nameLC]) === true) {
|
||||
$itemInfo = array(
|
||||
'name' => $name,
|
||||
'nameLc' => $nameLC,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $i, $itemInfo);
|
||||
}
|
||||
|
||||
// Reset for a potential multi-catch.
|
||||
$name = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test for when a return type token is encountered.
|
||||
*
|
||||
* - Detect new classes when used as a return type declaration.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processReturnTypeToken(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$returnTypeHint = $this->getReturnTypeHintName($phpcsFile, $stackPtr);
|
||||
if (empty($returnTypeHint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$returnTypeHint = ltrim($returnTypeHint, '\\');
|
||||
$returnTypeHintLc = strtolower($returnTypeHint);
|
||||
|
||||
if (isset($this->newClasses[$returnTypeHintLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Still here ? Then this is a return type declaration using a new class.
|
||||
$itemInfo = array(
|
||||
'name' => $returnTypeHint,
|
||||
'nameLc' => $returnTypeHintLc,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newClasses[$itemInfo['nameLc']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The built-in class ' . parent::getErrorMsgTemplate();
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Visibility for class constants is available since PHP 7.1.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/class_const_visibility
|
||||
* @link https://www.php.net/manual/en/language.oop5.constants.php#language.oop5.basic.class.this
|
||||
*
|
||||
* @since 7.0.7
|
||||
*/
|
||||
class NewConstVisibilitySniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_CONST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
|
||||
|
||||
// Is the previous token a visibility indicator ?
|
||||
if ($prevToken === false || isset(Tokens::$scopeModifiers[$tokens[$prevToken]['code']]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this a class constant ?
|
||||
if ($this->isClassConstant($phpcsFile, $stackPtr) === false) {
|
||||
// This may be a constant declaration in the global namespace with visibility,
|
||||
// but that would throw a parse error, i.e. not our concern.
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Visibility indicators for class constants are not supported in PHP 7.0 or earlier. Found "%s const"',
|
||||
$stackPtr,
|
||||
'Found',
|
||||
array($tokens[$prevToken]['content'])
|
||||
);
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect use of late static binding as introduced in PHP 5.3.
|
||||
*
|
||||
* Checks for:
|
||||
* - Late static binding as introduced in PHP 5.3.
|
||||
* - Late static binding being used outside of class scope (unsupported).
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/language.oop5.late-static-bindings.php
|
||||
* @link https://wiki.php.net/rfc/lsb_parentself_forwarding
|
||||
*
|
||||
* @since 7.0.3
|
||||
* @since 9.0.0 Renamed from `LateStaticBindingSniff` to `NewLateStaticBindingSniff`.
|
||||
*/
|
||||
class NewLateStaticBindingSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_STATIC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
|
||||
if ($nextNonEmpty === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
if ($tokens[$nextNonEmpty]['code'] !== \T_DOUBLE_COLON) {
|
||||
return;
|
||||
}
|
||||
|
||||
$inClass = $this->inClassScope($phpcsFile, $stackPtr, false);
|
||||
|
||||
if ($inClass === true && $this->supportsBelow('5.2') === true) {
|
||||
$phpcsFile->addError(
|
||||
'Late static binding is not supported in PHP 5.2 or earlier.',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
|
||||
if ($inClass === false) {
|
||||
$phpcsFile->addError(
|
||||
'Late static binding is not supported outside of class scope.',
|
||||
$stackPtr,
|
||||
'OutsideClassScope'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Typed class property declarations are available since PHP 7.4.
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.new-features.php#migration74.new-features.core.typed-properties
|
||||
* @link https://wiki.php.net/rfc/typed_properties_v2
|
||||
*
|
||||
* @since 9.2.0
|
||||
*/
|
||||
class NewTypedPropertiesSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Valid property modifier keywords.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $modifierKeywords = array(
|
||||
\T_PRIVATE => \T_PRIVATE,
|
||||
\T_PROTECTED => \T_PROTECTED,
|
||||
\T_PUBLIC => \T_PUBLIC,
|
||||
\T_STATIC => \T_STATIC,
|
||||
\T_VAR => \T_VAR,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_VARIABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->isClassProperty($phpcsFile, $stackPtr) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$find = $this->modifierKeywords;
|
||||
$find += array(
|
||||
\T_SEMICOLON => \T_SEMICOLON,
|
||||
\T_OPEN_CURLY_BRACKET => \T_OPEN_CURLY_BRACKET,
|
||||
);
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$modifier = $phpcsFile->findPrevious($find, ($stackPtr - 1));
|
||||
if ($modifier === false
|
||||
|| $tokens[$modifier]['code'] === \T_SEMICOLON
|
||||
|| $tokens[$modifier]['code'] === \T_OPEN_CURLY_BRACKET
|
||||
) {
|
||||
// Parse error. Ignore.
|
||||
return;
|
||||
}
|
||||
|
||||
$type = $phpcsFile->findNext(Tokens::$emptyTokens, ($modifier + 1), null, true);
|
||||
if ($tokens[$type]['code'] === \T_VARIABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Still here ? In that case, this will be a typed property.
|
||||
if ($this->supportsBelow('7.3') === true) {
|
||||
$phpcsFile->addError(
|
||||
'Typed properties are not supported in PHP 7.3 or earlier',
|
||||
$type,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->supportsAbove('7.4') === true) {
|
||||
// Examine the type to verify it's valid.
|
||||
if ($tokens[$type]['type'] === 'T_NULLABLE'
|
||||
// Needed to support PHPCS < 3.5.0 which doesn't correct to the nullable token type yet.
|
||||
|| $tokens[$type]['code'] === \T_INLINE_THEN
|
||||
) {
|
||||
$type = $phpcsFile->findNext(Tokens::$emptyTokens, ($type + 1), null, true);
|
||||
}
|
||||
|
||||
$content = $tokens[$type]['content'];
|
||||
if ($content === 'void' || $content === 'callable') {
|
||||
$phpcsFile->addError(
|
||||
'%s is not supported as a type declaration for properties',
|
||||
$type,
|
||||
'InvalidType',
|
||||
array($content)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$endOfStatement = $phpcsFile->findNext(\T_SEMICOLON, ($stackPtr + 1));
|
||||
if ($endOfStatement !== false) {
|
||||
// Don't throw the same error multiple times for multi-property declarations.
|
||||
return ($endOfStatement + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Using `parent` inside a class without parent is deprecated since PHP 7.4.
|
||||
*
|
||||
* This will throw a compile-time error in the future. Currently an error will only
|
||||
* be generated if/when the parent is accessed at run-time.
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.deprecated.php#migration74.deprecated.core.parent
|
||||
*
|
||||
* @since 9.2.0
|
||||
*/
|
||||
class RemovedOrphanedParentSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Class scopes to check the class declaration.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $classScopeTokens = array(
|
||||
'T_CLASS' => true,
|
||||
'T_ANON_CLASS' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_PARENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.4') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (empty($tokens[$stackPtr]['conditions']) === true) {
|
||||
// Use within the global namespace. Not our concern.
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the class within which this parent keyword is used.
|
||||
*/
|
||||
$conditions = $tokens[$stackPtr]['conditions'];
|
||||
$conditions = array_reverse($conditions, true);
|
||||
$classPtr = false;
|
||||
|
||||
foreach ($conditions as $ptr => $type) {
|
||||
if (isset($this->classScopeTokens[$tokens[$ptr]['type']])) {
|
||||
$classPtr = $ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($classPtr === false) {
|
||||
// Use outside of a class scope. Not our concern.
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($tokens[$classPtr]['scope_opener']) === false) {
|
||||
// No scope opener known. Probably a parse error.
|
||||
return;
|
||||
}
|
||||
|
||||
$extends = $phpcsFile->findNext(\T_EXTENDS, ($classPtr + 1), $tokens[$classPtr]['scope_opener']);
|
||||
if ($extends !== false) {
|
||||
// Class has a parent.
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Using "parent" inside a class without parent is deprecated since PHP 7.4',
|
||||
$stackPtr,
|
||||
'Deprecated'
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+3756
File diff suppressed because it is too large
Load Diff
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Constants;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect usage of the magic `::class` constant introduced in PHP 5.5.
|
||||
*
|
||||
* The special `ClassName::class` constant is available as of PHP 5.5.0, and allows
|
||||
* for fully qualified class name resolution at compile time.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/class_name_scalars
|
||||
* @link https://www.php.net/manual/en/language.oop5.constants.php#example-186
|
||||
*
|
||||
* @since 7.1.4
|
||||
* @since 7.1.5 Removed the incorrect checks against invalid usage of the constant.
|
||||
*/
|
||||
class NewMagicClassConstantSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.4') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (strtolower($tokens[$stackPtr]['content']) !== 'class') {
|
||||
return;
|
||||
}
|
||||
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
|
||||
if ($prevToken === false || $tokens[$prevToken]['code'] !== \T_DOUBLE_COLON) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'The magic class constant ClassName::class was not available in PHP 5.4 or earlier',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+574
@@ -0,0 +1,574 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Constants;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect use of deprecated and/or removed PHP native global constants.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @since 8.1.0
|
||||
*/
|
||||
class RemovedConstantsSniff extends AbstractRemovedFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of removed PHP Constants.
|
||||
*
|
||||
* The array lists : version number with false (deprecated) or true (removed).
|
||||
* If's sufficient to list the first version where the constant was deprecated/removed.
|
||||
*
|
||||
* Optional, the array can contain an `alternative` key listing an alternative constant
|
||||
* to be used instead.
|
||||
*
|
||||
* Note: PHP Constants are case-sensitive!
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @var array(string => array(string => bool|string))
|
||||
*/
|
||||
protected $removedConstants = array(
|
||||
// Disabled since PHP 5.3.0 due to thread safety issues.
|
||||
'FILEINFO_COMPRESS' => array(
|
||||
'5.3' => true,
|
||||
),
|
||||
|
||||
'CURLOPT_CLOSEPOLICY' => array(
|
||||
'5.6' => true,
|
||||
),
|
||||
'CURLCLOSEPOLICY_LEAST_RECENTLY_USED' => array(
|
||||
'5.6' => true,
|
||||
),
|
||||
'CURLCLOSEPOLICY_LEAST_TRAFFIC' => array(
|
||||
'5.6' => true,
|
||||
),
|
||||
'CURLCLOSEPOLICY_SLOWEST' => array(
|
||||
'5.6' => true,
|
||||
),
|
||||
'CURLCLOSEPOLICY_CALLBACK' => array(
|
||||
'5.6' => true,
|
||||
),
|
||||
'CURLCLOSEPOLICY_OLDEST' => array(
|
||||
'5.6' => true,
|
||||
),
|
||||
|
||||
'PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT' => array(
|
||||
'7.0' => true,
|
||||
),
|
||||
'T_CHARACTER' => array(
|
||||
'7.0' => true,
|
||||
),
|
||||
'T_BAD_CHARACTER' => array(
|
||||
'7.0' => true,
|
||||
),
|
||||
|
||||
'INTL_IDNA_VARIANT_2003' => array(
|
||||
'7.2' => false,
|
||||
),
|
||||
|
||||
'MCRYPT_MODE_ECB' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_MODE_CBC' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_MODE_CFB' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_MODE_OFB' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_MODE_NOFB' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_MODE_STREAM' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_ENCRYPT' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_DECRYPT' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_DEV_RANDOM' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_DEV_URANDOM' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_RAND' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_3DES' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_ARCFOUR_IV' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_ARCFOUR' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_BLOWFISH' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_CAST_128' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_CAST_256' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_CRYPT' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_DES' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_DES_COMPAT' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_ENIGMA' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_GOST' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_IDEA' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_LOKI97' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_MARS' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_PANAMA' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_RIJNDAEL_128' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_RIJNDAEL_192' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_RIJNDAEL_256' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_RC2' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_RC4' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_RC6' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_RC6_128' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_RC6_192' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_RC6_256' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_SAFER64' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_SAFER128' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_SAFERPLUS' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_SERPENT' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_SERPENT_128' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_SERPENT_192' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_SERPENT_256' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_SKIPJACK' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_TEAN' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_THREEWAY' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_TRIPLEDES' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_TWOFISH' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_TWOFISH128' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_TWOFISH192' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_TWOFISH256' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_WAKE' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'MCRYPT_XTEA' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
|
||||
'PHPDBG_FILE' => array(
|
||||
'7.3' => true,
|
||||
),
|
||||
'PHPDBG_METHOD' => array(
|
||||
'7.3' => true,
|
||||
),
|
||||
'PHPDBG_LINENO' => array(
|
||||
'7.3' => true,
|
||||
),
|
||||
'PHPDBG_FUNC' => array(
|
||||
'7.3' => true,
|
||||
),
|
||||
'FILTER_FLAG_SCHEME_REQUIRED' => array(
|
||||
'7.3' => false,
|
||||
),
|
||||
'FILTER_FLAG_HOST_REQUIRED' => array(
|
||||
'7.3' => false,
|
||||
),
|
||||
|
||||
'CURLPIPE_HTTP1' => array(
|
||||
'7.4' => false,
|
||||
),
|
||||
'FILTER_SANITIZE_MAGIC_QUOTES' => array(
|
||||
'7.4' => false,
|
||||
'alternative' => 'FILTER_SANITIZE_ADD_SLASHES',
|
||||
),
|
||||
'IBASE_BKP_CONVERT' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_BKP_IGNORE_CHECKSUMS' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_BKP_IGNORE_LIMBO' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_BKP_METADATA_ONLY' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_BKP_NO_GARBAGE_COLLECT' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_BKP_NON_TRANSPORTABLE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_BKP_OLD_DESCRIPTIONS' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_COMMITTED' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_CONCURRENCY' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_CONSISTENCY' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_DEFAULT' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_FETCH_ARRAYS' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_FETCH_BLOBS' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_NOWAIT' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_ACCESS_MODE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_ACTIVATE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_AM_READONLY' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_AM_READWRITE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_DENY_NEW_ATTACHMENTS' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_DENY_NEW_TRANSACTIONS' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_DB_ONLINE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_PAGE_BUFFERS' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_RES' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_RES_USE_FULL' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_RESERVE_SPACE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_SET_SQL_DIALECT' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_SHUTDOWN_DB' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_SWEEP_INTERVAL' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_WM_ASYNC' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_WM_SYNC' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_PRP_WRITE_MODE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_READ' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RES_CREATE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RES_DEACTIVATE_IDX' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RES_NO_SHADOW' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RES_NO_VALIDITY' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RES_ONE_AT_A_TIME' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RES_REPLACE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RES_USE_ALL_SPACE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RPR_CHECK_DB' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RPR_FULL' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RPR_IGNORE_CHECKSUM' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RPR_KILL_SHADOWS' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RPR_MEND_DB' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RPR_SWEEP_DB' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_RPR_VALIDATE_DB' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_STS_DATA_PAGES' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_STS_DB_LOG' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_STS_HDR_PAGES' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_STS_IDX_PAGES' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_STS_SYS_RELATIONS' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_SVC_GET_ENV' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_SVC_GET_ENV_LOCK' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_SVC_GET_ENV_MSG' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_SVC_GET_USERS' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_SVC_IMPLEMENTATION' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_SVC_SERVER_VERSION' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_SVC_SVR_DB_INFO' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_SVC_USER_DBPATH' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_UNIXTIME' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_WAIT' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'IBASE_WRITE' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$constantName = $tokens[$stackPtr]['content'];
|
||||
|
||||
if (isset($this->removedConstants[$constantName]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isUseOfGlobalConstant($phpcsFile, $stackPtr) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $constantName,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->removedConstants[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The constant "%s" is ';
|
||||
}
|
||||
}
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect use of `continue` in `switch` control structures.
|
||||
*
|
||||
* As of PHP 7.3, PHP will throw a warning when `continue` is used to target a `switch`
|
||||
* control structure.
|
||||
* The sniff takes numeric arguments used with `continue` into account.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration73.incompatible.php#migration73.incompatible.core.continue-targeting-switch
|
||||
* @link https://wiki.php.net/rfc/continue_on_switch_deprecation
|
||||
* @link https://github.com/php/php-src/commit/04e3523b7d095341f65ed5e71a3cac82fca690e4
|
||||
* (actual implementation which is different from the RFC).
|
||||
* @link https://www.php.net/manual/en/control-structures.switch.php
|
||||
*
|
||||
* @since 8.2.0
|
||||
*/
|
||||
class DiscouragedSwitchContinueSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Token codes of control structures which can be targeted using continue.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $loopStructures = array(
|
||||
\T_FOR => \T_FOR,
|
||||
\T_FOREACH => \T_FOREACH,
|
||||
\T_WHILE => \T_WHILE,
|
||||
\T_DO => \T_DO,
|
||||
\T_SWITCH => \T_SWITCH,
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokens which start a new case within a switch.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $caseTokens = array(
|
||||
\T_CASE => \T_CASE,
|
||||
\T_DEFAULT => \T_DEFAULT,
|
||||
);
|
||||
|
||||
/**
|
||||
* Token codes which are accepted to determine the level for the continue.
|
||||
*
|
||||
* This array is enriched with the arithmetic operators in the register() method.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $acceptedLevelTokens = array(
|
||||
\T_LNUMBER => \T_LNUMBER,
|
||||
\T_OPEN_PARENTHESIS => \T_OPEN_PARENTHESIS,
|
||||
\T_CLOSE_PARENTHESIS => \T_CLOSE_PARENTHESIS,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->acceptedLevelTokens += Tokens::$arithmeticTokens;
|
||||
$this->acceptedLevelTokens += Tokens::$emptyTokens;
|
||||
|
||||
return array(\T_SWITCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$switchOpener = $tokens[$stackPtr]['scope_opener'];
|
||||
$switchCloser = $tokens[$stackPtr]['scope_closer'];
|
||||
|
||||
// Quick check whether we need to bother with the more complex logic.
|
||||
$hasContinue = $phpcsFile->findNext(\T_CONTINUE, ($switchOpener + 1), $switchCloser);
|
||||
if ($hasContinue === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$caseDefault = $switchOpener;
|
||||
|
||||
do {
|
||||
$caseDefault = $phpcsFile->findNext($this->caseTokens, ($caseDefault + 1), $switchCloser);
|
||||
if ($caseDefault === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($tokens[$caseDefault]['scope_opener']) === false) {
|
||||
// Unknown start of the case, skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
$caseOpener = $tokens[$caseDefault]['scope_opener'];
|
||||
$nextCaseDefault = $phpcsFile->findNext($this->caseTokens, ($caseDefault + 1), $switchCloser);
|
||||
if ($nextCaseDefault === false) {
|
||||
$caseCloser = $switchCloser;
|
||||
} else {
|
||||
$caseCloser = $nextCaseDefault;
|
||||
}
|
||||
|
||||
// Check for unscoped control structures within the case.
|
||||
$controlStructure = $caseOpener;
|
||||
$doCount = 0;
|
||||
while (($controlStructure = $phpcsFile->findNext($this->loopStructures, ($controlStructure + 1), $caseCloser)) !== false) {
|
||||
if ($tokens[$controlStructure]['code'] === \T_DO) {
|
||||
$doCount++;
|
||||
}
|
||||
|
||||
if (isset($tokens[$controlStructure]['scope_opener'], $tokens[$controlStructure]['scope_closer']) === false) {
|
||||
if ($tokens[$controlStructure]['code'] === \T_WHILE && $doCount > 0) {
|
||||
// While in a do-while construct.
|
||||
$doCount--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Control structure without braces found within the case, ignore this case.
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Examine the contents of the case.
|
||||
$continue = $caseOpener;
|
||||
|
||||
do {
|
||||
$continue = $phpcsFile->findNext(\T_CONTINUE, ($continue + 1), $caseCloser);
|
||||
if ($continue === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
$nextSemicolon = $phpcsFile->findNext(array(\T_SEMICOLON, \T_CLOSE_TAG), ($continue + 1), $caseCloser);
|
||||
$codeString = '';
|
||||
for ($i = ($continue + 1); $i < $nextSemicolon; $i++) {
|
||||
if (isset($this->acceptedLevelTokens[$tokens[$i]['code']]) === false) {
|
||||
// Function call/variable or other token which make numeric level impossible to determine.
|
||||
continue 2;
|
||||
}
|
||||
|
||||
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$codeString .= $tokens[$i]['content'];
|
||||
}
|
||||
|
||||
$level = null;
|
||||
if ($codeString !== '') {
|
||||
if (is_numeric($codeString)) {
|
||||
$level = (int) $codeString;
|
||||
} else {
|
||||
// With the above logic, the string can only contain digits and operators, eval!
|
||||
$level = eval("return ( $codeString );");
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($level) === false || $level === 0) {
|
||||
$level = 1;
|
||||
}
|
||||
|
||||
// Examine which control structure is being targeted by the continue statement.
|
||||
if (isset($tokens[$continue]['conditions']) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$conditions = array_reverse($tokens[$continue]['conditions'], true);
|
||||
// PHPCS adds more structures to the conditions array than we want to take into
|
||||
// consideration, so clean up the array.
|
||||
foreach ($conditions as $tokenPtr => $tokenCode) {
|
||||
if (isset($this->loopStructures[$tokenCode]) === false) {
|
||||
unset($conditions[$tokenPtr]);
|
||||
}
|
||||
}
|
||||
|
||||
$targetCondition = \array_slice($conditions, ($level - 1), 1, true);
|
||||
if (empty($targetCondition)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$conditionToken = key($targetCondition);
|
||||
if ($conditionToken === $stackPtr) {
|
||||
$phpcsFile->addWarning(
|
||||
"Targeting a 'switch' control structure with a 'continue' statement is strongly discouraged and will throw a warning as of PHP 7.3.",
|
||||
$continue,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
|
||||
} while ($continue < $caseCloser);
|
||||
|
||||
} while ($caseDefault < $switchCloser);
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect using `break` and/or `continue` statements outside of a looping structure.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.other.break-continue
|
||||
* @link https://www.php.net/manual/en/control-structures.break.php
|
||||
* @link https://www.php.net/manual/en/control-structures.continue.php
|
||||
*
|
||||
* @since 7.0.7
|
||||
*/
|
||||
class ForbiddenBreakContinueOutsideLoopSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Token codes of control structure in which usage of break/continue is valid.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $validLoopStructures = array(
|
||||
\T_FOR => true,
|
||||
\T_FOREACH => true,
|
||||
\T_WHILE => true,
|
||||
\T_DO => true,
|
||||
\T_SWITCH => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Token codes which did not correctly get a condition assigned in older PHPCS versions.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $backCompat = array(
|
||||
\T_CASE => true,
|
||||
\T_DEFAULT => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_BREAK,
|
||||
\T_CONTINUE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$token = $tokens[$stackPtr];
|
||||
|
||||
// Check if the break/continue is within a valid loop structure.
|
||||
if (empty($token['conditions']) === false) {
|
||||
foreach ($token['conditions'] as $tokenCode) {
|
||||
if (isset($this->validLoopStructures[$tokenCode]) === true) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Deal with older PHPCS versions.
|
||||
if (isset($token['scope_condition']) === true && isset($this->backCompat[$tokens[$token['scope_condition']]['code']]) === true) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're still here, no valid loop structure container has been found, so throw an error.
|
||||
$error = "Using '%s' outside of a loop or switch structure is invalid";
|
||||
$isError = false;
|
||||
$errorCode = 'Found';
|
||||
$data = array($token['content']);
|
||||
|
||||
if ($this->supportsAbove('7.0')) {
|
||||
$error .= ' and will throw a fatal error since PHP 7.0';
|
||||
$isError = true;
|
||||
$errorCode = 'FatalError';
|
||||
}
|
||||
|
||||
$this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detects using 0 and variable numeric arguments on `break` and `continue` statements.
|
||||
*
|
||||
* This sniff checks for:
|
||||
* - Using `break` and/or `continue` with a variable as the numeric argument.
|
||||
* - Using `break` and/or `continue` with a zero - 0 - as the numeric argument.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration54.incompatible.php
|
||||
* @link https://www.php.net/manual/en/control-structures.break.php
|
||||
* @link https://www.php.net/manual/en/control-structures.continue.php
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 5.6 Now extends the base `Sniff` class.
|
||||
*/
|
||||
class ForbiddenBreakContinueVariableArgumentsSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Error types this sniff handles for forbidden break/continue arguments.
|
||||
*
|
||||
* Array key is the error code. Array value will be used as part of the error message.
|
||||
*
|
||||
* @since 7.0.5
|
||||
* @since 7.1.0 Changed from class constants to property.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $errorTypes = array(
|
||||
'variableArgument' => 'a variable argument',
|
||||
'zeroArgument' => '0 as an argument',
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_BREAK, \T_CONTINUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('5.4') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$nextSemicolonToken = $phpcsFile->findNext(array(\T_SEMICOLON, \T_CLOSE_TAG), ($stackPtr), null, false);
|
||||
$errorType = '';
|
||||
for ($curToken = $stackPtr + 1; $curToken < $nextSemicolonToken; $curToken++) {
|
||||
if ($tokens[$curToken]['type'] === 'T_STRING') {
|
||||
// If the next non-whitespace token after the string
|
||||
// is an opening parenthesis then it's a function call.
|
||||
$openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, $curToken + 1, null, true);
|
||||
if ($tokens[$openBracket]['code'] === \T_OPEN_PARENTHESIS) {
|
||||
$errorType = 'variableArgument';
|
||||
break;
|
||||
}
|
||||
|
||||
} elseif (\in_array($tokens[$curToken]['type'], array('T_VARIABLE', 'T_FUNCTION', 'T_CLOSURE'), true)) {
|
||||
$errorType = 'variableArgument';
|
||||
break;
|
||||
|
||||
} elseif ($tokens[$curToken]['type'] === 'T_LNUMBER' && $tokens[$curToken]['content'] === '0') {
|
||||
$errorType = 'zeroArgument';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($errorType !== '') {
|
||||
$error = 'Using %s on break or continue is forbidden since PHP 5.4';
|
||||
$errorCode = $errorType . 'Found';
|
||||
$data = array($this->errorTypes[$errorType]);
|
||||
|
||||
$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Switch statements can not have multiple default blocks since PHP 7.0.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/switch.default.multiple
|
||||
* @link https://www.php.net/manual/en/control-structures.switch.php
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
class ForbiddenSwitchWithMultipleDefaultBlocksSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_SWITCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
if (isset($tokens[$stackPtr]['scope_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaultToken = $stackPtr;
|
||||
$defaultCount = 0;
|
||||
$targetLevel = $tokens[$stackPtr]['level'] + 1;
|
||||
while ($defaultCount < 2 && ($defaultToken = $phpcsFile->findNext(array(\T_DEFAULT), $defaultToken + 1, $tokens[$stackPtr]['scope_closer'])) !== false) {
|
||||
// Same level or one below (= two default cases after each other).
|
||||
if ($tokens[$defaultToken]['level'] === $targetLevel || $tokens[$defaultToken]['level'] === ($targetLevel + 1)) {
|
||||
$defaultCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($defaultCount > 1) {
|
||||
$phpcsFile->addError(
|
||||
'Switch statements can not have multiple default blocks since PHP 7.0',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+378
@@ -0,0 +1,378 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Check for valid execution directives set with `declare()`.
|
||||
*
|
||||
* The sniff contains three distinct checks:
|
||||
* - Check if the execution directive used is valid. PHP currently only supports
|
||||
* three execution directives.
|
||||
* - Check if the execution directive used is available in the PHP versions
|
||||
* for which support is being checked.
|
||||
* In the case of the `encoding` directive on PHP 5.3, support is conditional
|
||||
* on the `--enable-zend-multibyte` compilation option. This will be indicated as such.
|
||||
* - Check whether the value for the directive is valid.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://www.php.net/manual/en/control-structures.declare.php
|
||||
* @link https://wiki.php.net/rfc/scalar_type_hints_v5#strict_types_declare_directive
|
||||
*
|
||||
* @since 7.0.3
|
||||
* @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.
|
||||
*/
|
||||
class NewExecutionDirectivesSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of new execution directives
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If the execution order is conditional, add the condition as a string to the version nr.
|
||||
* If's sufficient to list the first version where the execution directive appears.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var array(string => array(string => bool|string|array))
|
||||
*/
|
||||
protected $newDirectives = array(
|
||||
'ticks' => array(
|
||||
'3.1' => false,
|
||||
'4.0' => true,
|
||||
'valid_value_callback' => 'isNumeric',
|
||||
),
|
||||
'encoding' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => '--enable-zend-multibyte', // Directive ignored unless.
|
||||
'5.4' => true,
|
||||
'valid_value_callback' => 'validEncoding',
|
||||
),
|
||||
'strict_types' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'valid_values' => array(1),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Tokens to ignore when trying to find the value for the directive.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $ignoreTokens = array();
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->ignoreTokens = Tokens::$emptyTokens;
|
||||
$this->ignoreTokens[\T_EQUAL] = \T_EQUAL;
|
||||
|
||||
return array(\T_DECLARE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === true) {
|
||||
$openParenthesis = $tokens[$stackPtr]['parenthesis_opener'];
|
||||
$closeParenthesis = $tokens[$stackPtr]['parenthesis_closer'];
|
||||
} else {
|
||||
if (version_compare(PHPCSHelper::getVersion(), '2.3.4', '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Deal with PHPCS 2.3.0-2.3.3 which do not yet set the parenthesis properly for declare statements.
|
||||
$openParenthesis = $phpcsFile->findNext(\T_OPEN_PARENTHESIS, ($stackPtr + 1), null, false, null, true);
|
||||
if ($openParenthesis === false || isset($tokens[$openParenthesis]['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
$closeParenthesis = $tokens[$openParenthesis]['parenthesis_closer'];
|
||||
}
|
||||
|
||||
$directivePtr = $phpcsFile->findNext(\T_STRING, ($openParenthesis + 1), $closeParenthesis, false);
|
||||
if ($directivePtr === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$directiveContent = $tokens[$directivePtr]['content'];
|
||||
|
||||
if (isset($this->newDirectives[$directiveContent]) === false) {
|
||||
$error = 'Declare can only be used with the directives %s. Found: %s';
|
||||
$data = array(
|
||||
implode(', ', array_keys($this->newDirectives)),
|
||||
$directiveContent,
|
||||
);
|
||||
|
||||
$phpcsFile->addError($error, $stackPtr, 'InvalidDirectiveFound', $data);
|
||||
|
||||
} else {
|
||||
// Check for valid directive for version.
|
||||
$itemInfo = array(
|
||||
'name' => $directiveContent,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
|
||||
// Check for valid directive value.
|
||||
$valuePtr = $phpcsFile->findNext($this->ignoreTokens, $directivePtr + 1, $closeParenthesis, true);
|
||||
if ($valuePtr === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addWarningOnInvalidValue($phpcsFile, $valuePtr, $directiveContent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether an error/warning should be thrown for an item based on collected information.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $errorInfo Detail information about an item.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldThrowError(array $errorInfo)
|
||||
{
|
||||
return ($errorInfo['not_in_version'] !== '' || $errorInfo['conditional_version'] !== '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newDirectives[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of the non-PHP-version array keys used in a sub-array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array(
|
||||
'valid_value_callback',
|
||||
'valid_values',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemArray Version and other information about the item.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorInfo(array $itemArray, array $itemInfo)
|
||||
{
|
||||
$errorInfo = parent::getErrorInfo($itemArray, $itemInfo);
|
||||
$errorInfo['conditional_version'] = '';
|
||||
$errorInfo['condition'] = '';
|
||||
|
||||
$versionArray = $this->getVersionArray($itemArray);
|
||||
|
||||
if (empty($versionArray) === false) {
|
||||
foreach ($versionArray as $version => $present) {
|
||||
if (\is_string($present) === true && $this->supportsBelow($version) === true) {
|
||||
// We cannot test for compilation option (ok, except by scraping the output of phpinfo...).
|
||||
$errorInfo['conditional_version'] = $version;
|
||||
$errorInfo['condition'] = $present;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errorInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'Directive ' . parent::getErrorMsgTemplate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the error or warning for this item.
|
||||
*
|
||||
* @since 7.0.3
|
||||
* @since 7.1.0 This method now overloads the method from the `AbstractNewFeatureSniff` class.
|
||||
* - Renamed from `maybeAddError()` to `addError()`.
|
||||
* - Changed visibility from `protected` to `public`.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the relevant token in
|
||||
* the stack.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
* @param array $errorInfo Array with detail (version) information
|
||||
* relevant to the item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addError(File $phpcsFile, $stackPtr, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
if ($errorInfo['not_in_version'] !== '') {
|
||||
parent::addError($phpcsFile, $stackPtr, $itemInfo, $errorInfo);
|
||||
} elseif ($errorInfo['conditional_version'] !== '') {
|
||||
$error = 'Directive %s is present in PHP version %s but will be disregarded unless PHP is compiled with %s';
|
||||
$errorCode = $this->stringToErrorCode($itemInfo['name']) . 'WithConditionFound';
|
||||
$data = array(
|
||||
$itemInfo['name'],
|
||||
$errorInfo['conditional_version'],
|
||||
$errorInfo['condition'],
|
||||
);
|
||||
|
||||
$phpcsFile->addWarning($error, $stackPtr, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a error or warning for this sniff.
|
||||
*
|
||||
* @since 7.0.3
|
||||
* @since 7.0.6 Renamed from `addErrorOnInvalidValue()` to `addWarningOnInvalidValue()`.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the execution directive value
|
||||
* in the token array.
|
||||
* @param string $directive The directive.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addWarningOnInvalidValue(File $phpcsFile, $stackPtr, $directive)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$value = $tokens[$stackPtr]['content'];
|
||||
if (isset(Tokens::$stringTokens[$tokens[$stackPtr]['code']]) === true) {
|
||||
$value = $this->stripQuotes($value);
|
||||
}
|
||||
|
||||
$isError = false;
|
||||
if (isset($this->newDirectives[$directive]['valid_values'])) {
|
||||
if (\in_array($value, $this->newDirectives[$directive]['valid_values']) === false) {
|
||||
$isError = true;
|
||||
}
|
||||
} elseif (isset($this->newDirectives[$directive]['valid_value_callback'])) {
|
||||
$valid = \call_user_func(array($this, $this->newDirectives[$directive]['valid_value_callback']), $value);
|
||||
if ($valid === false) {
|
||||
$isError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isError === true) {
|
||||
$error = 'The execution directive %s does not seem to have a valid value. Please review. Found: %s';
|
||||
$errorCode = $this->stringToErrorCode($directive) . 'InvalidValueFound';
|
||||
$data = array(
|
||||
$directive,
|
||||
$value,
|
||||
);
|
||||
|
||||
$phpcsFile->addWarning($error, $stackPtr, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether a value is numeric.
|
||||
*
|
||||
* Callback function to test whether the value for an execution directive is valid.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param mixed $value The value to test.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isNumeric($value)
|
||||
{
|
||||
return is_numeric($value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether a value is a valid encoding.
|
||||
*
|
||||
* Callback function to test whether the value for an execution directive is valid.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param mixed $value The value to test.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validEncoding($value)
|
||||
{
|
||||
static $encodings;
|
||||
if (isset($encodings) === false && function_exists('mb_list_encodings')) {
|
||||
$encodings = mb_list_encodings();
|
||||
}
|
||||
|
||||
if (empty($encodings) || \is_array($encodings) === false) {
|
||||
// If we can't test the encoding, let it pass through.
|
||||
return true;
|
||||
}
|
||||
|
||||
return \in_array($value, $encodings, true);
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect `foreach` expression referencing.
|
||||
*
|
||||
* Before PHP 5.5.0, referencing `$value` in a `foreach` was only possible
|
||||
* if the iterated array could be referenced (i.e. if it is a variable).
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @link https://www.php.net/manual/en/control-structures.foreach.php
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class NewForeachExpressionReferencingSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_FOREACH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.4') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$opener = $tokens[$stackPtr]['parenthesis_opener'];
|
||||
$closer = $tokens[$stackPtr]['parenthesis_closer'];
|
||||
|
||||
$asToken = $phpcsFile->findNext(\T_AS, ($opener + 1), $closer);
|
||||
if ($asToken === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: referencing $key is not allowed in any version, so this should only find referenced $values.
|
||||
* If it does find a referenced key, it would be a parse error anyway.
|
||||
*/
|
||||
$hasReference = $phpcsFile->findNext(\T_BITWISE_AND, ($asToken + 1), $closer);
|
||||
if ($hasReference === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nestingLevel = 0;
|
||||
if ($asToken !== ($opener + 1) && isset($tokens[$opener + 1]['nested_parenthesis'])) {
|
||||
$nestingLevel = \count($tokens[$opener + 1]['nested_parenthesis']);
|
||||
}
|
||||
|
||||
if ($this->isVariable($phpcsFile, ($opener + 1), $asToken, $nestingLevel) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-variable detected before the `as` keyword.
|
||||
$phpcsFile->addError(
|
||||
'Referencing $value is only possible if the iterated array is a variable in PHP 5.4 or earlier.',
|
||||
$hasReference,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect unpacking nested arrays with `list()` in a `foreach()` as available since PHP 5.5.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration55.new-features.php#migration55.new-features.foreach-list
|
||||
* @link https://wiki.php.net/rfc/foreachlist
|
||||
* @link https://www.php.net/manual/en/control-structures.foreach.php#control-structures.foreach.list
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class NewListInForeachSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_FOREACH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.4') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$opener = $tokens[$stackPtr]['parenthesis_opener'];
|
||||
$closer = $tokens[$stackPtr]['parenthesis_closer'];
|
||||
|
||||
$asToken = $phpcsFile->findNext(\T_AS, ($opener + 1), $closer);
|
||||
if ($asToken === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hasList = $phpcsFile->findNext(array(\T_LIST, \T_OPEN_SHORT_ARRAY), ($asToken + 1), $closer);
|
||||
if ($hasList === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Unpacking nested arrays with list() in a foreach is not supported in PHP 5.4 or earlier.',
|
||||
$hasList,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Catching multiple exception types in one statement is available since PHP 7.1.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration71.new-features.php#migration71.new-features.mulit-catch-exception-handling
|
||||
* @link https://wiki.php.net/rfc/multiple-catch
|
||||
* @link https://www.php.net/manual/en/language.exceptions.php#language.exceptions.catch
|
||||
*
|
||||
* @since 7.0.7
|
||||
*/
|
||||
class NewMultiCatchSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_CATCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$token = $tokens[$stackPtr];
|
||||
|
||||
// Bow out during live coding.
|
||||
if (isset($token['parenthesis_opener'], $token['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hasBitwiseOr = $phpcsFile->findNext(\T_BITWISE_OR, $token['parenthesis_opener'], $token['parenthesis_closer']);
|
||||
|
||||
if ($hasBitwiseOr === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Catching multiple exceptions within one statement is not supported in PHP 7.0 or earlier.',
|
||||
$hasBitwiseOr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+346
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Extensions;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect the use of deprecated and/or removed PHP extensions.
|
||||
*
|
||||
* This sniff examines function calls made and flags function calls to functions
|
||||
* prefixed with the dedicated prefix from a deprecated/removed native PHP extension.
|
||||
*
|
||||
* Suggests alternative extensions if available.
|
||||
*
|
||||
* As userland functions may be prefixed with a prefix also used by a native
|
||||
* PHP extension, the sniff offers the ability to whitelist specific functions
|
||||
* from being flagged by this sniff via a property in a custom ruleset
|
||||
* (since PHPCompatibility 7.0.2).
|
||||
*
|
||||
* {@internal This sniff is a candidate for removal once all functions from all
|
||||
* deprecated/removed extensions have been added to the RemovedFunctions sniff.}
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 7.1.0 Now extends the `AbstractRemovedFeatureSniff` instead of the base `Sniff` class.
|
||||
*/
|
||||
class RemovedExtensionsSniff extends AbstractRemovedFeatureSniff
|
||||
{
|
||||
/**
|
||||
* A list of functions to whitelist, if any.
|
||||
*
|
||||
* This is intended for projects using functions which start with the same
|
||||
* prefix as one of the removed extensions.
|
||||
*
|
||||
* This property can be set from the ruleset, like so:
|
||||
* <rule ref="PHPCompatibility.Extensions.RemovedExtensions">
|
||||
* <properties>
|
||||
* <property name="functionWhitelist" type="array" value="mysql_to_rfc3339,mysql_another_function" />
|
||||
* </properties>
|
||||
* </rule>
|
||||
*
|
||||
* @since 7.0.2
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $functionWhitelist;
|
||||
|
||||
/**
|
||||
* A list of removed extensions with their alternative, if any.
|
||||
*
|
||||
* The array lists : version number with false (deprecated) and true (removed).
|
||||
* If's sufficient to list the first version where the extension was deprecated/removed.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @var array(string => array(string => bool|string|null))
|
||||
*/
|
||||
protected $removedExtensions = array(
|
||||
'activescript' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => 'pecl/activescript',
|
||||
),
|
||||
'cpdf' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => 'pecl/pdflib',
|
||||
),
|
||||
'dbase' => array(
|
||||
'5.3' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'dbx' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => 'pecl/dbx',
|
||||
),
|
||||
'dio' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => 'pecl/dio',
|
||||
),
|
||||
'ereg' => array(
|
||||
'5.3' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'pcre',
|
||||
),
|
||||
'fam' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'fbsql' => array(
|
||||
'5.3' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'fdf' => array(
|
||||
'5.3' => true,
|
||||
'alternative' => 'pecl/fdf',
|
||||
),
|
||||
'filepro' => array(
|
||||
'5.2' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'hw_api' => array(
|
||||
'5.2' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'ibase' => array(
|
||||
'7.4' => true,
|
||||
'alternative' => 'pecl/ibase',
|
||||
),
|
||||
'ingres' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => 'pecl/ingres',
|
||||
),
|
||||
'ircg' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'mcrypt' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'openssl (preferred) or pecl/mcrypt once available',
|
||||
),
|
||||
'mcve' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => 'pecl/mcve',
|
||||
),
|
||||
'ming' => array(
|
||||
'5.3' => true,
|
||||
'alternative' => 'pecl/ming',
|
||||
),
|
||||
'mnogosearch' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'msql' => array(
|
||||
'5.3' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'mssql' => array(
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'mysql_' => array(
|
||||
'5.5' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'mysqli',
|
||||
),
|
||||
'ncurses' => array(
|
||||
'5.3' => true,
|
||||
'alternative' => 'pecl/ncurses',
|
||||
),
|
||||
'oracle' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => 'oci8 or pdo_oci',
|
||||
),
|
||||
'ovrimos' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'pfpro_' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'recode' => array(
|
||||
'7.4' => true,
|
||||
'alternative' => 'iconv or mbstring',
|
||||
),
|
||||
'sqlite' => array(
|
||||
'5.4' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
// Has to be before `sybase` as otherwise it will never match.
|
||||
'sybase_ct' => array(
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'sybase' => array(
|
||||
'5.3' => true,
|
||||
'alternative' => 'sybase_ct',
|
||||
),
|
||||
'w32api' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => 'pecl/ffi',
|
||||
),
|
||||
'wddx' => array(
|
||||
'7.4' => true,
|
||||
'alternative' => 'pecl/wddx',
|
||||
),
|
||||
'yp' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Handle case-insensitivity of function names.
|
||||
$this->removedExtensions = $this->arrayKeysToLowercase($this->removedExtensions);
|
||||
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
// Find the next non-empty token.
|
||||
$openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
|
||||
if ($tokens[$openBracket]['code'] !== \T_OPEN_PARENTHESIS) {
|
||||
// Not a function call.
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($tokens[$openBracket]['parenthesis_closer']) === false) {
|
||||
// Not a function call.
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the previous non-empty token.
|
||||
$search = Tokens::$emptyTokens;
|
||||
$search[] = \T_BITWISE_AND;
|
||||
$previous = $phpcsFile->findPrevious($search, ($stackPtr - 1), null, true);
|
||||
if ($tokens[$previous]['code'] === \T_FUNCTION) {
|
||||
// It's a function definition, not a function call.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$previous]['code'] === \T_NEW) {
|
||||
// We are creating an object, not calling a function.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$previous]['code'] === \T_OBJECT_OPERATOR) {
|
||||
// We are calling a method of an object.
|
||||
return;
|
||||
}
|
||||
|
||||
$function = $tokens[$stackPtr]['content'];
|
||||
$functionLc = strtolower($function);
|
||||
|
||||
if ($this->isWhiteListed($functionLc) === true) {
|
||||
// Function is whitelisted.
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->removedExtensions as $extension => $versionList) {
|
||||
if (strpos($functionLc, $extension) === 0) {
|
||||
$itemInfo = array(
|
||||
'name' => $extension,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is the current function being checked whitelisted ?
|
||||
*
|
||||
* Parsing the list late as it may be provided as a property, but also inline.
|
||||
*
|
||||
* @since 7.0.2
|
||||
*
|
||||
* @param string $content Content of the current token.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isWhiteListed($content)
|
||||
{
|
||||
if (isset($this->functionWhitelist) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (\is_string($this->functionWhitelist) === true) {
|
||||
if (strpos($this->functionWhitelist, ',') !== false) {
|
||||
$this->functionWhitelist = explode(',', $this->functionWhitelist);
|
||||
} else {
|
||||
$this->functionWhitelist = (array) $this->functionWhitelist;
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_array($this->functionWhitelist) === true) {
|
||||
$this->functionWhitelist = array_map('strtolower', $this->functionWhitelist);
|
||||
return \in_array($content, $this->functionWhitelist, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->removedExtensions[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return "Extension '%s' is ";
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect the use of superglobals as parameters for functions, support for which was removed in PHP 5.4.
|
||||
*
|
||||
* {@internal List of superglobals is maintained in the parent class.}
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration54.incompatible.php
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
class ForbiddenParameterShadowSuperGlobalsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Register the tokens to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.1.3 Allows for closures.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_FUNCTION,
|
||||
\T_CLOSURE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the test.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('5.4') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all parameters from function signature.
|
||||
$parameters = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr);
|
||||
if (empty($parameters) || \is_array($parameters) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($parameters as $param) {
|
||||
if (isset($this->superglobals[$param['name']]) === true) {
|
||||
$error = 'Parameter shadowing super global (%s) causes fatal error since PHP 5.4';
|
||||
$errorCode = $this->stringToErrorCode(substr($param['name'], 1)) . 'Found';
|
||||
$data = array($param['name']);
|
||||
|
||||
$phpcsFile->addError($error, $param['token'], $errorCode, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Functions can not have multiple parameters with the same name since PHP 7.0.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.other.func-parameters
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
class ForbiddenParametersWithSameNameSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.1.3 Allows for closures.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_FUNCTION,
|
||||
\T_CLOSURE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$token = $tokens[$stackPtr];
|
||||
// Skip function without body.
|
||||
if (isset($token['scope_opener']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all parameters from method signature.
|
||||
$parameters = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr);
|
||||
if (empty($parameters) || \is_array($parameters) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paramNames = array();
|
||||
foreach ($parameters as $param) {
|
||||
$paramNames[] = $param['name'];
|
||||
}
|
||||
|
||||
if (\count($paramNames) !== \count(array_unique($paramNames))) {
|
||||
$phpcsFile->addError(
|
||||
'Functions can not have multiple parameters with the same name since PHP 7.0',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* As of PHP 5.3, the __toString() magic method can no longer accept arguments.
|
||||
*
|
||||
* Sister-sniff to `PHPCompatibility.MethodUse.ForbiddenToStringParameters`.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration53.incompatible.php
|
||||
* @link https://www.php.net/manual/en/language.oop5.magic.php#object.tostring
|
||||
*
|
||||
* @since 9.2.0
|
||||
*/
|
||||
class ForbiddenToStringParametersSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Valid scopes for the __toString() method to live in.
|
||||
*
|
||||
* @since 9.2.0
|
||||
* @since 9.3.2 Visibility changed from `public` to `protected`.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $ooScopeTokens = array(
|
||||
'T_CLASS' => true,
|
||||
'T_INTERFACE' => true,
|
||||
'T_TRAIT' => true,
|
||||
'T_ANON_CLASS' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_FUNCTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('5.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$functionName = $phpcsFile->getDeclarationName($stackPtr);
|
||||
if (strtolower($functionName) !== '__tostring') {
|
||||
// Not the right function.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) === false) {
|
||||
// Function, not method.
|
||||
return;
|
||||
}
|
||||
|
||||
$params = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr);
|
||||
if (empty($params)) {
|
||||
// Function declared without parameters.
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'The __toString() magic method can no longer accept arguments since PHP 5.3',
|
||||
$stackPtr,
|
||||
'Declared'
|
||||
);
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect variable names forbidden to be used in closure `use` statements.
|
||||
*
|
||||
* Variables bound to a closure via the `use` construct cannot use the same name
|
||||
* as any superglobals, `$this`, or any parameter since PHP 7.1.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration71.incompatible.php#migration71.incompatible.lexical-names
|
||||
* @link https://www.php.net/manual/en/functions.anonymous.php
|
||||
*
|
||||
* @since 7.1.4
|
||||
*/
|
||||
class ForbiddenVariableNamesInClosureUseSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_USE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.1') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
// Verify this use statement is used with a closure - if so, it has to have parenthesis before it.
|
||||
$previousNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
|
||||
if ($previousNonEmpty === false || $tokens[$previousNonEmpty]['code'] !== \T_CLOSE_PARENTHESIS
|
||||
|| isset($tokens[$previousNonEmpty]['parenthesis_opener']) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ... and (a variable within) parenthesis after it.
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
|
||||
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) {
|
||||
// Live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
$closurePtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($tokens[$previousNonEmpty]['parenthesis_opener'] - 1), null, true);
|
||||
if ($closurePtr === false || $tokens[$closurePtr]['code'] !== \T_CLOSURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the parameters declared by the closure.
|
||||
$closureParams = PHPCSHelper::getMethodParameters($phpcsFile, $closurePtr);
|
||||
|
||||
$errorMsg = 'Variables bound to a closure via the use construct cannot use the same name as superglobals, $this, or a declared parameter since PHP 7.1. Found: %s';
|
||||
|
||||
for ($i = ($nextNonEmpty + 1); $i < $tokens[$nextNonEmpty]['parenthesis_closer']; $i++) {
|
||||
if ($tokens[$i]['code'] !== \T_VARIABLE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$variableName = $tokens[$i]['content'];
|
||||
|
||||
if ($variableName === '$this') {
|
||||
$phpcsFile->addError($errorMsg, $i, 'FoundThis', array($variableName));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($this->superglobals[$variableName]) === true) {
|
||||
$phpcsFile->addError($errorMsg, $i, 'FoundSuperglobal', array($variableName));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check whether it is one of the parameters declared by the closure.
|
||||
if (empty($closureParams) === false) {
|
||||
foreach ($closureParams as $param) {
|
||||
if ($param['name'] === $variableName) {
|
||||
$phpcsFile->addError($errorMsg, $i, 'FoundShadowParam', array($variableName));
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect closures and verify that the features used are supported.
|
||||
*
|
||||
* Version based checks:
|
||||
* - Closures are available since PHP 5.3.
|
||||
* - Closures can be declared as `static` since PHP 5.4.
|
||||
* - Closures can use the `$this` variable within a class context since PHP 5.4.
|
||||
* - Closures can use `self`/`parent`/`static` since PHP 5.4.
|
||||
*
|
||||
* Version independent checks:
|
||||
* - Static closures don't have access to the `$this` variable.
|
||||
* - Closures declared outside of a class context don't have access to the `$this`
|
||||
* variable unless bound to an object.
|
||||
*
|
||||
* PHP version 5.3
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/functions.anonymous.php
|
||||
* @link https://wiki.php.net/rfc/closures
|
||||
* @link https://wiki.php.net/rfc/closures/object-extension
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
class NewClosureSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_CLOSURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.1.4 - Added check for closure being declared as static < 5.4.
|
||||
* - Added check for use of `$this` variable in class context < 5.4.
|
||||
* - Added check for use of `$this` variable in static closures (unsupported).
|
||||
* - Added check for use of `$this` variable outside class context (unsupported).
|
||||
* @since 8.2.0 Added check for use of `self`/`static`/`parent` < 5.4.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.2')) {
|
||||
$phpcsFile->addError(
|
||||
'Closures / anonymous functions are not available in PHP 5.2 or earlier',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Closures can only be declared as static since PHP 5.4.
|
||||
*/
|
||||
$isStatic = $this->isClosureStatic($phpcsFile, $stackPtr);
|
||||
if ($this->supportsBelow('5.3') && $isStatic === true) {
|
||||
$phpcsFile->addError(
|
||||
'Closures / anonymous functions could not be declared as static in PHP 5.3 or earlier',
|
||||
$stackPtr,
|
||||
'StaticFound'
|
||||
);
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
|
||||
// Live coding or parse error.
|
||||
return;
|
||||
}
|
||||
|
||||
$scopeStart = ($tokens[$stackPtr]['scope_opener'] + 1);
|
||||
$scopeEnd = $tokens[$stackPtr]['scope_closer'];
|
||||
$usesThis = $this->findThisUsageInClosure($phpcsFile, $scopeStart, $scopeEnd);
|
||||
|
||||
if ($this->supportsBelow('5.3')) {
|
||||
/*
|
||||
* Closures declared within classes only have access to $this since PHP 5.4.
|
||||
*/
|
||||
if ($usesThis !== false) {
|
||||
$thisFound = $usesThis;
|
||||
do {
|
||||
$phpcsFile->addError(
|
||||
'Closures / anonymous functions did not have access to $this in PHP 5.3 or earlier',
|
||||
$thisFound,
|
||||
'ThisFound'
|
||||
);
|
||||
|
||||
$thisFound = $this->findThisUsageInClosure($phpcsFile, ($thisFound + 1), $scopeEnd);
|
||||
|
||||
} while ($thisFound !== false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Closures declared within classes only have access to self/parent/static since PHP 5.4.
|
||||
*/
|
||||
$usesClassRef = $this->findClassRefUsageInClosure($phpcsFile, $scopeStart, $scopeEnd);
|
||||
|
||||
if ($usesClassRef !== false) {
|
||||
do {
|
||||
$phpcsFile->addError(
|
||||
'Closures / anonymous functions could not use "%s::" in PHP 5.3 or earlier',
|
||||
$usesClassRef,
|
||||
'ClassRefFound',
|
||||
array(strtolower($tokens[$usesClassRef]['content']))
|
||||
);
|
||||
|
||||
$usesClassRef = $this->findClassRefUsageInClosure($phpcsFile, ($usesClassRef + 1), $scopeEnd);
|
||||
|
||||
} while ($usesClassRef !== false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for correct usage.
|
||||
*/
|
||||
if ($this->supportsAbove('5.4') && $usesThis !== false) {
|
||||
|
||||
$thisFound = $usesThis;
|
||||
|
||||
do {
|
||||
/*
|
||||
* Closures only have access to $this if not declared as static.
|
||||
*/
|
||||
if ($isStatic === true) {
|
||||
$phpcsFile->addError(
|
||||
'Closures / anonymous functions declared as static do not have access to $this',
|
||||
$thisFound,
|
||||
'ThisFoundInStatic'
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Closures only have access to $this if used within a class context.
|
||||
*/
|
||||
elseif ($this->inClassScope($phpcsFile, $stackPtr, false) === false) {
|
||||
$phpcsFile->addWarning(
|
||||
'Closures / anonymous functions only have access to $this if used within a class or when bound to an object using bindTo(). Please verify.',
|
||||
$thisFound,
|
||||
'ThisFoundOutsideClass'
|
||||
);
|
||||
}
|
||||
|
||||
$thisFound = $this->findThisUsageInClosure($phpcsFile, ($thisFound + 1), $scopeEnd);
|
||||
|
||||
} while ($thisFound !== false);
|
||||
}
|
||||
|
||||
// Prevent double reporting for nested closures.
|
||||
return $scopeEnd;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the closure is declared as static.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isClosureStatic(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
|
||||
|
||||
return ($prevToken !== false && $tokens[$prevToken]['code'] === \T_STATIC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the code within a closure uses the $this variable.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $startToken The position within the closure to continue searching from.
|
||||
* @param int $endToken The closure scope closer to stop searching at.
|
||||
*
|
||||
* @return int|false The stackPtr to the first $this usage if found or false if
|
||||
* $this is not used.
|
||||
*/
|
||||
protected function findThisUsageInClosure(File $phpcsFile, $startToken, $endToken)
|
||||
{
|
||||
// Make sure the $startToken is valid.
|
||||
if ($startToken >= $endToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $phpcsFile->findNext(
|
||||
\T_VARIABLE,
|
||||
$startToken,
|
||||
$endToken,
|
||||
false,
|
||||
'$this'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the code within a closure uses "self/parent/static".
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $startToken The position within the closure to continue searching from.
|
||||
* @param int $endToken The closure scope closer to stop searching at.
|
||||
*
|
||||
* @return int|false The stackPtr to the first classRef usage if found or false if
|
||||
* they are not used.
|
||||
*/
|
||||
protected function findClassRefUsageInClosure(File $phpcsFile, $startToken, $endToken)
|
||||
{
|
||||
// Make sure the $startToken is valid.
|
||||
if ($startToken >= $endToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$classRef = $phpcsFile->findNext(array(\T_SELF, \T_PARENT, \T_STATIC), $startToken, $endToken);
|
||||
|
||||
if ($classRef === false || $tokens[$classRef]['code'] !== \T_STATIC) {
|
||||
return $classRef;
|
||||
}
|
||||
|
||||
// T_STATIC, make sure it is used as a class reference.
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($classRef + 1), $endToken, true);
|
||||
if ($next === false || $tokens[$next]['code'] !== \T_DOUBLE_COLON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $classRef;
|
||||
}
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* As of PHP 7.4, throwing exceptions from a `__toString()` method is allowed.
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/tostring_exceptions
|
||||
* @link https://www.php.net/manual/en/language.oop5.magic.php#object.tostring
|
||||
*
|
||||
* @since 9.2.0
|
||||
*/
|
||||
class NewExceptionsFromToStringSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Valid scopes for the __toString() method to live in.
|
||||
*
|
||||
* @since 9.2.0
|
||||
* @since 9.3.0 Visibility changed from `public` to `protected`.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $ooScopeTokens = array(
|
||||
'T_CLASS' => true,
|
||||
'T_TRAIT' => true,
|
||||
'T_ANON_CLASS' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokens which should be ignored when they preface a function declaration
|
||||
* when trying to find the docblock (if any).
|
||||
*
|
||||
* Array will be added to in the register() method.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $docblockIgnoreTokens = array(
|
||||
\T_WHITESPACE => \T_WHITESPACE,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Enhance the array of tokens to ignore for finding the docblock.
|
||||
$this->docblockIgnoreTokens += Tokens::$methodPrefixes;
|
||||
if (isset(Tokens::$phpcsCommentTokens)) {
|
||||
$this->docblockIgnoreTokens += Tokens::$phpcsCommentTokens;
|
||||
}
|
||||
|
||||
return array(\T_FUNCTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('7.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
|
||||
// Abstract function, interface function, live coding or parse error.
|
||||
return;
|
||||
}
|
||||
|
||||
$functionName = $phpcsFile->getDeclarationName($stackPtr);
|
||||
if (strtolower($functionName) !== '__tostring') {
|
||||
// Not the right function.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) === false) {
|
||||
// Function, not method.
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Examine the content of the function.
|
||||
*/
|
||||
$error = 'Throwing exceptions from __toString() was not allowed prior to PHP 7.4';
|
||||
$throwPtr = $tokens[$stackPtr]['scope_opener'];
|
||||
$errorThrown = false;
|
||||
|
||||
do {
|
||||
$throwPtr = $phpcsFile->findNext(\T_THROW, ($throwPtr + 1), $tokens[$stackPtr]['scope_closer']);
|
||||
if ($throwPtr === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
$conditions = $tokens[$throwPtr]['conditions'];
|
||||
$conditions = array_reverse($conditions, true);
|
||||
$inTryCatch = false;
|
||||
foreach ($conditions as $ptr => $type) {
|
||||
if ($type === \T_TRY) {
|
||||
$inTryCatch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($ptr === $stackPtr) {
|
||||
// Don't check the conditions outside the function scope.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($inTryCatch === false) {
|
||||
$phpcsFile->addError($error, $throwPtr, 'Found');
|
||||
$errorThrown = true;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if ($errorThrown === true) {
|
||||
// We've already thrown an error for this method, no need to examine the docblock.
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the function has a docblock and if so, whether it contains a @throws tag.
|
||||
*
|
||||
* {@internal This can be partially replaced by the findCommentAboveFunction()
|
||||
* utility function in due time.}
|
||||
*/
|
||||
$commentEnd = $phpcsFile->findPrevious($this->docblockIgnoreTokens, ($stackPtr - 1), null, true);
|
||||
if ($commentEnd === false || $tokens[$commentEnd]['code'] !== \T_DOC_COMMENT_CLOSE_TAG) {
|
||||
return;
|
||||
}
|
||||
|
||||
$commentStart = $tokens[$commentEnd]['comment_opener'];
|
||||
foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
|
||||
if ($tokens[$tag]['content'] !== '@throws') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found a throws tag.
|
||||
$phpcsFile->addError($error, $stackPtr, 'ThrowsTagFoundInDocblock');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Nullable parameter type declarations and return types are available since PHP 7.1.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration71.new-features.php#migration71.new-features.nullable-types
|
||||
* @link https://wiki.php.net/rfc/nullable_types
|
||||
* @link https://www.php.net/manual/en/functions.arguments.php#example-146
|
||||
*
|
||||
* @since 7.0.7
|
||||
*/
|
||||
class NewNullableTypesSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* {@internal Not sniffing for T_NULLABLE which was introduced in PHPCS 2.7.2
|
||||
* as in that case we can't distinguish between parameter type hints and
|
||||
* return type hints for the error message.}
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$tokens = array(
|
||||
\T_FUNCTION,
|
||||
\T_CLOSURE,
|
||||
);
|
||||
|
||||
if (\defined('T_RETURN_TYPE')) {
|
||||
$tokens[] = \T_RETURN_TYPE;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$tokenCode = $tokens[$stackPtr]['code'];
|
||||
|
||||
if ($tokenCode === \T_FUNCTION || $tokenCode === \T_CLOSURE) {
|
||||
$this->processFunctionDeclaration($phpcsFile, $stackPtr);
|
||||
|
||||
// Deal with older PHPCS version which don't recognize return type hints
|
||||
// as well as newer PHPCS versions (3.3.0+) where the tokenization has changed.
|
||||
$returnTypeHint = $this->getReturnTypeHintToken($phpcsFile, $stackPtr);
|
||||
if ($returnTypeHint !== false) {
|
||||
$this->processReturnType($phpcsFile, $returnTypeHint);
|
||||
}
|
||||
} else {
|
||||
$this->processReturnType($phpcsFile, $stackPtr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process this test for function tokens.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processFunctionDeclaration(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$params = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr);
|
||||
|
||||
if (empty($params) === false && \is_array($params)) {
|
||||
foreach ($params as $param) {
|
||||
if ($param['nullable_type'] === true) {
|
||||
$phpcsFile->addError(
|
||||
'Nullable type declarations are not supported in PHP 7.0 or earlier. Found: %s',
|
||||
$param['token'],
|
||||
'typeDeclarationFound',
|
||||
array($param['type_hint'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process this test for return type tokens.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processReturnType(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[($stackPtr - 1)]['code']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$previous = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
|
||||
// Deal with namespaced class names.
|
||||
if ($tokens[$previous]['code'] === \T_NS_SEPARATOR) {
|
||||
$validTokens = Tokens::$emptyTokens;
|
||||
$validTokens[\T_STRING] = true;
|
||||
$validTokens[\T_NS_SEPARATOR] = true;
|
||||
|
||||
$stackPtr--;
|
||||
|
||||
while (isset($validTokens[$tokens[($stackPtr - 1)]['code']]) === true) {
|
||||
$stackPtr--;
|
||||
}
|
||||
|
||||
$previous = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
}
|
||||
|
||||
// T_NULLABLE token was introduced in PHPCS 2.7.2. Before that it identified as T_INLINE_THEN.
|
||||
if ((\defined('T_NULLABLE') === true && $tokens[$previous]['type'] === 'T_NULLABLE')
|
||||
|| (\defined('T_NULLABLE') === false && $tokens[$previous]['code'] === \T_INLINE_THEN)
|
||||
) {
|
||||
$phpcsFile->addError(
|
||||
'Nullable return types are not supported in PHP 7.0 or earlier.',
|
||||
$stackPtr,
|
||||
'returnTypeFound'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect and verify the use of parameter type declarations in function declarations.
|
||||
*
|
||||
* Parameter type declarations - class/interface names only - is available since PHP 5.0.
|
||||
* - Since PHP 5.1, the `array` keyword can be used.
|
||||
* - Since PHP 5.2, `self` and `parent` can be used. Previously, those were interpreted as
|
||||
* class names.
|
||||
* - Since PHP 5.4, the `callable` keyword.
|
||||
* - Since PHP 7.0, scalar type declarations are available.
|
||||
* - Since PHP 7.1, the `iterable` pseudo-type is available.
|
||||
* - Since PHP 7.2, the generic `object` type is available.
|
||||
*
|
||||
* Additionally, this sniff does a cursory check for typical invalid type declarations,
|
||||
* such as:
|
||||
* - `boolean` (should be `bool`), `integer` (should be `int`) and `static`.
|
||||
* - `self`/`parent` as type declaration used outside class context throws a fatal error since PHP 7.0.
|
||||
*
|
||||
* PHP version 5.0+
|
||||
*
|
||||
* @link https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration
|
||||
* @link https://wiki.php.net/rfc/callable
|
||||
* @link https://wiki.php.net/rfc/scalar_type_hints_v5
|
||||
* @link https://wiki.php.net/rfc/iterable
|
||||
* @link https://wiki.php.net/rfc/object-typehint
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.
|
||||
* @since 9.0.0 Renamed from `NewScalarTypeDeclarationsSniff` to `NewParamTypeDeclarationsSniff`.
|
||||
*/
|
||||
class NewParamTypeDeclarationsSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of new types.
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If's sufficient to list the first version where the keyword appears.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.0.3 Now lists all param type declarations, not just the PHP 7+ scalar ones.
|
||||
*
|
||||
* @var array(string => array(string => bool))
|
||||
*/
|
||||
protected $newTypes = array(
|
||||
'array' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'self' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'parent' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'callable' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'int' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'float' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'bool' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'string' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'iterable' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'object' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Invalid types
|
||||
*
|
||||
* The array lists : the invalid type hint => what was probably intended/alternative.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var array(string => string)
|
||||
*/
|
||||
protected $invalidTypes = array(
|
||||
'static' => 'self',
|
||||
'boolean' => 'bool',
|
||||
'integer' => 'int',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.1.3 Now also checks closures.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_FUNCTION,
|
||||
\T_CLOSURE,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.0.3 - Added check for non-scalar type declarations.
|
||||
* - Added check for invalid type declarations.
|
||||
* - Added check for usage of `self` type declaration outside
|
||||
* class scope.
|
||||
* @since 8.2.0 Added check for `parent` type declaration outside class scope.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
// Get all parameters from method signature.
|
||||
$paramNames = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr);
|
||||
if (empty($paramNames)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$supportsPHP4 = $this->supportsBelow('4.4');
|
||||
|
||||
foreach ($paramNames as $param) {
|
||||
if ($param['type_hint'] === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Strip off potential nullable indication.
|
||||
$typeHint = ltrim($param['type_hint'], '?');
|
||||
|
||||
if ($supportsPHP4 === true) {
|
||||
$phpcsFile->addError(
|
||||
'Type declarations were not present in PHP 4.4 or earlier.',
|
||||
$param['token'],
|
||||
'TypeHintFound'
|
||||
);
|
||||
|
||||
} elseif (isset($this->newTypes[$typeHint])) {
|
||||
$itemInfo = array(
|
||||
'name' => $typeHint,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $param['token'], $itemInfo);
|
||||
|
||||
// As of PHP 7.0, using `self` or `parent` outside class scope throws a fatal error.
|
||||
// Only throw this error for PHP 5.2+ as before that the "type hint not supported" error
|
||||
// will be thrown.
|
||||
if (($typeHint === 'self' || $typeHint === 'parent')
|
||||
&& $this->inClassScope($phpcsFile, $stackPtr, false) === false
|
||||
&& $this->supportsAbove('5.2') !== false
|
||||
) {
|
||||
$phpcsFile->addError(
|
||||
"'%s' type cannot be used outside of class scope",
|
||||
$param['token'],
|
||||
ucfirst($typeHint) . 'OutsideClassScopeFound',
|
||||
array($typeHint)
|
||||
);
|
||||
}
|
||||
} elseif (isset($this->invalidTypes[$typeHint])) {
|
||||
$error = "'%s' is not a valid type declaration. Did you mean %s ?";
|
||||
$data = array(
|
||||
$typeHint,
|
||||
$this->invalidTypes[$typeHint],
|
||||
);
|
||||
|
||||
$phpcsFile->addError($error, $param['token'], 'InvalidTypeHintFound', $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newTypes[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return "'%s' type declaration is not present in PHP version %s or earlier";
|
||||
}
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect and verify the use of return type declarations in function declarations.
|
||||
*
|
||||
* Return type declarations are available since PHP 7.0.
|
||||
* - Since PHP 7.1, the `iterable` and `void` pseudo-types are available.
|
||||
* - Since PHP 7.2, the generic `object` type is available.
|
||||
*
|
||||
* PHP version 7.0+
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.return-type-declarations
|
||||
* @link https://www.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration
|
||||
* @link https://wiki.php.net/rfc/return_types
|
||||
* @link https://wiki.php.net/rfc/iterable
|
||||
* @link https://wiki.php.net/rfc/void_return_type
|
||||
* @link https://wiki.php.net/rfc/object-typehint
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.
|
||||
* @since 7.1.2 Renamed from `NewScalarReturnTypeDeclarationsSniff` to `NewReturnTypeDeclarationsSniff`.
|
||||
*/
|
||||
class NewReturnTypeDeclarationsSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of new types
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If's sufficient to list the first version where the keyword appears.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @var array(string => array(string => bool))
|
||||
*/
|
||||
protected $newTypes = array(
|
||||
'int' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'float' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'bool' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'string' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'array' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'callable' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'parent' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'self' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'Class name' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
|
||||
'iterable' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'void' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
|
||||
'object' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.1.2 Now also checks based on the function and closure keywords.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$tokens = array(
|
||||
\T_FUNCTION,
|
||||
\T_CLOSURE,
|
||||
);
|
||||
|
||||
if (\defined('T_RETURN_TYPE')) {
|
||||
$tokens[] = \T_RETURN_TYPE;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
// Deal with older PHPCS version which don't recognize return type hints
|
||||
// as well as newer PHPCS versions (3.3.0+) where the tokenization has changed.
|
||||
if ($tokens[$stackPtr]['code'] === \T_FUNCTION || $tokens[$stackPtr]['code'] === \T_CLOSURE) {
|
||||
$returnTypeHint = $this->getReturnTypeHintToken($phpcsFile, $stackPtr);
|
||||
if ($returnTypeHint !== false) {
|
||||
$stackPtr = $returnTypeHint;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->newTypes[$tokens[$stackPtr]['content']]) === true) {
|
||||
$itemInfo = array(
|
||||
'name' => $tokens[$stackPtr]['content'],
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
// Handle class name based return types.
|
||||
elseif ($tokens[$stackPtr]['code'] === \T_STRING
|
||||
|| (\defined('T_RETURN_TYPE') && $tokens[$stackPtr]['code'] === \T_RETURN_TYPE)
|
||||
) {
|
||||
$itemInfo = array(
|
||||
'name' => 'Class name',
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newTypes[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return '%s return type is not present in PHP version %s or earlier';
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Verifies the use of the correct visibility and static properties of magic methods.
|
||||
*
|
||||
* The requirements have always existed, but as of PHP 5.3, a warning will be thrown
|
||||
* when magic methods have the wrong modifiers.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/language.oop5.magic.php
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 5.6 Now extends the base `Sniff` class.
|
||||
*/
|
||||
class NonStaticMagicMethodsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of PHP magic methods and their visibility and static requirements.
|
||||
*
|
||||
* Method names in the array should be all *lowercase*.
|
||||
* Visibility can be either 'public', 'protected' or 'private'.
|
||||
* Static can be either 'true' - *must* be static, or 'false' - *must* be non-static.
|
||||
* When a method does not have a specific requirement for either visibility or static,
|
||||
* do *not* add the key.
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 5.6 The array format has changed to allow the sniff to also verify the
|
||||
* use of the correct visibility for a magic method.
|
||||
*
|
||||
* @var array(string)
|
||||
*/
|
||||
protected $magicMethods = array(
|
||||
'__construct' => array(
|
||||
'static' => false,
|
||||
),
|
||||
'__destruct' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => false,
|
||||
),
|
||||
'__clone' => array(
|
||||
'static' => false,
|
||||
),
|
||||
'__get' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => false,
|
||||
),
|
||||
'__set' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => false,
|
||||
),
|
||||
'__isset' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => false,
|
||||
),
|
||||
'__unset' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => false,
|
||||
),
|
||||
'__call' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => false,
|
||||
),
|
||||
'__callstatic' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => true,
|
||||
),
|
||||
'__sleep' => array(
|
||||
'visibility' => 'public',
|
||||
),
|
||||
'__tostring' => array(
|
||||
'visibility' => 'public',
|
||||
),
|
||||
'__set_state' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => true,
|
||||
),
|
||||
'__debuginfo' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => false,
|
||||
),
|
||||
'__invoke' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => false,
|
||||
),
|
||||
'__serialize' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => false,
|
||||
),
|
||||
'__unserialize' => array(
|
||||
'visibility' => 'public',
|
||||
'static' => false,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 5.6 Now also checks traits.
|
||||
* @since 7.1.4 Now also checks anonymous classes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$targets = array(
|
||||
\T_CLASS,
|
||||
\T_INTERFACE,
|
||||
\T_TRAIT,
|
||||
);
|
||||
|
||||
if (\defined('T_ANON_CLASS')) {
|
||||
$targets[] = \T_ANON_CLASS;
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
// Should be removed, the requirement was previously also there, 5.3 just started throwing a warning about it.
|
||||
if ($this->supportsAbove('5.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[$stackPtr]['scope_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$classScopeCloser = $tokens[$stackPtr]['scope_closer'];
|
||||
$functionPtr = $stackPtr;
|
||||
|
||||
// Find all the functions in this class or interface.
|
||||
while (($functionToken = $phpcsFile->findNext(\T_FUNCTION, $functionPtr, $classScopeCloser)) !== false) {
|
||||
/*
|
||||
* Get the scope closer for this function in order to know how
|
||||
* to advance to the next function.
|
||||
* If no body of function (e.g. for interfaces), there is
|
||||
* no closing curly brace; advance the pointer differently.
|
||||
*/
|
||||
if (isset($tokens[$functionToken]['scope_closer'])) {
|
||||
$scopeCloser = $tokens[$functionToken]['scope_closer'];
|
||||
} else {
|
||||
$scopeCloser = ($functionToken + 1);
|
||||
}
|
||||
|
||||
$methodName = $phpcsFile->getDeclarationName($functionToken);
|
||||
$methodNameLc = strtolower($methodName);
|
||||
if (isset($this->magicMethods[$methodNameLc]) === false) {
|
||||
$functionPtr = $scopeCloser;
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodProperties = $phpcsFile->getMethodProperties($functionToken);
|
||||
$errorCodeBase = $this->stringToErrorCode($methodNameLc);
|
||||
|
||||
if (isset($this->magicMethods[$methodNameLc]['visibility']) && $this->magicMethods[$methodNameLc]['visibility'] !== $methodProperties['scope']) {
|
||||
$error = 'Visibility for magic method %s must be %s. Found: %s';
|
||||
$errorCode = $errorCodeBase . 'MethodVisibility';
|
||||
$data = array(
|
||||
$methodName,
|
||||
$this->magicMethods[$methodNameLc]['visibility'],
|
||||
$methodProperties['scope'],
|
||||
);
|
||||
|
||||
$phpcsFile->addError($error, $functionToken, $errorCode, $data);
|
||||
}
|
||||
|
||||
if (isset($this->magicMethods[$methodNameLc]['static']) && $this->magicMethods[$methodNameLc]['static'] !== $methodProperties['is_static']) {
|
||||
$error = 'Magic method %s cannot be defined as static.';
|
||||
$errorCode = $errorCodeBase . 'MethodStatic';
|
||||
$data = array($methodName);
|
||||
|
||||
if ($this->magicMethods[$methodNameLc]['static'] === true) {
|
||||
$error = 'Magic method %s must be defined as static.';
|
||||
$errorCode = $errorCodeBase . 'MethodNonStatic';
|
||||
}
|
||||
|
||||
$phpcsFile->addError($error, $functionToken, $errorCode, $data);
|
||||
}
|
||||
|
||||
// Advance to next function.
|
||||
$functionPtr = $scopeCloser;
|
||||
}
|
||||
}
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionNameRestrictions;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Warns for non-magic behaviour of magic methods prior to becoming magic.
|
||||
*
|
||||
* PHP version 5.0+
|
||||
*
|
||||
* @link https://www.php.net/manual/en/language.oop5.magic.php
|
||||
* @link https://wiki.php.net/rfc/closures#additional_goodyinvoke
|
||||
* @link https://wiki.php.net/rfc/debug-info
|
||||
*
|
||||
* @since 7.0.4
|
||||
* @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class.
|
||||
*/
|
||||
class NewMagicMethodsSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of new magic methods, not considered magic in older versions.
|
||||
*
|
||||
* Method names in the array should be all *lowercase*.
|
||||
* The array lists : version number with false (not magic) or true (magic).
|
||||
* If's sufficient to list the first version where the method became magic.
|
||||
*
|
||||
* @since 7.0.4
|
||||
*
|
||||
* @var array(string => array(string => bool|string))
|
||||
*/
|
||||
protected $newMagicMethods = array(
|
||||
'__construct' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'__destruct' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'__get' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
|
||||
'__isset' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'__unset' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'__set_state' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
|
||||
'__callstatic' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'__invoke' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
|
||||
'__debuginfo' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => true,
|
||||
),
|
||||
|
||||
// Special case - only became properly magical in 5.2.0,
|
||||
// before that it was only called for echo and print.
|
||||
'__tostring' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
'message' => 'The method %s() was not truly magical in PHP version %s and earlier. The associated magic functionality will only be called when directly combined with echo or print.',
|
||||
),
|
||||
|
||||
'__serialize' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
'__unserialize' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_FUNCTION);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$functionName = $phpcsFile->getDeclarationName($stackPtr);
|
||||
$functionNameLc = strtolower($functionName);
|
||||
|
||||
if (isset($this->newMagicMethods[$functionNameLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->inClassScope($phpcsFile, $stackPtr, false) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $functionName,
|
||||
'nameLc' => $functionNameLc,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newMagicMethods[$itemInfo['nameLc']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of the non-PHP-version array keys used in a sub-array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('message');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemArray Version and other information about the item.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorInfo(array $itemArray, array $itemInfo)
|
||||
{
|
||||
$errorInfo = parent::getErrorInfo($itemArray, $itemInfo);
|
||||
$errorInfo['error'] = false; // Warning, not error.
|
||||
$errorInfo['message'] = '';
|
||||
|
||||
if (empty($itemArray['message']) === false) {
|
||||
$errorInfo['message'] = $itemArray['message'];
|
||||
}
|
||||
|
||||
return $errorInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The method %s() was not magical in PHP version %s and earlier. The associated magic functionality will not be invoked.';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow for concrete child classes to filter the error message before it's passed to PHPCS.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param string $error The error message which was created.
|
||||
* @param array $itemInfo Base information about the item this error message applies to.
|
||||
* @param array $errorInfo Detail information about an item this error message applies to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function filterErrorMsg($error, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
if ($errorInfo['message'] !== '') {
|
||||
$error = $errorInfo['message'];
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionNameRestrictions;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect declaration of the magic `__autoload()` method.
|
||||
*
|
||||
* This method has been deprecated in PHP 7.2 in favour of `spl_autoload_register()`.
|
||||
*
|
||||
* PHP version 7.2
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration72.deprecated.php#migration72.deprecated.__autoload-method
|
||||
* @link https://wiki.php.net/rfc/deprecations_php_7_2#autoload
|
||||
* @link https://www.php.net/manual/en/function.autoload.php
|
||||
*
|
||||
* @since 8.1.0
|
||||
* @since 9.0.0 Renamed from `DeprecatedMagicAutoloadSniff` to `RemovedMagicAutoloadSniff`.
|
||||
*/
|
||||
class RemovedMagicAutoloadSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Scopes to look for when testing using validDirectScope.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $checkForScopes = array(
|
||||
'T_CLASS' => true,
|
||||
'T_ANON_CLASS' => true,
|
||||
'T_INTERFACE' => true,
|
||||
'T_TRAIT' => true,
|
||||
'T_NAMESPACE' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_FUNCTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.2') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$funcName = $phpcsFile->getDeclarationName($stackPtr);
|
||||
|
||||
if (strtolower($funcName) !== '__autoload') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->validDirectScope($phpcsFile, $stackPtr, $this->checkForScopes) !== false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->determineNamespace($phpcsFile, $stackPtr) !== '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addWarning('Use of __autoload() function is deprecated since PHP 7.2', $stackPtr, 'Found');
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionNameRestrictions;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect declaration of a namespaced function called `assert()`.
|
||||
*
|
||||
* As of PHP 7.3, a compile-time deprecation warning will be thrown when a function
|
||||
* called `assert()` is declared. In PHP 8 this will become a compile-error.
|
||||
*
|
||||
* Methods are unaffected.
|
||||
* Global, non-namespaced, `assert()` function declarations were always a fatal
|
||||
* "function already declared" error, so not the concern of this sniff.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration73.deprecated.php#migration73.deprecated.core.assert
|
||||
* @link https://wiki.php.net/rfc/deprecations_php_7_3#defining_a_free-standing_assert_function
|
||||
* @link https://www.php.net/manual/en/function.assert.php
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class RemovedNamespacedAssertSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Scopes in which an `assert` function can be declared without issue.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $scopes = array(
|
||||
\T_CLASS,
|
||||
\T_INTERFACE,
|
||||
\T_TRAIT,
|
||||
\T_CLOSURE,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Enrich the scopes list.
|
||||
if (\defined('T_ANON_CLASS')) {
|
||||
$this->scopes[] = \T_ANON_CLASS;
|
||||
}
|
||||
|
||||
return array(\T_FUNCTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$funcName = $phpcsFile->getDeclarationName($stackPtr);
|
||||
|
||||
if (strtolower($funcName) !== 'assert') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($phpcsFile->hasCondition($stackPtr, $this->scopes) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->determineNamespace($phpcsFile, $stackPtr) === '') {
|
||||
// Not a namespaced function declaration. This may be a parse error, but not our concern.
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addWarning('Declaring a free-standing function called assert() is deprecated since PHP 7.3.', $stackPtr, 'Found');
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionNameRestrictions;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect declarations of PHP 4 style constructors which are deprecated as of PHP 7.0.0.
|
||||
*
|
||||
* PHP 4 style constructors - methods that have the same name as the class they are defined in -
|
||||
* are deprecated as of PHP 7.0.0, and will be removed in the future.
|
||||
* PHP 7 will emit `E_DEPRECATED` if a PHP 4 constructor is the only constructor defined
|
||||
* within a class. Classes that implement a `__construct()` method are unaffected.
|
||||
*
|
||||
* Note: Methods with the same name as the class they are defined in _within a namespace_
|
||||
* are not recognized as constructors anyway and therefore outside the scope of this sniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration70.deprecated.php#migration70.deprecated.php4-constructors
|
||||
* @link https://wiki.php.net/rfc/remove_php4_constructors
|
||||
* @link https://www.php.net/manual/en/language.oop5.decon.php
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.0.8 This sniff now throws a warning instead of an error as the functionality is
|
||||
* only deprecated (for now).
|
||||
* @since 9.0.0 Renamed from `DeprecatedPHP4StyleConstructorsSniff` to `RemovedPHP4StyleConstructorsSniff`.
|
||||
*/
|
||||
class RemovedPHP4StyleConstructorsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_CLASS,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.0.8 The message is downgraded from error to warning as - for now - support
|
||||
* for PHP4-style constructors is just deprecated, not yet removed.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->determineNamespace($phpcsFile, $stackPtr) !== '') {
|
||||
/*
|
||||
* Namespaced methods with the same name as the class are treated as
|
||||
* regular methods, so we can bow out if we're in a namespace.
|
||||
*
|
||||
* Note: the exception to this is PHP 5.3.0-5.3.2. This is currently
|
||||
* not dealt with.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$class = $tokens[$stackPtr];
|
||||
|
||||
if (isset($class['scope_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_STRING) {
|
||||
// Anonymous class in combination with PHPCS 2.3.x.
|
||||
return;
|
||||
}
|
||||
|
||||
$scopeCloser = $class['scope_closer'];
|
||||
$className = $tokens[$nextNonEmpty]['content'];
|
||||
|
||||
if (empty($className) || \is_string($className) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nextFunc = $stackPtr;
|
||||
$classNameLc = strtolower($className);
|
||||
$newConstructorFound = false;
|
||||
$oldConstructorFound = false;
|
||||
$oldConstructorPos = -1;
|
||||
while (($nextFunc = $phpcsFile->findNext(array(\T_FUNCTION, \T_DOC_COMMENT_OPEN_TAG), ($nextFunc + 1), $scopeCloser)) !== false) {
|
||||
// Skip over docblocks.
|
||||
if ($tokens[$nextFunc]['code'] === \T_DOC_COMMENT_OPEN_TAG) {
|
||||
$nextFunc = $tokens[$nextFunc]['comment_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
$functionScopeCloser = $nextFunc;
|
||||
if (isset($tokens[$nextFunc]['scope_closer'])) {
|
||||
// Normal (non-interface, non-abstract) method.
|
||||
$functionScopeCloser = $tokens[$nextFunc]['scope_closer'];
|
||||
}
|
||||
|
||||
$funcName = $phpcsFile->getDeclarationName($nextFunc);
|
||||
if (empty($funcName) || \is_string($funcName) === false) {
|
||||
$nextFunc = $functionScopeCloser;
|
||||
continue;
|
||||
}
|
||||
|
||||
$funcNameLc = strtolower($funcName);
|
||||
|
||||
if ($funcNameLc === '__construct') {
|
||||
$newConstructorFound = true;
|
||||
}
|
||||
|
||||
if ($funcNameLc === $classNameLc) {
|
||||
$oldConstructorFound = true;
|
||||
$oldConstructorPos = $nextFunc;
|
||||
}
|
||||
|
||||
// If both have been found, no need to continue looping through the functions.
|
||||
if ($newConstructorFound === true && $oldConstructorFound === true) {
|
||||
break;
|
||||
}
|
||||
|
||||
$nextFunc = $functionScopeCloser;
|
||||
}
|
||||
|
||||
if ($newConstructorFound === false && $oldConstructorFound === true) {
|
||||
$phpcsFile->addWarning(
|
||||
'Use of deprecated PHP4 style class constructor is not supported since PHP 7.',
|
||||
$oldConstructorPos,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionNameRestrictions;
|
||||
|
||||
use Generic_Sniffs_NamingConventions_CamelCapsFunctionNameSniff as PHPCS_CamelCapsFunctionNameSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Standards_AbstractScopeSniff as PHPCS_AbstractScopeSniff;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* All function and method names starting with double underscore are reserved by PHP.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* {@internal Extends an upstream sniff to benefit from the properties contained therein.
|
||||
* The properties are lists of valid PHP magic function and method names, which
|
||||
* should be ignored for the purposes of this sniff.
|
||||
* As this sniff is not PHP version specific, we don't need access to the utility
|
||||
* methods in the PHPCompatibility\Sniff, so extending the upstream sniff is fine.
|
||||
* As the upstream sniff checks the same (and more, but we don't need the rest),
|
||||
* the logic in this sniff is largely the same as used upstream.
|
||||
* Extending the upstream sniff instead of including it via the ruleset, however,
|
||||
* prevents hard to debug issues of errors not being reported from the upstream sniff
|
||||
* if this library is used in combination with other rulesets.}
|
||||
*
|
||||
* @link https://www.php.net/manual/en/language.oop5.magic.php
|
||||
*
|
||||
* @since 8.2.0 This was previously, since 7.0.3, checked by the upstream sniff.
|
||||
* @since 9.3.2 The sniff will now ignore functions marked as `@deprecated` by design.
|
||||
*/
|
||||
class ReservedFunctionNamesSniff extends PHPCS_CamelCapsFunctionNameSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Overload the constructor to work round various PHPCS cross-version compatibility issues.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$scopeTokens = array(\T_CLASS, \T_INTERFACE, \T_TRAIT);
|
||||
if (\defined('T_ANON_CLASS')) {
|
||||
$scopeTokens[] = \T_ANON_CLASS;
|
||||
}
|
||||
|
||||
// Call the grand-parent constructor directly.
|
||||
PHPCS_AbstractScopeSniff::__construct($scopeTokens, array(\T_FUNCTION), true);
|
||||
|
||||
// Make sure debuginfo is included in the array. Upstream only includes it since 2.5.1.
|
||||
$this->magicMethods['debuginfo'] = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes the tokens within the scope.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being processed.
|
||||
* @param int $stackPtr The position where this token was
|
||||
* found.
|
||||
* @param int $currScope The position of the current scope.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
/*
|
||||
* Determine if this is a function which needs to be examined.
|
||||
* The `processTokenWithinScope()` is called for each valid scope a method is in,
|
||||
* so for nested classes, we need to make sure we only examine the token for
|
||||
* the lowest level valid scope.
|
||||
*/
|
||||
$conditions = $tokens[$stackPtr]['conditions'];
|
||||
end($conditions);
|
||||
$deepestScope = key($conditions);
|
||||
if ($deepestScope !== $currScope) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isFunctionDeprecated($phpcsFile, $stackPtr) === true) {
|
||||
/*
|
||||
* Deprecated functions don't have to comply with the naming conventions,
|
||||
* otherwise functions deprecated in favour of a function with a compliant
|
||||
* name would still trigger an error.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
$methodName = $phpcsFile->getDeclarationName($stackPtr);
|
||||
if ($methodName === null) {
|
||||
// Ignore closures.
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this a magic method. i.e., is prefixed with "__" ?
|
||||
if (preg_match('|^__[^_]|', $methodName) > 0) {
|
||||
$magicPart = strtolower(substr($methodName, 2));
|
||||
if (isset($this->magicMethods[$magicPart]) === false
|
||||
&& isset($this->methodsDoubleUnderscore[$magicPart]) === false
|
||||
) {
|
||||
$className = '[anonymous class]';
|
||||
$scopeNextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($currScope + 1), null, true);
|
||||
if ($scopeNextNonEmpty !== false && $tokens[$scopeNextNonEmpty]['code'] === \T_STRING) {
|
||||
$className = $tokens[$scopeNextNonEmpty]['content'];
|
||||
}
|
||||
|
||||
$phpcsFile->addWarning(
|
||||
'Method name "%s" is discouraged; PHP has reserved all method names with a double underscore prefix for future use.',
|
||||
$stackPtr,
|
||||
'MethodDoubleUnderscore',
|
||||
array($className . '::' . $methodName)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes the tokens outside the scope.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being processed.
|
||||
* @param int $stackPtr The position where this token was
|
||||
* found.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->isFunctionDeprecated($phpcsFile, $stackPtr) === true) {
|
||||
/*
|
||||
* Deprecated functions don't have to comply with the naming conventions,
|
||||
* otherwise functions deprecated in favour of a function with a compliant
|
||||
* name would still trigger an error.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
$functionName = $phpcsFile->getDeclarationName($stackPtr);
|
||||
if ($functionName === null) {
|
||||
// Ignore closures.
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this a magic function. i.e., it is prefixed with "__".
|
||||
if (preg_match('|^__[^_]|', $functionName) > 0) {
|
||||
$magicPart = strtolower(substr($functionName, 2));
|
||||
if (isset($this->magicFunctions[$magicPart]) === false) {
|
||||
$phpcsFile->addWarning(
|
||||
'Function name "%s" is discouraged; PHP has reserved all method names with a double underscore prefix for future use.',
|
||||
$stackPtr,
|
||||
'FunctionDoubleUnderscore',
|
||||
array($functionName)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether a function has been marked as deprecated via a @deprecated tag
|
||||
* in the function docblock.
|
||||
*
|
||||
* @since 9.3.2
|
||||
*
|
||||
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of a T_FUNCTION
|
||||
* token in the stack.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isFunctionDeprecated(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$find = Tokens::$methodPrefixes;
|
||||
$find[] = \T_WHITESPACE;
|
||||
|
||||
$commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
|
||||
if ($tokens[$commentEnd]['code'] !== \T_DOC_COMMENT_CLOSE_TAG) {
|
||||
// Function doesn't have a doc comment or is using the wrong type of comment.
|
||||
return false;
|
||||
}
|
||||
|
||||
$commentStart = $tokens[$commentEnd]['comment_opener'];
|
||||
foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
|
||||
if ($tokens[$tag]['content'] === '@deprecated') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+455
@@ -0,0 +1,455 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionUse;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Functions inspecting function arguments report the current parameter value
|
||||
* instead of the original since PHP 7.0.
|
||||
*
|
||||
* `func_get_arg()`, `func_get_args()`, `debug_backtrace()` and exception backtraces
|
||||
* will no longer report the original parameter value as was passed to the function,
|
||||
* but will instead provide the current value (which might have been modified).
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.other.func-parameter-modified
|
||||
*
|
||||
* @since 9.1.0
|
||||
*/
|
||||
class ArgumentFunctionsReportCurrentValueSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of functions that, when called, can behave differently in PHP 7
|
||||
* when dealing with parameters of the function they're called in.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $changedFunctions = array(
|
||||
'func_get_arg' => true,
|
||||
'func_get_args' => true,
|
||||
'debug_backtrace' => true,
|
||||
'debug_print_backtrace' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokens to look out for to allow us to skip past nested scoped structures.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $skipPastNested = array(
|
||||
'T_CLASS' => true,
|
||||
'T_ANON_CLASS' => true,
|
||||
'T_INTERFACE' => true,
|
||||
'T_TRAIT' => true,
|
||||
'T_FUNCTION' => true,
|
||||
'T_CLOSURE' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* List of tokens which when they preceed a T_STRING *within a function* indicate
|
||||
* this is not a call to a PHP native function.
|
||||
*
|
||||
* This list already takes into account that nested scoped structures are being
|
||||
* skipped over, so doesn't check for those again.
|
||||
* Similarly, as constants won't have parentheses, those don't need to be checked
|
||||
* for either.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $noneFunctionCallIndicators = array(
|
||||
\T_DOUBLE_COLON => true,
|
||||
\T_OBJECT_OPERATOR => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* The tokens for variable incrementing/decrementing.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $plusPlusMinusMinus = array(
|
||||
\T_DEC => true,
|
||||
\T_INC => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokens to ignore when determining the start of a statement.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $ignoreForStartOfStatement = array(
|
||||
\T_COMMA,
|
||||
\T_DOUBLE_ARROW,
|
||||
\T_OPEN_SQUARE_BRACKET,
|
||||
\T_OPEN_PARENTHESIS,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_FUNCTION,
|
||||
\T_CLOSURE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
|
||||
// Abstract function, interface function, live coding or parse error.
|
||||
return;
|
||||
}
|
||||
|
||||
$scopeOpener = $tokens[$stackPtr]['scope_opener'];
|
||||
$scopeCloser = $tokens[$stackPtr]['scope_closer'];
|
||||
|
||||
// Does the function declaration have parameters ?
|
||||
$params = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr);
|
||||
if (empty($params)) {
|
||||
// No named arguments found, so no risk of them being changed.
|
||||
return;
|
||||
}
|
||||
|
||||
$paramNames = array();
|
||||
foreach ($params as $param) {
|
||||
$paramNames[] = $param['name'];
|
||||
}
|
||||
|
||||
for ($i = ($scopeOpener + 1); $i < $scopeCloser; $i++) {
|
||||
if (isset($this->skipPastNested[$tokens[$i]['type']]) && isset($tokens[$i]['scope_closer'])) {
|
||||
// Skip past nested structures.
|
||||
$i = $tokens[$i]['scope_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] !== \T_STRING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$foundFunctionName = strtolower($tokens[$i]['content']);
|
||||
|
||||
if (isset($this->changedFunctions[$foundFunctionName]) === false) {
|
||||
// Not one of the target functions.
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, so is this really a function call to one of the PHP native functions ?
|
||||
*/
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
|
||||
if ($next === false || $tokens[$next]['code'] !== \T_OPEN_PARENTHESIS) {
|
||||
// Live coding, parse error or not a function call.
|
||||
continue;
|
||||
}
|
||||
|
||||
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($i - 1), null, true);
|
||||
if ($prev !== false) {
|
||||
if (isset($this->noneFunctionCallIndicators[$tokens[$prev]['code']])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for namespaced functions, ie: \foo\bar() not \bar().
|
||||
if ($tokens[ $prev ]['code'] === \T_NS_SEPARATOR) {
|
||||
$pprev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true);
|
||||
if ($pprev !== false && $tokens[ $pprev ]['code'] === \T_STRING) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Address some special cases.
|
||||
*/
|
||||
if ($foundFunctionName !== 'func_get_args') {
|
||||
$paramOne = $this->getFunctionCallParameter($phpcsFile, $i, 1);
|
||||
if ($paramOne !== false) {
|
||||
switch ($foundFunctionName) {
|
||||
/*
|
||||
* Check if `debug_(print_)backtrace()` is called with the
|
||||
* `DEBUG_BACKTRACE_IGNORE_ARGS` option.
|
||||
*/
|
||||
case 'debug_backtrace':
|
||||
case 'debug_print_backtrace':
|
||||
$hasIgnoreArgs = $phpcsFile->findNext(
|
||||
\T_STRING,
|
||||
$paramOne['start'],
|
||||
($paramOne['end'] + 1),
|
||||
false,
|
||||
'DEBUG_BACKTRACE_IGNORE_ARGS'
|
||||
);
|
||||
|
||||
if ($hasIgnoreArgs !== false) {
|
||||
// Debug_backtrace() called with ignore args option.
|
||||
continue 2;
|
||||
}
|
||||
break;
|
||||
|
||||
/*
|
||||
* Collect the necessary information to only throw a notice if the argument
|
||||
* touched/changed is in line with the passed $arg_num.
|
||||
*
|
||||
* Also, we can ignore `func_get_arg()` if the argument offset passed is
|
||||
* higher than the number of named parameters.
|
||||
*
|
||||
* {@internal Note: This does not take calculations into account!
|
||||
* Should be exceptionally rare and can - if needs be - be addressed at a later stage.}
|
||||
*/
|
||||
case 'func_get_arg':
|
||||
$number = $phpcsFile->findNext(\T_LNUMBER, $paramOne['start'], ($paramOne['end'] + 1));
|
||||
if ($number !== false) {
|
||||
$argNumber = $tokens[$number]['content'];
|
||||
|
||||
if (isset($paramNames[$argNumber]) === false) {
|
||||
// Requesting a non-named additional parameter. Ignore.
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Check if the call to func_get_args() happens to be in an array_slice() or
|
||||
* array_splice() with an $offset higher than the number of named parameters.
|
||||
* In that case, we can ignore it.
|
||||
*
|
||||
* {@internal Note: This does not take offset calculations into account!
|
||||
* Should be exceptionally rare and can - if needs be - be addressed at a later stage.}
|
||||
*/
|
||||
if ($prev !== false && $tokens[$prev]['code'] === \T_OPEN_PARENTHESIS) {
|
||||
|
||||
$maybeFunctionCall = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true);
|
||||
if ($maybeFunctionCall !== false
|
||||
&& $tokens[$maybeFunctionCall]['code'] === \T_STRING
|
||||
&& ($tokens[$maybeFunctionCall]['content'] === 'array_slice'
|
||||
|| $tokens[$maybeFunctionCall]['content'] === 'array_splice')
|
||||
) {
|
||||
$parentFuncParamTwo = $this->getFunctionCallParameter($phpcsFile, $maybeFunctionCall, 2);
|
||||
$number = $phpcsFile->findNext(
|
||||
\T_LNUMBER,
|
||||
$parentFuncParamTwo['start'],
|
||||
($parentFuncParamTwo['end'] + 1)
|
||||
);
|
||||
|
||||
if ($number !== false && isset($paramNames[$tokens[$number]['content']]) === false) {
|
||||
// Requesting non-named additional parameters. Ignore.
|
||||
continue ;
|
||||
}
|
||||
|
||||
// Slice starts at a named argument, but we know which params are being accessed.
|
||||
$paramNamesSubset = \array_slice($paramNames, $tokens[$number]['content']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For debug_backtrace(), check if the result is being dereferenced and if so,
|
||||
* whether the `args` index is used.
|
||||
* I.e. whether `$index` in `debug_backtrace()[$stackFrame][$index]` is a string
|
||||
* with the content `args`.
|
||||
*
|
||||
* Note: We already know that $next is the open parenthesis of the function call.
|
||||
*/
|
||||
if ($foundFunctionName === 'debug_backtrace' && isset($tokens[$next]['parenthesis_closer'])) {
|
||||
$afterParenthesis = $phpcsFile->findNext(
|
||||
Tokens::$emptyTokens,
|
||||
($tokens[$next]['parenthesis_closer'] + 1),
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
if ($tokens[$afterParenthesis]['code'] === \T_OPEN_SQUARE_BRACKET
|
||||
&& isset($tokens[$afterParenthesis]['bracket_closer'])
|
||||
) {
|
||||
$afterStackFrame = $phpcsFile->findNext(
|
||||
Tokens::$emptyTokens,
|
||||
($tokens[$afterParenthesis]['bracket_closer'] + 1),
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
if ($tokens[$afterStackFrame]['code'] === \T_OPEN_SQUARE_BRACKET
|
||||
&& isset($tokens[$afterStackFrame]['bracket_closer'])
|
||||
) {
|
||||
$arrayIndex = $phpcsFile->findNext(
|
||||
\T_CONSTANT_ENCAPSED_STRING,
|
||||
($afterStackFrame + 1),
|
||||
$tokens[$afterStackFrame]['bracket_closer']
|
||||
);
|
||||
|
||||
if ($arrayIndex !== false && $this->stripQuotes($tokens[$arrayIndex]['content']) !== 'args') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Only check for variables before the start of the statement to
|
||||
* prevent false positives on the return value of the function call
|
||||
* being assigned to one of the parameters, i.e.:
|
||||
* `$param = func_get_args();`.
|
||||
*/
|
||||
$startOfStatement = PHPCSHelper::findStartOfStatement($phpcsFile, $i, $this->ignoreForStartOfStatement);
|
||||
|
||||
/*
|
||||
* Ok, so we've found one of the target functions in the right scope.
|
||||
* Now, let's check if any of the passed parameters were touched.
|
||||
*/
|
||||
$scanResult = 'clean';
|
||||
for ($j = ($scopeOpener + 1); $j < $startOfStatement; $j++) {
|
||||
if (isset($this->skipPastNested[$tokens[$j]['type']])
|
||||
&& isset($tokens[$j]['scope_closer'])
|
||||
) {
|
||||
// Skip past nested structures.
|
||||
$j = $tokens[$j]['scope_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$j]['code'] !== \T_VARIABLE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($foundFunctionName === 'func_get_arg' && isset($argNumber)) {
|
||||
if (isset($paramNames[$argNumber])
|
||||
&& $tokens[$j]['content'] !== $paramNames[$argNumber]
|
||||
) {
|
||||
// Different param than the one requested by func_get_arg().
|
||||
continue;
|
||||
}
|
||||
} elseif ($foundFunctionName === 'func_get_args' && isset($paramNamesSubset)) {
|
||||
if (\in_array($tokens[$j]['content'], $paramNamesSubset, true) === false) {
|
||||
// Different param than the ones requested by func_get_args().
|
||||
continue;
|
||||
}
|
||||
} elseif (\in_array($tokens[$j]['content'], $paramNames, true) === false) {
|
||||
// Variable is not one of the function parameters.
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, so we've found a variable which was passed as one of the parameters.
|
||||
* Now, is this variable being changed, i.e. incremented, decremented or
|
||||
* assigned something ?
|
||||
*/
|
||||
$scanResult = 'warning';
|
||||
if (isset($variableToken) === false) {
|
||||
$variableToken = $j;
|
||||
}
|
||||
|
||||
$beforeVar = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($j - 1), null, true);
|
||||
if ($beforeVar !== false && isset($this->plusPlusMinusMinus[$tokens[$beforeVar]['code']])) {
|
||||
// Variable is being (pre-)incremented/decremented.
|
||||
$scanResult = 'error';
|
||||
$variableToken = $j;
|
||||
break;
|
||||
}
|
||||
|
||||
$afterVar = $phpcsFile->findNext(Tokens::$emptyTokens, ($j + 1), null, true);
|
||||
if ($afterVar === false) {
|
||||
// Shouldn't be possible, but just in case.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($this->plusPlusMinusMinus[$tokens[$afterVar]['code']])) {
|
||||
// Variable is being (post-)incremented/decremented.
|
||||
$scanResult = 'error';
|
||||
$variableToken = $j;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($tokens[$afterVar]['code'] === \T_OPEN_SQUARE_BRACKET
|
||||
&& isset($tokens[$afterVar]['bracket_closer'])
|
||||
) {
|
||||
// Skip past array access on the variable.
|
||||
while (($afterVar = $phpcsFile->findNext(Tokens::$emptyTokens, ($tokens[$afterVar]['bracket_closer'] + 1), null, true)) !== false) {
|
||||
if ($tokens[$afterVar]['code'] !== \T_OPEN_SQUARE_BRACKET
|
||||
|| isset($tokens[$afterVar]['bracket_closer']) === false
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($afterVar !== false
|
||||
&& isset(Tokens::$assignmentTokens[$tokens[$afterVar]['code']])
|
||||
) {
|
||||
// Variable is being assigned something.
|
||||
$scanResult = 'error';
|
||||
$variableToken = $j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unset($argNumber, $paramNamesSubset);
|
||||
|
||||
if ($scanResult === 'clean') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$error = 'Since PHP 7.0, functions inspecting arguments, like %1$s(), no longer report the original value as passed to a parameter, but will instead provide the current value. The parameter "%2$s" was %4$s on line %3$s.';
|
||||
$data = array(
|
||||
$foundFunctionName,
|
||||
$tokens[$variableToken]['content'],
|
||||
$tokens[$variableToken]['line'],
|
||||
);
|
||||
|
||||
if ($scanResult === 'error') {
|
||||
$data[] = 'changed';
|
||||
$phpcsFile->addError($error, $i, 'Changed', $data);
|
||||
|
||||
} elseif ($scanResult === 'warning') {
|
||||
$data[] = 'used, and possibly changed (by reference),';
|
||||
$phpcsFile->addWarning($error, $i, 'NeedsInspection', $data);
|
||||
}
|
||||
|
||||
unset($variableToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionUse;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect usage of `func_get_args()`, `func_get_arg()` and `func_num_args()` in invalid context.
|
||||
*
|
||||
* Checks for:
|
||||
* - Prior to PHP 5.3, these functions could not be used as a function call parameter.
|
||||
* - Calling these functions from the outermost scope of a file which has been included by
|
||||
* calling `include` or `require` from within a function in the calling file, worked
|
||||
* prior to PHP 5.3. As of PHP 5.3, this will generate a warning and will always return false/-1.
|
||||
* If the file was called directly or included in the global scope, calls to these
|
||||
* functions would already generate a warning prior to PHP 5.3.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration53.incompatible.php
|
||||
*
|
||||
* @since 8.2.0
|
||||
*/
|
||||
class ArgumentFunctionsUsageSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* The target functions for this sniff.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'func_get_args' => true,
|
||||
'func_get_arg' => true,
|
||||
'func_num_args' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$functionLc = strtolower($tokens[$stackPtr]['content']);
|
||||
if (isset($this->targetFunctions[$functionLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Next non-empty token should be the open parenthesis.
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
|
||||
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ignore = array(
|
||||
\T_DOUBLE_COLON => true,
|
||||
\T_OBJECT_OPERATOR => true,
|
||||
\T_FUNCTION => true,
|
||||
\T_NEW => true,
|
||||
);
|
||||
|
||||
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
if (isset($ignore[$tokens[$prevNonEmpty]['code']]) === true) {
|
||||
// Not a call to a PHP function.
|
||||
return;
|
||||
} elseif ($tokens[$prevNonEmpty]['code'] === \T_NS_SEPARATOR && $tokens[$prevNonEmpty - 1]['code'] === \T_STRING) {
|
||||
// Namespaced function.
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $tokens[$stackPtr]['content'];
|
||||
|
||||
/*
|
||||
* Check for use of the functions in the global scope.
|
||||
*
|
||||
* As PHPCS can not determine whether a file is included from within a function in
|
||||
* another file, so always throw a warning/error.
|
||||
*/
|
||||
if ($phpcsFile->hasCondition($stackPtr, array(\T_FUNCTION, \T_CLOSURE)) === false) {
|
||||
$isError = false;
|
||||
$message = 'Use of %s() outside of a user-defined function is only supported if the file is included from within a user-defined function in another file prior to PHP 5.3.';
|
||||
|
||||
if ($this->supportsAbove('5.3') === true) {
|
||||
$isError = true;
|
||||
$message .= ' As of PHP 5.3, it is no longer supported at all.';
|
||||
}
|
||||
|
||||
$this->addMessage($phpcsFile, $message, $stackPtr, $isError, 'OutsideFunctionScope', $data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for use of the functions as a parameter in a function call.
|
||||
*/
|
||||
if ($this->supportsBelow('5.2') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($tokens[$stackPtr]['nested_parenthesis']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$throwError = false;
|
||||
|
||||
$closer = end($tokens[$stackPtr]['nested_parenthesis']);
|
||||
if (isset($tokens[$closer]['parenthesis_owner'])
|
||||
&& $tokens[$tokens[$closer]['parenthesis_owner']]['type'] === 'T_CLOSURE'
|
||||
) {
|
||||
$throwError = true;
|
||||
} else {
|
||||
$opener = key($tokens[$stackPtr]['nested_parenthesis']);
|
||||
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), null, true);
|
||||
if ($tokens[$prevNonEmpty]['code'] !== \T_STRING) {
|
||||
return;
|
||||
}
|
||||
|
||||
$prevPrevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevNonEmpty - 1), null, true);
|
||||
if ($tokens[$prevPrevNonEmpty]['code'] === \T_FUNCTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
$throwError = true;
|
||||
}
|
||||
|
||||
if ($throwError === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'%s() could not be used in parameter lists prior to PHP 5.3.',
|
||||
$stackPtr,
|
||||
'InParameterList',
|
||||
$data
|
||||
);
|
||||
}
|
||||
}
|
||||
+1109
File diff suppressed because it is too large
Load Diff
Vendored
+2008
File diff suppressed because it is too large
Load Diff
+173
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionUse;
|
||||
|
||||
use PHPCompatibility\Sniffs\FunctionUse\RequiredToOptionalFunctionParametersSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect missing required function parameters in calls to native PHP functions.
|
||||
*
|
||||
* Specifically when those function parameters used to be optional in older PHP versions.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://www.php.net/manual/en/doc.changelog.php
|
||||
*
|
||||
* @since 8.1.0
|
||||
* @since 9.0.0 Renamed from `OptionalRequiredFunctionParametersSniff` to `OptionalToRequiredFunctionParametersSniff`.
|
||||
*/
|
||||
class OptionalToRequiredFunctionParametersSniff extends RequiredToOptionalFunctionParametersSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of function parameters, which were optional in older versions and became required later on.
|
||||
*
|
||||
* The array lists : version number with true (required) and false (optional use deprecated).
|
||||
*
|
||||
* The index is the location of the parameter in the parameter list, starting at 0 !
|
||||
* If's sufficient to list the last version in which the parameter was not yet required.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $functionParameters = array(
|
||||
// Special case, the optional nature is not deprecated, but usage is recommended
|
||||
// and leaving the parameter out will throw an E_NOTICE.
|
||||
'crypt' => array(
|
||||
1 => array(
|
||||
'name' => 'salt',
|
||||
'5.6' => 'recommended',
|
||||
),
|
||||
),
|
||||
'parse_str' => array(
|
||||
1 => array(
|
||||
'name' => 'result',
|
||||
'7.2' => false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether an error/warning should be thrown for an item based on collected information.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @param array $errorInfo Detail information about an item.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldThrowError(array $errorInfo)
|
||||
{
|
||||
return ($errorInfo['optionalDeprecated'] !== ''
|
||||
|| $errorInfo['optionalRemoved'] !== ''
|
||||
|| $errorInfo['optionalRecommended'] !== '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @param array $itemArray Version and other information about the item.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorInfo(array $itemArray, array $itemInfo)
|
||||
{
|
||||
$errorInfo = array(
|
||||
'paramName' => '',
|
||||
'optionalRecommended' => '',
|
||||
'optionalDeprecated' => '',
|
||||
'optionalRemoved' => '',
|
||||
'error' => false,
|
||||
);
|
||||
|
||||
$versionArray = $this->getVersionArray($itemArray);
|
||||
|
||||
if (empty($versionArray) === false) {
|
||||
foreach ($versionArray as $version => $required) {
|
||||
if ($this->supportsAbove($version) === true) {
|
||||
if ($required === true && $errorInfo['optionalRemoved'] === '') {
|
||||
$errorInfo['optionalRemoved'] = $version;
|
||||
$errorInfo['error'] = true;
|
||||
} elseif ($required === 'recommended' && $errorInfo['optionalRecommended'] === '') {
|
||||
$errorInfo['optionalRecommended'] = $version;
|
||||
} elseif ($errorInfo['optionalDeprecated'] === '') {
|
||||
$errorInfo['optionalDeprecated'] = $version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$errorInfo['paramName'] = $itemArray['name'];
|
||||
|
||||
return $errorInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the error or warning for this item.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the relevant token in
|
||||
* the stack.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
* @param array $errorInfo Array with detail (version) information
|
||||
* relevant to the item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addError(File $phpcsFile, $stackPtr, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
$error = 'The "%s" parameter for function %s() is missing. Passing this parameter is ';
|
||||
if ($errorInfo['optionalRecommended'] === '') {
|
||||
$error .= 'no longer optional. The optional nature of the parameter is ';
|
||||
} else {
|
||||
$error .= 'strongly recommended ';
|
||||
}
|
||||
|
||||
$errorCode = $this->stringToErrorCode($itemInfo['name'] . '_' . $errorInfo['paramName']);
|
||||
$data = array(
|
||||
$errorInfo['paramName'],
|
||||
$itemInfo['name'],
|
||||
);
|
||||
|
||||
if ($errorInfo['optionalRecommended'] !== '') {
|
||||
$error .= 'since PHP %s ';
|
||||
$errorCode .= 'SoftRecommended';
|
||||
$data[] = $errorInfo['optionalRecommended'];
|
||||
} else {
|
||||
if ($errorInfo['optionalDeprecated'] !== '') {
|
||||
$error .= 'deprecated since PHP %s and ';
|
||||
$errorCode .= 'SoftRequired';
|
||||
$data[] = $errorInfo['optionalDeprecated'];
|
||||
}
|
||||
|
||||
if ($errorInfo['optionalRemoved'] !== '') {
|
||||
$error .= 'removed since PHP %s and ';
|
||||
$errorCode .= 'HardRequired';
|
||||
$data[] = $errorInfo['optionalRemoved'];
|
||||
}
|
||||
|
||||
// Remove the last 'and' from the message.
|
||||
$error = substr($error, 0, (\strlen($error) - 5));
|
||||
}
|
||||
|
||||
$this->addMessage($phpcsFile, $error, $stackPtr, $errorInfo['error'], $errorCode, $data);
|
||||
}
|
||||
}
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionUse;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect use of deprecated/removed function parameters in calls to native PHP functions.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://www.php.net/manual/en/doc.changelog.php
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.1.0 Now extends the `AbstractRemovedFeatureSniff` instead of the base `Sniff` class.
|
||||
*/
|
||||
class RemovedFunctionParametersSniff extends AbstractRemovedFeatureSniff
|
||||
{
|
||||
/**
|
||||
* A list of removed function parameters, which were present in older versions.
|
||||
*
|
||||
* The array lists : version number with false (deprecated) and true (removed).
|
||||
* The index is the location of the parameter in the parameter list, starting at 0 !
|
||||
* If's sufficient to list the first version where the function parameter was deprecated/removed.
|
||||
*
|
||||
* The optional `callback` key can be used to pass a method name which should be called for an
|
||||
* additional check. The method will be passed the parameter info and should return true
|
||||
* if the notice should be thrown or false otherwise.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.0.2 Visibility changed from `public` to `protected`.
|
||||
* @since 9.3.0 Optional `callback` key.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $removedFunctionParameters = array(
|
||||
'curl_version' => array(
|
||||
0 => array(
|
||||
'name' => 'age',
|
||||
'7.4' => false,
|
||||
'callback' => 'curlVersionInvalidValue',
|
||||
),
|
||||
),
|
||||
'define' => array(
|
||||
2 => array(
|
||||
'name' => 'case_insensitive',
|
||||
'7.3' => false, // Slated for removal in PHP 8.0.0.
|
||||
),
|
||||
),
|
||||
'gmmktime' => array(
|
||||
6 => array(
|
||||
'name' => 'is_dst',
|
||||
'5.1' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
),
|
||||
'ldap_first_attribute' => array(
|
||||
2 => array(
|
||||
'name' => 'ber_identifier',
|
||||
'5.2.4' => true,
|
||||
),
|
||||
),
|
||||
'ldap_next_attribute' => array(
|
||||
2 => array(
|
||||
'name' => 'ber_identifier',
|
||||
'5.2.4' => true,
|
||||
),
|
||||
),
|
||||
'mktime' => array(
|
||||
6 => array(
|
||||
'name' => 'is_dst',
|
||||
'5.1' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Handle case-insensitivity of function names.
|
||||
$this->removedFunctionParameters = $this->arrayKeysToLowercase($this->removedFunctionParameters);
|
||||
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$ignore = array(
|
||||
\T_DOUBLE_COLON => true,
|
||||
\T_OBJECT_OPERATOR => true,
|
||||
\T_FUNCTION => true,
|
||||
\T_CONST => true,
|
||||
);
|
||||
|
||||
$prevToken = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true);
|
||||
if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
|
||||
// Not a call to a PHP function.
|
||||
return;
|
||||
}
|
||||
|
||||
$function = $tokens[$stackPtr]['content'];
|
||||
$functionLc = strtolower($function);
|
||||
|
||||
if (isset($this->removedFunctionParameters[$functionLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parameters = $this->getFunctionCallParameters($phpcsFile, $stackPtr);
|
||||
$parameterCount = \count($parameters);
|
||||
if ($parameterCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the parameter count returned > 0, we know there will be valid open parenthesis.
|
||||
$openParenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
|
||||
$parameterOffsetFound = $parameterCount - 1;
|
||||
|
||||
foreach ($this->removedFunctionParameters[$functionLc] as $offset => $parameterDetails) {
|
||||
if ($offset <= $parameterOffsetFound) {
|
||||
if (isset($parameterDetails['callback']) && method_exists($this, $parameterDetails['callback'])) {
|
||||
if ($this->{$parameterDetails['callback']}($phpcsFile, $parameters[($offset + 1)]) === false) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $function,
|
||||
'nameLc' => $functionLc,
|
||||
'offset' => $offset,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $openParenthesis, $itemInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->removedFunctionParameters[$itemInfo['nameLc']][$itemInfo['offset']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of the non-PHP-version array keys used in a sub-array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('name', 'callback');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemArray Version and other information about the item.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorInfo(array $itemArray, array $itemInfo)
|
||||
{
|
||||
$errorInfo = parent::getErrorInfo($itemArray, $itemInfo);
|
||||
$errorInfo['paramName'] = $itemArray['name'];
|
||||
|
||||
return $errorInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the item name to be used for the creation of the error code.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
* @param array $errorInfo Detail information about an item.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getItemName(array $itemInfo, array $errorInfo)
|
||||
{
|
||||
return $itemInfo['name'] . '_' . $errorInfo['paramName'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The "%s" parameter for function %s() is ';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the error data before it's passed to PHPCS.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $data The error data array which was created.
|
||||
* @param array $itemInfo Base information about the item this error message applies to.
|
||||
* @param array $errorInfo Detail information about an item this error message applies to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function filterErrorData(array $data, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
array_shift($data);
|
||||
array_unshift($data, $errorInfo['paramName'], $itemInfo['name']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether curl_version() was passed the default CURLVERSION_NOW.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param array $parameter Parameter info array.
|
||||
*
|
||||
* @return bool True if the value was not CURLVERSION_NOW, false otherwise.
|
||||
*/
|
||||
protected function curlVersionInvalidValue(File $phpcsFile, array $parameter)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$raw = '';
|
||||
for ($i = $parameter['start']; $i <= $parameter['end']; $i++) {
|
||||
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$raw .= $tokens[$i]['content'];
|
||||
}
|
||||
|
||||
if ($raw !== 'CURLVERSION_NOW'
|
||||
&& $raw !== (string) \CURLVERSION_NOW
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+1104
File diff suppressed because it is too large
Load Diff
+350
@@ -0,0 +1,350 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionUse;
|
||||
|
||||
use PHPCompatibility\AbstractComplexVersionSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect missing required function parameters in calls to native PHP functions.
|
||||
*
|
||||
* Specifically when those function parameters are no longer required in more recent PHP versions.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://www.php.net/manual/en/doc.changelog.php
|
||||
*
|
||||
* @since 7.0.3
|
||||
* @since 7.1.0 Now extends the `AbstractComplexVersionSniff` instead of the base `Sniff` class.
|
||||
* @since 9.0.0 Renamed from `RequiredOptionalFunctionParametersSniff` to `RequiredToOptionalFunctionParametersSniff`.
|
||||
*/
|
||||
class RequiredToOptionalFunctionParametersSniff extends AbstractComplexVersionSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of function parameters, which were required in older versions and became optional later on.
|
||||
*
|
||||
* The array lists : version number with true (required) and false (optional).
|
||||
*
|
||||
* The index is the location of the parameter in the parameter list, starting at 0 !
|
||||
* If's sufficient to list the last version in which the parameter was still required.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $functionParameters = array(
|
||||
'array_merge' => array(
|
||||
0 => array(
|
||||
'name' => 'array(s) to merge',
|
||||
'7.3' => true,
|
||||
'7.4' => false,
|
||||
),
|
||||
),
|
||||
'array_merge_recursive' => array(
|
||||
0 => array(
|
||||
'name' => 'array(s) to merge',
|
||||
'7.3' => true,
|
||||
'7.4' => false,
|
||||
),
|
||||
),
|
||||
'array_push' => array(
|
||||
1 => array(
|
||||
'name' => 'element to push',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'array_unshift' => array(
|
||||
1 => array(
|
||||
'name' => 'element to prepend',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'bcscale' => array(
|
||||
0 => array(
|
||||
'name' => 'scale',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'ftp_fget' => array(
|
||||
3 => array(
|
||||
'name' => 'mode',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'ftp_fput' => array(
|
||||
3 => array(
|
||||
'name' => 'mode',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'ftp_get' => array(
|
||||
3 => array(
|
||||
'name' => 'mode',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'ftp_nb_fget' => array(
|
||||
3 => array(
|
||||
'name' => 'mode',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'ftp_nb_fput' => array(
|
||||
3 => array(
|
||||
'name' => 'mode',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'ftp_nb_get' => array(
|
||||
3 => array(
|
||||
'name' => 'mode',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'ftp_nb_put' => array(
|
||||
3 => array(
|
||||
'name' => 'mode',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'ftp_put' => array(
|
||||
3 => array(
|
||||
'name' => 'mode',
|
||||
'7.2' => true,
|
||||
'7.3' => false,
|
||||
),
|
||||
),
|
||||
'getenv' => array(
|
||||
0 => array(
|
||||
'name' => 'varname',
|
||||
'7.0' => true,
|
||||
'7.1' => false,
|
||||
),
|
||||
),
|
||||
'preg_match_all' => array(
|
||||
2 => array(
|
||||
'name' => 'matches',
|
||||
'5.3' => true,
|
||||
'5.4' => false,
|
||||
),
|
||||
),
|
||||
'stream_socket_enable_crypto' => array(
|
||||
2 => array(
|
||||
'name' => 'crypto_type',
|
||||
'5.5' => true,
|
||||
'5.6' => false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Handle case-insensitivity of function names.
|
||||
$this->functionParameters = $this->arrayKeysToLowercase($this->functionParameters);
|
||||
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$ignore = array(
|
||||
\T_DOUBLE_COLON => true,
|
||||
\T_OBJECT_OPERATOR => true,
|
||||
\T_FUNCTION => true,
|
||||
\T_CONST => true,
|
||||
\T_NEW => true,
|
||||
);
|
||||
|
||||
$prevToken = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true);
|
||||
if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
|
||||
// Not a call to a PHP function.
|
||||
return;
|
||||
}
|
||||
|
||||
$function = $tokens[$stackPtr]['content'];
|
||||
$functionLc = strtolower($function);
|
||||
|
||||
if (isset($this->functionParameters[$functionLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parameterCount = $this->getFunctionCallParameterCount($phpcsFile, $stackPtr);
|
||||
$openParenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
|
||||
|
||||
// If the parameter count returned > 0, we know there will be valid open parenthesis.
|
||||
if ($parameterCount === 0 && $tokens[$openParenthesis]['code'] !== \T_OPEN_PARENTHESIS) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parameterOffsetFound = $parameterCount - 1;
|
||||
|
||||
foreach ($this->functionParameters[$functionLc] as $offset => $parameterDetails) {
|
||||
if ($offset > $parameterOffsetFound) {
|
||||
$itemInfo = array(
|
||||
'name' => $function,
|
||||
'nameLc' => $functionLc,
|
||||
'offset' => $offset,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $openParenthesis, $itemInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether an error/warning should be thrown for an item based on collected information.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $errorInfo Detail information about an item.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldThrowError(array $errorInfo)
|
||||
{
|
||||
return ($errorInfo['requiredVersion'] !== '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->functionParameters[$itemInfo['nameLc']][$itemInfo['offset']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of the non-PHP-version array keys used in a sub-array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('name');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemArray Version and other information about the item.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorInfo(array $itemArray, array $itemInfo)
|
||||
{
|
||||
$errorInfo = array(
|
||||
'paramName' => '',
|
||||
'requiredVersion' => '',
|
||||
);
|
||||
|
||||
$versionArray = $this->getVersionArray($itemArray);
|
||||
|
||||
if (empty($versionArray) === false) {
|
||||
foreach ($versionArray as $version => $required) {
|
||||
if ($required === true && $this->supportsBelow($version) === true) {
|
||||
$errorInfo['requiredVersion'] = $version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$errorInfo['paramName'] = $itemArray['name'];
|
||||
|
||||
return $errorInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The "%s" parameter for function %s() is missing, but was required for PHP version %s and lower';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the error or warning for this item.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the relevant token in
|
||||
* the stack.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
* @param array $errorInfo Array with detail (version) information
|
||||
* relevant to the item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addError(File $phpcsFile, $stackPtr, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
$error = $this->getErrorMsgTemplate();
|
||||
$errorCode = $this->stringToErrorCode($itemInfo['name'] . '_' . $errorInfo['paramName']) . 'Missing';
|
||||
$data = array(
|
||||
$errorInfo['paramName'],
|
||||
$itemInfo['name'],
|
||||
$errorInfo['requiredVersion'],
|
||||
);
|
||||
|
||||
$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Generators;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* As of PHP 7.0, a `return` statement can be used within a generator for a final expression to be returned.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.generator-return-expressions
|
||||
* @link https://wiki.php.net/rfc/generator-return-expressions
|
||||
* @link https://www.php.net/manual/en/language.generators.syntax.php
|
||||
*
|
||||
* @since 8.2.0
|
||||
*/
|
||||
class NewGeneratorReturnSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Scope conditions within which a yield can exist.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $validConditions = array(
|
||||
\T_FUNCTION => \T_FUNCTION,
|
||||
\T_CLOSURE => \T_CLOSURE,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$targets = array(
|
||||
\T_YIELD,
|
||||
);
|
||||
|
||||
/*
|
||||
* The `yield` keyword was introduced in PHP 5.5 with the token T_YIELD.
|
||||
* The `yield from` keyword was introduced in PHP 7.0 and tokenizes as
|
||||
* "T_YIELD T_WHITESPACE T_STRING".
|
||||
*
|
||||
* Pre-PHPCS 3.1.0, the T_YIELD token was not correctly back-filled for PHP < 5.5.
|
||||
* Also, as of PHPCS 3.1.0, the PHPCS tokenizer adds a new T_YIELD_FROM
|
||||
* token.
|
||||
*
|
||||
* So for PHP 5.3-5.4 icw PHPCS < 3.1.0, we need to look for T_STRING with content "yield".
|
||||
* For PHP 5.5+ we need to look for T_YIELD.
|
||||
* For PHPCS 3.1.0+, we also need to look for T_YIELD_FROM.
|
||||
*/
|
||||
if (version_compare(\PHP_VERSION_ID, '50500', '<') === true
|
||||
&& version_compare(PHPCSHelper::getVersion(), '3.1.0', '<') === true
|
||||
) {
|
||||
$targets[] = \T_STRING;
|
||||
}
|
||||
|
||||
if (\defined('T_YIELD_FROM')) {
|
||||
$targets[] = \T_YIELD_FROM;
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void|int Void or a stack pointer to skip forward.
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.6') !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === \T_STRING
|
||||
&& $tokens[$stackPtr]['content'] !== 'yield'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($tokens[$stackPtr]['conditions']) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Walk the condition from inner to outer to see if we can find a valid function/closure scope.
|
||||
$conditions = array_reverse($tokens[$stackPtr]['conditions'], true);
|
||||
foreach ($conditions as $ptr => $type) {
|
||||
if (isset($this->validConditions[$type]) === true) {
|
||||
$function = $ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($function) === false) {
|
||||
// Yield outside function scope, fatal error, but not our concern.
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($tokens[$function]['scope_opener'], $tokens[$function]['scope_closer']) === false) {
|
||||
// Can't reliably determine start/end of function scope.
|
||||
return;
|
||||
}
|
||||
|
||||
$targets = array(\T_RETURN, \T_CLOSURE, \T_FUNCTION, \T_CLASS);
|
||||
if (\defined('T_ANON_CLASS')) {
|
||||
$targets[] = \T_ANON_CLASS;
|
||||
}
|
||||
|
||||
$current = $tokens[$function]['scope_opener'];
|
||||
|
||||
while (($current = $phpcsFile->findNext($targets, ($current + 1), $tokens[$function]['scope_closer'])) !== false) {
|
||||
if ($tokens[$current]['code'] === \T_RETURN) {
|
||||
$phpcsFile->addError(
|
||||
'Returning a final expression from a generator was not supported in PHP 5.6 or earlier',
|
||||
$current,
|
||||
'ReturnFound'
|
||||
);
|
||||
|
||||
return $tokens[$function]['scope_closer'];
|
||||
}
|
||||
|
||||
// Found a nested scope in which return can exist without problems.
|
||||
if (isset($tokens[$current]['scope_closer'])) {
|
||||
// Skip past the nested scope.
|
||||
$current = $tokens[$current]['scope_closer'];
|
||||
}
|
||||
}
|
||||
|
||||
// Don't examine this function again.
|
||||
return $tokens[$function]['scope_closer'];
|
||||
}
|
||||
}
|
||||
+855
@@ -0,0 +1,855 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\IniDirectives;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect the use of new INI directives through `ini_set()` or `ini_get()`.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://www.php.net/manual/en/ini.list.php
|
||||
* @link https://www.php.net/manual/en/ini.core.php
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 7.0.7 When a new directive is used with `ini_set()`, the sniff will now throw an error
|
||||
* instead of a warning.
|
||||
* @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class..
|
||||
*/
|
||||
class NewIniDirectivesSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
/**
|
||||
* A list of new INI directives
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If's sufficient to list the first version where the ini directive appears.
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 7.0.3 Support for 'alternative' has been added.
|
||||
*
|
||||
* @var array(string)
|
||||
*/
|
||||
protected $newIniDirectives = array(
|
||||
'auto_globals_jit' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'com.code_page' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'date.default_latitude' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'date.default_longitude' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'date.sunrise_zenith' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'date.sunset_zenith' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'ibase.default_charset' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'ibase.default_db' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'mail.force_extra_parameters' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'mime_magic.debug' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'mysqli.max_links' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'mysqli.default_port' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'mysqli.default_socket' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'mysqli.default_host' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'mysqli.default_user' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'mysqli.default_pw' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'report_zend_debug' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'session.hash_bits_per_character' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'session.hash_function' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'soap.wsdl_cache_dir' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'soap.wsdl_cache_enabled' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'soap.wsdl_cache_ttl' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'sqlite.assoc_case' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'tidy.clean_output' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'tidy.default_config' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'zend.ze1_compatibility_mode' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
|
||||
'date.timezone' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'detect_unicode' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'fbsql.batchsize' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
'alternative' => 'fbsql.batchSize',
|
||||
),
|
||||
'realpath_cache_size' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'realpath_cache_ttl' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
|
||||
'mbstring.strict_detection' => array(
|
||||
'5.1.1' => false,
|
||||
'5.1.2' => true,
|
||||
),
|
||||
'mssql.charset' => array(
|
||||
'5.1.1' => false,
|
||||
'5.1.2' => true,
|
||||
),
|
||||
|
||||
'gd.jpeg_ignore_warning' => array(
|
||||
'5.1.2' => false,
|
||||
'5.1.3' => true,
|
||||
),
|
||||
|
||||
'fbsql.show_timestamp_decimals' => array(
|
||||
'5.1.4' => false,
|
||||
'5.1.5' => true,
|
||||
),
|
||||
'soap.wsdl_cache' => array(
|
||||
'5.1.4' => false,
|
||||
'5.1.5' => true,
|
||||
),
|
||||
'soap.wsdl_cache_limit' => array(
|
||||
'5.1.4' => false,
|
||||
'5.1.5' => true,
|
||||
),
|
||||
|
||||
'allow_url_include' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'filter.default' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'filter.default_flags' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'pcre.backtrack_limit' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'pcre.recursion_limit' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
'session.cookie_httponly' => array(
|
||||
'5.1' => false,
|
||||
'5.2' => true,
|
||||
),
|
||||
|
||||
'cgi.check_shebang_line' => array(
|
||||
'5.2.0' => false,
|
||||
'5.2.1' => true,
|
||||
),
|
||||
|
||||
'max_input_nesting_level' => array(
|
||||
'5.2.2' => false,
|
||||
'5.2.3' => true,
|
||||
),
|
||||
|
||||
'mysqli.allow_local_infile' => array(
|
||||
'5.2.3' => false,
|
||||
'5.2.4' => true,
|
||||
),
|
||||
|
||||
'max_file_uploads' => array(
|
||||
'5.2.11' => false,
|
||||
'5.2.12' => true,
|
||||
),
|
||||
|
||||
'cgi.discard_path' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'exit_on_timeout' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'intl.default_locale' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'intl.error_level' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'mail.add_x_header' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'mail.log' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'mbstring.http_output_conv_mimetype' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'mysqli.allow_persistent' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'mysqli.cache_size' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'mysqli.max_persistent' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'mysqlnd.collect_memory_statistics' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'mysqlnd.collect_statistics' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'mysqlnd.debug' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'mysqlnd.net_read_buffer_size' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'odbc.default_cursortype' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'request_order' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'user_ini.cache_ttl' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'user_ini.filename' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'zend.enable_gc' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
|
||||
'curl.cainfo' => array(
|
||||
'5.3.6' => false,
|
||||
'5.3.7' => true,
|
||||
),
|
||||
|
||||
'max_input_vars' => array(
|
||||
'5.3.8' => false,
|
||||
'5.3.9' => true,
|
||||
),
|
||||
|
||||
'sqlite3.extension_dir' => array(
|
||||
'5.3.10' => false,
|
||||
'5.3.11' => true,
|
||||
),
|
||||
|
||||
'cli.pager' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'cli.prompt' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'cli_server.color' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'enable_post_data_reading' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'mysqlnd.mempool_default_size' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'mysqlnd.net_cmd_buffer_size' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'mysqlnd.net_read_timeout' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'phar.cache_list' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'session.upload_progress.enabled' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'session.upload_progress.cleanup' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'session.upload_progress.name' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'session.upload_progress.freq' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'session.upload_progress.min_freq' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'session.upload_progress.prefix' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'windows_show_crt_warning' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'zend.detect_unicode' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => 'detect_unicode',
|
||||
),
|
||||
'zend.multibyte' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'zend.script_encoding' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'zend.signal_check' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'mysqlnd.log_mask' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
|
||||
'intl.use_exceptions' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'mysqlnd.sha256_server_public_key' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'mysqlnd.trace_alloc' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'sys_temp_dir' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'xsl.security_prefs' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.enable' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.enable_cli' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.memory_consumption' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.interned_strings_buffer' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.max_accelerated_files' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.max_wasted_percentage' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.use_cwd' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.validate_timestamps' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.revalidate_freq' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.revalidate_path' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.save_comments' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.load_comments' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.fast_shutdown' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.enable_file_override' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.optimization_level' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.inherited_hack' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.dups_fix' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.blacklist_filename' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.max_file_size' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.consistency_checks' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.force_restart_timeout' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.error_log' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.log_verbosity_level' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.preferred_memory_model' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.protect_memory' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.mmap_base' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.restrict_api' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.file_update_protection' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.huge_code_pages' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.lockfile_path' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'opcache.opt_debug_level' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
|
||||
'session.use_strict_mode' => array(
|
||||
'5.5.1' => false,
|
||||
'5.5.2' => true,
|
||||
),
|
||||
|
||||
'mysqli.rollback_on_cached_plink' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => true,
|
||||
),
|
||||
|
||||
'assert.exception' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'pcre.jit' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'session.lazy_write' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'zend.assertions' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'opcache.file_cache' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'opcache.file_cache_only' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'opcache.file_cache_consistency_checks' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'opcache.file_cache_fallback' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
), // Windows only.
|
||||
|
||||
'opcache.validate_permission' => array(
|
||||
'7.0.13' => false,
|
||||
'7.0.14' => true,
|
||||
),
|
||||
'opcache.validate_root' => array(
|
||||
'7.0.13' => false,
|
||||
'7.0.14' => true,
|
||||
),
|
||||
|
||||
'hard_timeout' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'session.sid_length' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'session.sid_bits_per_character' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'session.trans_sid_hosts' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'session.trans_sid_tags' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'url_rewriter.hosts' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
|
||||
// Introduced in PHP 7.1.25, 7.2.13, 7.3.0.
|
||||
'imap.enable_insecure_rsh' => array(
|
||||
'7.1.24' => false,
|
||||
'7.1.25' => true,
|
||||
),
|
||||
|
||||
'syslog.facility' => array(
|
||||
'7.2' => false,
|
||||
'7.3' => true,
|
||||
),
|
||||
'syslog.filter' => array(
|
||||
'7.2' => false,
|
||||
'7.3' => true,
|
||||
),
|
||||
'syslog.ident' => array(
|
||||
'7.2' => false,
|
||||
'7.3' => true,
|
||||
),
|
||||
'session.cookie_samesite' => array(
|
||||
'7.2' => false,
|
||||
'7.3' => true,
|
||||
),
|
||||
|
||||
'ffi.enable' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
'ffi.preload' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
'opcache.cache_id' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
'opcache.preload' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
'zend.exception_ignore_args' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$ignore = array(
|
||||
\T_DOUBLE_COLON => true,
|
||||
\T_OBJECT_OPERATOR => true,
|
||||
\T_FUNCTION => true,
|
||||
\T_CONST => true,
|
||||
);
|
||||
|
||||
$prevToken = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true);
|
||||
if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
|
||||
// Not a call to a PHP function.
|
||||
return;
|
||||
}
|
||||
|
||||
$functionLc = strtolower($tokens[$stackPtr]['content']);
|
||||
if (isset($this->iniFunctions[$functionLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$iniToken = $this->getFunctionCallParameter($phpcsFile, $stackPtr, $this->iniFunctions[$functionLc]);
|
||||
if ($iniToken === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filteredToken = $this->stripQuotes($iniToken['raw']);
|
||||
if (isset($this->newIniDirectives[$filteredToken]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $filteredToken,
|
||||
'functionLc' => $functionLc,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $iniToken['end'], $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newIniDirectives[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of the non-PHP-version array keys used in a sub-array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('alternative');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemArray Version and other information about the item.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorInfo(array $itemArray, array $itemInfo)
|
||||
{
|
||||
$errorInfo = parent::getErrorInfo($itemArray, $itemInfo);
|
||||
$errorInfo['alternative'] = '';
|
||||
|
||||
if (isset($itemArray['alternative']) === true) {
|
||||
$errorInfo['alternative'] = $itemArray['alternative'];
|
||||
}
|
||||
|
||||
// Lower error level to warning if the function used was ini_get.
|
||||
if ($errorInfo['error'] === true && $itemInfo['functionLc'] === 'ini_get') {
|
||||
$errorInfo['error'] = false;
|
||||
}
|
||||
|
||||
return $errorInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return "INI directive '%s' is not present in PHP version %s or earlier";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow for concrete child classes to filter the error message before it's passed to PHPCS.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param string $error The error message which was created.
|
||||
* @param array $itemInfo Base information about the item this error message applies to.
|
||||
* @param array $errorInfo Detail information about an item this error message applies to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function filterErrorMsg($error, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
if ($errorInfo['alternative'] !== '') {
|
||||
$error .= ". This directive was previously called '%s'.";
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow for concrete child classes to filter the error data before it's passed to PHPCS.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $data The error data array which was created.
|
||||
* @param array $itemInfo Base information about the item this error message applies to.
|
||||
* @param array $errorInfo Detail information about an item this error message applies to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function filterErrorData(array $data, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
if ($errorInfo['alternative'] !== '') {
|
||||
$data[] = $errorInfo['alternative'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\IniDirectives;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect the use of deprecated and removed INI directives through `ini_set()` or `ini_get()`.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://www.php.net/manual/en/ini.list.php
|
||||
* @link https://www.php.net/manual/en/ini.core.php
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 7.0.0 This sniff now throws a warning (deprecated) or an error (removed) depending
|
||||
* on the `testVersion` set. Previously it would always throw a warning.
|
||||
* @since 7.0.1 The sniff will now only throw warnings for `ini_get()`.
|
||||
* @since 7.1.0 Now extends the `AbstractRemovedFeatureSniff` instead of the base `Sniff` class.
|
||||
* @since 9.0.0 Renamed from `DeprecatedIniDirectivesSniff` to `RemovedIniDirectivesSniff`.
|
||||
*/
|
||||
class RemovedIniDirectivesSniff extends AbstractRemovedFeatureSniff
|
||||
{
|
||||
/**
|
||||
* A list of deprecated/removed INI directives.
|
||||
*
|
||||
* The array lists : version number with false (deprecated) and true (removed).
|
||||
* If's sufficient to list the first version where the ini directive was deprecated/removed.
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 7.0.3 Support for 'alternative' has been added.
|
||||
*
|
||||
* @var array(string)
|
||||
*/
|
||||
protected $deprecatedIniDirectives = array(
|
||||
'fbsql.batchSize' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => 'fbsql.batchsize',
|
||||
),
|
||||
'pfpro.defaulthost' => array(
|
||||
'5.1' => true,
|
||||
),
|
||||
'pfpro.defaultport' => array(
|
||||
'5.1' => true,
|
||||
),
|
||||
'pfpro.defaulttimeout' => array(
|
||||
'5.1' => true,
|
||||
),
|
||||
'pfpro.proxyaddress' => array(
|
||||
'5.1' => true,
|
||||
),
|
||||
'pfpro.proxyport' => array(
|
||||
'5.1' => true,
|
||||
),
|
||||
'pfpro.proxylogon' => array(
|
||||
'5.1' => true,
|
||||
),
|
||||
'pfpro.proxypassword' => array(
|
||||
'5.1' => true,
|
||||
),
|
||||
|
||||
'ifx.allow_persistent' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
'ifx.blobinfile' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
'ifx.byteasvarchar' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
'ifx.charasvarchar' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
'ifx.default_host' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
'ifx.default_password' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
'ifx.default_user' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
'ifx.max_links' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
'ifx.max_persistent' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
'ifx.nullformat' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
'ifx.textasvarchar' => array(
|
||||
'5.2.1' => true,
|
||||
),
|
||||
|
||||
'zend.ze1_compatibility_mode' => array(
|
||||
'5.3' => true,
|
||||
),
|
||||
|
||||
'allow_call_time_pass_reference' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'define_syslog_variables' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'detect_unicode' => array(
|
||||
'5.4' => true,
|
||||
'alternative' => 'zend.detect_unicode',
|
||||
),
|
||||
'highlight.bg' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'magic_quotes_gpc' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'magic_quotes_runtime' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'magic_quotes_sybase' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'mbstring.script_encoding' => array(
|
||||
'5.4' => true,
|
||||
'alternative' => 'zend.script_encoding',
|
||||
),
|
||||
'register_globals' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'register_long_arrays' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'safe_mode' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'safe_mode_allowed_env_vars' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'safe_mode_exec_dir' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'safe_mode_gid' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'safe_mode_include_dir' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'safe_mode_protected_env_vars' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'session.bug_compat_42' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'session.bug_compat_warn' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'y2k_compliance' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
|
||||
'always_populate_raw_post_data' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'iconv.input_encoding' => array(
|
||||
'5.6' => false,
|
||||
),
|
||||
'iconv.output_encoding' => array(
|
||||
'5.6' => false,
|
||||
),
|
||||
'iconv.internal_encoding' => array(
|
||||
'5.6' => false,
|
||||
),
|
||||
'mbstring.http_input' => array(
|
||||
'5.6' => false,
|
||||
),
|
||||
'mbstring.http_output' => array(
|
||||
'5.6' => false,
|
||||
),
|
||||
'mbstring.internal_encoding' => array(
|
||||
'5.6' => false,
|
||||
),
|
||||
|
||||
'asp_tags' => array(
|
||||
'7.0' => true,
|
||||
),
|
||||
'xsl.security_prefs' => array(
|
||||
'7.0' => true,
|
||||
),
|
||||
'opcache.load_comments' => array(
|
||||
'7.0' => true,
|
||||
),
|
||||
|
||||
'mcrypt.algorithms_dir' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'mcrypt.modes_dir' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
'session.entropy_file' => array(
|
||||
'7.1' => true,
|
||||
),
|
||||
'session.entropy_length' => array(
|
||||
'7.1' => true,
|
||||
),
|
||||
'session.hash_function' => array(
|
||||
'7.1' => true,
|
||||
),
|
||||
'session.hash_bits_per_character' => array(
|
||||
'7.1' => true,
|
||||
),
|
||||
|
||||
'mbstring.func_overload' => array(
|
||||
'7.2' => false,
|
||||
),
|
||||
'sql.safe_mode' => array(
|
||||
'7.2' => true,
|
||||
),
|
||||
'track_errors' => array(
|
||||
'7.2' => false,
|
||||
),
|
||||
'opcache.fast_shutdown' => array(
|
||||
'7.2' => true,
|
||||
),
|
||||
|
||||
'birdstep.max_links' => array(
|
||||
'7.3' => true,
|
||||
),
|
||||
'opcache.inherited_hack' => array(
|
||||
'5.3' => false, // Soft deprecated, i.e. ignored.
|
||||
'7.3' => true,
|
||||
),
|
||||
'pdo_odbc.db2_instance_name' => array(
|
||||
'7.3' => false, // Has been marked as deprecated in the manual from before this time. Now hard-deprecated.
|
||||
),
|
||||
|
||||
'allow_url_include' => array(
|
||||
'7.4' => false,
|
||||
),
|
||||
'ibase.allow_persistent' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'ibase.max_persistent' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'ibase.max_links' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'ibase.default_db' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'ibase.default_user' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'ibase.default_password' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'ibase.default_charset' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'ibase.timestampformat' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'ibase.dateformat' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
'ibase.timeformat' => array(
|
||||
'7.4' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$ignore = array(
|
||||
\T_DOUBLE_COLON => true,
|
||||
\T_OBJECT_OPERATOR => true,
|
||||
\T_FUNCTION => true,
|
||||
\T_CONST => true,
|
||||
);
|
||||
|
||||
$prevToken = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true);
|
||||
if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
|
||||
// Not a call to a PHP function.
|
||||
return;
|
||||
}
|
||||
|
||||
$functionLc = strtolower($tokens[$stackPtr]['content']);
|
||||
if (isset($this->iniFunctions[$functionLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$iniToken = $this->getFunctionCallParameter($phpcsFile, $stackPtr, $this->iniFunctions[$functionLc]);
|
||||
if ($iniToken === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filteredToken = $this->stripQuotes($iniToken['raw']);
|
||||
if (isset($this->deprecatedIniDirectives[$filteredToken]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $filteredToken,
|
||||
'functionLc' => $functionLc,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $iniToken['end'], $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->deprecatedIniDirectives[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemArray Version and other information about the item.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorInfo(array $itemArray, array $itemInfo)
|
||||
{
|
||||
$errorInfo = parent::getErrorInfo($itemArray, $itemInfo);
|
||||
|
||||
// Lower error level to warning if the function used was ini_get.
|
||||
if ($errorInfo['error'] === true && $itemInfo['functionLc'] === 'ini_get') {
|
||||
$errorInfo['error'] = false;
|
||||
}
|
||||
|
||||
return $errorInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return "INI directive '%s' is ";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for suggesting an alternative for a specific sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAlternativeOptionTemplate()
|
||||
{
|
||||
return str_replace('%s', "'%s'", parent::getAlternativeOptionTemplate());
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\InitialValue;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect declaration of constants using the `const` keyword with a (constant) array value
|
||||
* as supported since PHP 5.6.
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/const_scalar_exprs
|
||||
* @link https://www.php.net/manual/en/language.constants.syntax.php
|
||||
*
|
||||
* @since 7.1.4
|
||||
* @since 9.0.0 Renamed from `ConstantArraysUsingConstSniff` to `NewConstantArraysUsingConstSniff`.
|
||||
*/
|
||||
class NewConstantArraysUsingConstSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_CONST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.5') !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$find = array(
|
||||
\T_ARRAY => \T_ARRAY,
|
||||
\T_OPEN_SHORT_ARRAY => \T_OPEN_SHORT_ARRAY,
|
||||
);
|
||||
|
||||
while (($hasArray = $phpcsFile->findNext($find, ($stackPtr + 1), null, false, null, true)) !== false) {
|
||||
$phpcsFile->addError(
|
||||
'Constant arrays using the "const" keyword are not allowed in PHP 5.5 or earlier',
|
||||
$hasArray,
|
||||
'Found'
|
||||
);
|
||||
|
||||
// Skip past the content of the array.
|
||||
$stackPtr = $hasArray;
|
||||
if ($tokens[$hasArray]['code'] === \T_OPEN_SHORT_ARRAY && isset($tokens[$hasArray]['bracket_closer'])) {
|
||||
$stackPtr = $tokens[$hasArray]['bracket_closer'];
|
||||
} elseif ($tokens[$hasArray]['code'] === \T_ARRAY && isset($tokens[$hasArray]['parenthesis_closer'])) {
|
||||
$stackPtr = $tokens[$hasArray]['parenthesis_closer'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\InitialValue;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect declaration of constants using `define()` with a (constant) array value
|
||||
* as supported since PHP 7.0.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.define-array
|
||||
* @link https://www.php.net/manual/en/language.constants.syntax.php
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 9.0.0 Renamed from `ConstantArraysUsingDefineSniff` to `NewConstantArraysUsingDefineSniff`.
|
||||
*/
|
||||
class NewConstantArraysUsingDefineSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.6') !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$ignore = array(
|
||||
\T_DOUBLE_COLON => true,
|
||||
\T_OBJECT_OPERATOR => true,
|
||||
\T_FUNCTION => true,
|
||||
\T_CONST => true,
|
||||
);
|
||||
|
||||
$prevToken = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true);
|
||||
if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
|
||||
// Not a call to a PHP function.
|
||||
return;
|
||||
}
|
||||
|
||||
$functionLc = strtolower($tokens[$stackPtr]['content']);
|
||||
if ($functionLc !== 'define') {
|
||||
return;
|
||||
}
|
||||
|
||||
$secondParam = $this->getFunctionCallParameter($phpcsFile, $stackPtr, 2);
|
||||
if (isset($secondParam['start'], $secondParam['end']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$targetNestingLevel = 0;
|
||||
if (isset($tokens[$secondParam['start']]['nested_parenthesis'])) {
|
||||
$targetNestingLevel = \count($tokens[$secondParam['start']]['nested_parenthesis']);
|
||||
}
|
||||
|
||||
$array = $phpcsFile->findNext(array(\T_ARRAY, \T_OPEN_SHORT_ARRAY), $secondParam['start'], ($secondParam['end'] + 1));
|
||||
if ($array !== false) {
|
||||
if ((isset($tokens[$array]['nested_parenthesis']) === false && $targetNestingLevel === 0) || \count($tokens[$array]['nested_parenthesis']) === $targetNestingLevel) {
|
||||
$phpcsFile->addError(
|
||||
'Constant arrays using define are not allowed in PHP 5.6 or earlier',
|
||||
$array,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+556
@@ -0,0 +1,556 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\InitialValue;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect constant scalar expressions being used to set an initial value.
|
||||
*
|
||||
* Since PHP 5.6, it is now possible to provide a scalar expression involving
|
||||
* numeric and string literals and/or constants in contexts where PHP previously
|
||||
* expected a static value, such as constant and property declarations and
|
||||
* default values for function parameters.
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration56.new-features.php#migration56.new-features.const-scalar-exprs
|
||||
* @link https://wiki.php.net/rfc/const_scalar_exprs
|
||||
*
|
||||
* @since 8.2.0
|
||||
*/
|
||||
class NewConstantScalarExpressionsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Error message.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const ERROR_PHRASE = 'Constant scalar expressions are not allowed %s in PHP 5.5 or earlier.';
|
||||
|
||||
/**
|
||||
* Partial error phrases to be used in combination with the error message constant.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $errorPhrases = array(
|
||||
'const' => 'when defining constants using the const keyword',
|
||||
'property' => 'in property declarations',
|
||||
'staticvar' => 'in static variable declarations',
|
||||
'default' => 'in default function arguments',
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokens which were allowed to be used in these declarations prior to PHP 5.6.
|
||||
*
|
||||
* This list will be enriched in the setProperties() method.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $safeOperands = array(
|
||||
\T_LNUMBER => \T_LNUMBER,
|
||||
\T_DNUMBER => \T_DNUMBER,
|
||||
\T_CONSTANT_ENCAPSED_STRING => \T_CONSTANT_ENCAPSED_STRING,
|
||||
\T_TRUE => \T_TRUE,
|
||||
\T_FALSE => \T_FALSE,
|
||||
\T_NULL => \T_NULL,
|
||||
|
||||
\T_LINE => \T_LINE,
|
||||
\T_FILE => \T_FILE,
|
||||
\T_DIR => \T_DIR,
|
||||
\T_FUNC_C => \T_FUNC_C,
|
||||
\T_CLASS_C => \T_CLASS_C,
|
||||
\T_TRAIT_C => \T_TRAIT_C,
|
||||
\T_METHOD_C => \T_METHOD_C,
|
||||
\T_NS_C => \T_NS_C,
|
||||
|
||||
// Special cases:
|
||||
\T_NS_SEPARATOR => \T_NS_SEPARATOR,
|
||||
/*
|
||||
* This can be neigh anything, but for any usage except constants,
|
||||
* the T_STRING will be combined with non-allowed tokens, so we should be good.
|
||||
*/
|
||||
\T_STRING => \T_STRING,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Set the properties up only once.
|
||||
$this->setProperties();
|
||||
|
||||
return array(
|
||||
\T_CONST,
|
||||
\T_VARIABLE,
|
||||
\T_FUNCTION,
|
||||
\T_CLOSURE,
|
||||
\T_STATIC,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make some adjustments to the $safeOperands property.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setProperties()
|
||||
{
|
||||
$this->safeOperands += Tokens::$heredocTokens;
|
||||
$this->safeOperands += Tokens::$emptyTokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('5.5') !== true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void|int Null or integer stack pointer to skip forward.
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->bowOutEarly() === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
switch ($tokens[$stackPtr]['type']) {
|
||||
case 'T_FUNCTION':
|
||||
case 'T_CLOSURE':
|
||||
$params = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr);
|
||||
if (empty($params)) {
|
||||
// No parameters.
|
||||
return;
|
||||
}
|
||||
|
||||
$funcToken = $tokens[$stackPtr];
|
||||
|
||||
if (isset($funcToken['parenthesis_owner'], $funcToken['parenthesis_opener'], $funcToken['parenthesis_closer']) === false
|
||||
|| $funcToken['parenthesis_owner'] !== $stackPtr
|
||||
|| isset($tokens[$funcToken['parenthesis_opener']], $tokens[$funcToken['parenthesis_closer']]) === false
|
||||
) {
|
||||
// Hmm.. something is going wrong as these should all be available & valid.
|
||||
return;
|
||||
}
|
||||
|
||||
$opener = $funcToken['parenthesis_opener'];
|
||||
$closer = $funcToken['parenthesis_closer'];
|
||||
|
||||
// Which nesting level is the one we are interested in ?
|
||||
$nestedParenthesisCount = 1;
|
||||
if (isset($tokens[$opener]['nested_parenthesis'])) {
|
||||
$nestedParenthesisCount += \count($tokens[$opener]['nested_parenthesis']);
|
||||
}
|
||||
|
||||
foreach ($params as $param) {
|
||||
if (isset($param['default']) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$end = $param['token'];
|
||||
while (($end = $phpcsFile->findNext(array(\T_COMMA, \T_CLOSE_PARENTHESIS), ($end + 1), ($closer + 1))) !== false) {
|
||||
$maybeSkipTo = $this->isRealEndOfDeclaration($tokens, $end, $nestedParenthesisCount);
|
||||
if ($maybeSkipTo !== true) {
|
||||
$end = $maybeSkipTo;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore closing parenthesis/bracket if not 'ours'.
|
||||
if ($tokens[$end]['code'] === \T_CLOSE_PARENTHESIS && $end !== $closer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ok, we've found the end of the param default value declaration.
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->isValidAssignment($phpcsFile, $param['token'], $end) === false) {
|
||||
$this->throwError($phpcsFile, $param['token'], 'default', $param['content']);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* No need for the sniff to be triggered by the T_VARIABLEs in the function
|
||||
* definition as we've already examined them above, so let's skip over them.
|
||||
*/
|
||||
return $closer;
|
||||
|
||||
case 'T_VARIABLE':
|
||||
case 'T_STATIC':
|
||||
case 'T_CONST':
|
||||
$type = 'const';
|
||||
|
||||
// Filter out non-property declarations.
|
||||
if ($tokens[$stackPtr]['code'] === \T_VARIABLE) {
|
||||
if ($this->isClassProperty($phpcsFile, $stackPtr) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type = 'property';
|
||||
|
||||
// Move back one token to have the same starting point as the others.
|
||||
$stackPtr = ($stackPtr - 1);
|
||||
}
|
||||
|
||||
// Filter out late static binding and class properties.
|
||||
if ($tokens[$stackPtr]['code'] === \T_STATIC) {
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
|
||||
if ($next === false || $tokens[$next]['code'] !== \T_VARIABLE) {
|
||||
// Late static binding.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isClassProperty($phpcsFile, $next) === true) {
|
||||
// Class properties are examined based on the T_VARIABLE token.
|
||||
return;
|
||||
}
|
||||
unset($next);
|
||||
|
||||
$type = 'staticvar';
|
||||
}
|
||||
|
||||
$endOfStatement = $phpcsFile->findNext(array(\T_SEMICOLON, \T_CLOSE_TAG), ($stackPtr + 1));
|
||||
if ($endOfStatement === false) {
|
||||
// No semi-colon - live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
$targetNestingLevel = 0;
|
||||
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
|
||||
$targetNestingLevel = \count($tokens[$stackPtr]['nested_parenthesis']);
|
||||
}
|
||||
|
||||
// Examine each variable/constant in multi-declarations.
|
||||
$start = $stackPtr;
|
||||
$end = $stackPtr;
|
||||
while (($end = $phpcsFile->findNext(array(\T_COMMA, \T_SEMICOLON, \T_OPEN_SHORT_ARRAY, \T_CLOSE_TAG), ($end + 1), ($endOfStatement + 1))) !== false) {
|
||||
|
||||
$maybeSkipTo = $this->isRealEndOfDeclaration($tokens, $end, $targetNestingLevel);
|
||||
if ($maybeSkipTo !== true) {
|
||||
$end = $maybeSkipTo;
|
||||
continue;
|
||||
}
|
||||
|
||||
$start = $phpcsFile->findNext(Tokens::$emptyTokens, ($start + 1), $end, true);
|
||||
if ($start === false
|
||||
|| ($tokens[$stackPtr]['code'] === \T_CONST && $tokens[$start]['code'] !== \T_STRING)
|
||||
|| ($tokens[$stackPtr]['code'] !== \T_CONST && $tokens[$start]['code'] !== \T_VARIABLE)
|
||||
) {
|
||||
// Shouldn't be possible.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->isValidAssignment($phpcsFile, $start, $end) === false) {
|
||||
// Create the "found" snippet.
|
||||
$content = '';
|
||||
$tokenCount = ($end - $start);
|
||||
if ($tokenCount < 20) {
|
||||
// Prevent large arrays from being added to the error message.
|
||||
$content = $phpcsFile->getTokensAsString($start, ($tokenCount + 1));
|
||||
}
|
||||
|
||||
$this->throwError($phpcsFile, $start, $type, $content);
|
||||
}
|
||||
|
||||
$start = $end;
|
||||
}
|
||||
|
||||
// Skip to the end of the statement to prevent duplicate messages for multi-declarations.
|
||||
return $endOfStatement;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is a value declared and is the value declared valid pre-PHP 5.6 ?
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
* @param int $end The end of the value definition.
|
||||
* This will normally be a comma or semi-colon.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValidAssignment(File $phpcsFile, $stackPtr, $end)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), $end, true);
|
||||
if ($next === false || $tokens[$next]['code'] !== \T_EQUAL) {
|
||||
// No value assigned.
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->isStaticValue($phpcsFile, $tokens, ($next + 1), ($end - 1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is a value declared and is the value declared constant as accepted in PHP 5.5 and lower ?
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param array $tokens The token stack of the current file.
|
||||
* @param int $start The stackPtr from which to start examining.
|
||||
* @param int $end The end of the value definition (inclusive),
|
||||
* i.e. this token will be examined as part of
|
||||
* the snippet.
|
||||
* @param int $nestedArrays Optional. Array nesting level when examining
|
||||
* the content of an array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isStaticValue(File $phpcsFile, $tokens, $start, $end, $nestedArrays = 0)
|
||||
{
|
||||
$nextNonSimple = $phpcsFile->findNext($this->safeOperands, $start, ($end + 1), true);
|
||||
if ($nextNonSimple === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* OK, so we have at least one token which needs extra examination.
|
||||
*/
|
||||
switch ($tokens[$nextNonSimple]['code']) {
|
||||
case \T_MINUS:
|
||||
case \T_PLUS:
|
||||
if ($this->isNumber($phpcsFile, $start, $end, true) !== false) {
|
||||
// Int or float with sign.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case \T_NAMESPACE:
|
||||
case \T_PARENT:
|
||||
case \T_SELF:
|
||||
case \T_DOUBLE_COLON:
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonSimple + 1), ($end + 1), true);
|
||||
|
||||
if ($tokens[$nextNonSimple]['code'] === \T_NAMESPACE) {
|
||||
// Allow only `namespace\...`.
|
||||
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_NS_SEPARATOR) {
|
||||
return false;
|
||||
}
|
||||
} elseif ($tokens[$nextNonSimple]['code'] === \T_PARENT
|
||||
|| $tokens[$nextNonSimple]['code'] === \T_SELF
|
||||
) {
|
||||
// Allow only `parent::` and `self::`.
|
||||
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_DOUBLE_COLON) {
|
||||
return false;
|
||||
}
|
||||
} elseif ($tokens[$nextNonSimple]['code'] === \T_DOUBLE_COLON) {
|
||||
// Allow only `T_STRING::T_STRING`.
|
||||
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_STRING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($nextNonSimple - 1), null, true);
|
||||
// No need to worry about parent/self, that's handled above and
|
||||
// the double colon is skipped over in that case.
|
||||
if ($prevNonEmpty === false || $tokens[$prevNonEmpty]['code'] !== \T_STRING) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Examine what comes after the namespace/parent/self/double colon, if anything.
|
||||
return $this->isStaticValue($phpcsFile, $tokens, ($nextNonEmpty + 1), $end, $nestedArrays);
|
||||
|
||||
case \T_ARRAY:
|
||||
case \T_OPEN_SHORT_ARRAY:
|
||||
++$nestedArrays;
|
||||
|
||||
$arrayItems = $this->getFunctionCallParameters($phpcsFile, $nextNonSimple);
|
||||
if (empty($arrayItems) === false) {
|
||||
foreach ($arrayItems as $item) {
|
||||
// Check for a double arrow, but only if it's for this array item, not for a nested array.
|
||||
$doubleArrow = false;
|
||||
|
||||
$maybeDoubleArrow = $phpcsFile->findNext(
|
||||
array(\T_DOUBLE_ARROW, \T_ARRAY, \T_OPEN_SHORT_ARRAY),
|
||||
$item['start'],
|
||||
($item['end'] + 1)
|
||||
);
|
||||
if ($maybeDoubleArrow !== false && $tokens[$maybeDoubleArrow]['code'] === \T_DOUBLE_ARROW) {
|
||||
// Double arrow is for this nesting level.
|
||||
$doubleArrow = $maybeDoubleArrow;
|
||||
}
|
||||
|
||||
if ($doubleArrow === false) {
|
||||
if ($this->isStaticValue($phpcsFile, $tokens, $item['start'], $item['end'], $nestedArrays) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Examine array key.
|
||||
if ($this->isStaticValue($phpcsFile, $tokens, $item['start'], ($doubleArrow - 1), $nestedArrays) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Examine array value.
|
||||
if ($this->isStaticValue($phpcsFile, $tokens, ($doubleArrow + 1), $item['end'], $nestedArrays) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
--$nestedArrays;
|
||||
|
||||
/*
|
||||
* Find the end of the array.
|
||||
* We already know we will have a valid closer as otherwise we wouldn't have been
|
||||
* able to get the array items.
|
||||
*/
|
||||
$closer = ($nextNonSimple + 1);
|
||||
if ($tokens[$nextNonSimple]['code'] === \T_OPEN_SHORT_ARRAY
|
||||
&& isset($tokens[$nextNonSimple]['bracket_closer']) === true
|
||||
) {
|
||||
$closer = $tokens[$nextNonSimple]['bracket_closer'];
|
||||
} else {
|
||||
$maybeOpener = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonSimple + 1), ($end + 1), true);
|
||||
if ($tokens[$maybeOpener]['code'] === \T_OPEN_PARENTHESIS) {
|
||||
$opener = $maybeOpener;
|
||||
if (isset($tokens[$opener]['parenthesis_closer']) === true) {
|
||||
$closer = $tokens[$opener]['parenthesis_closer'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($closer === $end) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Examine what comes after the array, if anything.
|
||||
return $this->isStaticValue($phpcsFile, $tokens, ($closer + 1), $end, $nestedArrays);
|
||||
|
||||
}
|
||||
|
||||
// Ok, so this unsafe token was not one of the exceptions, i.e. this is a PHP 5.6+ syntax.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Throw an error if a scalar expression is found.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the token to link the error to.
|
||||
* @param string $type Type of usage found.
|
||||
* @param string $content Optional. The value for the declaration as found.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function throwError(File $phpcsFile, $stackPtr, $type, $content = '')
|
||||
{
|
||||
$error = static::ERROR_PHRASE;
|
||||
$phrase = '';
|
||||
$errorCode = 'Found';
|
||||
|
||||
if (isset($this->errorPhrases[$type]) === true) {
|
||||
$errorCode = $this->stringToErrorCode($type) . 'Found';
|
||||
$phrase = $this->errorPhrases[$type];
|
||||
}
|
||||
|
||||
$data = array($phrase);
|
||||
|
||||
if (empty($content) === false) {
|
||||
$error .= ' Found: %s';
|
||||
$data[] = $content;
|
||||
}
|
||||
|
||||
$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to find the end of multi variable/constant declarations.
|
||||
*
|
||||
* Checks whether a certain part of a declaration needs to be skipped over or
|
||||
* if it is the real end of the declaration.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param array $tokens Token stack of the current file.
|
||||
* @param int $endPtr The token to examine as a candidate end pointer.
|
||||
* @param int $targetLevel Target nesting level.
|
||||
*
|
||||
* @return bool|int True if this is the real end. Int stackPtr to skip to if not.
|
||||
*/
|
||||
private function isRealEndOfDeclaration($tokens, $endPtr, $targetLevel)
|
||||
{
|
||||
// Ignore anything within short array definition brackets for now.
|
||||
if ($tokens[$endPtr]['code'] === \T_OPEN_SHORT_ARRAY
|
||||
&& (isset($tokens[$endPtr]['bracket_opener'])
|
||||
&& $tokens[$endPtr]['bracket_opener'] === $endPtr)
|
||||
&& isset($tokens[$endPtr]['bracket_closer'])
|
||||
) {
|
||||
// Skip forward to the end of the short array definition.
|
||||
return $tokens[$endPtr]['bracket_closer'];
|
||||
}
|
||||
|
||||
// Skip past comma's at a lower nesting level.
|
||||
if ($tokens[$endPtr]['code'] === \T_COMMA) {
|
||||
// Check if a comma is at the nesting level we're targetting.
|
||||
$nestingLevel = 0;
|
||||
if (isset($tokens[$endPtr]['nested_parenthesis']) === true) {
|
||||
$nestingLevel = \count($tokens[$endPtr]['nested_parenthesis']);
|
||||
}
|
||||
if ($nestingLevel > $targetLevel) {
|
||||
return $endPtr;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\InitialValue;
|
||||
|
||||
use PHPCompatibility\Sniffs\InitialValue\NewConstantScalarExpressionsSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect a heredoc being used to set an initial value.
|
||||
*
|
||||
* As of PHP 5.3.0, it's possible to initialize static variables, class properties
|
||||
* and constants declared using the `const` keyword, using the Heredoc syntax.
|
||||
* And while not documented, heredoc initialization can now also be used for function param defaults.
|
||||
* See: https://3v4l.org/JVH8W
|
||||
*
|
||||
* These heredocs (still) cannot contain variables. That's, however, outside the scope of the
|
||||
* PHPCompatibility library until such time as there is a PHP version in which this would be accepted.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration53.new-features.php
|
||||
* @link https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc
|
||||
*
|
||||
* @since 7.1.4
|
||||
* @since 8.2.0 Now extends the NewConstantScalarExpressionsSniff instead of the base Sniff class.
|
||||
* @since 9.0.0 Renamed from `NewHeredocInitializeSniff` to `NewHeredocSniff`.
|
||||
*/
|
||||
class NewHeredocSniff extends NewConstantScalarExpressionsSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Error message.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const ERROR_PHRASE = 'Initializing %s using the Heredoc syntax was not supported in PHP 5.2 or earlier';
|
||||
|
||||
/**
|
||||
* Partial error phrases to be used in combination with the error message constant.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $errorPhrases = array(
|
||||
'const' => 'constants',
|
||||
'property' => 'class properties',
|
||||
'staticvar' => 'static variables',
|
||||
'default' => 'default parameter values',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('5.2') !== true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is a value declared and does the declared value not contain an heredoc ?
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
* @param int $end The end of the value definition.
|
||||
*
|
||||
* @return bool True if no heredoc (or assignment) is found, false otherwise.
|
||||
*/
|
||||
protected function isValidAssignment(File $phpcsFile, $stackPtr, $end)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), $end, true);
|
||||
if ($next === false || $tokens[$next]['code'] !== \T_EQUAL) {
|
||||
// No value assigned.
|
||||
return true;
|
||||
}
|
||||
|
||||
return ($phpcsFile->findNext(\T_START_HEREDOC, ($next + 1), $end, false, null, true) === false);
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Interfaces;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect classes which implement PHP native interfaces intended only for PHP internal use.
|
||||
*
|
||||
* PHP version 5.0+
|
||||
*
|
||||
* @link https://www.php.net/manual/en/class.traversable.php
|
||||
* @link https://www.php.net/manual/en/class.throwable.php
|
||||
* @link https://www.php.net/manual/en/class.datetimeinterface.php
|
||||
*
|
||||
* @since 7.0.3
|
||||
*/
|
||||
class InternalInterfacesSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of PHP internal interfaces, not intended to be implemented by userland classes.
|
||||
*
|
||||
* The array lists : the error message to use.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var array(string => string)
|
||||
*/
|
||||
protected $internalInterfaces = array(
|
||||
'Traversable' => 'shouldn\'t be implemented directly, implement the Iterator or IteratorAggregate interface instead.',
|
||||
'DateTimeInterface' => 'is intended for type hints only and is not implementable.',
|
||||
'Throwable' => 'cannot be implemented directly, extend the Exception class instead.',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Handle case-insensitivity of interface names.
|
||||
$this->internalInterfaces = $this->arrayKeysToLowercase($this->internalInterfaces);
|
||||
|
||||
$targets = array(\T_CLASS);
|
||||
|
||||
if (\defined('T_ANON_CLASS')) {
|
||||
$targets[] = \T_ANON_CLASS;
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$interfaces = PHPCSHelper::findImplementedInterfaceNames($phpcsFile, $stackPtr);
|
||||
|
||||
if (\is_array($interfaces) === false || $interfaces === array()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($interfaces as $interface) {
|
||||
$interface = ltrim($interface, '\\');
|
||||
$interfaceLc = strtolower($interface);
|
||||
if (isset($this->internalInterfaces[$interfaceLc]) === true) {
|
||||
$error = 'The interface %s %s';
|
||||
$errorCode = $this->stringToErrorCode($interfaceLc) . 'Found';
|
||||
$data = array(
|
||||
$interface,
|
||||
$this->internalInterfaces[$interfaceLc],
|
||||
);
|
||||
|
||||
$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+362
@@ -0,0 +1,362 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Interfaces;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect use of new PHP native interfaces and unsupported interface methods.
|
||||
*
|
||||
* PHP version 5.0+
|
||||
*
|
||||
* @since 7.0.3
|
||||
* @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class..
|
||||
* @since 7.1.4 Now also detects new interfaces when used as parameter type declarations.
|
||||
* @since 8.2.0 Now also detects new interfaces when used as return type declarations.
|
||||
*/
|
||||
class NewInterfacesSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of new interfaces, not present in older versions.
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If's sufficient to list the first version where the interface appears.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var array(string => array(string => bool))
|
||||
*/
|
||||
protected $newInterfaces = array(
|
||||
'Traversable' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
'Reflector' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
),
|
||||
|
||||
'Countable' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'OuterIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'RecursiveIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'SeekableIterator' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'Serializable' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'SplObserver' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
'SplSubject' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
),
|
||||
|
||||
'JsonSerializable' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'SessionHandlerInterface' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
|
||||
'DateTimeInterface' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
|
||||
'SessionIdInterface' => array(
|
||||
'5.5.0' => false,
|
||||
'5.5.1' => true,
|
||||
),
|
||||
|
||||
'Throwable' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
'SessionUpdateTimestampHandlerInterface' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* A list of methods which cannot be used in combination with particular interfaces.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var array(string => array(string => string))
|
||||
*/
|
||||
protected $unsupportedMethods = array(
|
||||
'Serializable' => array(
|
||||
'__sleep' => 'https://www.php.net/serializable',
|
||||
'__wakeup' => 'https://www.php.net/serializable',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Handle case-insensitivity of interface names.
|
||||
$this->newInterfaces = $this->arrayKeysToLowercase($this->newInterfaces);
|
||||
$this->unsupportedMethods = $this->arrayKeysToLowercase($this->unsupportedMethods);
|
||||
|
||||
$targets = array(
|
||||
\T_CLASS,
|
||||
\T_FUNCTION,
|
||||
\T_CLOSURE,
|
||||
);
|
||||
|
||||
if (\defined('T_ANON_CLASS')) {
|
||||
$targets[] = \T_ANON_CLASS;
|
||||
}
|
||||
|
||||
if (\defined('T_RETURN_TYPE')) {
|
||||
$targets[] = \T_RETURN_TYPE;
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
switch ($tokens[$stackPtr]['type']) {
|
||||
case 'T_CLASS':
|
||||
case 'T_ANON_CLASS':
|
||||
$this->processClassToken($phpcsFile, $stackPtr);
|
||||
break;
|
||||
|
||||
case 'T_FUNCTION':
|
||||
case 'T_CLOSURE':
|
||||
$this->processFunctionToken($phpcsFile, $stackPtr);
|
||||
|
||||
// Deal with older PHPCS versions which don't recognize return type hints
|
||||
// as well as newer PHPCS versions (3.3.0+) where the tokenization has changed.
|
||||
$returnTypeHint = $this->getReturnTypeHintToken($phpcsFile, $stackPtr);
|
||||
if ($returnTypeHint !== false) {
|
||||
$this->processReturnTypeToken($phpcsFile, $returnTypeHint);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'T_RETURN_TYPE':
|
||||
$this->processReturnTypeToken($phpcsFile, $stackPtr);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Deliberately left empty.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test for when a class token is encountered.
|
||||
*
|
||||
* - Detect classes implementing the new interfaces.
|
||||
* - Detect classes implementing the new interfaces with unsupported functions.
|
||||
*
|
||||
* @since 7.1.4 Split off from the `process()` method.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processClassToken(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$interfaces = PHPCSHelper::findImplementedInterfaceNames($phpcsFile, $stackPtr);
|
||||
|
||||
if (\is_array($interfaces) === false || $interfaces === array()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$checkMethods = false;
|
||||
|
||||
if (isset($tokens[$stackPtr]['scope_closer'])) {
|
||||
$checkMethods = true;
|
||||
$scopeCloser = $tokens[$stackPtr]['scope_closer'];
|
||||
}
|
||||
|
||||
foreach ($interfaces as $interface) {
|
||||
$interface = ltrim($interface, '\\');
|
||||
$interfaceLc = strtolower($interface);
|
||||
|
||||
if (isset($this->newInterfaces[$interfaceLc]) === true) {
|
||||
$itemInfo = array(
|
||||
'name' => $interface,
|
||||
'nameLc' => $interfaceLc,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
if ($checkMethods === true && isset($this->unsupportedMethods[$interfaceLc]) === true) {
|
||||
$nextFunc = $stackPtr;
|
||||
while (($nextFunc = $phpcsFile->findNext(\T_FUNCTION, ($nextFunc + 1), $scopeCloser)) !== false) {
|
||||
$funcName = $phpcsFile->getDeclarationName($nextFunc);
|
||||
$funcNameLc = strtolower($funcName);
|
||||
if ($funcNameLc === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($this->unsupportedMethods[$interfaceLc][$funcNameLc]) === true) {
|
||||
$error = 'Classes that implement interface %s do not support the method %s(). See %s';
|
||||
$errorCode = $this->stringToErrorCode($interface) . 'UnsupportedMethod';
|
||||
$data = array(
|
||||
$interface,
|
||||
$funcName,
|
||||
$this->unsupportedMethods[$interfaceLc][$funcNameLc],
|
||||
);
|
||||
|
||||
$phpcsFile->addError($error, $nextFunc, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test for when a function token is encountered.
|
||||
*
|
||||
* - Detect new interfaces when used as a type hint.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processFunctionToken(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$typeHints = $this->getTypeHintsFromFunctionDeclaration($phpcsFile, $stackPtr);
|
||||
if (empty($typeHints) || \is_array($typeHints) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($typeHints as $hint) {
|
||||
|
||||
$typeHintLc = strtolower($hint);
|
||||
|
||||
if (isset($this->newInterfaces[$typeHintLc]) === true) {
|
||||
$itemInfo = array(
|
||||
'name' => $hint,
|
||||
'nameLc' => $typeHintLc,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test for when a return type token is encountered.
|
||||
*
|
||||
* - Detect new interfaces when used as a return type declaration.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processReturnTypeToken(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$returnTypeHint = $this->getReturnTypeHintName($phpcsFile, $stackPtr);
|
||||
if (empty($returnTypeHint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$returnTypeHint = ltrim($returnTypeHint, '\\');
|
||||
$returnTypeHintLc = strtolower($returnTypeHint);
|
||||
|
||||
if (isset($this->newInterfaces[$returnTypeHintLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Still here ? Then this is a return type declaration using a new interface.
|
||||
$itemInfo = array(
|
||||
'name' => $returnTypeHint,
|
||||
'nameLc' => $returnTypeHintLc,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newInterfaces[$itemInfo['nameLc']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The built-in interface ' . parent::getErrorMsgTemplate();
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Keywords;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect usage of `self`, `parent` and `static` and verify they are lowercase.
|
||||
*
|
||||
* Prior to PHP 5.5, cases existed where the `self`, `parent`, and `static` keywords
|
||||
* were treated in a case sensitive fashion.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration55.incompatible.php#migration55.incompatible.self-parent-static
|
||||
*
|
||||
* @since 7.1.4
|
||||
*/
|
||||
class CaseSensitiveKeywordsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_SELF,
|
||||
\T_STATIC,
|
||||
\T_PARENT,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.4') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$tokenContentLC = strtolower($tokens[$stackPtr]['content']);
|
||||
|
||||
if ($tokenContentLC !== $tokens[$stackPtr]['content']) {
|
||||
$phpcsFile->addError(
|
||||
'The keyword \'%s\' was treated in a case-sensitive fashion in certain cases in PHP 5.4 or earlier. Use the lowercase version for consistent support.',
|
||||
$stackPtr,
|
||||
'NonLowercaseFound',
|
||||
array($tokenContentLC)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Keywords;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detects the use of some reserved keywords to name a class, interface, trait or namespace.
|
||||
*
|
||||
* Emits errors for reserved words and warnings for soft-reserved words.
|
||||
*
|
||||
* PHP version 7.0+
|
||||
*
|
||||
* @link https://www.php.net/manual/en/reserved.other-reserved-words.php
|
||||
* @link https://wiki.php.net/rfc/reserve_more_types_in_php_7
|
||||
*
|
||||
* @since 7.0.8
|
||||
* @since 7.1.4 This sniff now throws a warning (soft reserved) or an error (reserved) depending
|
||||
* on the `testVersion` set. Previously it would always throw an error.
|
||||
*/
|
||||
class ForbiddenNamesAsDeclaredSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* List of tokens which can not be used as class, interface, trait names or as part of a namespace.
|
||||
*
|
||||
* @since 7.0.8
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $forbiddenTokens = array(
|
||||
\T_NULL => '7.0',
|
||||
\T_TRUE => '7.0',
|
||||
\T_FALSE => '7.0',
|
||||
);
|
||||
|
||||
/**
|
||||
* T_STRING keywords to recognize as forbidden names.
|
||||
*
|
||||
* @since 7.0.8
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $forbiddenNames = array(
|
||||
'null' => '7.0',
|
||||
'true' => '7.0',
|
||||
'false' => '7.0',
|
||||
'bool' => '7.0',
|
||||
'int' => '7.0',
|
||||
'float' => '7.0',
|
||||
'string' => '7.0',
|
||||
'iterable' => '7.1',
|
||||
'void' => '7.1',
|
||||
'object' => '7.2',
|
||||
);
|
||||
|
||||
/**
|
||||
* T_STRING keywords to recognize as soft reserved names.
|
||||
*
|
||||
* Using any of these keywords to name a class, interface, trait or namespace
|
||||
* is highly discouraged since they may be used in future versions of PHP.
|
||||
*
|
||||
* @since 7.0.8
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $softReservedNames = array(
|
||||
'resource' => '7.0',
|
||||
'object' => '7.0',
|
||||
'mixed' => '7.0',
|
||||
'numeric' => '7.0',
|
||||
);
|
||||
|
||||
/**
|
||||
* Combined list of the two lists above.
|
||||
*
|
||||
* Used for quick check whether or not something is a reserved
|
||||
* word.
|
||||
* Set from the `register()` method.
|
||||
*
|
||||
* @since 7.0.8
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allForbiddenNames = array();
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.8
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Do the list merge only once.
|
||||
$this->allForbiddenNames = array_merge($this->forbiddenNames, $this->softReservedNames);
|
||||
|
||||
$targets = array(
|
||||
\T_CLASS,
|
||||
\T_INTERFACE,
|
||||
\T_TRAIT,
|
||||
\T_NAMESPACE,
|
||||
\T_STRING, // Compat for PHPCS < 2.4.0 and PHP < 5.3.
|
||||
);
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.8
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$tokenCode = $tokens[$stackPtr]['code'];
|
||||
$tokenType = $tokens[$stackPtr]['type'];
|
||||
$tokenContentLc = strtolower($tokens[$stackPtr]['content']);
|
||||
|
||||
// For string tokens we only care about 'trait' as that is the only one
|
||||
// which may not be correctly recognized as it's own token.
|
||||
// This only happens in older versions of PHP where the token doesn't exist yet as a keyword.
|
||||
if ($tokenCode === \T_STRING && $tokenContentLc !== 'trait') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (\in_array($tokenType, array('T_CLASS', 'T_INTERFACE', 'T_TRAIT'), true)) {
|
||||
// Check for the declared name being a name which is not tokenized as T_STRING.
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($nextNonEmpty !== false && isset($this->forbiddenTokens[$tokens[$nextNonEmpty]['code']]) === true) {
|
||||
$name = $tokens[$nextNonEmpty]['content'];
|
||||
} else {
|
||||
// Get the declared name if it's a T_STRING.
|
||||
$name = $phpcsFile->getDeclarationName($stackPtr);
|
||||
}
|
||||
unset($nextNonEmpty);
|
||||
|
||||
if (isset($name) === false || \is_string($name) === false || $name === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$nameLc = strtolower($name);
|
||||
if (isset($this->allForbiddenNames[$nameLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
} elseif ($tokenCode === \T_NAMESPACE) {
|
||||
$namespaceName = $this->getDeclaredNamespaceName($phpcsFile, $stackPtr);
|
||||
|
||||
if ($namespaceName === false || $namespaceName === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$namespaceParts = explode('\\', $namespaceName);
|
||||
foreach ($namespaceParts as $namespacePart) {
|
||||
$partLc = strtolower($namespacePart);
|
||||
if (isset($this->allForbiddenNames[$partLc]) === true) {
|
||||
$name = $namespacePart;
|
||||
$nameLc = $partLc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} elseif ($tokenCode === \T_STRING) {
|
||||
// Traits which are not yet tokenized as T_TRAIT.
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($nextNonEmpty === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nextNonEmptyCode = $tokens[$nextNonEmpty]['code'];
|
||||
|
||||
if ($nextNonEmptyCode !== \T_STRING && isset($this->forbiddenTokens[$nextNonEmptyCode]) === true) {
|
||||
$name = $tokens[$nextNonEmpty]['content'];
|
||||
$nameLc = strtolower($tokens[$nextNonEmpty]['content']);
|
||||
} elseif ($nextNonEmptyCode === \T_STRING) {
|
||||
$endOfStatement = $phpcsFile->findNext(array(\T_SEMICOLON, \T_OPEN_CURLY_BRACKET), ($stackPtr + 1));
|
||||
if ($endOfStatement === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
$nextNonEmptyLc = strtolower($tokens[$nextNonEmpty]['content']);
|
||||
|
||||
if (isset($this->allForbiddenNames[$nextNonEmptyLc]) === true) {
|
||||
$name = $tokens[$nextNonEmpty]['content'];
|
||||
$nameLc = $nextNonEmptyLc;
|
||||
break;
|
||||
}
|
||||
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), $endOfStatement, true);
|
||||
} while ($nextNonEmpty !== false);
|
||||
}
|
||||
unset($nextNonEmptyCode, $nextNonEmptyLc, $endOfStatement);
|
||||
}
|
||||
|
||||
if (isset($name, $nameLc) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Still here, so this is one of the reserved words.
|
||||
// Build up the error message.
|
||||
$error = "'%s' is a";
|
||||
$isError = null;
|
||||
$errorCode = $this->stringToErrorCode($nameLc) . 'Found';
|
||||
$data = array(
|
||||
$nameLc,
|
||||
);
|
||||
|
||||
if (isset($this->softReservedNames[$nameLc]) === true
|
||||
&& $this->supportsAbove($this->softReservedNames[$nameLc]) === true
|
||||
) {
|
||||
$error .= ' soft reserved keyword as of PHP version %s';
|
||||
$isError = false;
|
||||
$data[] = $this->softReservedNames[$nameLc];
|
||||
}
|
||||
|
||||
if (isset($this->forbiddenNames[$nameLc]) === true
|
||||
&& $this->supportsAbove($this->forbiddenNames[$nameLc]) === true
|
||||
) {
|
||||
if (isset($isError) === true) {
|
||||
$error .= ' and a';
|
||||
}
|
||||
$error .= ' reserved keyword as of PHP version %s';
|
||||
$isError = true;
|
||||
$data[] = $this->forbiddenNames[$nameLc];
|
||||
}
|
||||
|
||||
if (isset($isError) === true) {
|
||||
$error .= ' and should not be used to name a class, interface or trait or as part of a namespace (%s)';
|
||||
$data[] = $tokens[$stackPtr]['type'];
|
||||
|
||||
$this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Keywords;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Prohibits the use of reserved keywords invoked as functions.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://www.php.net/manual/en/reserved.keywords.php
|
||||
*
|
||||
* @since 5.5
|
||||
*/
|
||||
class ForbiddenNamesAsInvokedFunctionsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* List of tokens to register.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetedTokens = array(
|
||||
\T_ABSTRACT => '5.0',
|
||||
\T_CALLABLE => '5.4',
|
||||
\T_CATCH => '5.0',
|
||||
\T_FINAL => '5.0',
|
||||
\T_FINALLY => '5.5',
|
||||
\T_GOTO => '5.3',
|
||||
\T_IMPLEMENTS => '5.0',
|
||||
\T_INTERFACE => '5.0',
|
||||
\T_INSTANCEOF => '5.0',
|
||||
\T_INSTEADOF => '5.4',
|
||||
\T_NAMESPACE => '5.3',
|
||||
\T_PRIVATE => '5.0',
|
||||
\T_PROTECTED => '5.0',
|
||||
\T_PUBLIC => '5.0',
|
||||
\T_TRAIT => '5.4',
|
||||
\T_TRY => '5.0',
|
||||
|
||||
);
|
||||
|
||||
/**
|
||||
* T_STRING keywords to recognize as targetted tokens.
|
||||
*
|
||||
* Compatibility for PHP versions where the keyword is not yet recognized
|
||||
* as its own token and for PHPCS versions which change the token to
|
||||
* T_STRING when used in a method call.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetedStringTokens = array(
|
||||
'abstract' => '5.0',
|
||||
'callable' => '5.4',
|
||||
'catch' => '5.0',
|
||||
'final' => '5.0',
|
||||
'finally' => '5.5',
|
||||
'goto' => '5.3',
|
||||
'implements' => '5.0',
|
||||
'interface' => '5.0',
|
||||
'instanceof' => '5.0',
|
||||
'insteadof' => '5.4',
|
||||
'namespace' => '5.3',
|
||||
'private' => '5.0',
|
||||
'protected' => '5.0',
|
||||
'public' => '5.0',
|
||||
'trait' => '5.4',
|
||||
'try' => '5.0',
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$tokens = array_keys($this->targetedTokens);
|
||||
$tokens[] = \T_STRING;
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$tokenCode = $tokens[$stackPtr]['code'];
|
||||
$tokenContentLc = strtolower($tokens[$stackPtr]['content']);
|
||||
$isString = false;
|
||||
|
||||
/*
|
||||
* For string tokens we only care if the string is a reserved word used
|
||||
* as a function. This only happens in older versions of PHP where the
|
||||
* token doesn't exist yet for that keyword or in later versions when the
|
||||
* token is used in a method invocation.
|
||||
*/
|
||||
if ($tokenCode === \T_STRING
|
||||
&& (isset($this->targetedStringTokens[$tokenContentLc]) === false)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokenCode === \T_STRING) {
|
||||
$isString = true;
|
||||
}
|
||||
|
||||
// Make sure this is a function call.
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($next === false || $tokens[$next]['code'] !== \T_OPEN_PARENTHESIS) {
|
||||
// Not a function call.
|
||||
return;
|
||||
}
|
||||
|
||||
// This sniff isn't concerned about function declarations.
|
||||
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
if ($prev !== false && $tokens[$prev]['code'] === \T_FUNCTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with PHP 7 relaxing the rules.
|
||||
* "As of PHP 7.0.0 these keywords are allowed as property, constant, and method names
|
||||
* of classes, interfaces and traits...", i.e. they can be invoked as a method call.
|
||||
*
|
||||
* Only needed for those keywords which we sniff out via T_STRING.
|
||||
*/
|
||||
if (($tokens[$prev]['code'] === \T_OBJECT_OPERATOR || $tokens[$prev]['code'] === \T_DOUBLE_COLON)
|
||||
&& $this->supportsBelow('5.6') === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For the word catch, it is valid to have an open parenthesis
|
||||
// after it, but only if it is preceded by a right curly brace.
|
||||
if ($tokenCode === \T_CATCH) {
|
||||
if ($prev !== false && $tokens[$prev]['code'] === \T_CLOSE_CURLY_BRACKET) {
|
||||
// Ok, it's fine.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isString === true) {
|
||||
$version = $this->targetedStringTokens[$tokenContentLc];
|
||||
} else {
|
||||
$version = $this->targetedTokens[$tokenCode];
|
||||
}
|
||||
|
||||
if ($this->supportsAbove($version)) {
|
||||
$error = "'%s' is a reserved keyword introduced in PHP version %s and cannot be invoked as a function (%s)";
|
||||
$errorCode = $this->stringToErrorCode($tokenContentLc) . 'Found';
|
||||
$data = array(
|
||||
$tokenContentLc,
|
||||
$version,
|
||||
$tokens[$stackPtr]['type'],
|
||||
);
|
||||
|
||||
$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+442
@@ -0,0 +1,442 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Keywords;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detects the use of reserved keywords as class, function, namespace or constant names.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://www.php.net/manual/en/reserved.keywords.php
|
||||
*
|
||||
* @since 5.5
|
||||
*/
|
||||
class ForbiddenNamesSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of keywords that can not be used as function, class and namespace name or constant name.
|
||||
* Mentions since which version it's not allowed.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @var array(string => string)
|
||||
*/
|
||||
protected $invalidNames = array(
|
||||
'abstract' => '5.0',
|
||||
'and' => 'all',
|
||||
'array' => 'all',
|
||||
'as' => 'all',
|
||||
'break' => 'all',
|
||||
'callable' => '5.4',
|
||||
'case' => 'all',
|
||||
'catch' => '5.0',
|
||||
'class' => 'all',
|
||||
'clone' => '5.0',
|
||||
'const' => 'all',
|
||||
'continue' => 'all',
|
||||
'declare' => 'all',
|
||||
'default' => 'all',
|
||||
'die' => 'all',
|
||||
'do' => 'all',
|
||||
'echo' => 'all',
|
||||
'else' => 'all',
|
||||
'elseif' => 'all',
|
||||
'empty' => 'all',
|
||||
'enddeclare' => 'all',
|
||||
'endfor' => 'all',
|
||||
'endforeach' => 'all',
|
||||
'endif' => 'all',
|
||||
'endswitch' => 'all',
|
||||
'endwhile' => 'all',
|
||||
'eval' => 'all',
|
||||
'exit' => 'all',
|
||||
'extends' => 'all',
|
||||
'final' => '5.0',
|
||||
'finally' => '5.5',
|
||||
'for' => 'all',
|
||||
'foreach' => 'all',
|
||||
'function' => 'all',
|
||||
'global' => 'all',
|
||||
'goto' => '5.3',
|
||||
'if' => 'all',
|
||||
'implements' => '5.0',
|
||||
'include' => 'all',
|
||||
'include_once' => 'all',
|
||||
'instanceof' => '5.0',
|
||||
'insteadof' => '5.4',
|
||||
'interface' => '5.0',
|
||||
'isset' => 'all',
|
||||
'list' => 'all',
|
||||
'namespace' => '5.3',
|
||||
'new' => 'all',
|
||||
'or' => 'all',
|
||||
'print' => 'all',
|
||||
'private' => '5.0',
|
||||
'protected' => '5.0',
|
||||
'public' => '5.0',
|
||||
'require' => 'all',
|
||||
'require_once' => 'all',
|
||||
'return' => 'all',
|
||||
'static' => 'all',
|
||||
'switch' => 'all',
|
||||
'throw' => '5.0',
|
||||
'trait' => '5.4',
|
||||
'try' => '5.0',
|
||||
'unset' => 'all',
|
||||
'use' => 'all',
|
||||
'var' => 'all',
|
||||
'while' => 'all',
|
||||
'xor' => 'all',
|
||||
'yield' => '5.5',
|
||||
'__class__' => 'all',
|
||||
'__dir__' => '5.3',
|
||||
'__file__' => 'all',
|
||||
'__function__' => 'all',
|
||||
'__method__' => 'all',
|
||||
'__namespace__' => '5.3',
|
||||
);
|
||||
|
||||
/**
|
||||
* A list of keywords that can follow use statements.
|
||||
*
|
||||
* @since 7.0.1
|
||||
*
|
||||
* @var array(string => string)
|
||||
*/
|
||||
protected $validUseNames = array(
|
||||
'const' => true,
|
||||
'function' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Scope modifiers and other keywords allowed in trait use statements.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowedModifiers = array();
|
||||
|
||||
/**
|
||||
* Targeted tokens.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetedTokens = array(
|
||||
\T_CLASS,
|
||||
\T_FUNCTION,
|
||||
\T_NAMESPACE,
|
||||
\T_STRING,
|
||||
\T_CONST,
|
||||
\T_USE,
|
||||
\T_AS,
|
||||
\T_EXTENDS,
|
||||
\T_INTERFACE,
|
||||
\T_TRAIT,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->allowedModifiers = Tokens::$scopeModifiers;
|
||||
$this->allowedModifiers[\T_FINAL] = \T_FINAL;
|
||||
|
||||
$tokens = $this->targetedTokens;
|
||||
|
||||
if (\defined('T_ANON_CLASS')) {
|
||||
$tokens[] = \T_ANON_CLASS;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
/*
|
||||
* We distinguish between the class, function and namespace names vs the define statements.
|
||||
*/
|
||||
if ($tokens[$stackPtr]['type'] === 'T_STRING') {
|
||||
$this->processString($phpcsFile, $stackPtr, $tokens);
|
||||
} else {
|
||||
$this->processNonString($phpcsFile, $stackPtr, $tokens);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
* @param array $tokens The stack of tokens that make up
|
||||
* the file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function processNonString(File $phpcsFile, $stackPtr, $tokens)
|
||||
{
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($nextNonEmpty === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with anonymous classes - `class` before a reserved keyword is sometimes
|
||||
* misidentified as `T_ANON_CLASS`.
|
||||
* In PHPCS < 2.3.4 these were tokenized as T_CLASS no matter what.
|
||||
*/
|
||||
if ($tokens[$stackPtr]['type'] === 'T_ANON_CLASS' || $tokens[$stackPtr]['type'] === 'T_CLASS') {
|
||||
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
if ($prevNonEmpty !== false && $tokens[$prevNonEmpty]['type'] === 'T_NEW') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PHP 5.6 allows for use const and use function, but only if followed by the function/constant name.
|
||||
* - `use function HelloWorld` => move to the next token (HelloWorld) to verify.
|
||||
* - `use const HelloWorld` => move to the next token (HelloWorld) to verify.
|
||||
*/
|
||||
elseif ($tokens[$stackPtr]['type'] === 'T_USE'
|
||||
&& isset($this->validUseNames[strtolower($tokens[$nextNonEmpty]['content'])]) === true
|
||||
) {
|
||||
$maybeUseNext = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
|
||||
if ($maybeUseNext !== false && $this->isEndOfUseStatement($tokens[$maybeUseNext]) === false) {
|
||||
$nextNonEmpty = $maybeUseNext;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with visibility modifiers.
|
||||
* - `use HelloWorld { sayHello as protected; }` => valid, bow out.
|
||||
* - `use HelloWorld { sayHello as private myPrivateHello; }` => move to the next token to verify.
|
||||
*/
|
||||
elseif ($tokens[$stackPtr]['type'] === 'T_AS'
|
||||
&& isset($this->allowedModifiers[$tokens[$nextNonEmpty]['code']]) === true
|
||||
&& $phpcsFile->hasCondition($stackPtr, \T_USE) === true
|
||||
) {
|
||||
$maybeUseNext = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
|
||||
if ($maybeUseNext === false || $this->isEndOfUseStatement($tokens[$maybeUseNext]) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nextNonEmpty = $maybeUseNext;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with foreach ( ... as list() ).
|
||||
*/
|
||||
elseif ($tokens[$stackPtr]['type'] === 'T_AS'
|
||||
&& isset($tokens[$stackPtr]['nested_parenthesis']) === true
|
||||
&& $tokens[$nextNonEmpty]['code'] === \T_LIST
|
||||
) {
|
||||
$parentheses = array_reverse($tokens[$stackPtr]['nested_parenthesis'], true);
|
||||
foreach ($parentheses as $open => $close) {
|
||||
if (isset($tokens[$open]['parenthesis_owner'])
|
||||
&& $tokens[$tokens[$open]['parenthesis_owner']]['code'] === \T_FOREACH
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with functions declared to return by reference.
|
||||
*/
|
||||
elseif ($tokens[$stackPtr]['type'] === 'T_FUNCTION'
|
||||
&& $tokens[$nextNonEmpty]['type'] === 'T_BITWISE_AND'
|
||||
) {
|
||||
$maybeUseNext = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true, null, true);
|
||||
if ($maybeUseNext === false) {
|
||||
// Live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
$nextNonEmpty = $maybeUseNext;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with nested namespaces.
|
||||
*/
|
||||
elseif ($tokens[$stackPtr]['type'] === 'T_NAMESPACE') {
|
||||
if ($tokens[$stackPtr + 1]['code'] === \T_NS_SEPARATOR) {
|
||||
// Not a namespace declaration, but use of, i.e. `namespace\someFunction();`.
|
||||
return;
|
||||
}
|
||||
|
||||
$endToken = $phpcsFile->findNext(array(\T_SEMICOLON, \T_OPEN_CURLY_BRACKET), ($stackPtr + 1), null, false, null, true);
|
||||
$namespaceName = trim($phpcsFile->getTokensAsString(($stackPtr + 1), ($endToken - $stackPtr - 1)));
|
||||
if (empty($namespaceName) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$namespaceParts = explode('\\', $namespaceName);
|
||||
foreach ($namespaceParts as $namespacePart) {
|
||||
$partLc = strtolower($namespacePart);
|
||||
if (isset($this->invalidNames[$partLc]) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the token position of the part which matched.
|
||||
for ($i = ($stackPtr + 1); $i < $endToken; $i++) {
|
||||
if ($tokens[$i]['content'] === $namespacePart) {
|
||||
$nextNonEmpty = $i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($i, $namespacePart, $partLc);
|
||||
}
|
||||
|
||||
$nextContentLc = strtolower($tokens[$nextNonEmpty]['content']);
|
||||
if (isset($this->invalidNames[$nextContentLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with PHP 7 relaxing the rules.
|
||||
* "As of PHP 7.0.0 these keywords are allowed as property, constant, and method names
|
||||
* of classes, interfaces and traits, except that class may not be used as constant name."
|
||||
*/
|
||||
if ((($tokens[$stackPtr]['type'] === 'T_FUNCTION'
|
||||
&& $this->inClassScope($phpcsFile, $stackPtr, false) === true)
|
||||
|| ($tokens[$stackPtr]['type'] === 'T_CONST'
|
||||
&& $this->isClassConstant($phpcsFile, $stackPtr) === true
|
||||
&& $nextContentLc !== 'class'))
|
||||
&& $this->supportsBelow('5.6') === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->supportsAbove($this->invalidNames[$nextContentLc])) {
|
||||
$data = array(
|
||||
$tokens[$nextNonEmpty]['content'],
|
||||
$this->invalidNames[$nextContentLc],
|
||||
);
|
||||
$this->addError($phpcsFile, $stackPtr, $tokens[$nextNonEmpty]['content'], $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
* @param array $tokens The stack of tokens that make up
|
||||
* the file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function processString(File $phpcsFile, $stackPtr, $tokens)
|
||||
{
|
||||
$tokenContentLc = strtolower($tokens[$stackPtr]['content']);
|
||||
|
||||
/*
|
||||
* Special case for PHP versions where the target is not yet identified as
|
||||
* its own token, but presents as T_STRING.
|
||||
* - trait keyword in PHP < 5.4
|
||||
*/
|
||||
if (version_compare(\PHP_VERSION_ID, '50400', '<') && $tokenContentLc === 'trait') {
|
||||
$this->processNonString($phpcsFile, $stackPtr, $tokens);
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for any define/defined tokens (both T_STRING ones, blame Tokenizer).
|
||||
if ($tokenContentLc !== 'define' && $tokenContentLc !== 'defined') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the define(d) constant name.
|
||||
$firstParam = $this->getFunctionCallParameter($phpcsFile, $stackPtr, 1);
|
||||
if ($firstParam === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defineName = $this->stripQuotes($firstParam['raw']);
|
||||
$defineNameLc = strtolower($defineName);
|
||||
|
||||
if (isset($this->invalidNames[$defineNameLc]) && $this->supportsAbove($this->invalidNames[$defineNameLc])) {
|
||||
$data = array(
|
||||
$defineName,
|
||||
$this->invalidNames[$defineNameLc],
|
||||
);
|
||||
$this->addError($phpcsFile, $stackPtr, $defineNameLc, $data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add the error message.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
* @param string $content The token content found.
|
||||
* @param array $data The data to pass into the error message.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addError(File $phpcsFile, $stackPtr, $content, $data)
|
||||
{
|
||||
$error = "Function name, class name, namespace name or constant name can not be reserved keyword '%s' (since version %s)";
|
||||
$errorCode = $this->stringToErrorCode($content) . 'Found';
|
||||
$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the current token code is for a token which can be considered
|
||||
* the end of a (partial) use statement.
|
||||
*
|
||||
* @since 7.0.8
|
||||
*
|
||||
* @param int $token The current token information.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isEndOfUseStatement($token)
|
||||
{
|
||||
return \in_array($token['code'], array(\T_CLOSE_CURLY_BRACKET, \T_SEMICOLON, \T_COMMA), true);
|
||||
}
|
||||
}
|
||||
Vendored
+391
@@ -0,0 +1,391 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Keywords;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect use of new PHP keywords.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/heredoc-with-double-quotes
|
||||
* @link https://wiki.php.net/rfc/horizontalreuse (traits)
|
||||
* @link https://wiki.php.net/rfc/generators
|
||||
* @link https://wiki.php.net/rfc/finally
|
||||
* @link https://wiki.php.net/rfc/generator-delegation
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class..
|
||||
*/
|
||||
class NewKeywordsSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of new keywords, not present in older versions.
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If's sufficient to list the last version which did not contain the keyword.
|
||||
*
|
||||
* Description will be used as part of the error message.
|
||||
* Condition is the name of a callback method within this class or the parent class
|
||||
* which checks whether the token complies with a certain condition.
|
||||
* The callback function will be passed the $phpcsFile and the $stackPtr.
|
||||
* The callback function should return `true` if the condition is met and the
|
||||
* error should *not* be thrown.
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 7.0.3 Support for 'condition' has been added.
|
||||
*
|
||||
* @var array(string => array(string => bool|string))
|
||||
*/
|
||||
protected $newKeywords = array(
|
||||
'T_HALT_COMPILER' => array(
|
||||
'5.0' => false,
|
||||
'5.1' => true,
|
||||
'description' => '"__halt_compiler" keyword',
|
||||
),
|
||||
'T_CONST' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
'description' => '"const" keyword',
|
||||
'condition' => 'isClassConstant', // Keyword is only new when not in class context.
|
||||
),
|
||||
'T_CALLABLE' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'description' => '"callable" keyword',
|
||||
'content' => 'callable',
|
||||
),
|
||||
'T_DIR' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
'description' => '__DIR__ magic constant',
|
||||
'content' => '__DIR__',
|
||||
),
|
||||
'T_GOTO' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
'description' => '"goto" keyword',
|
||||
'content' => 'goto',
|
||||
),
|
||||
'T_INSTEADOF' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'description' => '"insteadof" keyword (for traits)',
|
||||
'content' => 'insteadof',
|
||||
),
|
||||
'T_NAMESPACE' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
'description' => '"namespace" keyword',
|
||||
'content' => 'namespace',
|
||||
),
|
||||
'T_NS_C' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
'description' => '__NAMESPACE__ magic constant',
|
||||
'content' => '__NAMESPACE__',
|
||||
),
|
||||
'T_USE' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
'description' => '"use" keyword (for traits/namespaces/anonymous functions)',
|
||||
),
|
||||
'T_START_NOWDOC' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
'description' => 'nowdoc functionality',
|
||||
),
|
||||
'T_END_NOWDOC' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
'description' => 'nowdoc functionality',
|
||||
),
|
||||
'T_START_HEREDOC' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
'description' => '(Double) quoted Heredoc identifier',
|
||||
'condition' => 'isNotQuoted', // Heredoc is only new with quoted identifier.
|
||||
),
|
||||
'T_TRAIT' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'description' => '"trait" keyword',
|
||||
'content' => 'trait',
|
||||
),
|
||||
'T_TRAIT_C' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'description' => '__TRAIT__ magic constant',
|
||||
'content' => '__TRAIT__',
|
||||
),
|
||||
// The specifics for distinguishing between 'yield' and 'yield from' are dealt
|
||||
// with in the translation logic.
|
||||
// This token has to be placed above the `T_YIELD` token in this array to allow for this.
|
||||
'T_YIELD_FROM' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'description' => '"yield from" keyword (for generators)',
|
||||
'content' => 'yield',
|
||||
),
|
||||
'T_YIELD' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
'description' => '"yield" keyword (for generators)',
|
||||
'content' => 'yield',
|
||||
),
|
||||
'T_FINALLY' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
'description' => '"finally" keyword (in exception handling)',
|
||||
'content' => 'finally',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Translation table for T_STRING tokens.
|
||||
*
|
||||
* Will be set up from the register() method.
|
||||
*
|
||||
* @since 7.0.5
|
||||
*
|
||||
* @var array(string => string)
|
||||
*/
|
||||
protected $translateContentToToken = array();
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$tokens = array();
|
||||
$translate = array();
|
||||
foreach ($this->newKeywords as $token => $versions) {
|
||||
if (\defined($token)) {
|
||||
$tokens[] = constant($token);
|
||||
}
|
||||
if (isset($versions['content'])) {
|
||||
$translate[strtolower($versions['content'])] = $token;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with tokens not recognized by the PHP version the sniffer is run
|
||||
* under and (not correctly) compensated for by PHPCS.
|
||||
*/
|
||||
if (empty($translate) === false) {
|
||||
$this->translateContentToToken = $translate;
|
||||
$tokens[] = \T_STRING;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$tokenType = $tokens[$stackPtr]['type'];
|
||||
|
||||
// Allow for dealing with multi-token keywords, like "yield from".
|
||||
$end = $stackPtr;
|
||||
|
||||
// Translate T_STRING token if necessary.
|
||||
if ($tokens[$stackPtr]['type'] === 'T_STRING') {
|
||||
$content = strtolower($tokens[$stackPtr]['content']);
|
||||
|
||||
if (isset($this->translateContentToToken[$content]) === false) {
|
||||
// Not one of the tokens we're looking for.
|
||||
return;
|
||||
}
|
||||
|
||||
$tokenType = $this->translateContentToToken[$content];
|
||||
}
|
||||
|
||||
/*
|
||||
* Special case: distinguish between `yield` and `yield from`.
|
||||
*
|
||||
* PHPCS currently (at least up to v 3.0.1) does not backfill for the
|
||||
* `yield` nor the `yield from` keywords.
|
||||
* See: https://github.com/squizlabs/PHP_CodeSniffer/issues/1524
|
||||
*
|
||||
* In PHP < 5.5, both `yield` as well as `from` are tokenized as T_STRING.
|
||||
* In PHP 5.5 - 5.6, `yield` is tokenized as T_YIELD and `from` as T_STRING,
|
||||
* but the `T_YIELD_FROM` token *is* defined in PHP.
|
||||
* In PHP 7.0+ both are tokenized as their respective token, however,
|
||||
* a multi-line "yield from" is tokenized as two tokens.
|
||||
*/
|
||||
if ($tokenType === 'T_YIELD') {
|
||||
$nextToken = $phpcsFile->findNext(\T_WHITESPACE, ($end + 1), null, true);
|
||||
if ($tokens[$nextToken]['code'] === \T_STRING
|
||||
&& $tokens[$nextToken]['content'] === 'from'
|
||||
) {
|
||||
$tokenType = 'T_YIELD_FROM';
|
||||
$end = $nextToken;
|
||||
}
|
||||
unset($nextToken);
|
||||
}
|
||||
|
||||
if ($tokenType === 'T_YIELD_FROM' && $tokens[($stackPtr - 1)]['type'] === 'T_YIELD_FROM') {
|
||||
// Multi-line "yield from", no need to report it twice.
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->newKeywords[$tokenType]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
|
||||
if ($prevToken !== false
|
||||
&& ($tokens[$prevToken]['code'] === \T_DOUBLE_COLON
|
||||
|| $tokens[$prevToken]['code'] === \T_OBJECT_OPERATOR)
|
||||
) {
|
||||
// Class property of the same name as one of the keywords. Ignore.
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip attempts to use keywords as functions or class names - the former
|
||||
// will be reported by ForbiddenNamesAsInvokedFunctionsSniff, whilst the
|
||||
// latter will be (partially) reported by the ForbiddenNames sniff.
|
||||
// Either type will result in false-positives when targetting lower versions
|
||||
// of PHP where the name was not reserved, unless we explicitly check for
|
||||
// them.
|
||||
if (($nextToken === false
|
||||
|| $tokens[$nextToken]['type'] !== 'T_OPEN_PARENTHESIS')
|
||||
&& ($prevToken === false
|
||||
|| $tokens[$prevToken]['type'] !== 'T_CLASS'
|
||||
|| $tokens[$prevToken]['type'] !== 'T_INTERFACE')
|
||||
) {
|
||||
// Skip based on token scope condition.
|
||||
if (isset($this->newKeywords[$tokenType]['condition'])
|
||||
&& \call_user_func(array($this, $this->newKeywords[$tokenType]['condition']), $phpcsFile, $stackPtr) === true
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $tokenType,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newKeywords[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of the non-PHP-version array keys used in a sub-array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array(
|
||||
'description',
|
||||
'condition',
|
||||
'content',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemArray Version and other information about the item.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorInfo(array $itemArray, array $itemInfo)
|
||||
{
|
||||
$errorInfo = parent::getErrorInfo($itemArray, $itemInfo);
|
||||
$errorInfo['description'] = $itemArray['description'];
|
||||
|
||||
return $errorInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow for concrete child classes to filter the error data before it's passed to PHPCS.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $data The error data array which was created.
|
||||
* @param array $itemInfo Base information about the item this error message applies to.
|
||||
* @param array $errorInfo Detail information about an item this error message applies to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function filterErrorData(array $data, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
$data[0] = $errorInfo['description'];
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback for the quoted heredoc identifier condition.
|
||||
*
|
||||
* A double quoted identifier will have the opening quote on position 3
|
||||
* in the string: `<<<"ID"`.
|
||||
*
|
||||
* @since 8.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotQuoted(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
return ($tokens[$stackPtr]['content'][3] !== '"');
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\LanguageConstructs;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Verify that nothing but variables are passed to empty().
|
||||
*
|
||||
* Prior to PHP 5.5, `empty()` only supported variables; anything else resulted in a parse error.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/empty_isset_exprs
|
||||
* @link https://www.php.net/manual/en/function.empty.php
|
||||
*
|
||||
* @since 7.0.4
|
||||
* @since 9.0.0 The "is the parameter a variable" determination has been abstracted out
|
||||
* and moved to a separate method `Sniff::isVariable()`.
|
||||
* @since 9.0.0 Renamed from `EmptyNonVariableSniff` to `NewEmptyNonVariableSniff`.
|
||||
*/
|
||||
class NewEmptyNonVariableSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.4') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$open = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
|
||||
if ($open === false
|
||||
|| $tokens[$open]['code'] !== \T_OPEN_PARENTHESIS
|
||||
|| isset($tokens[$open]['parenthesis_closer']) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$close = $tokens[$open]['parenthesis_closer'];
|
||||
|
||||
$nestingLevel = 0;
|
||||
if ($close !== ($open + 1) && isset($tokens[$open + 1]['nested_parenthesis'])) {
|
||||
$nestingLevel = \count($tokens[$open + 1]['nested_parenthesis']);
|
||||
}
|
||||
|
||||
if ($this->isVariable($phpcsFile, ($open + 1), $close, $nestingLevel) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Only variables can be passed to empty() prior to PHP 5.5.',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\LanguageConstructs;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect use of new PHP language constructs.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/namespaceseparator
|
||||
* @link https://wiki.php.net/rfc/variadics
|
||||
* @link https://wiki.php.net/rfc/argument_unpacking
|
||||
*
|
||||
* @since 5.6
|
||||
* @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class..
|
||||
* @since 9.0.0 Detection for new operator tokens has been moved to the `NewOperators` sniff.
|
||||
*/
|
||||
class NewLanguageConstructsSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of new language constructs, not present in older versions.
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If's sufficient to list the first version where the keyword appears.
|
||||
*
|
||||
* @since 5.6
|
||||
*
|
||||
* @var array(string => array(string => bool|string))
|
||||
*/
|
||||
protected $newConstructs = array(
|
||||
'T_NS_SEPARATOR' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
'description' => 'the \ operator (for namespaces)',
|
||||
),
|
||||
'T_ELLIPSIS' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => true,
|
||||
'description' => 'the ... spread operator',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$tokens = array();
|
||||
foreach ($this->newConstructs as $token => $versions) {
|
||||
$tokens[] = constant($token);
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.6
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$tokenType = $tokens[$stackPtr]['type'];
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $tokenType,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newConstructs[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of the non-PHP-version array keys used in a sub-array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('description');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemArray Version and other information about the item.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorInfo(array $itemArray, array $itemInfo)
|
||||
{
|
||||
$errorInfo = parent::getErrorInfo($itemArray, $itemInfo);
|
||||
$errorInfo['description'] = $itemArray['description'];
|
||||
|
||||
return $errorInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow for concrete child classes to filter the error data before it's passed to PHPCS.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $data The error data array which was created.
|
||||
* @param array $itemInfo Base information about the item this error message applies to.
|
||||
* @param array $errorInfo Detail information about an item this error message applies to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function filterErrorData(array $data, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
$data[0] = $errorInfo['description'];
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
Vendored
+188
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Lists;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect code affected by the changed list assignment order in PHP 7.0+.
|
||||
*
|
||||
* The `list()` construct no longer assigns variables in reverse order.
|
||||
* This affects all list constructs where non-unique variables are used.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.list.order
|
||||
* @link https://wiki.php.net/rfc/abstract_syntax_tree#changes_to_list
|
||||
* @link https://www.php.net/manual/en/function.list.php
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class AssignmentOrderSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_LIST,
|
||||
\T_OPEN_SHORT_ARRAY,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void|int Void if not a valid list. If a list construct has been
|
||||
* examined, the stack pointer to the list closer to skip
|
||||
* passed any nested lists which don't need to be examined again.
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === \T_OPEN_SHORT_ARRAY
|
||||
&& $this->isShortList($phpcsFile, $stackPtr) === false
|
||||
) {
|
||||
// Short array, not short list.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === \T_LIST) {
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($nextNonEmpty === false
|
||||
|| $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS
|
||||
|| isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false
|
||||
) {
|
||||
// Parse error or live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
$opener = $nextNonEmpty;
|
||||
$closer = $tokens[$nextNonEmpty]['parenthesis_closer'];
|
||||
} else {
|
||||
// Short list syntax.
|
||||
$opener = $stackPtr;
|
||||
|
||||
if (isset($tokens[$stackPtr]['bracket_closer'])) {
|
||||
$closer = $tokens[$stackPtr]['bracket_closer'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($opener, $closer) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* OK, so we have the opener & closer, now we need to check all the variables in the
|
||||
* list() to see if there are duplicates as that's the problem.
|
||||
*/
|
||||
$hasVars = $phpcsFile->findNext(array(\T_VARIABLE, \T_DOLLAR), ($opener + 1), $closer);
|
||||
if ($hasVars === false) {
|
||||
// Empty list, not our concern.
|
||||
return ($closer + 1);
|
||||
}
|
||||
|
||||
// Set the variable delimiters based on the list type being examined.
|
||||
$stopPoints = array(\T_COMMA);
|
||||
if ($tokens[$stackPtr]['code'] === \T_OPEN_SHORT_ARRAY) {
|
||||
$stopPoints[] = \T_CLOSE_SHORT_ARRAY;
|
||||
} else {
|
||||
$stopPoints[] = \T_CLOSE_PARENTHESIS;
|
||||
}
|
||||
|
||||
$listVars = array();
|
||||
$lastStopPoint = $opener;
|
||||
|
||||
/*
|
||||
* Create a list of all variables used within the `list()` construct.
|
||||
* We're not concerned with whether these are nested or not, as any duplicate
|
||||
* variable name used will be problematic, independent of nesting.
|
||||
*/
|
||||
do {
|
||||
$nextStopPoint = $phpcsFile->findNext($stopPoints, ($lastStopPoint + 1), $closer);
|
||||
if ($nextStopPoint === false) {
|
||||
$nextStopPoint = $closer;
|
||||
}
|
||||
|
||||
// Also detect this in PHP 7.1 keyed lists.
|
||||
$hasDoubleArrow = $phpcsFile->findNext(\T_DOUBLE_ARROW, ($lastStopPoint + 1), $nextStopPoint);
|
||||
if ($hasDoubleArrow !== false) {
|
||||
$lastStopPoint = $hasDoubleArrow;
|
||||
}
|
||||
|
||||
// Find the start of the variable, allowing for variable variables.
|
||||
$nextStartPoint = $phpcsFile->findNext(array(\T_VARIABLE, \T_DOLLAR), ($lastStopPoint + 1), $nextStopPoint);
|
||||
if ($nextStartPoint === false) {
|
||||
// Skip past empty bits in the list, i.e. `list( $a, , ,)`.
|
||||
$lastStopPoint = $nextStopPoint;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gather the content of all non-empty tokens to determine the "variable name".
|
||||
* Variable name in this context includes array or object property syntaxes, such
|
||||
* as `$a['name']` and `$b->property`.
|
||||
*/
|
||||
$varContent = '';
|
||||
|
||||
for ($i = $nextStartPoint; $i < $nextStopPoint; $i++) {
|
||||
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$varContent .= $tokens[$i]['content'];
|
||||
}
|
||||
|
||||
if ($varContent !== '') {
|
||||
$listVars[] = $varContent;
|
||||
}
|
||||
|
||||
$lastStopPoint = $nextStopPoint;
|
||||
|
||||
} while ($lastStopPoint < $closer);
|
||||
|
||||
if (empty($listVars)) {
|
||||
// Shouldn't be possible, but just in case.
|
||||
return ($closer + 1);
|
||||
}
|
||||
|
||||
// Verify that all variables used in the list() construct are unique.
|
||||
if (\count($listVars) !== \count(array_unique($listVars))) {
|
||||
$phpcsFile->addError(
|
||||
'list() will assign variable from left-to-right since PHP 7.0. Ensure all variables in list() are unique to prevent unexpected results.',
|
||||
$stackPtr,
|
||||
'Affected'
|
||||
);
|
||||
}
|
||||
|
||||
return ($closer + 1);
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Lists;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Support for empty `list()` expressions has been removed in PHP 7.0.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.list.empty
|
||||
* @link https://wiki.php.net/rfc/abstract_syntax_tree#changes_to_list
|
||||
* @link https://www.php.net/manual/en/function.list.php
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
class ForbiddenEmptyListAssignmentSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* List of tokens to disregard when determining whether the list() is empty.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $ignoreTokens = array();
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Set up a list of tokens to disregard when determining whether the list() is empty.
|
||||
// Only needs to be set up once.
|
||||
$this->ignoreTokens = Tokens::$emptyTokens;
|
||||
$this->ignoreTokens[\T_COMMA] = \T_COMMA;
|
||||
$this->ignoreTokens[\T_OPEN_PARENTHESIS] = \T_OPEN_PARENTHESIS;
|
||||
$this->ignoreTokens[\T_CLOSE_PARENTHESIS] = \T_CLOSE_PARENTHESIS;
|
||||
|
||||
return array(
|
||||
\T_LIST,
|
||||
\T_OPEN_SHORT_ARRAY,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === \T_OPEN_SHORT_ARRAY) {
|
||||
if ($this->isShortList($phpcsFile, $stackPtr) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$open = $stackPtr;
|
||||
$close = $tokens[$stackPtr]['bracket_closer'];
|
||||
} else {
|
||||
// T_LIST.
|
||||
$open = $phpcsFile->findNext(\T_OPEN_PARENTHESIS, $stackPtr, null, false, null, true);
|
||||
if ($open === false || isset($tokens[$open]['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$close = $tokens[$open]['parenthesis_closer'];
|
||||
}
|
||||
|
||||
$error = true;
|
||||
if (($close - $open) > 1) {
|
||||
for ($cnt = $open + 1; $cnt < $close; $cnt++) {
|
||||
if (isset($this->ignoreTokens[$tokens[$cnt]['code']]) === false) {
|
||||
$error = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($error === true) {
|
||||
$phpcsFile->addError(
|
||||
'Empty list() assignments are not allowed since PHP 7.0',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+227
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Lists;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Since PHP 7.1, you can specify keys in `list()`, or its new shorthand `[]` syntax.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/list_keys
|
||||
* @link https://www.php.net/manual/en/function.list.php
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class NewKeyedListSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Tokens which represent the start of a list construct.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $sniffTargets = array(
|
||||
\T_LIST => \T_LIST,
|
||||
\T_OPEN_SHORT_ARRAY => \T_OPEN_SHORT_ARRAY,
|
||||
);
|
||||
|
||||
/**
|
||||
* The token(s) within the list construct which is being targeted.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetsInList = array(
|
||||
\T_DOUBLE_ARROW => \T_DOUBLE_ARROW,
|
||||
);
|
||||
|
||||
/**
|
||||
* All tokens needed to walk through the list construct and
|
||||
* determine whether the target token is contained within.
|
||||
*
|
||||
* Set by the setUpAllTargets() method which is called from within register().
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $allTargets;
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->setUpAllTargets();
|
||||
|
||||
return $this->sniffTargets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the $allTargets array only once.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUpAllTargets()
|
||||
{
|
||||
$this->allTargets = $this->sniffTargets + $this->targetsInList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('7.0') === false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->bowOutEarly() === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === \T_OPEN_SHORT_ARRAY
|
||||
&& $this->isShortList($phpcsFile, $stackPtr) === false
|
||||
) {
|
||||
// Short array, not short list.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === \T_LIST) {
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($nextNonEmpty === false
|
||||
|| $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS
|
||||
|| isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false
|
||||
) {
|
||||
// Parse error or live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
$opener = $nextNonEmpty;
|
||||
$closer = $tokens[$nextNonEmpty]['parenthesis_closer'];
|
||||
} else {
|
||||
// Short list syntax.
|
||||
$opener = $stackPtr;
|
||||
|
||||
if (isset($tokens[$stackPtr]['bracket_closer'])) {
|
||||
$closer = $tokens[$stackPtr]['bracket_closer'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($opener, $closer) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->examineList($phpcsFile, $opener, $closer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Examine the contents of a list construct to determine whether an error needs to be thrown.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $opener The position of the list open token.
|
||||
* @param int $closer The position of the list close token.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function examineList(File $phpcsFile, $opener, $closer)
|
||||
{
|
||||
$start = $opener;
|
||||
while (($start = $this->hasTargetInList($phpcsFile, $start, $closer)) !== false) {
|
||||
$phpcsFile->addError(
|
||||
'Specifying keys in list constructs is not supported in PHP 7.0 or earlier.',
|
||||
$start,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether a certain target token exists within a list construct.
|
||||
*
|
||||
* Skips past nested list constructs, so these can be examined based on their own token.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $start The position of the list open token or a token
|
||||
* within the list to start (resume) the examination from.
|
||||
* @param int $closer The position of the list close token.
|
||||
*
|
||||
* @return int|bool Stack pointer to the target token if encountered. False otherwise.
|
||||
*/
|
||||
protected function hasTargetInList(File $phpcsFile, $start, $closer)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
for ($i = ($start + 1); $i < $closer; $i++) {
|
||||
if (isset($this->allTargets[$tokens[$i]['code']]) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($this->targetsInList[$tokens[$i]['code']]) === true) {
|
||||
return $i;
|
||||
}
|
||||
|
||||
// Skip past nested list constructs.
|
||||
if ($tokens[$i]['code'] === \T_LIST) {
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
|
||||
if ($nextNonEmpty !== false
|
||||
&& $tokens[$nextNonEmpty]['code'] === \T_OPEN_PARENTHESIS
|
||||
&& isset($tokens[$nextNonEmpty]['parenthesis_closer']) === true
|
||||
) {
|
||||
$i = $tokens[$nextNonEmpty]['parenthesis_closer'];
|
||||
}
|
||||
} elseif ($tokens[$i]['code'] === \T_OPEN_SHORT_ARRAY
|
||||
&& isset($tokens[$i]['bracket_closer'])
|
||||
) {
|
||||
$i = $tokens[$i]['bracket_closer'];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Lists;
|
||||
|
||||
use PHPCompatibility\Sniffs\Lists\NewKeyedListSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect reference assignments in array destructuring using (short) list.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration73.new-features.php#migration73.new-features.core.destruct-reference
|
||||
* @link https://wiki.php.net/rfc/list_reference_assignment
|
||||
* @link https://www.php.net/manual/en/function.list.php
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class NewListReferenceAssignmentSniff extends NewKeyedListSniff
|
||||
{
|
||||
/**
|
||||
* The token(s) within the list construct which is being targeted.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetsInList = array(
|
||||
\T_BITWISE_AND => \T_BITWISE_AND,
|
||||
);
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('7.2') === false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine the contents of a list construct to determine whether an error needs to be thrown.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $opener The position of the list open token.
|
||||
* @param int $closer The position of the list close token.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function examineList(File $phpcsFile, $opener, $closer)
|
||||
{
|
||||
$start = $opener;
|
||||
while (($start = $this->hasTargetInList($phpcsFile, $start, $closer)) !== false) {
|
||||
$phpcsFile->addError(
|
||||
'Reference assignments within list constructs are not supported in PHP 7.2 or earlier.',
|
||||
$start,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Lists;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect short list syntax for symmetric array destructuring.
|
||||
*
|
||||
* "The shorthand array syntax (`[]`) may now be used to destructure arrays for
|
||||
* assignments (including within `foreach`), as an alternative to the existing
|
||||
* `list()` syntax, which is still supported."
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration71.new-features.php#migration71.new-features.symmetric-array-destructuring
|
||||
* @link https://wiki.php.net/rfc/short_list_syntax
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class NewShortListSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_OPEN_SHORT_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isShortList($phpcsFile, $stackPtr) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$closer = $tokens[$stackPtr]['bracket_closer'];
|
||||
|
||||
$hasVariable = $phpcsFile->findNext(\T_VARIABLE, ($stackPtr + 1), $closer);
|
||||
if ($hasVariable === false) {
|
||||
// List syntax is only valid if there are variables in it.
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'The shorthand list syntax "[]" to destructure arrays is not available in PHP 7.0 or earlier.',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
|
||||
return ($closer + 1);
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\MethodUse;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* As of PHP 5.3, the `__toString()` magic method can no longer be passed arguments.
|
||||
*
|
||||
* Sister-sniff to `PHPCompatibility.FunctionDeclarations.ForbiddenToStringParameters`.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration53.incompatible.php
|
||||
* @link https://www.php.net/manual/en/language.oop5.magic.php#object.tostring
|
||||
*
|
||||
* @since 9.2.0
|
||||
*/
|
||||
class ForbiddenToStringParametersSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_DOUBLE_COLON,
|
||||
\T_OBJECT_OPERATOR,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('5.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_STRING) {
|
||||
/*
|
||||
* Not a method call.
|
||||
*
|
||||
* Note: This disregards method calls with the method name in a variable, like:
|
||||
* $method = '__toString';
|
||||
* $obj->$method();
|
||||
* However, that would be very hard to examine reliably anyway.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (strtolower($tokens[$nextNonEmpty]['content']) !== '__tostring') {
|
||||
// Not a call to the __toString() method.
|
||||
return;
|
||||
}
|
||||
|
||||
$openParens = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true);
|
||||
if ($openParens === false || $tokens[$openParens]['code'] !== \T_OPEN_PARENTHESIS) {
|
||||
// Not a method call.
|
||||
return;
|
||||
}
|
||||
|
||||
$closeParens = $phpcsFile->findNext(Tokens::$emptyTokens, ($openParens + 1), null, true);
|
||||
if ($closeParens === false || $tokens[$closeParens]['code'] === \T_CLOSE_PARENTHESIS) {
|
||||
// Not a method call.
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're still here, then this is a call to the __toString() magic method passing parameters.
|
||||
$phpcsFile->addError(
|
||||
'The __toString() magic method will no longer accept passed arguments since PHP 5.3',
|
||||
$stackPtr,
|
||||
'Passed'
|
||||
);
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\MethodUse;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect direct calls to the `__clone()` magic method, which is allowed since PHP 7.0.
|
||||
*
|
||||
* "Doing calls like `$obj->__clone()` is now allowed. This was the only magic method
|
||||
* that had a compile-time check preventing some calls to it, which doesn't make sense.
|
||||
* If we allow all other magic methods to be called, there's no reason to forbid this one."
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/abstract_syntax_tree#directly_calling_clone_is_allowed
|
||||
* @link https://www.php.net/manual/en/language.oop5.cloning.php
|
||||
*
|
||||
* @since 9.1.0
|
||||
*/
|
||||
class NewDirectCallsToCloneSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Tokens which indicate class internal use.
|
||||
*
|
||||
* @since 9.3.2
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $classInternal = array(
|
||||
\T_PARENT => true,
|
||||
\T_SELF => true,
|
||||
\T_STATIC => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_DOUBLE_COLON,
|
||||
\T_OBJECT_OPERATOR,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.6') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_STRING) {
|
||||
/*
|
||||
* Not a method call.
|
||||
*
|
||||
* Note: This disregards method calls with the method name in a variable, like:
|
||||
* $method = '__clone';
|
||||
* $obj->$method();
|
||||
* However, that would be very hard to examine reliably anyway.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (strtolower($tokens[$nextNonEmpty]['content']) !== '__clone') {
|
||||
// Not a call to the __clone() method.
|
||||
return;
|
||||
}
|
||||
|
||||
$nextNextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true);
|
||||
if ($nextNextNonEmpty === false || $tokens[$nextNextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) {
|
||||
// Not a method call.
|
||||
return;
|
||||
}
|
||||
|
||||
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
if ($prevNonEmpty === false || isset($this->classInternal[$tokens[$prevNonEmpty]['code']])) {
|
||||
// Class internal call to __clone().
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Direct calls to the __clone() magic method are not allowed in PHP 5.6 or earlier.',
|
||||
$nextNonEmpty,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Miscellaneous;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* PHP 7.4 now supports stand-alone PHP tags at the end of a file (without new line).
|
||||
*
|
||||
* > `<?php` at the end of the file (without trailing newline) will now be
|
||||
* > interpreted as an opening PHP tag. Previously it was interpreted either as
|
||||
* > `<? php` and resulted in a syntax error (with short_open_tag=1) or was
|
||||
* > interpreted as a literal `<?php` string (with short_open_tag=0).
|
||||
*
|
||||
* {@internal Due to an issue with the Tokenizer, this sniff will not work correctly
|
||||
* on PHP 5.3 in combination with PHPCS < 2.6.0 when short_open_tag is `On`.
|
||||
* As this is causing "Undefined offset" notices, there is nothing we can
|
||||
* do to work-around this.}
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.incompatible.php#migration74.incompatible.core.php-tag
|
||||
* @link https://github.com/php/php-src/blob/30de357fa14480468132bbc22a272aeb91789ba8/UPGRADING#L37-L40
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class NewPHPOpenTagEOFSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether or not short open tags is enabled on the install running the sniffs.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $shortOpenTags;
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$targets = array(
|
||||
\T_OPEN_TAG_WITH_ECHO,
|
||||
);
|
||||
|
||||
$this->shortOpenTags = (bool) ini_get('short_open_tag');
|
||||
if ($this->shortOpenTags === false) {
|
||||
$targets[] = \T_INLINE_HTML;
|
||||
} else {
|
||||
$targets[] = \T_STRING;
|
||||
}
|
||||
|
||||
if (version_compare(\PHP_VERSION_ID, '70399', '>')) {
|
||||
$targets[] = \T_OPEN_TAG;
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('7.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($stackPtr !== ($phpcsFile->numTokens - 1)) {
|
||||
// We're only interested in the last token in the file.
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$contents = $tokens[$stackPtr]['content'];
|
||||
$error = false;
|
||||
|
||||
switch ($tokens[$stackPtr]['code']) {
|
||||
case \T_INLINE_HTML:
|
||||
// PHP < 7.4 with short open tags off.
|
||||
if ($contents === '<?php') {
|
||||
$error = true;
|
||||
} elseif ($contents === '<?=') {
|
||||
// Also cover short open echo tags in PHP 5.3 with short open tags off.
|
||||
$error = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case \T_STRING:
|
||||
// PHP < 7.4 with short open tags on.
|
||||
if ($contents === 'php'
|
||||
&& $tokens[($stackPtr - 1)]['code'] === \T_OPEN_TAG
|
||||
&& $tokens[($stackPtr - 1)]['content'] === '<?'
|
||||
) {
|
||||
$error = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case \T_OPEN_TAG_WITH_ECHO:
|
||||
// PHP 5.4+.
|
||||
if (rtrim($contents) === '<?=') {
|
||||
$error = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case \T_OPEN_TAG:
|
||||
// PHP 7.4+.
|
||||
if ($contents === '<?php') {
|
||||
$error = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ($error === true) {
|
||||
$phpcsFile->addError(
|
||||
'A PHP open tag at the end of a file, without trailing newline, was not supported in PHP 7.3 or earlier and would result in a syntax error or be interpreted as a literal string',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Miscellaneous;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Check for use of alternative PHP tags, support for which was removed in PHP 7.0.
|
||||
*
|
||||
* {@internal Based on `Generic.PHP.DisallowAlternativePHPTags` by Juliette Reinders Folmer
|
||||
* (with permission) which was merged into PHPCS 2.7.0.}
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/remove_alternative_php_tags
|
||||
* @link https://www.php.net/manual/en/language.basic-syntax.phptags.php
|
||||
*
|
||||
* @since 7.0.4
|
||||
*/
|
||||
class RemovedAlternativePHPTagsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether ASP tags are enabled or not.
|
||||
*
|
||||
* @since 7.0.4
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $aspTags = false;
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if (version_compare(\PHP_VERSION_ID, '70000', '<') === true) {
|
||||
// phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.asp_tagsRemoved
|
||||
$this->aspTags = (bool) ini_get('asp_tags');
|
||||
}
|
||||
|
||||
return array(
|
||||
\T_OPEN_TAG,
|
||||
\T_OPEN_TAG_WITH_ECHO,
|
||||
\T_INLINE_HTML,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$openTag = $tokens[$stackPtr];
|
||||
$content = trim($openTag['content']);
|
||||
|
||||
if ($content === '' || $content === '<?php') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($openTag['code'] === \T_OPEN_TAG || $openTag['code'] === \T_OPEN_TAG_WITH_ECHO) {
|
||||
|
||||
if ($content === '<%' || $content === '<%=') {
|
||||
$data = array(
|
||||
'ASP',
|
||||
$content,
|
||||
);
|
||||
$errorCode = 'ASPOpenTagFound';
|
||||
|
||||
} elseif (strpos($content, '<script ') !== false) {
|
||||
$data = array(
|
||||
'Script',
|
||||
$content,
|
||||
);
|
||||
$errorCode = 'ScriptOpenTagFound';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Account for incorrect script open tags.
|
||||
// The "(?:<s)?" in the regex is to work-around a bug in the tokenizer in PHP 5.2.
|
||||
elseif ($openTag['code'] === \T_INLINE_HTML
|
||||
&& preg_match('`((?:<s)?cript (?:[^>]+)?language=[\'"]?php[\'"]?(?:[^>]+)?>)`i', $content, $match) === 1
|
||||
) {
|
||||
$found = $match[1];
|
||||
$data = array(
|
||||
'Script',
|
||||
$found,
|
||||
);
|
||||
$errorCode = 'ScriptOpenTagFound';
|
||||
}
|
||||
|
||||
if (isset($errorCode, $data)) {
|
||||
$phpcsFile->addError(
|
||||
'%s style opening tags have been removed in PHP 7.0. Found "%s"',
|
||||
$stackPtr,
|
||||
$errorCode,
|
||||
$data
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're still here, we can't be sure if what we found was really intended as ASP open tags.
|
||||
if ($openTag['code'] === \T_INLINE_HTML && $this->aspTags === false) {
|
||||
if (strpos($content, '<%') !== false) {
|
||||
$error = 'Possible use of ASP style opening tags detected. ASP style opening tags have been removed in PHP 7.0. Found: %s';
|
||||
$snippet = $this->getSnippet($content, '<%');
|
||||
$data = array('<%' . $snippet);
|
||||
|
||||
$phpcsFile->addWarning($error, $stackPtr, 'MaybeASPOpenTagFound', $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a snippet from a HTML token.
|
||||
*
|
||||
* @since 7.0.4
|
||||
*
|
||||
* @param string $content The content of the HTML token.
|
||||
* @param string $startAt Partial string to use as a starting point for the snippet.
|
||||
* @param int $length The target length of the snippet to get. Defaults to 25.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSnippet($content, $startAt = '', $length = 25)
|
||||
{
|
||||
$startPos = 0;
|
||||
|
||||
if ($startAt !== '') {
|
||||
$startPos = strpos($content, $startAt);
|
||||
if ($startPos !== false) {
|
||||
$startPos += \strlen($startAt);
|
||||
}
|
||||
}
|
||||
|
||||
$snippet = substr($content, $startPos, $length);
|
||||
if ((\strlen($content) - $startPos) > $length) {
|
||||
$snippet .= '...';
|
||||
}
|
||||
|
||||
return $snippet;
|
||||
}
|
||||
}
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Miscellaneous;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Check for valid integer types and values.
|
||||
*
|
||||
* Checks:
|
||||
* - PHP 5.4 introduced binary integers.
|
||||
* - PHP 7.0 removed tolerance for invalid octals. These were truncated prior to PHP 7
|
||||
* and give a parse error since PHP 7.
|
||||
* - PHP 7.0 removed support for recognizing hexadecimal numeric strings as numeric.
|
||||
* Type juggling and recognition was inconsistent prior to PHP 7. As of PHP 7, they
|
||||
* are no longer treated as numeric.
|
||||
*
|
||||
* PHP version 5.4+
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/binnotation4ints
|
||||
* @link https://wiki.php.net/rfc/remove_hex_support_in_numeric_strings
|
||||
* @link https://www.php.net/manual/en/language.types.integer.php
|
||||
*
|
||||
* @since 7.0.3
|
||||
* @since 7.0.8 This sniff now throws a warning instead of an error for invalid binary integers.
|
||||
*/
|
||||
class ValidIntegersSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether PHPCS is run on a PHP < 5.4.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $isLowPHPVersion = false;
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->isLowPHPVersion = version_compare(\PHP_VERSION_ID, '50400', '<');
|
||||
|
||||
return array(
|
||||
\T_LNUMBER, // Binary, octal integers.
|
||||
\T_CONSTANT_ENCAPSED_STRING, // Hex numeric string.
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$token = $tokens[$stackPtr];
|
||||
|
||||
if ($this->couldBeBinaryInteger($tokens, $stackPtr) === true) {
|
||||
if ($this->supportsBelow('5.3')) {
|
||||
$error = 'Binary integer literals were not present in PHP version 5.3 or earlier. Found: %s';
|
||||
if ($this->isLowPHPVersion === false) {
|
||||
$data = array($token['content']);
|
||||
} else {
|
||||
$data = array($this->getBinaryInteger($phpcsFile, $tokens, $stackPtr));
|
||||
}
|
||||
$phpcsFile->addError($error, $stackPtr, 'BinaryIntegerFound', $data);
|
||||
}
|
||||
|
||||
if ($this->isInvalidBinaryInteger($tokens, $stackPtr) === true) {
|
||||
$error = 'Invalid binary integer detected. Found: %s';
|
||||
$data = array($this->getBinaryInteger($phpcsFile, $tokens, $stackPtr));
|
||||
$phpcsFile->addWarning($error, $stackPtr, 'InvalidBinaryIntegerFound', $data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$isError = $this->supportsAbove('7.0');
|
||||
$data = array( $token['content'] );
|
||||
|
||||
if ($this->isInvalidOctalInteger($tokens, $stackPtr) === true) {
|
||||
$this->addMessage(
|
||||
$phpcsFile,
|
||||
'Invalid octal integer detected. Prior to PHP 7 this would lead to a truncated number. From PHP 7 onwards this causes a parse error. Found: %s',
|
||||
$stackPtr,
|
||||
$isError,
|
||||
'InvalidOctalIntegerFound',
|
||||
$data
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isHexidecimalNumericString($tokens, $stackPtr) === true) {
|
||||
$this->addMessage(
|
||||
$phpcsFile,
|
||||
'The behaviour of hexadecimal numeric strings was inconsistent prior to PHP 7 and support has been removed in PHP 7. Found: %s',
|
||||
$stackPtr,
|
||||
$isError,
|
||||
'HexNumericStringFound',
|
||||
$data
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Could the current token potentially be a binary integer ?
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param array $tokens Token stack.
|
||||
* @param int $stackPtr The current position in the token stack.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function couldBeBinaryInteger($tokens, $stackPtr)
|
||||
{
|
||||
$token = $tokens[$stackPtr];
|
||||
|
||||
if ($token['code'] !== \T_LNUMBER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isLowPHPVersion === false) {
|
||||
return (preg_match('`^0b[0-1]+$`iD', $token['content']) === 1);
|
||||
}
|
||||
// Pre-5.4, binary strings are tokenized as T_LNUMBER (0) + T_STRING ("b01010101").
|
||||
// At this point, we don't yet care whether it's a valid binary int, that's a separate check.
|
||||
else {
|
||||
return($token['content'] === '0' && $tokens[$stackPtr + 1]['code'] === \T_STRING && preg_match('`^b[0-9]+$`iD', $tokens[$stackPtr + 1]['content']) === 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current token an invalid binary integer ?
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param array $tokens Token stack.
|
||||
* @param int $stackPtr The current position in the token stack.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isInvalidBinaryInteger($tokens, $stackPtr)
|
||||
{
|
||||
if ($this->couldBeBinaryInteger($tokens, $stackPtr) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isLowPHPVersion === false) {
|
||||
// If it's an invalid binary int, the token will be split into two T_LNUMBER tokens.
|
||||
return ($tokens[$stackPtr + 1]['code'] === \T_LNUMBER);
|
||||
} else {
|
||||
return (preg_match('`^b[0-1]+$`iD', $tokens[$stackPtr + 1]['content']) === 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the content of the tokens which together look like a binary integer.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param array $tokens Token stack.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getBinaryInteger(File $phpcsFile, $tokens, $stackPtr)
|
||||
{
|
||||
$length = 2; // PHP < 5.4 T_LNUMBER + T_STRING.
|
||||
|
||||
if ($this->isLowPHPVersion === false) {
|
||||
$i = $stackPtr;
|
||||
while ($tokens[$i]['code'] === \T_LNUMBER) {
|
||||
$i++;
|
||||
}
|
||||
$length = ($i - $stackPtr);
|
||||
}
|
||||
|
||||
return $phpcsFile->getTokensAsString($stackPtr, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current token an invalid octal integer ?
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param array $tokens Token stack.
|
||||
* @param int $stackPtr The current position in the token stack.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isInvalidOctalInteger($tokens, $stackPtr)
|
||||
{
|
||||
$token = $tokens[$stackPtr];
|
||||
|
||||
if ($token['code'] === \T_LNUMBER && preg_match('`^0[0-7]*[8-9]+[0-9]*$`D', $token['content']) === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current token a hexidecimal numeric string ?
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @param array $tokens Token stack.
|
||||
* @param int $stackPtr The current position in the token stack.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isHexidecimalNumericString($tokens, $stackPtr)
|
||||
{
|
||||
$token = $tokens[$stackPtr];
|
||||
|
||||
if ($token['code'] === \T_CONSTANT_ENCAPSED_STRING && preg_match('`^0x[a-f0-9]+$`iD', $this->stripQuotes($token['content'])) === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Operators;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect code affected by the change in operator precedence of concatenation in PHP 8.0.
|
||||
*
|
||||
* In PHP < 8.0 the operator precedence of `.`, `+` and `-` are the same.
|
||||
* As of PHP 8.0, the operator precedence of the concatenation operator will be
|
||||
* lowered to be right below the `<<` and `>>` operators.
|
||||
*
|
||||
* As of PHP 7.4, a deprecation warning will be thrown upon encountering an
|
||||
* unparenthesized expression containing an `.` before a `+` or `-`.
|
||||
*
|
||||
* PHP version 7.4
|
||||
* PHP version 8.0
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/concatenation_precedence
|
||||
* @link https://www.php.net/manual/en/language.operators.precedence.php
|
||||
*
|
||||
* @since 9.2.0
|
||||
*/
|
||||
class ChangedConcatOperatorPrecedenceSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* List of tokens with a lower operator precedence than concatenation in PHP >= 8.0.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $tokensWithLowerPrecedence = array(
|
||||
'T_BITWISE_AND' => true,
|
||||
'T_BITWISE_XOR' => true,
|
||||
'T_BITWISE_OR' => true,
|
||||
'T_COALESCE' => true,
|
||||
'T_INLINE_THEN' => true,
|
||||
'T_INLINE_ELSE' => true,
|
||||
'T_YIELD_FROM' => true,
|
||||
'T_YIELD' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_PLUS,
|
||||
\T_MINUS,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.4') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isUnaryPlusMinus($phpcsFile, $stackPtr) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
for ($i = ($stackPtr - 1); $stackPtr >= 0; $i--) {
|
||||
if ($tokens[$i]['code'] === \T_STRING_CONCAT) {
|
||||
// Found one.
|
||||
break;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] === \T_SEMICOLON
|
||||
|| $tokens[$i]['code'] === \T_OPEN_CURLY_BRACKET
|
||||
|| $tokens[$i]['code'] === \T_OPEN_TAG
|
||||
|| $tokens[$i]['code'] === \T_OPEN_TAG_WITH_ECHO
|
||||
|| $tokens[$i]['code'] === \T_COMMA
|
||||
|| $tokens[$i]['code'] === \T_COLON
|
||||
|| $tokens[$i]['code'] === \T_CASE
|
||||
) {
|
||||
// If we reached any of the above tokens, we've reached the end of
|
||||
// the statement without encountering a concatenation operator.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] === \T_OPEN_CURLY_BRACKET
|
||||
&& isset($tokens[$i]['bracket_closer'])
|
||||
&& $tokens[$i]['bracket_closer'] > $stackPtr
|
||||
) {
|
||||
// No need to look any further, this is plus/minus within curly braces
|
||||
// and we've reached the open curly.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS
|
||||
&& isset($tokens[$i]['parenthesis_closer'])
|
||||
&& $tokens[$i]['parenthesis_closer'] > $stackPtr
|
||||
) {
|
||||
// No need to look any further, this is plus/minus within parenthesis
|
||||
// and we've reached the open parenthesis.
|
||||
return;
|
||||
}
|
||||
|
||||
if (($tokens[$i]['code'] === \T_OPEN_SHORT_ARRAY
|
||||
|| $tokens[$i]['code'] === \T_OPEN_SQUARE_BRACKET)
|
||||
&& isset($tokens[$i]['bracket_closer'])
|
||||
&& $tokens[$i]['bracket_closer'] > $stackPtr
|
||||
) {
|
||||
// No need to look any further, this is plus/minus within a short array
|
||||
// or array key square brackets and we've reached the opener.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] === \T_CLOSE_CURLY_BRACKET) {
|
||||
if (isset($tokens[$i]['scope_owner'])) {
|
||||
// Different scope, we've passed the start of the statement.
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($tokens[$i]['bracket_opener'])) {
|
||||
$i = $tokens[$i]['bracket_opener'];
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] === \T_CLOSE_PARENTHESIS
|
||||
&& isset($tokens[$i]['parenthesis_opener'])
|
||||
) {
|
||||
// Skip over statements in parenthesis, including long arrays.
|
||||
$i = $tokens[$i]['parenthesis_opener'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($tokens[$i]['code'] === \T_CLOSE_SQUARE_BRACKET
|
||||
|| $tokens[$i]['code'] === \T_CLOSE_SHORT_ARRAY)
|
||||
&& isset($tokens[$i]['bracket_opener'])
|
||||
) {
|
||||
// Skip over array keys and short arrays.
|
||||
$i = $tokens[$i]['bracket_opener'];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for chain being broken by a token with a lower precedence.
|
||||
if (isset(Tokens::$booleanOperators[$tokens[$i]['code']]) === true
|
||||
|| isset(Tokens::$assignmentTokens[$tokens[$i]['code']]) === true
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->tokensWithLowerPrecedence[$tokens[$i]['type']]) === true) {
|
||||
if ($tokens[$i]['code'] === \T_BITWISE_AND
|
||||
&& $phpcsFile->isReference($i) === true
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$message = 'Using an unparenthesized expression containing a "." before a "+" or "-" has been deprecated in PHP 7.4';
|
||||
$isError = false;
|
||||
if ($this->supportsAbove('8.0') === true) {
|
||||
$message .= ' and removed in PHP 8.0';
|
||||
$isError = true;
|
||||
}
|
||||
|
||||
$this->addMessage($phpcsFile, $message, $i, $isError);
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Operators;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Bitwise shifts by negative number will throw an ArithmeticError since PHP 7.0.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/integer_semantics
|
||||
* @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.integers.negative-bitshift
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
class ForbiddenNegativeBitshiftSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Potential end tokens for which the end pointer has to be set back by one.
|
||||
*
|
||||
* {@internal The PHPCS `findEndOfStatement()` method is not completely consistent
|
||||
* in how it returns the statement end. This is just a simple way to bypass
|
||||
* the inconsistency for our purposes.}
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $inclusiveStopPoints = array(
|
||||
\T_COLON => true,
|
||||
\T_COMMA => true,
|
||||
\T_DOUBLE_ARROW => true,
|
||||
\T_SEMICOLON => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 8.2.0 Now registers all bitshift tokens, not just bitshift right (`T_SR`).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_SL,
|
||||
\T_SL_EQUAL,
|
||||
\T_SR,
|
||||
\T_SR_EQUAL,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
// Determine the start and end of the part of the statement we need to examine.
|
||||
$start = ($stackPtr + 1);
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, $start, null, true);
|
||||
if ($next !== false && $tokens[$next]['code'] === \T_OPEN_PARENTHESIS) {
|
||||
$start = ($next + 1);
|
||||
}
|
||||
|
||||
$end = PHPCSHelper::findEndOfStatement($phpcsFile, $start);
|
||||
if (isset($this->inclusiveStopPoints[$tokens[$end]['code']]) === true) {
|
||||
--$end;
|
||||
}
|
||||
|
||||
if ($this->isNegativeNumber($phpcsFile, $start, $end, true) !== true) {
|
||||
// Not a negative number or undetermined.
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Bitwise shifts by negative number will throw an ArithmeticError in PHP 7.0. Found: %s',
|
||||
$stackPtr,
|
||||
'Found',
|
||||
array($phpcsFile->getTokensAsString($start, ($end - $start + 1)))
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+317
@@ -0,0 +1,317 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Operators;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect use of new PHP operators.
|
||||
*
|
||||
* PHP version All
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/pow-operator
|
||||
* @link https://wiki.php.net/rfc/combined-comparison-operator
|
||||
* @link https://wiki.php.net/rfc/isset_ternary
|
||||
* @link https://wiki.php.net/rfc/null_coalesce_equal_operator
|
||||
*
|
||||
* @since 9.0.0 Detection of new operators was originally included in the
|
||||
* `NewLanguageConstruct` sniff (since 5.6).
|
||||
*/
|
||||
class NewOperatorsSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of new operators, not present in older versions.
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If's sufficient to list the first version where the operator appears.
|
||||
*
|
||||
* @since 5.6
|
||||
*
|
||||
* @var array(string => array(string => bool|string))
|
||||
*/
|
||||
protected $newOperators = array(
|
||||
'T_POW' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => true,
|
||||
'description' => 'power operator (**)',
|
||||
), // Identified in PHP < 5.6 icw PHPCS < 2.4.0 as T_MULTIPLY + T_MULTIPLY.
|
||||
'T_POW_EQUAL' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => true,
|
||||
'description' => 'power assignment operator (**=)',
|
||||
), // Identified in PHP < 5.6 icw PHPCS < 2.6.0 as T_MULTIPLY + T_MUL_EQUAL.
|
||||
'T_SPACESHIP' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'description' => 'spaceship operator (<=>)',
|
||||
), // Identified in PHP < 7.0 icw PHPCS < 2.5.1 as T_IS_SMALLER_OR_EQUAL + T_GREATER_THAN.
|
||||
'T_COALESCE' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'description' => 'null coalescing operator (??)',
|
||||
), // Identified in PHP < 7.0 icw PHPCS < 2.6.2 as T_INLINE_THEN + T_INLINE_THEN.
|
||||
'T_COALESCE_EQUAL' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
'description' => 'null coalesce equal operator (??=)',
|
||||
), // Identified in PHP < 7.0 icw PHPCS < 2.6.2 as T_INLINE_THEN + T_INLINE_THEN + T_EQUAL and between PHPCS 2.6.2 and PHPCS 2.8.1 as T_COALESCE + T_EQUAL.
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* A list of new operators which are not recognized in older PHPCS versions.
|
||||
*
|
||||
* The array lists an alternative token to listen for.
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var array(string => int)
|
||||
*/
|
||||
protected $newOperatorsPHPCSCompat = array(
|
||||
'T_POW' => \T_MULTIPLY,
|
||||
'T_POW_EQUAL' => \T_MUL_EQUAL,
|
||||
'T_SPACESHIP' => \T_GREATER_THAN,
|
||||
'T_COALESCE' => \T_INLINE_THEN,
|
||||
'T_COALESCE_EQUAL' => \T_EQUAL,
|
||||
);
|
||||
|
||||
/**
|
||||
* Token translation table for older PHPCS versions.
|
||||
*
|
||||
* The 'before' index lists the token which would have to be directly before the
|
||||
* token found for it to be one of the new operators.
|
||||
* The 'real_token' index indicates which operator was found in that case.
|
||||
*
|
||||
* If the token combination has multi-layer complexity, such as is the case
|
||||
* with T_COALESCE(_EQUAL), a 'callback' index is added instead pointing to a
|
||||
* separate function which can determine whether this is the targetted token across
|
||||
* PHP and PHPCS versions.
|
||||
*
|
||||
* {@internal 'before' was chosen rather than 'after' as that allowed for a 1-on-1
|
||||
* translation list with the current tokens.}
|
||||
*
|
||||
* @since 7.0.3
|
||||
*
|
||||
* @var array(string => array(string => string))
|
||||
*/
|
||||
protected $PHPCSCompatTranslate = array(
|
||||
'T_MULTIPLY' => array(
|
||||
'before' => 'T_MULTIPLY',
|
||||
'real_token' => 'T_POW',
|
||||
),
|
||||
'T_MUL_EQUAL' => array(
|
||||
'before' => 'T_MULTIPLY',
|
||||
'real_token' => 'T_POW_EQUAL',
|
||||
),
|
||||
'T_GREATER_THAN' => array(
|
||||
'before' => 'T_IS_SMALLER_OR_EQUAL',
|
||||
'real_token' => 'T_SPACESHIP',
|
||||
),
|
||||
'T_INLINE_THEN' => array(
|
||||
'callback' => 'isTCoalesce',
|
||||
'real_token' => 'T_COALESCE',
|
||||
),
|
||||
'T_EQUAL' => array(
|
||||
'callback' => 'isTCoalesceEqual',
|
||||
'real_token' => 'T_COALESCE_EQUAL',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$tokens = array();
|
||||
foreach ($this->newOperators as $token => $versions) {
|
||||
if (\defined($token)) {
|
||||
$tokens[] = constant($token);
|
||||
} elseif (isset($this->newOperatorsPHPCSCompat[$token])) {
|
||||
$tokens[] = $this->newOperatorsPHPCSCompat[$token];
|
||||
}
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.6
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$tokenType = $tokens[$stackPtr]['type'];
|
||||
|
||||
// Translate older PHPCS token combis for new operators to the actual operator.
|
||||
if (isset($this->newOperators[$tokenType]) === false) {
|
||||
if (isset($this->PHPCSCompatTranslate[$tokenType])
|
||||
&& ((isset($this->PHPCSCompatTranslate[$tokenType]['before'], $tokens[$stackPtr - 1]) === true
|
||||
&& $tokens[$stackPtr - 1]['type'] === $this->PHPCSCompatTranslate[$tokenType]['before'])
|
||||
|| (isset($this->PHPCSCompatTranslate[$tokenType]['callback']) === true
|
||||
&& \call_user_func(array($this, $this->PHPCSCompatTranslate[$tokenType]['callback']), $tokens, $stackPtr) === true))
|
||||
) {
|
||||
$tokenType = $this->PHPCSCompatTranslate[$tokenType]['real_token'];
|
||||
}
|
||||
} elseif ($tokenType === 'T_COALESCE') {
|
||||
// Make sure that T_COALESCE is not confused with T_COALESCE_EQUAL.
|
||||
if (isset($tokens[($stackPtr + 1)]) !== false && $tokens[($stackPtr + 1)]['code'] === \T_EQUAL) {
|
||||
// Ignore as will be dealt with via the T_EQUAL token.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the translation did not yield one of the tokens we are looking for, bow out.
|
||||
if (isset($this->newOperators[$tokenType]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $tokenType,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newOperators[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of the non-PHP-version array keys used in a sub-array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('description');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemArray Version and other information about the item.
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrorInfo(array $itemArray, array $itemInfo)
|
||||
{
|
||||
$errorInfo = parent::getErrorInfo($itemArray, $itemInfo);
|
||||
$errorInfo['description'] = $itemArray['description'];
|
||||
|
||||
return $errorInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the error data before it's passed to PHPCS.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $data The error data array which was created.
|
||||
* @param array $itemInfo Base information about the item this error message applies to.
|
||||
* @param array $errorInfo Detail information about an item this error message applies to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function filterErrorData(array $data, array $itemInfo, array $errorInfo)
|
||||
{
|
||||
$data[0] = $errorInfo['description'];
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback function to determine whether a T_EQUAL token is really a T_COALESCE_EQUAL token.
|
||||
*
|
||||
* @since 7.1.2
|
||||
*
|
||||
* @param array $tokens The token stack.
|
||||
* @param int $stackPtr The current position in the token stack.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isTCoalesceEqual($tokens, $stackPtr)
|
||||
{
|
||||
if ($tokens[$stackPtr]['code'] !== \T_EQUAL || isset($tokens[($stackPtr - 1)]) === false) {
|
||||
// Function called for wrong token or token has no predecessor.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($tokens[($stackPtr - 1)]['type'] === 'T_COALESCE') {
|
||||
return true;
|
||||
}
|
||||
if ($tokens[($stackPtr - 1)]['type'] === 'T_INLINE_THEN'
|
||||
&& (isset($tokens[($stackPtr - 2)]) && $tokens[($stackPtr - 2)]['type'] === 'T_INLINE_THEN')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function to determine whether a T_INLINE_THEN token is really a T_COALESCE token.
|
||||
*
|
||||
* @since 7.1.2
|
||||
*
|
||||
* @param array $tokens The token stack.
|
||||
* @param int $stackPtr The current position in the token stack.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isTCoalesce($tokens, $stackPtr)
|
||||
{
|
||||
if ($tokens[$stackPtr]['code'] !== \T_INLINE_THEN || isset($tokens[($stackPtr - 1)]) === false) {
|
||||
// Function called for wrong token or token has no predecessor.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($tokens[($stackPtr - 1)]['code'] === \T_INLINE_THEN) {
|
||||
// Make sure not to confuse it with the T_COALESCE_EQUAL token.
|
||||
if (isset($tokens[($stackPtr + 1)]) === false || $tokens[($stackPtr + 1)]['code'] !== \T_EQUAL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
vendor/phpcompatibility/php-compatibility/PHPCompatibility/Sniffs/Operators/NewShortTernarySniff.php
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Operators;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect usage of the short ternary (elvis) operator as introduced in PHP 5.3.
|
||||
*
|
||||
* Performs checks on ternary operators, specifically that the middle expression
|
||||
* is not omitted for versions that don't support this.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration53.new-features.php
|
||||
* @link https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @since 7.0.8 This sniff now throws an error instead of a warning.
|
||||
* @since 9.0.0 Renamed from `TernaryOperatorsSniff` to `NewShortTernarySniff`.
|
||||
*/
|
||||
class NewShortTernarySniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_INLINE_THEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.2') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isShortTernary($phpcsFile, $stackPtr) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Middle may not be omitted from ternary operators in PHP < 5.3',
|
||||
$stackPtr,
|
||||
'MiddleMissing'
|
||||
);
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Operators;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* The left-associativity of the ternary operator is deprecated in PHP 7.4 and
|
||||
* removed in PHP 8.0.
|
||||
*
|
||||
* PHP version 7.4
|
||||
* PHP version 8.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.deprecated.php#migration74.deprecated.core.nested-ternary
|
||||
* @link https://wiki.php.net/rfc/ternary_associativity
|
||||
* @link https://github.com/php/php-src/pull/4017
|
||||
*
|
||||
* @since 9.2.0
|
||||
*/
|
||||
class RemovedTernaryAssociativitySniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* List of tokens with a lower operator precedence than ternary.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $tokensWithLowerPrecedence = array(
|
||||
'T_YIELD_FROM' => true,
|
||||
'T_YIELD' => true,
|
||||
'T_LOGICAL_AND' => true,
|
||||
'T_LOGICAL_OR' => true,
|
||||
'T_LOGICAL_XOR' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_INLINE_THEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.4') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$endOfStatement = PHPCSHelper::findEndOfStatement($phpcsFile, $stackPtr);
|
||||
if ($tokens[$endOfStatement]['code'] !== \T_SEMICOLON
|
||||
&& $tokens[$endOfStatement]['code'] !== \T_COLON
|
||||
&& $tokens[$endOfStatement]['code'] !== \T_COMMA
|
||||
&& $tokens[$endOfStatement]['code'] !== \T_DOUBLE_ARROW
|
||||
&& $tokens[$endOfStatement]['code'] !== \T_OPEN_TAG
|
||||
&& $tokens[$endOfStatement]['code'] !== \T_CLOSE_TAG
|
||||
) {
|
||||
// End of statement is last non-empty before close brace, so make sure we examine that token too.
|
||||
++$endOfStatement;
|
||||
}
|
||||
|
||||
$ternaryCount = 0;
|
||||
$elseCount = 0;
|
||||
$shortTernaryCount = 0;
|
||||
|
||||
// Start at $stackPtr so we don't need duplicate code for short ternary determination.
|
||||
for ($i = $stackPtr; $i < $endOfStatement; $i++) {
|
||||
if (($tokens[$i]['code'] === \T_OPEN_SHORT_ARRAY
|
||||
|| $tokens[$i]['code'] === \T_OPEN_SQUARE_BRACKET
|
||||
|| $tokens[$i]['code'] === \T_OPEN_CURLY_BRACKET)
|
||||
&& isset($tokens[$i]['bracket_closer'])
|
||||
) {
|
||||
// Skip over short arrays, array access keys and curlies.
|
||||
$i = $tokens[$i]['bracket_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS
|
||||
&& isset($tokens[$i]['parenthesis_closer'])
|
||||
) {
|
||||
// Skip over anything between parentheses.
|
||||
$i = $tokens[$i]['parenthesis_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for operators with lower operator precedence.
|
||||
if (isset(Tokens::$assignmentTokens[$tokens[$i]['code']])
|
||||
|| isset($this->tokensWithLowerPrecedence[$tokens[$i]['code']])
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] === \T_INLINE_THEN) {
|
||||
++$ternaryCount;
|
||||
|
||||
if ($this->isShortTernary($phpcsFile, $i) === true) {
|
||||
++$shortTernaryCount;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] === \T_INLINE_ELSE) {
|
||||
if (($ternaryCount - $elseCount) >= 2) {
|
||||
// This is the `else` for a ternary in the middle part of a previous ternary.
|
||||
--$ternaryCount;
|
||||
} else {
|
||||
++$elseCount;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ternaryCount > 1 && $ternaryCount === $elseCount && $ternaryCount > $shortTernaryCount) {
|
||||
$message = 'The left-associativity of the ternary operator has been deprecated in PHP 7.4';
|
||||
$isError = false;
|
||||
if ($this->supportsAbove('8.0') === true) {
|
||||
$message .= ' and removed in PHP 8.0';
|
||||
$isError = true;
|
||||
}
|
||||
|
||||
$message .= '. Multiple consecutive ternaries detected. Use parenthesis to clarify the order in which the operations should be executed';
|
||||
|
||||
$this->addMessage($phpcsFile, $message, $stackPtr, $isError);
|
||||
}
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect: Passing `null` to `get_class()` is no longer allowed as of PHP 7.2.
|
||||
* This will now result in an `E_WARNING` being thrown.
|
||||
*
|
||||
* PHP version 7.2
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/get_class_disallow_null_parameter
|
||||
* @link https://www.php.net/manual/en/function.get-class.php#refsect1-function.get-class-changelog
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class ForbiddenGetClassNullSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'get_class' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('7.2') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[1]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($parameters[1]['raw'] !== 'null') {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Passing "null" as the $object to get_class() is not allowed since PHP 7.2.',
|
||||
$parameters[1]['start'],
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Since PHP 5.3.4, `strip_tags()` ignores self-closing XHTML tags in allowable_tags
|
||||
*
|
||||
* PHP version 5.3.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.strip-tags.php#refsect1-function.strip-tags-changelog
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class ForbiddenStripTagsSelfClosingXHTMLSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'strip_tags' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Text string tokens to examine.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $textStringTokens = array(
|
||||
\T_CONSTANT_ENCAPSED_STRING => true,
|
||||
\T_DOUBLE_QUOTED_STRING => true,
|
||||
\T_INLINE_HTML => true,
|
||||
\T_HEREDOC => true,
|
||||
\T_NOWDOC => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('5.4') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[2]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$targetParam = $parameters[2];
|
||||
for ($i = $targetParam['start']; $i <= $targetParam['end']; $i++) {
|
||||
if ($tokens[$i]['code'] === \T_STRING
|
||||
|| $tokens[$i]['code'] === \T_VARIABLE
|
||||
) {
|
||||
// Variable, constant, function call. Ignore as undetermined.
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->textStringTokens[$tokens[$i]['code']]) === true
|
||||
&& strpos($tokens[$i]['content'], '/>') !== false
|
||||
) {
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Self-closing XHTML tags are ignored. Only non-self-closing tags should be used in the strip_tags() $allowable_tags parameter since PHP 5.3.4. Found: %s',
|
||||
$i,
|
||||
'Found',
|
||||
array($targetParam['raw'])
|
||||
);
|
||||
|
||||
// Only throw one error per function call.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* In PHP 5.2 and lower, the `$initial` parameter for `array_reduce()` had to be an integer.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration53.other.php#migration53.other
|
||||
* @link https://www.php.net/manual/en/function.array-reduce.php#refsect1-function.array-reduce-changelog
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class NewArrayReduceInitialTypeSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'array_reduce' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokens which, for the purposes of this sniff, indicate that there is
|
||||
* a variable element to the value passed.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $variableValueTokens = array(
|
||||
\T_VARIABLE,
|
||||
\T_STRING,
|
||||
\T_SELF,
|
||||
\T_PARENT,
|
||||
\T_STATIC,
|
||||
\T_DOUBLE_QUOTED_STRING,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('5.2') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[3]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$targetParam = $parameters[3];
|
||||
if ($this->isNumber($phpcsFile, $targetParam['start'], $targetParam['end'], true) !== false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isNumericCalculation($phpcsFile, $targetParam['start'], $targetParam['end']) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$error = 'Passing a non-integer as the value for $initial to array_reduce() is not supported in PHP 5.2 or lower.';
|
||||
if ($phpcsFile->findNext($this->variableValueTokens, $targetParam['start'], ($targetParam['end'] + 1)) === false) {
|
||||
$phpcsFile->addError(
|
||||
$error . ' Found %s',
|
||||
$targetParam['start'],
|
||||
'InvalidTypeFound',
|
||||
array($targetParam['raw'])
|
||||
);
|
||||
} else {
|
||||
$phpcsFile->addWarning(
|
||||
$error . ' Variable value found. Found %s',
|
||||
$targetParam['start'],
|
||||
'VariableFound',
|
||||
array($targetParam['raw'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Check for valid values for the `fopen()` `$mode` parameter.
|
||||
*
|
||||
* PHP version 5.2+
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.fopen.php#refsect1-function.fopen-changelog
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class NewFopenModesSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'fopen' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
// Version used here should be (above) the highest version from the `newModes` control,
|
||||
// structure below, i.e. the last PHP version in which a new mode was introduced.
|
||||
return ($this->supportsBelow('7.1') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[2]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$targetParam = $parameters[2];
|
||||
$errors = array();
|
||||
|
||||
for ($i = $targetParam['start']; $i <= $targetParam['end']; $i++) {
|
||||
if ($tokens[$i]['code'] !== \T_CONSTANT_ENCAPSED_STRING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strpos($tokens[$i]['content'], 'c+') !== false && $this->supportsBelow('5.2.5')) {
|
||||
$errors['cplusFound'] = array(
|
||||
'c+',
|
||||
'5.2.5',
|
||||
$targetParam['raw'],
|
||||
);
|
||||
} elseif (strpos($tokens[$i]['content'], 'c') !== false && $this->supportsBelow('5.2.5')) {
|
||||
$errors['cFound'] = array(
|
||||
'c',
|
||||
'5.2.5',
|
||||
$targetParam['raw'],
|
||||
);
|
||||
}
|
||||
|
||||
if (strpos($tokens[$i]['content'], 'e') !== false && $this->supportsBelow('7.0.15')) {
|
||||
$errors['eFound'] = array(
|
||||
'e',
|
||||
'7.0.15',
|
||||
$targetParam['raw'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($errors) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($errors as $errorCode => $errorData) {
|
||||
$phpcsFile->addError(
|
||||
'Passing "%s" as the $mode to fopen() is not supported in PHP %s or lower. Found %s',
|
||||
$targetParam['start'],
|
||||
$errorCode,
|
||||
$errorData
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* As of PHP 5.4, the default character set for `htmlspecialchars()`, `htmlentities()`
|
||||
* and `html_entity_decode()` is now `UTF-8`, instead of `ISO-8859-1`.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration54.other.php
|
||||
* @link https://www.php.net/manual/en/function.html-entity-decode.php#refsect1-function.html-entity-decode-changelog
|
||||
* @link https://www.php.net/manual/en/function.htmlentities.php#refsect1-function.htmlentities-changelog
|
||||
* @link https://www.php.net/manual/en/function.htmlspecialchars.php#refsect1-function.htmlspecialchars-changelog
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class NewHTMLEntitiesEncodingDefaultSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* Key is the function name, value the 1-based parameter position of
|
||||
* the $encoding parameter.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'html_entity_decode' => 3,
|
||||
'htmlentities' => 3,
|
||||
'htmlspecialchars' => 3,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* Note: This sniff should only trigger errors when both PHP 5.3 or lower,
|
||||
* as well as PHP 5.4 or higher need to be supported within the application.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('5.3') === false || $this->supportsAbove('5.4') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
$functionLC = strtolower($functionName);
|
||||
if (isset($parameters[$this->targetFunctions[$functionLC]]) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'The default value of the $encoding parameter for %s() was changed from ISO-8859-1 to UTF-8 in PHP 5.4. For cross-version compatibility, the $encoding parameter should be explicitly set.',
|
||||
$stackPtr,
|
||||
'NotSet',
|
||||
array($functionName)
|
||||
);
|
||||
}
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect the use of newly introduced hash algorithms.
|
||||
*
|
||||
* PHP version 5.2+
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.hash-algos.php#refsect1-function.hash-algos-changelog
|
||||
*
|
||||
* @since 7.0.7
|
||||
* @since 7.1.0 Now extends the `AbstractNewFeatureSniff` instead of the base `Sniff` class..
|
||||
*/
|
||||
class NewHashAlgorithmsSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
/**
|
||||
* A list of new hash algorithms, not present in older versions.
|
||||
*
|
||||
* The array lists : version number with false (not present) or true (present).
|
||||
* If's sufficient to list the first version where the hash algorithm appears.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @var array(string => array(string => bool))
|
||||
*/
|
||||
protected $newAlgorithms = array(
|
||||
'md2' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'ripemd256' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'ripemd320' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'salsa10' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'salsa20' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'snefru256' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'sha224' => array(
|
||||
'5.2' => false,
|
||||
'5.3' => true,
|
||||
),
|
||||
'joaat' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'fnv132' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'fnv164' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
),
|
||||
'gost-crypto' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => true,
|
||||
),
|
||||
|
||||
'sha512/224' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'sha512/256' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'sha3-224' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'sha3-256' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'sha3-384' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'sha3-512' => array(
|
||||
'7.0' => false,
|
||||
'7.1' => true,
|
||||
),
|
||||
'crc32c' => array(
|
||||
'7.3' => false,
|
||||
'7.4' => true,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$algo = $this->getHashAlgorithmParameter($phpcsFile, $stackPtr);
|
||||
if (empty($algo) || \is_string($algo) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bow out if not one of the algorithms we're targetting.
|
||||
if (isset($this->newAlgorithms[$algo]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the algorithm used is new.
|
||||
$itemInfo = array(
|
||||
'name' => $algo,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newAlgorithms[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The %s hash algorithm is not present in PHP version %s or earlier';
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* The default value for the `$variant` parameter has changed from `INTL_IDNA_VARIANT_2003`
|
||||
* to `INTL_IDNA_VARIANT_UTS46` in PHP 7.4.
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.incompatible.php#migration74.incompatible.intl
|
||||
* @link https://wiki.php.net/rfc/deprecate-and-remove-intl_idna_variant_2003
|
||||
* @link https://www.php.net/manual/en/function.idn-to-ascii.php
|
||||
* @link https://www.php.net/manual/en/function.idn-to-utf8.php
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class NewIDNVariantDefaultSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* Key is the function name, value the 1-based parameter position of
|
||||
* the $variant parameter.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'idn_to_ascii' => 3,
|
||||
'idn_to_utf8' => 3,
|
||||
);
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* Note: This sniff should only trigger errors when both PHP 7.3 or lower,
|
||||
* as well as PHP 7.4 or higher need to be supported within the application.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('7.3') === false || $this->supportsAbove('7.4') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
$functionLC = strtolower($functionName);
|
||||
if (isset($parameters[$this->targetFunctions[$functionLC]]) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$error = 'The default value of the %s() $variant parameter has changed from INTL_IDNA_VARIANT_2003 to INTL_IDNA_VARIANT_UTS46 in PHP 7.4. For optimal cross-version compatibility, the $variant parameter should be explicitly set.';
|
||||
$phpcsFile->addError(
|
||||
$error,
|
||||
$stackPtr,
|
||||
'NotSet',
|
||||
array($functionName)
|
||||
);
|
||||
}
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect calls to Iconv and Mbstring functions with the optional `$charset`/`$encoding` parameter not set.
|
||||
*
|
||||
* The default value for the iconv `$charset` and the MbString $encoding` parameters was changed
|
||||
* in PHP 5.6 to the value of `default_charset`, which defaults to `UTF-8`.
|
||||
*
|
||||
* Previously, the iconv functions would default to the value of `iconv.internal_encoding`;
|
||||
* The Mbstring functions would default to the return value of `mb_internal_encoding()`.
|
||||
* In both case, this would normally come down to `ISO-8859-1`.
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration56.new-features.php#migration56.new-features.default-encoding
|
||||
* @link https://www.php.net/manual/en/migration56.deprecated.php#migration56.deprecated.iconv-mbstring-encoding
|
||||
* @link https://wiki.php.net/rfc/default_encoding
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class NewIconvMbstringCharsetDefaultSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* Only those functions where the charset/encoding parameter is optional need to be listed.
|
||||
*
|
||||
* Key is the function name, value the 1-based parameter position of
|
||||
* the $charset/$encoding parameter.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'iconv_mime_decode_headers' => 3,
|
||||
'iconv_mime_decode' => 3,
|
||||
'iconv_mime_encode' => 3, // Special case.
|
||||
'iconv_strlen' => 2,
|
||||
'iconv_strpos' => 4,
|
||||
'iconv_strrpos' => 3,
|
||||
'iconv_substr' => 4,
|
||||
|
||||
'mb_check_encoding' => 2,
|
||||
'mb_chr' => 2,
|
||||
'mb_convert_case' => 3,
|
||||
'mb_convert_encoding' => 3,
|
||||
'mb_convert_kana' => 3,
|
||||
'mb_decode_numericentity' => 3,
|
||||
'mb_encode_numericentity' => 3,
|
||||
'mb_ord' => 2,
|
||||
'mb_scrub' => 2,
|
||||
'mb_strcut' => 4,
|
||||
'mb_stripos' => 4,
|
||||
'mb_stristr' => 4,
|
||||
'mb_strlen' => 2,
|
||||
'mb_strpos' => 4,
|
||||
'mb_strrchr' => 4,
|
||||
'mb_strrichr' => 4,
|
||||
'mb_strripos' => 4,
|
||||
'mb_strrpos' => 4,
|
||||
'mb_strstr' => 4,
|
||||
'mb_strtolower' => 2,
|
||||
'mb_strtoupper' => 2,
|
||||
'mb_strwidth' => 2,
|
||||
'mb_substr_count' => 3,
|
||||
'mb_substr' => 4,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* Note: This sniff should only trigger errors when both PHP 5.5 or lower,
|
||||
* as well as PHP 5.6 or higher need to be supported within the application.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('5.5') === false || $this->supportsAbove('5.6') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
$functionLC = strtolower($functionName);
|
||||
if ($functionLC === 'iconv_mime_encode') {
|
||||
// Special case the iconv_mime_encode() function.
|
||||
return $this->processIconvMimeEncode($phpcsFile, $stackPtr, $functionName, $parameters);
|
||||
}
|
||||
|
||||
if (isset($parameters[$this->targetFunctions[$functionLC]]) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paramName = '$encoding';
|
||||
if (strpos($functionLC, 'iconv_') === 0) {
|
||||
$paramName = '$charset';
|
||||
} elseif ($functionLC === 'mb_convert_encoding') {
|
||||
$paramName = '$from_encoding';
|
||||
}
|
||||
|
||||
$error = 'The default value of the %1$s parameter for %2$s() was changed from ISO-8859-1 to UTF-8 in PHP 5.6. For cross-version compatibility, the %1$s parameter should be explicitly set.';
|
||||
$data = array(
|
||||
$paramName,
|
||||
$functionName,
|
||||
);
|
||||
|
||||
$phpcsFile->addError($error, $stackPtr, 'NotSet', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched call to the iconv_mime_encode() function.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processIconvMimeEncode(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
$error = 'The default value of the %s parameter index for iconv_mime_encode() was changed from ISO-8859-1 to UTF-8 in PHP 5.6. For cross-version compatibility, the %s should be explicitly set.';
|
||||
|
||||
$functionLC = strtolower($functionName);
|
||||
if (isset($parameters[$this->targetFunctions[$functionLC]]) === false) {
|
||||
$phpcsFile->addError(
|
||||
$error,
|
||||
$stackPtr,
|
||||
'PreferencesNotSet',
|
||||
array(
|
||||
'$preferences[\'input/output-charset\']',
|
||||
'$preferences[\'input-charset\'] and $preferences[\'output-charset\'] indexes',
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$targetParam = $parameters[$this->targetFunctions[$functionLC]];
|
||||
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $targetParam['start'], ($targetParam['end'] + 1), true);
|
||||
if ($firstNonEmpty === false) {
|
||||
// Parse error or live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$firstNonEmpty]['code'] === \T_ARRAY
|
||||
|| $tokens[$firstNonEmpty]['code'] === \T_OPEN_SHORT_ARRAY
|
||||
) {
|
||||
$hasInputCharset = preg_match('`([\'"])input-charset\1\s*=>`', $targetParam['raw']);
|
||||
$hasOutputCharset = preg_match('`([\'"])output-charset\1\s*=>`', $targetParam['raw']);
|
||||
if ($hasInputCharset === 1 && $hasOutputCharset === 1) {
|
||||
// Both input as well as output charset are set.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($hasInputCharset !== 1) {
|
||||
$phpcsFile->addError(
|
||||
$error,
|
||||
$firstNonEmpty,
|
||||
'InputPreferenceNotSet',
|
||||
array(
|
||||
'$preferences[\'input-charset\']',
|
||||
'$preferences[\'input-charset\'] index',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($hasOutputCharset !== 1) {
|
||||
$phpcsFile->addError(
|
||||
$error,
|
||||
$firstNonEmpty,
|
||||
'OutputPreferenceNotSet',
|
||||
array(
|
||||
'$preferences[\'output-charset\']',
|
||||
'$preferences[\'output-charset\'] index',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The $preferences parameter was passed, but it was a variable/constant/output of a function call.
|
||||
$phpcsFile->addWarning(
|
||||
$error,
|
||||
$firstNonEmpty,
|
||||
'Undetermined',
|
||||
array(
|
||||
'$preferences[\'input/output-charset\']',
|
||||
'$preferences[\'input-charset\'] and $preferences[\'output-charset\'] indexes',
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect negative string offsets as parameters passed to functions where this
|
||||
* was not allowed prior to PHP 7.1.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/negative-string-offsets
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class NewNegativeStringOffsetSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array Function name => 1-based parameter offset of the affected parameters => parameter name.
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'file_get_contents' => array(
|
||||
4 => 'offset',
|
||||
),
|
||||
'grapheme_extract' => array(
|
||||
4 => 'start',
|
||||
),
|
||||
'grapheme_stripos' => array(
|
||||
3 => 'offset',
|
||||
),
|
||||
'grapheme_strpos' => array(
|
||||
3 => 'offset',
|
||||
),
|
||||
'iconv_strpos' => array(
|
||||
3 => 'offset',
|
||||
),
|
||||
'mb_ereg_search_setpos' => array(
|
||||
1 => 'position',
|
||||
),
|
||||
'mb_strimwidth' => array(
|
||||
2 => 'start',
|
||||
3 => 'width',
|
||||
),
|
||||
'mb_stripos' => array(
|
||||
3 => 'offset',
|
||||
),
|
||||
'mb_strpos' => array(
|
||||
3 => 'offset',
|
||||
),
|
||||
'stripos' => array(
|
||||
3 => 'offset',
|
||||
),
|
||||
'strpos' => array(
|
||||
3 => 'offset',
|
||||
),
|
||||
'substr_count' => array(
|
||||
3 => 'offset',
|
||||
4 => 'length',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('7.0') === false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
$functionLC = strtolower($functionName);
|
||||
foreach ($this->targetFunctions[$functionLC] as $pos => $name) {
|
||||
if (isset($parameters[$pos]) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$targetParam = $parameters[$pos];
|
||||
|
||||
if ($this->isNegativeNumber($phpcsFile, $targetParam['start'], $targetParam['end']) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Negative string offsets were not supported for the $%s parameter in %s() in PHP 7.0 or lower. Found %s',
|
||||
$targetParam['start'],
|
||||
'Found',
|
||||
array(
|
||||
$name,
|
||||
$functionName,
|
||||
$targetParam['raw'],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\Sniffs\ParameterValues\RemovedPCREModifiersSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Check for the use of newly added regex modifiers for PCRE functions.
|
||||
*
|
||||
* Initially just checks for the PHP 7.2 new `J` modifier.
|
||||
*
|
||||
* PHP version 7.2+
|
||||
*
|
||||
* @link https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
|
||||
* @link https://www.php.net/manual/en/migration72.new-features.php#migration72.new-features.pcre
|
||||
*
|
||||
* @since 8.2.0
|
||||
* @since 9.0.0 Renamed from `PCRENewModifiersSniff` to `NewPCREModifiersSniff`.
|
||||
*/
|
||||
class NewPCREModifiersSniff extends RemovedPCREModifiersSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'preg_filter' => true,
|
||||
'preg_grep' => true,
|
||||
'preg_match_all' => true,
|
||||
'preg_match' => true,
|
||||
'preg_replace_callback_array' => true,
|
||||
'preg_replace_callback' => true,
|
||||
'preg_replace' => true,
|
||||
'preg_split' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Array listing newly introduced regex modifiers.
|
||||
*
|
||||
* The key should be the modifier (case-sensitive!).
|
||||
* The value should be the PHP version in which the modifier was introduced.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $newModifiers = array(
|
||||
'J' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
// Version used here should be the highest version from the `$newModifiers` array,
|
||||
// i.e. the last PHP version in which a new modifier was introduced.
|
||||
return ($this->supportsBelow('7.2') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Examine the regex modifier string.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
* @param string $functionName The function which contained the pattern.
|
||||
* @param string $modifiers The regex modifiers found.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function examineModifiers(File $phpcsFile, $stackPtr, $functionName, $modifiers)
|
||||
{
|
||||
$error = 'The PCRE regex modifier "%s" is not present in PHP version %s or earlier';
|
||||
|
||||
foreach ($this->newModifiers as $modifier => $versionArray) {
|
||||
if (strpos($modifiers, $modifier) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$notInVersion = '';
|
||||
foreach ($versionArray as $version => $present) {
|
||||
if ($notInVersion === '' && $present === false
|
||||
&& $this->supportsBelow($version) === true
|
||||
) {
|
||||
$notInVersion = $version;
|
||||
}
|
||||
}
|
||||
|
||||
if ($notInVersion === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$errorCode = $modifier . 'ModifierFound';
|
||||
$data = array(
|
||||
$modifier,
|
||||
$notInVersion,
|
||||
);
|
||||
|
||||
$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Check for valid values for the `$format` passed to `pack()`.
|
||||
*
|
||||
* PHP version 5.4+
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.pack.php#refsect1-function.pack-changelog
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class NewPackFormatSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'pack' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* List of new format character codes added to pack().
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array Regex pattern => Version array.
|
||||
*/
|
||||
protected $newFormats = array(
|
||||
'`([Z])`' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => true,
|
||||
),
|
||||
'`([qQJP])`' => array(
|
||||
'5.6.2' => false,
|
||||
'5.6.3' => true,
|
||||
),
|
||||
'`([eEgG])`' => array(
|
||||
'7.0.14' => false,
|
||||
'7.0.15' => true, // And 7.1.1.
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('7.1') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[1]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$targetParam = $parameters[1];
|
||||
|
||||
for ($i = $targetParam['start']; $i <= $targetParam['end']; $i++) {
|
||||
if ($tokens[$i]['code'] !== \T_CONSTANT_ENCAPSED_STRING
|
||||
&& $tokens[$i]['code'] !== \T_DOUBLE_QUOTED_STRING
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$content = $tokens[$i]['content'];
|
||||
if ($tokens[$i]['code'] === \T_DOUBLE_QUOTED_STRING) {
|
||||
$content = $this->stripVariables($content);
|
||||
}
|
||||
|
||||
foreach ($this->newFormats as $pattern => $versionArray) {
|
||||
if (preg_match($pattern, $content, $matches) !== 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($versionArray as $version => $present) {
|
||||
if ($present === false && $this->supportsBelow($version) === true) {
|
||||
$phpcsFile->addError(
|
||||
'Passing the $format(s) "%s" to pack() is not supported in PHP %s or lower. Found %s',
|
||||
$targetParam['start'],
|
||||
'NewFormatFound',
|
||||
array(
|
||||
$matches[1],
|
||||
$version,
|
||||
$targetParam['raw'],
|
||||
)
|
||||
);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* The constant value of the password hash algorithm constants has changed in PHP 7.4.
|
||||
*
|
||||
* Applications using the constants `PASSWORD_DEFAULT`, `PASSWORD_BCRYPT`,
|
||||
* `PASSWORD_ARGON2I`, and `PASSWORD_ARGON2ID` will continue to function correctly.
|
||||
* Using an integer will still work, but will produce a deprecation warning.
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.incompatible.php#migration74.incompatible.core.password-algorithm-constants
|
||||
* @link https://wiki.php.net/rfc/password_registry
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class NewPasswordAlgoConstantValuesSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* Key is the function name, value the 1-based parameter position of
|
||||
* the $algo parameter.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'password_hash' => 2,
|
||||
'password_needs_rehash' => 2,
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokens types which indicate that the parameter passed is not the PHP native constant.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $invalidTokenTypes = array(
|
||||
\T_NULL => true,
|
||||
\T_TRUE => true,
|
||||
\T_FALSE => true,
|
||||
\T_LNUMBER => true,
|
||||
\T_DNUMBER => true,
|
||||
\T_CONSTANT_ENCAPSED_STRING => true,
|
||||
\T_DOUBLE_QUOTED_STRING => true,
|
||||
\T_HEREDOC => true,
|
||||
\T_NOWDOC => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('7.4') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
$functionLC = strtolower($functionName);
|
||||
if (isset($parameters[$this->targetFunctions[$functionLC]]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$targetParam = $parameters[$this->targetFunctions[$functionLC]];
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
for ($i = $targetParam['start']; $i <= $targetParam['end']; $i++) {
|
||||
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($this->invalidTokenTypes[$tokens[$i]['code']]) === true) {
|
||||
$phpcsFile->addWarning(
|
||||
'The value of the password hash algorithm constants has changed in PHP 7.4. Pass a PHP native constant to the %s() function instead of using the value of the constant. Found: %s',
|
||||
$stackPtr,
|
||||
'NotAlgoConstant',
|
||||
array(
|
||||
$functionName,
|
||||
$targetParam['raw'],
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* As of PHP 7.4, `proc_open()` now also accepts an array instead of a string for the command.
|
||||
*
|
||||
* In that case, the process will be opened directly (without going through a shell) and
|
||||
* PHP will take care of any necessary argument escaping.
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.new-features.php#migration74.new-features.standard.proc-open
|
||||
* @link https://www.php.net/manual/en/function.proc-open.php
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class NewProcOpenCmdArraySniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'proc_open' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[1]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$targetParam = $parameters[1];
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $targetParam['start'], $targetParam['end'], true);
|
||||
|
||||
if ($nextNonEmpty === false) {
|
||||
// Shouldn't be possible.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$nextNonEmpty]['code'] !== \T_ARRAY
|
||||
&& $tokens[$nextNonEmpty]['code'] !== \T_OPEN_SHORT_ARRAY
|
||||
) {
|
||||
// Not passed as an array.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->supportsBelow('7.3') === true) {
|
||||
$phpcsFile->addError(
|
||||
'The proc_open() function did not accept $cmd to be passed in array format in PHP 7.3 and earlier.',
|
||||
$nextNonEmpty,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->supportsAbove('7.4') === true) {
|
||||
if (strpos($targetParam['raw'], 'escapeshellarg(') === false) {
|
||||
// Efficiency: prevent needlessly walking the array.
|
||||
return;
|
||||
}
|
||||
|
||||
$items = $this->getFunctionCallParameters($phpcsFile, $nextNonEmpty);
|
||||
|
||||
if (empty($items)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
for ($i = $item['start']; $i <= $item['end']; $i++) {
|
||||
if ($tokens[$i]['code'] !== \T_STRING
|
||||
|| $tokens[$i]['content'] !== 'escapeshellarg'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// @todo Potential future enhancement: check if it's a call to the PHP native function.
|
||||
|
||||
$phpcsFile->addWarning(
|
||||
'When passing proc_open() the $cmd parameter as an array, PHP will take care of any necessary argument escaping. Found: %s',
|
||||
$i,
|
||||
'Invalid',
|
||||
array($item['raw'])
|
||||
);
|
||||
|
||||
// Only throw one error per array item.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* As of PHP 7.4, `strip_tags()` now also accepts an array of `$allowable_tags`.
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.new-features.php#migration74.new-features.standard.strip-tags
|
||||
* @link https://www.php.net/manual/en/function.strip-tags.php
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class NewStripTagsAllowableTagsArraySniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'strip_tags' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Text string tokens to examine.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $textStringTokens = array(
|
||||
\T_CONSTANT_ENCAPSED_STRING => true,
|
||||
\T_DOUBLE_QUOTED_STRING => true,
|
||||
\T_INLINE_HTML => true,
|
||||
\T_HEREDOC => true,
|
||||
\T_NOWDOC => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[2]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$targetParam = $parameters[2];
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $targetParam['start'], $targetParam['end'], true);
|
||||
|
||||
if ($nextNonEmpty === false) {
|
||||
// Shouldn't be possible.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$nextNonEmpty]['code'] !== \T_ARRAY
|
||||
&& $tokens[$nextNonEmpty]['code'] !== \T_OPEN_SHORT_ARRAY
|
||||
) {
|
||||
// Not passed as a hard-coded array.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->supportsBelow('7.3') === true) {
|
||||
$phpcsFile->addError(
|
||||
'The strip_tags() function did not accept $allowable_tags to be passed in array format in PHP 7.3 and earlier.',
|
||||
$nextNonEmpty,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->supportsAbove('7.4') === true) {
|
||||
if (strpos($targetParam['raw'], '>') === false) {
|
||||
// Efficiency: prevent needlessly walking the array.
|
||||
return;
|
||||
}
|
||||
|
||||
$items = $this->getFunctionCallParameters($phpcsFile, $nextNonEmpty);
|
||||
|
||||
if (empty($items)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
for ($i = $item['start']; $i <= $item['end']; $i++) {
|
||||
if ($tokens[$i]['code'] === \T_STRING
|
||||
|| $tokens[$i]['code'] === \T_VARIABLE
|
||||
) {
|
||||
// Variable, constant, function call. Ignore complete item as undetermined.
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($this->textStringTokens[$tokens[$i]['code']]) === true
|
||||
&& strpos($tokens[$i]['content'], '>') !== false
|
||||
) {
|
||||
|
||||
$phpcsFile->addWarning(
|
||||
'When passing strip_tags() the $allowable_tags parameter as an array, the tags should not be enclosed in <> brackets. Found: %s',
|
||||
$i,
|
||||
'Invalid',
|
||||
array($item['raw'])
|
||||
);
|
||||
|
||||
// Only throw one error per array item.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect the use of deprecated and removed hash algorithms.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.hash-algos.php#refsect1-function.hash-algos-changelog
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 7.1.0 Now extends the `AbstractRemovedFeatureSniff` instead of the base `Sniff` class.
|
||||
*/
|
||||
class RemovedHashAlgorithmsSniff extends AbstractRemovedFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of removed hash algorithms, which were present in older versions.
|
||||
*
|
||||
* The array lists : version number with false (deprecated) and true (removed).
|
||||
* If's sufficient to list the first version where the hash algorithm was deprecated/removed.
|
||||
*
|
||||
* @since 7.0.7
|
||||
*
|
||||
* @var array(string => array(string => bool))
|
||||
*/
|
||||
protected $removedAlgorithms = array(
|
||||
'salsa10' => array(
|
||||
'5.4' => true,
|
||||
),
|
||||
'salsa20' => array(
|
||||
'5.4' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(\T_STRING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$algo = $this->getHashAlgorithmParameter($phpcsFile, $stackPtr);
|
||||
if (empty($algo) || \is_string($algo) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bow out if not one of the algorithms we're targetting.
|
||||
if (isset($this->removedAlgorithms[$algo]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $algo,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->removedAlgorithms[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The %s hash algorithm is ';
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect passing deprecated `$type` values to `iconv_get_encoding()`.
|
||||
*
|
||||
* "The iconv and mbstring configuration options related to encoding have been
|
||||
* deprecated in favour of default_charset."
|
||||
*
|
||||
* {@internal It is unclear which mbstring functions should be targetted, so for now,
|
||||
* only the iconv function is handled.}
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration56.deprecated.php#migration56.deprecated.iconv-mbstring-encoding
|
||||
* @link https://wiki.php.net/rfc/default_encoding
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class RemovedIconvEncodingSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'iconv_set_encoding' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('5.6') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[1]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addWarning(
|
||||
'All previously accepted values for the $type parameter of iconv_set_encoding() have been deprecated since PHP 5.6. Found %s',
|
||||
$parameters[1]['start'],
|
||||
'DeprecatedValueFound',
|
||||
$parameters[1]['raw']
|
||||
);
|
||||
}
|
||||
}
|
||||
+323
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Passing the `$glue` and `$pieces` parameters to `implode()` in reverse order has
|
||||
* been deprecated in PHP 7.4.
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.deprecated.php#migration74.deprecated.core.implode-reverse-parameters
|
||||
* @link https://wiki.php.net/rfc/deprecations_php_7_4#implode_parameter_order_mix
|
||||
* @link https://php.net/manual/en/function.implode.php
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class RemovedImplodeFlexibleParamOrderSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'implode' => true,
|
||||
'join' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* List of PHP native constants which should be recognized as text strings.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $constantStrings = array(
|
||||
'DIRECTORY_SEPARATOR' => true,
|
||||
'PHP_EOL' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* List of PHP native functions which should be recognized as returning an array.
|
||||
*
|
||||
* Note: The array_*() functions will always be taken into account.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $arrayFunctions = array(
|
||||
'compact' => true,
|
||||
'explode' => true,
|
||||
'range' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* List of PHP native array functions which should *not* be recognized as returning an array.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $arrayFunctionExceptions = array(
|
||||
'array_key_exists' => true,
|
||||
'array_key_first' => true,
|
||||
'array_key_last' => true,
|
||||
'array_multisort' => true,
|
||||
'array_pop' => true,
|
||||
'array_product' => true,
|
||||
'array_push' => true,
|
||||
'array_search' => true,
|
||||
'array_shift' => true,
|
||||
'array_sum' => true,
|
||||
'array_unshift' => true,
|
||||
'array_walk_recursive' => true,
|
||||
'array_walk' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('7.4') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[2]) === false) {
|
||||
// Only one parameter, this must be $pieces. Bow out.
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
/*
|
||||
* Examine the first parameter.
|
||||
* If there is any indication that this is an array declaration, we have an error.
|
||||
*/
|
||||
|
||||
$targetParam = $parameters[1];
|
||||
$start = $targetParam['start'];
|
||||
$end = ($targetParam['end'] + 1);
|
||||
$isOnlyText = true;
|
||||
|
||||
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $start, $end, true);
|
||||
if ($firstNonEmpty === false) {
|
||||
// Parse error. Shouldn't be possible.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$firstNonEmpty]['code'] === \T_OPEN_PARENTHESIS) {
|
||||
$start = ($firstNonEmpty + 1);
|
||||
$end = $tokens[$firstNonEmpty]['parenthesis_closer'];
|
||||
}
|
||||
|
||||
$hasTernary = $phpcsFile->findNext(\T_INLINE_THEN, $start, $end);
|
||||
if ($hasTernary !== false
|
||||
&& isset($tokens[$start]['nested_parenthesis'], $tokens[$hasTernary]['nested_parenthesis'])
|
||||
&& count($tokens[$start]['nested_parenthesis']) === count($tokens[$hasTernary]['nested_parenthesis'])
|
||||
) {
|
||||
$start = ($hasTernary + 1);
|
||||
}
|
||||
|
||||
for ($i = $start; $i < $end; $i++) {
|
||||
$tokenCode = $tokens[$i]['code'];
|
||||
|
||||
if (isset(Tokens::$emptyTokens[$tokenCode])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokenCode === \T_STRING && isset($this->constantStrings[$tokens[$i]['content']])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($hasTernary !== false && $tokenCode === \T_INLINE_ELSE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset(Tokens::$stringTokens[$tokenCode]) === false) {
|
||||
$isOnlyText = false;
|
||||
}
|
||||
|
||||
if ($tokenCode === \T_ARRAY || $tokenCode === \T_OPEN_SHORT_ARRAY || $tokenCode === \T_ARRAY_CAST) {
|
||||
$this->throwNotice($phpcsFile, $stackPtr, $functionName);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokenCode === \T_STRING) {
|
||||
/*
|
||||
* Check for specific functions which return an array (i.e. $pieces).
|
||||
*/
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), $end, true);
|
||||
if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nameLc = strtolower($tokens[$i]['content']);
|
||||
if (isset($this->arrayFunctions[$nameLc]) === false
|
||||
&& (strpos($nameLc, 'array_') !== 0
|
||||
|| isset($this->arrayFunctionExceptions[$nameLc]) === true)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now make sure it's the PHP native function being called.
|
||||
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($i - 1), $start, true);
|
||||
if ($tokens[$prevNonEmpty]['code'] === \T_DOUBLE_COLON
|
||||
|| $tokens[$prevNonEmpty]['code'] === \T_OBJECT_OPERATOR
|
||||
) {
|
||||
// Method call, not a call to the PHP native function.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$prevNonEmpty]['code'] === \T_NS_SEPARATOR
|
||||
&& $tokens[$prevNonEmpty - 1]['code'] === \T_STRING
|
||||
) {
|
||||
// Namespaced function.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ok, so we know that there is an array function in the first param.
|
||||
// 99.9% chance that this is $pieces, not $glue.
|
||||
$this->throwNotice($phpcsFile, $stackPtr, $functionName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isOnlyText === true) {
|
||||
// First parameter only contained text string tokens, i.e. glue.
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Examine the second parameter.
|
||||
*/
|
||||
|
||||
$targetParam = $parameters[2];
|
||||
$start = $targetParam['start'];
|
||||
$end = ($targetParam['end'] + 1);
|
||||
|
||||
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $start, $end, true);
|
||||
if ($firstNonEmpty === false) {
|
||||
// Parse error. Shouldn't be possible.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$firstNonEmpty]['code'] === \T_OPEN_PARENTHESIS) {
|
||||
$start = ($firstNonEmpty + 1);
|
||||
$end = $tokens[$firstNonEmpty]['parenthesis_closer'];
|
||||
}
|
||||
|
||||
$hasTernary = $phpcsFile->findNext(\T_INLINE_THEN, $start, $end);
|
||||
if ($hasTernary !== false
|
||||
&& isset($tokens[$start]['nested_parenthesis'], $tokens[$hasTernary]['nested_parenthesis'])
|
||||
&& count($tokens[$start]['nested_parenthesis']) === count($tokens[$hasTernary]['nested_parenthesis'])
|
||||
) {
|
||||
$start = ($hasTernary + 1);
|
||||
}
|
||||
|
||||
for ($i = $start; $i < $end; $i++) {
|
||||
$tokenCode = $tokens[$i]['code'];
|
||||
|
||||
if (isset(Tokens::$emptyTokens[$tokenCode])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokenCode === \T_ARRAY || $tokenCode === \T_OPEN_SHORT_ARRAY || $tokenCode === \T_ARRAY_CAST) {
|
||||
// Found an array, $pieces is second.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokenCode === \T_STRING && isset($this->constantStrings[$tokens[$i]['content']])) {
|
||||
// One of the special cased, PHP native string constants found.
|
||||
$this->throwNotice($phpcsFile, $stackPtr, $functionName);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokenCode === \T_STRING || $tokenCode === \T_VARIABLE) {
|
||||
// Function call, constant or variable encountered.
|
||||
// No matter what this is combined with, we won't be able to reliably determine the value.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokenCode === \T_CONSTANT_ENCAPSED_STRING
|
||||
|| $tokenCode === \T_DOUBLE_QUOTED_STRING
|
||||
|| $tokenCode === \T_HEREDOC
|
||||
|| $tokenCode === \T_NOWDOC
|
||||
) {
|
||||
$this->throwNotice($phpcsFile, $stackPtr, $functionName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Throw the error/warning.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function throwNotice(File $phpcsFile, $stackPtr, $functionName)
|
||||
{
|
||||
$message = 'Passing the $glue and $pieces parameters in reverse order to %s has been deprecated since PHP 7.4';
|
||||
$isError = false;
|
||||
$errorCode = 'Deprecated';
|
||||
$data = array($functionName);
|
||||
|
||||
/*
|
||||
Support for the deprecated behaviour is expected to be removed in PHP 8.0.
|
||||
Once this has been implemented, this section should be uncommented.
|
||||
if ($this->supportsAbove('8.0') === true) {
|
||||
$message .= ' and is removed since PHP 8.0';
|
||||
$isError = true;
|
||||
$errorCode = 'Removed';
|
||||
}
|
||||
*/
|
||||
|
||||
$message .= '; $glue should be the first parameter and $pieces the second';
|
||||
|
||||
$this->addMessage($phpcsFile, $message, $stackPtr, $isError, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect passing `$encoding` to `mb_strrpos()` as 3rd argument.
|
||||
*
|
||||
* The `$encoding` parameter was moved from the third position to the fourth in PHP 5.2.0.
|
||||
* For backward compatibility, `$encoding` could be specified as the third parameter, but doing
|
||||
* so is deprecated and will be removed in the future.
|
||||
*
|
||||
* Between PHP 5.2 and PHP 7.3, this was a deprecation in documentation only.
|
||||
* As of PHP 7.4, a deprecation warning will be thrown if an encoding is passed as the 3rd
|
||||
* argument.
|
||||
* As of PHP 8, the argument is expected to change to accept an integer only.
|
||||
*
|
||||
* PHP version 5.2
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.deprecated.php#migration74.deprecated.mbstring
|
||||
* @link https://wiki.php.net/rfc/deprecations_php_7_4#mb_strrpos_with_encoding_as_3rd_argument
|
||||
* @link https://www.php.net/manual/en/function.mb-strrpos.php
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
class RemovedMbStrrposEncodingThirdParamSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'mb_strrpos' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokens which should be recognized as text.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $textStringTokens = array(
|
||||
\T_CONSTANT_ENCAPSED_STRING,
|
||||
\T_DOUBLE_QUOTED_STRING,
|
||||
\T_HEREDOC,
|
||||
\T_NOWDOC,
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokens which should be recognized as numbers.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $numberTokens = array(
|
||||
\T_LNUMBER => \T_LNUMBER,
|
||||
\T_DNUMBER => \T_DNUMBER,
|
||||
\T_MINUS => \T_MINUS,
|
||||
\T_PLUS => \T_PLUS,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return $this->supportsAbove('5.2') === false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[3]) === false) {
|
||||
// Optional third parameter not set.
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($parameters[4]) === true) {
|
||||
// Encoding set as fourth parameter.
|
||||
return;
|
||||
}
|
||||
|
||||
$targetParam = $parameters[3];
|
||||
$targets = $this->numberTokens + Tokens::$emptyTokens;
|
||||
$nonNumber = $phpcsFile->findNext($targets, $targetParam['start'], ($targetParam['end'] + 1), true);
|
||||
if ($nonNumber === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isNumericCalculation($phpcsFile, $targetParam['start'], $targetParam['end']) === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hasString = $phpcsFile->findNext($this->textStringTokens, $targetParam['start'], ($targetParam['end'] + 1));
|
||||
if ($hasString === false) {
|
||||
// No text strings found. Undetermined.
|
||||
return;
|
||||
}
|
||||
|
||||
$error = 'Passing the encoding to mb_strrpos() as third parameter is soft deprecated since PHP 5.2';
|
||||
if ($this->supportsAbove('7.4') === true) {
|
||||
$error .= ' and hard deprecated since PHP 7.4';
|
||||
}
|
||||
|
||||
$error .= '. Use an explicit 0 as the offset in the third parameter.';
|
||||
|
||||
$phpcsFile->addWarning(
|
||||
$error,
|
||||
$targetParam['start'],
|
||||
'Deprecated'
|
||||
);
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Check for use of deprecated and removed regex modifiers for MbString regex functions.
|
||||
*
|
||||
* Initially just checks for the PHP 7.1 deprecated `e` modifier.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/deprecate_mb_ereg_replace_eval_option
|
||||
* @link https://www.php.net/manual/en/function.mb-regex-set-options.php
|
||||
*
|
||||
* @since 7.0.5
|
||||
* @since 7.0.8 This sniff now throws a warning instead of an error as the functionality is
|
||||
* only deprecated (for now).
|
||||
* @since 8.2.0 Now extends the `AbstractFunctionCallParameterSniff` instead of the base `Sniff` class.
|
||||
* @since 9.0.0 Renamed from `MbstringReplaceEModifierSniff` to `RemovedMbstringModifiersSniff`.
|
||||
*/
|
||||
class RemovedMbstringModifiersSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* Key is the function name, value the parameter position of the options parameter.
|
||||
*
|
||||
* @since 7.0.5
|
||||
* @since 8.2.0 Renamed from `$functions` to `$targetFunctions`.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'mb_ereg_replace' => 4,
|
||||
'mb_eregi_replace' => 4,
|
||||
'mb_regex_set_options' => 1,
|
||||
'mbereg_replace' => 4, // Undocumented, but valid function alias.
|
||||
'mberegi_replace' => 4, // Undocumented, but valid function alias.
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
// Version used here should be the highest version from the `$newModifiers` array,
|
||||
// i.e. the last PHP version in which a new modifier was introduced.
|
||||
return ($this->supportsAbove('7.1') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 7.0.5
|
||||
* @since 8.2.0 Renamed from `process()` to `processParameters()` and removed
|
||||
* logic superfluous now the sniff extends the abstract.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$functionNameLc = strtolower($functionName);
|
||||
|
||||
// Check whether the options parameter in the function call is passed.
|
||||
if (isset($parameters[$this->targetFunctions[$functionNameLc]]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$optionsParam = $parameters[$this->targetFunctions[$functionNameLc]];
|
||||
|
||||
$stringToken = $phpcsFile->findNext(Tokens::$stringTokens, $optionsParam['start'], $optionsParam['end'] + 1);
|
||||
if ($stringToken === false) {
|
||||
// No string token found in the options parameter, so skip it (e.g. variable passed in).
|
||||
return;
|
||||
}
|
||||
|
||||
$options = '';
|
||||
|
||||
/*
|
||||
* Get the content of any string tokens in the options parameter and remove the quotes and variables.
|
||||
*/
|
||||
for ($i = $stringToken; $i <= $optionsParam['end']; $i++) {
|
||||
if (isset(Tokens::$stringTokens[$tokens[$i]['code']]) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$content = $this->stripQuotes($tokens[$i]['content']);
|
||||
if ($tokens[$i]['code'] === \T_DOUBLE_QUOTED_STRING) {
|
||||
$content = $this->stripVariables($content);
|
||||
}
|
||||
$content = trim($content);
|
||||
|
||||
if (empty($content) === false) {
|
||||
$options .= $content;
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($options, 'e') !== false) {
|
||||
$error = 'The Mbstring regex "e" modifier is deprecated since PHP 7.1.';
|
||||
|
||||
// The alternative mb_ereg_replace_callback() function is only available since 5.4.1.
|
||||
if ($this->supportsBelow('5.4.1') === false) {
|
||||
$error .= ' Use mb_ereg_replace_callback() instead (PHP 5.4.1+).';
|
||||
}
|
||||
|
||||
$phpcsFile->addWarning($error, $stackPtr, 'Deprecated');
|
||||
}
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect usage of non-cryptographic hashes.
|
||||
*
|
||||
* "The `hash_hmac()`, `hash_hmac_file()`, `hash_pbkdf2()`, and `hash_init()`
|
||||
* (with `HASH_HMAC`) functions no longer accept non-cryptographic hashes."
|
||||
*
|
||||
* PHP version 7.2
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration72.incompatible.php#migration72.incompatible.hash-functions
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class RemovedNonCryptoHashSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'hash_hmac' => true,
|
||||
'hash_hmac_file' => true,
|
||||
'hash_init' => true,
|
||||
'hash_pbkdf2' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* List of the non-cryptographic hashes.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $disabledCryptos = array(
|
||||
'adler32' => true,
|
||||
'crc32' => true,
|
||||
'crc32b' => true,
|
||||
'fnv132' => true,
|
||||
'fnv1a32' => true,
|
||||
'fnv164' => true,
|
||||
'fnv1a64' => true,
|
||||
'joaat' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('7.2') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[1]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$targetParam = $parameters[1];
|
||||
|
||||
if (isset($this->disabledCryptos[$this->stripQuotes($targetParam['raw'])]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strtolower($functionName) === 'hash_init'
|
||||
&& (isset($parameters[2]) === false
|
||||
|| ($parameters[2]['raw'] !== 'HASH_HMAC'
|
||||
&& $parameters[2]['raw'] !== (string) \HASH_HMAC))
|
||||
) {
|
||||
// For hash_init(), these hashes are only disabled with HASH_HMAC set.
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Non-cryptographic hashes are no longer accepted by function %s() since PHP 7.2. Found: %s',
|
||||
$targetParam['start'],
|
||||
$this->stringToErrorCode($functionName),
|
||||
array(
|
||||
$functionName,
|
||||
$targetParam['raw'],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Check for the use of deprecated and removed regex modifiers for PCRE regex functions.
|
||||
*
|
||||
* Initially just checks for the `e` modifier, which was deprecated since PHP 5.5
|
||||
* and removed as of PHP 7.0.
|
||||
*
|
||||
* {@internal If and when this sniff would need to start checking for other modifiers, a minor
|
||||
* refactor will be needed as all references to the `e` modifier are currently hard-coded.}
|
||||
*
|
||||
* PHP version 5.5
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/remove_preg_replace_eval_modifier
|
||||
* @link https://wiki.php.net/rfc/remove_deprecated_functionality_in_php7
|
||||
* @link https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
|
||||
*
|
||||
* @since 5.6
|
||||
* @since 7.0.8 This sniff now throws a warning (deprecated) or an error (removed) depending
|
||||
* on the `testVersion` set. Previously it would always throw an error.
|
||||
* @since 8.2.0 Now extends the `AbstractFunctionCallParameterSniff` instead of the base `Sniff` class.
|
||||
* @since 9.0.0 Renamed from `PregReplaceEModifierSniff` to `RemovedPCREModifiersSniff`.
|
||||
*/
|
||||
class RemovedPCREModifiersSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 7.0.1
|
||||
* @since 8.2.0 Renamed from `$functions` to `$targetFunctions`.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'preg_replace' => true,
|
||||
'preg_filter' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Regex bracket delimiters.
|
||||
*
|
||||
* @since 7.0.5 This array was originally contained within the `process()` method.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $doublesSeparators = array(
|
||||
'{' => '}',
|
||||
'[' => ']',
|
||||
'(' => ')',
|
||||
'<' => '>',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 5.6
|
||||
* @since 8.2.0 Renamed from `process()` to `processParameters()` and removed
|
||||
* logic superfluous now the sniff extends the abstract.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
// Check the first parameter in the function call as that should contain the regex(es).
|
||||
if (isset($parameters[1]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$functionNameLc = strtolower($functionName);
|
||||
$firstParam = $parameters[1];
|
||||
|
||||
// Differentiate between an array of patterns passed and a single pattern.
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $firstParam['start'], ($firstParam['end'] + 1), true);
|
||||
if ($nextNonEmpty !== false && ($tokens[$nextNonEmpty]['code'] === \T_ARRAY || $tokens[$nextNonEmpty]['code'] === \T_OPEN_SHORT_ARRAY)) {
|
||||
$arrayValues = $this->getFunctionCallParameters($phpcsFile, $nextNonEmpty);
|
||||
if ($functionNameLc === 'preg_replace_callback_array') {
|
||||
// For preg_replace_callback_array(), the patterns will be in the array keys.
|
||||
foreach ($arrayValues as $value) {
|
||||
$hasKey = $phpcsFile->findNext(\T_DOUBLE_ARROW, $value['start'], ($value['end'] + 1));
|
||||
if ($hasKey === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value['end'] = ($hasKey - 1);
|
||||
$value['raw'] = trim($phpcsFile->getTokensAsString($value['start'], ($hasKey - $value['start'])));
|
||||
$this->processRegexPattern($value, $phpcsFile, $value['end'], $functionName);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Otherwise, the patterns will be in the array values.
|
||||
foreach ($arrayValues as $value) {
|
||||
$hasKey = $phpcsFile->findNext(\T_DOUBLE_ARROW, $value['start'], ($value['end'] + 1));
|
||||
if ($hasKey !== false) {
|
||||
$value['start'] = ($hasKey + 1);
|
||||
$value['raw'] = trim($phpcsFile->getTokensAsString($value['start'], (($value['end'] + 1) - $value['start'])));
|
||||
}
|
||||
|
||||
$this->processRegexPattern($value, $phpcsFile, $value['end'], $functionName);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->processRegexPattern($firstParam, $phpcsFile, $stackPtr, $functionName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('5.5') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Analyse a potential regex pattern for use of the /e modifier.
|
||||
*
|
||||
* @since 7.1.2 This logic was originally contained within the `process()` method.
|
||||
*
|
||||
* @param array $pattern Array containing the start and end token
|
||||
* pointer of the potential regex pattern and
|
||||
* the raw string value of the pattern.
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
* @param string $functionName The function which contained the pattern.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function processRegexPattern($pattern, File $phpcsFile, $stackPtr, $functionName)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
/*
|
||||
* The pattern might be build up of a combination of strings, variables
|
||||
* and function calls. We are only concerned with the strings.
|
||||
*/
|
||||
$regex = '';
|
||||
for ($i = $pattern['start']; $i <= $pattern['end']; $i++) {
|
||||
if (isset(Tokens::$stringTokens[$tokens[$i]['code']]) === true) {
|
||||
$content = $this->stripQuotes($tokens[$i]['content']);
|
||||
if ($tokens[$i]['code'] === \T_DOUBLE_QUOTED_STRING) {
|
||||
$content = $this->stripVariables($content);
|
||||
}
|
||||
|
||||
$regex .= trim($content);
|
||||
}
|
||||
}
|
||||
|
||||
// Deal with multi-line regexes which were broken up in several string tokens.
|
||||
if ($tokens[$pattern['start']]['line'] !== $tokens[$pattern['end']]['line']) {
|
||||
$regex = $this->stripQuotes($regex);
|
||||
}
|
||||
|
||||
if ($regex === '') {
|
||||
// No string token found in the first parameter, so skip it (e.g. if variable passed in).
|
||||
return;
|
||||
}
|
||||
|
||||
$regexFirstChar = substr($regex, 0, 1);
|
||||
|
||||
// Make sure that the character identified as the delimiter is valid.
|
||||
// Otherwise, it is a false positive caused by the string concatenation.
|
||||
if (preg_match('`[a-z0-9\\\\ ]`i', $regexFirstChar) === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->doublesSeparators[$regexFirstChar])) {
|
||||
$regexEndPos = strrpos($regex, $this->doublesSeparators[$regexFirstChar]);
|
||||
} else {
|
||||
$regexEndPos = strrpos($regex, $regexFirstChar);
|
||||
}
|
||||
|
||||
if ($regexEndPos !== false) {
|
||||
$modifiers = substr($regex, $regexEndPos + 1);
|
||||
$this->examineModifiers($phpcsFile, $stackPtr, $functionName, $modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Examine the regex modifier string.
|
||||
*
|
||||
* @since 8.2.0 Split off from the `processRegexPattern()` method.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
* @param string $functionName The function which contained the pattern.
|
||||
* @param string $modifiers The regex modifiers found.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function examineModifiers(File $phpcsFile, $stackPtr, $functionName, $modifiers)
|
||||
{
|
||||
if (strpos($modifiers, 'e') !== false) {
|
||||
$error = '%s() - /e modifier is deprecated since PHP 5.5';
|
||||
$isError = false;
|
||||
$errorCode = 'Deprecated';
|
||||
$data = array($functionName);
|
||||
|
||||
if ($this->supportsAbove('7.0')) {
|
||||
$error .= ' and removed since PHP 7.0';
|
||||
$isError = true;
|
||||
$errorCode = 'Removed';
|
||||
}
|
||||
|
||||
$this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect passing a string literal as `$category` to `setlocale()`.
|
||||
*
|
||||
* Support for the category parameter passed as a string has been removed.
|
||||
* Only `LC_*` constants can be used as of PHP 7.0.0.
|
||||
*
|
||||
* PHP version 4.2
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/remove_deprecated_functionality_in_php7
|
||||
* @link https://www.php.net/manual/en/function.setlocale.php#refsect1-function.setlocale-changelog
|
||||
*
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class RemovedSetlocaleStringSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'setlocale' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('4.2') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @since 9.0.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the stack.
|
||||
* @param string $functionName The token content (function name) which was matched.
|
||||
* @param array $parameters Array with information about the parameters.
|
||||
*
|
||||
* @return int|void Integer stack pointer to skip forward or void to continue
|
||||
* normal file processing.
|
||||
*/
|
||||
public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
|
||||
{
|
||||
if (isset($parameters[1]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$targetParam = $parameters[1];
|
||||
|
||||
for ($i = $targetParam['start']; $i <= $targetParam['end']; $i++) {
|
||||
if ($tokens[$i]['code'] !== \T_CONSTANT_ENCAPSED_STRING
|
||||
&& $tokens[$i]['code'] !== \T_DOUBLE_QUOTED_STRING
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$message = 'Passing the $category as a string to setlocale() has been deprecated since PHP 4.2';
|
||||
$isError = false;
|
||||
$errorCode = 'Deprecated';
|
||||
$data = array($targetParam['raw']);
|
||||
|
||||
if ($this->supportsAbove('7.0') === true) {
|
||||
$message .= ' and is removed since PHP 7.0';
|
||||
$isError = true;
|
||||
$errorCode = 'Removed';
|
||||
}
|
||||
|
||||
$message .= '; Pass one of the LC_* constants instead. Found: %s';
|
||||
|
||||
$this->addMessage($phpcsFile, $message, $i, $isError, $errorCode, $data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2009-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect the use of call time pass by reference.
|
||||
*
|
||||
* This behaviour has been deprecated in PHP 5.3 and removed in PHP 5.4.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/calltimebyref
|
||||
* @link https://www.php.net/manual/en/language.references.pass.php
|
||||
*
|
||||
* @since 5.5
|
||||
* @since 7.0.8 This sniff now throws a warning (deprecated) or an error (removed) depending
|
||||
* on the `testVersion` set. Previously it would always throw an error.
|
||||
*/
|
||||
class ForbiddenCallTimePassByReferenceSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Tokens that represent assignments or equality comparisons.
|
||||
*
|
||||
* Near duplicate of Tokens::$assignmentTokens + Tokens::$equalityTokens.
|
||||
* Copied in for PHPCS cross-version compatibility.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $assignOrCompare = array(
|
||||
// Equality tokens.
|
||||
'T_IS_EQUAL' => true,
|
||||
'T_IS_NOT_EQUAL' => true,
|
||||
'T_IS_IDENTICAL' => true,
|
||||
'T_IS_NOT_IDENTICAL' => true,
|
||||
'T_IS_SMALLER_OR_EQUAL' => true,
|
||||
'T_IS_GREATER_OR_EQUAL' => true,
|
||||
|
||||
// Assignment tokens.
|
||||
'T_EQUAL' => true,
|
||||
'T_AND_EQUAL' => true,
|
||||
'T_OR_EQUAL' => true,
|
||||
'T_CONCAT_EQUAL' => true,
|
||||
'T_DIV_EQUAL' => true,
|
||||
'T_MINUS_EQUAL' => true,
|
||||
'T_POW_EQUAL' => true,
|
||||
'T_MOD_EQUAL' => true,
|
||||
'T_MUL_EQUAL' => true,
|
||||
'T_PLUS_EQUAL' => true,
|
||||
'T_XOR_EQUAL' => true,
|
||||
'T_DOUBLE_ARROW' => true,
|
||||
'T_SL_EQUAL' => true,
|
||||
'T_SR_EQUAL' => true,
|
||||
'T_COALESCE_EQUAL' => true,
|
||||
'T_ZSR_EQUAL' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_STRING,
|
||||
\T_VARIABLE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 5.5
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('5.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
// Skip tokens that are the names of functions or classes
|
||||
// within their definitions. For example: function myFunction...
|
||||
// "myFunction" is T_STRING but we should skip because it is not a
|
||||
// function or method *call*.
|
||||
$findTokens = Tokens::$emptyTokens;
|
||||
$findTokens[] = \T_BITWISE_AND;
|
||||
|
||||
$prevNonEmpty = $phpcsFile->findPrevious(
|
||||
$findTokens,
|
||||
($stackPtr - 1),
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
if ($prevNonEmpty !== false && \in_array($tokens[$prevNonEmpty]['type'], array('T_FUNCTION', 'T_CLASS', 'T_INTERFACE', 'T_TRAIT'), true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the next non-whitespace token after the function or method call
|
||||
// is not an opening parenthesis then it can't really be a *call*.
|
||||
$openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
|
||||
if ($openBracket === false || $tokens[$openBracket]['code'] !== \T_OPEN_PARENTHESIS
|
||||
|| isset($tokens[$openBracket]['parenthesis_closer']) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the function call parameters.
|
||||
$parameters = $this->getFunctionCallParameters($phpcsFile, $stackPtr);
|
||||
if (\count($parameters) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Which nesting level is the one we are interested in ?
|
||||
$nestedParenthesisCount = 1;
|
||||
if (isset($tokens[$openBracket]['nested_parenthesis'])) {
|
||||
$nestedParenthesisCount = \count($tokens[$openBracket]['nested_parenthesis']) + 1;
|
||||
}
|
||||
|
||||
foreach ($parameters as $parameter) {
|
||||
if ($this->isCallTimePassByReferenceParam($phpcsFile, $parameter, $nestedParenthesisCount) === true) {
|
||||
// T_BITWISE_AND represents a pass-by-reference.
|
||||
$error = 'Using a call-time pass-by-reference is deprecated since PHP 5.3';
|
||||
$isError = false;
|
||||
$errorCode = 'Deprecated';
|
||||
|
||||
if ($this->supportsAbove('5.4')) {
|
||||
$error .= ' and prohibited since PHP 5.4';
|
||||
$isError = true;
|
||||
$errorCode = 'NotAllowed';
|
||||
}
|
||||
|
||||
$this->addMessage($phpcsFile, $error, $parameter['start'], $isError, $errorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether a parameter is passed by reference.
|
||||
*
|
||||
* @since 7.0.6 Split off from the `process()` method.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param array $parameter Information on the current parameter
|
||||
* to be examined.
|
||||
* @param int $nestingLevel Target nesting level.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isCallTimePassByReferenceParam(File $phpcsFile, $parameter, $nestingLevel)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$searchStartToken = $parameter['start'] - 1;
|
||||
$searchEndToken = $parameter['end'] + 1;
|
||||
$nextVariable = $searchStartToken;
|
||||
do {
|
||||
$nextVariable = $phpcsFile->findNext(array(\T_VARIABLE, \T_OPEN_SHORT_ARRAY, \T_CLOSURE), ($nextVariable + 1), $searchEndToken);
|
||||
if ($nextVariable === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore anything within short array definition brackets.
|
||||
if ($tokens[$nextVariable]['type'] === 'T_OPEN_SHORT_ARRAY'
|
||||
&& (isset($tokens[$nextVariable]['bracket_opener'])
|
||||
&& $tokens[$nextVariable]['bracket_opener'] === $nextVariable)
|
||||
&& isset($tokens[$nextVariable]['bracket_closer'])
|
||||
) {
|
||||
// Skip forward to the end of the short array definition.
|
||||
$nextVariable = $tokens[$nextVariable]['bracket_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip past closures passed as function parameters.
|
||||
if ($tokens[$nextVariable]['type'] === 'T_CLOSURE'
|
||||
&& (isset($tokens[$nextVariable]['scope_condition'])
|
||||
&& $tokens[$nextVariable]['scope_condition'] === $nextVariable)
|
||||
&& isset($tokens[$nextVariable]['scope_closer'])
|
||||
) {
|
||||
// Skip forward to the end of the closure declaration.
|
||||
$nextVariable = $tokens[$nextVariable]['scope_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the variable belongs directly to this function call
|
||||
// and is not inside a nested function call or array.
|
||||
if (isset($tokens[$nextVariable]['nested_parenthesis']) === false
|
||||
|| (\count($tokens[$nextVariable]['nested_parenthesis']) !== $nestingLevel)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Checking this: $value = my_function(...[*]$arg...).
|
||||
$tokenBefore = $phpcsFile->findPrevious(
|
||||
Tokens::$emptyTokens,
|
||||
($nextVariable - 1),
|
||||
$searchStartToken,
|
||||
true
|
||||
);
|
||||
|
||||
if ($tokenBefore === false || $tokens[$tokenBefore]['code'] !== \T_BITWISE_AND) {
|
||||
// Nothing before the token or no &.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($phpcsFile->isReference($tokenBefore) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Checking this: $value = my_function(...[*]&$arg...).
|
||||
$tokenBefore = $phpcsFile->findPrevious(
|
||||
Tokens::$emptyTokens,
|
||||
($tokenBefore - 1),
|
||||
$searchStartToken,
|
||||
true
|
||||
);
|
||||
|
||||
// Prevent false positive on assign by reference and compare with reference
|
||||
// within function call parameters.
|
||||
if (isset($this->assignOrCompare[$tokens[$tokenBefore]['type']])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The found T_BITWISE_AND represents a pass-by-reference.
|
||||
return true;
|
||||
|
||||
} while ($nextVariable < $searchEndToken);
|
||||
|
||||
// This code should never be reached, but here in case of weird bugs.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect array and string literal dereferencing.
|
||||
*
|
||||
* As of PHP 5.5, array and string literals can now be dereferenced directly to
|
||||
* access individual elements and characters.
|
||||
*
|
||||
* As of PHP 7.0, this also works when using curly braces for the dereferencing.
|
||||
* While unclear, this most likely has to do with the Uniform Variable Syntax changes.
|
||||
*
|
||||
* PHP version 5.5
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration55.new-features.php#migration55.new-features.const-dereferencing
|
||||
* @link https://wiki.php.net/rfc/constdereference
|
||||
* @link https://wiki.php.net/rfc/uniform_variable_syntax
|
||||
* @link https://www.php.net/manual/en/language.types.array.php#example-63
|
||||
*
|
||||
* {@internal The reason for splitting the logic of this sniff into different methods is
|
||||
* to allow re-use of the logic by the PHP 7.4 `RemovedCurlyBraceArrayAccess` sniff.}
|
||||
*
|
||||
* @since 7.1.4
|
||||
* @since 9.3.0 Now also detects dereferencing using curly braces.
|
||||
*/
|
||||
class NewArrayStringDereferencingSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_ARRAY,
|
||||
\T_OPEN_SHORT_ARRAY,
|
||||
\T_CONSTANT_ENCAPSED_STRING,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 7.1.4
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.6') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dereferencing = $this->isArrayStringDereferencing($phpcsFile, $stackPtr);
|
||||
if (empty($dereferencing)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$supports54 = $this->supportsBelow('5.4');
|
||||
|
||||
foreach ($dereferencing['braces'] as $openBrace => $closeBrace) {
|
||||
if ($supports54 === true
|
||||
&& ($tokens[$openBrace]['type'] === 'T_OPEN_SQUARE_BRACKET'
|
||||
|| $tokens[$openBrace]['type'] === 'T_OPEN_SHORT_ARRAY') // Work around bug #1381 in PHPCS 2.8.1 and lower.
|
||||
) {
|
||||
$phpcsFile->addError(
|
||||
'Direct array dereferencing of %s is not present in PHP version 5.4 or earlier',
|
||||
$openBrace,
|
||||
'Found',
|
||||
array($dereferencing['type'])
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// PHP 7.0 Array/string dereferencing using curly braces.
|
||||
if ($tokens[$openBrace]['type'] === 'T_OPEN_CURLY_BRACKET') {
|
||||
$phpcsFile->addError(
|
||||
'Direct array dereferencing of %s using curly braces is not present in PHP version 5.6 or earlier',
|
||||
$openBrace,
|
||||
'FoundUsingCurlies',
|
||||
array($dereferencing['type'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if this string/array is being dereferenced.
|
||||
*
|
||||
* @since 9.3.0 Logic split off from the process method.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return array Array containing the type of access and stack pointers to the
|
||||
* open/close braces involved in the array/string dereferencing;
|
||||
* or an empty array if no array/string dereferencing was detected.
|
||||
*/
|
||||
public function isArrayStringDereferencing(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
switch ($tokens[$stackPtr]['code']) {
|
||||
case \T_CONSTANT_ENCAPSED_STRING:
|
||||
$type = 'string literals';
|
||||
$end = $stackPtr;
|
||||
break;
|
||||
|
||||
case \T_ARRAY:
|
||||
if (isset($tokens[$stackPtr]['parenthesis_closer']) === false) {
|
||||
// Live coding.
|
||||
return array();
|
||||
} else {
|
||||
$type = 'arrays';
|
||||
$end = $tokens[$stackPtr]['parenthesis_closer'];
|
||||
}
|
||||
break;
|
||||
|
||||
case \T_OPEN_SHORT_ARRAY:
|
||||
if (isset($tokens[$stackPtr]['bracket_closer']) === false) {
|
||||
// Live coding.
|
||||
return array();
|
||||
} else {
|
||||
$type = 'arrays';
|
||||
$end = $tokens[$stackPtr]['bracket_closer'];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($type, $end) === false) {
|
||||
// Shouldn't happen, but for some reason did.
|
||||
return array();
|
||||
}
|
||||
|
||||
$braces = array();
|
||||
|
||||
do {
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true, null, true);
|
||||
if ($nextNonEmpty === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($tokens[$nextNonEmpty]['type'] === 'T_OPEN_SQUARE_BRACKET'
|
||||
|| $tokens[$nextNonEmpty]['type'] === 'T_OPEN_CURLY_BRACKET' // PHP 7.0+.
|
||||
|| $tokens[$nextNonEmpty]['type'] === 'T_OPEN_SHORT_ARRAY' // Work around bug #1381 in PHPCS 2.8.1 and lower.
|
||||
) {
|
||||
if (isset($tokens[$nextNonEmpty]['bracket_closer']) === false) {
|
||||
// Live coding or parse error.
|
||||
break;
|
||||
}
|
||||
|
||||
$braces[$nextNonEmpty] = $tokens[$nextNonEmpty]['bracket_closer'];
|
||||
|
||||
// Continue, just in case there is nested array access, i.e. `array(1, 2, 3)[$i][$j];`.
|
||||
$end = $tokens[$nextNonEmpty]['bracket_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're still here, we've reached the end of the variable.
|
||||
break;
|
||||
|
||||
} while (true);
|
||||
|
||||
if (empty($braces)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array(
|
||||
'type' => $type,
|
||||
'braces' => $braces,
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+142
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Using the spread operator for unpacking arrays in array expressions is available since PHP 7.4.
|
||||
*
|
||||
* PHP version 7.4
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration74.new-features.php#migration74.new-features.core.unpack-inside-array
|
||||
* @link https://wiki.php.net/rfc/spread_operator_for_array
|
||||
*
|
||||
* @since 9.2.0
|
||||
*/
|
||||
class NewArrayUnpackingSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_ARRAY,
|
||||
\T_OPEN_SHORT_ARRAY,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 9.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('7.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
/*
|
||||
* Determine the array opener & closer.
|
||||
*/
|
||||
$closer = $phpcsFile->numTokens;
|
||||
if ($tokens[$stackPtr]['code'] === \T_ARRAY) {
|
||||
if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$opener = $tokens[$stackPtr]['parenthesis_opener'];
|
||||
|
||||
if (isset($tokens[$opener]['parenthesis_closer'])) {
|
||||
$closer = $tokens[$opener]['parenthesis_closer'];
|
||||
}
|
||||
} else {
|
||||
// Short array syntax.
|
||||
$opener = $stackPtr;
|
||||
|
||||
if (isset($tokens[$stackPtr]['bracket_closer'])) {
|
||||
$closer = $tokens[$stackPtr]['bracket_closer'];
|
||||
}
|
||||
}
|
||||
|
||||
$nestingLevel = 0;
|
||||
if (isset($tokens[($opener + 1)]['nested_parenthesis'])) {
|
||||
$nestingLevel = count($tokens[($opener + 1)]['nested_parenthesis']);
|
||||
}
|
||||
|
||||
for ($i = $opener; $i < $closer;) {
|
||||
$i = $phpcsFile->findNext(array(\T_ELLIPSIS, \T_OPEN_SHORT_ARRAY, \T_ARRAY), ($i + 1), $closer);
|
||||
if ($i === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] === \T_OPEN_SHORT_ARRAY) {
|
||||
if (isset($tokens[$i]['bracket_closer']) === false) {
|
||||
// Live coding, unfinished nested array, handle this when the array opener
|
||||
// of the nested array is passed.
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip over nested short arrays. These will be handled when the array opener
|
||||
// of the nested array is passed.
|
||||
$i = $tokens[$i]['bracket_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] === \T_ARRAY) {
|
||||
if (isset($tokens[$i]['parenthesis_closer']) === false) {
|
||||
// Live coding, unfinished nested array, handle this when the array opener
|
||||
// of the nested array is passed.
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip over nested long arrays. These will be handled when the array opener
|
||||
// of the nested array is passed.
|
||||
$i = $tokens[$i]['parenthesis_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure this is not function call variable unpacking.
|
||||
if (isset($tokens[$i]['nested_parenthesis'])
|
||||
&& count($tokens[$i]['nested_parenthesis']) > $nestingLevel
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ok, found one.
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
|
||||
$snippet = trim($phpcsFile->getTokensAsString($i, (($nextNonEmpty - $i) + 1)));
|
||||
$phpcsFile->addError(
|
||||
'Array unpacking within array declarations using the spread operator is not supported in PHP 7.3 or earlier. Found: %s',
|
||||
$i,
|
||||
'Found',
|
||||
array($snippet)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect class member access on object instantiation/cloning.
|
||||
*
|
||||
* PHP 5.4: Class member access on instantiation has been added, e.g. `(new Foo)->bar()`.
|
||||
* PHP 7.0: Class member access on cloning has been added, e.g. `(clone $foo)->bar()`.
|
||||
*
|
||||
* As of PHP 7.0, class member access on instantiation also works when using curly braces.
|
||||
* While unclear, this most likely has to do with the Uniform Variable Syntax changes.
|
||||
*
|
||||
* PHP version 5.4
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @link https://www.php.net/manual/en/language.oop5.basic.php#example-177
|
||||
* @link https://www.php.net/manual/en/language.oop5.cloning.php#language.oop5.traits.properties.example
|
||||
* @link https://www.php.net/manual/en/migration54.new-features.php
|
||||
* @link https://wiki.php.net/rfc/instance-method-call
|
||||
* @link https://wiki.php.net/rfc/uniform_variable_syntax
|
||||
*
|
||||
* {@internal The reason for splitting the logic of this sniff into different methods is
|
||||
* to allow re-use of the logic by the PHP 7.4 `RemovedCurlyBraceArrayAccess` sniff.}
|
||||
*
|
||||
* @since 8.2.0
|
||||
* @since 9.3.0 Now also detects class member access on instantiation using curly braces.
|
||||
*/
|
||||
class NewClassMemberAccessSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_NEW,
|
||||
\T_CLONE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 8.2.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.6') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pointers = $this->isClassMemberAccess($phpcsFile, $stackPtr);
|
||||
if (empty($pointers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$supports53 = $this->supportsBelow('5.3');
|
||||
|
||||
$error = 'Class member access on object %s was not supported in PHP %s or earlier';
|
||||
$data = array('instantiation', '5.3');
|
||||
$errorCode = 'OnNewFound';
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === \T_CLONE) {
|
||||
$data = array('cloning', '5.6');
|
||||
$errorCode = 'OnCloneFound';
|
||||
}
|
||||
|
||||
foreach ($pointers as $open => $close) {
|
||||
$itemData = $data;
|
||||
$itemErrorCode = $errorCode;
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === \T_NEW
|
||||
&& $tokens[$open]['code'] !== \T_OPEN_CURLY_BRACKET
|
||||
) {
|
||||
if ($supports53 === true) {
|
||||
$phpcsFile->addError($error, $open, $itemErrorCode, $itemData);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === \T_NEW
|
||||
&& $tokens[$open]['code'] === \T_OPEN_CURLY_BRACKET
|
||||
) {
|
||||
// Non-curlies was already handled above.
|
||||
$itemData = array('instantiation using curly braces', '5.6');
|
||||
$itemErrorCode = 'OnNewFoundUsingCurlies';
|
||||
}
|
||||
|
||||
$phpcsFile->addError($error, $open, $itemErrorCode, $itemData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the class being instantiated/cloned is being dereferenced.
|
||||
*
|
||||
* @since 9.3.0 Logic split off from the process method.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return array Array containing the stack pointers to the object operator or
|
||||
* the open/close braces involved in the class member access;
|
||||
* or an empty array if no class member access was detected.
|
||||
*/
|
||||
public function isClassMemberAccess(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[$stackPtr]['nested_parenthesis']) === false) {
|
||||
// The `new className/clone $a` has to be in parentheses, without is not supported.
|
||||
return array();
|
||||
}
|
||||
|
||||
$parenthesisCloser = end($tokens[$stackPtr]['nested_parenthesis']);
|
||||
$parenthesisOpener = key($tokens[$stackPtr]['nested_parenthesis']);
|
||||
|
||||
if (isset($tokens[$parenthesisOpener]['parenthesis_owner']) === true) {
|
||||
// If there is an owner, these parentheses are for a different purpose.
|
||||
return array();
|
||||
}
|
||||
|
||||
$prevBeforeParenthesis = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($parenthesisOpener - 1), null, true);
|
||||
if ($prevBeforeParenthesis !== false && $tokens[$prevBeforeParenthesis]['code'] === \T_STRING) {
|
||||
// This is most likely a function call with the new/cloned object as a parameter.
|
||||
return array();
|
||||
}
|
||||
|
||||
$braces = array();
|
||||
$end = $parenthesisCloser;
|
||||
|
||||
do {
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true, null, true);
|
||||
if ($nextNonEmpty === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($tokens[$nextNonEmpty]['code'] === \T_OBJECT_OPERATOR) {
|
||||
// No need to walk any further if this is object access.
|
||||
$braces[$nextNonEmpty] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($tokens[$nextNonEmpty]['code'] === \T_OPEN_SQUARE_BRACKET
|
||||
|| $tokens[$nextNonEmpty]['code'] === \T_OPEN_CURLY_BRACKET // PHP 7.0+.
|
||||
) {
|
||||
if (isset($tokens[$nextNonEmpty]['bracket_closer']) === false) {
|
||||
// Live coding or parse error.
|
||||
break;
|
||||
}
|
||||
|
||||
$braces[$nextNonEmpty] = $tokens[$nextNonEmpty]['bracket_closer'];
|
||||
|
||||
// Continue, just in case there is nested array access, i.e. `(new Foo())[1][0];`.
|
||||
$end = $tokens[$nextNonEmpty]['bracket_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're still here, we've reached the end.
|
||||
break;
|
||||
|
||||
} while (true);
|
||||
|
||||
return $braces;
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2019 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect dynamic access to static methods and properties, as well as class constants.
|
||||
*
|
||||
* As of PHP 5.3, static properties and methods as well as class constants
|
||||
* can be accessed using a dynamic (variable) class name.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @link https://www.php.net/manual/en/migration53.new-features.php
|
||||
*
|
||||
* @since 8.1.0
|
||||
* @since 9.0.0 Renamed from `DynamicAccessToStaticSniff` to `NewDynamicAccessToStaticSniff`.
|
||||
*/
|
||||
class NewDynamicAccessToStaticSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
\T_DOUBLE_COLON,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @since 8.1.0
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.2') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
|
||||
// Disregard `static::` as well. Late static binding is reported by another sniff.
|
||||
if ($tokens[$prevNonEmpty]['code'] === \T_SELF
|
||||
|| $tokens[$prevNonEmpty]['code'] === \T_PARENT
|
||||
|| $tokens[$prevNonEmpty]['code'] === \T_STATIC
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$prevNonEmpty]['code'] === \T_STRING) {
|
||||
$prevPrevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevNonEmpty - 1), null, true);
|
||||
|
||||
if ($tokens[$prevPrevNonEmpty]['code'] !== \T_OBJECT_OPERATOR) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Static class properties and methods, as well as class constants, could not be accessed using a dynamic (variable) classname in PHP 5.2 or earlier.',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user