Skip to content

Filesystem API: Remove the temporary file when FTPext put_contents() fails - #14891

Open
bluefairydevelopment wants to merge 2 commits into
WordPress:trunkfrom
bluefairydevelopment:fix/64519-ftpext-put-contents-temp-file
Open

bluefairydevelopment wants to merge 2 commits into
WordPress:trunkfrom
bluefairydevelopment:fix/64519-ftpext-put-contents-temp-file

Conversation

@bluefairydevelopment

Copy link
Copy Markdown

WP_Filesystem_FTPext::put_contents() writes the contents to a temporary file from wp_tempnam() and then uploads it with ftp_fput(). When there is no FTP connection ($this->link is null or false), or the connection has been closed, ftp_fput() throws on PHP 8, so the fclose() / unlink() cleanup never runs. As the reporter describes, busy sites can accumulate very large numbers of *.tmp files in the temp directory.

This change:

  • returns false early when there is no connection, matching get_contents(), which already has this check, so no temporary file is created at all;
  • moves the cleanup into a finally block around ftp_fput(), so the temporary file is removed even if the upload throws, for example on a closed connection. The exception still propagates as before.

Tests: new tests/phpunit/tests/filesystem/wpFilesystemFtpext/putContents.php.

  • test_put_contents_without_connection_leaves_no_temp_file (null and false links): returns false and leaves no temporary file.
  • test_put_contents_on_closed_connection_leaves_no_temp_file: opens a real connection to a minimal local FTP responder, closes it, then calls put_contents(). It requires the ftp extension and does not need a real FTP server.

Each part of the fix was checked separately:

  • Trunk: all three tests fail.
  • With only the early return: the closed-connection test still fails.
  • With the full change: all pass, and --group filesystem shows no new failures. Its 20 warnings are the same PHPUnit 10 deprecation notices trunk already has.
  • phpcs is clean.

Not in this PR: WP_Filesystem_FTPext has other methods that use $this->link without a connection check, and the reporter's point that the temporary file name doesn't match the target file comes from wp_tempnam(), which is shared by every filesystem method. Both are left for follow-up to keep this change focused.

Props lucianwpwhite.

Trac ticket: https://core.trac.wordpress.org/ticket/64519

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5.5
Used for: reading the ticket and the code, the fix, and the tests, including confirming that each test fails without the corresponding part of the fix. Submitted by Blue Fairy Development, who are responsible for the change.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

…fails.

WP_Filesystem_FTPext::put_contents() wrote a temporary file before
uploading it. Without an FTP connection, or on a closed one, ftp_fput()
throws, so the temporary file was never removed and accumulated in the
temp directory. Return early without a connection, as get_contents()
already does, and remove the temporary file in a finally block.

Props lucianwpwhite.
See #64519.
@github-actions

github-actions Bot commented Oct 26, 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.

Core Committers: Use this line as a base for the props when committing in SVN:

Props bluefairydevelopment.

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


Props Bot commands

You can trigger Props Bot actions by commenting on this PR:

  • @propsbot add <username> to give props to a GitHub user. Requires write access.
  • @propsbot remove <username> to leave a user out of the props. Requires write access.
  • @propsbot refresh to refresh this list. Adding the props-bot label works too.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

Before PHP 8.1 an FTP connection is a resource, and using a closed one only
raises a warning instead of throwing an Error, so the test failed on PHP 7.4.

See #64519.
@bluefairydevelopment

Copy link
Copy Markdown
Author

The closed-connection test failed on PHP 7.4: before PHP 8.1 an FTP connection is a resource, and using a closed one raises a warning rather than throwing an Error. That test now requires PHP 8.1 (8c04ab6). The early-return tests still run on all versions.

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.

1 participant