Files
2026-05-23 22:10:14 +08:00

153 lines
4.7 KiB
PHP

<?php
/*-----------------------------------------------------+
* 执行一些操作系统相关的操作
* @author yeahoo2000@gmail.com
+-----------------------------------------------------*/
class OS{
protected $cwd;
protected $env;
protected $descriptorspec = [
0 => ["pipe", "r"], // stdin
1 => ["pipe", "w"], // stdout
2 => ["pipe", "w"], // stderr
];
public function __construct($cwd = '', $env = [], $descriptorspec = null){
if(empty($cwd)){
$this->cwd = ROOT.'/var';
}
$env = array_merge(['HOME' => $_SERVER['HOME']], $env);
$this->env = new ObjectBaseArray($env);
if(is_array($descriptorspec)){
$this->descriptorspec = $descriptorspec;
}
}
public function setCwd($cwd){
$this->cwd = $cwd;
return $this;
}
public function setEnv($env){
$this->env = array_merge($this->env, $env);
return $this;
}
/**
* 执行一个操作系统命令,并返回退出状态码
* 0: 表示正常退出
* 其它值: 表示执行的命令发生了错误,具体意义要看被调用的命令
*
* # 支持设置环境变量
* # 支持stdin输入设定
* # stdout, stderr输出分离
*
* 使用示例:
* 被调用的命令:test.sh
* #!/bin/sh
* read -p "输入循环次数:" i
* while [ $i -gt 0 ]; do
* echo "stdout: $i"
* >&2 echo "stderr: $i"
* i=$(($i-1))
* sleep 1
* done
*
* PHP调用代码:
* $stdin = "3";
* App::os()->exec("bash test.sh", $stdout, $stderr, $stdin);
* echo "stdout:\n$stdout\n\nstderr:\n$stderr";
*/
public function exec($cmd, &$stdout = '', &$stderr = '', $stdin = ''){
App::debug("执行操作系统命令: $cmd");
$proc = proc_open($cmd, $this->descriptorspec, $pipes, $this->cwd, $this->env->getArray());
$txOff = 0; $txLen = strlen($stdin);
$stdoutDone = false;
$stderrDone = false;
// 设置所有管道为非阻塞模式
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
if($txLen == 0) fclose($pipes[0]);
while(true){
$rx = array();
if(!$stdoutDone) $rx[] = $pipes[1];
if(!$stderrDone) $rx[] = $pipes[2];
$tx = array();
if($txOff < $txLen) $tx[] = $pipes[0];
$ex = null;
// 查询管道状态
stream_select($rx, $tx, $ex, null, null);
// 将$stdin变量中的内容写入stdin管道
if(!empty($tx)){
$txRet = fwrite($pipes[0], substr($stdin, $txOff, 8192));
if($txRet !== false) $txOff += $txRet;
if($txOff >= $txLen) fclose($pipes[0]);
}
foreach($rx as $r){
if($r == $pipes[1]){
// 读取stdout管道中的内容
$stdout .= fread($pipes[1], 8192);
if(feof($pipes[1])){
fclose($pipes[1]);
$stdoutDone = true;
}
}else if($r == $pipes[2]){
// 读取stderr管道中的内容
$stderr .= fread($pipes[2], 8192);
if(feof($pipes[2])){
fclose($pipes[2]);
$stderrDone = true;
}
}
}
if(!is_resource($proc)) break;
if($txOff >= $txLen && $stdoutDone && $stderrDone) break;
}
return proc_close($proc);
}
/**
* 递归删除指定目录下的所有文件和目录
* @param string $name 目录路径或文件路径
* @return bool 是否成功
*/
public static function removeFile($name) {
$path = dirname($name);
if(is_file($name)){
$result = @unlink($name)? true : false;
}
if(is_dir($name)){
$handle = opendir($name);
while(($file = readdir($handle)) !== false){
if($file == '.' || $file == '..') continue;
$dir= $name.DIRECTORY_SEPARATOR.$file;
is_dir($dir) ? OS::removeFile($dir) : @unlink($dir);
}
closedir($handle);
$result = @rmdir($name) ? true : false;
}
return $result;
}
/**
* 写文件系统
* @param string $file 文件名
* @param string $content 内容
*/
public static function writeFile($file, $content, $mode='wb'){
$oldMask= umask(0);
$fp= @fopen($file, $mode);
fwrite($fp, $content);
fclose($fp);
umask($oldMask);
}
}