Skip to content

Hooks/RestrictedHooks: various sniff improvements #852

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 21, 2025

Conversation

jrfnl
Copy link
Collaborator

@jrfnl jrfnl commented Jul 21, 2025

Hooks/RestrictedHooks: improve the tests

  • Test against false positives for calls to methods or namespaced function calls.
  • Test against false positives for attribute class using the same name as the function.
  • Ensure function import use statements are not flagged. We're not interested in those.
  • Ensure PHP 8.1 first class callables are not flagged. We cannot determine the hook name for those.
  • Document how dynamic hook names are handled.
  • Add some more variations to the pre-existing tests:
    • Non-lowercase function call(s).
    • Fully qualified function calls.
    • Use PHP 7.3+ trailing comma's in a few function calls.

Hooks/RestrictedHooks: bug fix - disregard comments in hook name parameter

The PHPCSUtils PassedParameters::getParameters() return value includes a 'clean' array index, which contains the contents of the parameter stripped of surrounding whitespace and comments.

The RestrictedHooks sniff uses the parameter contents to compare against a list of restricted hooks, but would break if the hook name parameter would contain a comment.

Fixed now. Includes test.

Hooks/RestrictedHooks: add support for handling PHP 8.0+ function calls using named parameters

Includes minor efficiency fix by moving the call to normalize_hook_name_from_parameter() out of the loops.

Includes tests.

Hooks/RestrictedHooks: use PHPCSUtils MessageHelper

Hooks/RestrictedHooks: bug fix - hook names are case-sensitive

WordPress stores hooknames as provided as an array key and uses isset() to determine whether to run an action/filter.

It doesn't "normalize" hook names to lowercase, so neither should the sniff.

Includes test to safeguard the fix.

Refs:

Hooks/RestrictedHooks: bug fix - false negatives due to incorrect token walking

The $parameter['start'] key points to the very first token in the parameter, irrespective of what that token is (whitespace, comment, functional).
The $parameter['end'] key points to the very last token in the parameter, irrespective of what that token is (whitespace, comment, functional).

This means that when walking the tokens of a parameter both the 'start' token, as well as the 'end' token need to be examined.

The code in the normalize_hook_name_from_parameter() method handling concatenated hook names did not do this correctly, leading to false negatives.

Fixed now.

Includes test + updating some pre-existing tests which clearly had a trailing space in the parameter to get the test to pass despite this bug.

Hooks/RestrictedHooks: bug fix - false positives for dynamic name with concatenation

As things were, the code in the normalize_hook_name_from_parameter() method would check for concatenation and if found, would gather the contents of all T_CONSTANT_ENCAPSED_STRING tokens found in the parameter.

However, this presumes all the concatenated parts are text strings and disregards the possibility that variables would be concatenated in, leading to false positives.

Fixed now.

Includes tests.

Hooks/RestrictedHooks: bug fix - quotes within a hook name would be stripped

Use PHPCSUtils TextStrings::stripQuotes() to only strip surrounding quotes and not remove any quotes potentially present within the text string.

Includes tests.

Closes #522

jrfnl added 8 commits July 21, 2025 19:11
* Test against false positives for calls to methods or namespaced function calls.
* Test against false positives for attribute class using the same name as the function.
* Ensure function import `use` statements are not flagged. We're not interested in those.
* Ensure PHP 8.1 first class callables are not flagged. We cannot determine the hook name for those.
* Document how dynamic hook names are handled.
* Add some more variations to the pre-existing tests:
    - Non-lowercase function call(s).
    - Fully qualified function calls.
    - Use PHP 7.3+ trailing comma's in a few function calls.
…meter

The PHPCSUtils `PassedParameters::getParameters()` return value includes a `'clean'` array index, which contains the contents of the parameter stripped of surrounding whitespace and comments.

The `RestrictedHooks` sniff uses the parameter contents to compare against a list of restricted hooks, but would break if the hook name parameter would contain a comment.

Fixed now. Includes test.
…ls using named parameters

Includes minor efficiency fix by moving the call to `normalize_hook_name_from_parameter()` out of the loops.

Includes tests.
WordPress stores hooknames as provided as an array key and uses `isset()` to determine whether to run an action/filter.

It doesn't "normalize" hook names to lowercase, so neither should the sniff.

Includes test to safeguard the fix.

Refs:
* https://github.com/WordPress/wordpress-develop/blob/c726220a21d13fdb5409372b652c9460c59ce1db/src/wp-includes/plugin.php#L124-L126
* https://github.com/WordPress/wordpress-develop/blob/c726220a21d13fdb5409372b652c9460c59ce1db/src/wp-includes/plugin.php#L173-L210
…en walking

The `$parameter['start']` key points to the very first token in the parameter, irrespective of what that token is (whitespace, comment, functional).
The `$parameter['end']` key points to the very last token in the parameter, irrespective of what that token is (whitespace, comment, functional).

This means that when walking the tokens of a parameter both the 'start' token, as well as the 'end' token need to be examined.

The code in the `normalize_hook_name_from_parameter()` method handling concatenated hook names did not do this correctly, leading to false negatives.

Fixed now.

Includes test + updating some pre-existing tests which clearly had a trailing space in the parameter to get the test to pass despite this bug.
…h concatenation

As things were, the code in the `normalize_hook_name_from_parameter()` method would check for concatenation and if found, would gather the contents of all `T_CONSTANT_ENCAPSED_STRING` tokens found in the parameter.

However, this presumes all the concatenated parts are text strings and disregards the possibility that variables would be concatenated in, leading to false positives.

Fixed now.

Includes tests.
…tripped

Use PHPCSUtils `TextStrings::stripQuotes()` to only strip surrounding quotes and not remove any quotes potentially present _within_ the text string.

Includes tests.
@jrfnl jrfnl added this to the 3.1.0 milestone Jul 21, 2025
@jrfnl jrfnl requested a review from a team as a code owner July 21, 2025 17:37
@jrfnl
Copy link
Collaborator Author

jrfnl commented Jul 21, 2025

Note: It could be considered to also sniff for parameter contents provided as nowdoc or heredoc without interpolation, but I expect that will rarely, if ever, be used, so I deemed updating the sniff to go that far over the top, especially considering this is not a security sniff.

@GaryJones GaryJones merged commit a30da72 into develop Jul 21, 2025
42 checks passed
@GaryJones GaryJones deleted the feature/hooks-restrictedhooks-sniff-review branch July 21, 2025 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Review the WordPressVIPMinimum.Hooks.RestrictedHooks sniff
2 participants