Skip to content

Instantly share code, notes, and snippets.

@jrfnl
Last active May 24, 2019 00:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrfnl/16e1d64d9ec8efec7729e9bfbb05201a to your computer and use it in GitHub Desktop.
Save jrfnl/16e1d64d9ec8efec7729e9bfbb05201a to your computer and use it in GitHub Desktop.
Draft: Upgrade guide to PHP_CodeSniffer 3.5.0 for sniff developers

Upgrade guide to PHP_CodeSniffer 3.5.0 for sniff developers

PHP_CodeSniffer 3.5.0 contains a lot of changes under the hood. For the most part these are non-breaking.

If you maintain an external standard, you have three options:

  • Maintain compatibility with older PHP_CodeSniffer versions [1]. In that case, there is probably nothing you need to do. Just make sure you check the BC-breaks section below just in case. All the "old" utility methods and properties will still work. They will however be removed in PHPCS 4.0.
  • Maintain compatibility with older PHP_CodeSniffer versions [2]. If you want to be both back-ward as well as forward compatible for PHPCS 4.0, you can wrap calls to the moved PHPCS utility methods in method_exists() conditions, like so:
    use PHP_CodeSniffer\Sniffs\Sniff;
    use PHP_CodeSniffer\Util\Sniffs\Conditions;
    
    class MySniff extends Sniff {
        public function register() {
            return [T_FUNCTION]
        }
    
        public function process(File $phpcsFile, $stackPtr) {
            if (method_exists('Conditions', 'getCondition') === true) {
                $result = Conditions::getCondition($phpcsFile, $stackPtr, [T_IF]);
            } else {
                $result = $phpcsFile->getCondition($stackPtr, T_IF);
            }
    
            // Sniff logic.
        }
    }
    Note: if you choose this direction, you can not (yet) use the new methods available in the new utility classes.
  • Change you minimum PHPCS requirement to 3.5.0 and benefit fully from all the new features. If you want to get the most out of PHPCS and benefit from all the new goodies, you will need to raise the minimum PHPCS requirement for your standard to 3.5.0. You will also need to change calls to the moved PHPCS utility methods to point to their new locations. If you choose this direction, you can start using all the new methods available and possibly deprecate/remove similar utility methods from within your own standard.

Moved Methods

Old Method New Method
\PHP_CodeSniffer\Files\File::getCondition($stackPtr, $type) \PHP_CodeSniffer\Util\Sniffs\Conditions::getCondition(File $phpcsFile, $stackPtr, $types=[], $reverse=false)
\PHP_CodeSniffer\Files\File::hasCondition($stackPtr, $types) \PHP_CodeSniffer\Util\Sniffs\Conditions::hasCondition(File $phpcsFile, $stackPtr, $types)
\PHP_CodeSniffer\Files\File::getMethodParameters($stackPtr) \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getParameters(File $phpcsFile, $stackPtr)
\PHP_CodeSniffer\Files\File::getMethodProperties($stackPtr) \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getProperties(File $phpcsFile, $stackPtr)
\PHP_CodeSniffer\Files\File::getMemberProperties($stackPtr) \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::getMemberProperties(File $phpcsFile, $stackPtr)
\PHP_CodeSniffer\Files\File::getClassProperties($stackPtr) \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::getClassProperties(File $phpcsFile, $stackPtr)
\PHP_CodeSniffer\Files\File::findExtendedClassName($stackPtr) \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findExtendedClassName(File $phpcsFile, $stackPtr)
\PHP_CodeSniffer\Files\File::findImplementedInterfaceNames($stackPtr) \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findImplementedInterfaceNames(File $phpcsFile, $stackPtr)
\PHP_CodeSniffer\Files\File::isReference($stackPtr) \PHP_CodeSniffer\Util\Sniffs\TokenIs::isReference(File $phpcsFile, $stackPtr)
\PHP_CodeSniffer\Files\File::getDeclarationName($stackPtr) \PHP_CodeSniffer\Util\Sniffs\ConstructNames::getDeclarationName(File $phpcsFile, $stackPtr)
\PHP_CodeSniffer\Util\Common::isCamelCaps($string, $classFormat=false, $public=true, $strict=true) \PHP_CodeSniffer\Util\Sniffs\ConstructNames::isCamelCaps($string, $classFormat=false, $public=true, $strict=true)
\PHP_CodeSniffer\Util\Common::isUnderscoreName($string) \PHP_CodeSniffer\Util\Sniffs\ConstructNames::isUnderscoreName($string)
\PHP_CodeSniffer\Util\Common::suggestType($varType) PHP_CodeSniffer\Util\Sniffs\Comments::suggestType($varType, $form='short', $allowedTypes=null)

