【问题标题】:script does not continue after command line命令行后脚本不继续
【发布时间】:2017-07-17 00:15:40
【问题描述】:

我的命令行脚本有问题。PHP 脚本永远不会继续。

尝试通过 putty 直接调用命令行,它会输出很多错误,但会立即返回/完成。为什么不返回 PHP 呢?

它适用于其他 PDF 文件,但不是这个

pdf

http://docdro.id/b0M5vfw

代码

$Cmd = new Command;
if($err = $Cmd->exec('/var/bin/poppler-0.51.0/utils/pdfimages -list /var/test.pdf')){
    echo "ERR: $err\n";
}
echo "continue\n";

class Command {
    private $descriptorspec;

    private $output = '';

    private $process;
    private $pipes = [];

    public function __construct(){
        $this->descriptorspec = [
            0 => ['pipe', 'r'], // stdin
            1 => ['pipe', 'w'], // stdout
            2 => ['pipe', 'w']  // stderr
        ];
    }

    public function output(): string{
        return $this->output;
    }

    public function close(){
        foreach($this->pipes as $pipe){
            if(is_resource($pipe)){
                fclose($pipe);
            }
        }

        proc_close($this->process);
    }

    public function exec(string $syntax){
        $this->process = proc_open($syntax, $this->descriptorspec, $this->pipes);
        fclose($this->pipes[0]);

        $this->output = stream_get_contents($this->pipes[1]);

        $stderr = stream_get_contents($this->pipes[2]);

        $this->close();

        return $stderr;
    }
}

错误

# /var/bin/poppler-0.51.0/utils/pdfimages -list /var/test.pdf
page   num  type   width height color comp bpc  enc interp  object ID x-ppi y-ppi size ratio
--------------------------------------------------------------------------------------------
   1     0 image    2154   303  rgb     3   8  jpeg   yes  [inline]     289   292    -    -
Syntax Error (50560): Illegal character '>'
Syntax Error (50560): Unknown operator '<10><07><82>;w<ad><a2><b4>2r<1f><10><07><8f>~j<c4>Hq<cf>Z<86>'
Syntax Error (50568): Unknown operator '<0f><b5>X<8f><ae><d0>:<d7>DU<91><cb>'v'
Syntax Error (50568): Illegal character ')'

........

Syntax Error (66698): Illegal character <04> in hex string
Syntax Error (66699): Illegal character <ff> in hex string
Syntax Error (66699): Illegal character <c1> in hex string
Syntax Error (66705): Unknown operator '<9b>'
Syntax Error (66714): Illegal character ')'
Syntax Error (66714): Unknown operator '<bc>q<ff>'
Syntax Error (66720): Unknown operator '<05>6<f8><c2><fa><d7><c3>?<f8>'
Syntax Error (66741): Unknown operator '<df><ec><99><e1>-'
Syntax Error (66743): Unknown operator ']'
Syntax Error (66762): Unknown operator '<cc>'
Syntax Error: Unterminated string
Syntax Error: End of file inside array
Syntax Error: End of file inside array
Syntax Error: Leftover args in content stream

【问题讨论】:

  • 您的命令是否要求用户(您)提供任何类型的输入?
  • @AdarshSojitra 不,它没有
  • 很快,我很确定这个 PDF 即将死去,因为内容流包含一个内联图像,以“BI”开头,后跟随机数据,以“EI”结尾。 Adobe 工程师在设计这些运算符时正在休息,问题是出现二进制数据随机包含“EI”并使 PDF 无法解析的情况。一些工具可能会更好地处理此问题,但理想情况下,此图像的制作者应避免使用内嵌图像。
  • 它直接从命令行运行良好

标签: php pdf command-line poppler


【解决方案1】:

问题是这个程序/var/bin/poppler-0.51.0/utils/pdfimages 没有写任何东西到stdout 并且你的代码挂在$this-&gt;output = stream_get_contents($this-&gt;pipes[1]); 因此你的类不适合这个程序。对于不向stdout 写入任何内容的程序,您不得从$this-&gt;pipes[1] 中读取。您应该有另一个用于这种特定类型应用程序的类:

class CommandWithNoOutput {
    private $descriptorspec;

    private $process;
    private $pipes = [];
    private $output = '';

    public function __construct(){
        $this->descriptorspec = [
            0 => ['pipe', 'r'], // stdin
            1 => ['pipe', 'w'], // stdout
            2 => ['pipe', 'w']  // stderr
        ];
    }

