2016-08-29 15:50:59 -04:00
< ? php
/**
* CodeIgniter_Sniffs_Strings_DoubleQuoteUsageSniff .
*
* PHP version 5
*
* @ category PHP
* @ package PHP_CodeSniffer
* @ author Thomas Ernest < thomas . ernest @ baobaz . com >
* @ copyright 2011 Thomas Ernest
* @ license http :// thomas . ernest . fr / developement / php_cs / licence GNU General Public License
* @ link http :// pear . php . net / package / PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Strings ;
use PHP_CodeSniffer\Sniffs\Sniff ;
use PHP_CodeSniffer\Files\File ;
2017-12-06 14:40:13 -05:00
use Exception ;
2016-08-29 15:50:59 -04:00
/**
* CodeIgniter_Sniffs_Strings_DoubleQuoteUsageSniff .
*
* Ensures that double - quoted strings are used only to parse variables ,
* to avoid escape characters before single quotes or for chars that need
* to be interpreted like \r , \n or \t .
* If a double - quoted string contain both single and double quotes
* but no variable , then a warning is raised to encourage the use of
* single - quoted strings .
*
* @ category PHP
* @ package PHP_CodeSniffer
* @ author Thomas Ernest < thomas . ernest @ baobaz . com >
* @ copyright 2011 Thomas Ernest
* @ license http :// thomas . ernest . fr / developement / php_cs / licence GNU General Public License
* @ link http :// pear . php . net / package / PHP_CodeSniffer
*/
class VariableUsageSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for .
*
* @ return array
*/
public function register ()
{
/*
return array (
T_DOUBLE_QUOTED_STRING ,
T_CONSTANT_ENCAPSED_STRING ,
);
*/
return array ();
} //end register()
/**
* Processes this test , when one of its tokens is encountered .
*
* @ param File $phpcsFile The current 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 ();
$string = $tokens [ $stackPtr ][ 'content' ];
// makes sure that it is about a double quote string,
// since variables are not parsed out of double quoted string
$openDblQtStr = substr ( $string , 0 , 1 );
if ( 0 === strcmp ( $openDblQtStr , '"' )) {
$this -> processDoubleQuotedString ( $phpcsFile , $stackPtr , $string );
} else if ( 0 === strcmp ( $openDblQtStr , " ' " )) {
$this -> processSingleQuotedString ( $phpcsFile , $stackPtr , $string );
}
} //end process()
/**
* Processes this test , when the token encountered is a double - quoted string .
*
* @ param File $phpcsFile The current file being scanned .
* @ param int $stackPtr The position of the current token
* in the stack passed in $tokens .
* @ param string $dblQtString The double - quoted string content ,
* i . e . without quotes .
*
* @ return void
*/
protected function processDoubleQuotedString ( File $phpcsFile , $stackPtr , $dblQtString )
{
$variableFound = FALSE ;
$strTokens = token_get_all ( '<?php ' . $dblQtString );
$strPtr = 1 ; // skip php opening tag added by ourselves
$requireDblQuotes = FALSE ;
while ( $strPtr < count ( $strTokens )) {
$strToken = $strTokens [ $strPtr ];
if ( is_array ( $strToken )) {
if ( in_array ( $strToken [ 0 ], array ( T_DOLLAR_OPEN_CURLY_BRACES , T_CURLY_OPEN ))) {
$strPtr ++ ;
try {
$this -> _parseVariable ( $strTokens , $strPtr );
} catch ( Exception $err ) {
$error = 'There is no variable, object nor array between curly braces. Please use the escape char for $ or {.' ;
2017-12-06 14:40:13 -05:00
$phpcsFile -> addError ( $error , $stackPtr , 234 );
2016-08-29 15:50:59 -04:00
}
$variableFound = TRUE ;
if ( '}' !== $strTokens [ $strPtr ]) {
$error = 'There is no matching closing curly brace.' ;
2017-12-06 14:40:13 -05:00
$phpcsFile -> addError ( $error , $stackPtr , 345 );
2016-08-29 15:50:59 -04:00
}
// don't move forward, since it will be done in the main loop
// $strPtr++;
} else if ( T_VARIABLE === $strToken [ 0 ]) {
$variableFound = TRUE ;
$error = " Variable { $strToken [ 1 ] } in double-quoted strings should be enclosed with curly braces. Please consider { { $strToken [ 1 ] } } " ;
2017-12-06 14:40:13 -05:00
$phpcsFile -> addError ( $error , $stackPtr , 456 );
2016-08-29 15:50:59 -04:00
}
}
$strPtr ++ ;
}
return $variableFound ;
} //end processDoubleQuotedString()
/**
* Processes this test , when the token encountered is a single - quoted string .
*
* @ param File $phpcsFile The current file being scanned .
* @ param int $stackPtr The position of the current token
* in the stack passed in $tokens .
* @ param string $sglQtString The single - quoted string content ,
* i . e . without quotes .
*
* @ return void
*/
protected function processSingleQuotedString ( File $phpcsFile , $stackPtr , $sglQtString )
{
$variableFound = FALSE ;
$strTokens = token_get_all ( '<?php ' . $sglQtString );
$strPtr = 1 ; // skip php opening tag added by ourselves
while ( $strPtr < count ( $strTokens )) {
$strToken = $strTokens [ $strPtr ];
if ( is_array ( $strToken )) {
if ( T_VARIABLE === $strToken [ 0 ]) {
$error = " Variables like { $strToken [ 1 ] } should be in double-quoted strings only. " ;
$phpcsFile -> addError ( $error , $stackPtr );
}
}
$strPtr ++ ;
}
return $variableFound ;
} //end processSingleQuotedString()
/**
* Grammar rule to parse the use of a variable . Please notice that it
* doesn ' t manage the leading $ .
*
* _parseVariable ::= < variable >
* | < variable > _parseObjectAttribute ()
* | < variable > _parseArrayIndexes ()
*
* @ exception Exception raised if $strTokens starting from $strPtr
* doesn ' t matched the rule .
*
* @ param array $strTokens Tokens to parse .
* @ param int $strPtr Pointer to the token where parsing starts .
*
* @ return array The attribute name associated to index 'var' , an array with
* indexes 'obj' and 'attr' or an array with indexes 'arr' and 'idx' .
*/
private function _parseVariable ( $strTokens , & $strPtr )
{
if ( ! in_array ( $strTokens [ $strPtr ][ 0 ], array ( T_VARIABLE , T_STRING_VARNAME ))) {
throw new Exception ( 'Expected variable name.' );
}
$var = $strTokens [ $strPtr ][ 1 ];
$strPtr ++ ;
$startStrPtr = $strPtr ;
try {
$attr = $this -> _parseObjectAttribute ( $strTokens , $strPtr );
return array ( 'obj' => $var , 'attr' => $attr );
} catch ( Exception $err ) {
if ( $strPtr !== $startStrPtr ) {
throw $err ;
}
}
try {
$idx = $this -> _parseArrayIndexes ( $strTokens , $strPtr );
return array ( 'arr' => $var , 'idx' => $idx );
} catch ( Exception $err ) {
if ( $strPtr !== $startStrPtr ) {
throw $err ;
}
}
return array ( 'var' => $var );
} //end _parseVariable()
/**
* Grammar rule to parse the use of an object attribute .
*
* _parseObjectAttribute ::= ->< attribute >
* | ->< attribute > _parseObjectAttribute ()
* | ->< attribute > _parseArrayIndexes ()
*
* @ exception Exception raised if $strTokens starting from $strPtr
* doesn ' t matched the rule .
*
* @ param array $strTokens Tokens to parse .
* @ param int $strPtr Pointer to the token where parsing starts .
*
* @ return mixed The attribute name as a string , an array with indexes
* 'obj' and 'attr' or an array with indexes 'arr' and 'idx' .
*/
private function _parseObjectAttribute ( $strTokens , & $strPtr )
{
if ( T_OBJECT_OPERATOR !== $strTokens [ $strPtr ][ 0 ]) {
throw new Exception ( 'Expected ->.' );
}
$strPtr ++ ;
if ( T_STRING !== $strTokens [ $strPtr ][ 0 ]) {
throw new Exception ( 'Expected an object attribute.' );
}
$attr = $strTokens [ $strPtr ][ 1 ];
$strPtr ++ ;
$startStrPtr = $strPtr ;
try {
$sub_attr = $this -> _parseObjectAttribute ( $strTokens , $strPtr );
return array ( 'obj' => $attr , 'attr' => $sub_attr );
} catch ( Exception $err ) {
if ( $strPtr !== $startStrPtr ) {
throw $err ;
}
}
try {
$idx = $this -> _parseArrayIndexes ( $strTokens , $strPtr );
return array ( 'arr' => $attr , 'idx' => $idx );
} catch ( Exception $err ) {
if ( $strPtr !== $startStrPtr ) {
throw $err ;
}
}
return $attr ;
} //end _parseObjectAttribute()
/**
* Grammar rule to parse the use of one or more array indexes .
*
* _parseArrayIndexes ::= _parseArrayIndex () +
*
* @ exception Exception raised if $strTokens starting from $strPtr
* doesn ' t matched the rule .
*
* @ param array $strTokens Tokens to parse .
* @ param int $strPtr Pointer to the token where parsing starts .
*
* @ return array Indexes in the same order as in the string .
*/
private function _parseArrayIndexes ( $strTokens , & $strPtr )
{
$indexes = array ( $this -> _parseArrayIndex ( $strTokens , $strPtr ));
try {
while ( 1 ) {
$startStrPtr = $strPtr ;
$indexes [] = $this -> _parseArrayIndex ( $strTokens , $strPtr );
}
} catch ( Exception $err ) {
if ( 0 !== ( $strPtr - $startStrPtr )) {
throw $err ;
}
return $indexes ;
}
} //end _parseArrayIndexes()
/**
* Grammar rule to parse the use of array index .
*
* _parseArrayIndex ::= [ < index > ]
*
* @ exception Exception raised if $strTokens starting from $strPtr
* doesn ' t matched the rule .
*
* @ param array $strTokens Tokens to parse .
* @ param int $strPtr Pointer to the token where parsing starts .
*
* @ return string Index between the 2 square brackets
*/
private function _parseArrayIndex ( $strTokens , & $strPtr )
{
if ( '[' !== $strTokens [ $strPtr ]) {
throw new Exception ( 'Expected [.' );
}
$strPtr ++ ;
if ( ! in_array ( $strTokens [ $strPtr ][ 0 ], array ( T_CONSTANT_ENCAPSED_STRING , T_LNUMBER ))) {
throw new Exception ( 'Expected an array index.' );
}
$index = $strTokens [ $strPtr ][ 1 ];
$strPtr ++ ;
if ( ']' !== $strTokens [ $strPtr ]) {
throw new Exception ( 'Expected ].' );
}
$strPtr ++ ;
return $index ;
} //end _parseArrayIndex()
} //end class
/**
* CodeIgniter_Sniffs_Strings_VariableUsageSniff .
*
* Ensures that variables parsed in double - quoted strings are enclosed with
* braces to prevent greedy token parsing .
* Single - quoted strings don ' t parse variables , so there is no risk of greedy
* token parsing .
*
* @ category PHP
* @ package PHP_CodeSniffer
* @ author Thomas Ernest < thomas . ernest @ baobaz . com >
* @ copyright 2011 Thomas Ernest
* @ license http :// thomas . ernest . fr / developement / php_cs / licence GNU General Public License
* @ link http :// pear . php . net / package / PHP_CodeSniffer
*/
class DoubleQuoteUsageSniff extends VariableUsageSniff
{
/**
* Returns an array of tokens this test wants to listen for .
*
* @ return array
*/
public function register ()
{
return array (
T_DOUBLE_QUOTED_STRING ,
T_CONSTANT_ENCAPSED_STRING ,
);
} //end register()
/**
* Processes this test , when one of its tokens is encountered .
*
* @ param File $phpcsFile The current 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 )
{
// no variable are in the string from here
$tokens = $phpcsFile -> getTokens ();
$qtString = $tokens [ $stackPtr ][ 'content' ];
// makes sure that it is about a double quote string,
// since variables are not parsed out of double quoted string
$open_qt_str = substr ( $qtString , 0 , 1 );
// clean the enclosing quotes
$qtString = substr ( $qtString , 1 , strlen ( $qtString ) - 1 - 1 );
if ( 0 === strcmp ( $open_qt_str , '"' )) {
$this -> processDoubleQuotedString ( $phpcsFile , $stackPtr , $qtString );
} else if ( 0 === strcmp ( $open_qt_str , " ' " )) {
$this -> processSingleQuotedString ( $phpcsFile , $stackPtr , $qtString );
}
} //end process()
/**
* Processes this test , when the token encountered is a double - quoted string .
*
* @ param File $phpcsFile The current file being scanned .
* @ param int $stackPtr The position of the current token
* in the stack passed in $tokens .
* @ param string $qtString The double - quoted string content ,
* i . e . without quotes .
*
* @ return void
*/
protected function processDoubleQuotedString ( File $phpcsFile , $stackPtr , $qtString )
{
// so there should be at least a single quote or a special char
// if there are the 2 kinds of quote and no special char, then add a warning
$has_variable = parent :: processDoubleQuotedString ( $phpcsFile , $stackPtr , '"' . $qtString . '"' );
$has_specific_sequence = $this -> _hasSpecificSequence ( $qtString );
$dbl_qt_at = strpos ( $qtString , '"' );
$smpl_qt_at = strpos ( $qtString , " ' " );
if ( false === $has_variable && false === $has_specific_sequence
&& false === $smpl_qt_at
) {
$error = 'Single-quoted strings should be used unless it contains variables, special chars like \n or single quotes.' ;
2017-12-08 22:32:00 -05:00
$phpcsFile -> addError ( $error , $stackPtr , 111 );
2016-08-29 15:50:59 -04:00
} else if ( false !== $smpl_qt_at && false !== $dbl_qt_at
&& false === $has_variable && false === $has_specific_sequence
) {
$warning = 'It is encouraged to use a single-quoted string, since it doesn\'t contain any variable nor special char though it mixes single and double quotes.' ;
2017-12-08 22:32:00 -05:00
$phpcsFile -> addWarning ( $warning , $stackPtr , 222 );
2016-08-29 15:50:59 -04:00
}
} //end processDoubleQuotedString()
/**
* Processes this test , when the token encountered is a single - quoted string .
*
* @ param File $phpcsFile The current file being scanned .
* @ param int $stackPtr The position of the current token
* in the stack passed in $tokens .
* @ param string $qtString The single - quoted string content ,
* i . e . without quotes .
*
* @ return void
*/
protected function processSingleQuotedString ( File $phpcsFile , $stackPtr , $qtString )
{
// if there is single quotes without additional double quotes,
// then user is allowed to use double quote to avoid having to
// escape single quotes. Don't add the warning, if an error was
// already added, because a variable was found in a single-quoted
// string.
$has_variable = parent :: processSingleQuotedString ( $phpcsFile , $stackPtr , " ' " . $qtString . " ' " );
$dbl_qt_at = strpos ( $qtString , '"' );
$smpl_qt_at = strpos ( $qtString , " ' " );
if ( false === $has_variable && false !== $smpl_qt_at && false === $dbl_qt_at ) {
$warning = 'You may also use double-quoted strings if the string contains single quotes, so you do not have to use escape characters.' ;
2017-12-08 22:32:00 -05:00
$phpcsFile -> addWarning ( $warning , $stackPtr , 333 );
2016-08-29 15:50:59 -04:00
}
} //end processSingleQuotedString()
/**
* Return TRUE , if a sequence of chars that is parsed in a specific way
* in double - quoted strings is found , FALSE otherwise .
*
* @ param string $string String in which sequence of special chars will
* be researched .
*
* @ return TRUE , if a sequence of chars that is parsed in a specific way
* in double - quoted strings is found , FALSE otherwise .
*
* @ link http :// www . php . net / manual / en / language . types . string . php #language.types.string.syntax.double
*/
private function _hasSpecificSequence ( $string )
{
$hasSpecificSequence = FALSE ;
$specialMeaningStrs = array ( '\n' , '\r' , '\t' , '\v' , '\f' );
foreach ( $specialMeaningStrs as $splStr ) {
if ( FALSE !== strpos ( $string , $splStr )) {
$hasSpecificSequence = TRUE ;
}
}
$specialMeaningPtrns = array ( '\[0-7]{1,3}' , '\x[0-9A-Fa-f]{1,2}' );
foreach ( $specialMeaningPtrns as $splPtrn ) {
if ( 1 === preg_match ( " / { $splPtrn } / " , $string )) {
$hasSpecificSequence = TRUE ;
}
}
return $hasSpecificSequence ;
} //end _hasSpecificSequence()
} //end class
?>