Moved properties

Old property New Property
\PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\CamelCapsFunctionNameSniff::$magicMethods and \PHP_CodeSniffer\Standards\PEAR\Sniffs\NamingConventions\ValidFunctionNameSniff::$magicMethods \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::$magicMethods
\PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\CamelCapsFunctionNameSniff::$methodsDoubleUnderscore \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::$methodsDoubleUnderscore
\PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\CamelCapsFunctionNameSniff::$magicFunctions and \PHP_CodeSniffer\Standards\PEAR\Sniffs\NamingConventions\ValidFunctionNameSniff::$magicFunctions \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations::$magicFunctions
\PHP_CodeSniffer\Sniffs\AbstractVariableSniff::$phpReservedVars \PHP_CodeSniffer\Util\Sniffs\Variables::$phpReservedVars
\PHP_CodeSniffer\Util\Common::$allowedTypes PHP_CodeSniffer\Util\Sniffs\Comments::$allowedTypes

Changes to moved methods

PHP_CodeSniffer\Util\Sniffs\Comments::suggestType()

This method, in combination with the new PHP_CodeSniffer\Util\Sniffs\Comments::suggestTypeString() method, has been enhanced to support a lot more, common type formats.

  • Both array(type => type) as well as array<type, type> style are now supported.
  • PSR-5 style array types - string[] and (string|int)[] - are now supported.
  • Union and intersect types are now fully supported, including for combinations of styles.
  • Allows for prefixing a type with a ? to indicate nullability.

It now also accepts two additional parameters:

  • $form to elect whether "short" or "long" form of variable types should be suggested. Defaults to short as that is the more common preference nowadays. This only affects boolean vs bool and integer vs int.
  • $allowedTypes to overrule the default list of allowed variables types with a custom one.

In addition to this, the PHP_CodeSniffer\Util\Sniffs\Comments::suggestTypeString() method will now, if necessary:

  • Unduplicate a type string.
  • Combine multiple PSR-5 style array types, i.e. (int|float)[]|string[] will become (int|float|string)[].
  • Remove duplicate union/intersect separators, i.e. int||float will become int|float.
  • Remove the nullability indicator, if null is included in the type, i.e. ?int|float|null will become int|float|null.

\PHP_CodeSniffer\Util\Sniffs\Conditions::getCondition()

The old method accepted only a single token type for the $type parameter. The new method accepts both a single token type or an array of token types for the $type parameter, which has been renamed to $types to convey this. The parameter is also now optional.

Additionally, the new method has an optional boolean $reverse parameter which allows to indicate whether to get the first condition of a certain type or the last.

For your convenience, the class also contains getFirstCondition() and getLastCondition() methods which are wrappers for the getCondition() method.

\PHP_CodeSniffer\Util\Sniffs\ConstructNames::getDeclarationName()

While certain names may be invalid, such as PHP construct names starting with a number; for the purpose of alerting users to these invalid names, the getDeclarationName() method should be able to pick up on them. The getDeclarationName() method has now been adjusted to allow for such names.

