1<?php
2/**
3 * Runs jshint.js on the file.
4 *
5 * @author    Greg Sherwood <gsherwood@squiz.net>
6 * @author    Alexander Wei§ <aweisswa@gmx.de>
7 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
8 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
9 */
10
11namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Debug;
12
13use PHP_CodeSniffer\Config;
14use PHP_CodeSniffer\Files\File;
15use PHP_CodeSniffer\Sniffs\Sniff;
16use PHP_CodeSniffer\Util\Common;
17
18class JSHintSniff implements Sniff
19{
20
21    /**
22     * A list of tokenizers this sniff supports.
23     *
24     * @var array
25     */
26    public $supportedTokenizers = ['JS'];
27
28
29    /**
30     * Returns the token types that this sniff is interested in.
31     *
32     * @return int[]
33     */
34    public function register()
35    {
36        return [T_OPEN_TAG];
37
38    }//end register()
39
40
41    /**
42     * Processes the tokens that this sniff is interested in.
43     *
44     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
45     * @param int                         $stackPtr  The position in the stack where
46     *                                               the token was found.
47     *
48     * @return void
49     * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jshint.js could not be run
50     */
51    public function process(File $phpcsFile, $stackPtr)
52    {
53        $rhinoPath  = Config::getExecutablePath('rhino');
54        $jshintPath = Config::getExecutablePath('jshint');
55        if ($rhinoPath === null && $jshintPath === null) {
56            return;
57        }
58
59        $fileName   = $phpcsFile->getFilename();
60        $jshintPath = Common::escapeshellcmd($jshintPath);
61
62        if ($rhinoPath !== null) {
63            $rhinoPath = Common::escapeshellcmd($rhinoPath);
64            $cmd       = "$rhinoPath \"$jshintPath\" ".escapeshellarg($fileName);
65            exec($cmd, $output, $retval);
66
67            $regex = '`^(?P<error>.+)\(.+:(?P<line>[0-9]+).*:[0-9]+\)$`';
68        } else {
69            $cmd = "$jshintPath ".escapeshellarg($fileName);
70            exec($cmd, $output, $retval);
71
72            $regex = '`^(.+?): line (?P<line>[0-9]+), col [0-9]+, (?P<error>.+)$`';
73        }
74
75        if (is_array($output) === true) {
76            foreach ($output as $finding) {
77                $matches    = [];
78                $numMatches = preg_match($regex, $finding, $matches);
79                if ($numMatches === 0) {
80                    continue;
81                }
82
83                $line    = (int) $matches['line'];
84                $message = 'jshint says: '.trim($matches['error']);
85                $phpcsFile->addWarningOnLine($message, $line, 'ExternalTool');
86            }
87        }
88
89        // Ignore the rest of the file.
90        return ($phpcsFile->numTokens + 1);
91
92    }//end process()
93
94
95}//end class
96