All pastes #544279 Raw Edit

Marc Quinton

public php v1 · immutable
·published 2007-06-06 11:35 UTC
rendered paste body
<?phperror_reporting(E_ALL);/** * Marc Quinton - may 2007 * Licence : LGPL * * a pipe is a "channel" built to talk between your php script and an external command (shell, script or what ever) *  - you can "talk" to this command : just write to file descriptor  (see PipeIO::write()) (stdin) *  - you can read from this command,  (stdout) *  - there is also an channel for errors : stderr *  */class Pipe{	protected $proc_id;          # proc_id from proc_open call ; needed for many proc_* functions	protected $descriptor_spec;  # defaults descriptors (FD) for a pipe with reading and writing	protected $watch_ids;        # watch ids : when terminating pipe, need to call Gtk::input_remove($id);	protected $terminated;       # triggered to true when child terminate (hup signal)	protected $has_run;          # can run a pipe more that one time.	public function __construct(){		$this->descriptor_spec = array(			0 => array("pipe", "r"),  // stdin is a pipe that the child will read from			1 => array("pipe", "w"),  // stdout is a pipe that the child will write to			2 => array("pipe", "w")   // stderr is a file to write to		);		$this->terminated = false;		$this->proc_id = null;		$this->watch_ids = array();		$this->has_run = false;	}	# auto clean this pipe when object is destroyed. 	# You need to take care of this if you don't keep reference to object in a loop for example.	public function __destruct(){		$this->terminate();	}	public function terminate(){		if($this->proc_id != null){			# terminate each FD watch, using right ID			foreach ($this->watch_ids as $id)				Gtk::input_remove($id);			# close opened pipes.			fclose($this->pipes[0]);			fclose($this->pipes[1]);			fclose($this->pipes[2]);			# kill running child process			$this->kill_child();			# close pipe			proc_close($this->proc_id);			$this->proc_id = null;			$this->pipes = null;		}	}	public function is_running(){		$status = $this->get_status();		if($status == false)			return false;		return $status['running'] == 1;	}	public function get_pid(){		$status = $this->get_status();		if($status == false)			return false;		return $status['pid'];	}	public function get_exitcode(){		$status = $this->get_status();		if($status == false)			return false;		return $status['exitcode'];	}	/*		Array		(			[command] => php -q ...			[pid] => 13208			[running] => 1			[signaled] =>			[stopped] =>			[exitcode] => -1			[termsig] => 0			[stopsig] => 0		)	*/	public function get_status(){		if($this->proc_id == null)			return false;		return proc_get_status($this->proc_id);	}	public function kill_child(){		if($this->proc_id == null || $this->terminated)			return false;		if($this->is_running()){			posix_kill($this->get_pid(), SIGILL);			return true;		}		return false;	}	public function write($txt){		return fwrite($this->pipes[0], $txt);	}	public function flush(){		return fflush($this->pipes[0]);	}	# read a large block avoiding read block	public function read($size=32000){		return fread($this->pipes[1], $size);	}	public function run($cmd){		if($this->has_run)			throw new Exception("Pipe::run() : can't run this pipe instance twice or more.");		$this->has_run = true;		$this->proc_id = proc_open($cmd, $this->descriptor_spec, $this->pipes);		# could set non blocking IO here		# stream_set_blocking($this->pipes[1], false);  # stdout		# stream_set_blocking($this->pipes[2], false);  # stderr		if ($this->proc_id !== false) {			$this->watch_ids[] = Gtk::io_add_watch($this->pipes[0], Gtk::IO_IN,  array($this, 'stdin'));			$this->watch_ids[] = Gtk::io_add_watch($this->pipes[1], Gtk::IO_OUT, array($this, 'stdout'));			$this->watch_ids[] = Gtk::io_add_watch($this->pipes[2], Gtk::IO_OUT, array($this, 'stderr'));			# signals can be : Gtk::IO_IN, IO_OUT, IO_PRI, IO_ERR, IO_HUP, IO_NVAL			$this->watch_ids[] = Gtk::io_add_watch($this->pipes[1], Gtk::IO_HUP, array($this, 'io_hup'));			$this->watch_ids[] = Gtk::io_add_watch($this->pipes[0], Gtk::IO_ERR, array($this, 'io_err'));			$this->watch_ids[] = Gtk::io_add_watch($this->pipes[1], Gtk::IO_ERR, array($this, 'io_err'));			$this->watch_ids[] = Gtk::io_add_watch($this->pipes[2], Gtk::IO_ERR, array($this, 'io_err'));		} else			throw new Exception("PipeIO::run() : proc_open error");		echo "Pipe::run : cmd is running ($cmd)\n";	}	# process terminated ; pipe is broken	public function io_hup($pipe){		echo "Pipe::io_hup()\n";		$this->terminated = true;		$this->terminate();		return false;	}	# process terminated ; pipe is broken	public function io_err($pipe){		echo "Pipe::io_err()\n";		$this->terminated = true;		$this->terminate();		return false;	}	public function stdin($pipe){		echo "Pipe::stdin()\n";		$this->write("test me\n");		return true;	}	public function stdout($pipe){		if($this->pipes == null)			return;		# here avoid blocking read : don't do fread until "\n" for example or set stream_set_blocking()		$data = trim($this->read());		echo "Pipe::stdout($data)\n";		if(!$this->terminated)			return true;		else			return false;	}	public function stderr($pipe){		if($this->pipes == null)			return;		# here avoid blocking read : don't do fread until "\n" for example.		$data = trim($this->read());		echo "Pipe::stderr($data)\n";		return true;	}}$pipe = null;function run_a_pipe(){	global $pipe;	if($pipe != null)		$pipe->terminate();	# $pipe = new Pipe2('/bin/traceroute', array('-n', 'intrux'));	# php -r '$i=0;while(1) { echo "$i\n" ; sleep(1); $i++;}'	$pipe = new Pipe();	# $pipe = new Pipe2('php', array('-i'));	$pipe->run('php -q ' . __FILE__ . ' slave');	$pipe->write("test me\n");}function slave_function(){	$count = rand(1, 6);	for($i=0 ; $i< $count ; $i++){		fprintf(STDOUT, "stdout $i\n");		fprintf(STDERR, "stderr $i\n");		sleep(1);	}	exit(0);}$args = $_SERVER['argv'];if(count($args) == 2){	if($args[1] == 'slave'){		slave_function();		exit(0);	}}run_a_pipe();Gtk::timeout_add(3000, 'on_timeout');Gtk::timeout_add(500, 'on_timeout2');Gtk::main();function on_timeout(){	echo "I am alive\n";	global $pipe;	$pipe->terminate();	if($pipe->is_running())		echo "pipe is running\n";	else		echo "pipe is not running\n";	# $pipe->run('ls'); # this throw an exception ... can't run more that one time.	run_a_pipe();	return true;}function on_timeout2(){	global $pipe;	if($pipe->is_running())		echo "pipe is running\n";	else		echo "pipe is not running\n";	return true;}?>