Ferreteria/v0.6/clade/IO/Aspect/Connx/run/Session/@code/cur/2026/09/04

From WoozleCodes
Jump to navigation Jump to search
2026/09/03 2026/09/04 2026/09/05

I'm about to do some more intensive rearranging of how this works...

/* APipes*  [c,i] */ use Woozalia\Ferret\IO\Aspect\Connx\run\aux\A\{ cPipes as APipesClass, iPipes as APipesIface };
/* Base*   [ca,i] */ use Woozalia\Ferret\Sys\InOut\{ caConnx as BaseClass, iConnx as BaseIface };
/* Canals*  [c,i] */ use Woozalia\Ferret\IO\Aspect\Connx\aux\A\iCanals as CanalsIface;
/* CommandIface   */ use Woozalia\Ferret\IO\Aspect\Connx\run\iCommand as CommandIface;
/* EventIface     */ use Woozalia\Ferret\Sys\Events\iEvent as EventIface;
/* EventOnAfter   */ use Woozalia\Ferret\Sys\Events\Event\Loops\cOnAfter as EventOnAfter;
/* EventOnBefore  */ use Woozalia\Ferret\Sys\Events\Event\Loops\cOnBefore as EventOnBefore;
/* EventOnLoop    */ use Woozalia\Ferret\Sys\Events\Event\Loops\cOnLoop as EventOnLoop;
/* OpCmd*   [c,i] */ use Woozalia\Ferret\IO\Aspect\Connx\run\Starter\ops\{ cOpCmd as OpCmdClass, iOpCmd as OpCmdIface };
/* OpDataIface    */ use Woozalia\Ferret\Sys\Events\ItWent\OpComm\cOpData as OpDataIface;
/* OpOpenIface    */ use Woozalia\Ferret\Sys\Events\ItWent\connx\iOpOpen as OpOpenIface;
/* OpShutIface    */ use Woozalia\Ferret\Sys\Events\ItWent\connx\iOpShut as OpShutIface;
/* QPanel*  [c,i] */ use Woozalia\Ferret\IO\O\Panel\aux\{ cQPanel as QPanelClass, iQPanel as QPanelIface };
/* QStream* [c,i] */ use Woozalia\Ferret\Sys\InOut\Connx\aux\Q\{ cStream as QStreamClass, iStream as QStreamIface };
/* SelfIface      */ use Woozalia\Ferret\IO\Aspect\Connx\run\iSession as SelfIface;
/* StartIface     */ use Woozalia\Ferret\IO\Aspect\Connx\run\iStarter as StartIface;
/* StreamIface    */ use Woozalia\Ferret\Sys\InOut\Connx\iStream as StreamIface;

interface iSession extends BaseIface {
    // SETUP
    static function FromCommand(CommandIface $o) : SelfIface;
    #static function FromStartCmd(StartIface $o, CommandIface $oCmd) : SelfIface;
    #static function FromStarter(StartIface $o) : SelfIface;
    // STATUS
    var bool $IsRunning { get; }
    // ACTION
    #function DoCommand(CommandIface &$oCmd) : OpCmdIface;
    function DoStream(StreamIface $oStrm) : OpCmdIface;
    #function DoRequest(CommandIface $o) : OpDataIface;
    function DoInOutLoop()  : OpCmdIface; // FUTURE (2026/03/17): We *might* eventually need a different Op class. This is what we had been using, however.
    function DoBreak();
    // OBJECTS
    var StartIface    $OStart { get; }
    var CommandIface  $OCommand { get; }
    function QPanel() : QPanelIface;
    function OOpCmd(?OpCmdIface $o=NULL) : OpCmdIface;
    // OBJECTS: action
    function NewSession(CommandIface &$oCmd) : SelfIface;
    // OBJECTS: streams
    var StreamIface $OExecLecture { get; }  // stream received from process (stdout) (the process's lecture)
    var StreamIface $OExecRespond { get; }  // stream for talking to process (stdin) (responding to the process)
    var StreamIface $OExecErrsBin { get; }  // stream for process to send errors (stderr) (errors from process)
    var QStreamIface $QOAppLecturer { get; }
    var QStreamIface $QOAppListener { get; }
    var QStreamIface $QOAppErrorBin { get; }
}
class cSession extends BaseClass implements SelfIface {

    // ++ CONFIG ++ //

    // CONFIG
    public function SType() : string { return 'run session'; }

    public string $SIdentityForLog { get => parent::$SIdentityForLog::get().' '.$this->OCommand->AsString; }

    protected string $SCSession { get => cSession::class; }

    // -- CONFIG -- //
    // ++ SETUP ++ //

    public static function FromCommand(CommandIface $oCmd) : SelfIface {
        $oThis = new static;
        $oThis->OCommand = $oCmd;
        return $oThis;
    }

    /* 2026-09-03 use FromCommand() instead
    public static function FromStarter(StartIface $oStart) : SelfIface {
        $oThis = new static;
        $oThis->OStart = $oStart;
        return $oThis;
    }
    */

    // -- SETUP -- //
    // ++ STATUS ++ //

    var bool $IsRunning { get => $this->OCommand->OConn->IsRunning; }

    // -- STATUS -- //
    // ++ ACTION ++ //

    private $doBreak;
    public function DoBreak() { $this->doBreak = TRUE; }

