* 0 => array(
* 'name' => '$var', // The variable name.
* 'token' => integer, // The stack pointer to the variable name.
* 'content' => string, // The full content of the variable definition.
* 'pass_by_reference' => boolean, // Is the variable passed by reference?
* 'reference_token' => integer, // The stack pointer to the reference operator
* // or FALSE if the param is not passed by reference.
* 'variable_length' => boolean, // Is the param of variable length through use of `...` ?
* 'variadic_token' => integer, // The stack pointer to the ... operator
* // or FALSE if the param is not variable length.
* 'type_hint' => string, // The type hint for the variable.
* 'type_hint_token' => integer, // The stack pointer to the start of the type hint
* // or FALSE if there is no type hint.
* 'type_hint_end_token' => integer, // The stack pointer to the end of the type hint
* // or FALSE if there is no type hint.
* 'nullable_type' => boolean, // TRUE if the var type is nullable.
* 'comma_token' => integer, // The stack pointer to the comma after the param
* // or FALSE if this is the last param.
* )
*
*
* Parameters with default values have an additional array indexes of:
* 'default' => string, // The full content of the default value.
* 'default_token' => integer, // The stack pointer to the start of the default value.
* 'default_equal_token' => integer, // The stack pointer to the equals sign.
*
* @param int $stackPtr The position in the stack of the function token
* to acquire the parameters for.
*
* @return array
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified $stackPtr is not of
* type T_FUNCTION, T_CLOSURE, T_USE,
* or T_FN.
*/
public function getMethodParameters($stackPtr)
{
if ($this->tokens[$stackPtr]['code'] !== T_FUNCTION
&& $this->tokens[$stackPtr]['code'] !== T_CLOSURE
&& $this->tokens[$stackPtr]['code'] !== T_USE
&& $this->tokens[$stackPtr]['code'] !== T_FN
) {
throw new RuntimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE or T_USE or T_FN');
}
if ($this->tokens[$stackPtr]['code'] === T_USE) {
$opener = $this->findNext(T_OPEN_PARENTHESIS, ($stackPtr + 1));
if ($opener === false || isset($this->tokens[$opener]['parenthesis_owner']) === true) {
throw new RuntimeException('$stackPtr was not a valid T_USE');
}
} else {
if (isset($this->tokens[$stackPtr]['parenthesis_opener']) === false) {
// Live coding or syntax error, so no params to find.
return [];
}
$opener = $this->tokens[$stackPtr]['parenthesis_opener'];
}
if (isset($this->tokens[$opener]['parenthesis_closer']) === false) {
// Live coding or syntax error, so no params to find.
return [];
}
$closer = $this->tokens[$opener]['parenthesis_closer'];
$vars = [];
$currVar = null;
$paramStart = ($opener + 1);
$defaultStart = null;
$equalToken = null;
$paramCount = 0;
$passByReference = false;
$referenceToken = false;
$variableLength = false;
$variadicToken = false;
$typeHint = '';
$typeHintToken = false;
$typeHintEndToken = false;
$nullableType = false;
for ($i = $paramStart; $i <= $closer; $i++) {
// Check to see if this token has a parenthesis or bracket opener. If it does
// it's likely to be an array which might have arguments in it. This
// could cause problems in our parsing below, so lets just skip to the
// end of it.
if (isset($this->tokens[$i]['parenthesis_opener']) === true) {
// Don't do this if it's the close parenthesis for the method.
if ($i !== $this->tokens[$i]['parenthesis_closer']) {
$i = ($this->tokens[$i]['parenthesis_closer'] + 1);
}
}
if (isset($this->tokens[$i]['bracket_opener']) === true) {
// Don't do this if it's the close parenthesis for the method.
if ($i !== $this->tokens[$i]['bracket_closer']) {
$i = ($this->tokens[$i]['bracket_closer'] + 1);
}
}
switch ($this->tokens[$i]['code']) {
case T_BITWISE_AND:
if ($defaultStart === null) {
$passByReference = true;
$referenceToken = $i;
}
break;
case T_VARIABLE:
$currVar = $i;
break;
case T_ELLIPSIS:
$variableLength = true;
$variadicToken = $i;
break;
case T_CALLABLE:
if ($typeHintToken === false) {
$typeHintToken = $i;
}
$typeHint .= $this->tokens[$i]['content'];
$typeHintEndToken = $i;
break;
case T_SELF:
case T_PARENT:
case T_STATIC:
// Self and parent are valid, static invalid, but was probably intended as type hint.
if (isset($defaultStart) === false) {
if ($typeHintToken === false) {
$typeHintToken = $i;
}
$typeHint .= $this->tokens[$i]['content'];
$typeHintEndToken = $i;
}
break;
case T_STRING:
// This is a string, so it may be a type hint, but it could
// also be a constant used as a default value.
$prevComma = false;
for ($t = $i; $t >= $opener; $t--) {
if ($this->tokens[$t]['code'] === T_COMMA) {
$prevComma = $t;
break;
}
}
if ($prevComma !== false) {
$nextEquals = false;
for ($t = $prevComma; $t < $i; $t++) {
if ($this->tokens[$t]['code'] === T_EQUAL) {
$nextEquals = $t;
break;
}
}
if ($nextEquals !== false) {
break;
}
}
if ($defaultStart === null) {
if ($typeHintToken === false) {
$typeHintToken = $i;
}
$typeHint .= $this->tokens[$i]['content'];
$typeHintEndToken = $i;
}
break;
case T_NS_SEPARATOR:
// Part of a type hint or default value.
if ($defaultStart === null) {
if ($typeHintToken === false) {
$typeHintToken = $i;
}
$typeHint .= $this->tokens[$i]['content'];
$typeHintEndToken = $i;
}
break;
case T_NULLABLE:
if ($defaultStart === null) {
$nullableType = true;
$typeHint .= $this->tokens[$i]['content'];
$typeHintEndToken = $i;
}
break;
case T_CLOSE_PARENTHESIS:
case T_COMMA:
// If it's null, then there must be no parameters for this
// method.
if ($currVar === null) {
continue 2;
}
$vars[$paramCount] = [];
$vars[$paramCount]['token'] = $currVar;
$vars[$paramCount]['name'] = $this->tokens[$currVar]['content'];
$vars[$paramCount]['content'] = trim($this->getTokensAsString($paramStart, ($i - $paramStart)));
if ($defaultStart !== null) {
$vars[$paramCount]['default'] = trim($this->getTokensAsString($defaultStart, ($i - $defaultStart)));
$vars[$paramCount]['default_token'] = $defaultStart;
$vars[$paramCount]['default_equal_token'] = $equalToken;
}
$vars[$paramCount]['pass_by_reference'] = $passByReference;
$vars[$paramCount]['reference_token'] = $referenceToken;
$vars[$paramCount]['variable_length'] = $variableLength;
$vars[$paramCount]['variadic_token'] = $variadicToken;
$vars[$paramCount]['type_hint'] = $typeHint;
$vars[$paramCount]['type_hint_token'] = $typeHintToken;
$vars[$paramCount]['type_hint_end_token'] = $typeHintEndToken;
$vars[$paramCount]['nullable_type'] = $nullableType;
if ($this->tokens[$i]['code'] === T_COMMA) {
$vars[$paramCount]['comma_token'] = $i;
} else {
$vars[$paramCount]['comma_token'] = false;
}
// Reset the vars, as we are about to process the next parameter.
$currVar = null;
$paramStart = ($i + 1);
$defaultStart = null;
$equalToken = null;
$passByReference = false;
$referenceToken = false;
$variableLength = false;
$variadicToken = false;
$typeHint = '';
$typeHintToken = false;
$typeHintEndToken = false;
$nullableType = false;
$paramCount++;
break;
case T_EQUAL:
$defaultStart = $this->findNext(Util\Tokens::$emptyTokens, ($i + 1), null, true);
$equalToken = $i;
break;
}//end switch
}//end for
return $vars;
}//end getMethodParameters()
/**
* Returns the visibility and implementation properties of a method.
*
* The format of the return value is:
*
* array(
* 'scope' => 'public', // Public, private, or protected
* 'scope_specified' => true, // TRUE if the scope keyword was found.
* 'return_type' => '', // The return type of the method.
* 'return_type_token' => integer, // The stack pointer to the start of the return type
* // or FALSE if there is no return type.
* 'nullable_return_type' => false, // TRUE if the return type is nullable.
* 'is_abstract' => false, // TRUE if the abstract keyword was found.
* 'is_final' => false, // TRUE if the final keyword was found.
* 'is_static' => false, // TRUE if the static keyword was found.
* 'has_body' => false, // TRUE if the method has a body
* );
*
*
* @param int $stackPtr The position in the stack of the function token to
* acquire the properties for.
*
* @return array
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
* T_FUNCTION, T_CLOSURE, or T_FN token.
*/
public function getMethodProperties($stackPtr)
{
if ($this->tokens[$stackPtr]['code'] !== T_FUNCTION
&& $this->tokens[$stackPtr]['code'] !== T_CLOSURE
&& $this->tokens[$stackPtr]['code'] !== T_FN
) {
throw new RuntimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE or T_FN');
}
if ($this->tokens[$stackPtr]['code'] === T_FUNCTION) {
$valid = [
T_PUBLIC => T_PUBLIC,
T_PRIVATE => T_PRIVATE,
T_PROTECTED => T_PROTECTED,
T_STATIC => T_STATIC,
T_FINAL => T_FINAL,
T_ABSTRACT => T_ABSTRACT,
T_WHITESPACE => T_WHITESPACE,
T_COMMENT => T_COMMENT,
T_DOC_COMMENT => T_DOC_COMMENT,
];
} else {
$valid = [
T_STATIC => T_STATIC,
T_WHITESPACE => T_WHITESPACE,
T_COMMENT => T_COMMENT,
T_DOC_COMMENT => T_DOC_COMMENT,
];
}
$scope = 'public';
$scopeSpecified = false;
$isAbstract = false;
$isFinal = false;
$isStatic = false;
for ($i = ($stackPtr - 1); $i > 0; $i--) {
if (isset($valid[$this->tokens[$i]['code']]) === false) {
break;
}
switch ($this->tokens[$i]['code']) {
case T_PUBLIC:
$scope = 'public';
$scopeSpecified = true;
break;
case T_PRIVATE:
$scope = 'private';
$scopeSpecified = true;
break;
case T_PROTECTED:
$scope = 'protected';
$scopeSpecified = true;
break;
case T_ABSTRACT:
$isAbstract = true;
break;
case T_FINAL:
$isFinal = true;
break;
case T_STATIC:
$isStatic = true;
break;
}//end switch
}//end for
$returnType = '';
$returnTypeToken = false;
$nullableReturnType = false;
$hasBody = true;
if (isset($this->tokens[$stackPtr]['parenthesis_closer']) === true) {
$scopeOpener = null;
if (isset($this->tokens[$stackPtr]['scope_opener']) === true) {
$scopeOpener = $this->tokens[$stackPtr]['scope_opener'];
}
$valid = [
T_STRING => T_STRING,
T_CALLABLE => T_CALLABLE,
T_SELF => T_SELF,
T_PARENT => T_PARENT,
T_NS_SEPARATOR => T_NS_SEPARATOR,
];
for ($i = $this->tokens[$stackPtr]['parenthesis_closer']; $i < $this->numTokens; $i++) {
if (($scopeOpener === null && $this->tokens[$i]['code'] === T_SEMICOLON)
|| ($scopeOpener !== null && $i === $scopeOpener)
) {
// End of function definition.
break;
}
if ($this->tokens[$i]['code'] === T_NULLABLE) {
$nullableReturnType = true;
}
if (isset($valid[$this->tokens[$i]['code']]) === true) {
if ($returnTypeToken === false) {
$returnTypeToken = $i;
}
$returnType .= $this->tokens[$i]['content'];
}
}
if ($this->tokens[$stackPtr]['code'] === T_FN) {
$bodyToken = T_FN_ARROW;
} else {
$bodyToken = T_OPEN_CURLY_BRACKET;
}
$end = $this->findNext([$bodyToken, T_SEMICOLON], $this->tokens[$stackPtr]['parenthesis_closer']);
$hasBody = $this->tokens[$end]['code'] === $bodyToken;
}//end if
if ($returnType !== '' && $nullableReturnType === true) {
$returnType = '?'.$returnType;
}
return [
'scope' => $scope,
'scope_specified' => $scopeSpecified,
'return_type' => $returnType,
'return_type_token' => $returnTypeToken,
'nullable_return_type' => $nullableReturnType,
'is_abstract' => $isAbstract,
'is_final' => $isFinal,
'is_static' => $isStatic,
'has_body' => $hasBody,
];
}//end getMethodProperties()
/**
* Returns the visibility and implementation properties of a class member var.
*
* The format of the return value is:
*
*
* array(
* 'scope' => string, // Public, private, or protected.
* 'scope_specified' => boolean, // TRUE if the scope was explicitly specified.
* 'is_static' => boolean, // TRUE if the static keyword was found.
* 'type' => string, // The type of the var (empty if no type specified).
* 'type_token' => integer, // The stack pointer to the start of the type
* // or FALSE if there is no type.
* 'type_end_token' => integer, // The stack pointer to the end of the type
* // or FALSE if there is no type.
* 'nullable_type' => boolean, // TRUE if the type is nullable.
* );
*
*
* @param int $stackPtr The position in the stack of the T_VARIABLE token to
* acquire the properties for.
*
* @return array
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
* T_VARIABLE token, or if the position is not
* a class member variable.
*/
public function getMemberProperties($stackPtr)
{
if ($this->tokens[$stackPtr]['code'] !== T_VARIABLE) {
throw new RuntimeException('$stackPtr must be of type T_VARIABLE');
}
$conditions = array_keys($this->tokens[$stackPtr]['conditions']);
$ptr = array_pop($conditions);
if (isset($this->tokens[$ptr]) === false
|| ($this->tokens[$ptr]['code'] !== T_CLASS
&& $this->tokens[$ptr]['code'] !== T_ANON_CLASS
&& $this->tokens[$ptr]['code'] !== T_TRAIT)
) {
if (isset($this->tokens[$ptr]) === true
&& $this->tokens[$ptr]['code'] === T_INTERFACE
) {
// T_VARIABLEs in interfaces can actually be method arguments
// but they wont be seen as being inside the method because there
// are no scope openers and closers for abstract methods. If it is in
// parentheses, we can be pretty sure it is a method argument.
if (isset($this->tokens[$stackPtr]['nested_parenthesis']) === false
|| empty($this->tokens[$stackPtr]['nested_parenthesis']) === true
) {
$error = 'Possible parse error: interfaces may not include member vars';
$this->addWarning($error, $stackPtr, 'Internal.ParseError.InterfaceHasMemberVar');
return [];
}
} else {
throw new RuntimeException('$stackPtr is not a class member var');
}
}
// Make sure it's not a method parameter.
if (empty($this->tokens[$stackPtr]['nested_parenthesis']) === false) {
$parenthesis = array_keys($this->tokens[$stackPtr]['nested_parenthesis']);
$deepestOpen = array_pop($parenthesis);
if ($deepestOpen > $ptr
&& isset($this->tokens[$deepestOpen]['parenthesis_owner']) === true
&& $this->tokens[$this->tokens[$deepestOpen]['parenthesis_owner']]['code'] === T_FUNCTION
) {
throw new RuntimeException('$stackPtr is not a class member var');
}
}
$valid = [
T_PUBLIC => T_PUBLIC,
T_PRIVATE => T_PRIVATE,
T_PROTECTED => T_PROTECTED,
T_STATIC => T_STATIC,
T_VAR => T_VAR,
];
$valid += Util\Tokens::$emptyTokens;
$scope = 'public';
$scopeSpecified = false;
$isStatic = false;
$startOfStatement = $this->findPrevious(
[
T_SEMICOLON,
T_OPEN_CURLY_BRACKET,
T_CLOSE_CURLY_BRACKET,
],
($stackPtr - 1)
);
for ($i = ($startOfStatement + 1); $i < $stackPtr; $i++) {
if (isset($valid[$this->tokens[$i]['code']]) === false) {
break;
}
switch ($this->tokens[$i]['code']) {
case T_PUBLIC:
$scope = 'public';
$scopeSpecified = true;
break;
case T_PRIVATE:
$scope = 'private';
$scopeSpecified = true;
break;
case T_PROTECTED:
$scope = 'protected';
$scopeSpecified = true;
break;
case T_STATIC:
$isStatic = true;
break;
}
}//end for
$type = '';
$typeToken = false;
$typeEndToken = false;
$nullableType = false;
if ($i < $stackPtr) {
// We've found a type.
$valid = [
T_STRING => T_STRING,
T_CALLABLE => T_CALLABLE,
T_SELF => T_SELF,
T_PARENT => T_PARENT,
T_NS_SEPARATOR => T_NS_SEPARATOR,
];
for ($i; $i < $stackPtr; $i++) {
if ($this->tokens[$i]['code'] === T_VARIABLE) {
// Hit another variable in a group definition.
break;
}
if ($this->tokens[$i]['code'] === T_NULLABLE) {
$nullableType = true;
}
if (isset($valid[$this->tokens[$i]['code']]) === true) {
$typeEndToken = $i;
if ($typeToken === false) {
$typeToken = $i;
}
$type .= $this->tokens[$i]['content'];
}
}
if ($type !== '' && $nullableType === true) {
$type = '?'.$type;
}
}//end if
return [
'scope' => $scope,
'scope_specified' => $scopeSpecified,
'is_static' => $isStatic,
'type' => $type,
'type_token' => $typeToken,
'type_end_token' => $typeEndToken,
'nullable_type' => $nullableType,
];
}//end getMemberProperties()
/**
* Returns the visibility and implementation properties of a class.
*
* The format of the return value is:
*
* array(
* 'is_abstract' => false, // true if the abstract keyword was found.
* 'is_final' => false, // true if the final keyword was found.
* );
*
*
* @param int $stackPtr The position in the stack of the T_CLASS token to
* acquire the properties for.
*
* @return array
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a
* T_CLASS token.
*/
public function getClassProperties($stackPtr)
{
if ($this->tokens[$stackPtr]['code'] !== T_CLASS) {
throw new RuntimeException('$stackPtr must be of type T_CLASS');
}
$valid = [
T_FINAL => T_FINAL,
T_ABSTRACT => T_ABSTRACT,
T_WHITESPACE => T_WHITESPACE,
T_COMMENT => T_COMMENT,
T_DOC_COMMENT => T_DOC_COMMENT,
];
$isAbstract = false;
$isFinal = false;
for ($i = ($stackPtr - 1); $i > 0; $i--) {
if (isset($valid[$this->tokens[$i]['code']]) === false) {
break;
}
switch ($this->tokens[$i]['code']) {
case T_ABSTRACT:
$isAbstract = true;
break;
case T_FINAL:
$isFinal = true;
break;
}
}//end for
return [
'is_abstract' => $isAbstract,
'is_final' => $isFinal,
];
}//end getClassProperties()
/**
* Determine if the passed token is a reference operator.
*
* Returns true if the specified token position represents a reference.
* Returns false if the token represents a bitwise operator.
*
* @param int $stackPtr The position of the T_BITWISE_AND token.
*
* @return boolean
*/
public function isReference($stackPtr)
{
if ($this->tokens[$stackPtr]['code'] !== T_BITWISE_AND) {
return false;
}
$tokenBefore = $this->findPrevious(
Util\Tokens::$emptyTokens,
($stackPtr - 1),
null,
true
);
if ($this->tokens[$tokenBefore]['code'] === T_FUNCTION
|| $this->tokens[$tokenBefore]['code'] === T_FN
) {
// Function returns a reference.
return true;
}
if ($this->tokens[$tokenBefore]['code'] === T_DOUBLE_ARROW) {
// Inside a foreach loop or array assignment, this is a reference.
return true;
}
if ($this->tokens[$tokenBefore]['code'] === T_AS) {
// Inside a foreach loop, this is a reference.
return true;
}
if (isset(Util\Tokens::$assignmentTokens[$this->tokens[$tokenBefore]['code']]) === true) {
// This is directly after an assignment. It's a reference. Even if
// it is part of an operation, the other tests will handle it.
return true;
}
$tokenAfter = $this->findNext(
Util\Tokens::$emptyTokens,
($stackPtr + 1),
null,
true
);
if ($this->tokens[$tokenAfter]['code'] === T_NEW) {
return true;
}
if (isset($this->tokens[$stackPtr]['nested_parenthesis']) === true) {
$brackets = $this->tokens[$stackPtr]['nested_parenthesis'];
$lastBracket = array_pop($brackets);
if (isset($this->tokens[$lastBracket]['parenthesis_owner']) === true) {
$owner = $this->tokens[$this->tokens[$lastBracket]['parenthesis_owner']];
if ($owner['code'] === T_FUNCTION
|| $owner['code'] === T_CLOSURE
) {
$params = $this->getMethodParameters($this->tokens[$lastBracket]['parenthesis_owner']);
foreach ($params as $param) {
$varToken = $tokenAfter;
if ($param['variable_length'] === true) {
$varToken = $this->findNext(
(Util\Tokens::$emptyTokens + [T_ELLIPSIS]),
($stackPtr + 1),
null,
true
);
}
if ($param['token'] === $varToken
&& $param['pass_by_reference'] === true
) {
// Function parameter declared to be passed by reference.
return true;
}
}
}//end if
} else {
$prev = false;
for ($t = ($this->tokens[$lastBracket]['parenthesis_opener'] - 1); $t >= 0; $t--) {
if ($this->tokens[$t]['code'] !== T_WHITESPACE) {
$prev = $t;
break;
}
}
if ($prev !== false && $this->tokens[$prev]['code'] === T_USE) {
// Closure use by reference.
return true;
}
}//end if
}//end if
// Pass by reference in function calls and assign by reference in arrays.
if ($this->tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS
|| $this->tokens[$tokenBefore]['code'] === T_COMMA
|| $this->tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY
) {
if ($this->tokens[$tokenAfter]['code'] === T_VARIABLE) {
return true;
} else {
$skip = Util\Tokens::$emptyTokens;
$skip[] = T_NS_SEPARATOR;
$skip[] = T_SELF;
$skip[] = T_PARENT;
$skip[] = T_STATIC;
$skip[] = T_STRING;
$skip[] = T_NAMESPACE;
$skip[] = T_DOUBLE_COLON;
$nextSignificantAfter = $this->findNext(
$skip,
($stackPtr + 1),
null,
true
);
if ($this->tokens[$nextSignificantAfter]['code'] === T_VARIABLE) {
return true;
}
}//end if
}//end if
return false;
}//end isReference()
/**
* Returns the content of the tokens from the specified start position in
* the token stack for the specified length.
*
* @param int $start The position to start from in the token stack.
* @param int $length The length of tokens to traverse from the start pos.
* @param bool $origContent Whether the original content or the tab replaced
* content should be used.
*
* @return string The token contents.
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position does not exist.
*/
public function getTokensAsString($start, $length, $origContent=false)
{
if (is_int($start) === false || isset($this->tokens[$start]) === false) {
throw new RuntimeException('The $start position for getTokensAsString() must exist in the token stack');
}
if (is_int($length) === false || $length <= 0) {
return '';
}
$str = '';
$end = ($start + $length);
if ($end > $this->numTokens) {
$end = $this->numTokens;
}
for ($i = $start; $i < $end; $i++) {
// If tabs are being converted to spaces by the tokeniser, the
// original content should be used instead of the converted content.
if ($origContent === true && isset($this->tokens[$i]['orig_content']) === true) {
$str .= $this->tokens[$i]['orig_content'];
} else {
$str .= $this->tokens[$i]['content'];
}
}
return $str;
}//end getTokensAsString()
/**
* Returns the position of the previous specified token(s).
*
* If a value is specified, the previous token of the specified type(s)
* containing the specified value will be returned.
*
* Returns false if no token can be found.
*
* @param int|string|array $types The type(s) of tokens to search for.
* @param int $start The position to start searching from in the
* token stack.
* @param int $end The end position to fail if no token is found.
* if not specified or null, end will default to
* the start of the token stack.
* @param bool $exclude If true, find the previous token that is NOT of
* the types specified in $types.
* @param string $value The value that the token(s) must be equal to.
* If value is omitted, tokens with any value will
* be returned.
* @param bool $local If true, tokens outside the current statement
* will not be checked. IE. checking will stop
* at the previous semi-colon found.
*
* @return int|false
* @see findNext()
*/
public function findPrevious(
$types,
$start,
$end=null,
$exclude=false,
$value=null,
$local=false
) {
$types = (array) $types;
if ($end === null) {
$end = 0;
}
for ($i = $start; $i >= $end; $i--) {
$found = (bool) $exclude;
foreach ($types as $type) {
if ($this->tokens[$i]['code'] === $type) {
$found = !$exclude;
break;
}
}
if ($found === true) {
if ($value === null) {
return $i;
} else if ($this->tokens[$i]['content'] === $value) {
return $i;
}
}
if ($local === true) {
if (isset($this->tokens[$i]['scope_opener']) === true
&& $i === $this->tokens[$i]['scope_closer']
) {
$i = $this->tokens[$i]['scope_opener'];
} else if (isset($this->tokens[$i]['bracket_opener']) === true
&& $i === $this->tokens[$i]['bracket_closer']
) {
$i = $this->tokens[$i]['bracket_opener'];
} else if (isset($this->tokens[$i]['parenthesis_opener']) === true
&& $i === $this->tokens[$i]['parenthesis_closer']
) {
$i = $this->tokens[$i]['parenthesis_opener'];
} else if ($this->tokens[$i]['code'] === T_SEMICOLON) {
break;
}
}
}//end for
return false;
}//end findPrevious()
/**
* Returns the position of the next specified token(s).
*
* If a value is specified, the next token of the specified type(s)
* containing the specified value will be returned.
*
* Returns false if no token can be found.
*
* @param int|string|array $types The type(s) of tokens to search for.
* @param int $start The position to start searching from in the
* token stack.
* @param int $end The end position to fail if no token is found.
* if not specified or null, end will default to
* the end of the token stack.
* @param bool $exclude If true, find the next token that is NOT of
* a type specified in $types.
* @param string $value The value that the token(s) must be equal to.
* If value is omitted, tokens with any value will
* be returned.
* @param bool $local If true, tokens outside the current statement
* will not be checked. i.e., checking will stop
* at the next semi-colon found.
*
* @return int|false
* @see findPrevious()
*/
public function findNext(
$types,
$start,
$end=null,
$exclude=false,
$value=null,
$local=false
) {
$types = (array) $types;
if ($end === null || $end > $this->numTokens) {
$end = $this->numTokens;
}
for ($i = $start; $i < $end; $i++) {
$found = (bool) $exclude;
foreach ($types as $type) {
if ($this->tokens[$i]['code'] === $type) {
$found = !$exclude;
break;
}
}
if ($found === true) {
if ($value === null) {
return $i;
} else if ($this->tokens[$i]['content'] === $value) {
return $i;
}
}
if ($local === true && $this->tokens[$i]['code'] === T_SEMICOLON) {
break;
}
}//end for
return false;
}//end findNext()
/**
* Returns the position of the first non-whitespace token in a statement.
*
* @param int $start The position to start searching from in the token stack.
* @param int|string|array $ignore Token types that should not be considered stop points.
*
* @return int
*/
public function findStartOfStatement($start, $ignore=null)
{
$endTokens = Util\Tokens::$blockOpeners;
$endTokens[T_COLON] = true;
$endTokens[T_COMMA] = true;
$endTokens[T_DOUBLE_ARROW] = true;
$endTokens[T_SEMICOLON] = true;
$endTokens[T_OPEN_TAG] = true;
$endTokens[T_CLOSE_TAG] = true;
$endTokens[T_OPEN_SHORT_ARRAY] = true;
if ($ignore !== null) {
$ignore = (array) $ignore;
foreach ($ignore as $code) {
unset($endTokens[$code]);
}
}
$lastNotEmpty = $start;
for ($i = $start; $i >= 0; $i--) {
if (isset($endTokens[$this->tokens[$i]['code']]) === true) {
// Found the end of the previous statement.
return $lastNotEmpty;
}
if (isset($this->tokens[$i]['scope_opener']) === true
&& $i === $this->tokens[$i]['scope_closer']
) {
// Found the end of the previous scope block.
return $lastNotEmpty;
}
// Skip nested statements.
if (isset($this->tokens[$i]['bracket_opener']) === true
&& $i === $this->tokens[$i]['bracket_closer']
) {
$i = $this->tokens[$i]['bracket_opener'];
} else if (isset($this->tokens[$i]['parenthesis_opener']) === true
&& $i === $this->tokens[$i]['parenthesis_closer']
) {
$i = $this->tokens[$i]['parenthesis_opener'];
}
if (isset(Util\Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
$lastNotEmpty = $i;
}
}//end for
return 0;
}//end findStartOfStatement()
/**
* Returns the position of the last non-whitespace token in a statement.
*
* @param int $start The position to start searching from in the token stack.
* @param int|string|array $ignore Token types that should not be considered stop points.
*
* @return int
*/
public function findEndOfStatement($start, $ignore=null)
{
$endTokens = [
T_COLON => true,
T_COMMA => true,
T_DOUBLE_ARROW => true,
T_SEMICOLON => true,
T_CLOSE_PARENTHESIS => true,
T_CLOSE_SQUARE_BRACKET => true,
T_CLOSE_CURLY_BRACKET => true,
T_CLOSE_SHORT_ARRAY => true,
T_OPEN_TAG => true,
T_CLOSE_TAG => true,
];
if ($ignore !== null) {
$ignore = (array) $ignore;
foreach ($ignore as $code) {
unset($endTokens[$code]);
}
}
$lastNotEmpty = $start;
for ($i = $start; $i < $this->numTokens; $i++) {
if ($i !== $start && isset($endTokens[$this->tokens[$i]['code']]) === true) {
// Found the end of the statement.
if ($this->tokens[$i]['code'] === T_CLOSE_PARENTHESIS
|| $this->tokens[$i]['code'] === T_CLOSE_SQUARE_BRACKET
|| $this->tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET
|| $this->tokens[$i]['code'] === T_CLOSE_SHORT_ARRAY
|| $this->tokens[$i]['code'] === T_OPEN_TAG
|| $this->tokens[$i]['code'] === T_CLOSE_TAG
) {
return $lastNotEmpty;
}
return $i;
}
// Skip nested statements.
if (isset($this->tokens[$i]['scope_closer']) === true
&& ($i === $this->tokens[$i]['scope_opener']
|| $i === $this->tokens[$i]['scope_condition'])
) {
if ($this->tokens[$i]['code'] === T_FN) {
$i = ($this->tokens[$i]['scope_closer'] - 1);
continue;
}
if ($i === $start && isset(Util\Tokens::$scopeOpeners[$this->tokens[$i]['code']]) === true) {
return $this->tokens[$i]['scope_closer'];
}
$i = $this->tokens[$i]['scope_closer'];
} else if (isset($this->tokens[$i]['bracket_closer']) === true
&& $i === $this->tokens[$i]['bracket_opener']
) {
$i = $this->tokens[$i]['bracket_closer'];
} else if (isset($this->tokens[$i]['parenthesis_closer']) === true
&& $i === $this->tokens[$i]['parenthesis_opener']
) {
$i = $this->tokens[$i]['parenthesis_closer'];
} else if ($this->tokens[$i]['code'] === T_OPEN_USE_GROUP) {
$end = $this->findNext(T_CLOSE_USE_GROUP, ($i + 1));
if ($end !== false) {
$i = $end;
}
}//end if
if (isset(Util\Tokens::$emptyTokens[$this->tokens[$i]['code']]) === false) {
$lastNotEmpty = $i;
}
}//end for
return ($this->numTokens - 1);
}//end findEndOfStatement()
/**
* Returns the position of the first token on a line, matching given type.
*
* Returns false if no token can be found.
*
* @param int|string|array $types The type(s) of tokens to search for.
* @param int $start The position to start searching from in the
* token stack. The first token matching on
* this line before this token will be returned.
* @param bool $exclude If true, find the token that is NOT of
* the types specified in $types.
* @param string $value The value that the token must be equal to.
* If value is omitted, tokens with any value will
* be returned.
*
* @return int|false
*/
public function findFirstOnLine($types, $start, $exclude=false, $value=null)
{
if (is_array($types) === false) {
$types = [$types];
}
$foundToken = false;
for ($i = $start; $i >= 0; $i--) {
if ($this->tokens[$i]['line'] < $this->tokens[$start]['line']) {
break;
}
$found = $exclude;
foreach ($types as $type) {
if ($exclude === false) {
if ($this->tokens[$i]['code'] === $type) {
$found = true;
break;
}
} else {
if ($this->tokens[$i]['code'] === $type) {
$found = false;
break;
}
}
}
if ($found === true) {
if ($value === null) {
$foundToken = $i;
} else if ($this->tokens[$i]['content'] === $value) {
$foundToken = $i;
}
}
}//end for
return $foundToken;
}//end findFirstOnLine()
/**
* Determine if the passed token has a condition of one of the passed types.
*
* @param int $stackPtr The position of the token we are checking.
* @param int|string|array $types The type(s) of tokens to search for.
*
* @return boolean
*/
public function hasCondition($stackPtr, $types)
{
// Check for the existence of the token.
if (isset($this->tokens[$stackPtr]) === false) {
return false;
}
// Make sure the token has conditions.
if (isset($this->tokens[$stackPtr]['conditions']) === false) {
return false;
}
$types = (array) $types;
$conditions = $this->tokens[$stackPtr]['conditions'];
foreach ($types as $type) {
if (in_array($type, $conditions, true) === true) {
// We found a token with the required type.
return true;
}
}
return false;
}//end hasCondition()
/**
* Return the position of the condition for the passed token.
*
* Returns FALSE if the token does not have the condition.
*
* @param int $stackPtr The position of the token we are checking.
* @param int|string $type The type of token to search for.
* @param bool $first If TRUE, will return the matched condition
* furtherest away from the passed token.
* If FALSE, will return the matched condition
* closest to the passed token.
*
* @return int|false
*/
public function getCondition($stackPtr, $type, $first=true)
{
// Check for the existence of the token.
if (isset($this->tokens[$stackPtr]) === false) {
return false;
}
// Make sure the token has conditions.
if (isset($this->tokens[$stackPtr]['conditions']) === false) {
return false;
}
$conditions = $this->tokens[$stackPtr]['conditions'];
if ($first === false) {
$conditions = array_reverse($conditions, true);
}
foreach ($conditions as $token => $condition) {
if ($condition === $type) {
return $token;
}
}
return false;
}//end getCondition()
/**
* Returns the name of the class that the specified class extends.
* (works for classes, anonymous classes and interfaces)
*
* Returns FALSE on error or if there is no extended class name.
*
* @param int $stackPtr The stack position of the class.
*
* @return string|false
*/
public function findExtendedClassName($stackPtr)
{
// Check for the existence of the token.
if (isset($this->tokens[$stackPtr]) === false) {
return false;
}
if ($this->tokens[$stackPtr]['code'] !== T_CLASS
&& $this->tokens[$stackPtr]['code'] !== T_ANON_CLASS
&& $this->tokens[$stackPtr]['code'] !== T_INTERFACE
) {
return false;
}
if (isset($this->tokens[$stackPtr]['scope_opener']) === false) {
return false;
}
$classOpenerIndex = $this->tokens[$stackPtr]['scope_opener'];
$extendsIndex = $this->findNext(T_EXTENDS, $stackPtr, $classOpenerIndex);
if ($extendsIndex === false) {
return false;
}
$find = [
T_NS_SEPARATOR,
T_STRING,
T_WHITESPACE,
];
$end = $this->findNext($find, ($extendsIndex + 1), ($classOpenerIndex + 1), true);
$name = $this->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1));
$name = trim($name);
if ($name === '') {
return false;
}
return $name;
}//end findExtendedClassName()
/**
* Returns the names of the interfaces that the specified class implements.
*
* Returns FALSE on error or if there are no implemented interface names.
*
* @param int $stackPtr The stack position of the class.
*
* @return array|false
*/
public function findImplementedInterfaceNames($stackPtr)
{
// Check for the existence of the token.
if (isset($this->tokens[$stackPtr]) === false) {
return false;
}
if ($this->tokens[$stackPtr]['code'] !== T_CLASS
&& $this->tokens[$stackPtr]['code'] !== T_ANON_CLASS
) {
return false;
}
if (isset($this->tokens[$stackPtr]['scope_closer']) === false) {
return false;
}
$classOpenerIndex = $this->tokens[$stackPtr]['scope_opener'];
$implementsIndex = $this->findNext(T_IMPLEMENTS, $stackPtr, $classOpenerIndex);
if ($implementsIndex === false) {
return false;
}
$find = [
T_NS_SEPARATOR,
T_STRING,
T_WHITESPACE,
T_COMMA,
];
$end = $this->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
$name = $this->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));
$name = trim($name);
if ($name === '') {
return false;
} else {
$names = explode(',', $name);
$names = array_map('trim', $names);
return $names;
}
}//end findImplementedInterfaceNames()
}//end class
PHP_CodeSniffer-3.5.4/src/Files/FileList.php 0000644 0000765 0000024 00000013741 13614652361 020377 0 ustar gsherwood staff
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Files;
use PHP_CodeSniffer\Autoload;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Exceptions\DeepExitException;
class FileList implements \Iterator, \Countable
{
/**
* A list of file paths that are included in the list.
*
* @var array
*/
private $files = [];
/**
* The number of files in the list.
*
* @var integer
*/
private $numFiles = 0;
/**
* The config data for the run.
*
* @var \PHP_CodeSniffer\Config
*/
public $config = null;
/**
* The ruleset used for the run.
*
* @var \PHP_CodeSniffer\Ruleset
*/
public $ruleset = null;
/**
* An array of patterns to use for skipping files.
*
* @var array
*/
protected $ignorePatterns = [];
/**
* Constructs a file list and loads in an array of file paths to process.
*
* @param \PHP_CodeSniffer\Config $config The config data for the run.
* @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
*
* @return void
*/
public function __construct(Config $config, Ruleset $ruleset)
{
$this->ruleset = $ruleset;
$this->config = $config;
$paths = $config->files;
foreach ($paths as $path) {
$isPharFile = Util\Common::isPharFile($path);
if (is_dir($path) === true || $isPharFile === true) {
if ($isPharFile === true) {
$path = 'phar://'.$path;
}
$filterClass = $this->getFilterClass();
$di = new \RecursiveDirectoryIterator($path, (\RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS));
$filter = new $filterClass($di, $path, $config, $ruleset);
$iterator = new \RecursiveIteratorIterator($filter);
foreach ($iterator as $file) {
$this->files[$file->getPathname()] = null;
$this->numFiles++;
}
} else {
$this->addFile($path);
}//end if
}//end foreach
reset($this->files);
}//end __construct()
/**
* Add a file to the list.
*
* If a file object has already been created, it can be passed here.
* If it is left NULL, it will be created when accessed.
*
* @param string $path The path to the file being added.
* @param \PHP_CodeSniffer\Files\File $file The file being added.
*
* @return void
*/
public function addFile($path, $file=null)
{
// No filtering is done for STDIN when the filename
// has not been specified.
if ($path === 'STDIN') {
$this->files[$path] = $file;
$this->numFiles++;
return;
}
$filterClass = $this->getFilterClass();
$di = new \RecursiveArrayIterator([$path]);
$filter = new $filterClass($di, $path, $this->config, $this->ruleset);
$iterator = new \RecursiveIteratorIterator($filter);
foreach ($iterator as $path) {
$this->files[$path] = $file;
$this->numFiles++;
}
}//end addFile()
/**
* Get the class name of the filter being used for the run.
*
* @return string
* @throws \PHP_CodeSniffer\Exceptions\DeepExitException If the specified filter could not be found.
*/
private function getFilterClass()
{
$filterType = $this->config->filter;
if ($filterType === null) {
$filterClass = '\PHP_CodeSniffer\Filters\Filter';
} else {
if (strpos($filterType, '.') !== false) {
// This is a path to a custom filter class.
$filename = realpath($filterType);
if ($filename === false) {
$error = "ERROR: Custom filter \"$filterType\" not found".PHP_EOL;
throw new DeepExitException($error, 3);
}
$filterClass = Autoload::loadFile($filename);
} else {
$filterClass = '\PHP_CodeSniffer\Filters\\'.$filterType;
}
}
return $filterClass;
}//end getFilterClass()
/**
* Rewind the iterator to the first file.
*
* @return void
*/
public function rewind()
{
reset($this->files);
}//end rewind()
/**
* Get the file that is currently being processed.
*
* @return \PHP_CodeSniffer\Files\File
*/
public function current()
{
$path = key($this->files);
if ($this->files[$path] === null) {
$this->files[$path] = new LocalFile($path, $this->ruleset, $this->config);
}
return $this->files[$path];
}//end current()
/**
* Return the file path of the current file being processed.
*
* @return void
*/
public function key()
{
return key($this->files);
}//end key()
/**
* Move forward to the next file.
*
* @return void
*/
public function next()
{
next($this->files);
}//end next()
/**
* Checks if current position is valid.
*
* @return boolean
*/
public function valid()
{
if (current($this->files) === false) {
return false;
}
return true;
}//end valid()
/**
* Return the number of files in the list.
*
* @return integer
*/
public function count()
{
return $this->numFiles;
}//end count()
}//end class
PHP_CodeSniffer-3.5.4/src/Files/LocalFile.php 0000644 0000765 0000024 00000015522 13614652361 020515 0 ustar gsherwood staff
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Files;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Util\Cache;
class LocalFile extends File
{
/**
* Creates a LocalFile object and sets the content.
*
* @param string $path The absolute path to the file.
* @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
* @param \PHP_CodeSniffer\Config $config The config data for the run.
*
* @return void
*/
public function __construct($path, Ruleset $ruleset, Config $config)
{
$this->path = trim($path);
if (is_readable($this->path) === false) {
parent::__construct($this->path, $ruleset, $config);
$error = 'Error opening file; file no longer exists or you do not have access to read the file';
$this->addMessage(true, $error, 1, 1, 'Internal.LocalFile', [], 5, false);
$this->ignored = true;
return;
}
// Before we go and spend time tokenizing this file, just check
// to see if there is a tag up top to indicate that the whole
// file should be ignored. It must be on one of the first two lines.
if ($config->annotations === true) {
$handle = fopen($this->path, 'r');
if ($handle !== false) {
$firstContent = fgets($handle);
$firstContent .= fgets($handle);
fclose($handle);
if (strpos($firstContent, '@codingStandardsIgnoreFile') !== false
|| stripos($firstContent, 'phpcs:ignorefile') !== false
) {
// We are ignoring the whole file.
$this->ignored = true;
return;
}
}
}
$this->reloadContent();
parent::__construct($this->path, $ruleset, $config);
}//end __construct()
/**
* Loads the latest version of the file's content from the file system.
*
* @return void
*/
public function reloadContent()
{
$this->setContent(file_get_contents($this->path));
}//end reloadContent()
/**
* Processes the file.
*
* @return void
*/
public function process()
{
if ($this->ignored === true) {
return;
}
if ($this->configCache['cache'] === false) {
parent::process();
return;
}
$hash = md5_file($this->path);
$hash .= fileperms($this->path);
$cache = Cache::get($this->path);
if ($cache !== false && $cache['hash'] === $hash) {
// We can't filter metrics, so just load all of them.
$this->metrics = $cache['metrics'];
if ($this->configCache['recordErrors'] === true) {
// Replay the cached errors and warnings to filter out the ones
// we don't need for this specific run.
$this->configCache['cache'] = false;
$this->replayErrors($cache['errors'], $cache['warnings']);
$this->configCache['cache'] = true;
} else {
$this->errorCount = $cache['errorCount'];
$this->warningCount = $cache['warningCount'];
$this->fixableCount = $cache['fixableCount'];
}
if (PHP_CODESNIFFER_VERBOSITY > 0
|| (PHP_CODESNIFFER_CBF === true && empty($this->config->files) === false)
) {
echo "[loaded from cache]... ";
}
$this->numTokens = $cache['numTokens'];
$this->fromCache = true;
return;
}//end if
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo PHP_EOL;
}
parent::process();
$cache = [
'hash' => $hash,
'errors' => $this->errors,
'warnings' => $this->warnings,
'metrics' => $this->metrics,
'errorCount' => $this->errorCount,
'warningCount' => $this->warningCount,
'fixableCount' => $this->fixableCount,
'numTokens' => $this->numTokens,
];
Cache::set($this->path, $cache);
// During caching, we don't filter out errors in any way, so
// we need to do that manually now by replaying them.
if ($this->configCache['recordErrors'] === true) {
$this->configCache['cache'] = false;
$this->replayErrors($this->errors, $this->warnings);
$this->configCache['cache'] = true;
}
}//end process()
/**
* Clears and replays error and warnings for the file.
*
* Replaying errors and warnings allows for filtering rules to be changed
* and then errors and warnings to be reapplied with the new rules. This is
* particularly useful while caching.
*
* @param array $errors The list of errors to replay.
* @param array $warnings The list of warnings to replay.
*
* @return void
*/
private function replayErrors($errors, $warnings)
{
$this->errors = [];
$this->warnings = [];
$this->errorCount = 0;
$this->warningCount = 0;
$this->fixableCount = 0;
$this->replayingErrors = true;
foreach ($errors as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$this->activeListener = $error['listener'];
$this->addMessage(
true,
$error['message'],
$line,
$column,
$error['source'],
[],
$error['severity'],
$error['fixable']
);
}
}
}
foreach ($warnings as $line => $lineErrors) {
foreach ($lineErrors as $column => $colErrors) {
foreach ($colErrors as $error) {
$this->activeListener = $error['listener'];
$this->addMessage(
false,
$error['message'],
$line,
$column,
$error['source'],
[],
$error['severity'],
$error['fixable']
);
}
}
}
$this->replayingErrors = false;
}//end replayErrors()
}//end class
PHP_CodeSniffer-3.5.4/src/Filters/ExactMatch.php 0000644 0000765 0000024 00000005042 13614652361 021246 0 ustar gsherwood staff
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
abstract class ExactMatch extends Filter
{
/**
* A list of files to exclude.
*
* @var array
*/
private $blacklist = null;
/**
* A list of files to include.
*
* If the whitelist is empty, only files in the blacklist will be excluded.
*
* @var array
*/
private $whitelist = null;
/**
* Check whether the current element of the iterator is acceptable.
*
* If a file is both blacklisted and whitelisted, it will be deemed unacceptable.
*
* @return bool
*/
public function accept()
{
if (parent::accept() === false) {
return false;
}
if ($this->blacklist === null) {
$this->blacklist = $this->getblacklist();
}
if ($this->whitelist === null) {
$this->whitelist = $this->getwhitelist();
}
$filePath = Util\Common::realpath($this->current());
// If file is both blacklisted and whitelisted, the blacklist takes precedence.
if (isset($this->blacklist[$filePath]) === true) {
return false;
}
if (empty($this->whitelist) === true && empty($this->blacklist) === false) {
// We are only checking a blacklist, so everything else should be whitelisted.
return true;
}
return isset($this->whitelist[$filePath]);
}//end accept()
/**
* Returns an iterator for the current entry.
*
* Ensures that the blacklist and whitelist are preserved so they don't have
* to be generated each time.
*
* @return \RecursiveIterator
*/
public function getChildren()
{
$children = parent::getChildren();
$children->blacklist = $this->blacklist;
$children->whitelist = $this->whitelist;
return $children;
}//end getChildren()
/**
* Get a list of blacklisted file paths.
*
* @return array
*/
abstract protected function getBlacklist();
/**
* Get a list of whitelisted file paths.
*
* @return array
*/
abstract protected function getWhitelist();
}//end class
PHP_CodeSniffer-3.5.4/src/Filters/Filter.php 0000644 0000765 0000024 00000020416 13614652361 020454 0 ustar gsherwood staff
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Config;
class Filter extends \RecursiveFilterIterator
{
/**
* The top-level path we are filtering.
*
* @var string
*/
protected $basedir = null;
/**
* The config data for the run.
*
* @var \PHP_CodeSniffer\Config
*/
protected $config = null;
/**
* The ruleset used for the run.
*
* @var \PHP_CodeSniffer\Ruleset
*/
protected $ruleset = null;
/**
* A list of ignore patterns that apply to directories only.
*
* @var array
*/
protected $ignoreDirPatterns = null;
/**
* A list of ignore patterns that apply to files only.
*
* @var array
*/
protected $ignoreFilePatterns = null;
/**
* A list of file paths we've already accepted.
*
* Used to ensure we aren't following circular symlinks.
*
* @var array
*/
protected $acceptedPaths = [];
/**
* Constructs a filter.
*
* @param \RecursiveIterator $iterator The iterator we are using to get file paths.
* @param string $basedir The top-level path we are filtering.
* @param \PHP_CodeSniffer\Config $config The config data for the run.
* @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
*
* @return void
*/
public function __construct($iterator, $basedir, Config $config, Ruleset $ruleset)
{
parent::__construct($iterator);
$this->basedir = $basedir;
$this->config = $config;
$this->ruleset = $ruleset;
}//end __construct()
/**
* Check whether the current element of the iterator is acceptable.
*
* Files are checked for allowed extensions and ignore patterns.
* Directories are checked for ignore patterns only.
*
* @return bool
*/
public function accept()
{
$filePath = $this->current();
$realPath = Util\Common::realpath($filePath);
if ($realPath !== false) {
// It's a real path somewhere, so record it
// to check for circular symlinks.
if (isset($this->acceptedPaths[$realPath]) === true) {
// We've been here before.
return false;
}
}
$filePath = $this->current();
if (is_dir($filePath) === true) {
if ($this->config->local === true) {
return false;
}
} else if ($this->shouldProcessFile($filePath) === false) {
return false;
}
if ($this->shouldIgnorePath($filePath) === true) {
return false;
}
$this->acceptedPaths[$realPath] = true;
return true;
}//end accept()
/**
* Returns an iterator for the current entry.
*
* Ensures that the ignore patterns are preserved so they don't have
* to be generated each time.
*
* @return \RecursiveIterator
*/
public function getChildren()
{
$filterClass = get_called_class();
$children = new $filterClass(
new \RecursiveDirectoryIterator($this->current(), (\RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS)),
$this->basedir,
$this->config,
$this->ruleset
);
// Set the ignore patterns so we don't have to generate them again.
$children->ignoreDirPatterns = $this->ignoreDirPatterns;
$children->ignoreFilePatterns = $this->ignoreFilePatterns;
$children->acceptedPaths = $this->acceptedPaths;
return $children;
}//end getChildren()
/**
* Checks filtering rules to see if a file should be checked.
*
* Checks both file extension filters and path ignore filters.
*
* @param string $path The path to the file being checked.
*
* @return bool
*/
protected function shouldProcessFile($path)
{
// Check that the file's extension is one we are checking.
// We are strict about checking the extension and we don't
// let files through with no extension or that start with a dot.
$fileName = basename($path);
$fileParts = explode('.', $fileName);
if ($fileParts[0] === $fileName || $fileParts[0] === '') {
return false;
}
// Checking multi-part file extensions, so need to create a
// complete extension list and make sure one is allowed.
$extensions = [];
array_shift($fileParts);
foreach ($fileParts as $part) {
$extensions[implode('.', $fileParts)] = 1;
array_shift($fileParts);
}
$matches = array_intersect_key($extensions, $this->config->extensions);
if (empty($matches) === true) {
return false;
}
return true;
}//end shouldProcessFile()
/**
* Checks filtering rules to see if a path should be ignored.
*
* @param string $path The path to the file or directory being checked.
*
* @return bool
*/
protected function shouldIgnorePath($path)
{
if ($this->ignoreFilePatterns === null) {
$this->ignoreDirPatterns = [];
$this->ignoreFilePatterns = [];
$ignorePatterns = $this->config->ignored;
$rulesetIgnorePatterns = $this->ruleset->getIgnorePatterns();
foreach ($rulesetIgnorePatterns as $pattern => $type) {
// Ignore standard/sniff specific exclude rules.
if (is_array($type) === true) {
continue;
}
$ignorePatterns[$pattern] = $type;
}
foreach ($ignorePatterns as $pattern => $type) {
// If the ignore pattern ends with /* then it is ignoring an entire directory.
if (substr($pattern, -2) === '/*') {
// Need to check this pattern for dirs as well as individual file paths.
$this->ignoreFilePatterns[$pattern] = $type;
$pattern = substr($pattern, 0, -2);
$this->ignoreDirPatterns[$pattern] = $type;
} else {
// This is a file-specific pattern, so only need to check this
// for individual file paths.
$this->ignoreFilePatterns[$pattern] = $type;
}
}
}//end if
$relativePath = $path;
if (strpos($path, $this->basedir) === 0) {
// The +1 cuts off the directory separator as well.
$relativePath = substr($path, (strlen($this->basedir) + 1));
}
if (is_dir($path) === true) {
$ignorePatterns = $this->ignoreDirPatterns;
} else {
$ignorePatterns = $this->ignoreFilePatterns;
}
foreach ($ignorePatterns as $pattern => $type) {
// Maintains backwards compatibility in case the ignore pattern does
// not have a relative/absolute value.
if (is_int($pattern) === true) {
$pattern = $type;
$type = 'absolute';
}
$replacements = [
'\\,' => ',',
'*' => '.*',
];
// We assume a / directory separator, as do the exclude rules
// most developers write, so we need a special case for any system
// that is different.
if (DIRECTORY_SEPARATOR === '\\') {
$replacements['/'] = '\\\\';
}
$pattern = strtr($pattern, $replacements);
if ($type === 'relative') {
$testPath = $relativePath;
} else {
$testPath = $path;
}
$pattern = '`'.$pattern.'`i';
if (preg_match($pattern, $testPath) === 1) {
return true;
}
}//end foreach
return false;
}//end shouldIgnorePath()
}//end class
PHP_CodeSniffer-3.5.4/src/Filters/GitModified.php 0000644 0000765 0000024 00000002617 13614652361 021416 0 ustar gsherwood staff
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
class GitModified extends ExactMatch
{
/**
* Get a list of blacklisted file paths.
*
* @return array
*/
protected function getBlacklist()
{
return [];
}//end getBlacklist()
/**
* Get a list of whitelisted file paths.
*
* @return array
*/
protected function getWhitelist()
{
$modified = [];
$cmd = 'git ls-files -o -m --exclude-standard -- '.escapeshellarg($this->basedir);
$output = [];
exec($cmd, $output);
$basedir = $this->basedir;
if (is_dir($basedir) === false) {
$basedir = dirname($basedir);
}
foreach ($output as $path) {
$path = Util\Common::realpath($path);
if ($path === false) {
continue;
}
do {
$modified[$path] = true;
$path = dirname($path);
} while ($path !== $basedir);
}
return $modified;
}//end getWhitelist()
}//end class
PHP_CodeSniffer-3.5.4/src/Filters/GitStaged.php 0000644 0000765 0000024 00000003013 13614652361 021074 0 ustar gsherwood staff
* @copyright 2018 Juliette Reinders Folmer. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Filters;
use PHP_CodeSniffer\Util;
class GitStaged extends ExactMatch
{
/**
* Get a list of blacklisted file paths.
*
* @return array
*/
protected function getBlacklist()
{
return [];
}//end getBlacklist()
/**
* Get a list of whitelisted file paths.
*
* @return array
*/
protected function getWhitelist()
{
$modified = [];
$cmd = 'git diff --cached --name-only -- '.escapeshellarg($this->basedir);
$output = [];
exec($cmd, $output);
$basedir = $this->basedir;
if (is_dir($basedir) === false) {
$basedir = dirname($basedir);
}
foreach ($output as $path) {
$path = Util\Common::realpath($path);
if ($path === false) {
// Skip deleted files.
continue;
}
do {
$modified[$path] = true;
$path = dirname($path);
} while ($path !== $basedir);
}
return $modified;
}//end getWhitelist()
}//end class
PHP_CodeSniffer-3.5.4/src/Generators/Generator.php 0000644 0000765 0000024 00000006122 13614652361 021654 0 ustar gsherwood staff
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Generators;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Autoload;
abstract class Generator
{
/**
* The ruleset used for the run.
*
* @var \PHP_CodeSniffer\Ruleset
*/
public $ruleset = null;
/**
* XML documentation files used to produce the final output.
*
* @var string[]
*/
public $docFiles = [];
/**
* Constructs a doc generator.
*
* @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
*
* @see generate()
*/
public function __construct(Ruleset $ruleset)
{
$this->ruleset = $ruleset;
foreach ($ruleset->sniffs as $className => $sniffClass) {
$file = Autoload::getLoadedFileName($className);
$docFile = str_replace(
DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR,
DIRECTORY_SEPARATOR.'Docs'.DIRECTORY_SEPARATOR,
$file
);
$docFile = str_replace('Sniff.php', 'Standard.xml', $docFile);
if (is_file($docFile) === true) {
$this->docFiles[] = $docFile;
}
}
}//end __construct()
/**
* Retrieves the title of the sniff from the DOMNode supplied.
*
* @param \DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return string
*/
protected function getTitle(\DOMNode $doc)
{
return $doc->getAttribute('title');
}//end getTitle()
/**
* Generates the documentation for a standard.
*
* It's probably wise for doc generators to override this method so they
* have control over how the docs are produced. Otherwise, the processSniff
* method should be overridden to output content for each sniff.
*
* @return void
* @see processSniff()
*/
public function generate()
{
foreach ($this->docFiles as $file) {
$doc = new \DOMDocument();
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$this->processSniff($documentation);
}
}//end generate()
/**
* Process the documentation for a single sniff.
*
* Doc generators must implement this function to produce output.
*
* @param \DOMNode $doc The DOMNode object for the sniff.
* It represents the "documentation" tag in the XML
* standard file.
*
* @return void
* @see generate()
*/
abstract protected function processSniff(\DOMNode $doc);
}//end class
PHP_CodeSniffer-3.5.4/src/Generators/HTML.php 0000644 0000765 0000024 00000020522 13614652361 020472 0 ustar gsherwood staff
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Generators;
use PHP_CodeSniffer\Config;
class HTML extends Generator
{
/**
* Generates the documentation for a standard.
*
* @return void
* @see processSniff()
*/
public function generate()
{
ob_start();
$this->printHeader();
$this->printToc();
foreach ($this->docFiles as $file) {
$doc = new \DOMDocument();
$doc->load($file);
$documentation = $doc->getElementsByTagName('documentation')->item(0);
$this->processSniff($documentation);
}
$this->printFooter();
$content = ob_get_contents();
ob_end_clean();
echo $content;
}//end generate()
/**
* Print the header of the HTML page.
*
* @return void
*/
protected function printHeader()
{
$standard = $this->ruleset->name;
echo ''.PHP_EOL;
echo ' '.PHP_EOL;
echo " $content
".PHP_EOL; }//end printTextBlock() /** * Print a code comparison block found in a standard. * * @param \DOMNode $node The DOMNode object for the code comparison block. * * @return void */ protected function printCodeComparisonBlock(\DOMNode $node) { $codeBlocks = $node->getElementsByTagName('code'); $firstTitle = $codeBlocks->item(0)->getAttribute('title'); $first = trim($codeBlocks->item(0)->nodeValue); $first = str_replace('', $first); $first = str_replace(' ', ' ', $first); $first = str_replace('', '', $first); $first = str_replace('', '', $first); $secondTitle = $codeBlocks->item(1)->getAttribute('title'); $second = trim($codeBlocks->item(1)->nodeValue); $second = str_replace('', $second); $second = str_replace(' ', ' ', $second); $second = str_replace('', '', $second); $second = str_replace('', '', $second); echo '$firstTitle | ".PHP_EOL; echo "$secondTitle | ".PHP_EOL; echo '
$first | ".PHP_EOL; echo "$second | ".PHP_EOL; echo '
$firstTitle | ".PHP_EOL; echo "$secondTitle | ".PHP_EOL; echo '
---|---|
'.PHP_EOL.PHP_EOL; echo " $first".PHP_EOL.PHP_EOL; echo ' | '.PHP_EOL; echo ''.PHP_EOL.PHP_EOL; echo " $second".PHP_EOL.PHP_EOL; echo ' | '.PHP_EOL; echo '
* array(
* T_WHITESPACE => 0, // 0 is the position where the T_WHITESPACE token
* // should occur in the pattern.
* );
*
*
* @param array $pattern The parsed pattern to find the acquire the token
* types from.
*
* @return array
* return array(
* T_WHITESPACE,
* T_DOC_COMMENT,
* T_COMMENT,
* );
*
*
* @return mixed[]
* @see Tokens.php
*/
public function register();
/**
* Called when one of the token types that this sniff is listening for
* is found.
*
* The stackPtr variable indicates where in the stack the token was found.
* A sniff can acquire information this token, along with all the other
* tokens within the stack by first acquiring the token stack:
*
*
* $tokens = $phpcsFile->getTokens();
* echo 'Encountered a '.$tokens[$stackPtr]['type'].' token';
* echo 'token information: ';
* print_r($tokens[$stackPtr]);
*
*
* If the sniff discovers an anomaly in the code, they can raise an error
* by calling addError() on the \PHP_CodeSniffer\Files\File object, specifying an error
* message and the position of the offending token:
*
*
* $phpcsFile->addError('Encountered an error', $stackPtr);
*
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
* token was found.
* @param int $stackPtr The position in the PHP_CodeSniffer
* file's token stack where the token
* was found.
*
* @return void|int Optionally returns a stack pointer. The sniff will not be
* called again on the current file until the returned stack
* pointer is reached. Return (count($tokens) + 1) to skip
* the rest of the file.
*/
public function process(File $phpcsFile, $stackPtr);
}//end interface
PHP_CodeSniffer-3.5.4/src/Standards/Generic/Docs/Arrays/DisallowLongArraySyntaxStandard.xml 0000644 0000765 0000024 00000001000 13614652361 031531 0 ustar gsherwood staff
[
'foo' => 'bar',
];
]]>
array(
'foo' => 'bar',
);
]]>
array(
'foo' => 'bar',
);
]]>
[
'foo' => 'bar',
];
]]>
Foo
{
}
]]>
Foo
{
}
class Foo
{
}
]]>
{
}
]]>
{
}
]]>
// Start of class.
}
]]>
%; }
]]>
%; }
]]>
];
]]>
,];
]]>
var foo = 5;
]]>
foo = 5;
]]>
Handle strange case
if ($test) {
$var = 1;
}
]]>
FIXME: This needs to be fixed!
if ($test) {
$var = 1;
}
]]>
Handle strange case
if ($test) {
$var = 1;
}
]]>
TODO: This needs to be fixed!
if ($test) {
$var = 1;
}
]]>
$i++) {
for ($j = 0; $j < 10; $j++) {
}
}
]]>
$i++) {
for ($j = 0; $j < 10; $i++) {
}
}
]]>
$a + $b + $c;
}
]]>
$a + $b;
}
]]>
{
$var = 1;
}
]]>
{
$var = 1;
}
]]>
{
$var = 1;
}
]]>
$test === 'abc') {
// Code.
}
]]>
$test = 'abc') {
// Code.
}
]]>
// do nothing
}
]]>
$i = 0; $i < 10; $i++) {
echo "{$i}\n";
}
]]>
;$test;) {
$test = doSomething();
}
]]>
$end = count($foo);
for ($i = 0; $i < $end; $i++) {
echo $foo[$i]."\n";
}
]]>
count($foo); $i++) {
echo $foo[$i]."\n";
}
]]>
$test) {
$var = 1;
}
]]>
true) {
$var = 1;
}
]]>
$test) {
$var = 1;
}
]]>
false) {
$var = 1;
}
]]>
final function bar()
{
}
}
]]>
$this->doSomethingElse();
}
}
]]>
parent::bar();
}
}
]]>
some string here
class Foo
{
}
]]>
class Foo
{
}
class Bar
{
}
]]>
interface Foo
{
}
]]>
interface Foo
{
}
interface Bar
{
}
]]>
trait Foo
{
}
]]>
trait Foo
{
}
class Bar
{
}
]]>
trait Foo
{
}
]]>
trait Foo
{
}
trait Bar
{
}
]]>
= (1 + 2);
$veryLongVarName = 'string';
$var = foo($bar, $baz);
]]>
= (1 + 2);
$veryLongVarName = 'string';
$var = foo($bar, $baz);
]]>
+= 1;
$veryLongVarName = 1;
]]>
+= 1;
$veryLongVarName = 1;
]]>
= 1;
$veryLongVarName -= 1;
]]>
= 1;
$veryLongVarName -= 1;
]]>
1;
]]>
1;
]]>
1;
]]>
$someVar || ! $x instanceOf stdClass) {};
]]>
$someVar || !$x instanceOf stdClass) {};
]]>
$someVar || !
$x instanceOf stdClass) {};
]]>
&$bar)
{
$bar++;
}
$baz = 1;
foo($baz);
]]>
&$baz);
]]>
$baz)
{
}
]]>
$baz)
{
}
]]>
= true)
{
}
]]>
=true)
{
}
]]>
{
...
}
]]>
{
...
}
]]>
{
...
}
]]>
{
...
}
]]>
doSomething()
{
}
]]>
do_something()
{
}
]]>
__construct()
{
}
}
]]>
Foo()
{
}
}
]]>
FOO_CONSTANT', 'foo');
class FooClass
{
const FOO_CONSTANT = 'foo';
}
]]>
Foo_Constant', 'foo');
class FooClass
{
const foo_constant = 'foo';
}
]]>
Beginning content
echo 'Foo';
?>
]]>
echo 'Foo';
]]>
explode('a', $bar);
]]>
split('a', $bar);
]]>
count($bar);
]]>
sizeof($bar);
]]>
false || $var === null) {
$var = true;
}
]]>
FALSE || $var === NULL) {
$var = TRUE;
}
]]>
array();
]]>
Array();
]]>
Int $foo) : STRING {
}
]]>
(BOOL) $isValid;
]]>
isset($foo) && $foo) {
echo "Hello\n";
}
]]>
@$foo) {
echo "Hello\n";
}
]]>
PHP_SAPI === 'cli') {
echo "Hello, CLI user.";
}
]]>
php_sapi_name() === 'cli') {
echo "Hello, CLI user.";
}
]]>
FALSE || $var === NULL) {
$var = TRUE;
}
]]>
false || $var === null) {
$var = true;
}
]]>
$var = 1;
}
]]>
$var = 1;
}
]]>
&...$spread) {
bar(...$spread);
bar(
[...$foo],
...array_values($keyedArray)
);
}
]]>
... $spread) {
bar(...
$spread
);
bar(
[... $foo ],.../*comment*/array_values($keyedArray)
);
}
]]>
* class Foo
* {
* public function close()
* {
* if (true)
* {
* // ...
* }
* }
* }
*
*
* @author Manuel Pichler Some text <% echo $var; %> and some more text
<%= $var . ' and some more text to make sure the snippet works'; %>Some text <%= $var %> and some more text
Some text and some more text
Some text and some more text
Some text <% echo $var; %> and some more text
<%= $var . ' and some more text to make sure the snippet works'; %>Some text <%= $var %> and some more text
Testing a merge conflict.
=======Another text string.
>>>>>>> ref/heads/feature-branchTesting a merge conflict.
=======Another text string.
>>>>>>> ref/heads/feature-branchvalidate()) { $safe = $form->getSubmitValues(); } ?>open(); // error here } public function open() { // Some inline stuff that shouldn't error if (TRUE) echo 'hello'; foreach ($tokens as $token) echo $token; } /** * This is a comment 1. * This is a comment 2. * This is a comment 3. * This is a comment 4. */ public function close() { // All ok. if (TRUE) { if (TRUE) { } else if (FALSE) { foreach ($tokens as $token) { switch ($token) { case '1': case '2': if (true) { if (false) { if (false) { if (false) { echo 'hello'; } } } } break; case '5': break; } do { while (true) { foreach ($tokens as $token) { for ($i = 0; $i < $token; $i++) { echo 'hello'; } } } } while (true); } } } } /* This is another c style comment 1. This is another c style comment 2. This is another c style comment 3. This is another c style comment 4. This is another c style comment 5. */ /* This is a T_COMMENT * * * */ /** This is a T_DOC_COMMENT */ /* This T_COMMENT has a newline in it. */ public function read() { echo 'hello'; // no errors below. $array = array( 'this', 'that' => array( 'hello', 'hello again' => array( 'hello', ), ), ); } } abstract class Test3 { public function parse() { foreach ($t as $ndx => $token) { if (is_array($token)) { echo 'here'; } else { $ts[] = array("token" => $token, "value" => ''); $last = count($ts) - 1; switch ($token) { case '(': if ($last >= 3 && $ts[0]['token'] != T_CLASS && $ts[$last - 2]['token'] == T_OBJECT_OPERATOR && $ts[$last - 3]['token'] == T_VARIABLE ) { if (true) { echo 'hello'; } } array_push($braces, $token); break; } } } } } function test() { $o = <<
validate()) { $safe = $form->getSubmitValues(); } ?>open(); // error here } public function open() { // Some inline stuff that shouldn't error if (TRUE) echo 'hello'; foreach ($tokens as $token) echo $token; } /** * This is a comment 1. * This is a comment 2. * This is a comment 3. * This is a comment 4. */ public function close() { // All ok. if (TRUE) { if (TRUE) { } else if (FALSE) { foreach ($tokens as $token) { switch ($token) { case '1': case '2': if (true) { if (false) { if (false) { if (false) { echo 'hello'; } } } } break; case '5': break; } do { while (true) { foreach ($tokens as $token) { for ($i = 0; $i < $token; $i++) { echo 'hello'; } } } } while (true); } } } } /* This is another c style comment 1. This is another c style comment 2. This is another c style comment 3. This is another c style comment 4. This is another c style comment 5. */ /* This is a T_COMMENT * * * */ /** This is a T_DOC_COMMENT */ /* This T_COMMENT has a newline in it. */ public function read() { echo 'hello'; // no errors below. $array = array( 'this', 'that' => array( 'hello', 'hello again' => array( 'hello', ), ), ); } } abstract class Test3 { public function parse() { foreach ($t as $ndx => $token) { if (is_array($token)) { echo 'here'; } else { $ts[] = array("token" => $token, "value" => ''); $last = count($ts) - 1; switch ($token) { case '(': if ($last >= 3 && $ts[0]['token'] != T_CLASS && $ts[$last - 2]['token'] == T_OBJECT_OPERATOR && $ts[$last - 3]['token'] == T_VARIABLE ) { if (true) { echo 'hello'; } } array_push($braces, $token); break; } } } } } function test() { $o = <<
validate()) { $safe = $form->getSubmitValues(); } ?>open(); // error here } public function open() { // Some inline stuff that shouldn't error if (TRUE) echo 'hello'; foreach ($tokens as $token) echo $token; } /** * This is a comment 1. * This is a comment 2. * This is a comment 3. * This is a comment 4. */ public function close() { // All ok. if (TRUE) { if (TRUE) { } else if (FALSE) { foreach ($tokens as $token) { switch ($token) { case '1': case '2': if (true) { if (false) { if (false) { if (false) { echo 'hello'; } } } } break; case '5': break; } do { while (true) { foreach ($tokens as $token) { for ($i = 0; $i < $token; $i++) { echo 'hello'; } } } } while (true); } } } } /* This is another c style comment 1. This is another c style comment 2. This is another c style comment 3. This is another c style comment 4. This is another c style comment 5. */ /* This is a T_COMMENT * * * */ /** This is a T_DOC_COMMENT */ /* This T_COMMENT has a newline in it. */ public function read() { echo 'hello'; // no errors below. $array = array( 'this', 'that' => array( 'hello', 'hello again' => array( 'hello', ), ), ); } } abstract class Test3 { public function parse() { foreach ($t as $ndx => $token) { if (is_array($token)) { echo 'here'; } else { $ts[] = array("token" => $token, "value" => ''); $last = count($ts) - 1; switch ($token) { case '(': if ($last >= 3 && $ts[0]['token'] != T_CLASS && $ts[$last - 2]['token'] == T_OBJECT_OPERATOR && $ts[$last - 3]['token'] == T_VARIABLE ) { if (true) { echo 'hello'; } } array_push($braces, $token); break; } } } } } function test() { $o = <<
validate()) { $safe = $form->getSubmitValues(); } ?>open(); // error here } public function open() { // Some inline stuff that shouldn't error if (TRUE) echo 'hello'; foreach ($tokens as $token) echo $token; } /** * This is a comment 1. * This is a comment 2. * This is a comment 3. * This is a comment 4. */ public function close() { // All ok. if (TRUE) { if (TRUE) { } else if (FALSE) { foreach ($tokens as $token) { switch ($token) { case '1': case '2': if (true) { if (false) { if (false) { if (false) { echo 'hello'; } } } } break; case '5': break; } do { while (true) { foreach ($tokens as $token) { for ($i = 0; $i < $token; $i++) { echo 'hello'; } } } } while (true); } } } } /* This is another c style comment 1. This is another c style comment 2. This is another c style comment 3. This is another c style comment 4. This is another c style comment 5. */ /* This is a T_COMMENT * * * */ /** This is a T_DOC_COMMENT */ /* This T_COMMENT has a newline in it. */ public function read() { echo 'hello'; // no errors below. $array = array( 'this', 'that' => array( 'hello', 'hello again' => array( 'hello', ), ), ); } } abstract class Test3 { public function parse() { foreach ($t as $ndx => $token) { if (is_array($token)) { echo 'here'; } else { $ts[] = array("token" => $token, "value" => ''); $last = count($ts) - 1; switch ($token) { case '(': if ($last >= 3 && $ts[0]['token'] != T_CLASS && $ts[$last - 2]['token'] == T_OBJECT_OPERATOR && $ts[$last - 3]['token'] == T_VARIABLE ) { if (true) { echo 'hello'; } } array_push($braces, $token); break; } } } } } function test() { $o = <<