3

I want to execute on a button click event:

localhost/codeigniter/controller/method

The method will extract keywords from a webpage(s) and store them in DB. There are multiple sub-methods in it which should also run in background. I don't want to make the user wait during that duration. I read this. Will this work for me ? Any other life saving suggestions are most welcome.

Community
  • 1
  • 1
SilentAssassin
  • 468
  • 1
  • 9
  • 27

2 Answers2

1

Based on this thread I made this for my codeigniter project. It works just fine. You can have any function processed in the background.

A controller that accepts the async calls.

class Daemon extends CI_Controller
{
    // Remember to disable CI's csrf-checks for this controller

    function index( )
    {
        ignore_user_abort( 1 );
        try
        {
            if ( strcmp( $_SERVER['REMOTE_ADDR'], $_SERVER['SERVER_ADDR'] ) != 0 && !in_array( $_SERVER['REMOTE_ADDR'], $this->config->item( 'proxy_ips' ) ) )
            {
                log_message( "error", "Daemon called from untrusted IP-address: " . $_SERVER['REMOTE_ADDR'] );
                show_404( '/daemon' );
                return;
            }

            $this->load->library( 'encrypt' );
            $params = unserialize( urldecode( $this->encrypt->decode( $_POST['data'] ) ) );
            unset( $_POST );
            $model = array_shift( $params );
            $method = array_shift( $params );
            $this->load->model( $model );
            if ( call_user_func_array( array( $this->$model, $method ), $params ) === FALSE )
            {
                log_message( "error", "Daemon could not call: " . $model . "::" . $method . "()" );
            }
        }
        catch(Exception $e)
        {
            log_message( "error", "Daemon has error: " . $e->getMessage( ) . $e->getFile( ) . $e->getLine( ) );
        }
    }
}

And a library that does the async calls

class Daemon
{
    public function execute_background( /* model, method, params */ )
    {
        $ci = &get_instance( );
        // The callback URL (its ourselves)
        $parts = parse_url( $ci->config->item( 'base_url' ) . "/daemon" );
        if ( strcmp( $parts['scheme'], 'https' ) == 0 )
        {
            $port = 443;
            $host = "ssl://" . $parts['host'];
        }
        else 
        {
            $port = 80;
            $host = $parts['host'];
        }
        if ( ( $fp = fsockopen( $host, isset( $parts['port'] ) ? $parts['port'] : $port, $errno, $errstr, 30 ) ) === FALSE )
        {
            throw new Exception( "Internal server error: background process could not be started" );
        }
        $ci->load->library( 'encrypt' );
        $post_string = "data=" . urlencode( $ci->encrypt->encode( serialize( func_get_args( ) ) ) );
        $out = "POST " . $parts['path'] . " HTTP/1.1\r\n";
        $out .= "Host: " . $host . "\r\n";
        $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $out .= "Content-Length: " . strlen( $post_string ) . "\r\n";
        $out .= "Connection: Close\r\n\r\n";
        $out .= $post_string;
        fwrite( $fp, $out );
        fclose( $fp );
    }
}

This method can be called to process any model::method() in the 'background'. It uses variable arguments.

$this->load->library('daemon');
$this->daemon->execute_background( 'model', 'method', $arg1, $arg2, ... );
Patrick Savalle
  • 4,068
  • 3
  • 22
  • 24
  • It cannot be used for controller method ? And I don't have any POST data so $params is not needed. Will this work then ? – SilentAssassin Feb 08 '13 at 06:09
  • And you tried to give a link of the thread on the basis of which you created this but its not there actually. – SilentAssassin Feb 08 '13 at 06:11
  • I tried this shifting my two methods to model. I ran them but result of first method is required for second method to execute in background again. And its not available so undefined error is returned. This is not working in my scenario. – SilentAssassin Feb 08 '13 at 07:24
  • 1
    It will work without $_POST, just modify it :) This is a generic background processor. You cannot put both in a model. The controller is called from Apache which was called from your own PHP process. – Patrick Savalle Feb 08 '13 at 15:49
  • What part of this php code make CI async? At first glance this code doesn't seem async at all. – Viktor Joras Apr 21 '19 at 07:02
  • The server calls itself asynchronously. – Patrick Savalle Apr 22 '19 at 14:02
0

You can write in socket:

$fp = fsockopen($_SERVER['HTTP_HOST'], 80, $errno, $errstr, 30);
$data = http_build_query($params, '', '&'); //$params - array with POST data
fwrite($fp, "POST " . ('/controller/action') . " HTTP/1.1\r\n");
fwrite($fp, "Host: ".$_SERVER['HTTP_HOST']."\r\n");
fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fwrite($fp, "Content-Length: " . strlen($data) . "\r\n");
fwrite($fp, "Connection: Close\r\n\r\n");
fwrite($fp, $data);
fclose($fp);
Vlad
  • 3,626
  • 16
  • 14
  • Where will the URL I mentioned in the question go ? – SilentAssassin Feb 07 '13 at 12:14
  • There is no POST data to be given to the controller method. All the data is already available to it from the database. I simply want to call the method. $params is not needed. How can I just call the method ? – SilentAssassin Feb 07 '13 at 12:50