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