1<?php
2/**
3 * Generic_Sniffs_Debug_ESLintSniff.
4 *
5 * PHP version 5
6 *
7 * @category  PHP
8 * @package   PHP_CodeSniffer
9 * @author    Ryan McCue <ryan+gh@hmn.md>
10 * @copyright 2006-2017 Squiz Pty Ltd (ABN 77 084 670 600)
11 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
12 * @link      http://pear.php.net/package/PHP_CodeSniffer
13 */
14
15/**
16 * Generic_Sniffs_Debug_ESLintSniff.
17 *
18 * Runs eslint on the file.
19 *
20 * @category  PHP
21 * @package   PHP_CodeSniffer
22 * @author    Ryan McCue <ryan+gh@hmn.md>
23 * @copyright 2006-2017 Squiz Pty Ltd (ABN 77 084 670 600)
24 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
25 * @version   Release: @package_version@
26 * @link      http://pear.php.net/package/PHP_CodeSniffer
27 */
28class Generic_Sniffs_Debug_ESLintSniff implements PHP_CodeSniffer_Sniff
29{
30    /**
31     * A list of tokenizers this sniff supports.
32     *
33     * @var array
34     */
35    public $supportedTokenizers = array('JS');
36
37
38    /**
39     * ESLint configuration file path.
40     *
41     * @var string|null Path to eslintrc. Null to autodetect.
42     */
43    public $configFile = null;
44
45
46    /**
47     * Returns the token types that this sniff is interested in.
48     *
49     * @return int[]
50     */
51    public function register()
52    {
53        return array(T_OPEN_TAG);
54
55    }//end register()
56
57
58    /**
59     * Processes the tokens that this sniff is interested in.
60     *
61     * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found.
62     * @param int                  $stackPtr  The position in the stack where
63     *                                        the token was found.
64     *
65     * @return void
66     */
67    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
68    {
69        $filename   = $phpcsFile->getFilename();
70        $eslintPath = PHP_CodeSniffer::getConfigData('eslint_path');
71        if ($eslintPath === null) {
72            return;
73        }
74
75        $configFile = $this->configFile;
76        if (empty($configFile) === true) {
77            // Attempt to autodetect.
78            $candidates = glob('.eslintrc{.js,.yaml,.yml,.json}', GLOB_BRACE);
79            if (empty($candidates) === false) {
80                $configFile = $candidates[0];
81            }
82        }
83
84        $eslintOptions = array('--format json');
85        if (empty($configFile) === false) {
86            $eslintOptions[] = '--config '.escapeshellarg($configFile);
87        }
88
89        $cmd = escapeshellcmd(escapeshellarg($eslintPath).' '.implode(' ', $eslintOptions).' '.escapeshellarg($filename));
90
91        // Execute!
92        exec($cmd, $stdout, $code);
93
94        if ($code <= 0) {
95            // No errors, continue.
96            return ($phpcsFile->numTokens + 1);
97        }
98
99        $data = json_decode(implode("\n", $stdout));
100        if (json_last_error() !== JSON_ERROR_NONE) {
101            // Ignore any errors.
102            return ($phpcsFile->numTokens + 1);
103        }
104
105        // Data is a list of files, but we only pass a single one.
106        $messages = $data[0]->messages;
107        foreach ($messages as $error) {
108            if (empty($error->fatal) === false || $error->severity === 2) {
109                $phpcsFile->addErrorOnLine($error->message, $error->line, $error->ruleId);
110            } else {
111                $phpcsFile->addWarningOnLine($error->message, $error->line, $error->ruleId);
112            }
113        }
114
115        // Ignore the rest of the file.
116        return ($phpcsFile->numTokens + 1);
117
118    }//end process()
119
120
121}//end class
122