php/netOld.php

<?php
require_once 'cloud.php';

Class Pipeline {

    public static function cfgBase($src, $ignMiss) { # cleanup the base config $src and generatebase 
        $wlk = $rwk = $wNR = $fNR = [];
        foreach ($src as &$s) {
            for ($bx = 0; $bx < 4; $bx++)
                $s[$bx] = trim($s[$bx] ?? '');
            $s[4] = isset($s[4]) ? ($s[4] ? preg_split('/\s+/', $s[4], flags: PREG_SPLIT_NO_EMPTY) : []) : ($s[0] ? [$s[0]] : []);
            if ($s[0])
                isset($wlk[$s[0]]) ? err("duplicate wlk $s[0] in", $src) : $wlk[$s[0]] = $s;
            if ($s[1]) {
                $rwk[$s[1]][] = $s;
            } else {
                $wNR[$s[0]] = $s;
                array_push($fNR, ...$s[4]);
            }
        }
        $n = [$wlk, $rwk, $wNR, $fNR, $ignMiss, $src];
        return $n;

    }

    /* add a cfg to $c with name $cfgN and keys $kys 
       indexes: walk => by wlk ix, rwk => $rwkys empty => empty row , flds => fields */
   public static function cfg($c, $rwKys) { 
        $wlk = $c[2]; #walk indexes without row key
        $empty = [];
        $flds = $c[3]; #fileds without row key
        foreach($rwKys as $k) {
            if ( ! $aLi = $c[1][$k] ?? null) {
                if (isset($c[4][$k]))
                    continue;
                err("fieldName $k not in cfg", array_keys($c[1]));
            }
            $empty[$k] = null;
            foreach($aLi as $a) {
                if (isset($wlk[$a[0]]))
                    err("cfgAdd $a[0] already set in ele, keys", $kys);
                $wlk[$a[0]] = $a;
                array_push($flds, ...$a[4]);
            }
        }
        return ['walk' => $wlk, 'row' => $rwKys, 'empty' => $empty,  'flds' => $flds];
    }

    public function __construct($cl, $cfg) {
        $this->cloud = $cl;
        $this->cfg = $cfg;
        $this->errF = Net::ERRFILTER;
        $this->out = null;
    }

    public function makeWalk($trg) {
        if (str_starts_with($trg, 'meta')) {
            $this->metaDo = 1;
            $trg = substr($trg, 4);
        }
        $this->walk->rowWriter = $this->rowWriter = new $trg;
        return $this;
    }

    public function constructOld($tr, $cfg, $pp) {
        $this->cfg = $cfg;
        # $trA = preg_split('/\s+/', $trg, flags: PREG_SPLIT_NO_EMPTY);
        $pL = count($pp);   
        $pp[] = $this->target = $tr; # = array_shift($trA);   
        $mm = fn($ft) => match ($ft) {
                'req-string' => new Net2Str,
                'req-xmlRdr' => new Net2XMLReader,
                'string-jsonObj' => new Json(false),
                'string-jsonArr' => new Json(true),
                'xmlRdr-rowOut' => new WalkXML($cfg, 'rowOut'),
                'xmlRdr-rowTb' => new WalkXML($cfg, 'rowTb'),
                'xmlRdr-rowArr' => new WalkXML($cfg, 'rowArr'),
                'xmlRdr-rowCsv' => new WalkXML($cfg, 'rowCsv'),
                default => null,
            };  
        for ($px=0; $px < $pL; $px++) {
            $st =  $pp[$px];
            $nxt = $pp[$px+1];
            if ($this->$st = $mm("$st-$tr"))
                break;
            if (! ($this->$st = $mm("$st-$nxt")))
                err("no pipe step $st -> $nxt");
        }
    }
 
    public function request($uri, $ctx, $doBits=7){
        $res = 0;
        if ($doBits & 2) {
            $res = $this->req->request($uri, $ctx);
            # out("req got $res");
            if (is_int($res))
                return $res;
            if (isset($this->httpChk) and $this->httpChk !== $this->req->httpRsp[0])
                err("uri $uri bad status {$this->req->httpRsp[0]} expected $this->httpChk");
            if (isset($this->json)) {
                $res =  json_decode($o=$res, $this->json);
                if (('No error' !== $l = json_last_error_msg()) or ! $res) 
                    err('json returns', gettype($res), ", err $l\njson", $res, "input $o");
            }
        }
        if ( ! isset($this->walk))
            return $res;
        if ($doBits & 1) {
            $this->walk->pathPre = $this->cloud->fPath ?? null;
            if (isset($this->metaDo))
                $this->rowWriter->single('meta', $this->meta);
        }
        return $doBits === 2 ? $this->walk->walk($res) : $this->walk->walkAll($res, doBits: $doBits);
    }
} # end Class Pipeline

