-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Filesystem] Add appendToFile() #20612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
95aec8b
to
0e207b0
Compare
We could also go with |
@ro0NL We could and it would make sense imho. Though I'm comfortable with what is proposed here because it's something I actually needed a lot of times, I have concrete use cases for it. |
The implementation looks very fragile to me: it's not atomic at all; why should it fail when the file doesn't exist? |
You're right, it should not fail by default, but I think it should be opt-in for cases where the content must not be appended if the file doesn't already exist.
To ease error handling as Given the whole |
5bef77b
to
35d33fa
Compare
Removed the exception when the file doesn't exist. Now this behaves like |
88a308c
to
fc87aa0
Compare
|
||
$tmpFile = $this->tempnam(dirname($filename), basename($filename)); | ||
|
||
if (false === $originalContent = @file_get_contents($filename)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about forwarding to dumpFile
instead in this case? So above checks can be removed..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, done 👍
fc87aa0
to
35eb1cb
Compare
public function appendToFile($filename, $content) | ||
{ | ||
if (false !== $originalContent = @file_get_contents($filename)) { | ||
$originalContent = ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$content = $originalContent.$content;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops! thanks @ro0NL
$originalContent = ''; | ||
} | ||
|
||
return $this->dumpFile($filename, $originalContent.$content); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$filename, $content
c185ecd
to
ec7c144
Compare
Travis failure is unrelated |
*/ | ||
public function appendToFile($filename, $content) | ||
{ | ||
if (false !== $originalContent = @file_get_contents($filename)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now loads the current file content into memory (which doesn't look like an atomic append to me). Wouldn't it be better to rely on the FILE_APPEND
flag of file_put_contents
instead?
You could refactor a bit the logic inside dumpFile
in order to extract it in a private method adding the flag or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which doesn't look like an atomic append to me
The write is atomic, using file_put_contents
for the read operation would not make it more atomic to me, do I miss something?
You could refactor a bit the logic inside dumpFile in order to extract it in a private method adding the flag or not.
It was my first intention, but would you avoid creating a tmpfile for append? Otherwise we still have to use file_get_contents
or doing two file_put_contents
,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do FILE_APPEND|LOCK_EX
. Basically creating the tmp file is about forcing an exclusive lock right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do FILE_APPEND|LOCK_EX
Yeah, I was about to propose it :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im not an filesystem expert.. but the tmp file in the middle feels like a poor man solution to me.. so perhaps LOCK_EX
is the way to go indeed for dumpeFile
as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using file_put_contents for the read operation would not make it more atomic to me
Never told you to do so ^^'
We could do FILE_APPEND|LOCK_EX
That's basically what I meant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what about a public writeFile($filename, $content[, $append = false])
then? Using LOCK_EX
by design and let dumpFile
forward? That to me would be the simplest implementation.
But a private function in the middle allowing this API (appendToFile
and dumpFile
) is also fine 👍 i guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, using LOCK_EX
doesn't make file_put_contents
atomic, so in both cases (using file_get_contents/file_put_contents
or file_put_contents
with flags) the append would not be atomic. Considering this I prefer to let dumpFile()
as it is since the behavior is better (maybe not really atomic, but more than using file_put_contents
directly).
Refs:
#10018 (comment)
http://stackoverflow.com/questions/4899737/should-lock-ex-on-both-read-write-be-atomic
About appendToFile
, I'm still not sure what is better between using file_get_contents
or file_put_contents
with FILE_APPEND, but I tend to prefer using file_get_contents
to avoid having file_put_contents
breaks in the middle of the write..
@@ -624,7 +624,7 @@ public function tempnam($dir, $prefix) | |||
* @param string $filename The file to be written to | |||
* @param string $content The data to write into the file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For using file_put_contents
we technically allow string|array|resource
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, updated on the two newly added methods.
Since this targets master, would you mind to open a PR against 2.8 (it doesn't handle streams in 2.7, see 0fc513e) for changing this docblock? I think it's worth it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree.
ec7c144
to
9908df7
Compare
👍 We just need to validate there isn't any downside about using Status: Reviewed |
9908df7
to
2e21cf5
Compare
286de84
to
86fe81a
Compare
5681f39
to
4e737b4
Compare
$dir = dirname($filename); | ||
|
||
if (!is_dir($dir)) { | ||
$this->mkdir($dir); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we have to check here too that the directory is writable after we created it (the directory might have been created by another process)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, done. The same could be applied on dumpFile()
I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think you are right. Should be done on 2.7
then as bug fix IMO.
4e737b4
to
9fb4050
Compare
@@ -649,6 +649,31 @@ public function dumpFile($filename, $content) | |||
} | |||
|
|||
/** | |||
* Appends content to an existing file. | |||
* | |||
* @param string $filename The file for which to append content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file to which to append content
👍 |
9fb4050
to
0e52144
Compare
…d it in dumpFile() (chalasr) This PR was merged into the 2.7 branch. Discussion ---------- [Filesystem] Check that directory is writable after created it in dumpFile() | Q | A | ------------- | --- | Branch? | 2.7 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #20612 (comment) | License | MIT | Doc PR | n/a In case permissions have been changed meanwhile Commits ------- dbc4148 [Filesystem] Check that the directory is writable after created it in dumpFile()
* @param string $filename The file to which to append content | ||
* @param string $content The content to append | ||
* | ||
* @throws IOException If the file is not writable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remove the capital letter above, you should do the same here, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted the removal of the capital letter
0e52144
to
9fb5293
Compare
Can you add a note in the CHANGELOG? |
9fb5293
to
1f7b753
Compare
CHANGELOG updated |
Thank you @chalasr. |
This PR was merged into the 3.3-dev branch. Discussion ---------- [Filesystem] Add appendToFile() | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | todo So we could append content to a file: ```php (new Filesystem)->appendToFile($file, 'bar'); ``` instead of doing it in two steps: ```php if (false === $content = @file_get_contents($file)) { throw new \Exception(); } (new Filesystem)->dumpFile($file, $content.'bar'); ``` Doing it opt-in using `dumpFile(..., $append = false)` would have been enough but not possible for BC. Commits ------- 9fb5293 [Filesystem] Add appendToFile()
This PR was merged into the 3.3-dev branch. Discussion ---------- [Filesystem] Add appendToFile() | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | todo So we could append content to a file: ```php (new Filesystem)->appendToFile($file, 'bar'); ``` instead of doing it in two steps: ```php if (false === $content = @file_get_contents($file)) { throw new \Exception(); } (new Filesystem)->dumpFile($file, $content.'bar'); ``` Doing it opt-in using `dumpFile(..., $append = false)` would have been enough but not possible for BC. Commits ------- 1f7b753 [Filesystem] Add appendToFile()
I'm super late to this but I wonder if this method should be called |
I'm fine with the But that's only my 2 cents :) |
@javiereguiluz I hesitated for |
@fabpot I think there is something wrong with your git tool, commits are merged twice, see the master log |
Commits' contents aren't even the same... 😅 Also, the merge commit date is before the merge itself 😆 |
So we could append content to a file:
instead of doing it in two steps:
Doing it opt-in using
dumpFile(..., $append = false)
would have been enough but not possible for BC.