Home » Php » php – script does not continue after command line

php – script does not continue after command line

Posted by: admin July 12, 2020 Leave a comment

Questions:

I have a problem with a script with a command line.. The PHP script never continue..

Have tried to call the command line directly through putty and it outputs alot of errors but returns/completes instantly. Why doesn’t it then return back to PHP?

It works fine with other PDF files, but not this one

pdf

http://docdro.id/b0M5vfw

code

$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

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;
    }
}

error

# /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
How to&Answers:

The PDF is problematic – @dwarring already eluded to this in the comments (quoted here to credit the commenter)

@dwarring said “just quickly, I’m pretty sure that this PDF is dying because the content stream contains an inline image started by and ‘BI’ followed by random data and ended by ‘EI’. The Adobe engineers were having an off-day when they designed these operators, the problem being that situations arise where the binary data randomly contains ‘EI’ and makes the PDF unparsable. Some tools may handle this better, but ideally the producer of this image should avoid the use of inline images.”

From the PHP side of things, though, instead of an if statement use a try/catch block and you should retain control of the script.

$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";

Answer:

You could use stream_select in conjunction with feof to check which of the two read streams have data available, like the following code.

I’ve tested it (using PHP 7) and it does not block here (with the modifications made).

    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;
    }

The stream_select and feof functions are needed because of the following (cited from http://php.net/manual/en/function.stream-select.php):

The streams listed in the read array will be watched to see if characters become available for reading (more precisely, to see if a read will not block – in particular, a stream resource is also ready on end-of-file, in which case an fread() will return a zero length string).

Answer:

The problem is that this program /var/bin/poppler-0.51.0/utils/pdfimages does not write anything to the stdout and your code hangs at $this->output = stream_get_contents($this->pipes[1]); hence your class is not good for this program. For programs that don’t write anything to stdout you must not read from $this->pipes[1]. You should have another class that is used for this particular type of applications:

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";

This code outputs this:

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

UPDATE:
Another solution is to call stream_set_blocking($this->pipes[1], 0); immediately after the call to proc_open so the code will not wait for any output.