Ferreteria/v0.6/clade/IO/Aspect/Connx/Stream/Native/SSH2

From WoozleCodes
Jump to navigation Jump to search
clade: IO\Aspect\Connx\Stream\Native\SSH2
Clade Family
Native SSH2 (none)
Clade Aliases
Alias Clade
Base* [ca,i] IO\Aspect\Connx\Stream\Native
CableAdmin IO\Aspect\Connx\aux\Cable
OpData* [c,i] Sys\Events\ItWent\OpComm\OpData
OpOpen* [c,i] IO\Aspect\Connx\aux\itWent\OpComm
OpShut* [c,i] Sys\Events\ItWent
StreamClass IO\Aspect\Connx\Stream\Native\Exec
StreamIface IO\Aspect\Connx\Stream\Native
Subpages

About

  • Purpose: handles streams created by ssh2* library functions

History

  • 2026-02-19 started -- adapting from Generic.php

Code

as of 2026-04-06:

interface iSSH2 extends BaseIface {
    #function IsEoF() : bool;
    function ErrorStream() : StreamIface;
}
class cSSH2 extends BaseClass implements iSSH2 {
    // ++ CONFIG ++ //

    const OBUFF_SIZE = 1024*64;   // 64k output buffer

    public function SType() : string { return 'ssh2 stream'; }

    // -- CONFIG -- //
    // ++ SETTINGS ++ //

    private $bEoSWorks = TRUE;
    public function UseEoS(?bool $b=NULL) : bool { return is_bool($b) ? ($this->bEoSWorks = $b) : $this->bEoSWorks; }

    // ++ ADMIN: lifecycle  ++ //

    protected function ActualOpen() : OpOpenIface { return OpOpenClass::AsOkay(); }
    protected function ActualShut() : OpShutIface { return OpShutClass::AsOkay(); }

    // ++ ADMIN: state ++ //

    /*
    // NEW
    protected function IsEoS() : bool {
        if ($this->UseEoS()) {
            return feof($this->RNative());
        } else {
            return $this->GotEmpty();
        }
    }
    */
    #public function IsEoF() : bool { return $this->IsEoS(); }

    private $bGotEmpty = FALSE;
    protected function GotEmpty(?bool $b=NULL) : bool { return is_bool($b) ? ($this->bGotEmpty = $b) : $this->bGotEmpty; }

    // OVERRIDE
    #public function HasEnded() : bool { return $this->IsEoF(); }
    #public function HasMore() : bool { return !$this->IsEoS(); }
    public function HasMore() : bool {
        if ($this->UseEoS()) {
            $isEoS = feof($this->RNative());
        } else {
            $isEoS = $this->GotEmpty();
        }
        return !$isEoS;
    }

    // ++ ADMIN: aux streams ++ //

    public function ErrorStream() : StreamIface {
        $rMain = $this->RNative();
        // 2026/02/06 there are complications with this, apparently; see docs for ssh2_exec()
        $rAux = ssh2_fetch_stream($rMain, SSH2_STREAM_STDERR);
        $oStream = StreamClass::FromNative($rAux);
        return $oStream;
    }
    public function InOutStream() : StreamIface {
        $rMain = $this->RNative();
        $rAux = ssh2_fetch_stream($rMain, SSH2_STREAM_STDIO);
        $oStream = StreamClass::FromNative($rAux);
        return $oStream;
    }

    // -- ADMIN -- //
    // ++ I/O ++ //

    public function PullBytes(int $nMax=NULL) : OpDataIface {
        if ($this->HasMore()) {
            $r = $this->RNative();
            $sRead = @stream_get_contents($r,$nMax);
        }
        $oOp = $this->OOpData();
        $ok = is_string($sRead);
        $oOp->SetOkay($ok);
        if ($ok) {
            if ($sRead === '') {
                $oOp->QData()->ZapIt();
                $this->GotEmpty(TRUE);
                #$this->AmHere('GOT EMPTY STRING; GotEmpty(): ['.$this->GotEmpty().'] UseEoS()=['.$this->UseEoS().'] IsEoS=['.$this->IsEoS().']');
            } else {
                $oOp->QData()->SetIt($sRead);
                $this->nPulled += strlen($sRead);
            }
        }
        return $oOp;
    }

