Запуск Google Chrome без PHP с помощью exec не возвращает выходные данные до перезапуска IIS - PullRequest
6 голосов
/ 31 марта 2019

Моя среда - Windows Server 2016 и IIS 10. В моем сценарии PHP я пытаюсь запустить Google Chrome в режиме без головы, чтобы получить HTML-код внешней веб-страницы:

<?php
$chromeApp = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";

$command = "\"$chromeApp\" --headless --disable-gpu \
 --dump-dom $urladdress > page.html";

exec ($command);
?>

Этот код работает, если я запускаю

>C:\php script.php

из командной строки. Это также работает, если я запускаю фактическую команду:

>"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" \
--headless --disable-gpu --dump-dom https://google.com > page.html

Но если я запускаю этот скрипт из браузера, он создает пустой файл page.html и работает до истечения времени ожидания. Однако, если я перезагружаю IIS во время его выполнения, я получаю файл page.html, заполненный необходимыми данными.

В чем здесь проблема?

Ответы [ 2 ]

0 голосов
/ 05 апреля 2019

это не ответ, но слишком много, чтобы поместить его в комментарий, exec () на самом деле не дает большой обратной связи,

сначала не делайте этого:

$command = "\"$chromeApp\" ";

потому что разные оболочки не могут договориться о том, как вещи должны быть заключены в кавычки, поэтому вы должны вместо этого использовать функцию escapeshellarg (), также не делайте этого

--dump-dom $urladdress > page.html

, потому что $ urladdress может потребоваться экранировать (и если хакеры могут контролировать ваш $ urladdress, то это на самом деле уязвимость к выполнению произвольного кода), вместо этого сделайте следующее:

$command = escapeshellarg($chromeApp)." --headless --disable-gpu \
 --dump-dom ".escapeshellarg($urladdress)." > page.html";

(и если у вашего page.html также могут быть имена со специальными символами,Вы должны запустить это имя также через escapeshellarg ().)

, но замените exec () на proc_open, скажите, что вы получите, запустив это:

<?php
declare(strict_types=1);
$urladdress="http://google.com";
$chromeApp = _cygwinify_filepath("C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe");
$command = escapeshellarg($chromeApp)." --headless --disable-gpu --dump-dom ".escapeshellarg($urladdress);
$descriptorspec = array(
    0 => array("pipe", "rb"),  // by default stdin is inherited, we don't want that so we create a stdin pipe just so we can fclose() it.
    1 => array("pipe", "wb"),  // stdout
    2 => array("pipe", "wb"),  // stderr
);

$proc=proc_open($command,$descriptorspec,$pipes);
if(!$proc){
    throw new \RuntimeException("failed to create process! \"{$command}\"");
}
$stdout="";
$stderr="";
$fetch=function()use(&$stdout,&$stderr,&$pipes){
    $tmp=stream_get_contents($pipes[1]);
    if(is_string($tmp) && strlen($tmp) > 0){
        $stdout.=$tmp;
    }
    $tmp=stream_get_contents($pipes[2]);
    if(is_string($tmp) && strlen($tmp) > 0){
        $stderr.=$tmp;
    }
};
fclose($pipes[0]);
$status=array();
while(($status=proc_get_status($proc))['running']){
    $fetch();
}
$fetch();
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($proc);
var_dump($stdout,$stderr,$status);


     function _uncygwinify_filepath(string $path) : string
    {
        static $is_cygwin_cache = null;
        if ($is_cygwin_cache === null) {
            $is_cygwin_cache = (false !== stripos(PHP_OS, "cygwin"));
        }
        if ($is_cygwin_cache) {
            return trim(shell_exec("cygpath -aw " . escapeshellarg($path)));
        } else {
            return $path;
        }
    }
    function _cygwinify_filepath(string $path) : string
    {
        static $is_cygwin_cache = null;
        if ($is_cygwin_cache === null) {
            $is_cygwin_cache = (false !== stripos(PHP_OS, "cygwin"));
        }
        if ($is_cygwin_cache) {
            return trim(shell_exec("cygpath -a " . escapeshellarg($path)));
            //return "/cygdrive/" . strtr($path, array(':' => '', '\\' => '/'));
        } else {
            return $path;
        }
    }

edit: я написал use(&$stdout,$stderr,&$pipes) вместо use(&$stdout,&$stderr,&$pipes), извините, исправлено.перезапустите его, если вы только что запустили его без этого исправления.

0 голосов
/ 03 апреля 2019

У вас есть 4 процесса в игре здесь.

  • W3WP.exe
  • PHP.exe
  • CMD.exe
  • Chrome.exe

CMD.exe принимает вывод Chrome.exe и передает его в ваш файл.Он будет делать это после завершения Chrome.exe или может делать это частично с перерывами.Когда я запускаю код, подобный вашему выше, мой Chrome.exe не завершается.Я вижу, что Chrome.exe все еще работает в TaskManager, потребляя 25% ЦП (100% на одном из моих ядер).

Я предполагаю, что перезапуск IIS каким-то образом вызывает сбрасывание команд.В большинстве моих случаев в файле page.html до выполнения IISReset были данные, хотя и не все.(Проводник Windows показал 0 КБ, но открытие файла показало данные в этом файле.)в неинтерактивном сеансе.

...