1

I've studied some "sniffs" from the "Generic" and "Squiz" coding standards that come with CodeSniffer version 1.3, and learned enough to write a few "custom" sniffs to catch some coding anti-patterns which are specific to a PHP project that I'm working on.

But now my boss wants me to use the tool to identify all the places where the code calls exec(), popen(), passthru(), or uses the backtick operator to run an "external" command, and I've hit a snag dealing with the backticks.

The Generic_Sniffs_PHP_ForbiddenFunctionsSniff class which comes with the CodeSniffer 1.3 distribution makes it essentially trivial to identify any calls to a "dangerous function" like exec(), popen(), and passthru(), so that part is easy.

But I cannot see any references to backtick operators in the "stock" sniffs, nor do I see any mention of the backtick operator in any of the CodeSniffer logic itself - although I may be looking in the wrong place (it took me a while to figure out that "->" is really T_OBJECT_OPERATOR, for example).

So my question is this:

Can I use PHP_CodeSniffer to detect backtick operator usage in PHP code, and if so, how?

Peter
  • 2,526
  • 1
  • 23
  • 32

2 Answers2

2

http://php.net/manual/en/tokens.php

Looks like there's no token for backticks. You should, however, be able to follow the class hierarchy low enough down that you can find a connection point where you can just do strpos or preg_match looking for `. It should mention in the CodeSniffer documentation how to do that, or, like I said, you could follow the Generic_Sniffs_PHP_ForbiddenFunctionsSniff class up to its parent (and up to its parent if necessary) until you find where the actual searching is happening.

Edit: Just looked into the CodeSniffer code, and it appears that it might only support token searching... So it looks like you will have to make a new token.

Corbin
  • 33,060
  • 6
  • 68
  • 78
  • Well, it should be caught by the raw character token (not a named token, but a string instead of an array inside token_get_all output). So unless it filters ghouse out, should be psossible... – ircmaxell Sep 07 '11 at 00:04
  • @Corbin: I was afraid of that. Thanks for confirming my suspicions - at least I'll be less worried that I'm wasting my time when I dive into the guts of CodeSniffer, now. But I'm a bit surprised to find a gap of this size in the existing CodeSniffer functionality, given how mature the project seems to be. – Peter Sep 07 '11 at 00:45
  • @ircmaxell: Thanks for the tip. I'll let you know how it pans out. – Peter Sep 07 '11 at 00:46
  • @ircmaxell Code sniffer registers handlers based on token callbacks, so if the token does not exist in its system, you cannot register a callback. (Unless there's a way to register the callback for all files, but that's not very efficient.) Unless I misunderstood your advice, and you mean that CodeSniffer can register raw tokens. @Peter I'm quite surprised it's missing too, especially since people may wish to disallow ` token quoting SQL queries. – Corbin Sep 07 '11 at 00:47
  • 1
    @Corbin, @ircmaxell: Actually, the T_NONE token *is* defined, and the associated "content" attribute may be checked to see if it matches "`". This may be enough for me to write the sniffs which I need, without hacking CodeSniffer itself. – Peter Sep 07 '11 at 01:33
  • @Corbin: FYI, the T_BACKTICK token is now officially [part of CodeSniffer](https://pear.php.net/bugs/bug.php?id=18799). – Peter Sep 29 '11 at 12:20
  • Awesome! I'm kind of surprised they overlooked it to begin with.... But whatever works :). – Corbin Sep 30 '11 at 05:37
1

This example (with most comments stripped out) works with a few simple test cases - no CodeSniffer changes required!

class test_Sniffs_Dangerous_BackTickSniff implements PHP_CodeSniffer_Sniff {

public $supportedTokenizers = array('PHP');

public function register() {
    return array(T_NONE);
}

public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
    // generate one error for every pair of backticks:
    static $reported = array();

    $all_tokens = $phpcsFile->getTokens();

    if ($all_tokens[$stackPtr]['content'] == '`') {
        $lno = $all_tokens[$stackPtr]['line'];
        if (!isset($reported[$lno])) {
            $reported[$lno] = true;
            $phpcsFile->addError('Avoid backticks', $stackPtr);
        }
    }
}

}

As this is what I was after, I'm going to answer my own question. Thanks Corbin, and ircmaxell, for your comments.

Peter
  • 2,526
  • 1
  • 23
  • 32
  • Happily, Greg Sherwood has granted me a feature request: http://pear.php.net/bugs/bug.php?id=18799 – Peter Sep 12 '11 at 12:58