    /* 2026-03-06 These assume an internal buffer, which now seems like a bad idea.
    public function PullBytes(int $nMax=NULL) : OpDataIface {
        // If buffer empty, try to read more:
        #$this->AmHereShort('BUFFER EMPTY? ['.$this->IsBufferEmpty().']');
        if ($this->IsBufferEmpty()) {
            $this->ReadBytes($nMax);
        }

        // Buffer contents represents what we know we have available:
        $sBuff = $this->sBuff;
        $oOp = $this->OOpData();
        $oOp->SetOkay(is_string($sBuff));
        if (is_string($sBuff)) {
            if ($sBuff === '') {
                $oOp->QData()->ZapIt();
            } else {
                $oOp->QData()->SetIt($sBuff);
                $this->nPulled += strlen($sBuff);
                #$this->AmHereShort('PULLING ['.strlen($sBuff).'] -> ['.$this->nPulled.']');
                $this->sBuff = NULL;
            }
        } else {
            $this->IsEoS(TRUE);
        }
        return $oOp;
    }
    protected function ReadBytes(int $nMax=NULL) : void {
        if ($this->HasMore()) {
            $r = $this->RNative();
            $sRead = @stream_get_contents($r,$nMax);
            $this->sBuff = $sRead;
            #$this->AmHereShort("READ ≤ [$nMax] BYTES");
            $this->IsEoS(empty($sRead));
        }
    }
    */
    // 2025-12-25 This may not work for all stream-types. (Was this re-copied back from SSH2?)
    public function PushBytes(string $s) : OpDataIface {
      //* 2026-01-02 buffering version -- buffering functionality has been moved to Stream\Buffer\Sender
        $sRem = $s;
        $r = $this->RNative();
        $ok = TRUE;
        while ($ok && ($sRem !== '')) {
            $sNow = substr($sRem,0,self::OBUFF_SIZE); // get burst to send
            $ok = fwrite($r,$s);         // try to send it
            $n = strlen($s);                          // how much got sent?
            $sRem = substr($sRem,$n); // remove bytes-sent from local buffer
        }
        //* /
        $ok = fwrite($r,$s);         // try to send it
        // 2026-01-17 fwrite() supposedly works on non-file streams too.
        $oOp = $this->OOpData();
        $oOp->SetOkay($ok);
        return $oOp;
    }

    // -- I/O -- //
    // ++ DISPLAY ++ //

    /* 2026-03-06 moved to Stream parent
    public function DescribeInline() : string {
        $oPanel = CableAdmin::OPanel();

        $sID = $this->ObjectID();
        $nPull = $this->NPulledCount();
        $nPush = $this->NPushedCount();
        $sPull = number_format($nPull);
        $sPush = number_format($nPush);
        $sEoS = 'EoS:'.(isset($this->isEoS) ? ($this->isEoS ? 'Y' : 'n') : 'n/a');
        $sType = $this->SType();
        return "(id$sID) bytes: $sPull -> $sPush $sEoS [$sType]";
    }
    */
    public function VIEW_Readout() : string { return $this->DescribeInline(); }

    /* 2026-03-05 old version
    public function VIEW_Readout() : string {
        $nTot = $this->NPulledCount();
        $oPanel = CableAdmin::OPanel();
        $sTot = $oPanel->FormatOfInt()->format($nTot);
        $sS = (($nTot === 1) ? '' : 's');
        $sType = $this->SType();
        return "[$sType] $nTot byte$sS spent";
    }
    */

    // -- DISPLAY -- //
}