Search code examples
phprgearman

How to return R program status when run via PHP (gearman) worker?


I have a PHP script (worker in gearman) that calls Rscript (with status progress bar) which runs fine but I don't know the status of the program. My question if it is possible for rscript to return run status back to php script.

<?php
print_r(posix_getpwuid(getmyuid()));
print_r(pathinfo($YOUR_PATH));
exec(" RScript ~/dynamic.R 'select field1, field2 from table where 1=1 limit 100' ");
?>

So dynamic.R performs som data manipulations for 100 rows (i have plyr fucntion with progress bar) but with gearman progress bar does not show hence would like to show progress in gearman.

Hope my question makes sense

Regards, Savi


Solution

  • you could theoretically do this with exec() if you redirected output and then read the output from a file, but coordinating with the process would be nearly impossible without first finding its PID. This is what proc_open() is for. popen() might work, but I'm guessing the progress bar is written to stderr, and that is what I mocked up for you here.

    Here's a little program that should be a working example for you:

    <?php
    
    $descriptors = array(
        0 => array("pipe", "r"),
        1 => array("pipe", "w"),
        2 => array("pipe", "w")
    );
    
    $process = proc_open( "/usr/bin/env ruby ./rlike.rb 1 10", $descriptors, $pipes, getcwd() );
    if( is_resource($process) ){
        echo "ran process\n";
        do{
            $status = proc_get_status($process);
            if(! $status["running"]) break;
            fflush($pipes[2]);
            $progressbar = fread( $pipes[2],1 );
            if( $progressbar == "\r" ) echo "\n";
            else echo "$progressbar";
        }while( $status["running"] );
        fclose($pipes[0]);
        fclose($pipes[2]);
        echo "\nProcess complete. \n";
        print_r( $status );
    }else{
       echo "Failed to run process.\n";
    }
    

    The output looks like this:

    ran process
    
    |>                    |  00%
    |==>                  |  10%
    |====>                |  20%
    |======>              |  30%
    |========>            |  40%
    |==========>          |  50%
    |============>        |  60%
    |==============>      |  70%
    |================>    |  80%
    |==================>  |  90%
    |====================>| 100%
    Process complete.
    Array
    (
        [command] => /usr/bin/env ruby ./rlike.rb 1 10
        [pid] => 60306
        [running] =>
        [signaled] =>
        [stopped] =>
        [exitcode] => 0
        [termsig] => 0
        [stopsig] => 0
    )
    

    Finally, the ruby program that writes out a progress bar looks as follows ( behold, the awesomeness of ruby syntax :) ):

    wait = ARGV[1].to_f || 10.0
    repeat = ARGV[2].to_i
    repeat = 10 if !repeat or repeat == 0
    wait = 1 if !wait or wait == 0
    barlen = 20
    (repeat+1).times do |t|
        curlen = ( repeat == 0 ? 0 : ( t*barlen/repeat ) )
        bar = ( "%#{curlen}s>" % "" ).gsub( " ", "=")
        $stderr.print sprintf("\r|%- #{barlen+1}s| %3.2d%% ", bar, 100*t/repeat )
        $stderr.flush
        sleep 1 unless t == repeat
    end
    

    From that example, you should be able to get anything you like working. Word of warning: I'm rewriting the entire progress bar each time and returning to the beginning of the line with \r - but depending on how the Rscript progress bar works, this may not be the case.