mirror of
https://github.com/10h30/genesis-simple-sidebars.git
synced 2026-07-15 12:33:21 +09:00
Introducing coding standards validation.
This commit is contained in:
committed by
Nathan Rice
parent
6e40c921ab
commit
fd504d41ac
+87
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Classes\NewAnonymousClasses.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Classes\NewAnonymousClasses.
|
||||
*
|
||||
* Anonymous classes are supported in PHP 7.0
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $indicators = array(
|
||||
T_CLASS => T_CLASS,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
+842
@@ -0,0 +1,842 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Classes\NewClassesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2013 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Classes\NewClassesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2013 Cu.be Solutions bvba
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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,
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
/**
|
||||
* 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}}
|
||||
*
|
||||
* @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,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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 type hint.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
$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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The built-in class ' . parent::getErrorMsgTemplate();
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Classes\NewConstVisibility.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Classes\NewConstVisibility.
|
||||
*
|
||||
* Visibility for class constants is available since PHP 7.1.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewConstVisibilitySniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_CONST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'])
|
||||
);
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Classes\NewLateStaticBindingSniff.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Classes;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Classes\NewLateStaticBindingSniff.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewLateStaticBindingSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_STATIC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+3511
File diff suppressed because it is too large
Load Diff
+75
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Constants\NewMagicClassConstantSniff.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Constants;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Constants\NewMagicClassConstantSniff.
|
||||
*
|
||||
* The special ClassName::class constant is available as of PHP 5.5.0, and allows for
|
||||
* fully qualified class name resolution at compile.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewMagicClassConstantSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'
|
||||
);
|
||||
}
|
||||
}
|
||||
+360
@@ -0,0 +1,360 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Constants\RemovedConstantsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Constants;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Constants\RemovedConstantsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* Note: PHP Constants are case-sensitive!
|
||||
*
|
||||
* @var array(string => array(string => bool|string|null))
|
||||
*/
|
||||
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,
|
||||
),
|
||||
|
||||
'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,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_STRING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The constant "%s" is ';
|
||||
}
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\DiscouragedSwitchContinue.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\DiscouragedSwitchContinue.
|
||||
*
|
||||
* PHP 7.3 will throw a warning when continue is used to target a switch control structure.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class DiscouragedSwitchContinueSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Token codes of control structures which can be targeted using continue.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\ForbiddenBreakContinueOutsideLoop.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\ForbiddenBreakContinueOutsideLoop.
|
||||
*
|
||||
* Forbids use of break or continue statements outside of looping structures.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class ForbiddenBreakContinueOutsideLoopSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Token codes of control structure in which usage of break/continue is valid.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $backCompat = array(
|
||||
T_CASE => true,
|
||||
T_DEFAULT => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_BREAK,
|
||||
T_CONTINUE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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);
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\ForbiddenBreakContinueVariableArguments.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\ForbiddenBreakContinueVariableArguments.
|
||||
*
|
||||
* Forbids variable arguments on break or continue statements.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_BREAK, T_CONTINUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\ForbiddenSwitchWithMultipleDefaultBlocksSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\ForbiddenSwitchWithMultipleDefaultBlocksSniff.
|
||||
*
|
||||
* Switch statements can not have multiple default blocks since PHP 7.0
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
class ForbiddenSwitchWithMultipleDefaultBlocksSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_SWITCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+334
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\NewExecutionDirectivesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\NewExecutionDirectivesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string => array(string => int|string|null))
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $ignoreTokens = array();
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array(
|
||||
'valid_value_callback',
|
||||
'valid_values',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'Directive ' . parent::getErrorMsgTemplate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the error or warning for this item.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\NewForeachExpressionReferencingSniff.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* New `foreach` Expression Referencing.
|
||||
*
|
||||
* Before PHP 5.5.0, referencing $value is only possible if the iterated array
|
||||
* can be referenced (i.e. if it is a variable).
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewForeachExpressionReferencingSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_FOREACH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'
|
||||
);
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\NewListInForeachSniff.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Detect unpacking nested arrays with list() in a foreach().
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewListInForeachSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_FOREACH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'
|
||||
);
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\NewMultiCatch.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ControlStructures;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ControlStructures\NewMultiCatch.
|
||||
*
|
||||
* Catching multiple exception types in one statement is available since PHP 7.1.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewMultiCatchSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_CATCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'
|
||||
);
|
||||
}
|
||||
}
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Extensions\RemovedExtensionsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Extensions;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Extensions\RemovedExtensionsSniff.
|
||||
*
|
||||
* Discourages the use of removed extensions. Suggests alternative extensions if available
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
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>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @var array(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,
|
||||
),
|
||||
'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/mvce',
|
||||
),
|
||||
'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.3' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'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',
|
||||
),
|
||||
'yp' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return "Extension '%s' is ";
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\ForbiddenParameterShadowSuperGlobalsSniff
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Declan Kelly <declankelly90@gmail.com>
|
||||
* @copyright 2015 Declan Kelly
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\ForbiddenParameterShadowSuperGlobalsSniff
|
||||
*
|
||||
* Discourages use of superglobals as parameters for functions.
|
||||
*
|
||||
* {@internal List of superglobals is maintained in the parent class.}}
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Declan Kelly <declankelly90@gmail.com>
|
||||
* @copyright 2015 Declan Kelly
|
||||
*/
|
||||
class ForbiddenParameterShadowSuperGlobalsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Register the tokens to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_FUNCTION,
|
||||
T_CLOSURE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the test.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\ForbiddenParametersWithSameName.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\ForbiddenParametersWithSameName.
|
||||
*
|
||||
* Functions can not have multiple parameters with the same name since PHP 7.0
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
class ForbiddenParametersWithSameNameSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_FUNCTION,
|
||||
T_CLOSURE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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[] = strtolower($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'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP 7.1 Forbidden variable names in closure use statements.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* PHP 7.1 Forbidden variable names 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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class ForbiddenVariableNamesInClosureUseSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_USE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\NewClosure.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\NewClosure.
|
||||
*
|
||||
* Closures are available since PHP 5.3
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
class NewClosureSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_CLOSURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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".
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\NewNullableTypes.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\NewNullableTypes.
|
||||
*
|
||||
* Nullable type hints and return types are available since PHP 7.1.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
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.}}
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\NewParamTypeDeclarationsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\NewParamTypeDeclarationsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string => array(string => int|string|null))
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string => string)
|
||||
*/
|
||||
protected $invalidTypes = array(
|
||||
'static' => 'self',
|
||||
'boolean' => 'bool',
|
||||
'integer' => 'int',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_FUNCTION,
|
||||
T_CLOSURE,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return "'%s' type declaration is not present in PHP version %s or earlier";
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\NewReturnTypeDeclarationsSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\NewReturnTypeDeclarationsSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string => array(string => int|string|null))
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return '%s return type is not present in PHP version %s or earlier';
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\NonStaticMagicMethodsSniff.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionDeclarations\NonStaticMagicMethodsSniff.
|
||||
*
|
||||
* Verifies the use of the correct visibility and static properties of magic methods.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string)
|
||||
*/
|
||||
protected $magicMethods = array(
|
||||
'__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(
|
||||
'static' => true,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionNameRestrictions\NewMagicMethodsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionNameRestrictions;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionNameRestrictions\NewMagicMethodsSniff.
|
||||
*
|
||||
* Warns for non-magic behaviour of magic methods prior to becoming magic.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string => array(string => int|string|null))
|
||||
*/
|
||||
protected $newMagicMethods = array(
|
||||
'__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.',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_FUNCTION);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('message');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionNameRestrictions\RemovedMagicAutoloadSniff.
|
||||
*
|
||||
* PHP version 7.2
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionNameRestrictions;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionNameRestrictions\RemovedMagicAutoloadSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
class RemovedMagicAutoloadSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Scopes to look for when testing using validDirectScope
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_FUNCTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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');
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionNameRestrictions\RemovedNamespacedAssertSniff.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionNameRestrictions;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* Removed Namespaced 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.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class RemovedNamespacedAssertSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Scopes in which an `assert` function can be declared without issue.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $scopes = array(
|
||||
T_CLASS,
|
||||
T_INTERFACE,
|
||||
T_TRAIT,
|
||||
T_CLOSURE,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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. Parse error, but not our concern.
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addWarning('Declaring a free-standing function called assert() is deprecated since PHP 7.3.', $stackPtr, 'Found');
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionNameRestrictions\RemovedPHP4StyleConstructorsSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Koen Eelen <koen.eelen@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionNameRestrictions;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionNameRestrictions\RemovedPHP4StyleConstructorsSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Koen Eelen <koen.eelen@cu.be>
|
||||
*/
|
||||
class RemovedPHP4StyleConstructorsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_CLASS,
|
||||
T_INTERFACE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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(T_FUNCTION, ($nextFunc + 1), $scopeCloser)) !== false) {
|
||||
$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'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionNameRestrictions\ReservedFunctionNamesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionNameRestrictions\ReservedFunctionNamesSniff.
|
||||
*
|
||||
* All function and method names starting with double underscore are reserved by PHP.
|
||||
*
|
||||
* {@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.}}
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class ReservedFunctionNamesSniff extends PHPCS_CamelCapsFunctionNameSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Overload the constructor to work round various PHPCS cross-version compatibility issues.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
$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.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
$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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+439
@@ -0,0 +1,439 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2018 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 value that was passed to a parameter, but will
|
||||
* instead provide the current value (which might have been modified).
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $noneFunctionCallIndicators = array(
|
||||
T_DOUBLE_COLON => true,
|
||||
T_OBJECT_OPERATOR => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* The tokens for variable incrementing/decrementing.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $plusPlusMinusMinus = array(
|
||||
T_DEC => true,
|
||||
T_INC => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Tokens to ignore when determining the start of a statement.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_FUNCTION,
|
||||
T_CLOSURE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionUse\ArgumentFunctionsUsageSniff.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionUse;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionUse\ArgumentFunctionsUsageSniff.
|
||||
*
|
||||
* - 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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class ArgumentFunctionsUsageSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* The target functions for this sniff.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_STRING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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 usage 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 usage 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
|
||||
);
|
||||
}
|
||||
}
|
||||
+1074
File diff suppressed because it is too large
Load Diff
Vendored
+1925
File diff suppressed because it is too large
Load Diff
+159
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionUse\OptionalToRequiredFunctionParametersSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionUse;
|
||||
|
||||
use PHPCompatibility\Sniffs\FunctionUse\RequiredToOptionalFunctionParametersSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionUse\OptionalToRequiredFunctionParametersSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionUse\RemovedFunctionParametersSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionUse;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionUse\RemovedFunctionParametersSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $removedFunctionParameters = array(
|
||||
'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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
$parameterCount = $this->getFunctionCallParameterCount($phpcsFile, $stackPtr);
|
||||
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) {
|
||||
$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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('name');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The "%s" parameter for function %s() is ';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the error data before it's passed to PHPCS.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
+970
@@ -0,0 +1,970 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionUse\RemovedFunctionsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionUse;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionUse\RemovedFunctionsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
class RemovedFunctionsSniff extends AbstractRemovedFeatureSniff
|
||||
{
|
||||
/**
|
||||
* A list of deprecated and removed functions with their alternatives.
|
||||
*
|
||||
* The array lists : version number with false (deprecated) or true (removed) and an alternative function.
|
||||
* If no alternative exists, it is NULL, i.e, the function should just not be used.
|
||||
*
|
||||
* @var array(string => array(string => bool|string|null))
|
||||
*/
|
||||
protected $removedFunctions = array(
|
||||
'php_check_syntax' => array(
|
||||
'5.0.5' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'call_user_method' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'call_user_func()',
|
||||
),
|
||||
'call_user_method_array' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'call_user_func_array()',
|
||||
),
|
||||
'define_syslog_variables' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'dl' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => null,
|
||||
),
|
||||
'ereg' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'preg_match()',
|
||||
),
|
||||
'ereg_replace' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'preg_replace()',
|
||||
),
|
||||
'eregi' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'preg_match()',
|
||||
),
|
||||
'eregi_replace' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'preg_replace()',
|
||||
),
|
||||
'imagepsbbox' => array(
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'imagepsencodefont' => array(
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'imagepsextendfont' => array(
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'imagepsfreefont' => array(
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'imagepsloadfont' => array(
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'imagepsslantfont' => array(
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'imagepstext' => array(
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'import_request_variables' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'ldap_sort' => array(
|
||||
'7.0' => false,
|
||||
'alternative' => null,
|
||||
),
|
||||
'mcrypt_generic_end' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'mcrypt_generic_deinit()',
|
||||
),
|
||||
'mysql_db_query' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'mysqli::select_db() and mysqli::query()',
|
||||
),
|
||||
'mysql_escape_string' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'mysqli::real_escape_string()',
|
||||
),
|
||||
'mysql_list_dbs' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'mysqli_bind_param' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => 'mysqli_stmt::bind_param()',
|
||||
),
|
||||
'mysqli_bind_result' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => 'mysqli_stmt::bind_result()',
|
||||
),
|
||||
'mysqli_client_encoding' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => 'mysqli::character_set_name()',
|
||||
),
|
||||
'mysqli_fetch' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => 'mysqli_stmt::fetch()',
|
||||
),
|
||||
'mysqli_param_count' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => 'mysqli_stmt_param_count()',
|
||||
),
|
||||
'mysqli_get_metadata' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => 'mysqli_stmt::result_metadata()',
|
||||
),
|
||||
'mysqli_send_long_data' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => 'mysqli_stmt::send_long_data()',
|
||||
),
|
||||
'magic_quotes_runtime' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'session_register' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => '$_SESSION',
|
||||
),
|
||||
'session_unregister' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => '$_SESSION',
|
||||
),
|
||||
'session_is_registered' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => '$_SESSION',
|
||||
),
|
||||
'set_magic_quotes_runtime' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'set_socket_blocking' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'stream_set_blocking()',
|
||||
),
|
||||
'split' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'preg_split()',
|
||||
),
|
||||
'spliti' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'preg_split()',
|
||||
),
|
||||
'sql_regcase' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'php_logo_guid' => array(
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'php_egg_logo_guid' => array(
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'php_real_logo_guid' => array(
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'zend_logo_guid' => array(
|
||||
'5.5' => true,
|
||||
'5.6' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'datefmt_set_timezone_id' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'IntlDateFormatter::setTimeZone()',
|
||||
),
|
||||
'mcrypt_ecb' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'mcrypt_cbc' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'mcrypt_cfb' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'mcrypt_ofb' => array(
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
'ocibindbyname' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_bind_by_name()',
|
||||
),
|
||||
'ocicancel' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_cancel()',
|
||||
),
|
||||
'ocicloselob' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Lob::close()',
|
||||
),
|
||||
'ocicollappend' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Collection::append()',
|
||||
),
|
||||
'ocicollassign' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Collection::assign()',
|
||||
),
|
||||
'ocicollassignelem' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Collection::assignElem()',
|
||||
),
|
||||
'ocicollgetelem' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Collection::getElem()',
|
||||
),
|
||||
'ocicollmax' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Collection::max()',
|
||||
),
|
||||
'ocicollsize' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Collection::size()',
|
||||
),
|
||||
'ocicolltrim' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Collection::trim()',
|
||||
),
|
||||
'ocicolumnisnull' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_field_is_null()',
|
||||
),
|
||||
'ocicolumnname' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_field_name()',
|
||||
),
|
||||
'ocicolumnprecision' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_field_precision()',
|
||||
),
|
||||
'ocicolumnscale' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_field_scale()',
|
||||
),
|
||||
'ocicolumnsize' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_field_size()',
|
||||
),
|
||||
'ocicolumntype' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_field_type()',
|
||||
),
|
||||
'ocicolumntyperaw' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_field_type_raw()',
|
||||
),
|
||||
'ocicommit' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_commit()',
|
||||
),
|
||||
'ocidefinebyname' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_define_by_name()',
|
||||
),
|
||||
'ocierror' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_error()',
|
||||
),
|
||||
'ociexecute' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_execute()',
|
||||
),
|
||||
'ocifetch' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_fetch()',
|
||||
),
|
||||
'ocifetchinto' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => null,
|
||||
),
|
||||
'ocifetchstatement' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_fetch_all()',
|
||||
),
|
||||
'ocifreecollection' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Collection::free()',
|
||||
),
|
||||
'ocifreecursor' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_free_statement()',
|
||||
),
|
||||
'ocifreedesc' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Lob::free()',
|
||||
),
|
||||
'ocifreestatement' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_free_statement()',
|
||||
),
|
||||
'ociinternaldebug' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_internal_debug()',
|
||||
),
|
||||
'ociloadlob' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Lob::load()',
|
||||
),
|
||||
'ocilogoff' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_close()',
|
||||
),
|
||||
'ocilogon' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_connect()',
|
||||
),
|
||||
'ocinewcollection' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_new_collection()',
|
||||
),
|
||||
'ocinewcursor' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_new_cursor()',
|
||||
),
|
||||
'ocinewdescriptor' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_new_descriptor()',
|
||||
),
|
||||
'ocinlogon' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_new_connect()',
|
||||
),
|
||||
'ocinumcols' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_num_fields()',
|
||||
),
|
||||
'ociparse' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_parse()',
|
||||
),
|
||||
'ociplogon' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_pconnect()',
|
||||
),
|
||||
'ociresult' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_result()',
|
||||
),
|
||||
'ocirollback' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_rollback()',
|
||||
),
|
||||
'ocirowcount' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_num_rows()',
|
||||
),
|
||||
'ocisavelob' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Lob::save()',
|
||||
),
|
||||
'ocisavelobfile' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Lob::import()',
|
||||
),
|
||||
'ociserverversion' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_server_version()',
|
||||
),
|
||||
'ocisetprefetch' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_set_prefetch()',
|
||||
),
|
||||
'ocistatementtype' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'oci_statement_type()',
|
||||
),
|
||||
'ociwritelobtofile' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Lob::export()',
|
||||
),
|
||||
'ociwritetemporarylob' => array(
|
||||
'5.4' => false,
|
||||
'5.5' => false,
|
||||
'5.6' => false,
|
||||
'alternative' => 'OCI-Lob::writeTemporary()',
|
||||
),
|
||||
'mysqli_get_cache_stats' => array(
|
||||
'5.4' => true,
|
||||
'alternative' => null,
|
||||
),
|
||||
|
||||
'mcrypt_create_iv' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'random_bytes() or OpenSSL',
|
||||
),
|
||||
'mcrypt_decrypt' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_enc_get_algorithms_name' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_enc_get_block_size' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_enc_get_iv_size' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_enc_get_key_size' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_enc_get_modes_name' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_enc_get_supported_key_sizes' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_enc_is_block_algorithm_mode' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_enc_is_block_algorithm' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_enc_is_block_mode' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_enc_self_test' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_encrypt' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_generic_deinit' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_generic_init' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_generic' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_get_block_size' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_get_cipher_name' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_get_iv_size' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_get_key_size' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_list_algorithms' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_list_modes' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_module_close' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_module_get_algo_block_size' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_module_get_algo_key_size' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_module_get_supported_key_sizes' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_module_is_block_algorithm_mode' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_module_is_block_algorithm' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_module_is_block_mode' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_module_open' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mcrypt_module_self_test' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'mdecrypt_generic' => array(
|
||||
'7.1' => false,
|
||||
'7.2' => true,
|
||||
'alternative' => 'OpenSSL',
|
||||
),
|
||||
'jpeg2wbmp' => array(
|
||||
'7.2' => false,
|
||||
'alternative' => 'imagecreatefromjpeg() and imagewbmp()',
|
||||
),
|
||||
'png2wbmp' => array(
|
||||
'7.2' => false,
|
||||
'alternative' => 'imagecreatefrompng() or imagewbmp()',
|
||||
),
|
||||
'create_function' => array(
|
||||
'7.2' => false,
|
||||
'alternative' => 'an anonymous function',
|
||||
),
|
||||
'each' => array(
|
||||
'7.2' => false,
|
||||
'alternative' => 'a foreach loop',
|
||||
),
|
||||
'gmp_random' => array(
|
||||
'7.2' => false,
|
||||
'alternative' => 'gmp_random_bits() or gmp_random_range()',
|
||||
),
|
||||
'read_exif_data' => array(
|
||||
'7.2' => false,
|
||||
'alternative' => 'exif_read_data()',
|
||||
),
|
||||
|
||||
'image2wbmp' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'imagewbmp()',
|
||||
),
|
||||
'mbregex_encoding' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_regex_encoding()',
|
||||
),
|
||||
'mbereg' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_ereg()',
|
||||
),
|
||||
'mberegi' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_eregi()',
|
||||
),
|
||||
'mbereg_replace' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_ereg_replace()',
|
||||
),
|
||||
'mberegi_replace' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_eregi_replace()',
|
||||
),
|
||||
'mbsplit' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_split()',
|
||||
),
|
||||
'mbereg_match' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_ereg_match()',
|
||||
),
|
||||
'mbereg_search' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_ereg_search()',
|
||||
),
|
||||
'mbereg_search_pos' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_ereg_search_pos()',
|
||||
),
|
||||
'mbereg_search_regs' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_ereg_search_regs()',
|
||||
),
|
||||
'mbereg_search_init' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_ereg_search_init()',
|
||||
),
|
||||
'mbereg_search_getregs' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_ereg_search_getregs()',
|
||||
),
|
||||
'mbereg_search_getpos' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_ereg_search_getpos()',
|
||||
),
|
||||
'mbereg_search_setpos' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => 'mb_ereg_search_setpos()',
|
||||
),
|
||||
'fgetss' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => null,
|
||||
),
|
||||
'gzgetss' => array(
|
||||
'7.3' => false,
|
||||
'alternative' => null,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Handle case-insensitivity of function names.
|
||||
$this->removedFunctions = $this->arrayKeysToLowercase($this->removedFunctions);
|
||||
|
||||
return array(T_STRING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$ignore = array(
|
||||
T_DOUBLE_COLON => true,
|
||||
T_OBJECT_OPERATOR => true,
|
||||
T_FUNCTION => true,
|
||||
T_CLASS => true,
|
||||
T_CONST => true,
|
||||
T_USE => true,
|
||||
T_NS_SEPARATOR => 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->removedFunctions[$functionLc]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $function,
|
||||
'nameLc' => $functionLc,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->removedFunctions[$itemInfo['nameLc']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'Function %s() is ';
|
||||
}
|
||||
}
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionUse\RequiredToOptionalFunctionParametersSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\FunctionUse;
|
||||
|
||||
use PHPCompatibility\AbstractComplexVersionSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\FunctionUse\RequiredToOptionalFunctionParametersSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $functionParameters = array(
|
||||
'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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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->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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('name');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Generators\NewGeneratorReturnSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Generators;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Generators\NewGeneratorReturnSniff.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewGeneratorReturnSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Scope conditions within which a yield can exist.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $validConditions = array(
|
||||
T_FUNCTION => T_FUNCTION,
|
||||
T_CLOSURE => T_CLOSURE,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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'];
|
||||
}
|
||||
}
|
||||
+663
@@ -0,0 +1,663 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\IniDirectives\NewIniDirectivesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2013 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\IniDirectives;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\IniDirectives\NewIniDirectivesSniff.
|
||||
*
|
||||
* Discourages the use of new INI directives through ini_set() or ini_get().
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2013 Cu.be Solutions bvba
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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,
|
||||
),
|
||||
|
||||
'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,
|
||||
),
|
||||
|
||||
'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,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('alternative');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
+346
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\IniDirectives\RemovedIniDirectivesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\IniDirectives;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\IniDirectives\RemovedIniDirectivesSniff.
|
||||
*
|
||||
* Discourages the use of deprecated and removed INI directives through ini_set() or ini_get().
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
class RemovedIniDirectivesSniff extends AbstractRemovedFeatureSniff
|
||||
{
|
||||
/**
|
||||
* A list of deprecated 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.
|
||||
*
|
||||
* @var array(string)
|
||||
*/
|
||||
protected $deprecatedIniDirectives = array(
|
||||
'fbsql.batchSize' => array(
|
||||
'5.1' => true,
|
||||
'alternative' => 'fbsql.batchsize',
|
||||
),
|
||||
|
||||
'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,
|
||||
),
|
||||
|
||||
'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.
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return "INI directive '%s' is ";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for suggesting an alternative for a specific sniff.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAlternativeOptionTemplate()
|
||||
{
|
||||
return str_replace("%s", "'%s'", parent::getAlternativeOptionTemplate());
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\InitialValue\NewConstantArraysUsingConstSniff.
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\InitialValue;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\InitialValue\NewConstantArraysUsingConstSniff.
|
||||
*
|
||||
* Constant arrays using the const keyword in PHP 5.6
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewConstantArraysUsingConstSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_CONST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\InitialValue\NewConstantArraysUsingDefineSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\InitialValue;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\InitialValue\NewConstantArraysUsingDefineSniff.
|
||||
*
|
||||
* Constant arrays using define in PHP 7.0
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
class NewConstantArraysUsingDefineSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+534
@@ -0,0 +1,534 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\InitialValue\NewConstantScalarExpressionsSniff.
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\InitialValue;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\InitialValue\NewConstantScalarExpressionsSniff.
|
||||
*
|
||||
* 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 function arguments.
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewConstantScalarExpressionsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Error message.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('5.5') !== true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void|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 ?
|
||||
*
|
||||
* @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 ?
|
||||
*
|
||||
* @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 bool $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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
+90
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\InitialValue\NewHeredocSniff.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\InitialValue;
|
||||
|
||||
use PHPCompatibility\Sniffs\InitialValue\NewConstantScalarExpressionsSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\InitialValue\NewHeredocSniff.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewHeredocSniff extends NewConstantScalarExpressionsSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Error message.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('5.2') !== true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is a value declared and does the declared value not contain an heredoc ?
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Interfaces\InternalInterfacesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Interfaces;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Interfaces\InternalInterfacesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
+336
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Interfaces\NewInterfacesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Interfaces;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Interfaces\NewInterfacesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string => array(string => int|string|null))
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string => array(string => string))
|
||||
*/
|
||||
protected $unsupportedMethods = array(
|
||||
'Serializable' => array(
|
||||
'__sleep' => 'http://php.net/serializable',
|
||||
'__wakeup' => 'http://php.net/serializable',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
$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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The built-in interface ' . parent::getErrorMsgTemplate();
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Keywords\CaseSensitiveKeywordsSniff.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Keywords;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Keywords\CaseSensitiveKeywordsSniff.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class CaseSensitiveKeywordsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_SELF,
|
||||
T_STATIC,
|
||||
T_PARENT,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Keywords\ForbiddenNamesAsDeclaredClassSniff.
|
||||
*
|
||||
* PHP version 7.0+
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Keywords;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Keywords\ForbiddenNamesAsDeclaredClassSniff.
|
||||
*
|
||||
* Prohibits 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.
|
||||
*
|
||||
* @see http://php.net/manual/en/reserved.other-reserved-words.php
|
||||
*
|
||||
* PHP version 7.0+
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class ForbiddenNamesAsDeclaredSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* List of tokens which can not be used as class, interface, trait names or as part of a namespace.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allForbiddenNames = array();
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Keywords\ForbiddenNamesAsInvokedFunctionsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Jansen Price <jansen.price@gmail.com>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Keywords;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Keywords\ForbiddenNamesAsInvokedFunctionsSniff.
|
||||
*
|
||||
* Prohibits the use of reserved keywords invoked as functions.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Jansen Price <jansen.price@gmail.com>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
class ForbiddenNamesAsInvokedFunctionsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* List of tokens to register.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
+391
@@ -0,0 +1,391 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Keywords\ForbiddenNamesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Keywords;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Keywords\ForbiddenNamesSniff.
|
||||
*
|
||||
* Prohibits the use of reserved keywords as class, function, namespace or constant names.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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',
|
||||
'do' => 'all',
|
||||
'else' => 'all',
|
||||
'elseif' => 'all',
|
||||
'enddeclare' => 'all',
|
||||
'endfor' => 'all',
|
||||
'endforeach' => 'all',
|
||||
'endif' => 'all',
|
||||
'endswitch' => 'all',
|
||||
'endwhile' => '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',
|
||||
'interface' => '5.0',
|
||||
'instanceof' => '5.0',
|
||||
'insteadof' => '5.4',
|
||||
'namespace' => '5.3',
|
||||
'new' => 'all',
|
||||
'or' => 'all',
|
||||
'private' => '5.0',
|
||||
'protected' => '5.0',
|
||||
'public' => '5.0',
|
||||
'static' => 'all',
|
||||
'switch' => 'all',
|
||||
'throw' => '5.0',
|
||||
'trait' => '5.4',
|
||||
'try' => '5.0',
|
||||
'use' => 'all',
|
||||
'var' => 'all',
|
||||
'while' => 'all',
|
||||
'xor' => 'all',
|
||||
'__class__' => 'all',
|
||||
'__dir__' => '5.3',
|
||||
'__file__' => 'all',
|
||||
'__function__' => 'all',
|
||||
'__method__' => 'all',
|
||||
'__namespace__' => '5.3',
|
||||
);
|
||||
|
||||
/**
|
||||
* A list of keywords that can follow use statements.
|
||||
*
|
||||
* @var array(string => string)
|
||||
*/
|
||||
protected $validUseNames = array(
|
||||
'const' => true,
|
||||
'function' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Scope modifiers and other keywords allowed in trait use statements.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowedModifiers = array();
|
||||
|
||||
/**
|
||||
* Targeted tokens.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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 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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
+366
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Keywords\NewKeywordsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2013 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Keywords;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Keywords\NewKeywordsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2013 Cu.be Solutions bvba
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string => array(string => int|string|null))
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string => string)
|
||||
*/
|
||||
protected $translateContentToToken = array();
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array(
|
||||
'description',
|
||||
'condition',
|
||||
'content',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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"`.
|
||||
*
|
||||
* @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] !== '"');
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\LanguageConstructs\NewEmptyNonVariableSniff.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\LanguageConstructs;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\LanguageConstructs\NewEmptyNonVariableSniff.
|
||||
*
|
||||
* Verify that nothing but variables are passed to empty().
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewEmptyNonVariableSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'
|
||||
);
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\LanguageConstructs\NewLanguageConstructsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2013 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\LanguageConstructs;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\LanguageConstructs\NewLanguageConstructsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2013 Cu.be Solutions bvba
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @var array(string => array(string => int|string|null))
|
||||
*/
|
||||
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' => 'variadic functions using ...',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('description');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
+183
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Lists\AssignmentOrderSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Lists;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* List assignment order.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class AssignmentOrderSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_LIST,
|
||||
T_OPEN_SHORT_ARRAY,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void|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);
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Lists\ForbiddenEmptyListAssignmentSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Lists;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Lists\ForbiddenEmptyListAssignmentSniff.
|
||||
*
|
||||
* Empty list() assignments have been removed in PHP 7.0
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
class ForbiddenEmptyListAssignmentSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* List of tokens to disregard when determining whether the list() is empty.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $ignoreTokens = array();
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
+211
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Lists\NewKeyedListSniff.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Lists;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Lists\NewKeyedListSniff.
|
||||
*
|
||||
* "You can now specify keys in list(), or its new shorthand [] syntax. "
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewKeyedListSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Tokens which represent the start of a list construct.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $allTargets;
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->setUpAllTargets();
|
||||
|
||||
return $this->sniffTargets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the $allTargets array only once.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function setUpAllTargets()
|
||||
{
|
||||
$this->allTargets = $this->sniffTargets + $this->targetsInList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('7.0') === false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Lists\NewListReferenceAssignmentSniff.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewListReferenceAssignmentSniff extends NewKeyedListSniff
|
||||
{
|
||||
/**
|
||||
* The token(s) within the list construct which is being targeted.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Lists\NewShortListSniff.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Lists;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Lists\NewShortListSniff.
|
||||
*
|
||||
* "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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewShortListSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_OPEN_SHORT_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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);
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2018 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
|
||||
*
|
||||
* @since 9.1.0
|
||||
*/
|
||||
class NewDirectCallsToCloneSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Direct calls to the __clone() magic method are not allowed in PHP 5.6 or earlier.',
|
||||
$nextNonEmpty,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Miscellaneous\RemovedAlternativePHPTags.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Miscellaneous;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Miscellaneous\RemovedAlternativePHPTags.
|
||||
*
|
||||
* Check for usage of alternative PHP tags - removed in PHP 7.0.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*
|
||||
* Based on `Generic_Sniffs_PHP_DisallowAlternativePHPTags` by Juliette Reinders Folmer
|
||||
* which was merged into PHPCS 2.7.0.
|
||||
*/
|
||||
class RemovedAlternativePHPTagsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether ASP tags are enabled or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $aspTags = false;
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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 find 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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Miscellaneous\ValidIntegersSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Miscellaneous;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Miscellaneous\ValidIntegersSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class ValidIntegersSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether PHPCS is run on a PHP < 5.4.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $isLowPHPVersion = false;
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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 an potentially be a binary integer ?
|
||||
*
|
||||
* @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]+$`D', $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]+$`D', $tokens[$stackPtr + 1]['content']) === 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current token an invalid binary integer ?
|
||||
*
|
||||
* @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]+$`D', $tokens[$stackPtr + 1]['content']) === 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the content of the tokens which together look like a binary integer.
|
||||
*
|
||||
* @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 ?
|
||||
*
|
||||
* @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 ?
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Operators\ForbiddenNegativeBitshift.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Operators;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Operators\ForbiddenNegativeBitshift.
|
||||
*
|
||||
* Bitwise shifts by negative number will throw an ArithmeticError in PHP 7.0.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
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.}}
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
+294
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Operators\NewOperatorsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2013 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Operators;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Operators\NewOperatorsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2013 Cu.be Solutions bvba
|
||||
*/
|
||||
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 keyword appears.
|
||||
*
|
||||
* @var array(string => array(string => int|string|null))
|
||||
*/
|
||||
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.
|
||||
/*
|
||||
* Was slated for 7.2, but still not implemented. PHPCS however does already tokenize it.
|
||||
* @link https://wiki.php.net/rfc/null_coalesce_equal_operator
|
||||
*/
|
||||
'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.
|
||||
*
|
||||
* @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.}}
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('description');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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 predecesor.
|
||||
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.
|
||||
*
|
||||
* @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 predecesor.
|
||||
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
+74
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Operators\NewShortTernarySniff.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Ben Selby <bselby@plus.net>
|
||||
* @copyright 2012 Ben Selby
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Operators;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Operators\NewShortTernarySniff.
|
||||
*
|
||||
* Performs checks on ternary operators, specifically that the middle expression
|
||||
* is not omitted for versions that don't support this.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Ben Selby <bselby@plus.net>
|
||||
* @copyright 2012 Ben Selby
|
||||
*/
|
||||
class NewShortTernarySniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_INLINE_THEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.2') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
// Get next non-whitespace token, and check it isn't the related inline else
|
||||
// symbol, which is not allowed prior to PHP 5.3.
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
|
||||
if ($next !== false && $tokens[$next]['code'] === T_INLINE_ELSE) {
|
||||
$phpcsFile->addError(
|
||||
'Middle may not be omitted from ternary operators in PHP < 5.3',
|
||||
$stackPtr,
|
||||
'MiddleMissing'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\ForbiddenGetClassNullSniff.
|
||||
*
|
||||
* PHP version 7.2
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\ForbiddenGetClassNullSniff.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class ForbiddenGetClassNullSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'get_class' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('7.2') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @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'
|
||||
);
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewArrayReduceInitialTypeSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewArrayReduceInitialTypeSniff.
|
||||
*
|
||||
* Detect: In PHP 5.2 and lower, the $initial parameter had to be an integer.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewArrayReduceInitialTypeSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('5.2') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @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'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewFopenModesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewFopenModesSniff.
|
||||
*
|
||||
* Detect: Changes in allowed values for the fopen() $mode parameter.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewFopenModesSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'fopen' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewHashAlgorithmsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewHashAlgorithmsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_STRING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The %s hash algorithm is not present in PHP version %s or earlier';
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewNegativeStringOffsetSniff.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewNegativeStringOffsetSniff.
|
||||
*
|
||||
* Detect: negative string offsets as parameters passed to functions where this
|
||||
* was not allowed prior to PHP 7.1.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewNegativeStringOffsetSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('7.0') === false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @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'],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewPCREModifiers.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\Sniffs\ParameterValues\RemovedPCREModifiersSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewPCREModifiers.
|
||||
*
|
||||
* Check for usage of newly added regex modifiers for PCRE functions.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewPCREModifiersSniff extends RemovedPCREModifiersSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'preg_replace' => true,
|
||||
'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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewPackFormatSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\NewPackFormatSniff.
|
||||
*
|
||||
* Detect: Changes in the allowed values for $format passed to pack().
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewPackFormatSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'pack' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* List of new format character codes added to pack().
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsBelow('7.1') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedHashAlgorithmsSniff.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedHashAlgorithmsSniff.
|
||||
*
|
||||
* Discourages the use of deprecated and removed hash algorithms.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_STRING);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The %s hash algorithm is ';
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedIconvEncodingSniff.
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedIconvEncodingSniff.
|
||||
*
|
||||
* Detect: "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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class RemovedIconvEncodingSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'iconv_set_encoding' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('5.6') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @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']
|
||||
);
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedMbstringModifiersSniff.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedMbstringModifiersSniff.
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class RemovedMbstringModifiersSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* Key is the function name, value the parameter position of the options parameter.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* This method has to be made concrete in child classes.
|
||||
*
|
||||
* @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');
|
||||
}
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedNonCryptoHashSniff.
|
||||
*
|
||||
* PHP version 7.2
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedNonCryptoHashSniff.
|
||||
*
|
||||
* Detect: "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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class RemovedNonCryptoHashSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'hash_hmac' => true,
|
||||
'hash_hmac_file' => true,
|
||||
'hash_init' => true,
|
||||
'hash_pbkdf2' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* List of the non-cryptographic hashes.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('7.2') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @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'],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedPCREModifiersSniff.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2014 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedPCREModifiersSniff.
|
||||
*
|
||||
* Check for usage of the `e` modifier with PCRE functions which is deprecated since PHP 5.5
|
||||
* and removed as of PHP 7.0.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2014 Cu.be Solutions bvba
|
||||
*/
|
||||
class RemovedPCREModifiersSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'preg_replace' => true,
|
||||
'preg_filter' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Regex bracket delimiters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $doublesSeparators = array(
|
||||
'{' => '}',
|
||||
'[' => ']',
|
||||
'(' => ')',
|
||||
'<' => '>',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('5.5') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Analyse a potential regex pattern for usage of the /e modifier.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedSetlocaleStringSniff.
|
||||
*
|
||||
* PHP version 4.2
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\ParameterValues;
|
||||
|
||||
use PHPCompatibility\AbstractFunctionCallParameterSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\ParameterValues\RemovedSetlocaleStringSniff.
|
||||
*
|
||||
* Detect: Support for the category parameter passed as a string has been removed.
|
||||
* Only LC_* constants can be used as of this version [7.0.0].
|
||||
*
|
||||
* PHP version 4.2
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class RemovedSetlocaleStringSniff extends AbstractFunctionCallParameterSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Functions to check for.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $targetFunctions = array(
|
||||
'setlocale' => true,
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Do a version check to determine if this sniff needs to run at all.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function bowOutEarly()
|
||||
{
|
||||
return ($this->supportsAbove('4.2') === false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the parameters of a matched function.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\ForbiddenCallTimePassByReference.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Gary Rogers <gmrwebde@gmail.com>
|
||||
* @author Florian Grandel <jerico.dev@gmail.com>
|
||||
* @copyright 2009 Florian Grandel
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\ForbiddenCallTimePassByReference.
|
||||
*
|
||||
* Discourages the use of call time pass by references
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Gary Rogers <gmrwebde@gmail.com>
|
||||
* @author Florian Grandel <jerico.dev@gmail.com>
|
||||
* @copyright 2009 Florian Grandel
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_STRING,
|
||||
T_VARIABLE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewArrayStringDereferencingSniff.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewArrayStringDereferencingSniff.
|
||||
*
|
||||
* Array and string literals can now be dereferenced directly to access individual elements and characters.
|
||||
*
|
||||
* PHP version 5.5
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewArrayStringDereferencingSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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();
|
||||
|
||||
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;
|
||||
} else {
|
||||
$type = 'arrays';
|
||||
$end = $tokens[$stackPtr]['parenthesis_closer'];
|
||||
}
|
||||
break;
|
||||
|
||||
case T_OPEN_SHORT_ARRAY:
|
||||
if (isset($tokens[$stackPtr]['bracket_closer']) === false) {
|
||||
// Live coding.
|
||||
return;
|
||||
} else {
|
||||
$type = 'arrays';
|
||||
$end = $tokens[$stackPtr]['bracket_closer'];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($type, $end) === false) {
|
||||
// Shouldn't happen, but for some reason did.
|
||||
return;
|
||||
}
|
||||
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true, null, true);
|
||||
|
||||
if ($nextNonEmpty !== false
|
||||
&& ($tokens[$nextNonEmpty]['type'] === 'T_OPEN_SQUARE_BRACKET'
|
||||
|| $tokens[$nextNonEmpty]['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',
|
||||
$nextNonEmpty,
|
||||
'Found',
|
||||
array($type)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewClassMemberAccessSniff.
|
||||
*
|
||||
* PHP version 5.4
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewClassMemberAccessSniff.
|
||||
*
|
||||
* 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().
|
||||
*
|
||||
* PHP version 5.4
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewClassMemberAccessSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_NEW,
|
||||
T_CLONE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === T_NEW && $this->supportsBelow('5.3') !== true) {
|
||||
return;
|
||||
} elseif ($tokens[$stackPtr]['code'] === T_CLONE && $this->supportsBelow('5.6') !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($tokens[$stackPtr]['nested_parenthesis']) === false) {
|
||||
// The `new className/clone $a` has to be in parentheses, without is not supported.
|
||||
return;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$nextAfterParenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, ($parenthesisCloser + 1), null, true);
|
||||
if ($nextAfterParenthesis === false) {
|
||||
// Live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$nextAfterParenthesis]['code'] !== T_OBJECT_OPERATOR
|
||||
&& $tokens[$nextAfterParenthesis]['code'] !== T_OPEN_SQUARE_BRACKET
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = array('instantiation', '5.3');
|
||||
$errorCode = 'OnNewFound';
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === T_CLONE) {
|
||||
$data = array('cloning', '5.6');
|
||||
$errorCode = 'OnCloneFound';
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Class member access on object %s was not supported in PHP %s or earlier',
|
||||
$parenthesisCloser,
|
||||
$errorCode,
|
||||
$data
|
||||
);
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewDynamicAccessToStaticSniff.
|
||||
*
|
||||
* PHP version 5.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewDynamicAccessToStaticSniff.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewDynamicAccessToStaticSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_DOUBLE_COLON,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(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'
|
||||
);
|
||||
}
|
||||
}
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewFlexibleHeredocNowdocSniff.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* New Flexible Heredoc Nowdoc.
|
||||
*
|
||||
* As of PHP 7.3:
|
||||
* - The body and the closing marker of a Heredoc/nowdoc can be indented;
|
||||
* - The closing marker no longer needs to be on a line by itself;
|
||||
* - The heredoc/nowdoc body may no longer contain the closing marker at the
|
||||
* start of any of its lines.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewFlexibleHeredocNowdocSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$targets = array(
|
||||
T_END_HEREDOC,
|
||||
T_END_NOWDOC,
|
||||
);
|
||||
|
||||
if (version_compare(PHP_VERSION_ID, '70299', '>') === false) {
|
||||
// Start identifier of a PHP 7.3 flexible heredoc/nowdoc.
|
||||
$targets[] = T_STRING;
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
/*
|
||||
* Due to a tokenizer bug which gets hit when the PHP 7.3 heredoc/nowdoc syntax
|
||||
* is used, this part of the sniff cannot possibly work on PHPCS < 2.6.0.
|
||||
* See upstream issue #928.
|
||||
*/
|
||||
if ($this->supportsBelow('7.2') === true && version_compare(PHPCSHelper::getVersion(), '2.6.0', '>=')) {
|
||||
$this->detectIndentedNonStandAloneClosingMarker($phpcsFile, $stackPtr);
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
if ($this->supportsAbove('7.3') === true && $tokens[$stackPtr]['code'] !== T_STRING) {
|
||||
$this->detectClosingMarkerInBody($phpcsFile, $stackPtr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Detect indented and/or non-stand alone closing markers.
|
||||
*
|
||||
* @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 detectIndentedNonStandAloneClosingMarker(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$indentError = 'Heredoc/nowdoc with an indented closing marker is not supported in PHP 7.2 or earlier.';
|
||||
$indentErrorCode = 'IndentedClosingMarker';
|
||||
$trailingError = 'Having code - other than a semi-colon or new line - after the closing marker of a heredoc/nowdoc is not supported in PHP 7.2 or earlier.';
|
||||
$trailingErrorCode = 'ClosingMarkerNoNewLine';
|
||||
|
||||
if (version_compare(PHP_VERSION_ID, '70299', '>') === true) {
|
||||
|
||||
/*
|
||||
* Check for indented closing marker.
|
||||
*/
|
||||
if (ltrim($tokens[$stackPtr]['content']) !== $tokens[$stackPtr]['content']) {
|
||||
$phpcsFile->addError($indentError, $stackPtr, $indentErrorCode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for tokens after the closing marker.
|
||||
*/
|
||||
$nextNonWhitespace = $phpcsFile->findNext(array(T_WHITESPACE, T_SEMICOLON), ($stackPtr + 1), null, true);
|
||||
if ($tokens[$stackPtr]['line'] === $tokens[$nextNonWhitespace]['line']) {
|
||||
$phpcsFile->addError($trailingError, $stackPtr, $trailingErrorCode);
|
||||
}
|
||||
} else {
|
||||
// For PHP < 7.3, we're only interested in T_STRING tokens.
|
||||
if ($tokens[$stackPtr]['code'] !== T_STRING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('`^<<<([\'"]?)([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\1[\r\n]+`', $tokens[$stackPtr]['content'], $matches) !== 1) {
|
||||
// Not the start of a PHP 7.3 flexible heredoc/nowdoc.
|
||||
return;
|
||||
}
|
||||
|
||||
$identifier = $matches[2];
|
||||
|
||||
for ($i = ($stackPtr + 1); $i <= $phpcsFile->numTokens; $i++) {
|
||||
if ($tokens[$i]['code'] !== T_ENCAPSED_AND_WHITESPACE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$trimmed = ltrim($tokens[$i]['content']);
|
||||
|
||||
if (strpos($trimmed, $identifier) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// OK, we've found the PHP 7.3 flexible heredoc/nowdoc closing marker.
|
||||
|
||||
/*
|
||||
* Check for indented closing marker.
|
||||
*/
|
||||
if ($trimmed !== $tokens[$i]['content']) {
|
||||
// Indent found before closing marker.
|
||||
$phpcsFile->addError($indentError, $i, $indentErrorCode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for tokens after the closing marker.
|
||||
*/
|
||||
// Remove the identifier.
|
||||
$afterMarker = substr($trimmed, strlen($identifier));
|
||||
// Remove a potential semi-colon at the beginning of what's left of the string.
|
||||
$afterMarker = ltrim($afterMarker, ';');
|
||||
// Remove new line characters at the end of the string.
|
||||
$afterMarker = rtrim($afterMarker, "\r\n");
|
||||
|
||||
if ($afterMarker !== '') {
|
||||
$phpcsFile->addError($trailingError, $i, $trailingErrorCode);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Detect heredoc/nowdoc identifiers at the start of lines in the heredoc/nowdoc body.
|
||||
*
|
||||
* @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 detectClosingMarkerInBody(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$error = 'The body of a heredoc/nowdoc can not contain the heredoc/nowdoc closing marker as text at the start of a line since PHP 7.3.';
|
||||
$errorCode = 'ClosingMarkerNoNewLine';
|
||||
|
||||
if (version_compare(PHP_VERSION_ID, '70299', '>') === true) {
|
||||
$nextNonWhitespace = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true, null, true);
|
||||
if ($nextNonWhitespace === false
|
||||
|| $tokens[$nextNonWhitespace]['code'] === T_SEMICOLON
|
||||
|| (($tokens[$nextNonWhitespace]['code'] === T_COMMA
|
||||
|| $tokens[$nextNonWhitespace]['code'] === T_STRING_CONCAT)
|
||||
&& $tokens[$nextNonWhitespace]['line'] !== $tokens[$stackPtr]['line'])
|
||||
) {
|
||||
// This is most likely a correctly identified closing marker.
|
||||
return;
|
||||
}
|
||||
|
||||
// The real closing tag has to be before the next heredoc/nowdoc.
|
||||
$nextHereNowDoc = $phpcsFile->findNext(array(T_START_HEREDOC, T_START_NOWDOC), ($stackPtr + 1));
|
||||
if ($nextHereNowDoc === false) {
|
||||
$nextHereNowDoc = null;
|
||||
}
|
||||
|
||||
$identifier = trim($tokens[$stackPtr]['content']);
|
||||
$realClosingMarker = $stackPtr;
|
||||
|
||||
while (($realClosingMarker = $phpcsFile->findNext(T_STRING, ($realClosingMarker + 1), $nextHereNowDoc, false, $identifier)) !== false) {
|
||||
|
||||
$prevNonWhitespace = $phpcsFile->findPrevious(T_WHITESPACE, ($realClosingMarker - 1), null, true);
|
||||
if ($prevNonWhitespace === false
|
||||
|| $tokens[$prevNonWhitespace]['line'] === $tokens[$realClosingMarker]['line']
|
||||
) {
|
||||
// Marker text found, but not at the start of the line.
|
||||
continue;
|
||||
}
|
||||
|
||||
// The original T_END_HEREDOC/T_END_NOWDOC was most likely incorrect as we've found
|
||||
// a possible alternative closing marker.
|
||||
$phpcsFile->addError($error, $stackPtr, $errorCode);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (isset($tokens[$stackPtr]['scope_closer'], $tokens[$stackPtr]['scope_opener']) === true
|
||||
&& $tokens[$stackPtr]['scope_closer'] === $stackPtr
|
||||
) {
|
||||
$opener = $tokens[$stackPtr]['scope_opener'];
|
||||
} else {
|
||||
// PHPCS < 3.0.2 did not add scope_* values for Nowdocs.
|
||||
$opener = $phpcsFile->findPrevious(T_START_NOWDOC, ($stackPtr - 1));
|
||||
if ($opener === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$quotedIdentifier = preg_quote($tokens[$stackPtr]['content'], '`');
|
||||
|
||||
// Throw an error for each line in the body which starts with the identifier.
|
||||
for ($i = ($opener + 1); $i < $stackPtr; $i++) {
|
||||
if (preg_match('`^[ \t]*' . $quotedIdentifier . '\b`', $tokens[$i]['content']) === 1) {
|
||||
$phpcsFile->addError($error, $i, $errorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewFunctionArrayDereferencingSniff.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewFunctionArrayDereferencingSniff.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
class NewFunctionArrayDereferencingSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
// Next non-empty token should be the open parenthesis.
|
||||
$openParenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
|
||||
if ($openParenthesis === false || $tokens[$openParenthesis]['code'] !== T_OPEN_PARENTHESIS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't throw errors during live coding.
|
||||
if (isset($tokens[$openParenthesis]['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this T_STRING really a function or method call ?
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
if ($prevToken !== false && in_array($tokens[$prevToken]['code'], array(T_DOUBLE_COLON, T_OBJECT_OPERATOR), true) === false) {
|
||||
$ignore = array(
|
||||
T_FUNCTION => true,
|
||||
T_CONST => true,
|
||||
T_USE => true,
|
||||
T_NEW => true,
|
||||
T_CLASS => true,
|
||||
T_INTERFACE => true,
|
||||
);
|
||||
|
||||
if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
|
||||
// Not a call to a PHP function or method.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$closeParenthesis = $tokens[$openParenthesis]['parenthesis_closer'];
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($closeParenthesis + 1), null, true, null, true);
|
||||
if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['type'] === 'T_OPEN_SQUARE_BRACKET') {
|
||||
$phpcsFile->addError(
|
||||
'Function array dereferencing is not present in PHP version 5.3 or earlier',
|
||||
$nextNonEmpty,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewFunctionCallTrailingCommaSniff.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewFunctionCallTrailingCommaSniff.
|
||||
*
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewFunctionCallTrailingCommaSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_STRING,
|
||||
T_VARIABLE,
|
||||
T_ISSET,
|
||||
T_UNSET,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('7.2') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($tokens[$nextNonEmpty]['code'] !== T_OPEN_PARENTHESIS
|
||||
|| isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === T_STRING) {
|
||||
$ignore = array(
|
||||
T_FUNCTION => true,
|
||||
T_CONST => true,
|
||||
T_USE => true,
|
||||
);
|
||||
|
||||
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
if (isset($ignore[$tokens[$prevNonEmpty]['code']]) === true) {
|
||||
// Not a function call.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$closer = $tokens[$nextNonEmpty]['parenthesis_closer'];
|
||||
$lastInParenthesis = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($closer - 1), $nextNonEmpty, true);
|
||||
|
||||
if ($tokens[$lastInParenthesis]['code'] !== T_COMMA) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = array();
|
||||
switch ($tokens[$stackPtr]['code']) {
|
||||
case T_ISSET:
|
||||
$data[] = 'calls to isset()';
|
||||
$errorCode = 'FoundInIsset';
|
||||
break;
|
||||
|
||||
case T_UNSET:
|
||||
$data[] = 'calls to unset()';
|
||||
$errorCode = 'FoundInUnset';
|
||||
break;
|
||||
|
||||
default:
|
||||
$data[] = 'function calls';
|
||||
$errorCode = 'FoundInFunctionCall';
|
||||
break;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Trailing comma\'s are not allowed in %s in PHP 7.2 or earlier',
|
||||
$lastInParenthesis,
|
||||
$errorCode,
|
||||
$data
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewShortArray.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Alex Miroshnikov <unknown@example.com>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\NewShortArray.
|
||||
*
|
||||
* Short array syntax is available since PHP 5.4
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Alex Miroshnikov <unknown@example.com>
|
||||
*/
|
||||
class NewShortArraySniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_OPEN_SHORT_ARRAY,
|
||||
T_CLOSE_SHORT_ARRAY,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$token = $tokens[$stackPtr];
|
||||
|
||||
$error = '%s is available since 5.4';
|
||||
$data = array();
|
||||
|
||||
if ($token['type'] === 'T_OPEN_SHORT_ARRAY') {
|
||||
$data[] = 'Short array syntax (open)';
|
||||
} elseif ($token['type'] === 'T_CLOSE_SHORT_ARRAY') {
|
||||
$data[] = 'Short array syntax (close)';
|
||||
}
|
||||
|
||||
$phpcsFile->addError($error, $stackPtr, 'Found', $data);
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\RemovedNewReferenceSniff.
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Syntax;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Syntax\RemovedNewReferenceSniff.
|
||||
*
|
||||
* Discourages the use of assigning the return value of new by reference
|
||||
*
|
||||
* PHP version 5.4
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
* @copyright 2012 Cu.be Solutions bvba
|
||||
*/
|
||||
class RemovedNewReferenceSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_NEW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('5.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
|
||||
if ($prevNonEmpty === false || $tokens[$prevNonEmpty]['type'] !== 'T_BITWISE_AND') {
|
||||
return;
|
||||
}
|
||||
|
||||
$error = 'Assigning the return value of new by reference is deprecated in PHP 5.3';
|
||||
$isError = false;
|
||||
$errorCode = 'Deprecated';
|
||||
|
||||
if ($this->supportsAbove('7.0') === true) {
|
||||
$error .= ' and has been removed in PHP 7.0';
|
||||
$isError = true;
|
||||
$errorCode = 'Removed';
|
||||
}
|
||||
|
||||
$this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode);
|
||||
}
|
||||
}
|
||||
Vendored
+203
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\TypeCasts\NewTypeCastsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\TypeCasts;
|
||||
|
||||
use PHPCompatibility\AbstractNewFeatureSniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\TypeCasts\NewTypeCastsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewTypeCastsSniff extends AbstractNewFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of new type casts, 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.
|
||||
*
|
||||
* @var array(string => array(string => int|string|null))
|
||||
*/
|
||||
protected $newTypeCasts = array(
|
||||
'T_UNSET_CAST' => array(
|
||||
'4.4' => false,
|
||||
'5.0' => true,
|
||||
'description' => 'The unset cast',
|
||||
),
|
||||
'T_BINARY_CAST' => array(
|
||||
'5.2.0' => false,
|
||||
'5.2.1' => true,
|
||||
'description' => 'The binary cast',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$tokens = array();
|
||||
foreach ($this->newTypeCasts as $token => $versions) {
|
||||
if (defined($token)) {
|
||||
$tokens[] = constant($token);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Work around tokenizer issues.
|
||||
*
|
||||
* - (binary) cast is incorrectly tokenized as T_STRING_CAST by PHP and PHPCS.
|
||||
* - b"something" binary cast is incorrectly tokenized as T_CONSTANT_ENCAPSED_STRING by PHP and PHPCS.
|
||||
* - Since PHPCS 3.4.0, PHPCS *will* tokenize these correctly.
|
||||
*
|
||||
* @link https://github.com/squizlabs/PHP_CodeSniffer/issues/1574
|
||||
*/
|
||||
if (version_compare(PHPCSHelper::getVersion(), '3.4.0', '<') === true) {
|
||||
$tokens[] = T_STRING_CAST;
|
||||
$tokens[] = T_CONSTANT_ENCAPSED_STRING;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$tokenType = $tokens[$stackPtr]['type'];
|
||||
|
||||
// Detect incorrectly tokenized binary casts.
|
||||
if (isset($this->newTypeCasts[$tokenType]) === false) {
|
||||
$tokenContent = $tokens[$stackPtr]['content'];
|
||||
switch ($tokenType) {
|
||||
case 'T_STRING_CAST':
|
||||
if (preg_match('`^\(\s*binary\s*\)$`i', $tokenContent) !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokenType = 'T_BINARY_CAST';
|
||||
break;
|
||||
|
||||
case 'T_CONSTANT_ENCAPSED_STRING':
|
||||
if ((strpos($tokenContent, 'b"') === 0 && substr($tokenContent, -1) === '"')
|
||||
|| (strpos($tokenContent, "b'") === 0 && substr($tokenContent, -1) === "'")
|
||||
) {
|
||||
$tokenType = 'T_BINARY_CAST';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// If the translation did not yield one of the tokens we are looking for, bow out.
|
||||
if (isset($this->newTypeCasts[$tokenType]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $tokenType,
|
||||
'content' => $tokens[$stackPtr]['content'],
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->newTypeCasts[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of the non-PHP-version array keys used in a sub-array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('description');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the relevant detail (version) information for use in an error message.
|
||||
*
|
||||
* @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 message before it's passed to PHPCS.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
return $error . '. Found: %s';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the error data before it's passed to PHPCS.
|
||||
*
|
||||
* @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'];
|
||||
$data[] = $itemInfo['content'];
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\TypeCasts\RemovedTypeCastsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\TypeCasts;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\TypeCasts\RemovedTypeCastsSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class RemovedTypeCastsSniff extends AbstractRemovedFeatureSniff
|
||||
{
|
||||
/**
|
||||
* A list of deprecated and removed type casts with their alternatives.
|
||||
*
|
||||
* The array lists : version number with false (deprecated) or true (removed) and an alternative function.
|
||||
* If no alternative exists, it is NULL, i.e, the function should just not be used.
|
||||
*
|
||||
* @var array(string => array(string => bool|string|null))
|
||||
*/
|
||||
protected $deprecatedTypeCasts = array(
|
||||
'T_UNSET_CAST' => array(
|
||||
'7.2' => false,
|
||||
'alternative' => 'unset()',
|
||||
'description' => 'unset',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$tokens = array();
|
||||
foreach ($this->deprecatedTypeCasts as $token => $versions) {
|
||||
$tokens[] = constant($token);
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$tokenType = $tokens[$stackPtr]['type'];
|
||||
|
||||
$itemInfo = array(
|
||||
'name' => $tokenType,
|
||||
'description' => $this->deprecatedTypeCasts[$tokenType]['description'],
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of the non-PHP-version array keys used in a sub-array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNonVersionArrayKeys()
|
||||
{
|
||||
return array('description', 'alternative');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->deprecatedTypeCasts[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return 'The %s cast is ';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the error data before it's passed to PHPCS.
|
||||
*
|
||||
* @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] = $itemInfo['description'];
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
Vendored
+171
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Upgrade\LowPHPCSSniff.
|
||||
*
|
||||
* @category Upgrade
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Upgrade;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Upgrade\LowPHPCSSniff.
|
||||
*
|
||||
* Add a notification for users of low PHPCS versions.
|
||||
*
|
||||
* Originally PHPCompatibility supported PHPCS 1.5.x, 2.x and since PHPCompatibility 8.0.0, 3.x.
|
||||
* Support for PHPCS < 2.3.0 has been dropped in PHPCompatibility 9.0.0.
|
||||
*
|
||||
* The standard will - up to a point - still work for users of lower
|
||||
* PHPCS versions, but will give less accurate results and may throw
|
||||
* notices and warnings (or even fatal out).
|
||||
*
|
||||
* This sniff adds an explicit error/warning for users of the standard
|
||||
* using a PHPCS version below the recommended version.
|
||||
*
|
||||
* @category Upgrade
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class LowPHPCSSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* The minimum supported PHPCS version.
|
||||
*
|
||||
* Users on PHPCS versions below this will see an ERROR message.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $minSupportedVersion = '2.3.0';
|
||||
|
||||
/**
|
||||
* The minimum recommended PHPCS version.
|
||||
*
|
||||
* Users on PHPCS versions below this will see a WARNING.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $minRecommendedVersion = '2.6.0';
|
||||
|
||||
/**
|
||||
* Keep track of whether this sniff needs to actually run.
|
||||
*
|
||||
* This will be set to `false` when either a high enough PHPCS
|
||||
* version is detected or once the error/warning has been thrown,
|
||||
* to make sure that the notice will only be thrown once per run.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $examine = true;
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(
|
||||
T_OPEN_TAG,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
// Don't do anything if the warning has already been thrown or is not necessary.
|
||||
if ($this->examine === false) {
|
||||
return ($phpcsFile->numTokens + 1);
|
||||
}
|
||||
|
||||
$phpcsVersion = PHPCSHelper::getVersion();
|
||||
|
||||
// Don't do anything if the PHPCS version used is above the minimum recommended version.
|
||||
if (version_compare($phpcsVersion, $this->minRecommendedVersion, '>=')) {
|
||||
$this->examine = false;
|
||||
return ($phpcsFile->numTokens + 1);
|
||||
}
|
||||
|
||||
if (version_compare($phpcsVersion, $this->minSupportedVersion, '<')) {
|
||||
$isError = true;
|
||||
$message = "IMPORTANT: Please be advised that the minimum PHP_CodeSniffer version the PHPCompatibility standard supports is %s. You are currently using PHP_CodeSniffer %s. Please upgrade your PHP_CodeSniffer installation. The recommended version of PHP_CodeSniffer for PHPCompatibility is %s or higher.";
|
||||
$errorCode = 'Unsupported_' . $this->stringToErrorCode($this->minSupportedVersion);
|
||||
$replacements = array(
|
||||
$this->minSupportedVersion,
|
||||
$phpcsVersion,
|
||||
$this->minRecommendedVersion,
|
||||
$errorCode,
|
||||
);
|
||||
} else {
|
||||
$isError = false;
|
||||
$message = "IMPORTANT: Please be advised that for the most reliable PHPCompatibility results, PHP_CodeSniffer %s or higher should be used. Support for lower versions will be dropped in the foreseeable future. You are currently using PHP_CodeSniffer %s. Please upgrade your PHP_CodeSniffer installation to version %s or higher.";
|
||||
$errorCode = 'BelowRecommended_' . $this->stringToErrorCode($this->minRecommendedVersion);
|
||||
$replacements = array(
|
||||
$this->minRecommendedVersion,
|
||||
$phpcsVersion,
|
||||
$this->minRecommendedVersion,
|
||||
$errorCode,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out the report width to determine how long the delimiter lines should be.
|
||||
*
|
||||
* This is not an exact calculation as there are a number of unknowns at the time the
|
||||
* notice is thrown (whether there are other notices for the file, whether those are
|
||||
* warnings or errors, whether there are auto-fixable issues etc).
|
||||
*
|
||||
* In other words, this is just an approximation to get a reasonably stable and
|
||||
* readable message layout format.
|
||||
*
|
||||
* {@internal
|
||||
* PHPCS has had some changes as to how the messages display over the years.
|
||||
* Most significantly in 2.4.0 it was attempted to solve an issue with messages
|
||||
* containing new lines. Unfortunately, that solution is buggy.
|
||||
* An improved version has been pulled upstream and will hopefully make it
|
||||
* into PHPCS 3.3.1/3.4.0.
|
||||
*
|
||||
* Anyway, this means that instead of new lines, delimiter lines will be used to improved
|
||||
* the readability of the (long) message.
|
||||
*
|
||||
* Also, as of PHPCS 2.2.0, the report width when using the `-s` option is 8 wider than
|
||||
* it should be. A patch for that is included in the same upstream PR.
|
||||
*
|
||||
* If/when the upstream PR has been merged and the minimum supported/recommended version
|
||||
* of PHPCompatibility would go beyond that, the below code should be adjusted.}}
|
||||
*/
|
||||
$reportWidth = PHPCSHelper::getCommandLineData($phpcsFile, 'reportWidth');
|
||||
$showSources = PHPCSHelper::getCommandLineData($phpcsFile, 'showSources');
|
||||
if ($showSources === true && version_compare($phpcsVersion, '2.3.0', '>=')) {
|
||||
$reportWidth += 6;
|
||||
}
|
||||
|
||||
$messageWidth = ($reportWidth - 15); // 15 is length of " # | WARNING | ".
|
||||
$delimiterLine = str_repeat('-', ($messageWidth));
|
||||
$disableNotice = 'To disable this notice, add --exclude=PHPCompatibility.Upgrade.LowPHPCS to your command or add <exclude name="PHPCompatibility.Upgrade.LowPHPCS.%s"/> to your custom ruleset. ';
|
||||
$thankYou = 'Thank you for using PHPCompatibility!';
|
||||
|
||||
$message .= ' ' . $delimiterLine;
|
||||
$message .= ' ' . $disableNotice;
|
||||
$message .= ' ' . $delimiterLine;
|
||||
$message .= ' ' . $thankYou;
|
||||
|
||||
$this->addMessage($phpcsFile, $message, 0, $isError, $errorCode, $replacements);
|
||||
|
||||
$this->examine = false;
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\UseDeclarations\NewGroupUseDeclarationsSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\UseDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\UseDeclarations\NewGroupUseDeclarationsSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
class NewGroupUseDeclarationsSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if (defined('T_OPEN_USE_GROUP')) {
|
||||
return array(T_OPEN_USE_GROUP);
|
||||
} else {
|
||||
return array(T_USE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in
|
||||
* the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('7.1') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$token = $tokens[$stackPtr];
|
||||
|
||||
// Deal with PHPCS pre-2.6.0.
|
||||
if ($token['code'] === T_USE) {
|
||||
$hasCurlyBrace = $phpcsFile->findNext(T_OPEN_CURLY_BRACKET, ($stackPtr + 1), null, false, null, true);
|
||||
if ($hasCurlyBrace === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($hasCurlyBrace - 1), null, true);
|
||||
if ($prevToken === false || $tokens[$prevToken]['code'] !== T_NS_SEPARATOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stackPtr = $hasCurlyBrace;
|
||||
}
|
||||
|
||||
// Still here ? In that case, it is a group use statement.
|
||||
if ($this->supportsBelow('5.6') === true) {
|
||||
$phpcsFile->addError(
|
||||
'Group use declarations are not allowed in PHP 5.6 or earlier',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
|
||||
$closers = array(T_CLOSE_CURLY_BRACKET);
|
||||
if (defined('T_CLOSE_USE_GROUP')) {
|
||||
$closers[] = T_CLOSE_USE_GROUP;
|
||||
}
|
||||
|
||||
$closeCurly = $phpcsFile->findNext($closers, ($stackPtr + 1), null, false, null, true);
|
||||
if ($closeCurly === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($closeCurly - 1), null, true);
|
||||
if ($tokens[$prevToken]['code'] === T_COMMA) {
|
||||
$phpcsFile->addError(
|
||||
'Trailing comma\'s are not allowed in group use statements in PHP 7.1 or earlier',
|
||||
$prevToken,
|
||||
'TrailingCommaFound'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\UseDeclarations\NewUseConstFunctionSniff.
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\UseDeclarations;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\UseDeclarations\NewUseConstFunctionSniff.
|
||||
*
|
||||
* The use operator has been extended to support importing functions and
|
||||
* constants in addition to classes. This is achieved via the use function
|
||||
* and use const constructs, respectively.
|
||||
*
|
||||
* PHP version 5.6
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewUseConstFunctionSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of keywords that can follow use statements.
|
||||
*
|
||||
* @var array(string => string)
|
||||
*/
|
||||
protected $validUseNames = array(
|
||||
'const' => true,
|
||||
'function' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_USE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsBelow('5.5') !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($nextNonEmpty === false) {
|
||||
// Live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->validUseNames[strtolower($tokens[$nextNonEmpty]['content'])]) === false) {
|
||||
// Not a `use const` or `use function` statement.
|
||||
return;
|
||||
}
|
||||
|
||||
// `use const` and `use function` have to be followed by the function/constant name.
|
||||
$functionOrConstName = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), null, true);
|
||||
if ($functionOrConstName === false
|
||||
// Identifies as T_AS or T_STRING, this covers both.
|
||||
|| ($tokens[$functionOrConstName]['content'] === 'as'
|
||||
|| $tokens[$functionOrConstName]['code'] === T_COMMA)
|
||||
) {
|
||||
// Live coding or incorrect use of reserved keyword, but that is
|
||||
// covered by the ForbiddenNames sniff.
|
||||
return;
|
||||
}
|
||||
|
||||
// Still here ? In that case we have encountered a `use const` or `use function` statement.
|
||||
$phpcsFile->addError(
|
||||
'Importing functions and constants through a "use" statement is not supported in PHP 5.5 or lower.',
|
||||
$nextNonEmpty,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\ForbiddenGlobalVariableVariableSniff.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Variables;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\ForbiddenGlobalVariableVariableSniff.
|
||||
*
|
||||
* Variable variables are forbidden with global
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim@cu.be>
|
||||
*/
|
||||
class ForbiddenGlobalVariableVariableSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_GLOBAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$endOfStatement = $phpcsFile->findNext(array(T_SEMICOLON, T_CLOSE_TAG), ($stackPtr + 1));
|
||||
if ($endOfStatement === false) {
|
||||
// No semi-colon - live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
for ($ptr = ($stackPtr + 1); $ptr <= $endOfStatement; $ptr++) {
|
||||
$errorThrown = false;
|
||||
$nextComma = $phpcsFile->findNext(T_COMMA, $ptr, $endOfStatement, false, null, true);
|
||||
$varEnd = ($nextComma === false) ? $endOfStatement : $nextComma;
|
||||
$variable = $phpcsFile->findNext(T_VARIABLE, $ptr, $varEnd);
|
||||
$varString = trim($phpcsFile->getTokensAsString($ptr, ($varEnd - $ptr)));
|
||||
$data = array($varString);
|
||||
|
||||
if ($variable !== false) {
|
||||
|
||||
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($variable - 1), $ptr, true);
|
||||
|
||||
if ($prev !== false && $tokens[$prev]['type'] === 'T_DOLLAR') {
|
||||
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($variable + 1), $varEnd, true);
|
||||
|
||||
if ($next !== false
|
||||
&& in_array($tokens[$next]['code'], array(T_OPEN_SQUARE_BRACKET, T_OBJECT_OPERATOR, T_DOUBLE_COLON), true) === true
|
||||
) {
|
||||
$phpcsFile->addError(
|
||||
'Global with variable variables is not allowed since PHP 7.0. Found %s',
|
||||
$variable,
|
||||
'Found',
|
||||
$data
|
||||
);
|
||||
$errorThrown = true;
|
||||
} else {
|
||||
$phpcsFile->addWarning(
|
||||
'Global with anything other than bare variables is discouraged since PHP 7.0. Found %s',
|
||||
$variable,
|
||||
'NonBareVariableFound',
|
||||
$data
|
||||
);
|
||||
$errorThrown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($errorThrown === false) {
|
||||
$dollar = $phpcsFile->findNext(T_DOLLAR, $ptr, $varEnd);
|
||||
if ($dollar !== false) {
|
||||
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($dollar + 1), $varEnd, true);
|
||||
if ($tokens[$next]['code'] === T_OPEN_CURLY_BRACKET) {
|
||||
$phpcsFile->addWarning(
|
||||
'Global with anything other than bare variables is discouraged since PHP 7.0. Found %s',
|
||||
$dollar,
|
||||
'NonBareVariableFound',
|
||||
$data
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move the stack pointer forward to the next variable for multi-variable statements.
|
||||
if ($nextComma === false) {
|
||||
break;
|
||||
}
|
||||
$ptr = $nextComma;
|
||||
}
|
||||
}
|
||||
}
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
/**
|
||||
* PHPCompatibility, an external standard for PHP_CodeSniffer.
|
||||
*
|
||||
* @package PHPCompatibility
|
||||
* @copyright 2012-2018 PHPCompatibility Contributors
|
||||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
|
||||
* @link https://github.com/PHPCompatibility/PHPCompatibility
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Variables;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* Detect using $this in incompatible contexts.
|
||||
*
|
||||
* "Whilst $this is considered a special variable in PHP, it lacked proper checks
|
||||
* to ensure it wasn't used as a variable name or reassigned. This has now been
|
||||
* rectified to ensure that $this cannot be a user-defined variable, reassigned
|
||||
* to a different value, or be globalised."
|
||||
*
|
||||
* This sniff only addresses those situations which did *not* throw an error prior
|
||||
* to PHP 7.1, either at all or only in PHP 7.0.
|
||||
* In other words, the following situation, while mentioned in the RFC, will NOT
|
||||
* be sniffed for:
|
||||
* - Using $this as static variable. (error _message_ change only).
|
||||
*
|
||||
* Also, the changes with relation to assigning $this dynamically can not be
|
||||
* sniffed for reliably, so are not covered by this sniff.
|
||||
* - Disable ability to re-assign $this indirectly through $$.
|
||||
* - Disable ability to re-assign $this indirectly through reference.
|
||||
* - Disable ability to re-assign $this indirectly through extract() and parse_str().
|
||||
*
|
||||
* Other changes not (yet) covered:
|
||||
* - get_defined_vars() always doesn't show value of variable $this.
|
||||
* - Always show true $this value in magic method __call().
|
||||
* {@internal This could possibly be covered. Similar logic as "outside object context",
|
||||
* but with function name check and supportsBelow('7.0').}}
|
||||
*
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @link https://wiki.php.net/rfc/this_var
|
||||
*
|
||||
* @since 9.1.0
|
||||
*/
|
||||
class ForbiddenThisUseContextsSniff extends Sniff
|
||||
{
|
||||
|
||||
/**
|
||||
* OO scope tokens.
|
||||
*
|
||||
* Duplicate of Tokens::$ooScopeTokens array in PHPCS which was added in 3.1.0.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $ooScopeTokens = array(
|
||||
'T_CLASS' => T_CLASS,
|
||||
'T_INTERFACE' => T_INTERFACE,
|
||||
'T_TRAIT' => T_TRAIT,
|
||||
);
|
||||
|
||||
/**
|
||||
* Scopes to skip over when examining the contents of functions.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $skipOverScopes = array(
|
||||
'T_FUNCTION' => true,
|
||||
'T_CLOSURE' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Valid uses of $this in plain functions or methods outside object context.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $validUseOutsideObject = array(
|
||||
T_ISSET => true,
|
||||
T_EMPTY => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if (defined('T_ANON_CLASS')) {
|
||||
$this->ooScopeTokens['T_ANON_CLASS'] = T_ANON_CLASS;
|
||||
}
|
||||
|
||||
$this->skipOverScopes += $this->ooScopeTokens;
|
||||
|
||||
return array(
|
||||
T_FUNCTION,
|
||||
T_CLOSURE,
|
||||
T_GLOBAL,
|
||||
T_CATCH,
|
||||
T_FOREACH,
|
||||
T_UNSET,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.1') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
switch ($tokens[$stackPtr]['code']) {
|
||||
case T_FUNCTION:
|
||||
$this->isThisUsedAsParameter($phpcsFile, $stackPtr);
|
||||
$this->isThisUsedOutsideObjectContext($phpcsFile, $stackPtr);
|
||||
break;
|
||||
|
||||
case T_CLOSURE:
|
||||
$this->isThisUsedAsParameter($phpcsFile, $stackPtr);
|
||||
break;
|
||||
|
||||
case T_GLOBAL:
|
||||
/*
|
||||
* $this can no longer be imported using the `global` keyword.
|
||||
* This worked in PHP 7.0, though in PHP 5.x, it would throw a
|
||||
* fatal "Cannot re-assign $this" error.
|
||||
*/
|
||||
$endOfStatement = $phpcsFile->findNext(array(T_SEMICOLON, T_CLOSE_TAG), ($stackPtr + 1));
|
||||
if ($endOfStatement === false) {
|
||||
// No semi-colon - live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
for ($i = ($stackPtr + 1); $i < $endOfStatement; $i++) {
|
||||
if ($tokens[$i]['code'] !== T_VARIABLE || $tokens[$i]['content'] !== '$this') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used with the "global" keyword since PHP 7.1.',
|
||||
$i,
|
||||
'Global'
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case T_CATCH:
|
||||
/*
|
||||
* $this can no longer be used as a catch variable.
|
||||
*/
|
||||
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$varPtr = $phpcsFile->findNext(
|
||||
T_VARIABLE,
|
||||
($tokens[$stackPtr]['parenthesis_opener'] + 1),
|
||||
$tokens[$stackPtr]['parenthesis_closer']
|
||||
);
|
||||
|
||||
if ($varPtr === false || $tokens[$varPtr]['content'] !== '$this') {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used as a catch variable since PHP 7.1.',
|
||||
$varPtr,
|
||||
'Catch'
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case T_FOREACH:
|
||||
/*
|
||||
* $this can no longer be used as a foreach *value* variable.
|
||||
* This worked in PHP 7.0, though in PHP 5.x, it would throw a
|
||||
* fatal "Cannot re-assign $this" error.
|
||||
*/
|
||||
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stopPtr = $phpcsFile->findPrevious(
|
||||
array(T_AS, T_DOUBLE_ARROW),
|
||||
($tokens[$stackPtr]['parenthesis_closer'] - 1),
|
||||
$tokens[$stackPtr]['parenthesis_opener']
|
||||
);
|
||||
if ($stopPtr === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$valueVarPtr = $phpcsFile->findNext(
|
||||
T_VARIABLE,
|
||||
($stopPtr + 1),
|
||||
$tokens[$stackPtr]['parenthesis_closer']
|
||||
);
|
||||
if ($valueVarPtr === false || $tokens[$valueVarPtr]['content'] !== '$this') {
|
||||
return;
|
||||
}
|
||||
|
||||
$afterThis = $phpcsFile->findNext(
|
||||
Tokens::$emptyTokens,
|
||||
($valueVarPtr + 1),
|
||||
$tokens[$stackPtr]['parenthesis_closer'],
|
||||
true
|
||||
);
|
||||
|
||||
if ($afterThis !== false
|
||||
&& ($tokens[$afterThis]['code'] === T_OBJECT_OPERATOR
|
||||
|| $tokens[$afterThis]['code'] === T_DOUBLE_COLON)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used as value variable in a foreach control structure since PHP 7.1.',
|
||||
$valueVarPtr,
|
||||
'ForeachValueVar'
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case T_UNSET:
|
||||
/*
|
||||
* $this can no longer be unset.
|
||||
*/
|
||||
$openParenthesis = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
if ($openParenthesis === false
|
||||
|| $tokens[$openParenthesis]['code'] !== T_OPEN_PARENTHESIS
|
||||
|| isset($tokens[$openParenthesis]['parenthesis_closer']) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ($i = ($openParenthesis + 1); $i < $tokens[$openParenthesis]['parenthesis_closer']; $i++) {
|
||||
if ($tokens[$i]['code'] !== T_VARIABLE || $tokens[$i]['content'] !== '$this') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$afterThis = $phpcsFile->findNext(
|
||||
Tokens::$emptyTokens,
|
||||
($i + 1),
|
||||
$tokens[$openParenthesis]['parenthesis_closer'],
|
||||
true
|
||||
);
|
||||
|
||||
if ($afterThis !== false
|
||||
&& ($tokens[$afterThis]['code'] === T_OBJECT_OPERATOR
|
||||
|| $tokens[$afterThis]['code'] === T_DOUBLE_COLON
|
||||
|| $tokens[$afterThis]['code'] === T_OPEN_SQUARE_BRACKET)
|
||||
) {
|
||||
$i = $afterThis;
|
||||
continue;
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be unset since PHP 7.1.',
|
||||
$i,
|
||||
'Unset'
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $this is used as a parameter in a function declaration.
|
||||
*
|
||||
* $this can no longer be used as a parameter in a *global* function.
|
||||
* Use as a parameter in a method was already an error prior to PHP 7.1.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
protected function isThisUsedAsParameter(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) !== false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$params = PHPCSHelper::getMethodParameters($phpcsFile, $stackPtr);
|
||||
if (empty($params)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
foreach ($params as $param) {
|
||||
if ($param['name'] !== '$this') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$stackPtr]['code'] === T_FUNCTION) {
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used as a parameter since PHP 7.1.',
|
||||
$param['token'],
|
||||
'FunctionParam'
|
||||
);
|
||||
} else {
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used as a closure parameter since PHP 7.0.7.',
|
||||
$param['token'],
|
||||
'ClosureParam'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $this is used in a plain function or method.
|
||||
*
|
||||
* Prior to PHP 7.1, this would result in an "undefined variable" notice
|
||||
* and execution would continue with $this regarded as `null`.
|
||||
* As of PHP 7.1, this throws an exception.
|
||||
*
|
||||
* Note: use within isset() and empty() to check object context is still allowed.
|
||||
* Note: $this can still be used within a closure.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
protected function isThisUsedOutsideObjectContext(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
if (isset($tokens[$stackPtr]['scope_opener'], $tokens[$stackPtr]['scope_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->validDirectScope($phpcsFile, $stackPtr, $this->ooScopeTokens) !== false) {
|
||||
$methodProps = $phpcsFile->getMethodProperties($stackPtr);
|
||||
if ($methodProps['is_static'] === false) {
|
||||
return;
|
||||
} else {
|
||||
$methodName = $phpcsFile->getDeclarationName($stackPtr);
|
||||
if ($methodName === '__call') {
|
||||
/*
|
||||
* This is an exception.
|
||||
* @link https://wiki.php.net/rfc/this_var#always_show_true_this_value_in_magic_method_call
|
||||
*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = ($tokens[$stackPtr]['scope_opener'] + 1); $i < $tokens[$stackPtr]['scope_closer']; $i++) {
|
||||
if (isset($this->skipOverScopes[$tokens[$i]['type']])) {
|
||||
if (isset($tokens[$i]['scope_closer']) === false) {
|
||||
// Live coding or parse error, will only lead to inaccurate results.
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip over nested structures.
|
||||
$i = $tokens[$i]['scope_closer'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] !== T_VARIABLE || $tokens[$i]['content'] !== '$this') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($tokens[$i]['nested_parenthesis']) === true) {
|
||||
$nestedParenthesis = $tokens[$i]['nested_parenthesis'];
|
||||
$nestedOpenParenthesis = array_keys($nestedParenthesis);
|
||||
$lastOpenParenthesis = array_pop($nestedOpenParenthesis);
|
||||
|
||||
$previousNonEmpty = $phpcsFile->findPrevious(
|
||||
Tokens::$emptyTokens,
|
||||
($lastOpenParenthesis - 1),
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
if (isset($this->validUseOutsideObject[$tokens[$previousNonEmpty]['code']])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'"$this" can no longer be used in a plain function or method since PHP 7.1.',
|
||||
$i,
|
||||
'OutsideObjectContext'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\NewUniformVariableSyntax.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Variables;
|
||||
|
||||
use PHPCompatibility\Sniff;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\NewUniformVariableSyntax.
|
||||
*
|
||||
* The interpretation of variable variables has changed in PHP 7.0.
|
||||
*
|
||||
* PHP version 7.0
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
|
||||
*/
|
||||
class NewUniformVariableSyntaxSniff extends Sniff
|
||||
{
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_VARIABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token
|
||||
* in the stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('7.0') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
|
||||
// Verify that the next token is a square open bracket. If not, bow out.
|
||||
$nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true, null, true);
|
||||
|
||||
if ($nextToken === false || $tokens[$nextToken]['code'] !== T_OPEN_SQUARE_BRACKET || isset($tokens[$nextToken]['bracket_closer']) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The previous non-empty token has to be a $, -> or ::.
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
|
||||
if ($prevToken === false || in_array($tokens[$prevToken]['code'], array(T_DOLLAR, T_OBJECT_OPERATOR, T_DOUBLE_COLON), true) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For static object calls, it only applies when this is a function call.
|
||||
if ($tokens[$prevToken]['code'] === T_DOUBLE_COLON) {
|
||||
$hasBrackets = $tokens[$nextToken]['bracket_closer'];
|
||||
while (($hasBrackets = $phpcsFile->findNext(Tokens::$emptyTokens, ($hasBrackets + 1), null, true, null, true)) !== false) {
|
||||
if ($tokens[$hasBrackets]['code'] === T_OPEN_SQUARE_BRACKET) {
|
||||
if (isset($tokens[$hasBrackets]['bracket_closer'])) {
|
||||
$hasBrackets = $tokens[$hasBrackets]['bracket_closer'];
|
||||
continue;
|
||||
} else {
|
||||
// Live coding.
|
||||
return;
|
||||
}
|
||||
|
||||
} elseif ($tokens[$hasBrackets]['code'] === T_OPEN_PARENTHESIS) {
|
||||
// Caught!
|
||||
break;
|
||||
|
||||
} else {
|
||||
// Not a function call, so bow out.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now let's also prevent false positives when used with self and static which still work fine.
|
||||
$classToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevToken - 1), null, true, null, true);
|
||||
if ($classToken !== false) {
|
||||
if ($tokens[$classToken]['code'] === T_STATIC || $tokens[$classToken]['code'] === T_SELF) {
|
||||
return;
|
||||
} elseif ($tokens[$classToken]['code'] === T_STRING && $tokens[$classToken]['content'] === 'self') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$phpcsFile->addError(
|
||||
'Indirect access to variables, properties and methods will be evaluated strictly in left-to-right order since PHP 7.0. Use curly braces to remove ambiguity.',
|
||||
$stackPtr,
|
||||
'Found'
|
||||
);
|
||||
}
|
||||
}
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\RemovedPredefinedGlobalVariablesSniff.
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
|
||||
namespace PHPCompatibility\Sniffs\Variables;
|
||||
|
||||
use PHPCompatibility\AbstractRemovedFeatureSniff;
|
||||
use PHPCompatibility\PHPCSHelper;
|
||||
use PHP_CodeSniffer_File as File;
|
||||
use PHP_CodeSniffer_Tokens as Tokens;
|
||||
|
||||
/**
|
||||
* \PHPCompatibility\Sniffs\Variables\RemovedPredefinedGlobalVariablesSniff.
|
||||
*
|
||||
* Discourages the use of removed global variables. Suggests alternative extensions if available
|
||||
*
|
||||
* @category PHP
|
||||
* @package PHPCompatibility
|
||||
* @author Wim Godden <wim.godden@cu.be>
|
||||
*/
|
||||
class RemovedPredefinedGlobalVariablesSniff extends AbstractRemovedFeatureSniff
|
||||
{
|
||||
|
||||
/**
|
||||
* A list of removed global variables 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 variable was deprecated/removed.
|
||||
*
|
||||
* @var array(string|null)
|
||||
*/
|
||||
protected $removedGlobalVariables = array(
|
||||
'HTTP_POST_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_POST',
|
||||
),
|
||||
'HTTP_GET_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_GET',
|
||||
),
|
||||
'HTTP_ENV_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_ENV',
|
||||
),
|
||||
'HTTP_SERVER_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_SERVER',
|
||||
),
|
||||
'HTTP_COOKIE_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_COOKIE',
|
||||
),
|
||||
'HTTP_SESSION_VARS' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_SESSION',
|
||||
),
|
||||
'HTTP_POST_FILES' => array(
|
||||
'5.3' => false,
|
||||
'5.4' => true,
|
||||
'alternative' => '$_FILES',
|
||||
),
|
||||
|
||||
'HTTP_RAW_POST_DATA' => array(
|
||||
'5.6' => false,
|
||||
'7.0' => true,
|
||||
'alternative' => 'php://input',
|
||||
),
|
||||
|
||||
'php_errormsg' => array(
|
||||
'7.2' => false,
|
||||
'alternative' => 'error_get_last()',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of tokens this test wants to listen for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
return array(T_VARIABLE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes this test, when one of its tokens is encountered.
|
||||
*
|
||||
* @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
|
||||
* @param int $stackPtr The position of the current token in the
|
||||
* stack passed in $tokens.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process(File $phpcsFile, $stackPtr)
|
||||
{
|
||||
if ($this->supportsAbove('5.3') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokens = $phpcsFile->getTokens();
|
||||
$varName = substr($tokens[$stackPtr]['content'], 1);
|
||||
|
||||
if (isset($this->removedGlobalVariables[$varName]) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isClassProperty($phpcsFile, $stackPtr) === true) {
|
||||
// Ok, so this was a class property declaration, not our concern.
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for static usage of class properties shadowing the removed global variables.
|
||||
if ($this->inClassScope($phpcsFile, $stackPtr, false) === true) {
|
||||
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
|
||||
if ($prevToken !== false && $tokens[$prevToken]['code'] === T_DOUBLE_COLON) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Do some additional checks for the $php_errormsg variable.
|
||||
if ($varName === 'php_errormsg'
|
||||
&& $this->isTargetPHPErrormsgVar($phpcsFile, $stackPtr, $tokens) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Still here, so throw an error/warning.
|
||||
$itemInfo = array(
|
||||
'name' => $varName,
|
||||
);
|
||||
$this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the relevant sub-array for a specific item from a multi-dimensional array.
|
||||
*
|
||||
* @param array $itemInfo Base information about the item.
|
||||
*
|
||||
* @return array Version and other information about the item.
|
||||
*/
|
||||
public function getItemArray(array $itemInfo)
|
||||
{
|
||||
return $this->removedGlobalVariables[$itemInfo['name']];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the error message template for this sniff.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMsgTemplate()
|
||||
{
|
||||
return "Global variable '\$%s' is ";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the error message before it's passed to PHPCS.
|
||||
*
|
||||
* @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 ($itemInfo['name'] === 'php_errormsg') {
|
||||
$error = str_replace('Global', 'The', $error);
|
||||
}
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run some additional checks for the `$php_errormsg` variable.
|
||||
*
|
||||
* @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 Token array of the current file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isTargetPHPErrormsgVar(File $phpcsFile, $stackPtr, array $tokens)
|
||||
{
|
||||
$scopeStart = 0;
|
||||
|
||||
/*
|
||||
* If the variable is detected within the scope of a function/closure, limit the checking.
|
||||
*/
|
||||
$function = $phpcsFile->getCondition($stackPtr, T_CLOSURE);
|
||||
if ($function === false) {
|
||||
$function = $phpcsFile->getCondition($stackPtr, T_FUNCTION);
|
||||
}
|
||||
|
||||
// It could also be a function param, which is not in the function scope.
|
||||
if ($function === false && isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
|
||||
$nestedParentheses = $tokens[$stackPtr]['nested_parenthesis'];
|
||||
$parenthesisCloser = end($nestedParentheses);
|
||||
if (isset($tokens[$parenthesisCloser]['parenthesis_owner'])
|
||||
&& ($tokens[$tokens[$parenthesisCloser]['parenthesis_owner']]['code'] === T_FUNCTION
|
||||
|| $tokens[$tokens[$parenthesisCloser]['parenthesis_owner']]['code'] === T_CLOSURE)
|
||||
) {
|
||||
$function = $tokens[$parenthesisCloser]['parenthesis_owner'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($function !== false) {
|
||||
$scopeStart = $tokens[$function]['scope_opener'];
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, let's do some additional checks.
|
||||
*/
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
||||
|
||||
// Is the variable being used as an array ?
|
||||
if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === T_OPEN_SQUARE_BRACKET) {
|
||||
// The PHP native variable is a string, so this is probably not it
|
||||
// (except for array access to string, but why would you in this case ?).
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is this a variable assignment ?
|
||||
if ($nextNonEmpty !== false
|
||||
&& isset(Tokens::$assignmentTokens[$tokens[$nextNonEmpty]['code']]) === true
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is this a function param shadowing the PHP native one ?
|
||||
if ($function !== false) {
|
||||
$parameters = PHPCSHelper::getMethodParameters($phpcsFile, $function);
|
||||
if (is_array($parameters) === true && empty($parameters) === false) {
|
||||
foreach ($parameters as $param) {
|
||||
if ($param['name'] === '$php_errormsg') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$skipPast = array(
|
||||
'T_CLASS' => true,
|
||||
'T_ANON_CLASS' => true,
|
||||
'T_INTERFACE' => true,
|
||||
'T_TRAIT' => true,
|
||||
'T_FUNCTION' => true,
|
||||
'T_CLOSURE' => true,
|
||||
);
|
||||
|
||||
// Walk back and see if there is an assignment to the variable within the same scope.
|
||||
for ($i = ($stackPtr - 1); $i >= $scopeStart; $i--) {
|
||||
if ($tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
|
||||
&& isset($tokens[$i]['scope_condition'])
|
||||
&& isset($skipPast[$tokens[$tokens[$i]['scope_condition']]['type']])
|
||||
) {
|
||||
// Skip past functions, classes etc.
|
||||
$i = $tokens[$i]['scope_condition'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$i]['code'] !== T_VARIABLE || $tokens[$i]['content'] !== '$php_errormsg') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true);
|
||||
|
||||
if ($nextNonEmpty !== false
|
||||
&& isset(Tokens::$assignmentTokens[$tokens[$nextNonEmpty]['code']]) === true
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user