Skip to content

Fix false positive: exclude fclose from AlternativeFunctions sniff#1406

Open
ikamal7 wants to merge 7 commits into
WordPress:trunkfrom
ikamal7:fix/1377-fclose-php-output-false-positive
Open

Fix false positive: exclude fclose from AlternativeFunctions sniff#1406
ikamal7 wants to merge 7 commits into
WordPress:trunkfrom
ikamal7:fix/1377-fclose-php-output-false-positive

Conversation

@ikamal7

@ikamal7 ikamal7 commented Jul 23, 2026

Copy link
Copy Markdown

What?

Closes #1377

Exclude WordPress.WP.AlternativeFunctions.file_system_operations_fclose from the plugin-check ruleset, following the same pattern as the existing file_put_contents exclusion.

Why?

fclose() is always paired with a preceding fopen() or similar file-opening function. When used with php://output (a local data stream wrapper), the fopen call is already handled correctly by WPCS 3.4.0's built-in allowed_local_streams check — but there is no corresponding case 'fclose' in the sniff's switch statement, so fclose still gets flagged.

Since fclose without a flagged fopen is always noise (if the fopen was inappropriate, it's already flagged separately), excluding it removes this false positive with no loss of meaningful coverage.

How?

Added one line to phpcs-rulesets/plugin-check.ruleset.xml:

<exclude name="WordPress.WP.AlternativeFunctions.file_system_operations_fclose"/>

Testing Instructions

  1. Check out this branch
  2. Run phpunit --filter="Plugin_Review_PHPCS_Check_Tests"
  3. Verify the new assertion passes: fclose error should NOT appear in results

AI Usage Disclosure

  • This PR includes AI-assisted code or content
  • AI tools were used for investigation (tracing the sniff code, finding the root cause) and generating the fix implementation.

Screenshots or screencast

N/A

Open WordPress Playground Preview

The WordPress.WP.AlternativeFunctions.file_system_operations_fclose sniff
flags fclose() even when paired with fopen('php://output') which is already
allowed as a local data stream wrapper. Since fclose is always paired with
a preceding fopen, flagging it separately is redundant noise - if the fopen
was problematic, it's already flagged.

This follows the same pattern as the existing file_put_contents exclusion.
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: ikamal7 <ikamal@git.wordpress.org>
Co-authored-by: mukeshpanchal27 <mukesh27@git.wordpress.org>
Co-authored-by: davidperezgar <davidperez@git.wordpress.org>
Co-authored-by: abdullahkaludi <abdullah17@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@ikamal7

ikamal7 commented Jul 24, 2026

Copy link
Copy Markdown
Author

@mukeshpanchal27 could you please review and merge this PR when you get a chance? 🙏

@davidperezgar

Copy link
Copy Markdown
Member

@ikamal7 the tests are failing. You need to fix them before we can review it.

@ikamal7

ikamal7 commented Jul 24, 2026

Copy link
Copy Markdown
Author

Fixed the test assertion — the previous version tried to access $errors['load.php'][40] which no longer exists since all errors on that line are excluded. Now it uses wp_list_filter across the full file's errors instead. CI should pass now.

@davidperezgar

Copy link
Copy Markdown
Member

Same problem... ;)

@ikamal7

ikamal7 commented Jul 25, 2026

Copy link
Copy Markdown
Author

Test is fixed now. The root cause was that adding fclose(); to the test fixture shifted all subsequent line numbers by +1 in the fixture file, but the test assertions still referenced the old line numbers. All references have been updated to match.

@ikamal7

ikamal7 commented Jul 25, 2026

Copy link
Copy Markdown
Author

@davidperezgar all tests should be passing now — the lint formatting issue and the shifted line numbers are both fixed. Could you take another look? 🙏

@davidperezgar

Copy link
Copy Markdown
Member

[P2] With Codex’s help, I noticed that this assertion does not actually verify that the fclose sniff is excluded. $errors['load.php'] is keyed by line and then column, while wp_list_filter() only filters the immediate items. None of those line arrays has a top-level code, so this returns zero even if PHPCS reports the fclose sniff again.

Could we flatten the file’s messages before filtering, or assert directly against the line 40/column 1 entry, so the regression test actually protects this exclusion?

…umn 1

Previously the assertion used wp_list_filter() on the entire file's errors array, which never found 'code' keys since they are nested under [line][column]. This meant the test always passed even if the fclose sniff was still being reported.

Now drills down to the specific line/column like the other similar assertions in the same test, so the regression test actually protects the exclusion.
@ikamal7

ikamal7 commented Jul 26, 2026

Copy link
Copy Markdown
Author

Thanks @davidperezgar for catching this — you were absolutely right.

The assertion at line 64 used $errors['load.php'] directly in wp_list_filter(), which always returned empty since the code keys are nested under [line][column]. The test would pass even if the fclose sniff exclusion wasn't working.

Fix: Changed to $errors['load.php'][40][1] to drill down to the specific line/column where fclose() appears in the test fixture, matching the pattern all other similar assertions use.

Test result: OK (2 tests, 58 assertions) — the Plugin_Review_PHPCS_Check_Tests passes cleanly on PHP 8.4 / WP 6.7.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Plugin Check’s bundled PHPCS ruleset to prevent a known false positive from WPCS’s WordPress.WP.AlternativeFunctions sniff by excluding file_system_operations_fclose, and adjusts PHPUnit fixtures/assertions to verify the new behavior.

Changes:

  • Exclude WordPress.WP.AlternativeFunctions.file_system_operations_fclose in the Plugin Check PHPCS ruleset.
  • Update the PHPCS-check PHPUnit fixture (load.php) and test assertions to ensure fclose is not reported.
  • Add a manual test fixture plugin to reproduce/verify the original php://output scenario.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
phpcs-rulesets/plugin-check.ruleset.xml Excludes the file_system_operations_fclose AlternativeFunctions error code to remove the false positive.
tests/phpunit/testdata/plugins/test-plugin-review-phpcs-errors/load.php Adds a fclose() call to the PHPCS error fixture so tests can assert the exclusion.
tests/phpunit/tests/Checker/Checks/Plugin_Review_PHPCS_Check_Tests.php Updates expected line numbers and adds an assertion that fclose is not reported.
test-fixtures-for-manual-testing/fclose-php-output-test.php Adds a manual repro plugin for the php://output use case.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

$this->assertSame( $forbidden_found, $errors['load.php'][58][1][0]['code'] );

// Check for Generic.PHP.ForbiddenFunctions.wp_get_widget_defaultsFound error on Line no 59 and column no at 1.
// Check for Generic.PHP.ForbiddenFunctions.wp_get_sidebars_widgetsFound error on Line no 60 and column no at 1.
Comment on lines +11 to +14
// This fclose() call on php://output is a common WordPress pattern
// used to end output buffering. It should NOT trigger the
// WordPress.WP.AlternativeFunctions.file_system_operations_fclose sniff
// because php://output is not a file system resource.
fclose( $handle );
}

// This fclose() on a real file SHOULD still trigger the sniff.
Comment on lines +2 to +6
/**
* Plugin Name: Fclose PHP Output Test
* Description: A test plugin to verify the Plugin Check plugin's fclose() false positive fix — fclose(php://output) should not trigger an AlternativeFunctions sniff.
* Version: 1.0.0
* Requires PHP: 7.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive: WordPress.WP.AlternativeFunctions.file_system_operations_fopen for php://output CSV streaming

4 participants