    public function output(): string{
        return (string)$this->output;
    }


    public function close(){
        foreach($this->pipes as $pipe){
            if(is_resource($pipe)){
                fclose($pipe);
            }
        }

        proc_close($this->process);
    }

    public function exec($syntax){

        $this->process = proc_open($syntax, $this->descriptorspec, $this->pipes);
        fclose($this->pipes[0]);

        $stderr = stream_get_contents($this->pipes[2]);

        $this->close();

        $this->output = ob_get_clean();

        return $stderr;
    }
}

$Cmd = new CommandWithNoOutput;
if($err = $Cmd->exec('/usr/bin/pdfimages -list test.pdf')){
    echo "ERR: $err\n";
}
echo "continue\n";

这段代码输出如下:

ERR: Syntax Error (50560): Illegal character '>'
Syntax Error (50560): Unknown operator '<10><07><82>;w<ad><a2><b4>2r<1f><10><07><8f>~j<c4>Hq<cf>Z<86>'
Syntax Error (50568): Unknown operator '<0f><b5>X<8f><ae><d0>:<d7>DU<91><cb>'v'
Syntax Error (50568): Illegal character ')'
Syntax Error (50570): Unknown operator '<15><c7>=j<c4>X<f4><e8>'
.....a lot of errors.....
Syntax Error (66762): Unknown operator '<cc>'
Syntax Error: Unterminated string
Syntax Error: End of file inside array
Syntax Error: End of file inside array
Syntax Error: Leftover args in content stream

continue

Process finished with exit code 0

更新: 另一种解决方案是在调用proc_open 之后立即调用stream_set_blocking($this-&gt;pipes[1], 0);,这样代码就不会等待任何输出。

【讨论】:

    【解决方案2】:

    您可以将stream_selectfeof 结合使用来检查两个读取流中的哪一个具有可用数据,如以下代码。

    我已经对其进行了测试(使用 PHP 7)并且它不会在此处阻塞(进行了修改)。

        public function exec(string $syntax){
            $this->process = proc_open($syntax, $this->descriptorspec, $this->pipes);
            fclose($this->pipes[0]);
    
            $stderr = "";
    
            $num_changed_streams = NULL;
            while (!feof($this->pipes[1]) || !feof($this->pipes[2])) {
              $read = [$this->pipes[1], $this->pipes[2]];
              $write = NULL;
              $err = NULL;
              $num_changed_streams = stream_select($read, $write, $err, 3);
              if ($num_changed_streams === false) {
                $this->close();
                return $stderr;
              } else {
                if (isset($read[0])) {
                  $this->output .= stream_get_contents($read[0]);
                  echo "output: {$this->output} ";
                }
                if (isset($read[1])) {
                  $stderr .= stream_get_contents($read[1]);
                  echo "stderr: {$stderr}";
                }
              }
            }
            $this->close();
            return $stderr;
        }
    

    需要stream_selectfeof 函数,原因如下(引用自http://php.net/manual/en/function.stream-select.php):

    将监视读取数组中列出的流,以查看字符是否可用于读取(更准确地说,查看读取是否不会阻塞 - 特别是,流资源在文件结束时也已准备好,在这种情况下, fread() 将返回一个长度为零的字符串)。

    【讨论】:

      【解决方案3】:

      PDF 有问题 - @dwarring 在 cmets 中已经避开了这一点(在此引用以感谢评论者)

      @dwarring 说:“很快,我很确定这个 PDF 即将死去,因为内容流包含一个内联图像,以 'BI' 开头,后跟随机数据,以 'EI' 结尾。Adobe 工程师正在当他们设计这些运算符时,问题是出现二进制数据随机包含“EI”并使PDF无法解析的情况。一些工具可能会更好地处理这个问题,但理想情况下,该图像的制作者应该避免使用内嵌图片。”

      不过,从 PHP 方面来说,使用 try/catch 块代替 if 语句,您应该保留对脚本的控制权。

      $Cmd = new Command;
      
      try {
          $err = $Cmd->exec('/var/bin/poppler-0.51.0/utils/pdfimages - list/var/test.pdf')){
      } catch (Exception $e) {
          var_log($e);
      }
      
      echo "continue\n";
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-11-25
        • 1970-01-01
        • 2017-10-31
        • 2015-04-24
        • 2015-05-28
        • 1970-01-01
        相关资源
        最近更新 更多