The parse error resilience for the method has also been improved. To still be able to distinguish between an anonymous class/function having been passed to the method and a parse error, the method will continue to output null when an anonymous class/function has been passed but will return an empty string when a parse error has been encountered. Testing the function output with empty() catches both cases.

\PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findExtendedClassName and \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations::findImplementedInterfaceNames

If the extended class name/implemented interface name would be a FQN, it could be interlaced with comments and/or whitespace. Previously the return values would contain the name as found, including these comments/whitespace. The new method will return the "clean" name stripped off comments/whitespace.

Changes to moved properties

PHP_CodeSniffer\Util\Sniffs\Comments::$allowedTypes

This property used to only contain the "long" form of variable types as values. The array has now changed to contain "short" form types as keys and "long" form as values.

The array has also been expanded to include all types as contained in the draft for PSR5.

The old format of this property will, for the time being, remain in its original location.

\PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations properties

The moved $magicMethods, $methodsDoubleUnderscore, $magicFunctions properties have a slightly different format in their new location. The old format was name without double underscores => true The new format is name including double underscores => name without double underscores for $magicMethods and $magicFunctions. For $methodsDoubleUnderscore, the new format is name including double underscores => name of the PHP module which contains the method

The old format of the properties is maintained in their original location.

\PHP_CodeSniffer\Util\Sniffs\Variables::$phpReservedVars

The property now contains a more complete list of PHP reserved variable names. The array values used to always be true. This has been changed to true for variables which are superglobals and false for other PHP reserved variable names.

BC-breaks

There is one real BC break and it only occurs in a very specific situation: If your standard contains a sniff which extends either the Generic.NamingConventions.CamelCapsFunctionName or the PEAR.NamingConventions.ValidFunctionName sniff and overloads the __construct() method without calling parent::__construct(), the $magicMethods, $methodsDoubleUnderscore, $magicFunctions properties will be empty.

This is easily fixed by adding a call to the parent::__construct() method from within your overloaded __construct().

When a sniff extends the AbstractVariableSniff and overloads the __construct() method without calling parent::__construct() for the $phpReservedVars property, the same thing happens, i.e. $phpReservedVars will be empty.

Other than that, the FunctionDeclarations::getParameters(), FunctionDeclarations::getProperties(), Variables::getMemberProperties() and the ObjectDeclarations::getClassProperties() methods used to throw a TokenizerException when an incorrect token was passed. They will now throw a RuntimeException.

New public methods

In \PHP_CodeSniffer\Util\Sniffs\Comments

  • suggestTypeString($typeString, $form='short', $allowedTypes=null) method allows for passing a complete type string without pre-processing and receiving a valid type string suggestion back.

  • findEndOfComment(File $phpcsFile, $stackPtr) - to find the end of an inline/block comment based on the first comment token of a comment sequence. Returns the integer stackPtr position to the end of the comment.

  • findStartOfComment(File $phpcsFile, $stackPtr) - to find the start of an inline/block comment based on the last comment token of a comment sequence. Returns the integer stackPtr position to the start of the comment.

  • findConstantComment(File $phpcsFile, $stackPtr) - to find the end of a constant docblock/comment based on a T_CONST token. Returns the integer stackPtr position or false if no constant comment can be found.

  • findFunctionComment(File $phpcsFile, $stackPtr) - to find the end of a function docblock/comment based on a T_FUNCTION token. Returns the integer stackPtr position or false if no function comment can be found.

  • findOOStructureComment(File $phpcsFile, $stackPtr) - to find the end of a class/interface/trait docblock/comment based on a T_CLASS/T_INTERFACE/T_TRAIT token. Returns the integer stackPtr position or false if no comment can be found.

  • findPropertyComment(File $phpcsFile, $stackPtr) - to find the end of a property docblock/comment based on a T_VARIABLE token. Returns the integer stackPtr position or false if no property comment can be found.

  • findCommentAbove(File $phpcsFile, $stackPtr, $ignore=[]) - to find the end of a docblock/comment above an arbitrary token. Returns the integer stackPtr position or false if no comment can be found.

