6

I am running my code on CodeIgniter - Ubuntu Server.

I have been researching for async ways to run functions.

This is my function:

<?php   

    // Registers a new keyword for prod to the DB. 
    public function add_keyword() {

        $keyword_p = $this->input->post('key_word');

        $prod      = $this->input->post('prod_name');
        $prod      = $this->kas_model->search_prod_name($prod);
        $prod      = $prod[0]->prod_id;

        $country   = $this->input->post('key_country');

        $keyword = explode(", ", $keyword_p);
        var_dump($keyword); 
        $keyword_count = count($keyword);
        echo "the keyword count: $keyword_count";

        // problematic part that needs forking
        for ($i=0; $i < $keyword_count ; $i++) { 

            // get new vars from $keyword_count
            // run API functions to get new data_arrays
            // inserts new data for each $keyword_count to the DB 

        }

        // Redirect to main page. 
        redirect('banana/kas'); 

    }

The "foreach" uses variables with slow APIs, and updates the database.

I saw some tutorials using fork but haven't quite understood the syntax part of it. Most of the things I found were just explanations of how it works (2 processes: parent-child ect') but non-gave a good explanation of how applying this on the code.

Can someone explain how I work with the fork() syntax?

Continue PHP execution after sending HTTP response

http://www.onlinetechtutorials.com/2014/06/how-to-run-php-code-asynchronously.html

http://php.net/manual/en/function.pcntl-fork.php (more general)

From the server side: https://www.youtube.com/watch?v=xVSPv-9x3gk

EDIT:

Did I get it right?

<?php   

// Registers a new keyword for prod to the DB. 
public function add_keyword() {

    $keyword_p = $this->input->post('key_word');

    $prod      = $this->input->post('prod_name');
    $prod      = $this->kas_model->search_prod_name($prod);
    $prod      = $prod[0]->prod_id;

    $country   = $this->input->post('key_country');

    $keyword = explode(", ", $keyword_p);
    var_dump($keyword); 
    $keyword_count = count($keyword);
    echo "the keyword count: $keyword_count";

    for ($i=0; $i < $keyword_count ; $i++) { 
        // create your next fork
        $pid = pcntl_fork();

        if(!$pid){
            //*** get new vars from $keyword_count
            //*** run API functions to get new data_arrays
            //*** inserts new data for each $keyword_count to the DB 
            print "In child $i\n";
            exit($i);
            // end child
        }
    }

    // we are the parent (main), check child's (optional)
    while(pcntl_waitpid(0, $status) != -1){
        $status = pcntl_wexitstatus($status);
         echo "Child $status completed\n";
    }

    // your other main code: Redirect to main page. 
    redirect('banana/kas'); 

}
?>

This won't cause any problem with using this inside a loop? will it know to stack each process?

Community
  • 1
  • 1
Imnotapotato
  • 5,308
  • 13
  • 80
  • 147
  • I don't think using fork is the best solution in your case. I would rather use simple queue and worker script scheduled in cron – Ivan Yarych Apr 06 '16 at 20:35

3 Answers3

2

You must mention what's the operating system that you are using, because pcntl extensions are not available on Windows platform
Also you must be aware that activating process control on Linux/Unix within a web server can give you unexpected results, so only CLI/CGI mode is recommended to use PCNTL
Please read carefully this PCNTL Introduction

Now, your code seems to be correct and well implemented, but you must compile your PHP with this option --enable-pcntl to enable pcntl functions like int pcntl_fork(void) otherwise you will get

Fatal error: Call to undefined function pcntl_fork()

For me, the best solution to run functions/methods in asynchronous way is to use pthreads, if you are interested by this advice i can edit my response by adding examples and how to install it on Windows or Linux platforms

Read this article to know how to compile PHP

