diff --git a/src/stdlib/arrays.zig b/src/stdlib/arrays.zig index da54ab6d..77bfcff1 100644 --- a/src/stdlib/arrays.zig +++ b/src/stdlib/arrays.zig @@ -1240,7 +1240,10 @@ const ArrayCmp = enum { values, keys, assoc }; fn arraySetOp(comptime cmp: ArrayCmp, comptime keep_matches: bool) fn (*NativeContext, []const Value) RuntimeError!NativeResult { return struct { fn f(ctx: *NativeContext, args: []const Value) RuntimeError!NativeResult { - if (args.len < 2 or args[0] != .array) return NativeResult.scalar(.null); + // php8 accepts a single array with no other arrays, intersect keeps + // every entry (matchesAll over none) and diff drops none (matchesAny + // over none) soo both return a key-preserving copy of $array + if (args.len == 0 or args[0] != .array) return NativeResult.scalar(.null); const src = args[0].array; var result = try ctx.createArray(); for (src.entries.items) |entry| { @@ -1463,25 +1466,15 @@ fn array_rand(ctx: *NativeContext, args: []const Value) RuntimeError!NativeResul try ctx.vm.setPendingException("ValueError", "array_rand(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array)"); return error.RuntimeError; } - if (num == 1 and (args.len < 2 or Value.toInt(args[1]) == 1 and args.len == 1)) { - // single-arg form returns scalar + // php returns a scalar key whenever num is 1, whether $num was omitted or + // passed explicitly, an array of keys only for num > 1 + if (num == 1) { const idx = std.crypto.random.intRangeAtMost(usize, 0, arr.entries.items.len - 1); return switch (arr.entries.items[idx].key) { .int => |i| NativeResult.scalar(.{ .int = i }), .string => |s| NativeResult.shareString(s), }; } - // PHP returns scalar key when num=1 (default), array of keys otherwise. - if (num == 1) { - const out = try ctx.createArray(); - const idx = std.crypto.random.intRangeAtMost(usize, 0, arr.entries.items.len - 1); - const v: Value = switch (arr.entries.items[idx].key) { - .int => |i| .{ .int = i }, - .string => |s| .{ .string = s }, - }; - try out.append(ctx.allocator, v); - return NativeResult.borrowed(.{ .array = out }); - } // Fisher-Yates partial shuffle for `num` distinct picks var pool = try ctx.allocator.alloc(usize, arr.entries.items.len); defer ctx.allocator.free(pool); diff --git a/src/stdlib/datetime.zig b/src/stdlib/datetime.zig index 3f4d9c4f..c7d91846 100644 --- a/src/stdlib/datetime.zig +++ b/src/stdlib/datetime.zig @@ -44,6 +44,7 @@ pub const entries = .{ .{ "date_time_set", native_date_time_set }, .{ "date_parse", native_date_parse }, .{ "date_parse_from_format", native_date_parse_from_format }, + .{ "date_get_last_errors", dtGetLastErrors }, .{ "mktime", native_mktime }, .{ "gmmktime", native_gmmktime }, .{ "strtotime", native_strtotime }, diff --git a/tests/array_diff_intersect_single_array.php b/tests/array_diff_intersect_single_array.php new file mode 100644 index 00000000..7c9744a9 --- /dev/null +++ b/tests/array_diff_intersect_single_array.php @@ -0,0 +1,37 @@ + 1, 5 => 2, "y" => "2", 0 => 3]; + +var_dump(array_diff($a) === $a); +var_dump(array_intersect($a) === $a); +var_dump(array_diff_key($a) === $a); +var_dump(array_intersect_key($a) === $a); +var_dump(array_diff_assoc($a) === $a); +var_dump(array_intersect_assoc($a) === $a); + +// keys and their order survive exactly (=== above already implies it, but keep +// the shape visible in the output) +print_r(array_diff($a)); +print_r(array_intersect($a)); +var_dump(array_keys(array_diff_key($a))); + +// duplicate values are not collapsed by the single-array form +$dupes = ["a" => 1, "b" => 1, "c" => "1"]; +var_dump(array_diff($dupes) === $dupes); +var_dump(array_intersect($dupes) === $dupes); + +// empty array stays empty +var_dump(array_diff([]) === []); +var_dump(array_intersect([]) === []); +var_dump(array_diff_assoc([]) === []); +var_dump(array_intersect_assoc([]) === []); + +// two-array behavior is unchanged +print_r(array_diff($a, [2])); +print_r(array_intersect($a, [2])); +print_r(array_diff_key($a, ["x" => 0])); +print_r(array_intersect_key($a, ["x" => 0])); +print_r(array_diff_assoc($a, ["x" => 1])); +print_r(array_intersect_assoc($a, ["x" => 1])); diff --git a/tests/array_rand_num_one.php b/tests/array_rand_num_one.php new file mode 100644 index 00000000..a1a1b444 --- /dev/null +++ b/tests/array_rand_num_one.php @@ -0,0 +1,52 @@ + 1 returns an array of keys. no assertion depends on which key the +// random pick returned, only on its type and membership + +$assoc = ["a" => 1, "b" => 2, "c" => 3]; +var_dump(is_array(array_rand($assoc))); +var_dump(is_array(array_rand($assoc, 1))); +var_dump(is_array(array_rand($assoc, 2))); + +// string keys +$k = array_rand($assoc); +echo gettype($k) . " " . (array_key_exists($k, $assoc) ? "in" : "missing") . "\n"; +$k = array_rand($assoc, 1); +echo gettype($k) . " " . (array_key_exists($k, $assoc) ? "in" : "missing") . "\n"; + +// numeric keys +$list = [10, 20, 30, 40]; +$k = array_rand($list); +echo gettype($k) . " " . (array_key_exists($k, $list) ? "in" : "missing") . "\n"; +$k = array_rand($list, 1); +echo gettype($k) . " " . (array_key_exists($k, $list) ? "in" : "missing") . "\n"; + +// num > 1 keeps the array-of-keys form distinct keys of the source array +$picks = array_rand($assoc, 2); +echo gettype($picks) . " " . count($picks) . "\n"; +echo count(array_unique($picks)) === 2 ? "distinct\n" : "dupes\n"; +foreach ($picks as $p) { + echo (array_key_exists($p, $assoc) ? "in " : "missing "); +} +echo "\n"; + +// asking for every key is deterministic, all keys in their original order +var_dump(array_rand($list, 4) === [0, 1, 2, 3]); +var_dump(array_rand($assoc, 3) === ["a", "b", "c"]); + +// single-element array +$one = ["only" => 1]; +var_dump(array_rand($one)); +var_dump(array_rand($one, 1)); + +// out of range num still raises the ValueError +try { + array_rand($assoc, 4); +} catch (ValueError $e) { + echo get_class($e) . ": " . $e->getMessage() . "\n"; +} +try { + array_rand($assoc, 0); +} catch (ValueError $e) { + echo get_class($e) . ": " . $e->getMessage() . "\n"; +} diff --git a/tests/date_get_last_errors.php b/tests/date_get_last_errors.php new file mode 100644 index 00000000..2430bcfd --- /dev/null +++ b/tests/date_get_last_errors.php @@ -0,0 +1,27 @@ +