In \PHP_CodeSniffer\Util\Sniffs\Conditions

  • getFirstCondition(File $phpcsFile, $stackPtr, $types=[]) - to retrieve the first condition of a type passed in $types for a token or the first condition for a token if no types are specified. This is effectively just an alias for the getCondition() method.
  • getLastCondition(File $phpcsFile, $stackPtr, $types=[]) - to retrieve the last condition of a type passed in $types for a token or the last condition for a token if no types are specified.
  • isOOProperty(File $phpcsFile, $stackPtr) - to check if a T_VARIABLE token is a class/trait property. Returns true/false.
  • isOOConstant(File $phpcsFile, $stackPtr) - to check if a T_CONSTANT token is a class/interface constant. Returns true/false.
  • is_OOMethod(File $phpcsFile, $stackPtr) - to check if a T_FUNCTION token is a class/interface/trait method. Returns true/false.
  • validDirectScope(File $phpcsFile, $stackPtr, $validScopes) - to check the direct condition of an arbitrary token against a set of valid scope conditions. Returns the integer stackPtr to the direct condition if valid or false otherwise.

In \PHP_CodeSniffer\Util\Sniffs\ConstructNames

  • hasNumbers($name) - to check whether a name string contains numeric characters. Returns boolean.
  • ltrimNumbers($name) - to remove any numeric characters from the start of the name string. Returns the transformed string.
  • removeNumbers($name) - to remove all numeric characters from the name string. Returns the transformed string.
  • lowerConsecutiveCaps($name) - to transform consecutive caps in a name string to lowercase. Returns the adjusted string or the original string if no consecutive caps were found, or when the input string was non-ASCII and the MBString extension is not available.

In \PHP_CodeSniffer\Util\Sniffs\FunctionDeclarations

  • isMagicFunction(File $phpcsFile, $stackPtr) - to check if the function declared on a T_FUNCTION token is a PHP magic function. Returns true/false.
  • isMagicFunctionName($name) - to check if a given function name is the name of a PHP magic function. Returns true/false.
  • isMagicMethod(File $phpcsFile, $stackPtr) - to check if the function declared on a T_FUNCTION token is a PHP magic method. Returns true/false.
  • isMagicMethodName($name) - to check if a given function name is the name of a PHP magic method. Returns true/false.
  • isPHPDoubleUnderscoreMethod(File $phpcsFile, $stackPtr) - to check if the function declared on a T_FUNCTION token is a PHP native double underscore method. Returns true/false.
  • isPHPDoubleUnderscoreMethodName($name) - to check if a given function name is the name of a PHP native double underscore method. Returns true/false.
  • isSpecialMethod(File $phpcsFile, $stackPtr) - to check if the function declared on a T_FUNCTION token is a PHP magic method or a PHP native double underscore method. Returns true/false.
  • isSpecialMethodName($name) - to check if a given function name is the name of a PHP magic method or PHP native double underscore method. Returns true/false.

The Name methods only check a given name, the non-Name method also check if the function is a method or global function.

In \PHP_CodeSniffer\Util\Sniffs\Namespaces

  • getType(File $phpcsFile, $stackPtr) - to determine what a T_NAMESPACE token is used for. Returns a string. Either declaration or operator or an empty string when the type of statement for which it's used could not be determined.
  • isDeclaration(File $phpcsFile, $stackPtr) - to check whether a T_NAMESPACE token is used to declare a namespace. Returns boolean.
  • isOperator(File $phpcsFile, $stackPtr) - to check whether a T_NAMESPACE token is used as an operator. Returns boolean.
  • getDeclaredName(File $phpcsFile, $stackPtr, $clean=true) - to retrieve the (cleaned up) name of a namespace based on the T_NAMESPACE token for the namespace declaration. Returns string|false - the namespace name or an empty string if the code is declared to be in the global namespace. Will return false if the passed token is not for a namespace declaration or in the case of parse errors.
  • findNamespacePtr(File $phpcsFile, $stackPtr) - to find the stackPtr to the T_NAMESPACE token for the namespace an arbitrary token lives in. Returns the integer stackPtr to the keyword token or false if it couldn't be determined or if no namespace applies.
  • determineNamespace(File $phpcsFile, $stackPtr) - to retrieve the (clean) name of the namespace an arbitrary token lives in. Returns string - the namespace name or an empty string if the code is not namespaced or the name could not be determined.

