Index: contrib/phpcs/Observium/ruleset.xml =================================================================== --- contrib/phpcs/Observium/ruleset.xml (revision 0) +++ contrib/phpcs/Observium/ruleset.xml (revision 0) @@ -0,0 +1,182 @@ + + + The Observium coding standard. + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: contrib/phpcs/Observium/Sniffs/NamingConventions/ValidVariableNameSniff.php =================================================================== --- contrib/phpcs/Observium/Sniffs/NamingConventions/ValidVariableNameSniff.php (revision 0) +++ contrib/phpcs/Observium/Sniffs/NamingConventions/ValidVariableNameSniff.php (revision 0) @@ -0,0 +1,262 @@ +getTokens(); + $varName = ltrim($tokens[$stackPtr]['content'], '$'); + + $phpReservedVars = array( + '_SERVER', + '_GET', + '_POST', + '_REQUEST', + '_SESSION', + '_ENV', + '_COOKIE', + '_FILES', + 'GLOBALS', + ); + + // If it's a php reserved var, then its ok. + if (in_array($varName, $phpReservedVars) === true) { + return; + } + + $objOperator = $phpcsFile->findNext(array(T_WHITESPACE), ($stackPtr + 1), null, true); + if ($tokens[$objOperator]['code'] === T_OBJECT_OPERATOR) { + // Check to see if we are using a variable from an object. + $var = $phpcsFile->findNext(array(T_WHITESPACE), ($objOperator + 1), null, true); + if ($tokens[$var]['code'] === T_STRING) { + $bracket = $objOperator = $phpcsFile->findNext(array(T_WHITESPACE), ($var + 1), null, true); + if ($tokens[$bracket]['code'] !== T_OPEN_PARENTHESIS) { + $objVarName = $tokens[$var]['content']; + + // There is no way for us to know if the var is public or private, + // so we have to ignore a leading underscore if there is one and just + // check the main part of the variable name. + $originalVarName = $objVarName; + if (substr($objVarName, 0, 1) === '_') { + $objVarName = substr($objVarName, 1); + } + + if ($this->isUnderscoreLowerName($objVarName) === false) { + $error = 'Variable "%s" is not in valid lowercase underscore naming format'; + $data = array($originalVarName); + $phpcsFile->addError($error, $var, 'NotUnderscoreLowerName', $data); + } + }//end if + }//end if + }//end if + + // There is no way for us to know if the var is public or private, + // so we have to ignore a leading underscore if there is one and just + // check the main part of the variable name. + $originalVarName = $varName; + if (substr($varName, 0, 1) === '_') { + $objOperator = $phpcsFile->findPrevious(array(T_WHITESPACE), ($stackPtr - 1), null, true); + if ($tokens[$objOperator]['code'] === T_DOUBLE_COLON) { + // The variable lives within a class, and is referenced like + // this: MyClass::$_variable, so we don't know its scope. + $inClass = true; + } else { + $inClass = $phpcsFile->hasCondition($stackPtr, array(T_CLASS, T_INTERFACE)); + } + + if ($inClass === true) { + $varName = substr($varName, 1); + } + } + + if ($this->isUnderscoreLowerName($varName) === false) { + $error = 'Variable "%s" is not in valid lowercase underscore naming format'; + $data = array($originalVarName); + $phpcsFile->addError($error, $stackPtr, 'NotUnderscoreLowerName', $data); + } + + }//end processVariable() + + + /** + * Processes class member variables. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the + * stack passed in $tokens. + * + * @return void + */ + protected function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $varName = ltrim($tokens[$stackPtr]['content'], '$'); + $memberProps = $phpcsFile->getMemberProperties($stackPtr); + if (empty($memberProps) === true) { + // Couldn't get any info about this variable, which + // generally means it is invalid or possibly has a parse + // error. Any errors will be reported by the core, so + // we can ignore it. + return; + } + + $public = ($memberProps['scope'] !== 'private'); + $errorData = array($varName); + + if ($public === true) { + if (substr($varName, 0, 1) === '_') { + $error = '%s member variable "%s" must not contain a leading underscore'; + $data = array( + ucfirst($memberProps['scope']), + $errorData[0], + ); + $phpcsFile->addError($error, $stackPtr, 'PublicHasUnderscore', $data); + return; + } + } else { + if (substr($varName, 0, 1) !== '_') { + $error = 'Private member variable "%s" must contain a leading underscore'; + $phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $errorData); + return; + } + } + + if ($this->isUnderscoreLowerName($varName) === false) { + $error = 'Variable "%s" is not in valid lowercase underscore naming format'; + $phpcsFile->addError($error, $stackPtr, 'MemberNotUnderscoreLowerName', $errorData); + } + + }//end processMemberVar() + + + /** + * Processes the variable found within a double quoted string. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the double quoted + * string. + * + * @return void + */ + protected function processVariableInString(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $phpReservedVars = array( + '_SERVER', + '_GET', + '_POST', + '_REQUEST', + '_SESSION', + '_ENV', + '_COOKIE', + '_FILES', + 'GLOBALS', + ); + if (preg_match_all('|[^\\\]\${?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)|', $tokens[$stackPtr]['content'], $matches) !== 0) { + foreach ($matches[1] as $varName) { + // If it's a php reserved var, then its ok. + if (in_array($varName, $phpReservedVars) === true) { + continue; + } + + // There is no way for us to know if the var is public or private, + // so we have to ignore a leading underscore if there is one and just + // check the main part of the variable name. + $originalVarName = $varName; + if (substr($varName, 0, 1) === '_') { + if ($phpcsFile->hasCondition($stackPtr, array(T_CLASS, T_INTERFACE)) === true) { + $varName = substr($varName, 1); + } + } + + if ($this->isUnderscoreLowerName($varName) === false) { + $varName = $matches[0]; + $error = 'Variable "%s" is not in valid lowercase underscore naming format'; + $data = array($originalVarName); + $phpcsFile->addError($error, $stackPtr, 'StringNotUnderscoreLowerName', $data); + + } + } + }//end if + + }//end processVariableInString() + + +}//end class + +?> Index: contrib/phpcs/Observium/Sniffs/NamingConventions/ValidFunctionNameSniff.php =================================================================== --- contrib/phpcs/Observium/Sniffs/NamingConventions/ValidFunctionNameSniff.php (revision 0) +++ contrib/phpcs/Observium/Sniffs/NamingConventions/ValidFunctionNameSniff.php (revision 0) @@ -0,0 +1,94 @@ +getDeclarationName($stackPtr); + if ($functionName === null) { + return; + } + + $errorData = array($functionName); + + // Does this function claim to be magical? + if (preg_match('|^__|', $functionName) !== 0) { + $error = 'Function name "%s" is invalid; only PHP magic methods should be prefixed with a double underscore'; + $phpcsFile->addError($error, $stackPtr, 'DoubleUnderscore', $errorData); + return; + } + + if ($this->isUnderscoreLowerName($functionName) === false) { + $error = 'Function name "%s" is not in underscore naming format'; + $phpcsFile->addError($error, $stackPtr, 'NotUnderscoreLowerName', $errorData); + } + + }//end processTokenOutsideScope() + + +}//end class + +?> Index: contrib/phpcs/Observium/Sniffs/ControlStructures/ControlSignatureSniff.php =================================================================== --- contrib/phpcs/Observium/Sniffs/ControlStructures/ControlSignatureSniff.php (revision 0) +++ contrib/phpcs/Observium/Sniffs/ControlStructures/ControlSignatureSniff.php (revision 0) @@ -0,0 +1,46 @@ + Index: contrib/phpcs/Observium/Sniffs/PHP/EchoSniff.php =================================================================== --- contrib/phpcs/Observium/Sniffs/PHP/EchoSniff.php (revision 0) +++ contrib/phpcs/Observium/Sniffs/PHP/EchoSniff.php (revision 0) @@ -0,0 +1,50 @@ +getTokens(); + + $openingBracket = $phpcsFile->findNext(PHP_CodeSniffer_tokens::$emptyTokens, ($stackPtr+1), null, true); + + if ($tokens[$openingBracket]['code'] !== T_OPEN_PARENTHESIS) { + $error = 'Echo called without parentheses'; + $phpcsFile->addError($error, $stackPtr, 'NoParentheses'); + return; + } + }//end process() + + +}//end class + +?> Index: contrib/phpcs/Observium/Sniffs/Functions/FunctionCallSignatureSniff.php =================================================================== --- contrib/phpcs/Observium/Sniffs/Functions/FunctionCallSignatureSniff.php (revision 0) +++ contrib/phpcs/Observium/Sniffs/Functions/FunctionCallSignatureSniff.php (revision 0) @@ -0,0 +1,305 @@ +getTokens(); + + // Find the next non-empty token. + $openBracket = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true); + + if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { + // Not a function call. + return; + } + + if (isset($tokens[$openBracket]['parenthesis_closer']) === false) { + // Not a function call. + return; + } + + // Find the previous non-empty token. + $search = PHP_CodeSniffer_Tokens::$emptyTokens; + $search[] = T_BITWISE_AND; + $previous = $phpcsFile->findPrevious($search, ($stackPtr - 1), null, true); + if ($tokens[$previous]['code'] === T_FUNCTION) { + // It's a function definition, not a function call. + return; + } + + $closeBracket = $tokens[$openBracket]['parenthesis_closer']; + + if (($stackPtr + 1) !== $openBracket) { + // Checking this: $value = my_function[*](...). + $error = 'Space before opening parenthesis of function call prohibited'; + $phpcsFile->addError($error, $stackPtr, 'SpaceBeforeOpenBracket'); + } + + $next = $phpcsFile->findNext(T_WHITESPACE, ($closeBracket + 1), null, true); + if ($tokens[$next]['code'] === T_SEMICOLON) { + if (in_array($tokens[($closeBracket + 1)]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === true) { + $error = 'Space after closing parenthesis of function call prohibited'; + $phpcsFile->addError($error, $closeBracket, 'SpaceAfterCloseBracket'); + } + } + + // Check if this is a single line or multi-line function call. + if ($tokens[$openBracket]['line'] === $tokens[$closeBracket]['line']) { + $this->processSingleLineCall($phpcsFile, $stackPtr, $openBracket, $tokens); + } else { + $this->processMultiLineCall($phpcsFile, $stackPtr, $openBracket, $tokens); + } + + }//end process() + + + /** + * Processes single-line calls. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $openBracket The position of the opening bracket + * in the stack passed in $tokens. + * @param array $tokens The stack of tokens that make up + * the file. + * + * @return void + */ + public function processSingleLineCall(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $openBracket, $tokens) + { + if ($tokens[($openBracket + 1)]['code'] === T_WHITESPACE) { + // Checking this: $value = my_function([*]...). + $error = 'Space after opening parenthesis of function call prohibited'; + $phpcsFile->addError($error, $stackPtr, 'SpaceAfterOpenBracket'); + } + + $closer = $tokens[$openBracket]['parenthesis_closer']; + + if ($tokens[($closer - 1)]['code'] === T_WHITESPACE) { + // Checking this: $value = my_function(...[*]). + $between = $phpcsFile->findNext(T_WHITESPACE, ($openBracket + 1), null, true); + + // Only throw an error if there is some content between the parenthesis. + // i.e., Checking for this: $value = my_function(). + // If there is no content, then we would have thrown an error in the + // previous IF statement because it would look like this: + // $value = my_function( ). + if ($between !== $closer) { + $error = 'Space before closing parenthesis of function call prohibited'; + $phpcsFile->addError($error, $closer, 'SpaceBeforeCloseBracket'); + } + } + + }//end processSingleLineCall() + + + /** + * Processes multi-line calls. + * + * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $openBracket The position of the openning bracket + * in the stack passed in $tokens. + * @param array $tokens The stack of tokens that make up + * the file. + * + * @return void + */ + public function processMultiLineCall(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $openBracket, $tokens) + { + // We need to work out how far indented the function + // call itself is, so we can work out how far to + // indent the arguments. + $functionIndent = 0; + for ($i = ($stackPtr - 1); $i >= 0; $i--) { + if ($tokens[$i]['line'] !== $tokens[$stackPtr]['line']) { + $i++; + break; + } + } + + if ($tokens[$i]['code'] === T_WHITESPACE) { + $functionIndent = strlen($tokens[$i]['content']); + } + + // Each line between the parenthesis should be indented n spaces. + $closeBracket = $tokens[$openBracket]['parenthesis_closer']; + $lastLine = $tokens[$openBracket]['line']; + for ($i = ($openBracket + 1); $i < $closeBracket; $i++) { + // Skip nested function calls. + if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) { + $i = $tokens[$i]['parenthesis_closer']; + $lastLine = $tokens[$i]['line']; + continue; + } + + if ($tokens[$i]['line'] !== $lastLine) { + $lastLine = $tokens[$i]['line']; + + // Ignore heredoc indentation. + if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$heredocTokens) === true) { + continue; + } + + // Ignore multi-line string indentation. + if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) { + if ($tokens[$i]['code'] === $tokens[($i - 1)]['code']) { + continue; + } + } + + // We changed lines, so this should be a whitespace indent token, but first make + // sure it isn't a blank line because we don't need to check indent unless there + // is actually some code to indent. + if ($tokens[$i]['code'] === T_WHITESPACE) { + $nextCode = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), ($closeBracket + 1), true); + if ($tokens[$nextCode]['line'] !== $lastLine) { + $error = 'Empty lines are not allowed in multi-line function calls'; + $phpcsFile->addError($error, $i, 'EmptyLine'); + continue; + } + } else { + $nextCode = $i; + } + + // Check if the next line contains an object operator, if so rely on + // the ObjectOperatorIndentSniff to test the indent. + if ($tokens[$nextCode]['type'] === 'T_OBJECT_OPERATOR') { + continue; + } + + if ($nextCode === $closeBracket) { + // Closing brace needs to be indented to the same level + // as the function call. + $expectedIndent = $functionIndent; + } else { + $expectedIndent = ($functionIndent + $this->indent); + } + + if ($tokens[$i]['code'] !== T_WHITESPACE) { + // Just check if it is a multi-line block comment. If so, we can + // calculate the indent from the whitespace before the content. + if ($tokens[$i]['code'] === T_COMMENT + && $tokens[($i - 1)]['code'] === T_COMMENT + ) { + $trimmed = ltrim($tokens[$i]['content']); + $foundIndent = (strlen($tokens[$i]['content']) - strlen($trimmed)); + } else { + $foundIndent = 0; + } + } else { + $foundIndent = strlen($tokens[$i]['content']); + } + + if ($expectedIndent !== $foundIndent) { + $error = 'Multi-line function call not indented correctly; expected %s spaces but found %s'; + $data = array( + $expectedIndent, + $foundIndent, + ); + $phpcsFile->addError($error, $i, 'Indent', $data); + } + }//end if + + // Skip the rest of a closure. + if ($tokens[$i]['code'] === T_CLOSURE) { + $i = $tokens[$i]['scope_closer']; + $lastLine = $tokens[$i]['line']; + continue; + } + + // Skip the rest of a short array. + if ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) { + $i = $tokens[$i]['bracket_closer']; + $lastLine = $tokens[$i]['line']; + continue; + } + + if ($this->allowMultipleArguments === false && $tokens[$i]['code'] === T_COMMA) { + // Comma has to be the last token on the line. + $next = $phpcsFile->findNext(array(T_WHITESPACE, T_COMMENT), ($i + 1), $closeBracket, true); + if ($next !== false + && $tokens[$i]['line'] === $tokens[$next]['line'] + ) { + $error = 'Only one argument is allowed per line in a multi-line function call'; + $phpcsFile->addError($error, $next, 'MultipleArguments'); + } + } + }//end for + + if ($this->allowSameLineOpening === false && $tokens[($openBracket + 1)]['content'] !== $phpcsFile->eolChar) { + $error = 'Opening parenthesis of a multi-line function call must be the last content on the line'; + $phpcsFile->addError($error, $stackPtr, 'ContentAfterOpenBracket'); + } + + $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBracket - 1), null, true); + if ($this->allowSameLineClosing === false && $tokens[$prev]['line'] === $tokens[$closeBracket]['line']) { + $error = 'Closing parenthesis of a multi-line function call must be on a line by itself'; + $phpcsFile->addError($error, $closeBracket, 'CloseBracketLine'); + } + + }//end processMultiLineCall() + + +}//end class +?>