Skip to content

Commit 453e734

Browse files
committed
Added (user) noop checks. Added class checks. Removed debug code.
1 parent 7499f22 commit 453e734

1 file changed

Lines changed: 120 additions & 48 deletions

File tree

TSF/Sniffs/Performance/OpcodesSniff.php

Lines changed: 120 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
*
2727
* PHP version ^7.0.0
2828
*
29-
* @since TSF 1.0.0
29+
* @since 1.0.0
3030
*/
3131
class OpcodesSniff extends Sniff {
3232

3333
/**
3434
* @link <https://github.com/php/php-src/blob/PHP-7.4/Zend/zend_compile.c#L3750-L3829>
35-
* @var string[] $funcs
35+
* @var string[] $opFuncs
3636
*/
37-
protected $funcs = [
37+
protected $opFuncs = [
3838
'strlen',
3939
'is_null',
4040
'is_bool',
@@ -71,9 +71,9 @@ class OpcodesSniff extends Sniff {
7171

7272
/**
7373
* @link <https://github.com/php/php-src/blob/php-7.2.6/ext/opcache/Optimizer/pass1_5.c#L411-L416>
74-
* @var string[] $constfuncs
74+
* @var string[] $opConstFuncs
7575
*/
76-
protected $constfuncs = [
76+
protected $opConstFuncs = [
7777
'define',
7878
'function_exists',
7979
'is_callable',
@@ -83,9 +83,35 @@ class OpcodesSniff extends Sniff {
8383
];
8484

8585
/**
86-
* @var string[] $checks
86+
* @var string[] $internalFuncs
87+
* @see register(), there it's populated.
8788
*/
88-
public $checks = [];
89+
protected $internalFuncs = [];
90+
91+
/**
92+
* @var string[] $noopInternal
93+
* @see register(), there it's populated.
94+
*/
95+
protected $noopInternal = [];
96+
97+
/**
98+
* @var string[] $allNoopChecks
99+
* @see register(), there it's populated.
100+
*/
101+
protected $allNoopChecks = [];
102+
103+
/**
104+
* @var string[] $opChecks Internal functions that should be checked for opcode improvements (e.g., 'array_keys').
105+
* This should not yield a benefit, though.
106+
* @since 1.1.0
107+
*/
108+
public $opChecks = [];
109+
110+
/**
111+
* @var string[] $noopChecks User functions that should not be checked for opcode improvements (i.e., your namespaced functions).
112+
* @since 1.1.0
113+
*/
114+
public $userNoopChecks = [];
89115

90116
/**
91117
* Returns an array of tokens this test wants to listen for.
@@ -96,12 +122,25 @@ class OpcodesSniff extends Sniff {
96122
*/
97123
public function register() {
98124
// Handle case-insensitivity of function names.
99-
$this->funcs = $this->arrayKeysToLowercase( $this->funcs );
100-
$this->constfuncs = $this->arrayKeysToLowercase( $this->constfuncs );
101-
$this->checks = $this->arrayKeysToLowercase( $this->checks );
125+
$this->opFuncs = array_map( 'strtolower', $this->opFuncs );
126+
$this->opConstFuncs = array_map( 'strtolower', $this->opConstFuncs );
127+
$this->opChecks = array_map( 'strtolower', $this->opChecks );
128+
129+
// Combine opChecks.
130+
$this->opChecks = array_unique( array_merge( $this->opChecks, $this->opFuncs, $this->opConstFuncs ) );
102131

103-
// Combine checks.
104-
$this->checks = array_merge( $this->checks, $this->funcs, $this->constfuncs );
132+
$this->internalFuncs = array_map( 'strtolower', get_defined_functions()['internal'] );
133+
134+
$this->noopInternal = array_diff(
135+
$this->internalFuncs,
136+
$this->opFuncs,
137+
$this->opConstFuncs
138+
);
139+
140+
$this->userNoopChecks = array_map( 'strtolower', $this->userNoopChecks );
141+
142+
// Combine noopChecks.
143+
$this->allNoopChecks = array_unique( array_merge( $this->noopInternal, $this->userNoopChecks ) );
105144

106145
$targets = [
107146
\T_STRING,
@@ -142,50 +181,63 @@ public function process( File $phpcsFile, $stackPtr ) {
142181
*
143182
* @param int $stackPtr The position of the current token in
144183
* the stack passed in $tokens.
145-
*
146-
* @return bool
147184
*/
148185
protected function process_namespaces( $stackPtr ) {
149186

150187
$function = $this->tokens[ $stackPtr ]['content'];
151188
$functionLc = strtolower( $function );
152189

153190
if ( '' !== $this->determineNamespace( $this->phpcsFile, $stackPtr ) ) {
154-
if ( in_array( $functionLc, $this->checks, true ) && false === $this->is_token_globally_namespaced( $stackPtr ) ) {
155-
$this->phpcsFile->addWarning(
156-
'Function %s should have a leading namespace separator (`\`).',
157-
$stackPtr,
158-
'ShouldHaveNamespaceEscape',
159-
["{$function}()"]
160-
);
191+
if ( in_array( $functionLc, $this->opChecks, true ) ) {
192+
if ( false === $this->is_token_globally_namespaced( $stackPtr ) ) {
193+
194+
$warning = $this->is_object_creation( $stackPtr )
195+
? 'Class %s should have a leading namespace separator `\`.'
196+
: 'Function %s should have a leading namespace separator `\`.';
197+
198+
$this->phpcsFile->addWarning(
199+
$warning,
200+
$stackPtr,
201+
'ShouldHaveNamespaceEscape',
202+
[ "{$function}()" ]
203+
);
204+
}
205+
} elseif ( ! in_array( $functionLc, $this->internalFuncs, true ) && ! in_array( $functionLc, $this->allNoopChecks, true ) ) {
206+
if ( false === $this->is_token_globally_namespaced( $stackPtr ) ) {
207+
208+
$warning = $this->is_object_creation( $stackPtr )
209+
? 'Class %s should have a leading namespace separator `\`.'
210+
: 'Function %s should have a leading namespace separator `\`.';
211+
212+
$this->phpcsFile->addWarning(
213+
$warning,
214+
$stackPtr,
215+
'ShouldHaveNamespaceEscape',
216+
[ "{$function}()" ]
217+
);
218+
}
161219
}
162220
} else {
163221
// When there's no namespace, we're already in the correct scope for the opcode.
164222
// Warn dev that there's a useless NS escape.
165-
if ( true === $this->is_token_globally_namespaced( $stackPtr ) ) {
166-
$this->phpcsFile->addWarning(
167-
'Function %s does not need a leading namespace separator.',
168-
$stackPtr,
169-
'UselessLeadingNamespaceEscape',
170-
["{$function}()"]
171-
);
223+
if ( ! in_array( $functionLc, $this->userNoopChecks, true ) ) {
224+
if ( true === $this->is_token_globally_namespaced( $stackPtr ) ) {
225+
226+
$warning = $this->is_object_creation( $stackPtr )
227+
? 'Class %s should have a leading namespace separator `\`.'
228+
: 'Function %s should have a leading namespace separator `\`.';
229+
230+
$this->phpcsFile->addWarning(
231+
$warning,
232+
$stackPtr,
233+
'UselessLeadingNamespaceEscape',
234+
[ "{$function}()" ]
235+
);
236+
}
172237
}
173238
}
174239
}
175240

176-
/**
177-
* Is the class/function/constant name namespaced or global?
178-
*
179-
* @since 1.0.0
180-
*
181-
* @param string $FQName Fully Qualified name of a class, function etc.
182-
* I.e. should always start with a `\`.
183-
* @return bool True if namespaced, false if global.
184-
*/
185-
public function hasLeadingNamespaceDelimiter( $name ) {
186-
return 0 === strpos( $name, '\\' );
187-
}
188-
189241
/**
190242
* Verify is the current token is a function call.
191243
*
@@ -208,10 +260,6 @@ protected function is_targetted_token( $stackPtr ) {
208260
return false;
209261
}
210262

211-
// if ( $this->is_token_globally_namespaced( $stackPtr ) === true ) {
212-
// return false;
213-
// }
214-
215263
$prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true );
216264
if ( false !== $prev ) {
217265
// Skip sniffing on function, class definitions or for function aliases in use statements.
@@ -262,6 +310,7 @@ protected function is_targetted_token( $stackPtr ) {
262310
* @return bool
263311
*/
264312
protected function is_class_object_call( $stackPtr ) {
313+
265314
$before = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true, null, true );
266315

267316
if ( false === $before ) {
@@ -277,6 +326,31 @@ protected function is_class_object_call( $stackPtr ) {
277326
return true;
278327
}
279328

329+
/**
330+
* Check if a particular token is a (static or non-static) call to an object.
331+
*
332+
* @since 1.1.0
333+
*
334+
* @param int $stackPtr The position of the current token in
335+
* the stack passed in $tokens.
336+
*
337+
* @return bool
338+
*/
339+
protected function is_object_creation( $stackPtr ) {
340+
341+
$before = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 2 ), null, true, null, true );
342+
343+
if ( false === $before ) {
344+
return false;
345+
}
346+
347+
if ( \T_NEW !== $this->tokens[ $before ]['code'] ) {
348+
return false;
349+
}
350+
351+
return true;
352+
}
353+
280354
/**
281355
* Check if a particular token is prefixed with a namespace.
282356
*
@@ -292,6 +366,7 @@ protected function is_class_object_call( $stackPtr ) {
292366
* @return bool
293367
*/
294368
protected function is_token_globally_namespaced( $stackPtr ) {
369+
295370
$prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true, null, true );
296371

297372
if ( false === $prev ) {
@@ -309,9 +384,6 @@ protected function is_token_globally_namespaced( $stackPtr ) {
309384

310385
$function = $this->tokens[ $stackPtr ]['content'];
311386
$functionLc = strtolower( $function );
312-
if ( '_init_tsf' === $functionLc ) {
313-
throw new \Exception( serialize( $this->tokens[ $before_prev ] ) );
314-
}
315387

316388
// This is an actual non-global namespace lookup.
317389
if ( \T_STRING === $this->tokens[ $before_prev ]['code']

0 commit comments

Comments
 (0)