Halayem Anis
  • 7,654
  • 2
  • 25
  • 45
  • Thanks for your attention! :) I edited: `root@MYserver:/etc/php5/apache2# nano /etc/php5/apache2/php.ini` and added `;` before the `disable_functions = pcntl_alarm,pcntl_fork,,,,,,,,,,` restarted the server and I still get that same error :S – Imnotapotato Apr 05 '16 at 12:42
  • Oh, so you're saying I cant actually use it in my PHP code? :/ – Imnotapotato Apr 05 '16 at 13:12
  • I didn't understand what I need to do with `--enable-pcntl` – Imnotapotato Apr 05 '16 at 13:14
  • @RickSanchez what do you need to do is to compile PHP in your machine to be able to use PCNTL :) you can follow this tutorial for that http://php.net/manual/en/install.windows.building.php – Halayem Anis Apr 05 '16 at 13:27
  • Oh, I edited my main post following your previous comment, I am using an Ubuntu server. And I'm kinda new to it, just didn't understand the `--enable-pcntl` concept and what I should do with it (via terminal/putty?) :) – Imnotapotato Apr 05 '16 at 13:41
  • It's now not giving me this error anymore, but it doesn't submit and redirect, it's still loads all and get the website stuck untill it's done with it's process.. – Imnotapotato Apr 05 '16 at 15:05
  • @RickSanchez you need to download the PHP source code (C sources) and compile/build it in your Linux/Ubuntu machine (unzip, configure and make) Why do you need to do this job ? because available PHP engines that you can find in Internet are not compiled to use PCNTL Extensions... sorry for the delayed response :( – Halayem Anis Apr 08 '16 at 13:15
  • FYI, pthreads is not an typical async library but a parallel processing library. – Ikari Apr 09 '16 at 11:30
1

You have one fork after your pcntl_fork(), check $pid and then you can use it. Repeat for more forks.

<?php
    for($i = 1; $i <= 3; ++$i){
        // create your next fork
        $pid = pcntl_fork();

        if(!$pid){
            // begin child, your execution code
            sleep(1);
            print "In child $i\n";
            exit($i);
            // end child
        }
    }

    // we are the parent (main), check child's (optional)
    while(pcntl_waitpid(0, $status) != -1){
        $status = pcntl_wexitstatus($status);
        echo "Child $status completed\n";
    }
    // your other main code
?>

For async http requests you can use multicurl: http://php.net/manual/en/function.curl-multi-init.php

thecoder
  • 102
  • 8
  • Hi @thecoder and thanks for your quick reply :) ! I edited my main code following your example, can you please have a look at it and tell me if I got it right? – Imnotapotato Mar 31 '16 at 09:57
  • I think yes but I cannot test it without any full source code as example. – thecoder Apr 04 '16 at 17:28
  • http://stackoverflow.com/questions/25892163/call-to-undefined-function-pcntl-fork-ubuntu-server-apache This doesn't work.. I get the same error `Fatal error: Call to undefined function pcntl_fork() ` http://php.net/manual/en/function.pcntl-fork.php – Imnotapotato Apr 05 '16 at 10:25
  • I read your comment again and saw the multicurl. I think this is what I need.The question is - is this going to run processes in the background when clicking on the submit button, and redirect me to an other requestd page right in the moment, while the database is getting more data. – Imnotapotato Apr 06 '16 at 11:43
0

As far as I am aware of, you can achieve this in two ways...

Use:

  • Pthreads
  • Amphp
  • Pthreads is a parallel processing library, while amp is a pure asynchronous framework...

    So, the way for using pthreads would be to first download/enable the pthreads extension and add extension=/path/to/pthread.so in the php.ini file...

    And then create a class, which extends the Thread Class and override the method run, and put everything inside it, which you want to do parallely.

    So for your specific purpose the the class could be something like this:

    <?php
    class Inserter extends Thread {
        public $db_con = null;
        public $data;
    
        public function __construct($db_connection, $data) {
            $this->db_con = $db_connection;
            $this->data = data;
        }
    
        private function run() {
            // use your logic to insert the data...
        }
    }
    

    To use that, just instantiate the class, and put the the DB connection variable, and the data to be processed in the ctor. And call the start method of the object. Like:

    $inserter = new Inserter($dbConn, $data);
    $inserter->start();
    

    Where $dbConn stores the DB connection and $data stores the necessary data.

    And that's it...

    Ikari
    • 3,176
    • 3
    • 29
    • 34