In \PHP_CodeSniffer\Util\Sniffs\ObjectDeclarations

  • findExtendedInterfaceNames(File $phpcsFile, $stackPtr) - to retrieve the names of the interfaces that a specified interface extends. Returns an array of names or false when the interface doesn't extend another interface.

In \PHP_CodeSniffer\Util\Sniffs\Orthography

  • isFirstCharCapitalized($string) - to check whether the first character of an arbitrary text string is a capital letter. Returns boolean.
  • isFirstCharLowercase($string) - to check whether the first character of an arbitrary text string is a lowercase letter. Returns boolean.
  • isLastCharPunctuation($string, $allowedChars=self::TERMINAL_POINTS) - to check whether the last character of an arbitrary text string is a punctuation character. By default, the full stop, question mark and exclamation mark are accepted as valid punctuation, but this can easily be changed by passing the $allowedChars parameter. Returns boolean. Ref: https://www.thepunctuationguide.com/terminal-points.html

This class also contains a TERMINAL_POINTS class constant containing the default allowed punctuation characters.

Important note: The return of isFirstCharCapitalized() is not the direct opposite of the output of isFirstCharLowercase().

  • isFirstCharCapitalized() will return true for capital letters and letters which don't have a concept of capitalization. It will return false for lowercase letters and non-letters.
  • isFirstCharLowercase() will return true for lowercase letters only. It will return false for all other characters, including non-letters.

In \PHP_CodeSniffer\Util\Sniffs\Parentheses

Targeted at open/close parentheses tokens:

  • getOwner(File $phpcsFile, $stackPtr) - to retrieve the stack pointer to the parentheses owner of an open/close parenthesis. Returns int or false if the parenthesis does not have an owner.
  • isOwnerIn(File $phpcsFile, $stackPtr, $validOwners) - to check whether the parenthesis owner of an open/close parenthesis is within a limited set of valid owners. Returns bool.

Targeted at arbitrary tokens:

  • hasOwner(File $phpcsFile, $stackPtr, $validOwners) - to check whether the passed token is nested within parentheses owned by one of the valid owners. Returns bool.
  • lastOwnerIn(File $phpcsFile, $stackPtr, $validOwners) - to check whether the owner of a direct wrapping set of parentheses is within a limited set of acceptable tokens. Returns int/false.
  • getFirstOpener(File $phpcsFile, $stackPtr, $validOwners=[]) - to retrieve the position of the opener to the first set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.
  • getFirstCloser(File $phpcsFile, $stackPtr, $validOwners=[]) - to retrieve the position of the closer to the first set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.
  • getFirstOwner(File $phpcsFile, $stackPtr, $validOwners=[]) - to retrieve the position of the parentheses owner to the first set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.
  • getLastOpener(File $phpcsFile, $stackPtr, $validOwners=[]) - to retrieve the position of the opener to the last set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.
  • getLastCloser(File $phpcsFile, $stackPtr, $validOwners=[]) - to retrieve the position of the closer to the last set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.
  • getLastOwner(File $phpcsFile, $stackPtr, $validOwners=[]) - to retrieve the position of the parentheses owner to the last set of parentheses an arbitrary token is wrapped in where the parentheses owner is within the set of valid owners. Returns int/false.