Class Net {
    public const 
          OK = -1
        , NOTFOUND = -2
        , CD2TXT = [Net::OK => 'ok'
            , Net::NOTFOUND => 'notFound'
        ]
        , ERRFILTER = [ 'notFound' => ['notFound' => Net::NOTFOUND]]
        , N2STR = -11
        , N2JSONOBJ = -12
        , N2JSONARR = -13
        , N2XMLRDR = -14
        ;

    public static $errFun, $errCode,$errRspH, $errXml, $errLast;

    public static function errF($fun, $rspH = null) { # error filter: either fail with an error message or return an error result
        Net::$errFun = $fun;
        $r = Net::OK;
        Net::$errCode = Net::$errXml = Net::$errLast = null;
        if (Net::$errRspH = $rspH) 
            ($rspH[0] === 'HTTP/1.1 404 Not Found') ? $r = Net::NOTFOUND : err("rspHdr $fun response $rspH[0]", $rspH);
        if (Net::$errXml = libxml_get_last_error()) 
            err("$fun xml", Net::$errXml, ', errors', libxml_get_errors());
        if ((Net::$errLast = $e = error_get_last()) and $r === Net::OK)
            str_ends_with($e['message'], 'Failed to open stream: No such file or directory') ? $r = Net::NOTFOUND : err("$fun", $e);
        if ($r === Net::OK)
            err("$fun failed, but no message found"); 
        Net::errClear(true);
        return Net::$errCode = $r;  
    }

    public static function err($fun, $rspH = null) { # error filter: either fail with an error message or return an error result
        err("$fun code " . ($c = Net::errF($fun, $rspH) . " " . Net::CD2TXT[$c]));
    }

    public static function errClear($silent=false) {
        if ($e = error_get_last()) {
            $silent or out("clearing error_get_last", $e);
            error_clear_last();
        }
        if ($e = libxml_get_last_error()) {
            $silent or out("clearing libxml_get_last_error", $e, ', errors', libxml_get_errors());
            libxml_clear_errors();
        }
        libxml_use_internal_errors(true);
        Net::$errFun = Net::$errCode = Net::$errRspH = Net::$errXml = Net::$errLast = null;
    }
} #end Class Net

Class Net2Str extends Net {
    public function request($uri, $ctx) {
        Net::errClear();
        if (! is_string($r = @ file_get_contents($uri, 0, $ctx)))
           return Net::errF("get($uri)", $http_response_header);
        $this->httpRsp = $http_response_header;
         // echo "ff response\n" . print_r($http_response_header, true). "\n";
      /*  if ($r == '' or $http_response_header[0] != 'HTTP/1.1 207 Multi-Status')
            err("$uri returned $http_response_header", $http_response_header);
         echo "\nff result\n" . preg_replace_callback('=(<d:getlastmodified>)([^<>]*)(</d:getlastmodified>)='
            , fn ($m) => $m[1] . $m[2] . ' => ' . toLocalTst($m[2]) . $m[3]
            , xmlpp($r)) . "\n"; */
        return $r;
    }
}

Class Net2XMLReader extends Net {
    public function request($uri, $ctx) {
        Net::errClear();
        # libxml_use_internal_errors(true); done in errClear
        $ctx and libxml_set_streams_context($ctx);
        $this->xr ??= new XMLReader;
        if (!$this->xr->open($uri))
            return Net::errF("open($uri)", $http_response_header);
        $this->httpRsp = $http_response_header;
        return $this->xr;
    }
}

Class Req2SimpleXML {
    public function webRequest($uri, $ctx, $cld) {
        libxml_set_streams_context($ctx);
        $xml = simplexml_load_file($uri,options: LIBXML_PEDANTIC);
        var_dump($xml);
        print_r($xml);
        for ($xml->rewind(); $xml->valid(); $xml->next()) {
            foreach($xml->getChildren() as $name => $data) {
            echo "The $name is '$data' from the class " . get_class($data) . "\n";
            }
        }
        foreach($xml->children() as $c) out("child $c", $c->getName());
        out('getName', $xml->getName());
        out('children', $xml->getChildren());
        out('rsp getName', $xml->response[0]->getName());
        out('rsp children', $xml->response->children());
        return $xml;
        $r = file_get_contents($uri, 0, $ctx);
        // echo "ff response\n" . print_r($http_response_header, true). "\n";
        if ($r == '' or $http_response_header[0] != 'HTTP/1.1 207 Multi-Status')
            err("$uri returned $http_response_header", $http_response_header);
        /* echo "\nff result\n" . preg_replace_callback('=(<d:getlastmodified>)([^<>]*)(</d:getlastmodified>)='
            , fn ($m) => $m[1] . $m[2] . ' => ' . toLocalTst($m[2]) . $m[3]
            , xmlpp($r)) . "\n"; */
        return $r;
    }
}

class RowOut {
    private $rowC = 'rowC-construct';
    public function begin ($id, $hdr = '') { # write header info for the following rows
        $this->rowC = 0;
        out("RowWriter $id begin", $hdr);
    } 
    public function write($row) { # write one row
        out("row $this->rowC", $row);
        $this->rowC++;
    } 
    public function end($id) { # end of the row sequence
        out("RowWriter $id end, $this->rowC rows");
        $this->rowC = 'rowC-end';
    } 
    public function single ($id, $row) { # a sequence of a single row
        out("RowWriter $id single", $row);
    } 

}

class RowTb {
    private $rowC = 'rowC-construct';
    public function begin ($id, $hdr = '') { # write header info for the following rows
        $this->rowC = 0;
        outH("tbh $id", __CLASS__);
        outTb();
        if ($hdr)
            outTRH(...$hdr);
    } 
    public function write($row) { # write one row
        outTRD(...array_values($row));
        $this->rowC++;
    } 
    public function end($id) { # end of the row sequence
        outTbEnd();
        out("tbh $id end, $this->rowC rows", __CLASS__);
        $this->rowC = 'rowC-end';
    } 
    public function single ($id, $row) { # a sequence of a single row
        $this->begin($id, array_keys($row));
        $this->write($row);
        $this->end($id);
    } 

}


class WalkTree extends Cloud /* implements Stringable */ {
    public const SIBLING = 99;

    /*  build base config: trim $src, 
        indexes: 0 => by walk ix, 1 => by row key 2 => by wlkIx if rowkey='', 3 => default cloud fields, 4 => ignoreMiss, 5 src */
 
    public $e2m, $allB, $allE, $rowH, $pathPre, $out;

    public function __construct($cfg, $rw=null, $allB = false, $allE = false) {
        if (is_object($cfg))
            $cfg = ($this->ppl = $cfg)->cfg;
        $this->e2m = $cfg['walk'];
        $this->rowEmpty = $cfg['empty'];
        $this->rowWriter = $rw;
        $this->allB = $allB;
        $this->allE = $allE;
    }