    #public function DoCommand(CommandIface &$oCmd) : OpCmdIface {
    public function DoCommand() : OpCmdIface {
        #$this->OCommand = $oCmd;
        $oCmd = $this->OCommand;
        $oCmd->OSession = $this;

        // if this command has an updater, deploy it:
        $oUpd = $oCmd->OUpdate;
        $qoUpd = self::QOUpdater();
        $qoUpd->SetObjNz($oUpd);

        $qoUpd->OnEvent(new EventOnBefore);
        $oOpComm = $this->SendCommand();
        if ($oOpComm->IsOkay) {
            $oOpLoop = $this->DoInOutLoop();
            $oOpComm->Assimilate($oOpLoop);
        }
        $oUpd->OnEvent(new EventOnAfter);
        #$oStrm->Shut();
        return $oOpComm;
    }
    public function DoStream(StreamIface $oStrm) : OpCmdIface {
        $oUpd = $this->OCommand->OUpdate;
        $qoUpd = self::QOUpdater();
        $qoUpd->SetIt($oUpd);

        $this->QOAppLecturer->SetIt($oStrm);
        return $this->DoInOutLoop();
    }
    protected function SendCommand() : OpCmdIface {
        $oCmd = $this->OCommand;
        $sCmd = $oCmd->AsString;
        $oOpComm = $this->OExecRespond->PushBytes($sCmd."\n");
        $oOpCmd = new OpCmdClass;
        $oOpCmd->Assimilate($oOpComm);
        return $oOpCmd;
    }

    // -- ACTION -- //
    // ++ LIFECYCLE ++ //

    protected function ActualOpen() : OpOpenIface {
        $oConn = $this->OCommand->ORunner;
        return $oConn->Open();
    }
    protected function ActualShut() : OpShutIface {
        $oConn = $this->OCommand->ORunner;
        return $oConn->Shut();
    }

    // ++ LIFECYCLE: internals ++ //

    protected function StartProcess() {
        $oConn = $this->OCommand->ORunner;
        // WORKING HERE
    }

    // USAGE: Call this after setting up one or more Streams on the Command, and before calling DoInOutLoop().
    # 2026-09-03 If this is needed here, it should be a shortcut to the Comm object's implementation.
    #abstract protected function CanalSetup();

    public function DoInOutLoop() : OpCmdIface {
        $this->CanalSetup();
        $qoUpdate = self::QOUpdater();

        $oCanals = $this->OCanals;
        $oCanals->Open();

        $oOpConvey = NULL;
        $oOpThis = $this->OOpCmd();

        $doLoop = TRUE;
        $this->doBreak = FALSE;


        $oeBefore = new EventOnBefore;
        $oeLoop = new EventOnLoop;
        $oeAfter = new EventOnAfter;
        $oCanals->OnEvent($oeBefore);
        $qoUpdate->OnEvent($oeBefore);

        while ($doLoop) {
            $qoUpdate->OnEvent($oeLoop);
            $oOpNew = $oCanals->ConveyCheck();
            if (!$oOpNew->IsOkay) {
                $this->DoBreak();
            }
            $oOpThis->Assimilate($oOpNew);
            $doLoop = $this->IsRunning && !$this->doBreak;
        }
        $oCanals->OnEvent($oeAfter);
        $qoUpdate->OnEvent($oeAfter);

        $oCanals->Shut();

        return $oOpThis;
    }

    // -- LIFECYCLE -- //
    // ++ OBJECTS ++ //

    public function NewSession(CommandIface &$oCmd) : SelfIface {
        $oSess = ($this->SCSession)::FromStarter($this);
        $oSess->OCommand = $oCmd;
        return $oSess;
    }

    public StartIface $OStart;

    public APipesIface $OACodePipes { get => $this->OACodePipes ??= new APipesClass; }  // pipes from application code
    // NOTE: Local-Proc defines an additional set for the process (2026-08-23 why don't we need that for SSH2?)

    // OBJECTS: streams
    public StreamIface $OExecLecture { get => $this->OCommand->OConn->OLecture; }  // stream received from process (stdout) (the process's lecture)
    public StreamIface $OExecRespond { get => $this->OCommand->OConn->OListen; }  // stream for talking to process (stdin) (responding to the process)
    public StreamIface $OExecErrsBin { get => $this->OCommand->OConn->OErrors; }  // stream for process to send errors (stderr) (errors from process)
    // SHORTCUTS
    public QStreamIface $QOAppLecturer { get => $this->OACodePipes->QOStream; }
    public QStreamIface $QOAppListener { get => $this->OACodePipes->QIStream; }
    public QStreamIface $QOAppErrorBin { get => $this->OACodePipes->QEStream; }

    protected CanalsIface $OCanals;

    public CommandIface $OCommand {
        set(CommandIface $o) {
            $o->OSession = $this;
            $this->OCommand = $o;
            $oUpd = $o->OUpdate;
            self::QOUpdater()->SetObjNz($oUpd);
        }
    }

    private $qoPanel = NULL;
    public function QPanel() : QPanelIface { return $this->qoPanel ?? ($this->qoPanel = QPanelClass::AsNew()); }

    // 2026-04-01 copied from Starter
    private $oRes = NULL;
    public function OOpCmd(?OpCmdIface $o=NULL) : OpCmdIface { return is_null($o) ? ($this->oRes ?? $this->OOpCmdNew()) : ($this->oRes = $o); }
    protected function OOpCmdNew() : OpCmdIface { return new OpCmdClass; }

    // -- OBJECTS -- //
    // ++ DEBUG ++ //

    public function VIEW_Inline() : string {
        $a = $this->OACodePipes->AVals;
        $s = '';
        foreach ($a as $key => $oStream) {
            $sType = $oStream->SType();
            $sID = $oStream->SIdentityForLog;
            $s .= "[#$key:$sID ($sType)]";
        }
        return $s;
    }

    // -- DEBUG -- //
}