Where the return value is of the type int|false, the integer stack pointer to the parentheses opener/closer/owner will be returned or false when:

  • the token is not wrapped in parentheses;
  • the parentheses owner is not within the set of $validOwners passed;
  • and in the case of owner: when the desired set of parenthesis does not have an owner.

In \PHP_CodeSniffer\Util\Sniffs\PassedParameters

  • hasParameters(File $phpcsFile, $stackPtr) - to check if parameters have been passed. Returns true/false.
  • getParameters(File $phpcsFile, $stackPtr) - to retrieve details of the passed parameters. Returns an array with start (token position), end (token position) and raw (string content) information for each parameter.
  • getParameter(File $phpcsFile, $stackPtr, $paramOffset) - to retrieve the details of a specific parameter. Index for the parameters is 1-based. Returns an array with the details for the specified parameter or false if the parameter does not exist.
  • getParameterCount(File $phpcsFile, $stackPtr) - to get the a count of the number of parameters. Returns integer.
  • getDoubleArrowPosition(File $phpcsFile, $start, $end) - to find the position of a double arrow within an array item. Returns the integer stackPtr position or false if the array item doesn't have a key.

These methods are intended for use with:

  • T_STRING and T_VARIABLE tokens for function calls;
  • T_SELF and T_STATIC token for function calls in the form of new self();
  • T_ARRAY and T_OPEN_SHORT_ARRAY tokens for array declarations;
  • T_LIST and T_OPEN_SHORT_ARRAY tokens for list declarations;
  • T_ISSET and T_UNSET tokens for calls to these language constructs.

In \PHP_CodeSniffer\Util\Sniffs\TextString

  • getCompleteTextString(File $phpcsFile, $stackPtr, $stripQuotes=true) - to retrieve the complete text string content of a - potentially multi-line - text string. Returns the text string.
  • stripQuotes($string) - to strip the text delimiter quotes off an arbitrary text string. Returns the resulting text string.

In \PHP_CodeSniffer\Util\Sniffs\TokenIs

  • isShortList(File $phpcsFile, $stackPtr) - to check whether a short array is in actual fact a short list. Returns boolean.
  • isUnaryPlusMinus(File $phpcsFile, $stackPtr) - to check whether a plus/minus sign is a unary or an arithmetic operator. Returns boolean.

In \PHP_CodeSniffer\Util\Sniffs\UseStatements

  • getType(File $phpcsFile, $stackPtr) - to determine the type of statement a T_USE token is used for. Returns a string. Either closure, import or trait or an empty string when the type of statement for which it's used could not be determined.
  • isClosureUse(File $phpcsFile, $stackPtr) - Wrapper method to check whether a T_USE token is a closure use statement. Returns boolean.
  • isImportUse(File $phpcsFile, $stackPtr) - Wrapper method to check whether a T_USE token is a class/function/constant import use statement. Returns boolean.
  • isTraitUse(File $phpcsFile, $stackPtr) - Wrapper method to check whether a T_USE token is a trait importing use statement. Returns boolean.
  • splitImportUseStatement(File $phpcsFile, $stackPtr) - to split a multi-import or group-import use statement into information about the individual imports. Returns an array with information about all imports found in the use statement. Please refer to the function documentation for more detailed information.

In \PHP_CodeSniffer\Util\Sniffs\Variables

  • isPHPReservedVarName($name) - to check if a given variable name is the name of a PHP reserved variable. Returns true/false.
  • isSuperglobal(File $phpcsFile, $stackPtr) - to check if a given variable or string array key token points to a PHP superglobal. Returns true/false.
  • isSuperglobalName($name) - to check if a given variable name is the name of a PHP superglobal variable. Returns true/false.
  • isForeachAs(File $phpcsFile, $stackPtr) - to determine if a variable is in the as $key => $value part of a foreach condition. Returns true/false.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment