How to run a php script from the browser using popen?

(PHP 4, PHP 5, PHP 7, PHP 8)

popenOpens process file pointer

Description

popen(string $command, string $mode): resource|false

Parameters

command

The command

mode

The mode. Either 'r' for reading, or 'w' for writing.

On Windows, popen() defaults to text mode, i.e. any \n characters written to or read from the pipe will be translated to \r\n. If this is not desired, binary mode can be enforced by setting mode to 'rb' and 'wb', respectively.

Return Values

Returns a file pointer identical to that returned by fopen(), except that it is unidirectional (may only be used for reading or writing) and must be closed with pclose(). This pointer may be used with fgets(), fgetss(), and fwrite(). When the mode is 'r', the returned file pointer equals to the STDOUT of the command, when the mode is 'w', the returned file pointer equals to the STDIN of the command.

If an error occurs, returns false.

Examples

Example #1 popen() example

$handle popen("/bin/ls""r");
?>

If the command to be executed could not be found, a valid resource is returned. This may seem odd, but makes sense; it allows you to access any error message returned by the shell:

Example #2 popen() example

error_reporting(E_ALL);/* Add redirection so we can get stderr. */
$handle popen('/path/to/executable 2>&1''r');
echo 
"'$handle'; " gettype($handle) . "\n";
$read fread($handle2096);
echo 
$read;
pclose($handle);
?>

Notes

Note:

If you're looking for bi-directional support (two-way), use proc_open().

See Also

  • pclose() - Closes process file pointer
  • fopen() - Opens file or URL
  • proc_open() - Execute a command and open file pointers for input/output

webmaster at php-idee dot de

13 years ago

If you try to execute a command under Windows the PHP script normally waits until the process has been terminated. Executing long-term processes pauses a PHP script even if you don't want to wait for the end of the process.

It wasn't easy to find this beautiful example how to start a process under Windows without waiting for its termination:

$commandString = 'start /b c:\\programToRun.exe -attachment "c:\\temp\file1.txt"';
pclose(popen($commandString, 'r'));
?>

rockytriton

14 years ago

Note, when using this with a batch file in windows, you must put an "exit" at the end of your batch file or you will get a new cmd.exe stuck in your process list every time you execute the page.

anonymous at anon dot com

7 years ago

As a side note to the code provided by anonymous at anon dot com:

  $cmd = "php longscript.php";

  function execInBackground($cmd) { 
    if (substr(php_uname(), 0, 7) == "Windows"){
        pclose(popen("start /B ". $cmd, "r"));
    } else {
        exec($cmd . " > /dev/null &");  
    }
  }

I had a problem where Windows would close the call too fast before the entire script was interpreted, but I didn't want my main script to hang until it would be fully loaded.

As a workaround, I called a tiny .php script which would then call the larger script.

myfile.php:
  $cmd = "php timewrapper.php";

  function

execInBackground($cmd) { 
    if (
substr(php_uname(), 0, 7) == "Windows"){
       
pclose(popen("start /B ". $cmd, "r"));
    } else {
       
exec($cmd . " > /dev/null &");  
    }
  }
?>

timewrapper.php:
  $cmd = "php longscript.php";
 
$timer = popen("start /B ". $cmd, "r");
 
sleep(30);
 
pclose($timer);
?>

This way my main script would continue to run without having to pause, while the tiny script pauses while it loads the larger file.

anonymous at anon dot com

11 years ago

If, on windows, you need to start a batch file that needs administrator privileges, then you can make a shortcut to the batch file, click properties, check to on "run as administrator" on one of the property pages, and then double-click the shortcut once (to initialize that "run as administrator" business).

using popen("/path/to/shortcut.lnk") will then run your batch file with administrator privileges.

handy for when you want to use cli php to do some long running tasks and that php-cli needs to use sessions..

jlh

6 years ago

Don't expect this function to return false when the executable doesn't exist in the first place. A stream will be opened anyway but nothing can be read from it. An error similar to "sh: 1: asdfasdfasdf: not found" will be printed to STDERR.

Solution 1: Look at the return value of pclose(), it will be the exit status of the shell that runs the command. On Linux it will be 127 if the executable wasn't found. Otherwise it's the exit status of the executable itself.

Solution 2: Use proc_open() instead, which allows to also capture STDERR and then parse it for errors.

You probably should do both.

rjl at xs4all dot nl

16 years ago

Truncated output from ps command?

The solution lies in the way ps displays it's info
specifically the -w option which:
'uses 132 columns to display information,
instead of the default which is your window size.'....
somehow with fgets in php that results in 74 characters
regardless off the init length parameter

a bit of code:

echo '

' . "\n";
$fp=popen("/bin/ps -waux","r");
while (!
feof($fp)) {
   
$buffer = fgets($fp, 4096);
   
$croninf .= '
' . "\n";
}
pclose($fp);
echo
$croninf;
echo
'
cron
' . $buffer . '


' . "\n";
?>

Ciao,

Rene =<>=

Marbug at gmail dot com

13 years ago

If you want to download files from a linux server with a filesize bigger than 2GB you can use the following:

function serveFile( $file , $as ){
   
header( 'Expires: Mon, 1 Apr 1974 05:00:00 GMT' );
   
header( 'Pragma: no-cache' );
   
header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
   
header( 'Content-Description: File Download' );
   
header( 'Content-Type: application/octet-stream' );
   
header( 'Content-Length: '.trim(`stat -c%s "$file"`) );
   
header( 'Content-Disposition: attachment; filename="'. $as .'"' );
   
header( 'Content-Transfer-Encoding: binary' );
   
//@readfile( $file ); flush();
   
$fp = popen("tail -c ".trim(`stat -c%s "$file"`)." ".$file.' 2>&1', "r");
    while(!
feof($fp))
    {
       
// send the current file part to the browser
       
print fread($fp, 1024);
       
// flush the content to the browser
       
flush();
    }
   
fclose($fp);
}
?>

cyberlot at cyberlot dot net

20 years ago

The below code works for both way processing ;) Have fun folks

    system("mkfifo pipeout");
  
$pipe = popen("./nwserver -module Chapter1E > pipeout","w");
  
$pipeout = fopen("pipeout", "r");
   while (
$s = fgets($pipeout,1024)) {
    echo
$s;
   }
?>

hacklor [AT] NOSPAM [DOT] com

12 years ago

There is a simple way to start a process in the background but still find out what the process result is. I combined the information from some users below with some of my own coming up with the following:

$bat_filename = "C:\\my_bat_file.bat";
$bat_log_filename = "C:\\my_bat_file_bat.log";
$bat_file = fopen($bat_filename, "w");
if(
$bat_file) {
   
fwrite($bat_file, "@echo off"."\n");
   
fwrite($bat_file, "echo Starting proces >> ".$bat_log_filename."\n");
   
fwrite($bat_file, "php c:\\my_php_process.php >> ".$bat_log_filename."\n");
   
fwrite($bat_file, "echo End proces >> ".$bat_log_filename."\n");
   
fwrite($bat_file, "EXIT"."\n");
   
fclose($bat_file);
}
//
// Start the process in the background
//
$exe = "start /b ".$bat_filename;
if(
pclose(popen($exe, 'r')) ) {
    return
true;
}
return
false;
?>

In my case the file names of the .bat and .log files weren't always the same, so I needed a dynamic way to create the .bat file. The output from the php command is saved to the log file with the >> command. All prints and errors are stored there. At a later time you can open the log file and see what happened.

don at digithink dot com

16 years ago