    function walkAll($src, $rw=null, $id=__CLASS__, $allB = null, $allE = null, $doBits=7) { # configure, begin the writer, walk and end the writer
        if ($doBits & 1) {
            $rw and $this->rowWriter = $rw;
            $allB and $this->allB = $allB;
            $allE and $this->allE = $allE;
            $this->rowWriter->begin($id, array_keys($this->rowEmpty));
        }
        if ($doBits & 2) 
            $this->walk($src);
        if ($doBits & 4) 
            return $this->rowWriter->end($id);
    }

    public function aText($mm, $tx) { $this->row[$mm[1]] = $tx; } # assign text unchanged into row

    public function aTst($mm, $tx) { $this->row[$mm[1]] = toLocalTst($tx); } # assing timestamp converted to local timestamp

    public function aDir($mm) { $this->row[$mm[1]] = CLOUD::DIR; } # assign directory constant

    public function aType($mm, $tx) { $this->row[$mm[1]] = $this->typeMap[$tx] ?? $tx; } # assign the mapped mimeType

    public function aPpl($mm, $tx) { # assing text to ppl
        $k = $mm[0];
        if (! is_object($this->ppl ?? null))
            err("aPpl $k ppl missing or bad", $this->ppl ?? '') ;
        elseif ($this->ppl->$k ?? false)
            err("aPpl $k already set: {$this->ppl->$k}, new:", $tx) ;
        else
            $this->ppl->$k = $tx;
        return self::SIBLING;
    }

}  # end class WalkTree 

class WalkXML extends WalkTree {
    public function __construct($cfg, $rowH=null, $allB = null, $allE = null) {
        parent::__construct($cfg, $rowH, $allB, $allE);
    }

    public function __toString() {
        $xr = $this->xr;
        return '{' . __CLASS__ . "->xr $xr->nodeType: $xr->name}";
    }

    function walk($xr) { # walk the Tree by the XMLReader $xr
        $this->nst = '';
        ($this->xr = $xr)->read() or $this->rChk();
        $this->walkRec();
        $xr->nodeType === XMLReader::NONE or err("not at end after walk $this");
    }

    public function rChk () {
        static $fCnt = 0;
        ++$fCnt;
        # out("rChk $fCnt $this, last", libxml_get_last_error(), ", errors", libxml_get_errors(), ', error_get_last', error_get_last());
        if ($this->xr->nodeType !== XMLReader::NONE or libxml_get_last_error() or error_get_last())
            Net::err("rChk");
        ($fCnt > 30) and Net::err("too many XML errs");
        return false;
    }

    function walkRec() {
        $nm = ($xr = $this->xr)->name;
        $pn = $this->nst = "$this->nst/$nm";
        $mm = $this->e2m[$nm] ?? null;
        $r = $this->allB ? $this->{$this->allB}($mm) : (($mm[2] ?? false) ? $this->{$mm[2]}($mm) : null);
        if ($xr->isEmptyElement) 
            return $xr->read() or $this->rChk();
        elseif ($r === self::SIBLING)
            return $xr->next();
        $txt = '';
        $xr->read() or $this->rChk();
        while (XMLReader::END_ELEMENT !== $ty = $xr->nodeType) {
            # out("     ty $this->nodeType name $this->name");
            if ($ty === XMLReader::TEXT) {
                $txt .= $xr->value;
                $xr->read() or $this->rChk();
            } elseif ($ty === XMLReader::ELEMENT) {
                $this->walkRec();
                $this->nst = $pn;
            } else {
                err("unsported xml node", $this);
            }
        }
        $this->allE ? $this->{$this->allE}($mm, $txt) : (($mm[3] ?? false) ? $this->{$mm[3]}($mm, $txt) : null);
        if ($nm === $xr->name)
            return $xr->read() or $this->rChk();
        err("end $pn expected but $this");
    }

    public function outB($mm) {
        $xr = $this->xr;
        out(($xr->isEmptyElement ? 'emp' : 'beg') ." $this->nst mm", $mm);
        for($i=0; $xr->moveToNextAttribute();$i++)
            out("  att $i: $xr->name => $xr->value");
        if ($mm[2] ?? false) {
            if (($r = $this->{$mm[2]}($mm)) === self::SIBLING and ! $xr->isEmptyElement)
                out("nxt $this->nst skipping to next node");
            return $r;
        }
    }

