[code]
<?php

// Load the queueCallback scheme from the repository.
require_once 'repository/queueCallback.php';

class queueFarm
{
	private static $queueFarm = null;
	
	private $queueIndex = array();
	private $queueCount = 0;
	private $waitTime   = 0.0;
	
	private function __construct()
	{
	}
	
	public function __destruct()
	{
		$this -> queueIndex = array();
		$this -> queueCount = 0;
	}
	
	private function __clone()
	{
	}
	
	public static function getInstance()
	{
		if( self :: $queueFarm == null )
		{
			self :: $queueFarm = new self();
		}
		
		return self :: $queueFarm;
	}
	
	public function queueCount()
	{
		return $this -> queueCount;
	}
	
	public function waitTime()
	{
		return $this -> waitTime;
	}
	
	public function attachCallback( queueCallback $queueCallback )
	{
		// Put it in our index.
		$this -> queueIndex [] = $queueCallback;
		
		// Increase the queue count.
		$this -> queueCount ++;
	}
	
	public function detachCallback( $callbackClass )
	{
		// We have queue classes in our index?
		if( $this -> queueCount < 1 )
		{
			return true;
		}
		
		// Fetch the class's name (if we have to) ..
		if( !is_string( $callbackClass ) && !get_class( $callbackClass ) )
		{
			throw new InvalidArgumentException( 'Callback class should be either a class or a string.' );
		}
		
		if( !is_string( $callbackClass ) )
		{
			$callbackClass = get_class( $callbackClass );
		}
		
		foreach( $this -> queueIndex as $queueId => $queueCallback )
		{
			// Fetch the parameters ..
			$paramList = $queueCallback -> getParams();
			
			// This is a class :: method() call?
			if( !isset( $paramList [ 'callback' ][ 1 ] ) )
			{
				continue;
			}
			
			// Is this a match?
			if( $paramList [ 'callback' ][ 0 ] == $callbackClass )
			{
				// Remove it from our list.
				unset( $this -> queueIndex [ $queueId ] );
				
				// Change the queue count.
				$this -> queueCount --;
			}
		}
		
		return true;
	}
	
	public function runCallbacks()
	{
		// We have things to run?
		if( $this -> queueCount < 1 )
		{
			return true;
		}
		
		// Current micro-time.
		$microTime = microtime( true );
		
		// Lowest known runtime.
		$lowestTime = 100.0;
		
		// Loop trought our element tree.
		foreach( $this -> queueIndex as $queueId => $queueCallback )
		{
			// Fetch callback's parameters ..
			$paramList = $queueCallback -> getParams();
			
			// Calculate the time left 'till execution ..
			$timeLeft = $paramList [ 'time' ] - $microTime;
			
			// Is that number negative?
			if( $timeLeft < 1 )
			{
				// Run the bitch ..
				self :: stackCall( $paramList );
				
				// Remove the item from our list ..
				unset( $this -> queueIndex [ $queueId ] );
				
				// Change the queue count.
				$this -> queueCount --;
				
				// Go to the next iteration.
				continue;
			}
			
			// We don't have to call it, but, is this timeLeft lower than the current known?
			if( $timeLeft < $lowestTime )
			{
				$lowestTime = $timeLeft;
			}
		}
		
		// Set the new wait time ..
		$this -> waitTime = $lowestTime;
		
		// Finished.
	}
	
	private static function stackCall( $paramList )
	{
		// Function call?
		if( !isset( $paramList [ 'callback' ][ 1 ] ) )
		{
			call_user_func_array( $paramList [ 'callback' ][ 0 ], $paramList [ 'params' ] );
		} else
		{
			call_user_func_array( $paramList [ 'callback' ], $paramList [ 'params' ] );
		}
	}
}

[/code]

[code]
<?php
class queueCallback
{
	protected $paramList = array(
		'callback'  => null,
		'params'    => array(),
		'time'      => 0.0
	);
	
	protected function __construct( $callback )
	{
		$this -> paramList [  'callback' ] = $callback;
	}
	
	public function getParams()
	{
		return $this -> paramList;
	}
	
	public static function method()
	{
		$paramCount = func_num_args();
		$paramList  = func_get_args();
		
		switch( $paramCount )
		{
			case 1:
				if( !is_callable( $paramList [ 0 ] ) )
				{
					throw new BadMethodCallException( 'Specified function is not callable.' );
				}
			break;
			
			case 2:
				if( !is_callable( array( $paramList [ 0 ], $paramList [ 1 ] ) ) )
				{
					throw new BadMethodCallException( 'Please give a valid callback combination.' );
				}
			break;
			
			default:
				throw new UnexpectedValueException( 'Too many parameters given.' );
			break;
		}
		
		return new self( $paramList );
	}
	
	public function params()
	{
		$paramList = func_get_args();
		$this -> paramList [ 'params' ] = array_merge( $this -> paramList [ 'params' ], $paramList );
		
		return $this;
	}
	
	public function time( $microTime )
	{
		if( !is_float( $microTime ) )
		{
			throw new InvalidArgumentException( '1st parameter should be a float.' );
		}
		
		if( $microTime < 0.01 )
		{
			throw new InvalidArgumentException( '1st parameter should be > 0.01' );
		}
		
		$this -> paramList [ 'time' ]      = microtime( true ) + $microTime;
		
		return $this;
	}
}

[/code]

En een voorbeeldscript:

[code]
<?php

// Load the farm object.
require_once 'queueFarm.php';

$queueFarm = queueFarm :: getInstance();

// Create some tasks ..
$queueCallback1 = queueCallback :: method( 'strtolower' ) -> params( 'UPPERCASE' ) -> time( 1.0 );
$queueCallback2 = queueCallback :: method( 'strtoupper' ) -> params( 'lowercase' ) -> time( 2.0 );
$queueCallback3 = queueCallback :: method( 'substr' ) -> params( 'phphulp', 0, 3 ) -> time( 3.0 );

// Put them in our farm.
$queueFarm -> attachCallback( $queueCallback1 );
$queueFarm -> attachCallback( $queueCallback2 );
$queueFarm -> attachCallback( $queueCallback3 );

// Sleep 2 seconds ..
sleep( 2 );

// Process functions.
$queueFarm -> runCallbacks();

// Time 'till next function.
echo $queueFarm -> waitTime(); // weergeeft '1'

[/code]