The following function can be used to read both stdout and stderr even if both streams contain gigabytes of data. It solves most problems mentioned in other comments, like blocking, like further using the existing connection etc.
function ssh2_run($ssh2,$cmd,&$out=null,&$err=null){
$result=false;
$out='';
$err='';
$sshout=ssh2_exec($ssh2,$cmd);
if($sshout){
$ssherr=ssh2_fetch_stream($sshout,SSH2_STREAM_STDERR);
if($ssherr){
# we cannot use stream_select() with SSH2 streams
# so use non-blocking stream_get_contents() and usleep()
if(stream_set_blocking($sshout,false) and
stream_set_blocking($ssherr,false)
){
$result=true;
# loop until end of output on both stdout and stderr
$wait=0;
while(!feof($sshout) or !feof($ssherr)){
# sleep only after not reading any data
if($wait)usleep($wait);
$wait=50000; # 1/20 second
if(!feof($sshout)){
$one=stream_get_contents($sshout);
if($one===false){ $result=false; break; }
if($one!=''){ $out.=$one; $wait=0; }
}
if(!feof($ssherr)){
$one=stream_get_contents($ssherr);
if($one===false){ $result=false; break; }
if($one!=''){ $err.=$one; $wait=0; }
}
}
}
# we need to wait for end of command
stream_set_blocking($sshout,true);
stream_set_blocking($ssherr,true);
# these will not get any output
stream_get_contents($sshout);
stream_get_contents($ssherr);
fclose($ssherr);
}
fclose($sshout);
}
return $result;
}