Why does PHP think it's writing to a file but it isn't?

Rog*_*ohn 8 php file-handling raspberry-pi

I've got an HTML form that has a PHP pgm to write the results to a file. I've basically copied it from The Web. It's not rocket surgery. It's running with PHP 7 on a Raspberry Pi with the most recent Raspbian. The PHP pgm reports that the file is writeable, that it succeeded, and so on, but the file remains empty. I've changed the permissions to 777 (for testing) and that doesn't help. I'm stumped. Here is the HTML:

<!DOCTYPE html>
<html>
<head>
<title>Update</title>
</head>
<body>
<FORM NAME ="form1" METHOD ="POST" action="filewrite.php">
<INPUT TYPE = "TEXT" VALUE ="memory" NAME="memory">
<INPUT TYPE = "TEXT" VALUE ="desc"   NAME="desc">
<INPUT TYPE = "TEXT" VALUE ="comm"   NAME="comm">
<INPUT TYPE = "TEXT" VALUE ="reset"  NAME="reset">
<INPUT TYPE = "Submit" Name = "Submit1" VALUE = "Create">
</FORM>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

Here's the PHP (in filewrite.php):

<?php
ini_set('display_errors', true);

if(isset($_POST['memory']) && isset($_POST['desc']) && isset($_POST['comm']) && isset($_POST['reset']))
{
#    $data = $_POST['memory'] . '\t' . $_POST['desc']  . '\t' . $_POST['comm']  . '\t' . $_POST['reset'] . "\n";
    $data = "testing";
    $file = "/tmp/data.txt";

var_dump($_POST);

    if (is_writable($file)) {
       echo nl2br("\n\nThe file is writable\n\n");
    } else {
       echo nl2br("The file is not writable\n\n");
    }

    touch($file);
    $ret = file_put_contents($file, $data, LOCK_EX);
    if($ret === false)
    {
        die('There was an error writing this file');
    }
    else
    {
        echo nl2br("$ret bytes written to file\n");
        echo nl2br("data = $data\n");
        echo nl2br("file = $file\n");
    }
}
else
{
   die('no post data to process');
}

?>
Run Code Online (Sandbox Code Playgroud)

Here's what I get as output:

array(5) { ["memory"]=> string(4) "fred" ["desc"]=> string(6) "barney" ["comm"]=> string(5) "wilma" ["reset"]=> string(5) "betty" ["Submit1"]=> string(6) "Create" }

The file is writable

7 bytes written to file
data = testing
file = /tmp/data.txt
Run Code Online (Sandbox Code Playgroud)

and here's the file:

-rwxrwxrwx 1 pi pi 0 Mar 29 16:43 /tmp/data.txt
Run Code Online (Sandbox Code Playgroud)

I tried it without the file existing and with it there. At first I was trying to write the form data to the file but then switched to just trying to write "testing". I've tried other directories for the file, but they (as expected) failed due to permissions.

Edit: When I tried writing the file to /home/pi (which I expected to fail as the webserver is www-data), I got this error in /var/log/apache2/error.log:

[Sun Mar 29 17:04:24.648150 2020] [php7:warn] [pid 12075] [client fe80::cceb:ba3c:da1d:6b2a:53052] PHP Warning:  file_put_contents(/home/pi/data.txt): failed to open stream: Permission denied in /var/www/html/filewrite.php on line 19, referer: http://devberry-pi.local/rwupdate.html
Run Code Online (Sandbox Code Playgroud)

Setting it back to /tmp/data.txt cleared that error (and no other errors are generated.)

Edit 2: I copied a 6k file to /tmp to make sure there is enough space and that worked fine. I'm only writing 7 bytes at the moment ("testing") and really only want to write < 80b once I get it working. Also, this is a new installation and only has a few small pgms (that I've written) on it.

Edit 3: Brand new card, brand new install, same results. 8^( I think my next step is a different Raspberry Pi, though I'm not sure how the hardware could be the issue. Note: I can create files in /tmp from the command line.

Ooh, new development -- I created a new folder, /home/www, set its permissions to 777, and it does successfully create the file there. The permissions for /tmp are drwxrwxrwt and it's owned by root/root. The webserver/php user is www-data. I can use that as a work-around, but it's not ideal in my book. Is there a better place than /tmp to put stuff that different users/pgms need to have access to?

Any thoughts? What should I be looking at?

sat*_*vay -1

您可以使用 PHP 函数file_put_contents,该函数获取您想要写入的文件名和数据并将其写入文件。在您的情况下,这可能是发送的某种形式的 HTML POST 数据。在下面的解决方案中,我已将值编码为 JSON,因为它很好地表示了与此类表单关联的键/值对。这可以通过适合您需要的任何其他形式来完成。

我选择将该文件写入与包含表单的 PHP 文件相同的文件夹中。然而,这在生产环境中并不现实,它表明 PHP 可以保存文件。

如果文件的位置是问题所在,那么权限就是您应该查看的位置,因为这应该是使用以下方法时导致文件无法写入的唯一原因。我已在表单字段中输入值以便于测试,但占位符应保留在生产环境中并删除值。

在任何环境中都应该同时进行客户端和服务器端验证。为了简单起见,我删除了验证,并且它是偏离主题的,但每个值都应该在执行任何其他操作之前进行测试和过滤。无论如何,这是合并到一个文件中的工作示例:

<?php
/**
 * Check for submission of form.
 */
$nl="<br>";
if (isset($_POST['memory']) && isset($_POST['desc']) && isset($_POST['comm']) && isset($_POST['reset'])) {
    try {
        $data = json_encode($_POST, JSON_PRETTY_PRINT);
        $file = "data.txt";
        if ( file_put_contents ($file, $data) ) {
            $msg = "File successfully saved.";
        } else {
            $msg = "Error saving file";
        }            
    } catch (Exception $e) {
        $msg = "Error saving file:" .$nl.$nl . $e->getMessage() . $nl.$nl . $e-getTrace();
    }
}
?>
<!doctype html>
<html lang="en_US">
<head>
<meta charset='utf-8'>
<title>Example: Save File</title>
</head>
<body>
<?php if (isset($msg)){ echo '< style="color:#100">'.$msg.'</p>'; } ?>
<form name="form1" method="post" enctype="multipart/form-data">
<input type="text" placeholder="memory" name="memory" value="mem">
<input type="text" placeholder="desc"   name="desc" value="something">
<input type="text" placeholder="comm"   name="comm" value="more data">
<input type="text" placeholder="reset"  name="reset" value="reset">
<input type="Submit" Name = "Submit1" value="Create">
</form>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

此外,提交表单时,您应该在表单标记上设置 enctype 属性。如果您要上传文件,<input type="file">则必须将其设置为multipart/form-data. 这允许通过 HTTP Post 以二进制方式发送数据。

<form enctype="multipart/form-data" action="..." id="....">
Run Code Online (Sandbox Code Playgroud)

<input>此外,正如 MDN 在其文档中指出的那样,您可以向或元素添加属性button

“... or n 或元素formenctype的属性。”<input><button>

在所有情况下,您都应该指定表单的 enctype,包括在异步请求期间使用XMLHttpRequest.