1<?php
2/**
3 * Generic_Sniffs_Files_LineEndingsSniff.
4 *
5 * PHP version 5
6 *
7 * @category  PHP
8 * @package   PHP_CodeSniffer
9 * @author    Greg Sherwood <gsherwood@squiz.net>
10 * @author    Marc McIntyre <mmcintyre@squiz.net>
11 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
12 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
13 * @link      http://pear.php.net/package/PHP_CodeSniffer
14 */
15
16/**
17 * Generic_Sniffs_Files_LineEndingsSniff.
18 *
19 * Checks that end of line characters are correct.
20 *
21 * @category  PHP
22 * @package   PHP_CodeSniffer
23 * @author    Greg Sherwood <gsherwood@squiz.net>
24 * @author    Marc McIntyre <mmcintyre@squiz.net>
25 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
26 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
27 * @version   Release: @package_version@
28 * @link      http://pear.php.net/package/PHP_CodeSniffer
29 */
30class Generic_Sniffs_Files_LineEndingsSniff implements PHP_CodeSniffer_Sniff
31{
32
33    /**
34     * A list of tokenizers this sniff supports.
35     *
36     * @var array
37     */
38    public $supportedTokenizers = array(
39                                   'PHP',
40                                   'JS',
41                                   'CSS',
42                                  );
43
44    /**
45     * The valid EOL character.
46     *
47     * @var string
48     */
49    public $eolChar = '\n';
50
51
52    /**
53     * Returns an array of tokens this test wants to listen for.
54     *
55     * @return array
56     */
57    public function register()
58    {
59        return array(T_OPEN_TAG);
60
61    }//end register()
62
63
64    /**
65     * Processes this sniff, when one of its tokens is encountered.
66     *
67     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
68     * @param int                  $stackPtr  The position of the current token in
69     *                                        the stack passed in $tokens.
70     *
71     * @return int
72     */
73    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
74    {
75        $found = $phpcsFile->eolChar;
76        $found = str_replace("\n", '\n', $found);
77        $found = str_replace("\r", '\r', $found);
78
79        $phpcsFile->recordMetric($stackPtr, 'EOL char', $found);
80
81        if ($found === $this->eolChar) {
82            // Ignore the rest of the file.
83            return ($phpcsFile->numTokens + 1);
84        }
85
86        // Check for single line files without an EOL. This is a very special
87        // case and the EOL char is set to \n when this happens.
88        if ($found === '\n') {
89            $tokens    = $phpcsFile->getTokens();
90            $lastToken = ($phpcsFile->numTokens - 1);
91            if ($tokens[$lastToken]['line'] === 1
92                && $tokens[$lastToken]['content'] !== "\n"
93            ) {
94                return;
95            }
96        }
97
98        $error    = 'End of line character is invalid; expected "%s" but found "%s"';
99        $expected = $this->eolChar;
100        $expected = str_replace("\n", '\n', $expected);
101        $expected = str_replace("\r", '\r', $expected);
102        $data     = array(
103                     $expected,
104                     $found,
105                    );
106
107        // Errors are always reported on line 1, no matter where the first PHP tag is.
108        $fix = $phpcsFile->addFixableError($error, 0, 'InvalidEOLChar', $data);
109
110        if ($fix === true) {
111            $tokens = $phpcsFile->getTokens();
112            switch ($this->eolChar) {
113            case '\n':
114                $eolChar = "\n";
115                break;
116            case '\r':
117                $eolChar = "\r";
118                break;
119            case '\r\n':
120                $eolChar = "\r\n";
121                break;
122            default:
123                $eolChar = $this->eolChar;
124                break;
125            }
126
127            for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
128                if (isset($tokens[($i + 1)]) === false
129                    || $tokens[($i + 1)]['line'] > $tokens[$i]['line']
130                ) {
131                    // Token is the last on a line.
132                    if (isset($tokens[$i]['orig_content']) === true) {
133                        $tokenContent = $tokens[$i]['orig_content'];
134                    } else {
135                        $tokenContent = $tokens[$i]['content'];
136                    }
137
138                    $newContent  = rtrim($tokenContent, "\r\n");
139                    $newContent .= $eolChar;
140                    $phpcsFile->fixer->replaceToken($i, $newContent);
141                }
142            }
143        }//end if
144
145        // Ignore the rest of the file.
146        return ($phpcsFile->numTokens + 1);
147
148    }//end process()
149
150
151}//end class
152