Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@17
|
3 * \Drupal\Sniffs\Semantics\FunctionTSniff
|
Chris@0
|
4 *
|
Chris@0
|
5 * @category PHP
|
Chris@0
|
6 * @package PHP_CodeSniffer
|
Chris@0
|
7 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@17
|
10 namespace Drupal\Sniffs\Semantics;
|
Chris@17
|
11
|
Chris@17
|
12 use PHP_CodeSniffer\Files\File;
|
Chris@17
|
13 use PHP_CodeSniffer\Util\Tokens;
|
Chris@17
|
14
|
Chris@0
|
15 /**
|
Chris@0
|
16 * Check the usage of the t() function to not escape translateable strings with back
|
Chris@0
|
17 * slashes. Also checks that the first argument does not use string concatenation.
|
Chris@0
|
18 *
|
Chris@0
|
19 * @category PHP
|
Chris@0
|
20 * @package PHP_CodeSniffer
|
Chris@0
|
21 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
22 */
|
Chris@17
|
23 class FunctionTSniff extends FunctionCall
|
Chris@0
|
24 {
|
Chris@0
|
25
|
Chris@0
|
26 /**
|
Chris@0
|
27 * We also want to catch $this->t() calls in Drupal 8.
|
Chris@0
|
28 *
|
Chris@0
|
29 * @var bool
|
Chris@0
|
30 */
|
Chris@0
|
31 protected $includeMethodCalls = true;
|
Chris@0
|
32
|
Chris@0
|
33
|
Chris@0
|
34 /**
|
Chris@0
|
35 * Returns an array of function names this test wants to listen for.
|
Chris@0
|
36 *
|
Chris@0
|
37 * @return array
|
Chris@0
|
38 */
|
Chris@0
|
39 public function registerFunctionNames()
|
Chris@0
|
40 {
|
Chris@0
|
41 return array(
|
Chris@0
|
42 't',
|
Chris@0
|
43 'TranslatableMarkup',
|
Chris@0
|
44 'TranslationWrapper',
|
Chris@0
|
45 );
|
Chris@0
|
46
|
Chris@0
|
47 }//end registerFunctionNames()
|
Chris@0
|
48
|
Chris@0
|
49
|
Chris@0
|
50 /**
|
Chris@0
|
51 * Processes this function call.
|
Chris@0
|
52 *
|
Chris@17
|
53 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@17
|
54 * @param int $stackPtr The position of the function call in
|
Chris@17
|
55 * the stack.
|
Chris@17
|
56 * @param int $openBracket The position of the opening
|
Chris@17
|
57 * parenthesis in the stack.
|
Chris@17
|
58 * @param int $closeBracket The position of the closing
|
Chris@17
|
59 * parenthesis in the stack.
|
Chris@0
|
60 *
|
Chris@0
|
61 * @return void
|
Chris@0
|
62 */
|
Chris@0
|
63 public function processFunctionCall(
|
Chris@17
|
64 File $phpcsFile,
|
Chris@0
|
65 $stackPtr,
|
Chris@0
|
66 $openBracket,
|
Chris@0
|
67 $closeBracket
|
Chris@0
|
68 ) {
|
Chris@0
|
69 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
70 $argument = $this->getArgument(1);
|
Chris@0
|
71
|
Chris@0
|
72 if ($argument === false) {
|
Chris@0
|
73 $error = 'Empty calls to t() are not allowed';
|
Chris@0
|
74 $phpcsFile->addError($error, $stackPtr, 'EmptyT');
|
Chris@0
|
75 return;
|
Chris@0
|
76 }
|
Chris@0
|
77
|
Chris@0
|
78 if ($tokens[$argument['start']]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
|
Chris@0
|
79 // Not a translatable string literal.
|
Chris@0
|
80 $warning = 'Only string literals should be passed to t() where possible';
|
Chris@0
|
81 $phpcsFile->addWarning($warning, $argument['start'], 'NotLiteralString');
|
Chris@0
|
82 return;
|
Chris@0
|
83 }
|
Chris@0
|
84
|
Chris@0
|
85 $string = $tokens[$argument['start']]['content'];
|
Chris@0
|
86 if ($string === '""' || $string === "''") {
|
Chris@0
|
87 $warning = 'Do not pass empty strings to t()';
|
Chris@0
|
88 $phpcsFile->addWarning($warning, $argument['start'], 'EmptyString');
|
Chris@0
|
89 return;
|
Chris@0
|
90 }
|
Chris@0
|
91
|
Chris@17
|
92 $concatAfter = $phpcsFile->findNext(Tokens::$emptyTokens, ($closeBracket + 1), null, true, null, true);
|
Chris@0
|
93 if ($concatAfter !== false && $tokens[$concatAfter]['code'] === T_STRING_CONCAT) {
|
Chris@17
|
94 $stringAfter = $phpcsFile->findNext(Tokens::$emptyTokens, ($concatAfter + 1), null, true, null, true);
|
Chris@0
|
95 if ($stringAfter !== false
|
Chris@0
|
96 && $tokens[$stringAfter]['code'] === T_CONSTANT_ENCAPSED_STRING
|
Chris@0
|
97 && $this->checkConcatString($tokens[$stringAfter]['content']) === false
|
Chris@0
|
98 ) {
|
Chris@0
|
99 $warning = 'Do not concatenate strings to translatable strings, they should be part of the t() argument and you should use placeholders';
|
Chris@0
|
100 $phpcsFile->addWarning($warning, $stringAfter, 'ConcatString');
|
Chris@0
|
101 }
|
Chris@0
|
102 }
|
Chris@0
|
103
|
Chris@0
|
104 $lastChar = substr($string, -1);
|
Chris@0
|
105 if ($lastChar === '"' || $lastChar === "'") {
|
Chris@0
|
106 $message = substr($string, 1, -1);
|
Chris@0
|
107 if ($message !== trim($message)) {
|
Chris@0
|
108 $warning = 'Translatable strings must not begin or end with white spaces, use placeholders with t() for variables';
|
Chris@0
|
109 $phpcsFile->addWarning($warning, $argument['start'], 'WhiteSpace');
|
Chris@0
|
110 }
|
Chris@0
|
111 }
|
Chris@0
|
112
|
Chris@0
|
113 $concatFound = $phpcsFile->findNext(T_STRING_CONCAT, $argument['start'], $argument['end']);
|
Chris@0
|
114 if ($concatFound !== false) {
|
Chris@0
|
115 $error = 'Concatenating translatable strings is not allowed, use placeholders instead and only one string literal';
|
Chris@0
|
116 $phpcsFile->addError($error, $concatFound, 'Concat');
|
Chris@0
|
117 }
|
Chris@0
|
118
|
Chris@0
|
119 // Check if there is a backslash escaped single quote in the string and
|
Chris@0
|
120 // if the string makes use of double quotes.
|
Chris@0
|
121 if ($string{0} === "'" && strpos($string, "\'") !== false
|
Chris@0
|
122 && strpos($string, '"') === false
|
Chris@0
|
123 ) {
|
Chris@0
|
124 $warn = 'Avoid backslash escaping in translatable strings when possible, use "" quotes instead';
|
Chris@0
|
125 $phpcsFile->addWarning($warn, $argument['start'], 'BackslashSingleQuote');
|
Chris@0
|
126 return;
|
Chris@0
|
127 }
|
Chris@0
|
128
|
Chris@0
|
129 if ($string{0} === '"' && strpos($string, '\"') !== false
|
Chris@0
|
130 && strpos($string, "'") === false
|
Chris@0
|
131 ) {
|
Chris@0
|
132 $warn = "Avoid backslash escaping in translatable strings when possible, use '' quotes instead";
|
Chris@0
|
133 $phpcsFile->addWarning($warn, $argument['start'], 'BackslashDoubleQuote');
|
Chris@0
|
134 }
|
Chris@0
|
135
|
Chris@0
|
136 }//end processFunctionCall()
|
Chris@0
|
137
|
Chris@0
|
138
|
Chris@0
|
139 /**
|
Chris@0
|
140 * Checks if a string can be concatenated with a translatable string.
|
Chris@0
|
141 *
|
Chris@0
|
142 * @param string $string The string that is concatenated to a t() call.
|
Chris@0
|
143 *
|
Chris@0
|
144 * @return bool
|
Chris@0
|
145 * TRUE if the string is allowed to be concatenated with a translatable
|
Chris@0
|
146 * string, FALSE if not.
|
Chris@0
|
147 */
|
Chris@0
|
148 protected function checkConcatString($string)
|
Chris@0
|
149 {
|
Chris@0
|
150 // Remove outer quotes, spaces and HTML tags from the original string.
|
Chris@0
|
151 $string = trim($string, '"\'');
|
Chris@0
|
152 $string = trim(strip_tags($string));
|
Chris@0
|
153
|
Chris@0
|
154 if ($string === '') {
|
Chris@0
|
155 return true;
|
Chris@0
|
156 }
|
Chris@0
|
157
|
Chris@17
|
158 $allowed_items = array(
|
Chris@17
|
159 '(',
|
Chris@17
|
160 ')',
|
Chris@17
|
161 '[',
|
Chris@17
|
162 ']',
|
Chris@17
|
163 '-',
|
Chris@17
|
164 '<',
|
Chris@17
|
165 '>',
|
Chris@17
|
166 '«',
|
Chris@17
|
167 '»',
|
Chris@17
|
168 '\n',
|
Chris@17
|
169 );
|
Chris@17
|
170 foreach ($allowed_items as $item) {
|
Chris@17
|
171 if ($item === $string) {
|
Chris@17
|
172 return true;
|
Chris@17
|
173 }
|
Chris@0
|
174 }
|
Chris@0
|
175
|
Chris@0
|
176 return false;
|
Chris@0
|
177
|
Chris@0
|
178 }//end checkConcatString()
|
Chris@0
|
179
|
Chris@0
|
180
|
Chris@0
|
181 }//end class
|