php/net.php
<?php
require_once 'cloud.php';
Class Pipeline {
/*----- cfgBase: build the base rowConfiguration (rwc)
cfgBase converts the old base (the input) to a new base to work with later
a configuration item is a list with 5 elements (see table below)
cfgBase cleans up the items and replaces the old base with the new one, containing different maps (see table below)
predefined rwc are in the new base with keys starting with a % - they will be lazily generated by the rowCfg function in the cloud
item cfg base new base old
cpr cloud property name 0 0 => cpr => item 0 => cpr => item 0 => list(item...)
rwk row Key 1 1 => rwk => list(item...) 1 => igMi
meB walk begin method 2
meE walk end method 3
rwPr list of cpr for rwk 4 4 => list(all cpr)
rwEm empty row: rwk => false rwEm => rwEm
rws implode(' ', key rwk) rws => rws
nrPr list(cpr) for no rwk nrPr => list(cpr) for rwk=''
nrPI cpr => item if rwk='' nrPI => cpr => item if rwk=''
igMi ignore missing rwk igMi => rwk => 1
-----*/
public static function cfgBase(&$sCfg) { # cleanup the base config $src and generatebase
list ($src, $ignMiss) = $sCfg;
$cpr = $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($cpr[$s[0]]) ? err("duplicate cpr $s[0] in", $src) : $cpr[$s[0]] = $s;
if ($s[1]) {
$rwk[$s[1]][] = $s;
} else {
$wNR[$s[0]] = $s;
array_push($fNR, ...$s[4]);
}
}
$sCfg = [$cpr, $rwk, 'nrPI' => $wNR, 'nrPr' => $fNR, "igMi" => $ignMiss];
$sCfg['%allAll'] = $k = array_keys($rwk);
$sCfg['%all'] = array_filter($k, fn ($e) => ! str_starts_with($e, 'version'));
foreach(Cloud::ROWKYS as $nm => $flds)
$sCfg[$nm] = ! isset($sCfg[$nm]) ? $flds : err("rcw key $nm already defined:", array_keys($sCfg));
}
/*----- add a cfg to $c with name $cfgN and keys $kys
indexes: walk => by wlk ix, row => $rwkys empty => empty row , flds => fields
-----*/
public static function rowCfg($c, $rwKys) {
dbg1(__METHOD__, $c, "rwKys", $rwKys);
is_string($rwKys) and $rwKys = preg_split('/\s+/', $rwKys, flags: PREG_SPLIT_NO_EMPTY);
$wlk = $c['nrPI']; #walk indexes without row key
$empty = [];
$flds = $c['nrPr']; #fields without row key
foreach($rwKys as $k) {
if ( ! $aLi = $c[1][$k] ?? null) {
if (isset($c['igMi'][$k]))
continue;
else
err("unknow rowKey $k");
}
$empty[$k] = false; # not null because isset($empty[$k]) should yield true !
foreach($aLi as $a) {
if ($a[0]) {
if (isset($wlk[$a[0]]))
err("cfgAdd $a[0] already set in ele, keys", $wlk);
$wlk[$a[0]] = $a;
}
array_push($flds, ...$a[4]);
}
}
return [0 => $wlk, 'rws' => implode(' ', $rwKys), 'rwEm' => $empty, 4 => $flds];
}
public $doBits # a bit mask, may be different for pipelines with the identical ppl
, $ppc # this is an object, shared by all pipe of it, so changes are visible for all pipelines with this ppl
, $outFD;
public function __construct($clOrPi, $doBi=Net::PHST, $rwc=null) {
if ($clOrPi instanceof Pipeline) {
$rwc and err("rwc not null with pipeline", $clOrPi);
$this->ppc = $clOrPi->ppc;
} else {
$rwc or err("rwc null with cloud", $clOrPi);
$this->ppc = (object) ['cloud' => $clOrPi, 'rwcW' => $rwc, 'rwcR' => $rwc, 'rowWrite2' => null, 'out' => null, 'outFD' => null];
}
$this->doBits = is_int($doBi) ? $doBi : (ctype_digit($doBi) ? (int) $doBi : Net::s2bits($doBi));
}
public function setBits($doBi) { $this->doBits = $doBi; return $this; }
public function makeWalk($trg) {
if (str_starts_with($trg, 'meta')) {
$this->ppc->metaDo = 1;
$trg = substr($trg, 4);
}
$this->ppc->rowWriter = new $trg;
return $this;
}
public function outBegin() {
return (($p = $this->ppc)->outFD) ? err("outFD already exists") : $p->outFD = fopen(is_string($p->out) ? $p->out : 'php://temp', 'w+');
}
public function outEnd() {
if (! $fd = ($p = $this->ppc)->outFD)
err("no outFD");
$p->outFD = null;
return is_string($p->out) ? fclose($fd) : $fd;
}
public function request($uri, $ctx, ...$aa) {
dbg1("request $uri", $this);
$p = $this->ppc;
$doBits = $this->doBits;
foreach ($aa as $k => $v)
$p->$k = $v;
$res = 0;
if ($doBits & Net::W1) {
$res = $p->req->request($uri, $ctx);
dbg(5, "req got", $res);
if (isset($p->httpChk) and $p->httpChk !== $p->req->rspH[0])
throw new NetEx("unexpected status {$p->req->rspH[0]} expected $p->httpChk", rspBody: $res, uri: $uri, rspH: $p->req->rspH);
if (isset($p->json)) {
$res = json_decode($o=$res, $p->json);
if (('No error' !== $l = json_last_error_msg()) or ! $res)
throw new NetEx("json err $l", res: $res, json: $l, input: $o);
}
}
if ( ! isset($p->walk)) # config/begin/end is only necessary with walk !
return $res;
if ($doBits & Net::CONFIG) {
$p->rowWrite2 and $p->rowWrite2->config($this, $p->out); #$p->walk->beginpPath = $pPath ?? $p->cloud->pPath ?? null; # todo: am richtigen Ort?
$p->rowWriter->config($this, $p->rowWrite2 ?? $p->out);
$p->walk->config($this, $p->rowWriter);
}
if ($doBits & Net::BEGIN) {
$doBits & Net::OUT and $p->out and $this->outBegin();
if (isset($p->metaDo))
($p->rowWrite2 ?? $p->rowWriter)->single('meta', $p->meta);
if ($doBits & Net::RWR) {
$p->rowWrite2 and $p->rowWrite2->begin();
$p->rowWriter->begin();
}
}
$doBits & Net::W1 and $p->walk->walk($res);
if ($doBits & Net::END) {
if ($doBits & Net::RWR) {
$res = $p->rowWriter->end();
$p->rowWrite2 and $res = $p->rowWrite2->end();
}
return ($doBits & Net::OUT and $p->out) ? $this->outEnd() : $res;
}
}
} # end Class Pipeline
function netEx($m, ...$a) {
$a['NetExCall'] = 1 + ($a['NetExCall'] ?? 0);
if (($h = $a['rspH'] ?? false) and $h[0] !== 'HTTP/1.1 200 OK')
throw new (($ct = NetEx::STA2CT[$h[0]] ?? ['NetEx', $h[0]])[0])("$m $ct[1]", ...$a);
elseif ($e = error_get_last())
throw strpos($f = $e['message'], 'Failed to open stream: No such fil') !== false ? new NetNotFound("$m No such file or directory", ...$a) : new NetEx("$m $f", ...$a);
else
throw new NetEx($m, ...$a);
}
class NetEx extends Exception {
public const STA2CT =
[ 'HTTP/1.1 403 Forbidden' => ['NetDuplicate', 'already exists but no directory']
, 'HTTP/1.1 404 Not Found' => ['NetNotFound', '']
, 'HTTP/1.1 405 Method Not Allowed' => ['NetDuplicate', 'already exists']
, 'HTTP/1.1 409 Conflict' => ['NetNotFound', 'parent missing']
];
/*
check errors and put them in the array $a
if $all check all errors and return error message
if ! $all check only errors necessary to determine exception class and return its name
*/
public static $infoLv = 9;
public static function errCheck($a) {
if (isset($a['rspH']) and ($e = $a['rspH'][0]) !== 'HTTP/1.1 200 OK')
$n['rspE'] = $a['rspE'] ?? $e;
if ($e = error_get_last()) {
$n['errLast'] = $a['errLast'] ?? "$e[message] at $e[file]:$e[line]";
error_clear_last();
}
if ($e = libxml_get_last_error()) {
$n['xml'] = $a['xml'] ?? $e->message;
libxml_clear_errors();
}
if (($e = json_last_error_msg()) !== 'No error') {
$n['json'] = $a['json'] ?? $e;
json_decode('{}'); # decoding a correct json clears the error - I do not know another method ...
}
return $n ?? null;
}
public static function errClear($silent=false) {
$a = self::errCheck([]);
if (! $silent and $a)
out('clearing', count($a), 'errors', $a);
}
public function __construct(...$a) {
$a[0] ??= ($t = $this->getTrace()[$a['NetExCall'] ?? 0] ?? false) ? (($t['class'] ? "$t[class]$t[type]" : '') . "$t[function]()") : '?';
Exception::__construct($a[0]);
$this->det = $a;
}
public $det;
public function __toString() {
global $dbg;
if (! $c = self::errCheck($this->det)) {
$f = "errChck none";
} else {
$this->add(...$c);
$f = reset($c);
}
if (strlen($m = trim($this->det[0])) > 55)
$m = substr($m, 0, 50) . ' ...';
elseif (strlen($m) < 40 and strpos($m, $g = substr($f, 0, 50-strlen($m))) === false)
$m .= " $g" . ($f === $g ? '' : ' ...');
$r = get_class($this) . " $m";
if ($lv = self::$infoLv ?? $dbg) {
foreach ($this->det as $k => $v) {
if (($k === 0 and strlen($v) < 50) or ($k === 'rspH' and $lv < 2) or $k === 'NetExCall')
continue;
$w = trim(a2str($v));
$r .= "\n$k => " . (($lv === 1 and strlen($w) > 125) ? substr($w, 0, 120) . "..." : $w);
}
}
$tx = ($this->det['NetExCall'] ?? 0) -1;
$r .= "\n" . (($t = $this->getTrace()[$tx+1] ?? false) ? ("by " . ($t['class'] ? "$t[class]$t[type]" : '') . "$t[function]() ") : '')
."in " . ($tx < 0 ? $this->getFile() . ':' . $this->getLine()
: ($t = $this->getTrace()[$tx])['file'] . ":$t[line] $t[function]()");
if ($lv)
$r .= "\nstack trace\n" . $this->getTraceAsString();
return $r; # (self::$html ?? OutHtml) ? str_replace("\n", "\n<br>", $r) : $r;
}
/*----- add the given key/values at the beginning of det -----*/
public function add(...$a) {
array_key_exists(0, $a) or array_unshift($a, $this->det[0] ?? '?');
foreach($this->det as $k => $v)
! isset($a[$k]) ? $a[$k] = $v : (is_string($a[$k]) and is_string($v) and strpos($a[$k], $v) === false and (strpos($v, $a[$k]) === false ? $a[$k] .= "; $v" : $a[$k] = $v));
$this->det = $a;
return $this;
}
} # end class NetEx
class NetNotFound extends NetEx { }
class NetDuplicate extends NetEx { }
Class Net {
public const
BEGIN = 1 #--- 4 phases in the bitmask $doBits
, W1 = 2
, END = 4
, CONFIG= 8
, PHASE = Net::BEGIN | Net::W1 | Net::END | Net::CONFIG
, RWR = 16 #--- 2 steps (after the first two steps request and walk - which are governed by pipeline target) in the bitmask $doBits
, OUT = 32
, STEP = Net::RWR | Net::OUT
, PHST = Net::PHASE | Net::STEP
#--- operational options in the bitmask $doBits
, RECUR = 1<<8 # walk/recurse thru the ff tree
, WRIPA = 1<<9 # ff write parent/start directory
, CREPA = 1<<10 # create parent directories if necessary (recursive)
, KRF = 1<<11 # Keep Revision (Version) Forever
, OVERWR= 1<<12 # overwrite existing for copy etc.
;
public static function s2bits($s) { # return the http context array for a mime multipart request
static $cnsts;
$cnsts ??= (new ReflectionClass(__CLASS__))->getConstants();
$r = 0;
foreach(preg_split('/\s+/', $s, flags: PREG_SPLIT_NO_EMPTY) as $t)
$r |= $cnsts[$t] ?? err("not a Net constang $t");
return $r;
}
public static function mimeMultipart(...$parts) { # return the http context array for a mime multipart request
$cnt = '';
$bndry = 'seParaTor[}{)(]';
for($px=0; $px < count($parts); $px+=2) {
$c1 = $parts[$px+1] ?? '';
$cnt .= "\n--$bndry\nContent-Type: {$parts[$px]}\nContent-Length: " . strlen($c1) ."\n\n$c1";
}
$cnt .= "\n--$bndry--\n";
return ['header' => "Content-Type: Multipart/Related; boundary=$bndry\nContent-Length: " . strlen($cnt)
,'content' => $cnt];
}
public $rspH;
} #end Class Net
Class Net2Str extends Net {
public function request($uri, $ctx) {
NetEx::errClear();
dbg1(__METHOD__, "uri $uri, ctx", $ctx);
$r = @ file_get_contents($uri, 0, $ctx);
$h = $this->rspH = $http_response_header;
dbg1(__METHOD__, "got", is_string($r) ? "len " . strlen($r) . ': ' . xmlPP($r) : $r, "rspH", $h);
return is_string($r) ? $r : throw new (($ct = NetEx::STA2CT[$h[0]] ?? ['NetEx', 'error response'])[0])($ct[1], uri: $uri, rspH: $h);
}
}
Class Net2XMLReader extends Net {
public $xr;
public function request($uri, $ctx) {
NetEx::errClear();
# libxml_use_internal_errors(true); done in errClear
$ctx and libxml_set_streams_context($ctx);
$this->xr ??= new XMLReader;
dbg(3, "xmlReader request to uri $uri");
$r = @$this->xr->open($uri);
$h = $this->rspH = $http_response_header;
dbg1(__METHOD__, "got", $http_response_header);
return $r ? $this->xr : throw new (($ct = NetEx::STA2CT[$h[0]] ?? ['NetEx', 'error response'])[0])("XMLReader open $ct[1]", uri: $uri, rspH: $h);
}
}
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')
netEx("$uri bad response", rspH: $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 RowWriter {
public $writeC = 'writeCconstruct', $id, $rwEm;
public function config($ppl, $next) { $this->id = $ppl->ppc->meta['fun'] ?? '?'; $this->rwEm = $ppl->ppc->rwcR['rwEm']; }
public function single ($id, $row, $doBits = Net::PHASE) { # a sequence of a single row
$idO = $this->id;
$rwEmO = $this->rwEm;
$this->id = $id;
$this->rwEm = $row;
$doBits & Net::BEGIN and $this->begin();
$doBits & Net::W1 and $this->write($row);
$doBits & Net::END and $r = $this->end();
$this->id = $idO;
$this->rwEm = $rwEmO;
return $r ?? null;
}
} # end class RowWriter
class RowOut extends RowWriter {
public function begin () { # write header info for the following rows
$this->writeC = 0;
out("RowWriter $$this->id begin", array_keys($this->rwEm));
}
public function write($row) { # write one row
out("row $this->writeC", $row);
$this->writeC++;
}
public function end() { # end of the row sequence
out("RowWriter $this->id end, $this->writeC rows");
}
public function single($id, $row, $doBits=Net::PHASE) { # a sequence of a single row ????
($doBits & Net::W1) ? out("RowWriter $this->id single", $row) : parent::single($id, $row, $doBits);
}
} # end class RowOut
class RowTb extends RowWriter {
public function begin () { # write header info for the following rows
outH("tbh $this->id", __CLASS__);
$this->writeC = 0;
outTb();
if ($this->rwEm)
outTRH(...array_keys($this->rwEm));
}
public function write($row) { # write one row
outTRD(...array_values($row));
$this->writeC++;
}
public function end() { # end of the row sequence
outTbEnd();
out("tbh $this->id end, $this->writeC rows", __CLASS__);
}
} # end class RowTb
class Row1 extends RowWriter {
public $result;
public function begin () { $this->result = 0; $this->writeC = 0;}
public function write($row) { # write one row
if ($this->result)
throw new NetDuplicate(__METHOD__ . " only one row allowed", newRow: $row, oldResult: $this->result);
$this->result = $row;
$this->writeC++;
}
public function end() { return $this->result ? $this->result : throw new NetNotFound; } #Ex(NetEx::NOTFOUND, __METHOD__) ; }
public function single ($id, $row, $doBits=Net::PHASE) { return ($doBits & Net::W1) ? $this->result = $row : parent::single($id, $row, $doBits); }
} # end class Row1
class Rows extends RowWriter {
public $result;
public function begin () { $this->result = []; $this->writeC = 0; }
public function write($row) { $this->result[] = $row; $this->writeC++; }
public function end() { return $this->result; }
public function single ($id, $row, $doBits=Net::PHASE) { return ($doBits & Net::W1) ? $this->result = [$row] : parent::single($id, $row, $doBits); }
}# end class Rows
class RowCsv extends RowWriter {
private $outFD, $ppl;
public function config($ppl, $next) { parent::config($ppl, $next); $this->ppl = $ppl; $ppl->ppc->out ??= true; }
public function begin () {
$this->outFD = $this->ppl->ppc->outFD;
if ($this->rwEm) {
fwrite($this->outFD, "///tbh $this->id\n");
fputcsv($this->outFD, array_keys($this->rwEm));
} else {
fwrite($this->outFD, "///tb $this->id\n");
}
$this->writeC = 0;
}
public function write($row) { $this->writeC++; fputcsv($this->outFD, array_values($row));}
public function end() { fwrite($this->outFD, "///tbEnd $this->id - $this->writeC rows\n"); }
}# end class RowCsv
class RowWorker extends RowWriter {
public $writerNext;
public function __construct(...$aa) {
foreach ($aa as $k => $v)
$this->$k = $v;
}
public function config($ppl, $next) { parent::config($ppl, $next); $this->writerNext = $next; }
} # end class RowWorker
class WalkTree /* 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, $row, $rowWriter, $rowH, $rwEm, $nst, $pPath, $out, $ppl, $typeMap;
public function __construct($allB = null, $allE = null) {
$this->allB = $allB;
$this->allE = $allE;
$this->row = 0;
}
public function config($ppl, $next) {
$this->ppl = $ppl;
$this->e2m = $ppl->ppc->rwcW[0];
$this->rwEm = $ppl->ppc->rwcW['rwEm'];
$this->rowWriter = $next;
}
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); } # assign string timestamp converted to local timestamp
public function aUTi($mm, $tx) { $this->row[$mm[1]] = 0 == $tx ? '' : Date(DTLFMT, $tx); } # assign unix 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->ppc->$k ?? false)
err("aPpl $k already set: {$this->ppl->ppc->$k}, new:", $tx) ;
else
$this->ppl->ppc->$k = $tx;
return self::SIBLING;
}
} # end class WalkTree
class WalkXML extends WalkTree {
private $xr, $propC, $propS, $old;
public function __toString() {
$xr = $this->xr;
return '{' . __CLASS__ . "->xr $xr->nodeType: $xr->name}";
}
public function config($ppl, $next) {
parent::config($ppl, $next);
dbg1(" ---config 1", $this->rwEm, isset($this->rwEm['name']), isset($this->rwEm['path']), 'seq', );
$this->old = ($ppl->ppc->cloud->flags ?? false) && isset($this->rwEm['name']) && isset($this->rwEm['path']);
dbg1(" ---config 2 $this->old", $this->rwEm, isset($this->rwEm['name']), isset($this->rwEm['path']));
}
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 netEx("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())
netEx("rChk $this");
($fCnt > 30) and netEx("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 {
netEx("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();
netEx("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->rwEm;
$this->propC = $this->propS = -1;
}
public function responseE($mm) {
($this->propC === $this->propS) or netEx(__METHOD__ . "#prop $this->propC !== #status $this->propS");
if ($this->old and ! $this->row['name'] and $this->row['path'])
$this->row['name'] = basename($this->row['path']);
$this->rowWriter->write($this->row);
$this->row = 0;
unset($this->propC);
}
public function hrefE($mm, $tx) {
('' === $pa = str_starts_with($pa = rawurldecode($tx), $this->ppl->ppc->pPath) ? substr($pa, strlen($this->ppl->ppc->pPath)) : "???$pa") and $pa = Cloud::ROOT;
$this->row[$mm[1]] = $pa;
# $this->path = $pa;
}
public function propB($mm) {
($this->propC === $this->propS) or netEx(__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 netEx(__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 netEx("statusE #$c bad status $tx");
}
} # end class WalkXML
class WalkJson extends WalkTree {
function walk($js) { # walk the Tree of the json $js
$this->nst = '';
#$this->filesCnt = false;
$this->walkRec($js);
}
function walkRec($js) {
$pa = $this->nst;
if (! is_array($js)) {
netEx("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 {
if (! isSet($js['files']))
$this->row = $this->rwEm;
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);
}
}
if (! isSet($js['files'])) {
$this->row and $this->rowWriter->write($this->row);
$this->row = 0;
}
}
}
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