PHP中正确检查非标准文件描述符是否可写的方法
你好呀!首先得说,你目前用try-catch的写法其实是能正常工作的,但我懂你觉得这种“先尝试再捕获错误”的方式有点像“hack”,想找更直接的检查方法对吧?
为什么file_exists和is_writable对php://fd/$fd不生效
问题出在php://fd这种流的特性上——它是PHP提供的直接访问文件描述符的特殊流,不是文件系统里的真实文件。而file_exists和is_writable这类函数是针对文件系统路径设计的,它们无法识别这种伪协议流的状态,所以不管文件描述符是否有效,都会返回false,这就是你那套替代方案失效的核心原因。
更“优雅”的检查方式(不用try-catch)
在PHP里,我们可以用fopen来尝试打开文件描述符,通过判断是否能成功打开来验证它是否可写,而不是直接依赖错误捕获。下面是修改后的write2Fd函数:
function write2Fd(int $fd, string $contents, int $alt = 2) { // 尝试打开目标FD,用@抑制错误输出 $stream = @fopen("php://fd/$fd", 'w'); if ($stream !== false) { fwrite($stream, $contents); fclose($stream); } else { // 回退到备选文件描述符 file_put_contents("php://fd/$alt", $contents); } }
这里用@临时抑制了fopen的错误输出(因为我们自己会处理失败的情况),如果能成功打开流,说明文件描述符是可写的,就直接写入;如果打开失败,就回退到备选的FD。
补充:其实try-catch写法也不算“hack”
在Unix/Linux的编程哲学里,“先尝试操作再处理错误”(也就是EAFP:Easier to Ask for Forgiveness than Permission)是非常常见的做法,甚至在很多场景下比“先检查再操作”(LBYL:Look Before You Leap)更可靠——因为检查和操作之间可能会出现竞态条件(比如检查时FD还存在,但操作前被意外关闭了)。所以你最开始的写法其实是符合Unix风格的,完全没问题。
验证效果
用你原来的命令测试:
php test.php 3>fd3out.txt
终端依然会输出:
Writing to STDOUT! Writing to STDERR!
fd3out.txt里也会正常出现:
Writing to FD 3!
如果不指定3>fd3out.txt,程序会自动把内容写到STDERR里,和你预期的完全一致。
内容来源于stack exchange

