-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[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)); | ||
|
|
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)) { |
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
| 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.
$filename, $content
c185ecd to
ec7c144
Compare
|
Travis failure is unrelated |
| * @throws IOException If the file is not writable | ||
| */ | ||
| public function appendToFile($filename, $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.
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..
| * Atomically dumps content into a file. | ||
| * | ||
| * @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)) { |
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
|
|
||
| /** | ||
| * Appends content to an existing 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.
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 | ||
| * |
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.