PHP:ftp_get()函数报错时为何返回true?
ftp_get() return true despite "Failed to open file" warning for .filepart files? Let's break down this issue by examining the PHP FTP extension source code you referenced:
Key Observations from the Source Code
The behavior you're seeing hinges on two critical factors: PHP version differences and FTP server handling of incomplete files:
1. PHP 5.6 and Earlier: Missing Error Checks
In older PHP versions (like 5.6), the ftp_get() function has a flaw in its error-handling logic. Here's the relevant snippet:
if (php_stream_copy_to_fd(stream, fd, mode) == FAILURE) { php_error_docref(NULL, E_WARNING, "Transfer failed"); } php_stream_close(stream); VCWD_CLOSE(fd); RETURN_TRUE;
Notice that even if the file transfer fails (e.g., the remote .filepart file is locked or incomplete), the function unconditionally returns true after triggering a warning. There's no check for the final FTP server response code (like 226, which confirms successful transfer completion).
2. FTP Server Behavior for Incomplete Files
When dealing with an unfinished .filepart file (still being uploaded), some FTP servers may:
- Accept the
RETRcommand (returning a 1xx/2xx success code, so PHP thinks the command worked) - Fail to read the full file content (due to locks or partial data)
- Send a response message like "Failed to open file" which PHP surfaces as a warning
Since PHP only checks if the initial RETR command succeeded (not the final transfer status), it returns true even though the file wasn't properly downloaded.
3. Local File Edge Cases
In rare scenarios, the "Failed to open file" warning could come from local file system issues (e.g., sudden permission changes after opening the file). However, in the source code, a failed local file open would immediately return false—so this doesn't explain why you're getting true. The far more likely culprit is the combination of old PHP behavior and server-side handling of incomplete files.
Solution
To fix this:
- Upgrade PHP: Modern PHP versions (7.0+) added checks for the final FTP response code (226) and properly return
falseif the transfer fails:if (ftp_get_last_response_code(&ftp) != 226) { php_error_docref(NULL, E_WARNING, "%s", ftp.msg); retval = FAILURE; } if (retval == FAILURE) { RETURN_FALSE; } RETURN_TRUE; - Prevent downloading incomplete files: Before calling
ftp_get(), useftp_nlist()orftp_size()to verify the remote file isn't a.filepart(or check if its size is stable, indicating upload completion). - Add manual validation: After downloading, check if the local file size matches the remote file size (using
ftp_size()) to confirm the transfer succeeded.
内容的提问来源于stack exchange,提问作者Lars Moelleken

