How to check if a PHP stream resource is readable or writable?
Asked Answered
U

2

22

In PHP, how do I check if a stream resource (or file pointer, handle, or whatever you want to call them) is either readable or writable? For example, if you're faced with a situation where you know nothing about how the resource was opened or created, how do you check if it's readable? And how do you check if it's writable?

Based on the testing that I've done (just with regular text files using PHP 5.3.3), fread() does not throw any errors at any level when the resource is not readable. It just returns an empty string, but it also does that for an empty file. And ideally, it would be better to have a check that doesn't modify the resource itself. Testing if a resource is readable by trying to read from it will change the position of the pointer.

Conversely, fwrite() does not throw any errors at any level when the resource is not writable. It just returns zero. This is slightly more useful, because if you were trying to write a certain number of bytes to a file and fwrite() returns zero, you know something went wrong. But still, this is not an ideal method, because it would be much better to know if it's writable before I need to write to it rather than trying to write to it and see if it fails.

Also, ideally, the check should work on any sort of stream resource, not just files.

Is this possible? Does anything like this exist? I have been unable to find anything useful. Thanks in advance for your answers.

Unaunabated answered 14/3, 2011 at 3:2 Comment(1)
Of course, both fread() and fwrite() return bool(false) on failure according to their respective documentation page.... But that doesn't happen in PHP 5.3.5.Disinterested
F
30

Quite simple. Just call stream_get_meta_data($resource) from your script, then check the mode array element of the return value:

$f = fopen($file, 'r');
$meta = stream_get_meta_data($f);
var_dump($meta['mode']); // r

And if you want to know if the underlying data is writable:

var_dump(is_writable($meta['uri'])); // true if the file/uri is writable
Flavorous answered 14/3, 2011 at 3:28 Comment(5)
The stream can be any stream created by fopen(), fsockopen() and pfsockopen(). Doesn't work on any files opened with fwrite? fread? Better than my solution in some cases, much, much worse in others... edit never mind, fread and fwrite USE handles opened by fopen. I'm an idiot.Dissyllable
@rock: you can't open a file with fwrite or fread. Those functions only work on opened streams. This function will work on all stream resources in PHP. So I fail to see how it could ever be much, much worse. In fact, this is 100% platform independent and will work with stream wrappers as well. So it's actually the correct solution...Flavorous
I think I like this. Do you know if the table of modes on the fopen() page in the manual is a completely exhaustive list?Unaunabated
@Jon: According to the source code, yes it is completely exhaustive. (Line 56 on in that file, incase you don't know C too well)...Flavorous
Wow. Thanks for going the extra mile to find that for me. That also gives me an idea of the logic I should use to parse the mode in my own code. Thanks.Unaunabated
D
1

Okay, so this may not be the best solution, but I think it suffices, given that there's nothing in PHP to do this automagically.

For the first step, you'll get the inode of the resource from the file, and then read the filename:

$stat = fstat($fp);
$inode = $stat['ino'];
system("find -inum $inode", $result);

Taken directly from this question about finding the filename from a filehandle.

Now that you have the filename (in $result) you can do a fileperms($result) on it to get the permissions out.

Note that fileperms() returns an int, and the documentation does the magic (actually just treating the int as an octal) of retaining that leading 0 (e.g. 0755).

Also note that the documentation does the magic of converting that int into a nice string like -rw-r--r--

Dissyllable answered 14/3, 2011 at 3:13 Comment(11)
Assuming the resource is a file to begin with. Could be a socket, or php://stdin.Disinterested
...and the system is *nix. It's not perfect by any means, but I think it's the best PHP can do?Dissyllable
It's also quite possible that the file may be writable, but that the filehandle is only open for reading. This is pretty clever though.Duleba
This is not even really an answer to my question, in my opinion. As Andrew said, this only works with files. Also, it doesn't even work on all *nix systems. That find command does not work on my OS X machine. Also, as Charles pointed out, just because the file is writable or readable, it doesn't mean the resource pointing to it is the same. I was asking about resources, not files.Unaunabated
Not to mention that fileperms is not a good way of checking if you can read or write to a file. more goes into it than that. If you really wanted to do this, call is_readable($file) and is_writable($file). But there's no reason to drop down to the inode level, when there are functions that provide direct access to the filename of an open stream...Flavorous
@irc How do you propose finding the filename to pass to is_readable and is_writeable?Dissyllable
@Jonathan I assumed since you were using "regular text files" to test, this solution would be helpful. edit by the way, here's a link to the OS X "find" commandDissyllable
@rock: by using the stream_get_meta_data() function that I link to and demo in my answer. It was built for this, and will work with all stream types, including stream wrappers.Flavorous
@Dissyllable I was testing with "regular text files" just to see how fread() would react to a non-readable resource, and how fwrite() would react to a non-writable resource. I also said, "Also, ideally, the check should work on any sort of stream resource, not just files." Also, I know that the find command exists on OS X. I was merely pointing out that the syntax of that particular command is not compatible with all forms of *nix.Unaunabated
@jonathan this will be my last comment, as I'm tired of arguing with you. <sarcasm> Excuse me for not telepathically guessing which operating system you are using, and subsequently changing MY answer to YOUR problem to fit YOUR needs exactly. </sarcasm> If you knew the find command exists, and you knew I was unaware of your operating environment, why didn't you tell me the OS, or use your brain to modify the command to work on your operating system? In addition, since I was unaware of the stream_get_meta_data() that @Flavorous posted, this was an attempt at being HELPFUL. My mistake.Dissyllable
The OS that I'm using is irrelevant. I was responding to your implication that your solution worked on all *nix systems. I agree that your solution was somewhat helpful, and I was not trying to argue with you (I apologize for giving that impression). However, your solution was not really a solution to the problem I was trying to solve.Unaunabated

© 2022 - 2024 — McMap. All rights reserved.