wishthis/vendor/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Context.php
2023-09-20 13:52:46 +02:00

232 lines
7.4 KiB
PHP

<?php
/**
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
*
* @package PHPCSUtils
* @copyright 2019-2020 PHPCSUtils Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSUtils
*/
namespace PHPCSUtils\Utils;
use PHP_CodeSniffer\Files\File;
use PHPCSUtils\Utils\Parentheses;
/**
* Utility functions for examining in which context a certain token is used.
*
* Example use-cases:
* - A sniff looking for the use of certain variables may want to disregard the "use"
* of these variables within a call to `isset()` or `empty()`.
* - A sniff looking for incrementor/decrementors may want to disregard these when used
* as the third expression in a `for()` condition.
*
* @since 1.0.0
*/
final class Context
{
/**
* Check whether an arbitrary token is within a call to empty().
*
* _This method is a thin, descriptive wrapper around the {@see Parentheses::getLastOwner()} method.
* For more complex/combined queries, it is recommended to call the {@see Parentheses::getLastOwner()}
* method directly._
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the token we are checking.
*
* @return bool
*/
public static function inEmpty(File $phpcsFile, $stackPtr)
{
return (Parentheses::getLastOwner($phpcsFile, $stackPtr, \T_EMPTY) !== false);
}
/**
* Check whether an arbitrary token is within a call to isset().
*
* _This method is a thin, descriptive wrapper around the {@see Parentheses::getLastOwner()} method.
* For more complex/combined queries, it is recommended to call the {@see Parentheses::getLastOwner()}
* method directly._
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the token we are checking.
*
* @return bool
*/
public static function inIsset(File $phpcsFile, $stackPtr)
{
return (Parentheses::getLastOwner($phpcsFile, $stackPtr, \T_ISSET) !== false);
}
/**
* Check whether an arbitrary token is within a call to unset().
*
* _This method is a thin, descriptive wrapper around the {@see Parentheses::getLastOwner()} method.
* For more complex/combined queries, it is recommended to call the {@see Parentheses::getLastOwner()}
* method directly._
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the token we are checking.
*
* @return bool
*/
public static function inUnset(File $phpcsFile, $stackPtr)
{
return (Parentheses::getLastOwner($phpcsFile, $stackPtr, \T_UNSET) !== false);
}
/**
* Check whether an arbitrary token is within an attribute.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the token we are checking.
*
* @return bool
*/
public static function inAttribute(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// Check for the existence of the token.
if (isset($tokens[$stackPtr]) === false) {
return false;
}
if (isset($tokens[$stackPtr]['attribute_opener'], $tokens[$stackPtr]['attribute_closer']) === false) {
return false;
}
return ($stackPtr !== $tokens[$stackPtr]['attribute_opener']
&& $stackPtr !== $tokens[$stackPtr]['attribute_closer']);
}
/**
* Check whether an arbitrary token is in a foreach condition and if so, in which part:
* before or after the "as".
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the token we are checking.
*
* @return string|false String `'beforeAs'`, `'as'` or `'afterAs'` when the token is within
* a `foreach` condition.
* `FALSE` in all other cases, including for parse errors.
*/
public static function inForeachCondition(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// Check for the existence of the token.
if (isset($tokens[$stackPtr]) === false) {
return false;
}
$foreach = Parentheses::getLastOwner($phpcsFile, $stackPtr, \T_FOREACH);
if ($foreach === false) {
return false;
}
if ($tokens[$stackPtr]['code'] === \T_AS) {
return 'as';
}
$asPtr = $phpcsFile->findNext(
\T_AS,
($tokens[$foreach]['parenthesis_opener'] + 1),
$tokens[$foreach]['parenthesis_closer']
);
if ($asPtr === false) {
// Parse error or live coding.
return false;
}
if ($stackPtr < $asPtr) {
return 'beforeAs';
}
return 'afterAs';
}
/**
* Check whether an arbitrary token is in a for condition and if so, in which part:
* the first, second or third expression.
*
* Note: the semicolons separating the conditions are regarded as belonging with the
* expression before it.
*
* @since 1.0.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the token we are checking.
*
* @return string|false String `'expr1'`, `'expr2'` or `'expr3'` when the token is within
* a `for` condition.
* `FALSE` in all other cases, including for parse errors.
*/
public static function inForCondition(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// Check for the existence of the token.
if (isset($tokens[$stackPtr]) === false) {
return false;
}
$for = Parentheses::getLastOwner($phpcsFile, $stackPtr, \T_FOR);
if ($for === false) {
return false;
}
$semicolons = [];
$count = 0;
$opener = $tokens[$for]['parenthesis_opener'];
$closer = $tokens[$for]['parenthesis_closer'];
$level = $tokens[$for]['level'];
$parens = 1;
if (isset($tokens[$for]['nested_parenthesis'])) {
$parens = (\count($tokens[$for]['nested_parenthesis']) + 1);
}
for ($i = ($opener + 1); $i < $closer; $i++) {
if ($tokens[$i]['code'] !== \T_SEMICOLON) {
continue;
}
if ($tokens[$i]['level'] !== $level
|| \count($tokens[$i]['nested_parenthesis']) !== $parens
) {
// Disregard semi-colons at lower nesting/condition levels.
continue;
}
++$count;
$semicolons[$count] = $i;
}
if ($count !== 2) {
return false;
}
foreach ($semicolons as $key => $ptr) {
if ($stackPtr <= $ptr) {
return 'expr' . $key;
}
}
return 'expr3';
}
}