PHP Simulated Multi-Threading

This may or may not come in handy to someone, I found it here: http://blog.motane.lu/2009/01/02/multithreading-in-php/

Also… if there is a cleaner alternative to multi-threading I’m all ears! :)




<?php

/**

 * Implements threading in PHP

 * 

 * @package <none>

 * @version 1.0.0 - stable

 * @author Tudor Barbu <miau@motane.lu>

 * @copyright MIT

 */

class Thread {

    const FUNCTION_NOT_CALLABLE     = 10;

    const COULD_NOT_FORK            = 15;

    

    /**

     * possible errors

     *

     * @var array

     */

    private $errors = array(

        Thread::FUNCTION_NOT_CALLABLE   => 'You must specify a valid function name that can be called from the current scope.',

        Thread::COULD_NOT_FORK          => 'pcntl_fork() returned a status of -1. No new process was created',

    );

    

    /**

     * callback for the function that should

     * run as a separate thread

     *

     * @var callback

     */

    protected $runnable;

    

    /**

     * holds the current process id

     *

     * @var integer

     */

    private $pid;

    

    /**

     * checks if threading is supported by the current

     * PHP configuration

     *

     * @return boolean

     */

    public static function available() {

        $required_functions = array(

            'pcntl_fork',

        );

        

        foreach( $required_functions as $function ) {

            if ( !function_exists( $function ) ) {

                return false;

            }

        }

        

        return true;

    }

    

    /**

     * class constructor - you can pass

     * the callback function as an argument

     *

     * @param callback $_runnable

     */

    public function __construct( $_runnable = null ) {

    	if( $_runnable !== null ) {

        	$this->setRunnable( $_runnable );

    	}

    }

    

    /**

     * sets the callback

     *

     * @param callback $_runnable

     * @return callback

     */

    public function setRunnable( $_runnable ) {

        if( self::runnableOk( $_runnable ) ) {

            $this->runnable = $_runnable;

        }

        else {

            throw new Exception( $this->getError( Thread::FUNCTION_NOT_CALLABLE ), Thread::FUNCTION_NOT_CALLABLE );

        }

    }

    

    /**

     * gets the callback

     *

     * @return callback

     */

    public function getRunnable() {

        return $this->runnable;

    }

    

    /**

     * checks if the callback is ok (the function/method

     * actually exists and is runnable from the current

     * context)

     * 

     * can be called statically

     *

     * @param callback $_runnable

     * @return boolean

     */

    public static function runnableOk( $_runnable ) {

        return ( function_exists( $_runnable ) && is_callable( $_runnable ) );

    }

    

    /**

     * returns the process id (pid) of the simulated thread

     * 

     * @return int

     */

    public function getPid() {

        return $this->pid;

    }

    

    /**

     * checks if the child thread is alive

     *

     * @return boolean

     */

    public function isAlive() {

        $pid = pcntl_waitpid( $this->pid, $status, WNOHANG );

        return ( $pid === 0 );

        

    }

    

    /**

     * starts the thread, all the parameters are 

     * passed to the callback function

     * 

     * @return void

     */

    public function start() {

        $pid = @ pcntl_fork();

        if( $pid == -1 ) {

            throw new Exception( $this->getError( Thread::COULD_NOT_FORK ), Thread::COULD_NOT_FORK );

        }

        if( $pid ) {

            // parent 

            $this->pid = $pid;

        }

        else {

            // child

            pcntl_signal( SIGTERM, array( $this, 'signalHandler' ) );

            $arguments = func_get_args();

            if ( !empty( $arguments ) ) {

                call_user_func_array( $this->runnable, $arguments );

            }

            else {

                call_user_func( $this->runnable );

            }

            

            exit( 0 );

        }

    }

    

    /**

     * attempts to stop the thread

     * returns true on success and false otherwise

     *

     * @param integer $_signal - SIGKILL/SIGTERM

     * @param boolean $_wait

     */

    public function stop( $_signal = SIGKILL, $_wait = false ) {

        if( $this->isAlive() ) {

            posix_kill( $this->pid, $_signal );

            if( $_wait ) {

                pcntl_waitpid( $this->pid, $status = 0 );

            }

        }

    }

    

    /**

     * alias of stop();

     *

     * @return boolean

     */

    public function kill( $_signal = SIGKILL, $_wait = false ) {

        return $this->stop( $_signal, $_wait );

    }

    

    /**

     * gets the error's message based on

     * its id

     *

     * @param integer $_code

     * @return string

     */

    public function getError( $_code ) {

        if ( isset( $this->errors[$_code] ) ) {

            return $this->errors[$_code];

        }

        else {

            return 'No such error code ' . $_code . '! Quit inventing errors!!!';

        }

    }

    

    /**

     * signal handler

     *

     * @param integer $_signal

     */

    protected function signalHandler( $_signal ) {

        switch( $_signal ) {

            case SIGTERM:

                exit( 0 );

            break;

        }

    }

}


// EOF



Hi Im from the future… year 2015 with flying cars and hover skateboards… thank you for this lovely class…really helps me…