Why would file_put_contents refuse to work for the following code?
$f = file_put_contents("files/r.js", "Some text here");
if ($f) print 1;
else print 0;
asked Jan 2, 2011 at 4:35
stevesteve
3,8487 gold badges34 silver badges49 bronze badges
5
It could be a permission problem
Is the directory /files
chmoded to 777? sometimes php won’t allow you to access directories if they don’t have enough permission. I don’t know about blank errors though.
Try to see if it has enough permissions, if not, then set it to 777 and try it.
answered Jan 2, 2011 at 4:42
Famver TagsFamver Tags
1,9882 gold badges16 silver badges18 bronze badges
8
Are you using the full path on the filesystem or are you trying to use the URI? I think this PHP function expects you to give the path as the file is found on the filesystem.
Relative paths should be okay though.
You may need to make sure the file exists and that it’s permissions are set to 777. Sometimes I’ve found that it’s not enough to just have the directory permissions set to 777 but the file must also already exist.
answered Jan 2, 2011 at 4:45
jamesmortensenjamesmortensen
33.5k11 gold badges99 silver badges120 bronze badges
2
It is because of SELINUX.
You can mark the folder as of type httpd_sys_rw_content_t with this command to enable writing to this folder.
semanage fcontext -a -t httpd_sys_rw_content_t '/files(/.*)?'; restorecon -RF '/files/'
answered Feb 4, 2020 at 21:49
We’ve experienced this, requiring a workaround (regardless of method, permissions and anything else mentioned here). When all other fixes failed, we have found it can be linked to restrictions created by SELinux.
answered Jul 18, 2014 at 9:55
If you’re using windows the following solution worked perfectly for me on Windows 10 running php 5.5.38
If you’re having this problem on Windows/IIS try the following:
- Go to the Folder that you are trying to write to and right click it, then select properties.
- Select the Security Tab
- Click Edit
- Click Add
- Click Advanced
- Click Find Now
- From the list of User select IUSR and click OK
- Click OK again.
- The IUSR will be displayed in the top box labelled ‘Group of User Names’
- Select IUSR and grant necessary permissions in the ‘Permissions for BATCH’ list view.
- Click Apply and then you are done.
The steps may be slightly different for different versions of windows. This also applies to ASP.NET though I think the users you add are the network users (NETWORK AND OR NETWORK SERVICE users) as well as the IUSR.
answered Aug 20, 2017 at 17:37
user2288580user2288580
2,17023 silver badges16 bronze badges
1
Написал парсер и закинул его протестировать в сервис Railway.app: https://parser-php-production.up.railway.app/
При нажатии на кнопку обновить новые данные обновляются в файл json и отображаются на таблице AJAX’ом, но на хостинге появляется ошибка:
Warning: file_put_contents(./../../assets/data/weather.json): Failed to open stream:
Permission denied in /app/vendor/action/parse.php on line 23
Кроме этого появляется ошибка на header:
Warning: Cannot modify header information - headers already sent by
(output started at /app/vendor/action/parse.php:23) in /app/vendor/action/parse.php on line 27
На локалке всё работает и обновляется.
Вот код, где это происходит:
<?
set_time_limit(0);
ini_set('memory_limit', -1);
require './../../libs/autoload.php';
require 'function.php';
use GuzzleHttpClient;
use DiDomDocument;
$client = new Client();
$document = new Document();
$url = 'https://yandex.ru/pogoda/?via=hl';
$html = get_html($url, $client);
$document->loadHtml($html);
sleep(rand(1, 3));
$days_data = get_days($document, $client);
// СОХРАНЕНИЕ JSON
file_put_contents(
'./../../assets/data/weather.json', json_encode($days_data, JSON_UNESCAPED_UNICODE)
);
// ОТПРАВКА JSON-ОТВЕТА
header('Content-type: application/json');
echo json_encode($days_data, JSON_UNESCAPED_UNICODE);
(PHP 5, PHP 7, PHP
file_put_contents — Write data to a file
Description
file_put_contents(
string $filename
,
mixed $data
,
int $flags
= 0,
?resource $context
= null
): int|false
If filename
does not exist, the file is created.
Otherwise, the existing file is overwritten, unless the
FILE_APPEND
flag is set.
Parameters
-
filename
-
Path to the file where to write the data.
-
data
-
The data to write. Can be either a string, an
array or a stream resource.If
data
is a stream resource, the
remaining buffer of that stream will be copied to the specified file.
This is similar with using stream_copy_to_stream().You can also specify the
data
parameter as a single
dimension array. This is equivalent to
file_put_contents($filename, implode('', $array))
. -
flags
-
The value of
flags
can be any combination of
the following flags, joined with the binary OR (|
)
operator.Available flags
Flag Description FILE_USE_INCLUDE_PATH
Search for filename
in the include directory.
See include_path for more
information.FILE_APPEND
If file filename
already exists, append
the data to the file instead of overwriting it.LOCK_EX
Acquire an exclusive lock on the file while proceeding to the
writing. In other words, a flock() call happens
between the fopen() call and the
fwrite() call. This is not identical to an
fopen() call with mode «x». -
context
-
A valid context resource created with
stream_context_create().
Return Values
This function returns the number of bytes that were written to the file, or
false
on failure.
Warning
This function may
return Boolean false
, but may also return a non-Boolean value which
evaluates to false
. Please read the section on Booleans for more
information. Use the ===
operator for testing the return value of this
function.
Examples
Example #1 Simple usage example
<?php
$file = 'people.txt';
// Open the file to get existing content
$current = file_get_contents($file);
// Append a new person to the file
$current .= "John Smithn";
// Write the contents back to the file
file_put_contents($file, $current);
?>
Example #2 Using flags
<?php
$file = 'people.txt';
// The new person to add to the file
$person = "John Smithn";
// Write the contents to the file,
// using the FILE_APPEND flag to append the content to the end of the file
// and the LOCK_EX flag to prevent anyone else writing to the file at the same time
file_put_contents($file, $person, FILE_APPEND | LOCK_EX);
?>
Notes
Note: This function is
binary-safe.
Tip
A URL can be used as a
filename with this function if the fopen wrappers have been enabled.
See fopen() for more details on how to specify the
filename. See the Supported Protocols and Wrappers for links to information
about what abilities the various wrappers have, notes on their usage,
and information on any predefined variables they may
provide.
See Also
- fopen() — Opens file or URL
- fwrite() — Binary-safe file write
- file_get_contents() — Reads entire file into a string
- stream_context_create() — Creates a stream context
TrentTompkins at gmail dot com ¶
14 years ago
File put contents fails if you try to put a file in a directory that doesn't exist. This creates the directory.
<?php
function file_force_contents($dir, $contents){
$parts = explode('/', $dir);
$file = array_pop($parts);
$dir = '';
foreach($parts as $part)
if(!is_dir($dir .= "/$part")) mkdir($dir);
file_put_contents("$dir/$file", $contents);
}
?>
justin dot carlson at gmail dot com ¶
11 years ago
It should be obvious that this should only be used if you're making one write, if you are writing multiple times to the same file you should handle it yourself with fopen and fwrite, the fclose when you are done writing.
Benchmark below:
file_put_contents() for 1,000,000 writes - average of 3 benchmarks:
real 0m3.932s
user 0m2.487s
sys 0m1.437s
fopen() fwrite() for 1,000,000 writes, fclose() - average of 3 benchmarks:
real 0m2.265s
user 0m1.819s
sys 0m0.445s
maksam07 at gmail dot com ¶
4 years ago
A slightly simplified version of the method: http://php.net/manual/ru/function.file-put-contents.php#84180
<?php
function file_force_contents( $fullPath, $contents, $flags = 0 ){
$parts = explode( '/', $fullPath );
array_pop( $parts );
$dir = implode( '/', $parts );
if( !
is_dir( $dir ) )
mkdir( $dir, 0777, true );file_put_contents( $fullPath, $contents, $flags );
}file_force_contents( ROOT.'/newpath/file.txt', 'message', LOCK_EX );
?>
deqode at felosity dot nl ¶
13 years ago
Please note that when saving using an FTP host, an additional stream context must be passed through telling PHP to overwrite the file.
<?php
/* set the FTP hostname */
$user = "test";
$pass = "myFTP";
$host = "example.com";
$file = "test.txt";
$hostname = $user . ":" . $pass . "@" . $host . "/" . $file;
/* the file content */
$content = "this is just a test.";
/* create a stream context telling PHP to overwrite the file */
$options = array('ftp' => array('overwrite' => true));
$stream = stream_context_create($options);
/* and finally, put the contents */
file_put_contents($hostname, $content, 0, $stream);
?>
chris at ocportal dot com ¶
9 years ago
It's important to understand that LOCK_EX will not prevent reading the file unless you also explicitly acquire a read lock (shared locked) with the PHP 'flock' function.
i.e. in concurrent scenarios file_get_contents may return empty if you don't wrap it like this:
<?php
$myfile=fopen('test.txt','rt');
flock($myfile,LOCK_SH);
$read=file_get_contents('test.txt');
fclose($myfile);
?>
If you have code that does a file_get_contents on a file, changes the string, then re-saves using file_put_contents, you better be sure to do this correctly or your file will randomly wipe itself out.
Anonymous ¶
6 years ago
Make sure not to corrupt anything in case of failure.
<?phpfunction file_put_contents_atomically($filename, $data, $flags = 0, $context = null) {
if (file_put_contents($filename."~", $data, $flags, $context) === strlen($contents)) {
return rename($filename."~",$filename,$context);
}
@
unlink($filename."~", $context);
return FALSE;
}?>
egingell at sisna dot com ¶
16 years ago
In reply to the previous note:
If you want to emulate this function in PHP4, you need to return the bytes written as well as support for arrays, flags.
I can only figure out the FILE_APPEND flag and array support. If I could figure out "resource context" and the other flags, I would include those too.
<?
define('FILE_APPEND', 1);
function file_put_contents($n, $d, $flag = false) {
$mode = ($flag == FILE_APPEND || strtoupper($flag) == 'FILE_APPEND') ? 'a' : 'w';
$f = @fopen($n, $mode);
if ($f === false) {
return 0;
} else {
if (is_array($d)) $d = implode($d);
$bytes_written = fwrite($f, $d);
fclose($f);
return $bytes_written;
}
}
?>
aabaev arroba gmail coma com ¶
7 years ago
I suggest to expand file_force_contents() function of TrentTompkins at gmail dot com by adding verification if patch is like: "../foo/bar/file"
if (strpos($dir, "../") === 0)
$dir = str_replace("..", substr(__DIR__, 0, strrpos(__DIR__, "/")), $dir);
aidan at php dot net ¶
19 years ago
This functionality is now implemented in the PEAR package PHP_Compat.
More information about using this function without upgrading your version of PHP can be found on the below link:
http://pear.php.net/package/PHP_Compat
ravianshmsr08 at gmail dot com ¶
12 years ago
To upload file from your localhost to any FTP server.
pease note 'ftp_chdir' has been used instead of putting direct remote file path....in ftp_put ...remoth file should be only file name
<?php
$host = '*****';
$usr = '*****';
$pwd = '**********';
$local_file = './orderXML/order200.xml';
$ftp_path = 'order200.xml';
$conn_id = ftp_connect($host, 21) or die ("Cannot connect to host");
ftp_pasv($resource, true);
ftp_login($conn_id, $usr, $pwd) or die("Cannot login");
// perform file upload
ftp_chdir($conn_id, '/public_html/abc/');
$upload = ftp_put($conn_id, $ftp_path, $local_file, FTP_ASCII);
if($upload) { $ftpsucc=1; } else { $ftpsucc=0; }
// check upload status:
print (!$upload) ? 'Cannot upload' : 'Upload complete';
print "n";
// close the FTP stream
ftp_close($conn_id);
?>
vaneatona at gmail dot com ¶
6 years ago
I'm updating a function that was posted, as it would fail if there was no directory. It also returns the final value so you can determine if the actual file was written.
public static function file_force_contents($dir, $contents){
$parts = explode('/', $dir);
$file = array_pop($parts);
$dir = '';
foreach($parts as $part) {
if (! is_dir($dir .= "{$part}/")) mkdir($dir);
}
return file_put_contents("{$dir}{$file}", $contents);
}
gurjindersingh at SPAM dot hotmail dot com ¶
8 years ago
File put contents fails if you try to put a file in a directory that doesn't exist. This function creates the directory.
i have updated code of "TrentTompkins at gmail dot com". thanks
<?php
/**
* @param string $filename <p>file name including folder.
* example :: /path/to/file/filename.ext or filename.ext</p>
* @param string $data <p> The data to write.
* </p>
* @param int $flags same flags used for file_put_contents.
* more info: http://php.net/manual/en/function.file-put-contents.php
* @return bool <b>TRUE</b> file created succesfully <br> <b>FALSE</b> failed to create file.
*/
function file_force_contents($filename, $data, $flags = 0){
if(!is_dir(dirname($filename)))
mkdir(dirname($filename).'/', 0777, TRUE);
return file_put_contents($filename, $data,$flags);
}
// usagefile_force_contents('test1.txt','test1 content'); // test1.txt createdfile_force_contents('test2/test2.txt','test2 content');
// test2/test2.txt created "test2" folder. file_force_contents('~/test3/test3.txt','test3 content');
// /path/to/user/directory/test3/test3.txt created "test3" folder in user directory (check on linux "ll ~/ | grep test3").
?>
vahkos at mail dot ru ¶
11 years ago
file_put_contents does not issue an error message if file name is incorrect(for example has improper symbols on the end of it /n,/t)
that is why use trim() for file name.
$name=trim($name);
file_put_contents($name,$content);
error at example dot com ¶
12 years ago
It's worth noting that you must make sure to use the correct path when working with this function. I was using it to help with logging in an error handler and sometimes it would work - while other times it wouldn't. In the end it was because sometimes it was called from different paths resulting in a failure to write to the log file.
__DIR__ is your friend.
Anonymous ¶
1 year ago
A more simplified version of the method that creates subdirectories:
function path_put_contents($filePath, $contents, $flags = 0) {
if (! is_dir($dir = implode('/', explode('/', $filePath, -1))))
mkdir($dir, 0777, true);
file_put_contents($filePath, $contents, $flags);
}
Brandon Lockaby ¶
11 years ago
Calling file_put_contents within a destructor will cause the file to be written in SERVER_ROOT...
John Galt ¶
13 years ago
I use file_put_contents() as a method of very simple hit counters. These are two different examples of extremely simple hit counters, put on one line of code, each.
Keep in mind that they're not all that efficient. You must have a file called counter.txt with the initial value of 0.
For a text hit counter:
<?php
$counter = file_get_contents("counter.txt"); $counter++; file_put_contents("counter.txt", $counter); echo $counter;
?>
Or a graphic hit counter:
<?php
$counter = file_get_contents("counter.txt"); $counter++; file_put_contents("counter.txt", $counter); for($i = 0; $i < strlen($counter); $i++) echo "<img src="counter/".substr($counter, $i, 1).".gif" alt="".substr($counter, $i, 1)."" />";
?>
wjsams at gmail dot com ¶
13 years ago
file_put_contents() strips the last line ending
If you really want an extra line ending at the end of a file when writing with file_put_contents(), you must append an extra PHP_EOL to the end of the line as follows.
<?php
$a_str = array("these","are","new","lines");
$contents = implode(PHP_EOL, $a_str);
$contents .= PHP_EOL . PHP_EOL;
file_put_contents("newfile.txt", $contents);
print("|$contents|");
?>
You can see that when you print $contents you get two extra line endings, but if you view the file newfile.txt, you only get one.
daniel at garcianoriega dot com ¶
4 years ago
I faced the problem of converting a downloaded csv file that had Windows-1252 encoding, so to convert it to UTF-8 this worked for me:
$from = 'Windows-1252';
$to = 'UTF-8';
$content = file_get_contents($this->path());
file_put_contents($this->path(), mb_convert_encoding($content, $to, $from));
where "$this->path()" has the path of the file... Using this the file is converted from Windows-1252 to UTF-8.
With this you can import it with mysqlimport with no problems.
caiofior at gmail dot com ¶
11 years ago
I had some troubles using file_put_contents with an absolute but no canonicalized path (eg. w:/htdocs/pri/../test/log.txt): on windows environment php was unable to create the file also using the realpath function .
I had to use fopen and frwite functions to write the data.
briethings at gmail dot com ¶
5 years ago
Here is a stupid pitfall I just fell into.
I think it may happen rather frequently, so I report it.
A common situation is that the $filename argument is built from two variables:
file_put_contents($path . $file, $content);
Say that $path = 'path/to' and $file = 'file.txt': you see that $path lacks its ending "/", so the resulting full path is 'path/tofile.txt'.
Then you look at 'path/to' and don't see any 'file.txt', although no warning or notice was thrown!
And may be (like for me :D) it'll take time before you realize that a bad 'tofile.txt' was created in the *parent* directory of the one where you were looking for your file.
kola_lola at hotmail dot com ¶
14 years ago
I wrote this script implementing the file_put_contents() and file_get_contents() functions to be compatible with both php4.* and php 5.*. It is a PHP Command line interface script which searches and replaces a specific word recursively through all files in the supplied directory hierarchy.
Usage from a Linux command line: ./scriptname specifieddirectory searchString replaceString
#!/usr/bin/php
<?php
$argc = $_SERVER['argc'];
$argv = $_SERVER['argv'];
if(
$argc != 4)
{
echo "This command replaces a search string with a replacement stringn for the contents of all files in a directory hierachyn";
echo "command usage: $argv[0] directory searchString replaceStringn";
echo "n";
exit;
}
?><?phpif (!function_exists('file_put_contents')) {
function file_put_contents($filename, $data) {
$f = @fopen($filename, 'w');
if (!$f) {
return false;
} else {
$bytes = fwrite($f, $data);
fclose($f);
return $bytes;
}
}
}
function
get_file_contents($filename)/* Returns the contents of file name passed
*/
{
if (!function_exists('file_get_contents'))
{
$fhandle = fopen($filename, "r");
$fcontents = fread($fhandle, filesize($filename));
fclose($fhandle);
}
else
{
$fcontents = file_get_contents($filename);
}
return $fcontents;
}
?><?phpfunction openFileSearchAndReplace($parentDirectory, $searchFor, $replaceWith)
{
//echo "debug here- line 1an";
//echo "$parentDirectoryn";
//echo "$searchForn";
//echo "$replaceWithn";if ($handle = opendir("$parentDirectory")) {
while (false !== ($file = readdir($handle))) {
if (($file != "." && $file != "..") && !is_dir($file)) {
chdir("$parentDirectory"); //to make sure you are always in right directory
// echo "$filen";
$holdcontents = file_get_contents($file);
$holdcontents2 = str_replace($searchFor, $replaceWith, $holdcontents);
file_put_contents($file, $holdcontents2);
// echo "debug here- line 1n";
// echo "$filen";}
if(is_dir($file) && ($file != "." && $file != ".."))
{
$holdpwd = getcwd();
//echo "holdpwd = $holdpwd n";
$newdir = "$holdpwd"."/$file";
//echo "newdir = $newdir n"; //for recursive call
openFileSearchAndReplace($newdir, $searchFor, $replaceWith);
//echo "debug here- line 2n";
//echo "$filen";
}
}
closedir($handle);
}
}$parentDirectory2 = $argv[1];
$searchFor2 = $argv[2];
$replaceWith2 = $argv[3];//Please do not edit below to keep the rights to this script
//Free license, if contents below this line is not edited
echo "REPLACEDn'$searchFor2' with '$replaceWith2' recursively through directory listed belownFor all files that current user has write permissions fornDIRECTORY: '$parentDirectory2'n";
echo "command written by Kolapo Akande :) all rights reserved :)n";$holdpwd = getcwd();
//echo "$holdpwdn";
chdir($parentDirectory2);
openFileSearchAndReplace($parentDirectory2, $searchFor2, $replaceWith2);
exit;
?>
Curtis ¶
16 years ago
As to the previous user note, it would be wise to include that code within a conditional statement, as to prevent re-defining file_put_contents and the FILE_APPEND constant in PHP 5:
<?php
if ( !function_exists('file_put_contents') && !defined('FILE_APPEND') ) {
...
}
?>
Also, if the file could not be accessed for writing, the function should return boolean false, not 0. An error is different from 0 bytes written, in this case.
moura dot kadu at gmail dot com ¶
11 years ago
I made a ftp_put_contents function.
hope you enjoy.
<?phpfunction ftp_put_contents($fpc_path_and_name, $fpc_content) {//Temporary folder in the server
$cfg_temp_folder = str_replace("//", "/", $_SERVER['DOCUMENT_ROOT']."/_temp/");//Link to FTP
$cfg_ftp_server = "ftp://ftp.com";//FTP username
$cfg_user = "user";//FTP password
$cfg_pass = "password";//Document Root of FTP
$cfg_document_root = "DOCUMENT ROOT OF FTP";//Link to the website
$cfg_site_link = "Link to the website";//Check if conteins slash on the path of the file
$cotains_slash = strstr($fpc_path_and_name, "/");//Get filename and paths
if ($cotains_slash) {
$fpc_path_and_name_array = explode("/", $fpc_path_and_name);
$fpc_file_name = end($fpc_path_and_name_array);
}
else {
$fpc_file_name = $fpc_path_and_name;
}//Create local temp dir
if (!file_exists($cfg_temp_folder)) {
if (!mkdir($cfg_temp_folder, 0777)) {
echo "Unable to generate a temporary folder on the local server - $cfg_temp_folder.<br />";
die();
}
}//Create local file in temp dir
if (!file_put_contents(str_replace("//", "/", $cfg_temp_folder.$fpc_file_name), $fpc_content)) {
echo "Unable to generate the file in the temporary location - ".str_replace("//", "/", $cfg_temp_folder.$fpc_file_name).".<br />";
die();
}//Connection to the FTP Server
$fpc_ftp_conn = ftp_connect("$cfg_ftp_server");//Check connection
if (!$fpc_ftp_conn) {
echo "Could not connect to server <b>$cfg_ftp_server</b>.<br />";
die();
}
else {// login
// check username and password
if (!ftp_login($fpc_ftp_conn, "$cfg_user", "$cfg_pass")) {
echo "User or password.<br />";
die();
}
else {//Document Root
if (!ftp_chdir($fpc_ftp_conn, $cfg_document_root)) {
echo "Error to set Document Root.<br />";
die();
}//Check if there are folders to create
if ($cotains_slash) {//Check if have folders and is not just the file name
if (count($fpc_path_and_name_array) > 1) {//Remove last array
$fpc_remove_last_array = array_pop($fpc_path_and_name_array);//Checks if there slashs on the path
if (substr($fpc_path_and_name,0,1) == "/") {
$fpc_remove_first_array = array_shift($fpc_path_and_name_array);
}//Create each folder on ftp
foreach ($fpc_path_and_name_array as $fpc_ftp_path) {
if (!@ftp_chdir($fpc_ftp_conn, $fpc_ftp_path)) {
if (!ftp_mkdir($fpc_ftp_conn, $fpc_ftp_path)) {
echo "Error creating directory $fpc_ftp_path.<br />";
}
else {
if (!ftp_chdir($fpc_ftp_conn, $fpc_ftp_path)) {
echo "Error go to the directory $fpc_ftp_path.<br />";
}
}
}
}
}
else {
}
}
//Check upload file
if (!ftp_put($fpc_ftp_conn, $fpc_file_name, str_replace("//", "/", $cfg_temp_folder.$fpc_file_name), FTP_ASCII)) {
echo "File upload <b>$fpc_path_and_name</b> failed!<br />";
die();
}
else {
if (!unlink(str_replace("//", "/", $cfg_temp_folder.$fpc_file_name))) {
echo "Error deleting temporary file.<br />";
die();
}
else {
echo "File upload <a href='$cfg_site_link".str_replace("//", "/", "/$fpc_path_and_name")."'><b>$cfg_site_link".str_replace("//", "/", "/$fpc_path_and_name")."</a></b> successfully performed.<br />";
}
}//Close connection to FTP server
ftp_close($fpc_ftp_conn);
}
}
}#Sample$content_file = "<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Title</title>
</head>
<body>
<p>Test</p>
</body>
</html>";ftp_put_contents("test.php", $content_file);
?>
the geek man at hot mail point com ¶
15 years ago
I use the following code to create a rudimentary text editor. It's not fancy, but then it doesn't have to be. You could easily add a parameter to specify a file to edit; I have not done so to avoid the potential security headaches.
There are still obvious security holes here, but for most applications it should be reasonably safe if implemented for brief periods in a counterintuitive spot. (Nobody says you have to make a PHP file for that purpose; you can tack it on anywhere, so long as it is at the beginning of a file.)
<?php
$random1 = 'randomly_generated_string';
$random2 = 'another_randomly_generated_string';
$target_file = 'file_to_edit.php';
$this_file = 'the_current_file.php';
if (
$_REQUEST[$random1] === $random2) {
if (isset($_POST['content']))
file_put_contents($target_file, get_magic_quotes_qpc() ? stripslashes($_POST['content']) : $_POST['content']);
die(
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Editing...</title>
</head>
<body>
<form method="post" action="' . $this_file . '" />
<input type="hidden" name="' . $random1 . '" value="' . $random2 . '" />
<textarea name="content" rows="50" cols="100">' . file_get_contents($target_file) . '</textarea><br />
<input type="submit" value="Save Changes" />
</form>
</body>
</html>');
}
?>
Then simply browse to hxxp://www.example.com/{$this_file}?{$random1}={$random2}, with the appropriate values substituted for each bracketed variable. Please note that this code assumes the target file to be world writable (-rw-rw-rw- or 666) and will fail to save properly without error if it is not.
Once again, this is by no means secure or permanent, but as a quick fix for brief edits to noncritical files it should be sufficient, and its small size is a definite bonus.
marc at gutt dot it ¶
6 years ago
This php.net example could be confusing:
<?php
file_put_contents($file, $person, FILE_APPEND | LOCK_EX);
?>
You do not need LOCK_EX if you write only to a file (like log files). Multiple writing processes are already queued. Test it on your own by calling this script 4 times simultaneously:
<?php
function string_rand($len, $split="n") {
return substr(chunk_split(bin2hex(openssl_random_pseudo_bytes(ceil($len / 2))), 1023, $split), 0, $len);
}ini_set('memory_limit', '200M');
if (!
file_put_contents('../cache4/file.txt', string_rand(102400000), FILE_APPEND)) {
exit('file_put_contents() error!');
}clearstatcache();
echo 'filesize: ' . filesize('../cache4/file.txt') . PHP_EOL;$fp = fopen('../cache4/file.txt', 'r');
if ($fp) {
while (($line = fgets($fp)) !== false) {
if (strlen($line) != 1024) {
exit('Line length error!');
}
}
fclose($fp);
}
?>
You will not see an error, the last called script needs the longest time (as it is the last one in the queue) and the final size will be 409600000 bytes as it should be.
vilhar at hotmail dot com ¶
7 years ago
This should also handle $filename from other than root and also $filename without path.
function file_force_contents($filename, $data, $per = 0755, $flags = 0) {
$fn = "";
if(substr($filename, 0, 1) === "/") $fn .= "/";
$parts = explode("/", $filename);
$file = array_pop($parts);
foreach($parts as $part) {
if(!is_dir($fn .= "$part")) mkdir($fn, $per, true);
$fn .= "/";
}
file_put_contents($fn.$file, $data, $flags);
}
michel kollenhoven ¶
9 years ago
egingell at sisna dot com ¶
15 years ago
There is a better way. www.php.net/touch
Since you're not adding anything to the file,
<?php
function updateFile($filename) {
if (!file_exists($filename)) return;
touch($filename);
}
?>
Nadeem ¶
8 years ago
Log custom or error messages to a file.
<?phpclass Logger{
protected
$file;
protected
$content;
protected
$writeFlag;
protected
$endRow;
public function
__construct($file,$endRow="n",$writeFlag=FILE_APPEND) { $this->file=$file;$this->writeFlag=$writeFlag;$this->endRow=$endRow;
}
public function
AddRow($content="",$newLines=1){
for (
$m=0;$m<$newLines;$m++)
{$newRow .= $this->endRow;
}
$this->content .= $content . $newRow;
}
public function
Commit(){
return
file_put_contents($this->file,$this->content,$this->writeFlag);
}
public function
LogError($error,$newLines=1)
{
if ($error!=""){$this->AddRow($error,$newLines);
echo $error;
}
}
}
$logFileDirectoryAndName="/yourDirectory/yourFileName.xxx";$logger = new Logger($logFileDirectoryAndName);$logger->AddRow("Your Log Message");#log a system error and echo a message
$logger->LogError(mysql_error($conn));$logger->Commit();
?>
klunker dot roox at gmail dot com ¶
8 years ago
if path to the file not exist function file_put_contents can't create it. This simple function make it on UNIX-based and Windows servers.
<?php
function file_put_contents_force(){
$args = func_get_args();
$path = str_replace(array('/','\'), DIRECTORY_SEPARATOR, $args[0]);
$parts = explode(DIRECTORY_SEPARATOR, $path);
array_pop($parts);
$directory = '';
foreach($parts as $part):
$check_path = $directory.$part;
if( is_dir($check_path.DIRECTORY_SEPARATOR) === FALSE) {
mkdir($check_path, 0755);
}
$directory = $check_path.DIRECTORY_SEPARATOR;
endforeach;
call_user_func_array('file_put_contents',$args);
}
?>
hilton at allcor dot com dot br ¶
12 years ago
NOTE : file_put_contents create files UTF-8
<?php
$myFile = 'test.txt';
$myContent = 'I love PHP'; file_put_contents($myFile, utf8_encode($myContent));
?>
clement dot delmas at gmail dot com ¶
12 years ago
NOTE : file_put_contents doesn't add a valid BOM while creating the file
<?php
$myFile = 'test.txt';
$myContent = 'I love PHP';
file_put_contents($myFile, "xEFxBBxBF".$myContent);
?>
Abe ¶
5 years ago
If you need to read a file, process the contents, and then write the contents back, all inside a lock so that no other process can interfere, then you probably can't use file_put_contents in lock mode.
As a work around, you can consider trying the following code to do a file write (fwrite) inside of a file lock.
Play with the $overwriting= setting to switch from simply appending to completely overwriting.
<?php
// Tested in PHP7.0.18$trackErrors = ini_get('track_errors');
ini_set('track_errors', 1);$filename="c:/temp/test.txt";
if ( !
file_exists($filename) ) {
touch($filename);
}$mode="r+";
$fh=fopen($filename, $mode);
if ( ! $fh ) {
echo "<LI style='color:red'>Failed to open $filename in $mode";
die();
}
if (
1==1 ) {
flock($fh, LOCK_EX);
if (
1==1 ) {
if (filesize($filename) > 0) {
$txt = fread($fh, filesize($filename));
echo "BEFORE:<OL>$txt</OL>";
}
else {
$txt = "empty";
}$txt = "<LI>Date is now " . gmdate("Y-m-d H:i:s", time());
echo
"<HR>WRITING:<OL>";
if ( 1==1 ) {$overwrite = false;
if ($overwrite) {
echo "(Overwrite)<BR><BR>";
ftruncate($fh, 0);
// ftruncate is here as rewind will move the pointer
// to the beginning of the file and allow overwrite,
// but it will not remove existing content that
// extends beyond the length of the new write
rewind($fh);
}
else {
echo "(Appending)<BR><BR>";
}
if (
fwrite($fh, $txt) == FALSE) {
echo "<LI style='color:red'>Failed to write to $filename";
die();
}
else {
echo "$txt";
}
fflush($fh);
}
echo
"</OL>";
}
flock($fh, LOCK_UN);
}
fclose($fh);// -------------------------------------------------------------------echo "<HR>AFTER:<OL>";
if ( 1==2 ) {
// I've noticed that this block fails to pick up the newly
// written content when run with fread, but does work with
// the fgets logic below - possibly there is caching going on.
$mode = "r";
$fh2 = fopen($filename, $mode);
$contents = fread($fh2, filesize($filename));
echo "$contents";
fclose($fh2);
}
else {
$fh3 = fopen($filename, "r");
while (!feof($fh3)) {
$line = fgets($fh3);
echo $line;
}
fclose($fh3);
}
echo "</OL>";
echo
"<HR>Fin";
?>
josemiguel at likeik dot com ¶
6 years ago
Under PHP7.0, I have seen that using an empty string as $data, the function returns FALSE instead of 0 as it should:
$data = ''
$size = file_put_contents($filename, $data, FILE_APPEND);
if ($size == FALSE)
{
echo "error writing file"
}
Code will give error when $data is an empty string even using == operator.
Hope it helps somebody...
jul dot rosset at gmail dot com ¶
2 years ago
This function doesn't return False if all data isn't write, especially when data is a stream resource
You must check yourself using return value.
<?php
$url = 'https://monsite.fr/image.png';
$size = filesize($url);
$written = file_put_contents('image.png', fopen($url, 'r'));
if ($size === false || $size != $written) {
die('Download failed');
}
?>
admin at nabito dot net ¶
14 years ago
This is example, how to save Error Array into simple log file
<?php
$error
[] = 'some error';
$error[] = 'some error 2';
@
file_put_contents('log.txt',date('c')."n".implode("n", $error),FILE_APPEND);?>
File_put_contents php или как записать в файл данные php, как записывать текст в файл, как записать в конец файла, в начало файла!? Какая функция умеет записывать текст, код в файл!? Для записи в файл будем использовать функцию file_put_contents.
Что такое file_put_contents
С самого начала — давайте дадим определение — «что такое file_put_contents»
file_put_contents — это функция, которая записывает данные в строку, данные могут быть строкой и массивом(mixed $data). Если файл не существует, то файл будет создан.(путь до папки должен существовать и быть на сервере )
Синтаксис file_put_contents
В учебнике функция file_put_contents представлена таким видом:
int file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] )
Разбор синтаксиса file_put_contents
Если вы впервые увидели функцию, а тем более file_put_contents, то скорее всего вам мало понятен выше приведенный синтаксис file_put_contents — поэтому! Давайте его разберем по косточкам!
int — при удачной работе, вернет «int» — тип данных, целое число.
file_put_contents — название функции.
string — первое значение должно быть типом string — строка → $filename — путь сохранения — лучше использовать путь на сервере.
mixed $data — тип записываемых данных, смешанный(string или array).
int $flags — флаги, о них отдельно ниже.
resource $context — Корректный ресурс контекста, созданный с помощью функции stream_context_create().(никогда не пользовался.)
Флаги для file_put_contents
Флаги для file_put_contents — это очень полезная вещь!
FILE_USE_INCLUDE_PATH — Ищет filename в подключаемых директориях. Подробнее смотрите директиву include_path.(никогда не пользовался.)
FILE_APPEND — при использовании данного флага, функция «file_put_contents» запишет новые данные в конец файла, а не перезапишет ваш файл полностью.
LOCK_EX — закрывает файл в момент записи.
Синтаксис — это конечно хорошо, но я его никогда таким образом не запоминал.
Упрощенный синтаксис для file_put_contents
Чтобы можно было запомнить, упросим написание синтаксиса функции file_put_contents:
file_put_contents(«file_dir», «data», FILE_APPEND | LOCK_EX);
file_dir -> путь до файла, если путь указан только названием файла, например primer.txt, то файл создастся в той папке, где расположен файл со скриптом. Исключение -> если у вас на сайте единая точка входа, то файл в таком случае будет сохраняться в корневую папку. Тогда нужно указывать путь на сервере до файла. Если файл по указанному адресу не существует, но путь до папки существует, то файл будет создан! Иначе ошибка.
data -> данные, это может быть строка или массив.
FILE_APPEND -> флаг, который указывает на то, что запись будет производится в конец строки. Если флаг не указан, то файл будет перезаписываться!
LOCK_EX -> флаг, который указывает на то, что в момент записи файл будет недоступен для других пользователей!
Видео : Запись в файл с помощью file_put_content из формы
Друзья!
Мне очень нужны подписчики!
Пожалуйста подпишись на Дзене!
Заранее спасибо!
Записать данные в файл с помощью file_put_contents
Для того, чтобы записать данные в файл нам понадобится функция «file_put_contents».
Путь для записи с помощью file_put_contents
Как я уже неоднократно говорил выше, что путь лучше использовать на сервере до файла.
Смысла не вижу говорить о других вариантах — я их не использую.
Данные для записи с помощью file_put_contents
Как вы уже поняли, то «file_put_contents» может записать строку, это можно сделать таким образом(кавычки, в данном пример можно использовать, как одинарные так и двойные.):
file_put_contents(«test.txt», «привет мир!»);
Простой одинарный массив можно записать таким образом смотри подробности записи массива:
file_put_contents(«test.txt», array(«1″,»2″,»3″,»4»));
Для записи многомерного массива понадобится — serialize — превратить массив в строку.
Куда будем записывать данные с помощью file_put_contents
Мы должны определиться, как и что мы хотим записать.
Первый раз записать или перезаписать данные в файле, тогда здесь
Либо записать в конец файла.
Либо записать в начало файла.
Ошибка записи файла в функции file_put_contents
Как и из-за чего может возникнуть ошибка в функции file_put_contents.
При первом создании файла ВСЕГДА(кроме), будет возникать ошибка типа Warning, потому, что файл еще не существует!
Если путь существует, то файл будет создан, ошибка будет выведена на экран.
Если путь не существует, то функция file_put_contents вернет такую же ошибку:
Warning: file_put_contents(«путь_до_файла»): failed to open stream: No such file or directory in
путь
on line
строка
Для ликвидации ошибки failed to open stream можно пользоваться собакой
@file_put_contents()
Записать данные в файл, с ограничением 1 раз в сутки
Иногда требуется записать какую-то информацию в файл 1 раз в сутки и не важно, кто это зайдет на страницу и в какое время! Для того, чтобы определить, что данная запись в файл была сделана, то нужна какая-то метка, что именно сегодня запись была сделана! Логично предположить, например… , что это будет сегодняшняя дата!
Как я уже говорил, что начал переписывать страницу снизу и… этот скрипт короче предыдущих, поэтому его можно практически всего описать! погнали:
Получим дату в переменную:
$data = date(‘d.m.Y’);
путь до корневой до файла.
$dir_folder = ‘/__a-data/__all_for_scripts/__examples/php/file/primer_file_put_contents/’;
путь на сервере до файла:
$dir = $_SERVER[‘DOCUMENT_ROOT’].$dir_folder;
Получаем домен и http протокол
$domen = $_SERVER[«HTTP_X_FORWARDED_PROTO»].’://’ .$_SERVER[«HTTP_HOST»];
Получаем данные из файла file_get_contents
$get_data = @file_get_contents($dir.’example_1.dat’);
Проверяем есть ли сегодняшняя дата
substr_count
в зависимости от результатов проверки выдаем либо нет, либо записываем в файл новую дату в конец файла
if(substr_count($get_data, $data)) //проверим есть ли
{
$info = «Сегодня уже запись была сделана»;
}
else
{
$write = @file_put_contents($dir.’example_1.dat’ , $data .»n» , FILE_APPEND | LOCK_EX );
if($write) { $info = «Запись прошла»;} else {$info = «Запись не прошла»;}
}
echo ‘<red>’.$info.'</red>’;
Итого -> пример
Скачать см. выше.
Записать/перезаписать текст в файле через форму
Как бы странно это не звучало, но обновлял страницу с конца, поэтому, как и в предыдущих пунктах, прежде чем приступить к теме -> «Записать/перезаписать текст в файле через форму» у нас есть для данного пункта:
Это не такая сложная тема — «Записать/перезаписать текст в файле через форму» -> не стал делать дополнительных фильтров и собрал совсем простой код. Итак, что у нас есть!?:
У нас опять функция file_put_contents
Из формы получаем с помощью post в переменную текст:
$new_text = strip_tags($_POST[‘text’]);
Единственный фильтр поставил на количество символов:
$count = count(preg_split(«//u»,$new_text ,-1,PREG_SPLIT_NO_EMPTY));
С условием если количество больше 50 :
if($count < 50)
Записываем в файл $dir_file_server и добавил дату, чтобы можно было посмотреть,дату последней записи в файл…:
$write = file_put_contents( $dir_file_server , $data . ‘***’ .$new_text);
Путь до файла на сервере написал в одну строчку:
$dir_file_server = $_SERVER[«DOCUMENT_ROOT»].’/__a-data/__all_for_scripts/__examples/php/tets_zapisi/tekst_dlya_zapisi.txt’;
Всё описание и переменные из скрипта.
Скачать .
Как записать в конец файла file_put_contents из формы.
Прежде чем перейти к пункту записать в конец файла с помощью file_put_contents из формы, у нас есть для данного пункта :
Живой пример записи текста в конец файла -> пример
Его же можно скачать -> в архиве
Теперь давайте разберемся как работает выше приведенный пример и вообще как записывать в конец файла -> нам понадобится:
Функция, о которой мы с вами уже говорили выше file_put_contents
Путь до файла $dir_file -> путь до папки должен существовать физически, иначе будет ошибка записи в файл
Путь до файла должен быть на сервере
Далее -> данные для записи в конец файла $data, в нашем примере туда будет попадать текст из формы через post запрос.
Для того, чтобы запись в файл происходило в конец файла, нужно поставить флаг FILE_APPEND
И если вам нужен перенос, после данных ставим его -> . «n» , это должно выглядеть примерно так:
@file_put_contents( $dir_file , $data . «n» , FILE_APPEND);
Нам осталось собрать весь код вместе, его как уже говорилось вы можете скачать -> в архиве
Где может применяться запись в конец файла !? В одном из видео я делал экскурсию по своей админке и вот там, в том числе, применялась запись в файл в начало строки.
Запись происходит в несколько файлов, и вот запись на главную делается в конец файла!
Как записывать строку в начало файла с помощью file_put_contents ?
Как можно записать в начало файла через php в файл!? Повторимся, что у нас есть :
К сожалению у функции file_put_contents нет такого флага, чтобы записать в уже имеющийся контент в начало файла -> что нужно сделать!?
Получить существующий контент в переменную с помощью file_get_contents
file_get_contents($dir_file_server)
Поставить новый текст перед полученным, если требуется перенос строки ставим перенос:
file_put_contents( $dir_file_server, $data . «n» . file_get_contents($dir_file));
Где -> путь до папки от корневой:
$dir_folder = ‘/__a-data/__all_for_scripts/__examples/php/tets_zapisi/’;
Название файла, куда будем записывать в начало файла:
$name_file = ‘tekst_dlya_zapisi_2.txt’;
Весь путь на сервере до файла :
$dir_file_server = $_SERVER[‘DOCUMENT_ROOT’].$dir_folder.$name_file;
Все переменные и описание касаются того примера о котором мы уже сказали выше…
Где может применяться запись в начало файла !? В одном из видео я делал экскурсию по своей админке и вот там, в том числе, применялась запись в файл в начало строки
Как записать исполняемый код php в файл
Но вот вопрос! Как записать в файл исполняемый код!? Т.е. при записи он не должен выполниться, но уже на самой странице, куда был записан выполнится!?
В самом начале скажем пару слов :
Я уже делал страницу, где в реальности в каждый новый файл записывался html каркас страницы! -> здесь. А исполняемый php код — в нем нужно просто заэкранировать, либо поместить переменные в одинарные кавычки!
И где он применяется у меня на сайте!? На 115 секунде записываются данные на страницу, как раз в этом видео… все данные относительно страницы записываются в виде php кода с переменными!
Чтобы ничего не придумывать, мы возьмем живой пример, как записываю я, тот код, который должен исполниться! Как вы поняли из нашего сайта, но если не поняли то тоже скажу — что нам периодически приходится приводить примеры того, как тот или иной код работает,поэтому унас есть в админке, о которой я даже снял некоторое видео… экскурсию
На скрине — отдельное поле, в который вставляется тот код, который должен исполниться:
Нажмите, чтобы открыть в новом окне.
Это работает очень просто!
В textarea — вставляем наш код, потом передаем его с помощью post.
Обращаю ваше внимание на переменную $text_for_page -после нее первое рано красного цвета, а далее цвет изменился… на желтый — это говорит о том, что внутри код не будет исполняться… и будет обычной строкой… это достигается одиночной кавычкой.
И еще! Обращаю ваше внимание, что переменные внутри, белого цвета — это говорит, нам о том, что данные переданные выдадут свое содержание в строку!
Нажмите, чтобы открыть в новом окне.
Может это кажется страшным, но для меня это каждодневная работа!
смайлы
Как очистить файл от контента php!?
Для того, чтобы очистить файл от контента php нам опять понадобится функция file_put_contents и во втором параметре ставим ничего
После перезагрузки страницы, файл $file_dir будет очищен от контента!
file_put_contents( $file_dir , «» );
Как записать в файл с названием из поля.
Борис спрашивает:
Подскажите, пожалуйста, как сделать, чтобы файл создавался по имени из формы? Есть тест, человек вводит в поле фамилию. Можно сделать, чтоб файл создавался по этому полю? Например, человек вводит Иванов, и создается файл Иванов.txt У меня переменная $surname отвечает за поле методом POST. Но когда пытаюсь выполнить код, создается файл $surname.txt, а не по фамилии.»
Ответ по мотивам вопроса Бориса:
Данный вопрос интересный, поэтому, выделю его в отдельный пункт!
Давайте разбираться с самого начала…
Как вы сказали у вас есть поле ввода, предположим:
<input placeholder=»Фамилия» name=»surname»>
Как вы знаете, Вам понадобится post.
Проверяем был ли отправлен с помощью if — «$_POST[‘surname’]».
Не забываем про безопасность — strip_tags.
Присваиваем переменной «$surname» отправленное значение.
if($_POST[‘surname’]) { $surname = strip_tags($_POST[‘surname’]);}
Вообще… «Иванов.txt» — это конечно хорошо…, но раз уж Вы спрашиваете, то и про это тоже… Надо стараться, если возможно придерживаться каких-то неписанных правил(это необязательно)!
Кириллица — это безусловно приятно, понятно и радует глаз, но поскольку изначально php задумывался под латиницу, то с кириллицей периодически возникают проблемы(пример).
Я бы, к выше приведенному коду добавил: translit + my_strtolower :
if($_POST[‘surname’]) { $surname = translit(my_strtolower(strip_tags($_POST[‘surname’])));}
На выходе вы получите:
ivanov.txt
Код получения и конвертации данных фамилии сделали, теперь… записываем :
Прописываем путь(не буду подробно останавливаться на этом). Используем в примере __DIR__ — сохраняться будет в той папке, где находится скрипт.
В $data помещаем ваш контент для записи. Тут еще можно написать каскад проверок… а записалось ли, и записалось то, что мы передали… но для этого нужна отдельная страница. В коротком виде:
@file_put_contents(__DIR__ . ‘/’ . $surname . ‘.txt’ , $data );
Соберем весь код
Код: записать в файл с названием из поля.
Php:
<?
if($_POST['data']) { $data= strip_tags($_POST['data']);}
if($_POST['surname'])
{
$surname = translit(my_strtolower(strip_tags($_POST['surname'])));
@file_put_contents(__DIR__ . '/' . $surname . '.txt' , $data );
}
?>
Html:
<form method="post">
<input name="data" type="text" placeholder="Данные" required>
<input name="surname" type="text" placeholder="Фамилия" required>
<input name="sub_example" type="submit" value="отправить">
</form>
Finally reproduced this in a repeatable way.
Winnfsd
Winnfsd log output (duplicate lines truncated):
[15:44:52] NFS WRITE \?W:tms-vm-2appstmstmstest.txt
[ ... 320 lines ]
[15:44:53] NFS WRITE \?W:tms-vm-2appstmstmstest.txt
[15:44:53] NFS COMMIT \?W:tms-vm-2appstmstmstest.txt
[15:44:53] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[15:44:53] NFS ACCESS \?W:tms-vm-2appstmstmstest.txt
[15:44:53] NFS READ \?W:tms-vm-2appstmstmstest.txt
[ ... 320 lines ]
[15:44:53] NFS READ \?W:tms-vm-2appstmstmstest.txt
[15:44:53] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[15:44:53] NFS SETATTR \?W:tms-vm-2appstmstmstest.txt
[15:44:53] NFS WRITE \?W:tms-vm-2appstmstmstest.txt
[ ... 320 lines ]
[15:44:56] NFS WRITE \?W:tms-vm-2appstmstmstest.txt
[15:44:56] NFS COMMIT \?W:tms-vm-2appstmstmstest.txt
[15:44:56] NFS COMMIT \?W:tms-vm-2appstmstmstest.txt IO
[15:44:56] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[15:44:56] NFS ACCESS \?W:tms-vm-2appstmstmstest.txt
[15:44:56] NFS READ \?W:tms-vm-2appstmstmstest.txt
[ ... 320 lines ]
[15:44:56] NFS READ \?W:tms-vm-2appstmstmstest.txt
[15:44:56] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[15:44:56] NFS SETATTR \?W:tms-vm-2appstmstmstest.txt
[15:44:56] NFS WRITE \?W:tms-vm-2appstmstmstest.txt No such file or directory IO
[ ... 344 lines ]
[15:44:56] NFS WRITE \?W:tms-vm-2appstmstmstest.txt No such file or directory IO
[15:44:56] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
Script
PHP Script:
<?php const FILENAME = 'test.txt'; while (true) { // Create 1000 lines of 1K garbage $str = []; for ($i=0 ; $i<1024*100 ; $i++) { $str[] = str_pad("", 100, "a"); } // Write file_put_contents(FILENAME, json_encode($str)); // Read $size = filesize(FILENAME); $content = json_decode(file_get_contents(FILENAME)); // Stats echo sprintf("n %s | size %d | items %d", date("Y-m-d H:i:s"), $size, count($content)); // Check: Size is zero? if ($size === 0) { throw new Exception("Broke write (size)"); } // Check: content is broken? if ($content === null) { throw new Exception("Broke write (content)"); } };
Script output:
www-data@tms[/var/www/tms] (phpstan) $ php -f test.php
2018-07-19 15:44:22 | size 10547201 | items 102400
2018-07-19 15:44:25 | size 10547201 | items 102400
2018-07-19 15:44:28 | size 10547201 | items 102400
2018-07-19 15:44:30 | size 10547201 | items 102400
2018-07-19 15:44:33 | size 10547201 | items 102400
2018-07-19 15:44:36 | size 10547201 | items 102400
2018-07-19 15:44:39 | size 10547201 | items 102400
2018-07-19 15:44:42 | size 10547201 | items 102400
2018-07-19 15:44:44 | size 10547201 | items 102400
2018-07-19 15:44:47 | size 10547201 | items 102400
2018-07-19 15:44:49 | size 10547201 | items 102400
2018-07-19 15:44:52 | size 10547201 | items 102400
2018-07-19 15:44:55 | size 10547201 | items 102400
2018-07-19 15:44:55 | size 10547201 | items 0
PHP Fatal error: Uncaught Exception: Broke write (content) in /var/www/tms/test.php:31
Stack trace:
#0 {main}
thrown in /var/www/tms/test.php on line 31
Fatal error: Uncaught Exception: Broke write (content) in /var/www/tms/test.php:31
Stack trace:
#0 {main}
thrown in /var/www/tms/test.php on line 31
Doing an ls
-rwxrwxrwx 1 www-data www-data 0 Jul 19 05:44 test.txt
What’s happening
- This script is running in an infinite loop and generating ~10Mb of JSON data
- The data is written to disk (winnfsd mount) using
file_put_contents
. - The size of the file is read using
filesize
- The contents of the file is re-read using
file_get_contents
- The size is checked, and always echos out as the expected size (10547201)
- The content is checked, and the count of items is echoed (102400)
When it goes wrong
- PHP: size of the file is 10547201.
- Linux: size of the file is 0.
- Host: size of the file is 0.
- PHP: The file is empty.
- Linux: The file is empty.
- Host: The file is empty.
What happens next
Trying to run the script again causes the exact same error immediately.
Content does not get written to this file by PHP, and content does not get written to disk on the host.
Winnfsd logs:
[15:58:00] NFS GETATTR \?W:tms-vm-2appstmstmstest.php
[15:58:00] NFS ACCESS \?W:tms-vm-2appstmstmstest.php
[15:58:01] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[15:58:01] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[15:58:01] NFS ACCESS \?W:tms-vm-2appstmstmstest.txt
[15:58:01] NFS SETATTR \?W:tms-vm-2appstmstmstest.txt
[15:58:01] NFS WRITE \?W:tms-vm-2appstmstmstest.txt No such file or directory IO
[ ... 320 lines ]
[15:58:01] NFS WRITE \?W:tms-vm-2appstmstmstest.txt No such file or directory IO
[15:58:01] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[15:58:01] NFS ACCESS \?W:tms-vm-2appstmstmstest.txt
You can restore access to the file without restarting winnfsd.
Using vi
to open, write and quit (vi
-> :wq
) is enough to restore access.
[16:02:26] NFS ACCESS \?W:tms-vm-2appstmstmstest.txt
[16:02:26] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[16:02:26] NFS LOOKUP \?W:tms-vm-2appstmstms.test.txt.swp NOENT
[16:02:26] NFS CREATE \?W:tms-vm-2appstmstms.test.txt.swp
[16:02:26] NFS SETATTR \?W:tms-vm-2appstmstms.test.txt.swp
[16:02:26] NFS ACCESS \?W:tms-vm-2appstmstms
[16:02:26] NFS LOOKUP \?W:tms-vm-2appstmstms.test.txt.swx NOENT
[16:02:26] NFS CREATE \?W:tms-vm-2appstmstms.test.txt.swx
[16:02:26] NFS SETATTR \?W:tms-vm-2appstmstms.test.txt.swx
[16:02:26] NFS REMOVE \?W:tms-vm-2appstmstms.test.txt.swx
[16:02:26] NFS REMOVE \?W:tms-vm-2appstmstms.test.txt.swp
[16:02:26] NFS LOOKUP \?W:tms-vm-2appstmstms.test.txt.swp NOENT
[16:02:26] NFS CREATE \?W:tms-vm-2appstmstms.test.txt.swp
[16:02:26] NFS SETATTR \?W:tms-vm-2appstmstms.test.txt.swp
[16:02:26] NFS WRITE \?W:tms-vm-2appstmstms.test.txt.swp
[16:02:26] NFS SETATTR \?W:tms-vm-2appstmstms.test.txt.swp
[16:02:28] NFS LOOKUP \?W:tms-vm-2appstmstmstest.txt
[16:02:28] NFS LOOKUP \?W:tms-vm-2appstmstms4913 NOENT
[16:02:28] NFS CREATE \?W:tms-vm-2appstmstms4913
[16:02:28] NFS SETATTR \?W:tms-vm-2appstmstms4913
[16:02:28] NFS SETATTR \?W:tms-vm-2appstmstms4913
[16:02:28] NFS ACCESS \?W:tms-vm-2appstmstms
[16:02:28] NFS REMOVE \?W:tms-vm-2appstmstms4913
[16:02:28] NFS LOOKUP \?W:tms-vm-2appstmstmstest.txt~ NOENT
[16:02:28] NFS LOOKUP \?W:tms-vm-2appstmstmstest.txt
[16:02:28] NFS RENAME \?W:tms-vm-2appstmstmstest.txt \?W:tms-vm-2appstmstmstest.txt~
[16:02:28] NFS WRITE \?W:tms-vm-2appstmstms.test.txt.swp
[16:02:28] NFS LOOKUP \?W:tms-vm-2appstmstmstest.txt NOENT
[16:02:28] NFS CREATE \?W:tms-vm-2appstmstmstest.txt
[16:02:28] NFS SETATTR \?W:tms-vm-2appstmstmstest.txt
[16:02:28] NFS REMOVE \?W:tms-vm-2appstmstmstest.txt~
[16:02:29] NFS LOOKUP \?W:tms-vm-2appstmstms.test.txt.swp
[16:02:29] NFS REMOVE \?W:tms-vm-2appstmstms.test.txt.swp
It looks like vi
is «fixing» the problem because it performs a RENAME
of test.txt
to test.txt~
, and then does a CREATE
to replace the original file.
Compare this with touch
and output redirection which do not solve the problem:
touch test.txt
[16:02:12] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[16:02:12] NFS ACCESS \?W:tms-vm-2appstmstmstest.txt
[16:02:12] NFS SETATTR \?W:tms-vm-2appstmstmstest.txt
echo "" > test.txt
[16:08:47] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[16:08:47] NFS SETATTR \?W:tms-vm-2appstmstmstest.txt
[16:08:47] NFS WRITE \?W:tms-vm-2appstmstmstest.txt
Using echo "derp" > test.txt
will actually write content for the file back onto the host, however PHP is still unable to use the file.
Interestingly you can mv
the broken file around. Trying to use that broken file results in the same problem.
# Move the broken file
mv test.txt test2.txt
[16:10:06] NFS LOOKUP \?W:tms-vm-2appstmstmstest2.txt NOENT
[16:10:06] NFS GETATTR \?W:tms-vm-2appstmstmstest.txt
[16:10:06] NFS RENAME \?W:tms-vm-2appstmstmstest.txt \?W:tms-vm-2appstmstmstest2.txt
# Works: Kill with Ctrl-C before error occurs
php -f test.php
# Change the `FILENAME` constant to `test2.txt` in `test.php`
# Fails immediately
php -f test.php
Things I’ve tried that don’t help
- PHP
clearstatcache();
- echo 3 > /proc/sys/vm/drop_caches
Setup specifics
- Host: Windows 10 10.0.17134
- Virtualbox: 5.2.12r122591
- Vagrant: 2.1.1
- VM: Ubuntu 16.04.4 LTS (bento/ubuntu-16.04 @ 201806.08.0)
- Docker: Docker version 18.03.1-ce, build 9ee9f40
- Container: Amazon Linux 2 (2017.12) LTS Release Candidate
- Mount type: volume
- vagrant-winnfsd: 1.4.0
- NFS options: «`
mount_type = ‘nfs’
mount_options = [‘rw,vers=3,tcp,nolock’]
config.winnfsd.uid = 33
config.winnfsd.gid = 33
config.winnfsd.host_ip = «192.168.56.1»
config.winnfsd.logging = ‘on’