    public function outE($mm, $txt) {
        out("end $this->nst, txt=$txt");
        if ($mm[3] ?? false)
            return $this->{$mm[3]}($mm, $txt);
    }
    
    public function responseB($mm) {
        $this->row = $this->rowEmpty;
        $this->propC = $this->propS = -1;
    }

   public function responseE($mm) {
        ($this->propC === $this->propS) or err(__METHOD__ . "#prop $this->propC !==  #status $this->propS");
        $this->rowWriter->write($this->row);
        unset($this->row, $this->propC);
    }

    public function hrefE($mm, $tx) {
        $pa = rawurldecode($tx);
        $this->row[$mm[1]] = ! str_starts_with($pa, $this->pathPre) ? "???$pa" 
            : (strlen($pa) > strlen($this->pathPre) ? substr($pa, strlen($this->pathPre)) : "./");
    }
           
    public function propB($mm) {
        ($this->propC === $this->propS) or err(__METHOD__ . " #prop $this->propC !==  #status $this->propS");
        $c = ++$this->propC;
        return $c ? self::SIBLING : null;
    }

    public function statusE($mm, $tx) {
        ($this->propC !== $this->propS-1) or err(__METHOD__ . "#prop $this->propC !== -1 + #status $this->propS");
        $c = ++$this->propS;
        $c === 0 and $tx === 'HTTP/1.1 200 OK' or $c === 1 and $tx === 'HTTP/1.1 404 Not Found' or err("after $pn #$c bad status $tx");
     }
}  # end class WalkXML

class WalkJson extends WalkTree {
    public function __construct($cfg, $rowH=null, $allB = null, $allE = null) {
        parent::__construct($cfg, $rowH, $allB, $allE);
    }

    public function __toString() {
        $xr = $this->xr;
        return '{' . __CLASS__ . "->xr $xr->nodeType: $xr->name}";
    }

    function walk($js) { # walk the Tree of the json $js
        $this->nst = '';
        $this->walkRec($js);
    }

    function walkRec($js) {
        $pa = $this->nst;
        if (! is_array($js)) {
            err("walkRec not an array at $pa, js", $js);
        } elseif (array_is_list($js)) {
           foreach ($js as $w => $c) {
                $this->nst = "$pa/$w";
                $this->walkRec($c);
            }
        } else {
            $this->row = $this->rowEmpty;
            foreach ($js as $w => $c) {
                $mm = $this->e2m[$w] ?? null;
                $this->nst = $pc = "$pa/$w";
                $r = isset($this->allB) ? $this->{$this->allB}($mm, $c) : (($mm[2] ?? false) ? $this->{$mm[2]}($mm, $c) : 0);
                if ($r !== self::SIBLING and is_array($c)) {
                    $this->nst = $pc;
                    $this->walkRec($c);
                    $this->nst = $pc;
                    $r = isset($this->allE) ? $this->{$this->allE}($mm, $c) : (($mm[3] ?? false) ? $this->{$mm[3]}($mm, $c) : 0);
                }
            }
            
            $this->row and $this->rowWriter->write($this->row);
            $this->row = false;
        }
    }

    public function outB($mm, $js) {
        is_array($js) ? out("beg $this->nst", $mm) : out("b-e $this->nst", $js, $mm);
        if ($mm[2] ?? false) 
            return $this->{$mm[2]}($mm, $js);
    }

    public function outE($mm, $js) {
        if ($mm[3] ?? false) 
            return $this->{$mm[3]}($mm, $js);
        out("end $this->nst", $js);
    }
    public function parentB($mm, $js) {
        $this->row[$mm[1]] = $js[0];
        return self::SIBLING;
    }

    public function ownerB($mm, $js) {
        $this->row[$mm[1]] = $js[0]['emailAddress'];
        return self::SIBLING;
    }

} # end class WalkJson    

