@voxgig/sdkgen 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/cmp/Main.js +0 -12
- package/dist/cmp/Main.js.map +1 -1
- package/package.json +1 -1
- package/project/.sdk/src/cmp/go/Config_go.ts +6 -2
- package/project/.sdk/src/cmp/js/EntityBase_js.ts +34 -0
- package/project/.sdk/src/cmp/js/Main_js.ts +10 -0
- package/project/.sdk/src/cmp/js/ReadmeExplanation_js.ts +33 -0
- package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +123 -0
- package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +152 -0
- package/project/.sdk/src/cmp/js/ReadmeTopHowto_js.ts +25 -0
- package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +65 -0
- package/project/.sdk/src/cmp/js/ReadmeTopTest_js.ts +36 -0
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +53 -5
- package/project/.sdk/src/cmp/js/TestEntity_js.ts +114 -20
- package/project/.sdk/src/cmp/js/fragment/Entity.fragment.js +7 -139
- package/project/.sdk/src/cmp/js/fragment/EntityBase.fragment.js +149 -0
- package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +6 -10
- package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +6 -10
- package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +7 -11
- package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +7 -11
- package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +7 -11
- package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +2 -0
- package/project/.sdk/src/cmp/js/fragment/SdkError.fragment.js +0 -2
- package/project/.sdk/src/cmp/lua/Config_lua.ts +6 -2
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +3 -1
- package/project/.sdk/src/cmp/php/Config_php.ts +6 -2
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +2 -2
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +10 -15
- package/project/.sdk/src/cmp/py/Config_py.ts +6 -2
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +3 -1
- package/project/.sdk/src/cmp/rb/Config_rb.ts +6 -2
- package/project/.sdk/src/cmp/ts/Main_ts.ts +7 -0
- package/project/.sdk/tm/go/feature/log_feature.go +1 -1
- package/project/.sdk/tm/go/test/runner_test.go +16 -2
- package/project/.sdk/tm/js/src/Context.js +142 -0
- package/project/.sdk/tm/js/src/Control.js +16 -0
- package/project/.sdk/tm/js/src/Operation.js +19 -0
- package/project/.sdk/tm/js/src/Point.js +24 -0
- package/project/.sdk/tm/js/src/README.md +1 -0
- package/project/.sdk/tm/js/src/Response.js +19 -0
- package/project/.sdk/tm/js/src/Result.js +21 -0
- package/project/.sdk/tm/js/src/Spec.js +26 -0
- package/project/.sdk/tm/js/src/feature/README.md +1 -0
- package/project/.sdk/tm/js/src/feature/base/BaseFeature.js +45 -0
- package/project/.sdk/tm/js/src/feature/log/LogFeature.js +46 -47
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +207 -0
- package/project/.sdk/tm/js/src/types.js +22 -0
- package/project/.sdk/tm/js/src/utility/CleanUtility.js +31 -0
- package/project/.sdk/tm/js/src/utility/DoneUtility.js +11 -4
- package/project/.sdk/tm/js/src/utility/FeatureAddUtility.js +42 -0
- package/project/.sdk/tm/js/src/utility/FeatureHookUtility.js +25 -0
- package/project/.sdk/tm/js/src/utility/FeatureInitUtility.js +11 -0
- package/project/.sdk/tm/js/src/utility/FetcherUtility.js +28 -0
- package/project/.sdk/tm/js/src/utility/MakeContextUtility.js +11 -0
- package/project/.sdk/tm/js/src/utility/MakeErrorUtility.js +55 -0
- package/project/.sdk/tm/js/src/utility/MakeFetchDefUtility.js +44 -0
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +93 -0
- package/project/.sdk/tm/js/src/utility/MakePointUtility.js +77 -0
- package/project/.sdk/tm/js/src/utility/MakeRequestUtility.js +63 -0
- package/project/.sdk/tm/js/src/utility/MakeResponseUtility.js +55 -0
- package/project/.sdk/tm/js/src/utility/MakeResultUtility.js +54 -0
- package/project/.sdk/tm/js/src/utility/MakeSpecUtility.js +58 -0
- package/project/.sdk/tm/js/src/utility/MakeUrlUtility.js +40 -0
- package/project/.sdk/tm/js/src/utility/ParamUtility.js +61 -0
- package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +41 -0
- package/project/.sdk/tm/js/src/utility/PrepareBodyUtility.js +25 -0
- package/project/.sdk/tm/js/src/utility/PrepareHeadersUtility.js +18 -0
- package/project/.sdk/tm/js/src/utility/{MethodUtility.js → PrepareMethodUtility.js} +7 -7
- package/project/.sdk/tm/js/src/utility/PrepareParamsUtility.js +25 -0
- package/project/.sdk/tm/js/src/utility/PreparePathUtility.js +13 -0
- package/project/.sdk/tm/js/src/utility/PrepareQueryUtility.js +26 -0
- package/project/.sdk/tm/js/src/utility/README.md +1 -0
- package/project/.sdk/tm/js/src/utility/ResultBasicUtility.js +34 -0
- package/project/.sdk/tm/js/src/utility/ResultBodyUtility.js +18 -0
- package/project/.sdk/tm/js/src/utility/ResultHeadersUtility.js +22 -0
- package/project/.sdk/tm/js/src/utility/StructUtility.js +2219 -1078
- package/project/.sdk/tm/js/src/utility/TransformRequestUtility.js +28 -0
- package/project/.sdk/tm/js/src/utility/TransformResponseUtility.js +31 -0
- package/project/.sdk/tm/js/src/utility/Utility.js +61 -61
- package/project/.sdk/tm/js/test/README.md +1 -0
- package/project/.sdk/tm/js/test/exists.test.js +16 -0
- package/project/.sdk/tm/js/test/runner.js +323 -107
- package/project/.sdk/tm/js/test/utility/Custom.test.js +41 -63
- package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +390 -116
- package/project/.sdk/tm/js/test/utility/StructUtility.test.js +728 -175
- package/project/.sdk/tm/js/test/utility/index.js +9 -0
- package/project/.sdk/tm/js/test/utility.js +72 -0
- package/project/.sdk/tm/lua/test/primary_utility_test.lua +1213 -0
- package/project/.sdk/tm/lua/test/runner.lua +2 -2
- package/project/.sdk/tm/lua/test/struct_runner.lua +602 -0
- package/project/.sdk/tm/lua/test/struct_utility_test.lua +959 -0
- package/project/.sdk/tm/lua/utility/struct/struct.lua +10 -0
- package/project/.sdk/tm/php/feature/TestFeature.php +59 -96
- package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +1309 -0
- package/project/.sdk/tm/php/test/Runner.php +24 -1
- package/project/.sdk/tm/php/test/StructRunner.php +275 -0
- package/project/.sdk/tm/php/test/StructUtilityTest.php +1336 -0
- package/project/.sdk/tm/php/utility/Fetcher.php +6 -2
- package/project/.sdk/tm/php/utility/MakeOptions.php +5 -1
- package/project/.sdk/tm/php/utility/MakeResult.php +3 -0
- package/project/.sdk/tm/php/utility/Param.php +9 -7
- package/project/.sdk/tm/php/utility/struct/Struct.php +312 -208
- package/project/.sdk/tm/py/test/runner.py +13 -0
- package/project/.sdk/tm/py/test/struct_runner.py +411 -0
- package/project/.sdk/tm/py/test/test_primary_utility.py +1101 -0
- package/project/.sdk/tm/py/test/test_struct_utility.py +751 -0
- package/project/.sdk/tm/rb/test/primary_utility_test.rb +1083 -0
- package/project/.sdk/tm/rb/test/runner.rb +5 -0
- package/project/.sdk/tm/rb/test/struct_runner.rb +309 -0
- package/project/.sdk/tm/rb/test/struct_utility_test.rb +670 -0
- package/src/cmp/Main.ts +1 -16
- package/project/.sdk/src/cmp/js/Quick_js.ts +0 -78
- package/project/.sdk/src/cmp/js/TestAcceptEntity_js.ts +0 -13
- package/project/.sdk/src/cmp/js/TestAccept_js.ts +0 -18
- package/project/.sdk/tm/js/src/utility/AuthUtility.js +0 -21
- package/project/.sdk/tm/js/src/utility/BodyUtility.js +0 -29
- package/project/.sdk/tm/js/src/utility/ErrorUtility.js +0 -33
- package/project/.sdk/tm/js/src/utility/FindparamUtility.js +0 -31
- package/project/.sdk/tm/js/src/utility/FullurlUtility.js +0 -39
- package/project/.sdk/tm/js/src/utility/HeadersUtility.js +0 -13
- package/project/.sdk/tm/js/src/utility/JoinurlUtility.js +0 -14
- package/project/.sdk/tm/js/src/utility/OperatorUtility.js +0 -44
- package/project/.sdk/tm/js/src/utility/OptionsUtility.js +0 -54
- package/project/.sdk/tm/js/src/utility/ParamsUtility.js +0 -21
- package/project/.sdk/tm/js/src/utility/QueryUtility.js +0 -21
- package/project/.sdk/tm/js/src/utility/ReqformUtility.js +0 -32
- package/project/.sdk/tm/js/src/utility/RequestUtility.js +0 -48
- package/project/.sdk/tm/js/src/utility/ResbasicUtility.js +0 -27
- package/project/.sdk/tm/js/src/utility/ResbodyUtility.js +0 -15
- package/project/.sdk/tm/js/src/utility/ResformUtility.js +0 -34
- package/project/.sdk/tm/js/src/utility/ResheadersUtility.js +0 -19
- package/project/.sdk/tm/js/src/utility/ResponseUtility.js +0 -37
- package/project/.sdk/tm/js/src/utility/ResultUtility.js +0 -28
- package/project/.sdk/tm/js/src/utility/SpecUtility.js +0 -35
|
@@ -100,12 +100,23 @@ class Struct
|
|
|
100
100
|
public const S_BASE = 'base';
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* NOTE: This marker should be chosen to minimize collision with real data.
|
|
103
|
+
* Legacy string marker for undefined. Kept for backward compatibility.
|
|
104
|
+
* Internal code uses the $UNDEF sentinel object via undef().
|
|
106
105
|
*/
|
|
107
106
|
public const UNDEF = '__UNDEFINED__';
|
|
108
107
|
|
|
108
|
+
/** Sentinel object for undefined — can never collide with real data. */
|
|
109
|
+
private static ?\stdClass $UNDEF = null;
|
|
110
|
+
|
|
111
|
+
/** Return the sentinel object for undefined. */
|
|
112
|
+
public static function undef(): \stdClass
|
|
113
|
+
{
|
|
114
|
+
if (self::$UNDEF === null) {
|
|
115
|
+
self::$UNDEF = new \stdClass();
|
|
116
|
+
}
|
|
117
|
+
return self::$UNDEF;
|
|
118
|
+
}
|
|
119
|
+
|
|
109
120
|
public const T_any = (1 << 31) - 1;
|
|
110
121
|
public const T_noval = 1 << 30;
|
|
111
122
|
public const T_boolean = 1 << 29;
|
|
@@ -184,7 +195,7 @@ class Struct
|
|
|
184
195
|
|
|
185
196
|
public static function isnode(mixed $val): bool
|
|
186
197
|
{
|
|
187
|
-
if ($val === self::
|
|
198
|
+
if ($val === self::undef() || $val === null) {
|
|
188
199
|
return false;
|
|
189
200
|
}
|
|
190
201
|
if ($val instanceof \Closure) {
|
|
@@ -212,6 +223,9 @@ class Struct
|
|
|
212
223
|
if ($val instanceof \Closure) {
|
|
213
224
|
return false;
|
|
214
225
|
}
|
|
226
|
+
if ($val === self::undef()) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
215
229
|
if (is_object($val)) {
|
|
216
230
|
return true;
|
|
217
231
|
}
|
|
@@ -259,7 +273,7 @@ class Struct
|
|
|
259
273
|
*/
|
|
260
274
|
public static function iskey(mixed $key): bool
|
|
261
275
|
{
|
|
262
|
-
if ($key === self::
|
|
276
|
+
if ($key === self::undef()) { // Explicit check for UNDEF
|
|
263
277
|
return false;
|
|
264
278
|
}
|
|
265
279
|
if (is_string($key)) {
|
|
@@ -276,7 +290,7 @@ class Struct
|
|
|
276
290
|
*/
|
|
277
291
|
public static function isempty(mixed $val): bool
|
|
278
292
|
{
|
|
279
|
-
if ($val === self::
|
|
293
|
+
if ($val === self::undef() || $val === null || $val === self::S_MT) {
|
|
280
294
|
return true;
|
|
281
295
|
}
|
|
282
296
|
if (is_array($val) && count($val) === 0) {
|
|
@@ -301,7 +315,7 @@ class Struct
|
|
|
301
315
|
|
|
302
316
|
public static function typify(mixed $value): int
|
|
303
317
|
{
|
|
304
|
-
if ($value === self::
|
|
318
|
+
if ($value === self::undef()) {
|
|
305
319
|
return self::T_noval;
|
|
306
320
|
}
|
|
307
321
|
if ($value === null) {
|
|
@@ -355,7 +369,7 @@ class Struct
|
|
|
355
369
|
*/
|
|
356
370
|
public static function getdef(mixed $val, mixed $alt): mixed
|
|
357
371
|
{
|
|
358
|
-
if ($val === self::
|
|
372
|
+
if ($val === self::undef() || $val === null) {
|
|
359
373
|
return $alt;
|
|
360
374
|
}
|
|
361
375
|
return $val;
|
|
@@ -402,10 +416,25 @@ class Struct
|
|
|
402
416
|
return array_values($v);
|
|
403
417
|
}
|
|
404
418
|
|
|
405
|
-
public static function getprop(mixed $val, mixed $key, mixed $alt =
|
|
419
|
+
public static function getprop(mixed $val, mixed $key, mixed $alt = null): mixed
|
|
406
420
|
{
|
|
421
|
+
$altExplicit = func_num_args() >= 3;
|
|
422
|
+
$out = self::_getprop($val, $key, self::undef());
|
|
423
|
+
if ($out === self::undef()) {
|
|
424
|
+
return $altExplicit ? $alt : null;
|
|
425
|
+
}
|
|
426
|
+
return $out;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
// Internal getprop returning the UNDEF sentinel for missing keys so the
|
|
431
|
+
// injection/transform machinery can distinguish "missing" from a stored null.
|
|
432
|
+
// External callers should use getprop(), which normalises UNDEF to null (or $alt).
|
|
433
|
+
public static function _getprop(mixed $val, mixed $key, mixed $alt = null): mixed
|
|
434
|
+
{
|
|
435
|
+
if ($alt === null) { $alt = self::undef(); }
|
|
407
436
|
// 1) undefined‐marker or invalid key → alt
|
|
408
|
-
if ($val === self::
|
|
437
|
+
if ($val === self::undef() || $key === self::undef()) {
|
|
409
438
|
return $alt;
|
|
410
439
|
}
|
|
411
440
|
if (!self::iskey($key)) {
|
|
@@ -439,13 +468,13 @@ class Struct
|
|
|
439
468
|
}
|
|
440
469
|
|
|
441
470
|
// 5) JSON‐null‐marker check
|
|
442
|
-
return ($out === self::
|
|
471
|
+
return ($out === self::undef() ? $alt : $out);
|
|
443
472
|
}
|
|
444
473
|
|
|
445
474
|
|
|
446
|
-
public static function strkey(mixed $key =
|
|
475
|
+
public static function strkey(mixed $key = null): string
|
|
447
476
|
{
|
|
448
|
-
if ($key === self::
|
|
477
|
+
if ($key === null || $key === self::undef()) {
|
|
449
478
|
return self::S_MT;
|
|
450
479
|
}
|
|
451
480
|
if (is_string($key)) {
|
|
@@ -493,7 +522,7 @@ class Struct
|
|
|
493
522
|
* @param mixed $key
|
|
494
523
|
* @return bool
|
|
495
524
|
*/
|
|
496
|
-
public static function haskey(mixed $val =
|
|
525
|
+
public static function haskey(mixed $val = null, mixed $key = null): bool
|
|
497
526
|
{
|
|
498
527
|
// 1. Validate $val is a node
|
|
499
528
|
if (!self::isnode($val)) {
|
|
@@ -507,7 +536,7 @@ class Struct
|
|
|
507
536
|
|
|
508
537
|
// 3. Check property existence
|
|
509
538
|
$marker = new \stdClass();
|
|
510
|
-
return self::
|
|
539
|
+
return self::_getprop($val, $key, $marker) !== $marker;
|
|
511
540
|
}
|
|
512
541
|
|
|
513
542
|
public static function items(mixed $val, ?callable $apply = null): array
|
|
@@ -519,7 +548,7 @@ class Struct
|
|
|
519
548
|
}
|
|
520
549
|
} else {
|
|
521
550
|
foreach (self::keysof($val) as $k) {
|
|
522
|
-
$result[] = [$k, self::
|
|
551
|
+
$result[] = [$k, self::_getprop($val, $k)];
|
|
523
552
|
}
|
|
524
553
|
}
|
|
525
554
|
if ($apply !== null) {
|
|
@@ -605,11 +634,11 @@ class Struct
|
|
|
605
634
|
{
|
|
606
635
|
$str = 'null';
|
|
607
636
|
|
|
608
|
-
if ($val !== null && $val !== self::
|
|
637
|
+
if ($val !== null && $val !== self::undef() && !($val instanceof \Closure)) {
|
|
609
638
|
if ($val instanceof ListRef) {
|
|
610
639
|
$val = self::cloneUnwrap($val);
|
|
611
640
|
}
|
|
612
|
-
$indent = self::
|
|
641
|
+
$indent = self::_getprop($flags, 'indent', 2);
|
|
613
642
|
try {
|
|
614
643
|
$encoded = json_encode($val, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
|
615
644
|
if ($encoded === false) {
|
|
@@ -624,7 +653,7 @@ class Struct
|
|
|
624
653
|
|
|
625
654
|
$str = $encoded;
|
|
626
655
|
|
|
627
|
-
$offset = self::
|
|
656
|
+
$offset = self::_getprop($flags, 'offset', 0);
|
|
628
657
|
if (0 < $offset) {
|
|
629
658
|
$lines = explode("\n", $str);
|
|
630
659
|
$rest = array_slice($lines, 1);
|
|
@@ -650,7 +679,7 @@ class Struct
|
|
|
650
679
|
*/
|
|
651
680
|
public static function size(mixed $val): int
|
|
652
681
|
{
|
|
653
|
-
if ($val === null || $val === self::
|
|
682
|
+
if ($val === null || $val === self::undef()) {
|
|
654
683
|
return 0;
|
|
655
684
|
}
|
|
656
685
|
|
|
@@ -791,7 +820,7 @@ class Struct
|
|
|
791
820
|
|
|
792
821
|
public static function stringify(mixed $val, ?int $maxlen = null, mixed $pretty = null): string
|
|
793
822
|
{
|
|
794
|
-
if ($val === self::
|
|
823
|
+
if ($val === self::undef()) {
|
|
795
824
|
return $pretty ? '<>' : self::S_MT;
|
|
796
825
|
}
|
|
797
826
|
|
|
@@ -833,7 +862,7 @@ class Struct
|
|
|
833
862
|
|
|
834
863
|
public static function pathify(mixed $val, ?int $startin = null, ?int $endin = null): string
|
|
835
864
|
{
|
|
836
|
-
$UNDEF = self::
|
|
865
|
+
$UNDEF = self::undef();
|
|
837
866
|
$S_MT = self::S_MT;
|
|
838
867
|
$S_CN = self::S_CN;
|
|
839
868
|
$S_DT = self::S_DT;
|
|
@@ -912,15 +941,12 @@ class Struct
|
|
|
912
941
|
|
|
913
942
|
public static function clone(mixed $val): mixed
|
|
914
943
|
{
|
|
915
|
-
if ($val === self::
|
|
916
|
-
return self::
|
|
944
|
+
if ($val === self::undef()) {
|
|
945
|
+
return self::undef();
|
|
917
946
|
}
|
|
918
947
|
$refs = [];
|
|
919
948
|
$replacer = function (mixed $v) use (&$refs, &$replacer): mixed {
|
|
920
|
-
if ($v instanceof \Closure) {
|
|
921
|
-
$refs[] = $v;
|
|
922
|
-
return '`$FUNCTION:' . (count($refs) - 1) . '`';
|
|
923
|
-
} elseif (is_callable($v) && !is_array($v) && !($v instanceof ListRef)) {
|
|
949
|
+
if ($v instanceof \Closure || (is_object($v) && !($v instanceof \stdClass) && !($v instanceof ListRef) && method_exists($v, '__invoke'))) {
|
|
924
950
|
$refs[] = $v;
|
|
925
951
|
return '`$FUNCTION:' . (count($refs) - 1) . '`';
|
|
926
952
|
} elseif ($v instanceof ListRef) {
|
|
@@ -950,7 +976,10 @@ class Struct
|
|
|
950
976
|
$reviver = function (mixed $v) use (&$refs, &$reviver): mixed {
|
|
951
977
|
if (is_string($v)) {
|
|
952
978
|
if (preg_match('/^`\$FUNCTION:([0-9]+)`$/', $v, $matches)) {
|
|
953
|
-
|
|
979
|
+
$idx = (int) $matches[1];
|
|
980
|
+
if (isset($refs[$idx])) {
|
|
981
|
+
return $refs[$idx];
|
|
982
|
+
}
|
|
954
983
|
}
|
|
955
984
|
return $v;
|
|
956
985
|
} elseif ($v instanceof ListRef) {
|
|
@@ -985,10 +1014,10 @@ class Struct
|
|
|
985
1014
|
*/
|
|
986
1015
|
public static function cloneWrap(mixed $val): mixed
|
|
987
1016
|
{
|
|
988
|
-
if ($val === null || $val === self::
|
|
1017
|
+
if ($val === null || $val === self::undef()) {
|
|
989
1018
|
return $val;
|
|
990
1019
|
}
|
|
991
|
-
if (
|
|
1020
|
+
if ($val instanceof \Closure) {
|
|
992
1021
|
return $val;
|
|
993
1022
|
}
|
|
994
1023
|
if ($val instanceof ListRef) {
|
|
@@ -1076,7 +1105,7 @@ class Struct
|
|
|
1076
1105
|
return $parent;
|
|
1077
1106
|
}
|
|
1078
1107
|
$keyI = (int) floor((float) $key);
|
|
1079
|
-
if ($val === self::
|
|
1108
|
+
if ($val === self::undef()) {
|
|
1080
1109
|
if ($keyI >= 0 && $keyI < count($parent->list)) {
|
|
1081
1110
|
array_splice($parent->list, $keyI, 1);
|
|
1082
1111
|
}
|
|
@@ -1095,7 +1124,7 @@ class Struct
|
|
|
1095
1124
|
// ─── OBJECT (map) ───────────────────────────────────────────
|
|
1096
1125
|
if (is_object($parent)) {
|
|
1097
1126
|
$keyStr = self::strkey($key);
|
|
1098
|
-
if ($val === self::
|
|
1127
|
+
if ($val === self::undef()) {
|
|
1099
1128
|
unset($parent->$keyStr);
|
|
1100
1129
|
} else {
|
|
1101
1130
|
$parent->$keyStr = $val;
|
|
@@ -1108,7 +1137,7 @@ class Struct
|
|
|
1108
1137
|
if (!self::islist($parent)) {
|
|
1109
1138
|
// map‐array
|
|
1110
1139
|
$keyStr = self::strkey($key);
|
|
1111
|
-
if ($val === self::
|
|
1140
|
+
if ($val === self::undef()) {
|
|
1112
1141
|
unset($parent[$keyStr]);
|
|
1113
1142
|
} elseif (ctype_digit((string) $key)) {
|
|
1114
1143
|
// numeric string key: unshift (TS always merges maps by overwriting)
|
|
@@ -1122,7 +1151,7 @@ class Struct
|
|
|
1122
1151
|
return $parent;
|
|
1123
1152
|
}
|
|
1124
1153
|
$keyI = (int) floor((float) $key);
|
|
1125
|
-
if ($val === self::
|
|
1154
|
+
if ($val === self::undef()) {
|
|
1126
1155
|
if ($keyI >= 0 && $keyI < count($parent)) {
|
|
1127
1156
|
array_splice($parent, $keyI, 1);
|
|
1128
1157
|
}
|
|
@@ -1199,12 +1228,12 @@ class Struct
|
|
|
1199
1228
|
$lenlist = count($list);
|
|
1200
1229
|
|
|
1201
1230
|
if (0 === $lenlist) {
|
|
1202
|
-
return self::
|
|
1231
|
+
return self::undef();
|
|
1203
1232
|
} elseif (1 === $lenlist) {
|
|
1204
1233
|
return $list[0];
|
|
1205
1234
|
}
|
|
1206
1235
|
|
|
1207
|
-
$out = self::
|
|
1236
|
+
$out = self::_getprop($list, 0, new \stdClass());
|
|
1208
1237
|
|
|
1209
1238
|
for ($oI = 1; $oI < $lenlist; $oI++) {
|
|
1210
1239
|
$obj = $list[$oI];
|
|
@@ -1223,16 +1252,16 @@ class Struct
|
|
|
1223
1252
|
} elseif (!self::isnode($val)) {
|
|
1224
1253
|
$cur[$pI] = $val;
|
|
1225
1254
|
} else {
|
|
1226
|
-
$dst[$pI] = 0 < $pI ? self::
|
|
1255
|
+
$dst[$pI] = 0 < $pI ? self::_getprop($dst[$pI - 1], $key) : $dst[$pI];
|
|
1227
1256
|
$tval = $dst[$pI];
|
|
1228
1257
|
|
|
1229
|
-
if (self::
|
|
1258
|
+
if (self::undef() === $tval && 0 === (self::T_instance & self::typify($val))) {
|
|
1230
1259
|
$cur[$pI] = self::islist($val) ? [] : new \stdClass();
|
|
1231
1260
|
} elseif (self::typify($val) === self::typify($tval)) {
|
|
1232
1261
|
$cur[$pI] = $tval;
|
|
1233
1262
|
} else {
|
|
1234
1263
|
$cur[$pI] = $val;
|
|
1235
|
-
$val = self::
|
|
1264
|
+
$val = self::undef();
|
|
1236
1265
|
}
|
|
1237
1266
|
}
|
|
1238
1267
|
|
|
@@ -1268,17 +1297,17 @@ class Struct
|
|
|
1268
1297
|
// Convert path to array of parts
|
|
1269
1298
|
$parts = is_array($path) ? $path :
|
|
1270
1299
|
(is_string($path) ? explode('.', $path) :
|
|
1271
|
-
(is_numeric($path) ? [self::strkey($path)] : self::
|
|
1300
|
+
(is_numeric($path) ? [self::strkey($path)] : self::undef()));
|
|
1272
1301
|
|
|
1273
|
-
if ($parts === self::
|
|
1274
|
-
return self::
|
|
1302
|
+
if ($parts === self::undef()) {
|
|
1303
|
+
return self::undef();
|
|
1275
1304
|
}
|
|
1276
1305
|
|
|
1277
1306
|
$val = $store;
|
|
1278
|
-
$base = self::
|
|
1279
|
-
$src = self::
|
|
1307
|
+
$base = self::_getprop($injdef, 'base');
|
|
1308
|
+
$src = self::_getprop($store, $base, $store);
|
|
1280
1309
|
$numparts = count($parts);
|
|
1281
|
-
$dparent = self::
|
|
1310
|
+
$dparent = self::_getprop($injdef, 'dparent');
|
|
1282
1311
|
|
|
1283
1312
|
// An empty path (incl empty string) just finds the src (base data)
|
|
1284
1313
|
if ($path === null || $store === null || ($numparts === 1 && $parts[0] === '')) {
|
|
@@ -1286,7 +1315,7 @@ class Struct
|
|
|
1286
1315
|
} else if ($numparts > 0) {
|
|
1287
1316
|
// Check for $ACTIONs
|
|
1288
1317
|
if ($numparts === 1) {
|
|
1289
|
-
$val = self::
|
|
1318
|
+
$val = self::_getprop($store, $parts[0]);
|
|
1290
1319
|
}
|
|
1291
1320
|
|
|
1292
1321
|
if (!self::isfunc($val)) {
|
|
@@ -1294,17 +1323,17 @@ class Struct
|
|
|
1294
1323
|
|
|
1295
1324
|
// Check for meta path in first part
|
|
1296
1325
|
if (preg_match('/^([^$]+)\$([=~])(.+)$/', $parts[0], $m) && $injdef && isset($injdef->meta)) {
|
|
1297
|
-
$val = self::
|
|
1326
|
+
$val = self::_getprop($injdef->meta, $m[1]);
|
|
1298
1327
|
$parts[0] = $m[3];
|
|
1299
1328
|
}
|
|
1300
1329
|
|
|
1301
|
-
$dpath = self::
|
|
1330
|
+
$dpath = self::_getprop($injdef, 'dpath');
|
|
1302
1331
|
|
|
1303
|
-
for ($pI = 0; $val !== self::
|
|
1332
|
+
for ($pI = 0; $val !== self::undef() && $pI < count($parts); $pI++) {
|
|
1304
1333
|
$part = $parts[$pI];
|
|
1305
1334
|
|
|
1306
1335
|
if ($injdef && $part === '$KEY') {
|
|
1307
|
-
$part = self::
|
|
1336
|
+
$part = self::_getprop($injdef, 'key');
|
|
1308
1337
|
} else if ($injdef && str_starts_with($part, '$GET:')) {
|
|
1309
1338
|
// $GET:path$ -> get store value, use as path part (string)
|
|
1310
1339
|
$getpath = substr($part, 5, -1);
|
|
@@ -1313,10 +1342,10 @@ class Struct
|
|
|
1313
1342
|
} else if ($injdef && str_starts_with($part, '$REF:')) {
|
|
1314
1343
|
// $REF:refpath$ -> get spec value, use as path part (string)
|
|
1315
1344
|
$refpath = substr($part, 5, -1);
|
|
1316
|
-
$part = self::stringify(self::getpath(self::
|
|
1345
|
+
$part = self::stringify(self::getpath(self::_getprop($store, '$SPEC'), self::slice($part, 5, -1)));
|
|
1317
1346
|
} else if ($injdef && str_starts_with($part, '$META:')) {
|
|
1318
1347
|
// $META:metapath$ -> get meta value, use as path part (string)
|
|
1319
|
-
$part = self::stringify(self::getpath(self::
|
|
1348
|
+
$part = self::stringify(self::getpath(self::_getprop($injdef, 'meta'), substr($part, 6, -1)));
|
|
1320
1349
|
}
|
|
1321
1350
|
|
|
1322
1351
|
// $$ escapes $
|
|
@@ -1342,27 +1371,27 @@ class Struct
|
|
|
1342
1371
|
if (is_array($dpath) && $ascends <= count($dpath)) {
|
|
1343
1372
|
$val = self::getpath($store, $fullpath);
|
|
1344
1373
|
} else {
|
|
1345
|
-
$val = self::
|
|
1374
|
+
$val = self::undef();
|
|
1346
1375
|
}
|
|
1347
1376
|
break;
|
|
1348
1377
|
}
|
|
1349
1378
|
} else {
|
|
1350
1379
|
// Special case for single dot: use dparent if available
|
|
1351
|
-
if ($dparent !== null && $dparent !== self::
|
|
1380
|
+
if ($dparent !== null && $dparent !== self::undef()) {
|
|
1352
1381
|
$val = $dparent;
|
|
1353
1382
|
} else {
|
|
1354
1383
|
$val = $src;
|
|
1355
1384
|
}
|
|
1356
1385
|
}
|
|
1357
1386
|
} else {
|
|
1358
|
-
$val = self::
|
|
1387
|
+
$val = self::_getprop($val, $part);
|
|
1359
1388
|
}
|
|
1360
1389
|
}
|
|
1361
1390
|
}
|
|
1362
1391
|
}
|
|
1363
1392
|
|
|
1364
1393
|
// Inj may provide a custom handler to modify found value
|
|
1365
|
-
$handler = self::
|
|
1394
|
+
$handler = self::_getprop($injdef, 'handler');
|
|
1366
1395
|
if ($injdef !== null && self::isfunc($handler)) {
|
|
1367
1396
|
$ref = self::pathify($path);
|
|
1368
1397
|
$val = call_user_func($handler, $injdef, $val, $ref, $store);
|
|
@@ -1384,15 +1413,15 @@ class Struct
|
|
|
1384
1413
|
|
|
1385
1414
|
// Create state if at root of injection. The input value is placed
|
|
1386
1415
|
// inside a virtual parent holder to simplify edge cases.
|
|
1387
|
-
if (self::
|
|
1416
|
+
if (self::undef() === $injdef || null === $injdef || !($injdef instanceof Injection)) {
|
|
1388
1417
|
$inj = new Injection($val, (object) [self::S_DTOP => $val]);
|
|
1389
1418
|
$inj->dparent = $store;
|
|
1390
|
-
$inj->errs = self::
|
|
1419
|
+
$inj->errs = self::_getprop($store, self::S_DERRS, []);
|
|
1391
1420
|
if (!isset($inj->meta->__d)) {
|
|
1392
1421
|
$inj->meta->__d = 0;
|
|
1393
1422
|
}
|
|
1394
1423
|
|
|
1395
|
-
if (self::
|
|
1424
|
+
if (self::undef() !== $injdef && null !== $injdef) {
|
|
1396
1425
|
$inj->modify = (is_object($injdef) && property_exists($injdef, 'modify') && null !== $injdef->modify) ? $injdef->modify : $inj->modify;
|
|
1397
1426
|
$inj->extra = (is_object($injdef) && property_exists($injdef, 'extra') && null !== $injdef->extra) ? $injdef->extra : ($inj->extra ?? null);
|
|
1398
1427
|
$inj->meta = (is_object($injdef) && property_exists($injdef, 'meta') && null !== $injdef->meta) ? $injdef->meta : $inj->meta;
|
|
@@ -1434,8 +1463,8 @@ class Struct
|
|
|
1434
1463
|
$nodekeys = $childinj->keys;
|
|
1435
1464
|
|
|
1436
1465
|
// Prevent further processing by returning an undefined prekey
|
|
1437
|
-
if (self::
|
|
1438
|
-
$childinj->val = self::
|
|
1466
|
+
if (self::undef() !== $prekey) {
|
|
1467
|
+
$childinj->val = self::_getprop($val, $prekey);
|
|
1439
1468
|
$childinj->mode = self::M_VAL;
|
|
1440
1469
|
|
|
1441
1470
|
// Perform the val mode injection on the child value.
|
|
@@ -1460,7 +1489,7 @@ class Struct
|
|
|
1460
1489
|
if (is_array($val) && is_array($childinj->parent)) {
|
|
1461
1490
|
// Check that the grandparent (inj->parent) still references our list.
|
|
1462
1491
|
// If a transform like $REF replaced/deleted it, the stored value will differ.
|
|
1463
|
-
$storedVal = self::
|
|
1492
|
+
$storedVal = self::_getprop($inj->parent, $inj->key);
|
|
1464
1493
|
if (is_array($storedVal)) {
|
|
1465
1494
|
$val = $childinj->parent;
|
|
1466
1495
|
$inj->val = $val;
|
|
@@ -1482,7 +1511,7 @@ class Struct
|
|
|
1482
1511
|
if ($inj->modify && self::SKIP !== $val) {
|
|
1483
1512
|
$mkey = $inj->key;
|
|
1484
1513
|
$mparent = $inj->parent;
|
|
1485
|
-
$mval = self::
|
|
1514
|
+
$mval = self::_getprop($mparent, $mkey);
|
|
1486
1515
|
|
|
1487
1516
|
call_user_func(
|
|
1488
1517
|
$inj->modify,
|
|
@@ -1498,7 +1527,7 @@ class Struct
|
|
|
1498
1527
|
|
|
1499
1528
|
// Original val reference may no longer be correct.
|
|
1500
1529
|
// This return value is only used as the top level result.
|
|
1501
|
-
return self::
|
|
1530
|
+
return self::_getprop($inj->parent, self::S_DTOP);
|
|
1502
1531
|
}
|
|
1503
1532
|
|
|
1504
1533
|
|
|
@@ -1551,7 +1580,7 @@ class Struct
|
|
|
1551
1580
|
$found = self::getpath($store, $ref, $inj);
|
|
1552
1581
|
|
|
1553
1582
|
// Ensure inject value is a string.
|
|
1554
|
-
if ($found === self::
|
|
1583
|
+
if ($found === self::undef()) {
|
|
1555
1584
|
return self::S_MT;
|
|
1556
1585
|
}
|
|
1557
1586
|
if (is_string($found)) {
|
|
@@ -1581,7 +1610,7 @@ class Struct
|
|
|
1581
1610
|
$out = $val;
|
|
1582
1611
|
|
|
1583
1612
|
// Check if val is a function (command transforms)
|
|
1584
|
-
$iscmd = self::isfunc($val) && (self::
|
|
1613
|
+
$iscmd = self::isfunc($val) && (self::undef() === $ref || str_starts_with($ref, self::S_DS));
|
|
1585
1614
|
|
|
1586
1615
|
// Only call val function if it is a special command ($NAME format).
|
|
1587
1616
|
if ($iscmd) {
|
|
@@ -1614,8 +1643,8 @@ class Struct
|
|
|
1614
1643
|
mixed $store
|
|
1615
1644
|
): mixed {
|
|
1616
1645
|
// _setparentprop(state, UNDEF)
|
|
1617
|
-
$state->setval(self::
|
|
1618
|
-
return self::
|
|
1646
|
+
$state->setval(self::undef());
|
|
1647
|
+
return self::undef();
|
|
1619
1648
|
}
|
|
1620
1649
|
|
|
1621
1650
|
/**
|
|
@@ -1629,10 +1658,10 @@ class Struct
|
|
|
1629
1658
|
mixed $store
|
|
1630
1659
|
): mixed {
|
|
1631
1660
|
if (self::M_VAL !== $state->mode) {
|
|
1632
|
-
return self::
|
|
1661
|
+
return self::undef();
|
|
1633
1662
|
}
|
|
1634
1663
|
|
|
1635
|
-
$out = self::
|
|
1664
|
+
$out = self::_getprop($state->dparent, $state->key);
|
|
1636
1665
|
$state->setval($out);
|
|
1637
1666
|
|
|
1638
1667
|
return $out;
|
|
@@ -1651,24 +1680,24 @@ class Struct
|
|
|
1651
1680
|
): mixed {
|
|
1652
1681
|
// only in "val" mode do anything
|
|
1653
1682
|
if (self::M_VAL !== $state->mode) {
|
|
1654
|
-
return self::
|
|
1683
|
+
return self::undef();
|
|
1655
1684
|
}
|
|
1656
1685
|
|
|
1657
1686
|
// if parent has a "$KEY" override, use that
|
|
1658
|
-
$keyspec = self::
|
|
1659
|
-
if ($keyspec !== self::
|
|
1687
|
+
$keyspec = self::_getprop($state->parent, self::S_DKEY);
|
|
1688
|
+
if ($keyspec !== self::undef()) {
|
|
1660
1689
|
// remove the marker
|
|
1661
|
-
self::setprop($state->parent, self::S_DKEY, self::
|
|
1662
|
-
return self::
|
|
1690
|
+
self::setprop($state->parent, self::S_DKEY, self::undef());
|
|
1691
|
+
return self::_getprop($state->dparent, $keyspec);
|
|
1663
1692
|
}
|
|
1664
1693
|
|
|
1665
1694
|
// otherwise pull from $ANNO.KEY or fallback to the path index
|
|
1666
|
-
$meta = self::
|
|
1695
|
+
$meta = self::_getprop($state->parent, self::S_BANNO);
|
|
1667
1696
|
$idx = count($state->path) - 2;
|
|
1668
|
-
return self::
|
|
1697
|
+
return self::_getprop(
|
|
1669
1698
|
$meta,
|
|
1670
1699
|
self::S_KEY,
|
|
1671
|
-
self::
|
|
1700
|
+
self::_getprop($state->path, $idx)
|
|
1672
1701
|
);
|
|
1673
1702
|
}
|
|
1674
1703
|
|
|
@@ -1683,8 +1712,8 @@ class Struct
|
|
|
1683
1712
|
mixed $store
|
|
1684
1713
|
): mixed {
|
|
1685
1714
|
// remove the $META marker
|
|
1686
|
-
self::setprop($state->parent, self::S_DMETA, self::
|
|
1687
|
-
return self::
|
|
1715
|
+
self::setprop($state->parent, self::S_DMETA, self::undef());
|
|
1716
|
+
return self::undef();
|
|
1688
1717
|
}
|
|
1689
1718
|
|
|
1690
1719
|
/**
|
|
@@ -1698,8 +1727,8 @@ class Struct
|
|
|
1698
1727
|
mixed $store
|
|
1699
1728
|
): mixed {
|
|
1700
1729
|
// remove the $ANNO marker
|
|
1701
|
-
self::setprop($state->parent, self::S_BANNO, self::
|
|
1702
|
-
return self::
|
|
1730
|
+
self::setprop($state->parent, self::S_BANNO, self::undef());
|
|
1731
|
+
return self::undef();
|
|
1703
1732
|
}
|
|
1704
1733
|
|
|
1705
1734
|
/**
|
|
@@ -1717,7 +1746,7 @@ class Struct
|
|
|
1717
1746
|
$parent = $state->parent;
|
|
1718
1747
|
|
|
1719
1748
|
// Ensures $MERGE is removed from parent list (val mode).
|
|
1720
|
-
$out = self::
|
|
1749
|
+
$out = self::undef();
|
|
1721
1750
|
|
|
1722
1751
|
if (self::M_KEYPRE === $mode) {
|
|
1723
1752
|
$out = $key;
|
|
@@ -1726,11 +1755,11 @@ class Struct
|
|
|
1726
1755
|
elseif (self::M_KEYPOST === $mode) {
|
|
1727
1756
|
$out = $key;
|
|
1728
1757
|
|
|
1729
|
-
$args = self::
|
|
1758
|
+
$args = self::_getprop($parent, $key);
|
|
1730
1759
|
$args = self::islist($args) ? (($args instanceof ListRef) ? $args->list : $args) : [$args];
|
|
1731
1760
|
|
|
1732
1761
|
// Remove the $MERGE command from a parent map.
|
|
1733
|
-
$state->setval(self::
|
|
1762
|
+
$state->setval(self::undef());
|
|
1734
1763
|
|
|
1735
1764
|
// Literals in the parent have precedence, but we still merge onto
|
|
1736
1765
|
// the parent object, so that node tree references are not changed.
|
|
@@ -1753,15 +1782,15 @@ class Struct
|
|
|
1753
1782
|
$state->keys = array_slice($state->keys, 0, 1);
|
|
1754
1783
|
|
|
1755
1784
|
if (self::M_VAL !== $state->mode) {
|
|
1756
|
-
return self::
|
|
1785
|
+
return self::undef();
|
|
1757
1786
|
}
|
|
1758
1787
|
|
|
1759
1788
|
// Get arguments: ['`$EACH`', 'source-path', child-template]
|
|
1760
|
-
$srcpath = self::
|
|
1761
|
-
$child = self::clone(self::
|
|
1789
|
+
$srcpath = self::_getprop($state->parent, 1);
|
|
1790
|
+
$child = self::clone(self::_getprop($state->parent, 2));
|
|
1762
1791
|
|
|
1763
1792
|
// Source data.
|
|
1764
|
-
$srcstore = self::
|
|
1793
|
+
$srcstore = self::_getprop($store, $state->base, $store);
|
|
1765
1794
|
$src = self::getpath($srcstore, $srcpath, $state);
|
|
1766
1795
|
|
|
1767
1796
|
// Create parallel data structures: source entries :: child templates
|
|
@@ -1791,7 +1820,7 @@ class Struct
|
|
|
1791
1820
|
$rval = [];
|
|
1792
1821
|
|
|
1793
1822
|
if (0 < self::size($tval)) {
|
|
1794
|
-
$tcur = (null == $src) ? self::
|
|
1823
|
+
$tcur = (null == $src) ? self::undef() : ($src instanceof ListRef ? $src->list : array_values((array) $src));
|
|
1795
1824
|
|
|
1796
1825
|
$ckey = self::getelem($state->path, -2);
|
|
1797
1826
|
|
|
@@ -1826,7 +1855,7 @@ class Struct
|
|
|
1826
1855
|
self::setprop($target, $tkey, $rval);
|
|
1827
1856
|
|
|
1828
1857
|
// Prevent callee from damaging first list entry (since we are in `val` mode).
|
|
1829
|
-
return $rval[0] ?? self::
|
|
1858
|
+
return $rval[0] ?? self::undef();
|
|
1830
1859
|
}
|
|
1831
1860
|
|
|
1832
1861
|
|
|
@@ -1836,7 +1865,7 @@ class Struct
|
|
|
1836
1865
|
public static function transform_PACK(
|
|
1837
1866
|
object $state,
|
|
1838
1867
|
mixed $_val,
|
|
1839
|
-
|
|
1868
|
+
mixed $_ref,
|
|
1840
1869
|
mixed $store
|
|
1841
1870
|
): mixed {
|
|
1842
1871
|
$mode = $state->mode;
|
|
@@ -1847,13 +1876,17 @@ class Struct
|
|
|
1847
1876
|
|
|
1848
1877
|
// Only run in key:pre mode.
|
|
1849
1878
|
if (self::M_KEYPRE !== $mode) {
|
|
1850
|
-
return self::
|
|
1879
|
+
return self::undef();
|
|
1851
1880
|
}
|
|
1852
1881
|
|
|
1853
|
-
// Get arguments.
|
|
1854
|
-
|
|
1882
|
+
// Get arguments. Spec arrays are wrapped in ListRef by transform() so
|
|
1883
|
+
// accept either a plain array or a ListRef here.
|
|
1884
|
+
$args = self::_getprop($parent, $key);
|
|
1885
|
+
if ($args instanceof ListRef) {
|
|
1886
|
+
$args = $args->list;
|
|
1887
|
+
}
|
|
1855
1888
|
if (!is_array($args) || count($args) < 2) {
|
|
1856
|
-
return self::
|
|
1889
|
+
return self::undef();
|
|
1857
1890
|
}
|
|
1858
1891
|
|
|
1859
1892
|
$srcpath = $args[0];
|
|
@@ -1865,7 +1898,7 @@ class Struct
|
|
|
1865
1898
|
$target = self::getelem($nodes, $pathsize - 2) ?? self::getelem($nodes, $pathsize - 1);
|
|
1866
1899
|
|
|
1867
1900
|
// Source data
|
|
1868
|
-
$srcstore = self::
|
|
1901
|
+
$srcstore = self::_getprop($store, $state->base, $store);
|
|
1869
1902
|
$src = self::getpath($srcstore, $srcpath, $state);
|
|
1870
1903
|
|
|
1871
1904
|
// Prepare source as a list.
|
|
@@ -1878,16 +1911,16 @@ class Struct
|
|
|
1878
1911
|
}
|
|
1879
1912
|
$src = $newSrc;
|
|
1880
1913
|
} else {
|
|
1881
|
-
return self::
|
|
1914
|
+
return self::undef();
|
|
1882
1915
|
}
|
|
1883
1916
|
}
|
|
1884
1917
|
|
|
1885
1918
|
if (null == $src) {
|
|
1886
|
-
return self::
|
|
1919
|
+
return self::undef();
|
|
1887
1920
|
}
|
|
1888
1921
|
|
|
1889
1922
|
// Get keypath.
|
|
1890
|
-
$keypath = self::
|
|
1923
|
+
$keypath = self::_getprop($origchildspec, self::S_BKEY);
|
|
1891
1924
|
$childspec = self::delprop($origchildspec, self::S_BKEY);
|
|
1892
1925
|
|
|
1893
1926
|
$child = $childspec;
|
|
@@ -1900,7 +1933,7 @@ class Struct
|
|
|
1900
1933
|
$srcnode = $item[1];
|
|
1901
1934
|
|
|
1902
1935
|
$nkey = $srckey;
|
|
1903
|
-
if (self::
|
|
1936
|
+
if (self::undef() !== $keypath) {
|
|
1904
1937
|
if (is_string($keypath) && str_starts_with($keypath, '`')) {
|
|
1905
1938
|
$nkey = self::inject($keypath, self::merge([new \stdClass(), $store, (object) ['$TOP' => $srcnode]], 1));
|
|
1906
1939
|
} else {
|
|
@@ -1911,8 +1944,8 @@ class Struct
|
|
|
1911
1944
|
$tchild = self::clone($child);
|
|
1912
1945
|
self::setprop($tval, $nkey, $tchild);
|
|
1913
1946
|
|
|
1914
|
-
$anno = self::
|
|
1915
|
-
if (self::
|
|
1947
|
+
$anno = self::_getprop($srcnode, self::S_BANNO);
|
|
1948
|
+
if (self::undef() === $anno) {
|
|
1916
1949
|
self::delprop($tchild, self::S_BANNO);
|
|
1917
1950
|
} else {
|
|
1918
1951
|
self::setprop($tchild, self::S_BANNO, $anno);
|
|
@@ -1926,7 +1959,7 @@ class Struct
|
|
|
1926
1959
|
$tsrc = new \stdClass();
|
|
1927
1960
|
foreach ($src as $i => $n) {
|
|
1928
1961
|
$kn = null;
|
|
1929
|
-
if (self::
|
|
1962
|
+
if (self::undef() === $keypath) {
|
|
1930
1963
|
$kn = $i;
|
|
1931
1964
|
} elseif (is_string($keypath) && str_starts_with($keypath, '`')) {
|
|
1932
1965
|
$kn = self::inject($keypath, self::merge([new \stdClass(), $store, (object) ['$TOP' => $n]], 1));
|
|
@@ -1967,7 +2000,7 @@ class Struct
|
|
|
1967
2000
|
self::setprop($target, $tkey, $rval);
|
|
1968
2001
|
|
|
1969
2002
|
// Drop transform key.
|
|
1970
|
-
return self::
|
|
2003
|
+
return self::undef();
|
|
1971
2004
|
}
|
|
1972
2005
|
|
|
1973
2006
|
|
|
@@ -1977,16 +2010,16 @@ class Struct
|
|
|
1977
2010
|
$nodes = $state->nodes;
|
|
1978
2011
|
|
|
1979
2012
|
if (self::M_VAL !== $state->mode) {
|
|
1980
|
-
return self::
|
|
2013
|
+
return self::undef();
|
|
1981
2014
|
}
|
|
1982
2015
|
|
|
1983
2016
|
// Get arguments: ['`$REF`', 'ref-path'].
|
|
1984
|
-
$refpath = self::
|
|
2017
|
+
$refpath = self::_getprop($state->parent, 1);
|
|
1985
2018
|
$state->keyI = self::size($state->keys);
|
|
1986
2019
|
|
|
1987
2020
|
// Spec reference.
|
|
1988
|
-
$specFn = self::
|
|
1989
|
-
$spec = is_callable($specFn) ? $specFn() : self::
|
|
2021
|
+
$specFn = self::_getprop($store, '$SPEC');
|
|
2022
|
+
$spec = is_callable($specFn) ? $specFn() : self::undef();
|
|
1990
2023
|
|
|
1991
2024
|
$dpath = self::slice($state->path, 1);
|
|
1992
2025
|
$ref = self::getpath($spec, $refpath, (object) [
|
|
@@ -2010,9 +2043,9 @@ class Struct
|
|
|
2010
2043
|
$tpath = self::slice($state->path, -1);
|
|
2011
2044
|
$tcur = self::getpath($store, $cpath);
|
|
2012
2045
|
$tval = self::getpath($store, $tpath);
|
|
2013
|
-
$rval = self::
|
|
2046
|
+
$rval = self::undef();
|
|
2014
2047
|
|
|
2015
|
-
if (!$hasSubRef || self::
|
|
2048
|
+
if (!$hasSubRef || self::undef() !== $tval) {
|
|
2016
2049
|
$tinj = $state->child(0, [self::getelem($tpath, -1)]);
|
|
2017
2050
|
|
|
2018
2051
|
$tinj->path = $tpath;
|
|
@@ -2027,12 +2060,12 @@ class Struct
|
|
|
2027
2060
|
|
|
2028
2061
|
// If inject returned SKIP, use tref (mutated in place) not tinj->val (which may be SKIP)
|
|
2029
2062
|
if ($injResult === self::SKIP || $tinj->val === self::SKIP) {
|
|
2030
|
-
$rval = is_object($tref) ? $tref : self::
|
|
2063
|
+
$rval = is_object($tref) ? $tref : self::undef();
|
|
2031
2064
|
} else {
|
|
2032
2065
|
$rval = $tinj->val;
|
|
2033
2066
|
}
|
|
2034
2067
|
} else {
|
|
2035
|
-
$rval = self::
|
|
2068
|
+
$rval = self::undef();
|
|
2036
2069
|
}
|
|
2037
2070
|
|
|
2038
2071
|
$grandparent = $state->setval($rval, 2);
|
|
@@ -2041,7 +2074,7 @@ class Struct
|
|
|
2041
2074
|
// Sync the prior injection's parent if it's an array.
|
|
2042
2075
|
if ($state->prior && is_array($state->prior->parent)) {
|
|
2043
2076
|
$akey = self::getelem($state->path, -2);
|
|
2044
|
-
if (self::
|
|
2077
|
+
if (self::undef() === $rval) {
|
|
2045
2078
|
$state->prior->parent = self::delprop($state->prior->parent, $akey);
|
|
2046
2079
|
} else {
|
|
2047
2080
|
self::setprop($state->prior->parent, $akey, $rval);
|
|
@@ -2100,12 +2133,12 @@ class Struct
|
|
|
2100
2133
|
self::slice($inj->keys, 0, 1, true);
|
|
2101
2134
|
|
|
2102
2135
|
if (self::M_VAL !== $inj->mode) {
|
|
2103
|
-
return self::
|
|
2136
|
+
return self::undef();
|
|
2104
2137
|
}
|
|
2105
2138
|
|
|
2106
2139
|
// Get arguments: ['`$FORMAT`', 'name', child].
|
|
2107
|
-
$name = self::
|
|
2108
|
-
$child = self::
|
|
2140
|
+
$name = self::_getprop($inj->parent, 1);
|
|
2141
|
+
$child = self::_getprop($inj->parent, 2);
|
|
2109
2142
|
|
|
2110
2143
|
// Source data.
|
|
2111
2144
|
$tkey = self::getelem($inj->path, -2);
|
|
@@ -2115,11 +2148,11 @@ class Struct
|
|
|
2115
2148
|
$resolved = $cinj->val;
|
|
2116
2149
|
|
|
2117
2150
|
$formatters = self::_getFormatters();
|
|
2118
|
-
$formatter = (0 < (self::T_function & self::typify($name))) ? $name : ($formatters[$name] ?? self::
|
|
2151
|
+
$formatter = (0 < (self::T_function & self::typify($name))) ? $name : ($formatters[$name] ?? self::undef());
|
|
2119
2152
|
|
|
2120
|
-
if (self::
|
|
2153
|
+
if (self::undef() === $formatter) {
|
|
2121
2154
|
$inj->errs[] = '$FORMAT: unknown format: ' . $name . '.';
|
|
2122
|
-
return self::
|
|
2155
|
+
return self::undef();
|
|
2123
2156
|
}
|
|
2124
2157
|
|
|
2125
2158
|
$out = self::walk($resolved, $formatter);
|
|
@@ -2136,7 +2169,7 @@ class Struct
|
|
|
2136
2169
|
$ijname = 'APPLY';
|
|
2137
2170
|
|
|
2138
2171
|
if (!self::checkPlacement(self::M_VAL, $ijname, self::T_list, $inj)) {
|
|
2139
|
-
return self::
|
|
2172
|
+
return self::undef();
|
|
2140
2173
|
}
|
|
2141
2174
|
|
|
2142
2175
|
$args = self::slice($inj->parent, 1);
|
|
@@ -2149,9 +2182,9 @@ class Struct
|
|
|
2149
2182
|
}
|
|
2150
2183
|
}
|
|
2151
2184
|
[$err, $apply, $child] = self::injectorArgs([self::T_function, self::T_any], $argsList);
|
|
2152
|
-
if (self::
|
|
2185
|
+
if (self::undef() !== $err) {
|
|
2153
2186
|
$inj->errs[] = '$' . $ijname . ': ' . $err;
|
|
2154
|
-
return self::
|
|
2187
|
+
return self::undef();
|
|
2155
2188
|
}
|
|
2156
2189
|
|
|
2157
2190
|
$tkey = self::getelem($inj->path, -2);
|
|
@@ -2262,10 +2295,39 @@ class Struct
|
|
|
2262
2295
|
|
|
2263
2296
|
// When a child transform (e.g. $REF) deletes the key, inject returns SKIP; return mutated spec
|
|
2264
2297
|
if ($result === self::SKIP) {
|
|
2265
|
-
return self::cloneUnwrap($specClone);
|
|
2298
|
+
return self::_stdClassToArray(self::cloneUnwrap($specClone));
|
|
2266
2299
|
}
|
|
2267
2300
|
|
|
2268
|
-
|
|
2301
|
+
// Return maps as PHP associative arrays (the native map type) so callers
|
|
2302
|
+
// can use is_array()/array access directly. Internal processing may use
|
|
2303
|
+
// stdClass; the conversion here happens only at the public boundary.
|
|
2304
|
+
return self::_stdClassToArray(self::cloneUnwrap($result));
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
|
|
2308
|
+
// Deeply convert stdClass map nodes to associative arrays, leaving lists
|
|
2309
|
+
// (sequential arrays) and scalar values untouched. Used at the transform()
|
|
2310
|
+
// public boundary so consumers receive PHP-idiomatic arrays.
|
|
2311
|
+
private static function _stdClassToArray(mixed $val, int $depth = 0): mixed
|
|
2312
|
+
{
|
|
2313
|
+
if ($depth > 64) {
|
|
2314
|
+
return $val;
|
|
2315
|
+
}
|
|
2316
|
+
if ($val instanceof \stdClass) {
|
|
2317
|
+
$out = [];
|
|
2318
|
+
foreach (get_object_vars($val) as $k => $v) {
|
|
2319
|
+
$out[$k] = self::_stdClassToArray($v, $depth + 1);
|
|
2320
|
+
}
|
|
2321
|
+
return $out;
|
|
2322
|
+
}
|
|
2323
|
+
if (is_array($val)) {
|
|
2324
|
+
$out = [];
|
|
2325
|
+
foreach ($val as $k => $v) {
|
|
2326
|
+
$out[$k] = self::_stdClassToArray($v, $depth + 1);
|
|
2327
|
+
}
|
|
2328
|
+
return $out;
|
|
2329
|
+
}
|
|
2330
|
+
return $val;
|
|
2269
2331
|
}
|
|
2270
2332
|
|
|
2271
2333
|
/**
|
|
@@ -2275,7 +2337,7 @@ class Struct
|
|
|
2275
2337
|
private static function _cleanRefEntries(array $list): array {
|
|
2276
2338
|
$cleaned = [];
|
|
2277
2339
|
foreach ($list as $item) {
|
|
2278
|
-
if (self::islist($item) && count($item) >= 1 && self::
|
|
2340
|
+
if (self::islist($item) && count($item) >= 1 && self::_getprop($item, 0) === '`$REF`') {
|
|
2279
2341
|
// This is an unresolved $REF entry - remove it
|
|
2280
2342
|
continue;
|
|
2281
2343
|
}
|
|
@@ -2290,11 +2352,12 @@ class Struct
|
|
|
2290
2352
|
/** @internal */
|
|
2291
2353
|
private static function _invalidTypeMsg(array $path, string $needtype, int $vt, mixed $v): string
|
|
2292
2354
|
{
|
|
2293
|
-
$
|
|
2355
|
+
$missing = ($v === null || $v === self::undef());
|
|
2356
|
+
$vs = $missing ? 'no value' : self::stringify($v);
|
|
2294
2357
|
return 'Expected ' .
|
|
2295
2358
|
(1 < self::size($path) ? ('field ' . self::pathify($path, 1) . ' to be ') : '') .
|
|
2296
2359
|
$needtype . ', but found ' .
|
|
2297
|
-
($
|
|
2360
|
+
($missing ? '' : self::typename($vt) . ': ') . $vs . '.';
|
|
2298
2361
|
}
|
|
2299
2362
|
|
|
2300
2363
|
/* =======================
|
|
@@ -2306,19 +2369,19 @@ class Struct
|
|
|
2306
2369
|
*/
|
|
2307
2370
|
public static function validate_STRING(object $inj): mixed
|
|
2308
2371
|
{
|
|
2309
|
-
$out = self::
|
|
2372
|
+
$out = self::_getprop($inj->dparent, $inj->key);
|
|
2310
2373
|
|
|
2311
2374
|
$t = self::typify($out);
|
|
2312
2375
|
if (0 === (self::T_string & $t)) {
|
|
2313
2376
|
$msg = self::_invalidTypeMsg($inj->path, self::S_string, $t, $out);
|
|
2314
2377
|
$inj->errs[] = $msg;
|
|
2315
|
-
return self::
|
|
2378
|
+
return self::undef();
|
|
2316
2379
|
}
|
|
2317
2380
|
|
|
2318
2381
|
if (self::S_MT === $out) {
|
|
2319
2382
|
$msg = 'Empty string at ' . self::pathify($inj->path, 1);
|
|
2320
2383
|
$inj->errs[] = $msg;
|
|
2321
|
-
return self::
|
|
2384
|
+
return self::undef();
|
|
2322
2385
|
}
|
|
2323
2386
|
|
|
2324
2387
|
return $out;
|
|
@@ -2329,12 +2392,12 @@ class Struct
|
|
|
2329
2392
|
*/
|
|
2330
2393
|
public static function validate_NUMBER(object $inj): mixed
|
|
2331
2394
|
{
|
|
2332
|
-
$out = self::
|
|
2395
|
+
$out = self::_getprop($inj->dparent, $inj->key);
|
|
2333
2396
|
|
|
2334
2397
|
$t = self::typify($out);
|
|
2335
2398
|
if (0 === (self::T_number & $t)) {
|
|
2336
2399
|
$inj->errs[] = self::_invalidTypeMsg($inj->path, self::S_number, $t, $out);
|
|
2337
|
-
return self::
|
|
2400
|
+
return self::undef();
|
|
2338
2401
|
}
|
|
2339
2402
|
|
|
2340
2403
|
return $out;
|
|
@@ -2345,12 +2408,12 @@ class Struct
|
|
|
2345
2408
|
*/
|
|
2346
2409
|
public static function validate_BOOLEAN(object $inj): mixed
|
|
2347
2410
|
{
|
|
2348
|
-
$out = self::
|
|
2411
|
+
$out = self::_getprop($inj->dparent, $inj->key);
|
|
2349
2412
|
|
|
2350
2413
|
$t = self::typify($out);
|
|
2351
2414
|
if (0 === (self::T_boolean & $t)) {
|
|
2352
2415
|
$inj->errs[] = self::_invalidTypeMsg($inj->path, self::S_boolean, $t, $out);
|
|
2353
|
-
return self::
|
|
2416
|
+
return self::undef();
|
|
2354
2417
|
}
|
|
2355
2418
|
|
|
2356
2419
|
return $out;
|
|
@@ -2361,12 +2424,12 @@ class Struct
|
|
|
2361
2424
|
*/
|
|
2362
2425
|
public static function validate_OBJECT(object $inj): mixed
|
|
2363
2426
|
{
|
|
2364
|
-
$out = self::
|
|
2427
|
+
$out = self::_getprop($inj->dparent, $inj->key);
|
|
2365
2428
|
|
|
2366
2429
|
$t = self::typify($out);
|
|
2367
2430
|
if (0 === (self::T_map & $t)) {
|
|
2368
2431
|
$inj->errs[] = self::_invalidTypeMsg($inj->path, self::S_object, $t, $out);
|
|
2369
|
-
return self::
|
|
2432
|
+
return self::undef();
|
|
2370
2433
|
}
|
|
2371
2434
|
|
|
2372
2435
|
return $out;
|
|
@@ -2377,12 +2440,12 @@ class Struct
|
|
|
2377
2440
|
*/
|
|
2378
2441
|
public static function validate_ARRAY(object $inj): mixed
|
|
2379
2442
|
{
|
|
2380
|
-
$out = self::
|
|
2443
|
+
$out = self::_getprop($inj->dparent, $inj->key);
|
|
2381
2444
|
|
|
2382
2445
|
$t = self::typify($out);
|
|
2383
2446
|
if (0 === (self::T_list & $t)) {
|
|
2384
2447
|
$inj->errs[] = self::_invalidTypeMsg($inj->path, 'list', $t, $out);
|
|
2385
|
-
return self::
|
|
2448
|
+
return self::undef();
|
|
2386
2449
|
}
|
|
2387
2450
|
|
|
2388
2451
|
return $out;
|
|
@@ -2393,12 +2456,12 @@ class Struct
|
|
|
2393
2456
|
*/
|
|
2394
2457
|
public static function validate_FUNCTION(object $inj): mixed
|
|
2395
2458
|
{
|
|
2396
|
-
$out = self::
|
|
2459
|
+
$out = self::_getprop($inj->dparent, $inj->key);
|
|
2397
2460
|
|
|
2398
2461
|
$t = self::typify($out);
|
|
2399
2462
|
if (0 === (self::T_function & $t)) {
|
|
2400
2463
|
$inj->errs[] = self::_invalidTypeMsg($inj->path, self::S_function, $t, $out);
|
|
2401
|
-
return self::
|
|
2464
|
+
return self::undef();
|
|
2402
2465
|
}
|
|
2403
2466
|
|
|
2404
2467
|
return $out;
|
|
@@ -2412,12 +2475,12 @@ class Struct
|
|
|
2412
2475
|
$tname = strtolower(substr($ref ?? '', 1));
|
|
2413
2476
|
$idx = array_search($tname, self::TYPENAME);
|
|
2414
2477
|
$typev = ($idx !== false) ? (1 << (31 - $idx)) : 0;
|
|
2415
|
-
$out = self::
|
|
2478
|
+
$out = self::_getprop($inj->dparent, $inj->key);
|
|
2416
2479
|
|
|
2417
2480
|
$t = self::typify($out);
|
|
2418
2481
|
if (0 === ($t & $typev)) {
|
|
2419
2482
|
$inj->errs[] = self::_invalidTypeMsg($inj->path, $tname, $t, $out);
|
|
2420
|
-
return self::
|
|
2483
|
+
return self::undef();
|
|
2421
2484
|
}
|
|
2422
2485
|
|
|
2423
2486
|
return $out;
|
|
@@ -2428,7 +2491,7 @@ class Struct
|
|
|
2428
2491
|
*/
|
|
2429
2492
|
public static function validate_ANY(object $inj): mixed
|
|
2430
2493
|
{
|
|
2431
|
-
$out = self::
|
|
2494
|
+
$out = self::_getprop($inj->dparent, $inj->key);
|
|
2432
2495
|
return $out;
|
|
2433
2496
|
}
|
|
2434
2497
|
|
|
@@ -2447,18 +2510,18 @@ class Struct
|
|
|
2447
2510
|
|
|
2448
2511
|
// Map syntax.
|
|
2449
2512
|
if (self::M_KEYPRE === $mode) {
|
|
2450
|
-
$childtm = self::
|
|
2513
|
+
$childtm = self::_getprop($parent, $key);
|
|
2451
2514
|
|
|
2452
2515
|
// Get corresponding current object.
|
|
2453
|
-
$pkey = self::
|
|
2454
|
-
$tval = self::
|
|
2516
|
+
$pkey = self::_getprop($path, count($path) - 2);
|
|
2517
|
+
$tval = self::_getprop($inj->dparent, $pkey);
|
|
2455
2518
|
|
|
2456
|
-
if (self::
|
|
2519
|
+
if (self::undef() == $tval) {
|
|
2457
2520
|
$tval = new \stdClass();
|
|
2458
2521
|
} elseif (!self::ismap($tval)) {
|
|
2459
2522
|
$inj->errs[] = self::_invalidTypeMsg(
|
|
2460
2523
|
self::slice($inj->path, 0, -1), self::S_object, self::typify($tval), $tval);
|
|
2461
|
-
return self::
|
|
2524
|
+
return self::undef();
|
|
2462
2525
|
|
|
2463
2526
|
}
|
|
2464
2527
|
|
|
@@ -2471,8 +2534,8 @@ class Struct
|
|
|
2471
2534
|
$inj->keys = $keys;
|
|
2472
2535
|
|
|
2473
2536
|
// Remove $CHILD to cleanup output.
|
|
2474
|
-
$inj->setval(self::
|
|
2475
|
-
return self::
|
|
2537
|
+
$inj->setval(self::undef());
|
|
2538
|
+
return self::undef();
|
|
2476
2539
|
}
|
|
2477
2540
|
|
|
2478
2541
|
// List syntax.
|
|
@@ -2480,17 +2543,17 @@ class Struct
|
|
|
2480
2543
|
if (!self::islist($parent)) {
|
|
2481
2544
|
// $CHILD was not inside a list.
|
|
2482
2545
|
$inj->errs[] = 'Invalid $CHILD as value';
|
|
2483
|
-
return self::
|
|
2546
|
+
return self::undef();
|
|
2484
2547
|
}
|
|
2485
2548
|
|
|
2486
|
-
$childtm = self::
|
|
2549
|
+
$childtm = self::_getprop($parent, 1);
|
|
2487
2550
|
|
|
2488
|
-
if (self::
|
|
2551
|
+
if (self::undef() === $inj->dparent) {
|
|
2489
2552
|
// Empty list as default.
|
|
2490
2553
|
while (count($parent) > 0) {
|
|
2491
2554
|
array_pop($parent);
|
|
2492
2555
|
}
|
|
2493
|
-
return self::
|
|
2556
|
+
return self::undef();
|
|
2494
2557
|
}
|
|
2495
2558
|
|
|
2496
2559
|
if (!self::islist($inj->dparent)) {
|
|
@@ -2510,11 +2573,11 @@ class Struct
|
|
|
2510
2573
|
array_pop($parent);
|
|
2511
2574
|
}
|
|
2512
2575
|
$inj->keyI = 0;
|
|
2513
|
-
$out = self::
|
|
2576
|
+
$out = self::_getprop($inj->dparent, 0);
|
|
2514
2577
|
return $out;
|
|
2515
2578
|
}
|
|
2516
2579
|
|
|
2517
|
-
return self::
|
|
2580
|
+
return self::undef();
|
|
2518
2581
|
}
|
|
2519
2582
|
|
|
2520
2583
|
/**
|
|
@@ -2537,7 +2600,7 @@ class Struct
|
|
|
2537
2600
|
$inj->errs[] = 'The $ONE validator at field ' .
|
|
2538
2601
|
self::pathify($inj->path, 1, 1) .
|
|
2539
2602
|
' must be the first element of an array.';
|
|
2540
|
-
return self::
|
|
2603
|
+
return self::undef();
|
|
2541
2604
|
}
|
|
2542
2605
|
|
|
2543
2606
|
$inj->keyI = count($inj->keys ?? []);
|
|
@@ -2553,7 +2616,7 @@ class Struct
|
|
|
2553
2616
|
$inj->errs[] = 'The $ONE validator at field ' .
|
|
2554
2617
|
self::pathify($inj->path, 1, 1) .
|
|
2555
2618
|
' must have at least one argument.';
|
|
2556
|
-
return self::
|
|
2619
|
+
return self::undef();
|
|
2557
2620
|
}
|
|
2558
2621
|
|
|
2559
2622
|
// See if we can find a match.
|
|
@@ -2573,7 +2636,7 @@ class Struct
|
|
|
2573
2636
|
|
|
2574
2637
|
// Accept current value if there was a match
|
|
2575
2638
|
if (0 === count($terrs)) {
|
|
2576
|
-
return self::
|
|
2639
|
+
return self::undef();
|
|
2577
2640
|
}
|
|
2578
2641
|
}
|
|
2579
2642
|
|
|
@@ -2588,7 +2651,7 @@ class Struct
|
|
|
2588
2651
|
self::typify($inj->dparent), $inj->dparent);
|
|
2589
2652
|
}
|
|
2590
2653
|
|
|
2591
|
-
return self::
|
|
2654
|
+
return self::undef();
|
|
2592
2655
|
}
|
|
2593
2656
|
|
|
2594
2657
|
/**
|
|
@@ -2607,7 +2670,7 @@ class Struct
|
|
|
2607
2670
|
$inj->errs[] = 'The $EXACT validator at field ' .
|
|
2608
2671
|
self::pathify($inj->path, 1, 1) .
|
|
2609
2672
|
' must be the first element of an array.';
|
|
2610
|
-
return self::
|
|
2673
|
+
return self::undef();
|
|
2611
2674
|
}
|
|
2612
2675
|
|
|
2613
2676
|
$inj->keyI = count($inj->keys ?? []);
|
|
@@ -2623,7 +2686,7 @@ class Struct
|
|
|
2623
2686
|
$inj->errs[] = 'The $EXACT validator at field ' .
|
|
2624
2687
|
self::pathify($inj->path, 1, 1) .
|
|
2625
2688
|
' must have at least one argument.';
|
|
2626
|
-
return self::
|
|
2689
|
+
return self::undef();
|
|
2627
2690
|
}
|
|
2628
2691
|
|
|
2629
2692
|
// See if we can find an exact value match.
|
|
@@ -2638,7 +2701,7 @@ class Struct
|
|
|
2638
2701
|
}
|
|
2639
2702
|
|
|
2640
2703
|
if ($exactmatch) {
|
|
2641
|
-
return self::
|
|
2704
|
+
return self::undef();
|
|
2642
2705
|
}
|
|
2643
2706
|
}
|
|
2644
2707
|
|
|
@@ -2655,7 +2718,7 @@ class Struct
|
|
|
2655
2718
|
self::delprop($parent, $key);
|
|
2656
2719
|
}
|
|
2657
2720
|
|
|
2658
|
-
return self::
|
|
2721
|
+
return self::undef();
|
|
2659
2722
|
}
|
|
2660
2723
|
|
|
2661
2724
|
/**
|
|
@@ -2666,10 +2729,10 @@ class Struct
|
|
|
2666
2729
|
mixed $pval,
|
|
2667
2730
|
mixed $key = null,
|
|
2668
2731
|
mixed $parent = null,
|
|
2669
|
-
object $inj = null,
|
|
2732
|
+
?object $inj = null,
|
|
2670
2733
|
mixed $store = null
|
|
2671
2734
|
): void {
|
|
2672
|
-
if (self::
|
|
2735
|
+
if (self::undef() === $inj) {
|
|
2673
2736
|
return;
|
|
2674
2737
|
}
|
|
2675
2738
|
|
|
@@ -2678,12 +2741,12 @@ class Struct
|
|
|
2678
2741
|
}
|
|
2679
2742
|
|
|
2680
2743
|
// select needs exact matches
|
|
2681
|
-
$exact = self::
|
|
2744
|
+
$exact = self::_getprop($inj->meta, '`$EXACT`', false);
|
|
2682
2745
|
|
|
2683
2746
|
// Current val to verify.
|
|
2684
|
-
$cval = self::
|
|
2747
|
+
$cval = self::_getprop($inj->dparent, $key);
|
|
2685
2748
|
|
|
2686
|
-
if (self::
|
|
2749
|
+
if (self::undef() === $inj || (!$exact && self::undef() === $cval)) {
|
|
2687
2750
|
return;
|
|
2688
2751
|
}
|
|
2689
2752
|
|
|
@@ -2708,7 +2771,7 @@ class Struct
|
|
|
2708
2771
|
}
|
|
2709
2772
|
|
|
2710
2773
|
// Type mismatch.
|
|
2711
|
-
if ($ptype !== $ctype && self::
|
|
2774
|
+
if ($ptype !== $ctype && self::undef() !== $pval) {
|
|
2712
2775
|
$inj->errs[] = self::_invalidTypeMsg($inj->path, self::typename($ptype), $ctype, $cval);
|
|
2713
2776
|
return;
|
|
2714
2777
|
}
|
|
@@ -2723,7 +2786,7 @@ class Struct
|
|
|
2723
2786
|
$pkeys = self::keysof($pval);
|
|
2724
2787
|
|
|
2725
2788
|
// Empty spec object {} means object can be open (any keys).
|
|
2726
|
-
if (0 < count($pkeys) && true !== self::
|
|
2789
|
+
if (0 < count($pkeys) && true !== self::_getprop($pval, '`$OPEN`')) {
|
|
2727
2790
|
$badkeys = [];
|
|
2728
2791
|
foreach ($ckeys as $ckey) {
|
|
2729
2792
|
if (!self::haskey($pval, $ckey)) {
|
|
@@ -2802,7 +2865,17 @@ class Struct
|
|
|
2802
2865
|
$extra = is_object($injdef) && property_exists($injdef, 'extra') ? $injdef->extra : null;
|
|
2803
2866
|
|
|
2804
2867
|
$collect = null != $injdef && property_exists($injdef, 'errs');
|
|
2868
|
+
|
|
2869
|
+
// PHP arrays are value-copied, so a plain array on $injdef->errs would be
|
|
2870
|
+
// detached from $inj->errs deep inside inject. Wrap in ArrayObject so the
|
|
2871
|
+
// same storage is shared through the whole transform/inject chain.
|
|
2805
2872
|
$errs = (is_object($injdef) && property_exists($injdef, 'errs')) ? $injdef->errs : [];
|
|
2873
|
+
if (is_array($errs)) {
|
|
2874
|
+
$errs = new \ArrayObject($errs);
|
|
2875
|
+
}
|
|
2876
|
+
if ($collect) {
|
|
2877
|
+
$injdef->errs = $errs;
|
|
2878
|
+
}
|
|
2806
2879
|
|
|
2807
2880
|
$store = array_merge([
|
|
2808
2881
|
// Remove the transform commands.
|
|
@@ -2846,9 +2919,15 @@ class Struct
|
|
|
2846
2919
|
$transformOpts->errs = $errs;
|
|
2847
2920
|
$out = self::transform($data, $spec, $transformOpts);
|
|
2848
2921
|
|
|
2922
|
+
// Unwrap shared ArrayObject back to a plain array on the caller's injdef
|
|
2923
|
+
// so count()/foreach work as callers (including select) expect.
|
|
2924
|
+
if ($collect && $injdef->errs instanceof \ArrayObject) {
|
|
2925
|
+
$injdef->errs = $injdef->errs->getArrayCopy();
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2849
2928
|
$generr = (0 < count($errs) && !$collect);
|
|
2850
2929
|
if ($generr) {
|
|
2851
|
-
throw new \Exception('Invalid data: ' . implode(' | ', $errs));
|
|
2930
|
+
throw new \Exception('Invalid data: ' . implode(' | ', (array) $errs));
|
|
2852
2931
|
}
|
|
2853
2932
|
|
|
2854
2933
|
return $out;
|
|
@@ -2900,13 +2979,7 @@ class Struct
|
|
|
2900
2979
|
];
|
|
2901
2980
|
|
|
2902
2981
|
$q = self::clone($query);
|
|
2903
|
-
|
|
2904
|
-
self::walk($q, function($k, $v) {
|
|
2905
|
-
if (self::ismap($v)) {
|
|
2906
|
-
self::setprop($v, '`$OPEN`', self::getprop($v, '`$OPEN`', true));
|
|
2907
|
-
}
|
|
2908
|
-
return $v;
|
|
2909
|
-
});
|
|
2982
|
+
$q = self::_select_add_open($q);
|
|
2910
2983
|
|
|
2911
2984
|
foreach ($children as $child) {
|
|
2912
2985
|
$injdef->errs = [];
|
|
@@ -2920,13 +2993,41 @@ class Struct
|
|
|
2920
2993
|
return $results;
|
|
2921
2994
|
}
|
|
2922
2995
|
|
|
2996
|
+
// Recursively add `$OPEN` to all maps in a query for select.
|
|
2997
|
+
// PHP arrays are value types, so walk can't modify in-place.
|
|
2998
|
+
private static function _select_add_open(mixed $val): mixed
|
|
2999
|
+
{
|
|
3000
|
+
if (self::ismap($val)) {
|
|
3001
|
+
if (is_array($val)) {
|
|
3002
|
+
if (!array_key_exists('`$OPEN`', $val)) {
|
|
3003
|
+
$val['`$OPEN`'] = true;
|
|
3004
|
+
}
|
|
3005
|
+
foreach ($val as $k => $v) {
|
|
3006
|
+
$val[$k] = self::_select_add_open($v);
|
|
3007
|
+
}
|
|
3008
|
+
} elseif ($val instanceof \stdClass) {
|
|
3009
|
+
if (!property_exists($val, '`$OPEN`')) {
|
|
3010
|
+
$val->{'`$OPEN`'} = true;
|
|
3011
|
+
}
|
|
3012
|
+
foreach (get_object_vars($val) as $k => $v) {
|
|
3013
|
+
$val->$k = self::_select_add_open($v);
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
} elseif (self::islist($val) && is_array($val)) {
|
|
3017
|
+
foreach ($val as $i => $v) {
|
|
3018
|
+
$val[$i] = self::_select_add_open($v);
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
return $val;
|
|
3022
|
+
}
|
|
3023
|
+
|
|
2923
3024
|
/**
|
|
2924
3025
|
* Helper method for $AND operator in select queries
|
|
2925
3026
|
*/
|
|
2926
3027
|
private static function select_AND(object $state, mixed $_val, mixed $_ref, mixed $store): mixed
|
|
2927
3028
|
{
|
|
2928
3029
|
if (self::M_KEYPRE === $state->mode) {
|
|
2929
|
-
$terms = self::
|
|
3030
|
+
$terms = self::_getprop($state->parent, $state->key);
|
|
2930
3031
|
|
|
2931
3032
|
$ppath = self::slice($state->path, -1);
|
|
2932
3033
|
$point = self::getpath($store, $ppath);
|
|
@@ -2960,7 +3061,7 @@ class Struct
|
|
|
2960
3061
|
private static function select_OR(object $state, mixed $_val, mixed $_ref, mixed $store): mixed
|
|
2961
3062
|
{
|
|
2962
3063
|
if (self::M_KEYPRE === $state->mode) {
|
|
2963
|
-
$terms = self::
|
|
3064
|
+
$terms = self::_getprop($state->parent, $state->key);
|
|
2964
3065
|
|
|
2965
3066
|
$ppath = self::slice($state->path, -1);
|
|
2966
3067
|
$point = self::getpath($store, $ppath);
|
|
@@ -2996,7 +3097,7 @@ class Struct
|
|
|
2996
3097
|
private static function select_NOT(object $state, mixed $_val, mixed $_ref, mixed $store): mixed
|
|
2997
3098
|
{
|
|
2998
3099
|
if (self::M_KEYPRE === $state->mode) {
|
|
2999
|
-
$term = self::
|
|
3100
|
+
$term = self::_getprop($state->parent, $state->key);
|
|
3000
3101
|
|
|
3001
3102
|
$ppath = self::slice($state->path, -1);
|
|
3002
3103
|
$point = self::getpath($store, $ppath);
|
|
@@ -3028,7 +3129,7 @@ class Struct
|
|
|
3028
3129
|
private static function select_CMP(object $state, mixed $_val, mixed $ref, mixed $store): mixed
|
|
3029
3130
|
{
|
|
3030
3131
|
if (self::M_KEYPRE === $state->mode) {
|
|
3031
|
-
$term = self::
|
|
3132
|
+
$term = self::_getprop($state->parent, $state->key);
|
|
3032
3133
|
$gkey = self::getelem($state->path, -2);
|
|
3033
3134
|
|
|
3034
3135
|
$ppath = self::slice($state->path, -1);
|
|
@@ -3070,12 +3171,13 @@ class Struct
|
|
|
3070
3171
|
* The key should be an integer, or a string that can parse to an integer only.
|
|
3071
3172
|
* Negative integers count from the end of the list.
|
|
3072
3173
|
*/
|
|
3073
|
-
public static function getelem(mixed $val, mixed $key, mixed $alt =
|
|
3174
|
+
public static function getelem(mixed $val, mixed $key, mixed $alt = null): mixed
|
|
3074
3175
|
{
|
|
3075
|
-
$
|
|
3176
|
+
$altIsDefault = (func_num_args() < 3);
|
|
3177
|
+
$out = self::undef();
|
|
3076
3178
|
|
|
3077
|
-
if ($val === self::
|
|
3078
|
-
return $
|
|
3179
|
+
if ($val === null || $val === self::undef() || $key === null || $key === self::undef()) {
|
|
3180
|
+
return $altIsDefault ? null : (is_callable($alt) ? $alt() : $alt);
|
|
3079
3181
|
}
|
|
3080
3182
|
|
|
3081
3183
|
if (self::islist($val)) {
|
|
@@ -3083,25 +3185,25 @@ class Struct
|
|
|
3083
3185
|
$listLen = count($listArr);
|
|
3084
3186
|
if (is_string($key)) {
|
|
3085
3187
|
if (!preg_match('/^[-0-9]+$/', $key)) {
|
|
3086
|
-
$out = self::
|
|
3188
|
+
$out = self::undef();
|
|
3087
3189
|
} else {
|
|
3088
3190
|
$nkey = (int) $key;
|
|
3089
3191
|
if ($nkey < 0) {
|
|
3090
3192
|
$nkey = $listLen + $nkey;
|
|
3091
3193
|
}
|
|
3092
|
-
$out = ($nkey >= 0 && $nkey < $listLen) ? $listArr[$nkey] : self::
|
|
3194
|
+
$out = ($nkey >= 0 && $nkey < $listLen) ? $listArr[$nkey] : self::undef();
|
|
3093
3195
|
}
|
|
3094
3196
|
} elseif (is_int($key)) {
|
|
3095
3197
|
$nkey = $key;
|
|
3096
3198
|
if ($nkey < 0) {
|
|
3097
3199
|
$nkey = $listLen + $nkey;
|
|
3098
3200
|
}
|
|
3099
|
-
$out = ($nkey >= 0 && $nkey < $listLen) ? $listArr[$nkey] : self::
|
|
3201
|
+
$out = ($nkey >= 0 && $nkey < $listLen) ? $listArr[$nkey] : self::undef();
|
|
3100
3202
|
}
|
|
3101
3203
|
}
|
|
3102
3204
|
|
|
3103
|
-
if ($out === self::
|
|
3104
|
-
if ($
|
|
3205
|
+
if ($out === self::undef()) {
|
|
3206
|
+
if ($altIsDefault) {
|
|
3105
3207
|
return null;
|
|
3106
3208
|
}
|
|
3107
3209
|
return is_callable($alt) ? $alt() : $alt;
|
|
@@ -3168,19 +3270,19 @@ class Struct
|
|
|
3168
3270
|
|
|
3169
3271
|
$parts = (0 < (self::T_list & $pathType)) ? $path :
|
|
3170
3272
|
((0 < (self::T_string & $pathType)) ? explode('.', $path) :
|
|
3171
|
-
((0 < (self::T_number & $pathType)) ? [$path] : self::
|
|
3273
|
+
((0 < (self::T_number & $pathType)) ? [$path] : self::undef()));
|
|
3172
3274
|
|
|
3173
|
-
if (self::
|
|
3174
|
-
return self::
|
|
3275
|
+
if (self::undef() === $parts) {
|
|
3276
|
+
return self::undef();
|
|
3175
3277
|
}
|
|
3176
3278
|
|
|
3177
|
-
$base = self::
|
|
3279
|
+
$base = self::_getprop($injdef, self::S_BASE);
|
|
3178
3280
|
$numparts = self::size($parts);
|
|
3179
|
-
$parent = self::
|
|
3281
|
+
$parent = self::_getprop($store, $base, $store);
|
|
3180
3282
|
|
|
3181
3283
|
for ($pI = 0; $pI < $numparts - 1; $pI++) {
|
|
3182
3284
|
$partKey = self::getelem($parts, $pI);
|
|
3183
|
-
$nextParent = self::
|
|
3285
|
+
$nextParent = self::_getprop($parent, $partKey);
|
|
3184
3286
|
if (!self::isnode($nextParent)) {
|
|
3185
3287
|
$nextParent = (0 < (self::T_number & self::typify(self::getelem($parts, $pI + 1))))
|
|
3186
3288
|
? [] : new \stdClass();
|
|
@@ -3230,10 +3332,10 @@ class Struct
|
|
|
3230
3332
|
public static function injectorArgs(array $argTypes, array $args): array
|
|
3231
3333
|
{
|
|
3232
3334
|
$numargs = self::size($argTypes);
|
|
3233
|
-
$found = array_fill(0, 1 + $numargs, self::
|
|
3234
|
-
$found[0] = self::
|
|
3335
|
+
$found = array_fill(0, 1 + $numargs, self::undef());
|
|
3336
|
+
$found[0] = self::undef();
|
|
3235
3337
|
for ($argI = 0; $argI < $numargs; $argI++) {
|
|
3236
|
-
$arg = $args[$argI] ?? self::
|
|
3338
|
+
$arg = $args[$argI] ?? self::undef();
|
|
3237
3339
|
$argType = self::typify($arg);
|
|
3238
3340
|
if (0 === ($argTypes[$argI] & $argType)) {
|
|
3239
3341
|
$found[0] = 'invalid argument: ' . self::stringify($arg, 22) .
|
|
@@ -3287,7 +3389,9 @@ class Injection
|
|
|
3287
3389
|
public array $nodes;
|
|
3288
3390
|
/** @var callable */
|
|
3289
3391
|
public mixed $handler;
|
|
3290
|
-
|
|
3392
|
+
// Accepts plain array or ArrayObject. ArrayObject is used by validate/select
|
|
3393
|
+
// so that mutations propagate back through the inject/transform call chain.
|
|
3394
|
+
public array|\ArrayObject $errs;
|
|
3291
3395
|
public object $meta;
|
|
3292
3396
|
public mixed $dparent;
|
|
3293
3397
|
public array $dpath;
|
|
@@ -3336,7 +3440,7 @@ class Injection
|
|
|
3336
3440
|
' p=' . Struct::stringify($this->parent, -1, 1) .
|
|
3337
3441
|
' m=' . Struct::stringify($this->meta, -1, 1) .
|
|
3338
3442
|
' d/' . Struct::pathify($this->dpath, 1) . '=' . Struct::stringify($this->dparent, -1, 1) .
|
|
3339
|
-
' r=' . Struct::stringify(Struct::
|
|
3443
|
+
' r=' . Struct::stringify(Struct::_getprop($this->nodes[0] ?? null, '$TOP'), -1, 1);
|
|
3340
3444
|
}
|
|
3341
3445
|
|
|
3342
3446
|
|
|
@@ -3360,7 +3464,7 @@ class Injection
|
|
|
3360
3464
|
else {
|
|
3361
3465
|
// this->dparent is the containing node of the current store value.
|
|
3362
3466
|
if (null !== $parentkey && Struct::UNDEF !== $parentkey) {
|
|
3363
|
-
$this->dparent = Struct::
|
|
3467
|
+
$this->dparent = Struct::_getprop($this->dparent, $parentkey);
|
|
3364
3468
|
|
|
3365
3469
|
$lastpart = Struct::getelem($this->dpath, -1);
|
|
3366
3470
|
if ($lastpart === '$:' . $parentkey) {
|
|
@@ -3381,7 +3485,7 @@ class Injection
|
|
|
3381
3485
|
$key = Struct::strkey($keys[$keyI] ?? null);
|
|
3382
3486
|
$val = $this->val;
|
|
3383
3487
|
|
|
3384
|
-
$cinj = new Injection(Struct::
|
|
3488
|
+
$cinj = new Injection(Struct::_getprop($val, $key), $val);
|
|
3385
3489
|
$cinj->keyI = $keyI;
|
|
3386
3490
|
$cinj->keys = $keys;
|
|
3387
3491
|
$cinj->key = $key;
|
|
@@ -3428,4 +3532,4 @@ class Injection
|
|
|
3428
3532
|
return $parent;
|
|
3429
3533
|
}
|
|
3430
3534
|
}
|
|
3431
|
-
?>
|
|
3535
|
+
?>
|