Reputation: 483
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>
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');
}
?>
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
and here's the file:
-rwxrwxrwx 1 pi pi 0 Mar 29 16:43 /tmp/data.txt
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
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?
Upvotes: 8
Views: 233
Reputation: 162
You may use the PHP function file_put_contents
which takes a filename and data you wish to write and writes it to a file. In your case, this could be some form of the HTML POST data sent. In the solution below I have encoded the values in JSON as it is a good representation of key/value pairs associated with forms like this. This could be done in any other form that suits your needs.
I have chosen to write the file to the same folder as the PHP file containing the form. This is not realistic in a production environment however, it shows that the PHP works saving the file.
If the location of the file is the problem then permissions is where you should be looking as that should be the only thing causing it to not write when using the below method. I have entered values in the form fields for easier testing but the placeholders should stay in a production environment and the values removed.
In any environment there should be both client and server-side validation taking place. I have removed the validation for simplicity and that it is off-topic but each value should be tested and filtered prior to anything else being done with it. Anyway, here is the working example combined into one file:
<?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>
Furthermore, submitting a form you should have the enctype attribute set on the form tag. If you are uploading a file <input type="file">
then it must be set to multipart/form-data
. This allows for the binary sending of data via the HTTP Post.
<form enctype="multipart/form-data" action="..." id="....">
Additionally, as MDN points out in its documentation, you may add an attribute to the <input>
or button
elements.:
"... or n the
formenctype
attribute of the<input>
or<button>
elements."
In all cases you should be specifying the enctype of the form including during asynchronous requests using XMLHttpRequest
.
Upvotes: -1