/* ------------------
require_once 'cloud.php';

if (0) {
    $s = new Net2Str;
    $o = (object) ['net' => $s, 'errFltr' => Net::ERRFILTER['notFound']];
    out('txt', $s->request('../www/inf/php/aa.txt', opt: $o), 'hdr', $s->hdr, 'body', $s->body);
    out('txt', $s->request('https://localhost/home/inf/php/aa.txt', opt: $o), "\nhdr", $s->hdr, "\nbody", $s->body);
} elseif (0) {
    $x = new Net2XMLReader;
    $o = (object) ['net' => $x, 'errFltr' => Net::ERRFILTER['notFound']];
    out('txt', $r = $x->request('o.xml', opt: $o), 'hdr', $x->hdr); # '../www/inf/php/a.txt'
    for ($i=0; ($r->read() or Net::errF('xml read')) and  $i <10; $i++)
            out("$i $r->nodeType $r->name depth $r->depth");
} elseif (0) {
    out("notF", Net::NOTFOUND, Net::CD2TXT[Net::NOTFOUND]);
} else {
    $f = new CloudFactory;
    $cc = $f->make('wlklNC');
    $o = (object) ['net' => new Net2XMLReader, 'errFltr' => Net::ERRFILTER['notFound']
            , 'walk' => new WalkXML(WalkTree::$cfg['all'], 'outB', 'outE')];
    out($cc->ffReq('nf', $o)); #new Req2Str);
    outH('reuse xr');
    out($cc->ffReq('Documents', $xr)); #new Req2Str);
    #out('get', $cc->getHTTP('1OI9ebyLj9r6LsfFlEq9x8JdFWfEgT2Jq6ofUI4IW1PQ'));
}
exit(outEnd());

if (0) {
    $x = new MyXml(MyXml::$cfg['all'], 'outB', 'outE');
    $x->open('o.xml');
    if (0) {
        for ($i=0; $x->read() and $i <100; $i++)
            out("$i $x $x->localName depth $x->depth, uri $x->baseURI");
        exit();
    }
    $x->read();
    $x->walk();
    exit(outEnd());
}
$myXMLData =
"<?xml version='1.0' encoding='UTF-8'?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>";

$xml=simplexml_load_string($myXMLData) or die("Error: Cannot create object");
print_r($xml);
libxml_use_internal_errors(true);

$xml=simplexml_load_file('o.xml',options: LIBXML_PEDANTIC | LIBXML_NONET);
if (! $xml) {
    err('xml error', libxml_get_errors());
} else {
    print_r($xml);
    out('---all', $xml);
    out('title', $xml->title);
    out('href', (string)$xml->{'d:response'}->{'d:href'});
}
exit();
    $cc->ff('', fn ($n) => outTRD(...$n)
            , fn($m) => [out(']]tbh meta'), outTb(), outTRH(...$m[0]), outTRD(...$m[1]), outTbEnd(), out(']]tbh ffid'), outTb(), outTRH(...$m[2])]);
    outTbEnd();
    out("$cc->ffFo folders, $cc->ffFi files, " . sprintf('%7.2e', $cc->ffSz) . ' Bytes (file) at ' . toLocalTst('now'));
    $e = fopen('php://temp', 'w+');
    $o = fn ($n) => fputcsv($e, $n);
    $cc->ff('', $o
            , fn($m) => [$o([']]tbh meta']), $o($m[0]), $o($m[1]), $o([']]tbh ffid']), $o($m[2])]);
    out("$one $cc->ffFo folders, $cc->ffFi files, " . sprintf('%7.2e', $cc->ffSz) . ' Bytes (file) at ' . toLocalTst('now'));
    $sz = ftell($e);
    rewind($e);
    out("$one writing $sz bytes to $one:ffid-$one.csv, at " . toLocalTst('now'));
    $cc->upload("ffid-$one.csv" ,'text/csv', $sz < 1 ? '' : fread($e, $sz));
    fclose($e);

outEnd();
exit();
------------ */