1
+ <?php
2
+ /**
3
+ * PHPCompatibility, an external standard for PHP_CodeSniffer.
4
+ *
5
+ * @package PHPCompatibility
6
+ * @copyright 2012-2020 PHPCompatibility Contributors
7
+ * @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8
+ * @link https://github.com/PHPCompatibility/PHPCompatibility
9
+ */
10
+
11
+ namespace PHPCompatibilityMagento \Sniffs \FunctionUse ;
12
+
13
+ use PHPCompatibility \AbstractFunctionCallParameterSniff ;
14
+ use PHP_CodeSniffer \Files \File ;
15
+
16
+ /**
17
+ * Detect missing required function parameters in calls to native PHP functions.
18
+ *
19
+ * Specifically when those function parameters used to be optional in older PHP versions.
20
+ *
21
+ * PHP version All
22
+ *
23
+ * @link https://www.php.net/manual/en/doc.changelog.php
24
+ *
25
+ * @since 8.1.0
26
+ * @since 9.0.0 Renamed from `OptionalRequiredFunctionParametersSniff` to `OptionalToRequiredFunctionParametersSniff`.
27
+ * @since 10.0.0 Now extends the base `AbstractFunctionCallParameterSniff` class.
28
+ * Previously the sniff extended the sister-sniff `RequiredToOptionalFunctionParametersSniff`.
29
+ * Methods which were previously required due to the extending of the (grand-parent)
30
+ * `AbstractComplexVersionSniff` have been removed.
31
+ */
32
+ class OptionalToRequiredFunctionParametersSniff extends AbstractFunctionCallParameterSniff
33
+ {
34
+
35
+ /**
36
+ * A list of function parameters, which were optional in older versions and became required later on.
37
+ *
38
+ * The array lists : version number with true (required) and false (optional use deprecated).
39
+ *
40
+ * The index is the location of the parameter in the parameter list, starting at 0 !
41
+ * If's sufficient to list the last version in which the parameter was not yet required.
42
+ *
43
+ * @since 8.1.0
44
+ * @since 10.0.0 Parameter renamed from `$functionParameters` to `$targetFunctions` for
45
+ * compatibility with the `AbstractFunctionCallParameterSniff` class.
46
+ *
47
+ * @var array
48
+ */
49
+ protected $ targetFunctions = [
50
+ 'crypt ' => [
51
+ 1 => [
52
+ 'name ' => 'salt ' ,
53
+ '5.6 ' => false ,
54
+ '8.0 ' => true ,
55
+ ],
56
+ ],
57
+ 'gmmktime ' => [
58
+ 1 => [
59
+ 'name ' => 'hour ' ,
60
+ '8.0 ' => true ,
61
+ ],
62
+ ],
63
+ 'mb_parse_str ' => [
64
+ 1 => [
65
+ 'name ' => 'result ' ,
66
+ '8.0 ' => true ,
67
+ ],
68
+ ],
69
+ 'mktime ' => [
70
+ 0 => [
71
+ 'name ' => 'hour ' ,
72
+ '5.1 ' => false ,
73
+ '8.0 ' => true ,
74
+ ],
75
+ ],
76
+ 'openssl_seal ' => [
77
+ 4 => [
78
+ 'name ' => 'method ' ,
79
+ '8.0 ' => true ,
80
+ ],
81
+ ],
82
+ 'openssl_open ' => [
83
+ 4 => [
84
+ 'name ' => 'method ' ,
85
+ '8.0 ' => true ,
86
+ ],
87
+ ],
88
+ 'parse_str ' => [
89
+ 1 => [
90
+ 'name ' => 'result ' ,
91
+ '7.2 ' => false ,
92
+ '8.0 ' => true ,
93
+ ],
94
+ ],
95
+ ];
96
+
97
+
98
+ /**
99
+ * Bowing out early is not applicable to this sniff.
100
+ *
101
+ * @since 10.0.0
102
+ *
103
+ * @return bool
104
+ */
105
+ protected function bowOutEarly ()
106
+ {
107
+ return false ;
108
+ }
109
+
110
+ /**
111
+ * Process the parameters of a matched function.
112
+ *
113
+ * @since 10.0.0 Part of the logic in this method was previously contained in the
114
+ * parent sniff `process()` method (now removed).
115
+ *
116
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
117
+ * @param int $stackPtr The position of the current token in the stack.
118
+ * @param string $functionName The token content (function name) which was matched.
119
+ * @param array $parameters Array with information about the parameters.
120
+ *
121
+ * @return int|void Integer stack pointer to skip forward or void to continue
122
+ * normal file processing.
123
+ */
124
+ public function processParameters (File $ phpcsFile , $ stackPtr , $ functionName , $ parameters )
125
+ {
126
+ $ functionLc = \strtolower ($ functionName );
127
+ $ parameterCount = \count ($ parameters );
128
+ $ parameterOffsetFound = $ parameterCount - 1 ;
129
+
130
+ foreach ($ this ->targetFunctions [$ functionLc ] as $ offset => $ parameterDetails ) {
131
+ if ($ offset > $ parameterOffsetFound ) {
132
+ $ itemInfo = [
133
+ 'name ' => $ functionName ,
134
+ 'nameLc ' => $ functionLc ,
135
+ 'offset ' => $ offset ,
136
+ ];
137
+ $ this ->handleFeature ($ phpcsFile , $ stackPtr , $ itemInfo );
138
+ }
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Process the function if no parameters were found.
144
+ *
145
+ * @since 10.0.0
146
+ *
147
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
148
+ * @param int $stackPtr The position of the current token in the stack.
149
+ * @param string $functionName The token content (function name) which was matched.
150
+ *
151
+ * @return int|void Integer stack pointer to skip forward or void to continue
152
+ * normal file processing.
153
+ */
154
+ public function processNoParameters (File $ phpcsFile , $ stackPtr , $ functionName )
155
+ {
156
+ $ this ->processParameters ($ phpcsFile , $ stackPtr , $ functionName , []);
157
+ }
158
+
159
+ /**
160
+ * Retrieve the relevant detail (version) information for use in an error message.
161
+ *
162
+ * @since 8.1.0
163
+ * @since 10.0.0 - Method renamed from `getErrorInfo()` to `getVersionInfo().
164
+ * - Second function parameter `$itemInfo` removed.
165
+ * - Method visibility changed from `public` to `protected`.
166
+ *
167
+ * @param array $itemArray Version and other information about the item.
168
+ *
169
+ * @return array
170
+ */
171
+ protected function getVersionInfo (array $ itemArray )
172
+ {
173
+ $ versionInfo = [
174
+ 'optionalDeprecated ' => '' ,
175
+ 'optionalRemoved ' => '' ,
176
+ 'error ' => false ,
177
+ ];
178
+
179
+ foreach ($ itemArray as $ version => $ required ) {
180
+ if (\preg_match ('`^\d\.\d(\.\d{1,2})?$` ' , $ version ) !== 1 ) {
181
+ // Not a version key.
182
+ continue ;
183
+ }
184
+
185
+ if ($ this ->supportsAbove ($ version ) === true ) {
186
+ if ($ required === true && $ versionInfo ['optionalRemoved ' ] === '' ) {
187
+ $ versionInfo ['optionalRemoved ' ] = $ version ;
188
+ $ versionInfo ['error ' ] = true ;
189
+ } elseif ($ versionInfo ['optionalDeprecated ' ] === '' ) {
190
+ $ versionInfo ['optionalDeprecated ' ] = $ version ;
191
+ }
192
+ }
193
+ }
194
+
195
+ return $ versionInfo ;
196
+ }
197
+
198
+ /**
199
+ * Handle the retrieval of relevant information and - if necessary - throwing of an
200
+ * error for a matched item.
201
+ *
202
+ * @since 10.0.0 This was previously handled via a similar method in the `AbstractComplexVersionSniff`.
203
+ *
204
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
205
+ * @param int $stackPtr The position of the relevant token in
206
+ * the stack.
207
+ * @param array $itemInfo Base information about the item.
208
+ *
209
+ * @return void
210
+ */
211
+ protected function handleFeature (File $ phpcsFile , $ stackPtr , array $ itemInfo )
212
+ {
213
+ $ itemArray = $ this ->targetFunctions [$ itemInfo ['nameLc ' ]][$ itemInfo ['offset ' ]];
214
+ $ versionInfo = $ this ->getVersionInfo ($ itemArray );
215
+
216
+ if (empty ($ versionInfo ['optionalDeprecated ' ]) && empty ($ versionInfo ['optionalRemoved ' ])) {
217
+ return ;
218
+ }
219
+
220
+ $ this ->addError ($ phpcsFile , $ stackPtr , $ itemInfo , $ itemArray , $ versionInfo );
221
+ }
222
+
223
+ /**
224
+ * Generates the error or warning for this item.
225
+ *
226
+ * @since 8.1.0
227
+ * @since 10.0.0 - Method visibility changed from `public` to `protected`.
228
+ * - Introduced $itemArray parameter.
229
+ * - Renamed the last parameter from `$errorInfo` to `$versionInfo`.
230
+ *
231
+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
232
+ * @param int $stackPtr The position of the relevant token in
233
+ * the stack.
234
+ * @param array $itemInfo Base information about the item.
235
+ * @param array $itemArray The sub-array with all the details about
236
+ * this item.
237
+ * @param array $versionInfo Array with detail (version) information
238
+ * relevant to the item.
239
+ *
240
+ * @return void
241
+ */
242
+ protected function addError (File $ phpcsFile , $ stackPtr , array $ itemInfo , array $ itemArray , array $ versionInfo )
243
+ {
244
+ $ error = 'The "%s" parameter for function %s() is missing. Passing this parameter is no longer optional. The optional nature of the parameter is ' ;
245
+ $ errorCode = $ this ->stringToErrorCode ($ itemInfo ['name ' ] . '_ ' . $ itemArray ['name ' ]);
246
+ $ codeSuffix = '' ;
247
+ $ data = [
248
+ $ itemArray ['name ' ],
249
+ $ itemInfo ['name ' ],
250
+ ];
251
+
252
+ if ($ versionInfo ['optionalDeprecated ' ] !== '' ) {
253
+ $ error .= 'deprecated since PHP %s and ' ;
254
+ $ codeSuffix = 'Soft ' ;
255
+ $ data [] = $ versionInfo ['optionalDeprecated ' ];
256
+ }
257
+
258
+ if ($ versionInfo ['optionalRemoved ' ] !== '' ) {
259
+ $ error .= 'removed since PHP %s and ' ;
260
+ $ codeSuffix = 'Hard ' ;
261
+ $ data [] = $ versionInfo ['optionalRemoved ' ];
262
+ }
263
+
264
+ // Remove the last 'and' from the message.
265
+ $ error = \substr ($ error , 0 , (\strlen ($ error ) - 5 ));
266
+ $ errorCode .= $ codeSuffix . 'Required ' ;
267
+
268
+ $ this ->addMessage ($ phpcsFile , $ error , $ stackPtr , $ versionInfo ['error ' ], $ errorCode , $ data );
269
+ }
270
+ }
0 commit comments