// The above import function can be easily extended using
// /usr/local/bin/xls2csv (part of catdoc ) and popen
// to read excell files directly.
// In our particular application the first line was the file heading.
function importxls($file,$head=true,$throwfirst=true,$delim=",",$len=1000) {
  
$return = false;
  
$handle = popen("/usr/local/bin/xls2csv $file", "r");
// or die if not there.
  
if ($throwfirst) {
      
$throw = fgetcsv($handle, $len, $delim);
   }
   if (
$head) {
      
$header = fgetcsv($handle, $len, $delim);
   }
   while ((
$data = fgetcsv($handle, $len, $delim)) !== FALSE) {
       if (
$head AND isset($header)) {
           foreach (
$header as $key=>$heading) {
              
$row[$heading]=(isset($data[$key])) ? $data[$key] : '';
               print
"

  • ". $heading ."=>" . $row[$heading]."
  • ";
               }
              
    $return[]=$row;
           } else {
              
    $return[]=$data;
           }
       }
      
    fclose($handle);
       return
    $return;
    }
    ?>

    erco at seriss dot com

    3 years ago

    Another workaround for using popen() with "w" mode so that the stdout of the command reaches the browser:

    An easy solution is to have two php scripts; "real.php" with the popen($cmd, "w") command in it, the other being "wrapper.php", a one liner that simply invokes system("php real.php");

    Invoking "wrapper.php" from the browser allows the popen($cmd,"w") in "real.php" to work as expected, such that stdout of $cmd reaches the browser. If you try to skip the wrapper and just run "real.php", stdout of $cmd is lost to /dev/null.

    antman3351

    11 months ago

    Note for Windows users using popen with start to run an external script without having php wait.

    e.g.:
    pclose( popen(  'start /b php someLongScript.php *> nul', 'rb' ) );

    If start can't find the exe it will open a popup message and pclose hangs until the popup is closed.

    mrjake2

    11 years ago

    If you are running in a chroot'ed environment on Debian "Squeeze", this command won't work; there is a problem with the kernel code that popen() eventually calls.

    Note that pecl makes heavy use of this command, so if you are running in this environment you will need to install the pecl extension from source instead.

    atampone at NOSPAMFORME dot trdsupra dot com

    17 years ago

    If you want to fork a process under windows, this is the function to use.  I created a batch file called runcmd.bat with the following line

    start %1 %2 %3 %4

    then I have the folowing function

    define('RUNCMDPATH', 'c:\\htdocs\\nonwebspace\\runcmd.bat');

    function

    runCmd($cmd) {
       
    $externalProcess=popen(RUNCMDPATH.' '.$cmd, 'r');
       
    pclose($externalProcess);
    }   
    ?>

    with this, doing something like

    ('php.exe printWorkOrder.php 3498'); ?>
    will launch php.exe outside of apache and allow the script calling the runCmd() function to continue without waiting for the command line process to return.  The process will run under the same user account that Apache (or whatever webserver you're running) is running under, so make sure it has permissions to do whatever you need to do.  Also, make sure that the batch file has enough %n s in order to pass all the command line variables that you might need to pass.

    Special thanks to kicken from the devshed forums for coming up with the idea.

    linuxdude010 at yahoo dot com

    20 years ago

    I had all kinds of trouble encrypting a message with PGP, but I finanlly got it to work.  The trick was to 'chmod o+r pubring.pkr' so that the apache server could read the public keys!!!  Then, this function worked fine:

    function pgp_encrypt($keyring_location, $public_key_id, $plain_text) {$key_id = EscapeShellArg($public_key_id);
           
    putenv("PGPPATH=$keyring_location");// encrypt the message
           
    $pipe = popen("pgpe -r $key_id -af", "r");              
           
    fwrite($pipe, $plain_text);
           
    $encrypted_text = '';
            while(
    $s = fgets($pipe, 1024)) {
                   
    // read from the pipe
                   
    $encrypted_text .= $s;
            }
           
    pclose($pipe);

            return

    $encrypted_text;
    }
    $message = pgp_encrypt("/home/username/.pgp", "", "dummy text to be encrypted");
    print
    nl2br($message);?>

    http://vmlinuz.nl/about/contact/

    19 years ago

    From the popen linux programmers manual:

    "The  command  argument  is  a pointer to a null-terminated string containing a shell command line.  This  command  is passed  to  /bin/sh  using the -c flag."

    Since php uses this popen function, you need to be sure /bin/sh exists. This file may not exist in chroot()ed environments.

    PGP Dude

    17 years ago

    I should say, my host uses a modified form of safe mode, so I don't know if that might have caused a problem with "popen" as opposed to "proc_open".  With safe mode enabled, all words following the initial command string are treated as a single argument. Thus, echo y | echo x becomes echo "y | echo x".  [Because of this,] LinixDude010's srcipt did not work for me.  Seems wrong to read and write with popen, according to the manual.

    The script produced pgp text, but there was something wrong with the text and I could not decode it.

    This replacement script, using proc_open, which can read and write, DOES work:

    function pgp_encrypt($keyring_location, $public_key_id, $plain_text) {
     
    $encrypted_text='';
     
    $key_id = EscapeShellArg($public_key_id);
     
    putenv("PGPPATH=$keyring_location"); // encrypt the message
     
    $descriptorspec = array(
       
    0 => array("pipe", "r"),  // stdin
       
    1 => array("pipe", "w"),  // stdout
       
    2 => array("pipe", "w")   // stderr ?? instead of a file
     
    );
     
    $process = proc_open("pgpe -r $key_id -af", $descriptorspec, $pipes);
      if (
    is_resource($process)) {
       
    fwrite($pipes[0], $plain_text);
       
    fclose($pipes[0]);
        while(
    $s= fgets($pipes[1], 1024)) {
             
    // read from the pipe
             
    $encrypted_text .= $s;
        }
       
    fclose($pipes[1]);
       
    // optional:
       
    while($s= fgets($pipes[2], 1024)) {
         
    $encrypted_text.= "\n

    Error: $s

    \n";
        }
       
    fclose($pipes[2]);
      }
      return
    $encrypted_text;
    }
    $message = pgp_encrypt("/home/username/.pgp", "", "dummy text to be encrypted");
    print
    nl2br($message); ?>

    Cride5

    16 years ago

    Here is a nice little script for monitoring your http access log.

    $handle

    = popen("tail -f /etc/httpd/logs/access.log 2>&1", 'r');
    while(!
    feof($handle)) {
       
    $buffer = fgets($handle);
        echo
    "$buffer
    \n"
    ;
       
    ob_flush();
       
    flush();
    }
    pclose($handle);?>

    ----
    www.eviltree.co.uk
    www.solidsites.co.uk
    www.mongbong.com

    ajv-php at erkle dot org

    20 years ago

    I noticed that some of the examples above seem to advocate passing unencrypted data to gpg via the pipe shell escape, in the absence of a bi-directional popen (on some OSes).

    The approach I've taken is similar to:

      $prefix = 'example';
     
    $command = '/usr/local/bin/gpg --encrypt --armor --no-tty --batch --no-secmem-warning --recipient ""';
     
    $tmpfile = tempnam('/tmp', $prefix);
     
    $pipe = popen("$command 2>&1 >$tmpfile", 'w');
      if (!
    $pipe) {
       
    unlink($tmpfile);
      } else {
       
    fwrite($pipe, $plaintxt, strlen($plaintxt));
       
    pclose($pipe);
       
    $fd = fopen($tmpfile, "rb");
       
    $output = fread($fd, filesize($tmpfile));
       
    fclose($fd);
       
    unlink($tmpfile);
      }
      return
    $output;
    ?>

    This means that unencrypted information is not passed via a (potentially readable) shell command, and only encrypted information gets stored on disc.

    php at keithtyler dot net

    12 years ago

    Note that you *have* to do a read on the handle before you can feof(), even if the command outputs nothing! So..

    $f=popen("sleep 2","r");
    while (!
    feof($f)) {}
    pclose($f);
    print
    "done";
    ?>

    will never finish.

    nricciardi at mindspring dot com

    20 years ago

    ive tried using popen using bidirectional pipes without working for obvious reasons, but i managed to create a simple script that managed to take care of the problem.  This example is for gpg encryption.

       $message = "this is the text to encrypt with gpg";
      
    $sendto = 'Dummy Key <>'; system("mkfifo pipein");
      
    system("mkfifo pipeout");
      
    system("gpg --encrypt -a -r '$sendto' > pipeout < pipein &");
      
    $fo = fopen("pipeout", "r");
      
    $fi = fopen("pipein", "w");
      
    fwrite($fi, $message, strlen($message));
      
    fclose($fi);
       while (!
    feof($fo)) {
         
    $buf .= fread($fo, 1024);
       }
       echo
    $buf;
      
    unlink("pipein");
      
    unlink("pipeout");
    ?>

    If anyone has a better way of doing this I would love to see it.

    betchern0t

    15 years ago

    Care needs to be taken in the case of long running child processes. Say you want to run tail -f /var/log/messages or in my case burn dvds. If you have a busy wait, Apache2 can sit towards 100%cpu and steadily grow memory. In my case I crashed the server after about an hour and 90% of the dvd burned. During that time apache had consumed a gig of swap.

    Offending code - don't copy:

            $ThisCommand = sprintf("%s %s",COMMAND,$ThisFile);
           
    $fp=popen($ThisCommand,"r");
            while (!
    feof($fp)) {
                   
    set_time_limit (20);
                   
    $results = fgets($fp, 4096);
                    if (
    strlen($results) == 0) {
                      
    // stop the browser timing out
                      
    echo " ";
                      
    flush();
                    } else {
                      
    $tok = strtok($results, "\n");
                       while (
    $tok !== false) {
                            echo
    htmlentities(sprintf("%s\n",$tok))."
    "
    ;
                           
    flush();
                           
    $tok = strtok("\n");
                       }
                    }
            }
           
    pclose($fp);
    ?>

    to go from zero memory and 100% cpu  to negligible memory and negligible cpu add a sleep.

            while (!feof($fp)) {
                   
    set_time_limit (20);
                   
    $results = fgets($fp, 256);
                    if (
    strlen($results) == 0) {
                      
    // stop the browser timing out
                      
    echo " ";
                      
    flush();
                    } else {
                      
    $tok = strtok($results, "\n");
                       while (
    $tok !== false) {
                            echo
    htmlentities(sprintf("%s\n",$tok))."
    "
    ;
                           
    flush();
                           
    $tok = strtok("\n");
                       }
                    }
                   
    // avoid a busy wait
                   
    sleep(1);

            }

    ?>

    I think the continued banging of the space to keep the browser awake triggered some issues in apache.

    Anonymous

    20 years ago

    Here is a workaround for not having bidirectional pipes in php.

    If you have bidirectional pipe support, don't bother with this.

    The trick here is to send the input on the command line to the target application.  In particular I wanted to use openssl without using temp files or named pipes.  This solution should also be thread/process safe.

    This does work on Linux (RedHat 7).

    function filterThroughCmd($input, $commandLine) {
     
    $pipe = popen("echo \"$input\"|$commandLine" , 'r');
      if (!
    $pipe) {
        print
    "pipe failed.";
        return
    "";
      }
     
    $output = '';
      while(!
    feof($pipe)) {
       
    $output .= fread($pipe, 1024);
      }
     
    pclose($pipe);
      return
    $output;
    }
    # example:
    print filterThroughCmd("hello", "cat");
    # Piping to cat has the effect of echoing your input.
    ?>

    Michel Machado

    18 years ago

    Yet another workaround for not having bidirectional pipes in php.

    $Cmd =
    "bc 2>&1 << END\n" .
    "100+221\n" .
    "1+3*3\n" .
    "quit\n" .
    "END\n"; $fp = popen($Cmd, 'r');
    $read = fread($fp, 1024);
    echo
    $read;
    pclose($fp);
    ?>

    How do I run a PHP file in browser?

    Open up any Web browser on your desktop and enter "localhost" into the address box. The browser will open a list of files stored under the "HTDocs" folder on your computer. Click on the link to a PHP file and open it to run a script.

    How do I run a PHP script from a website?

    You just follow the steps to run PHP program using command line..
    Open terminal or command line window..
    Goto the specified folder or directory where php files are present..
    Then we can run php code using the following command: php file_name.php..

    What is the use of popen () function?

    The popen() function executes the command specified by the string command. It creates a pipe between the calling program and the executed command, and returns a pointer to a stream that can be used to either read from or write to the pipe.

    What is Popen in PHP?

    The popen() function opens a pipe to the program specified in the command parameter.