Skip to content

Commit f576c93

Browse files
authored
refactor: simplify materialize_missing to accept full defaults list (#241)
1 parent 870c409 commit f576c93

3 files changed

Lines changed: 49 additions & 46 deletions

File tree

phper/src/values.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -242,24 +242,35 @@ impl ExecuteData {
242242
}
243243
}
244244

245-
/// Fills missing optional parameters from `num_args()` upward with the
246-
/// given default values. Stops when the iterator runs out or
247-
/// `common_num_args()` is reached.
245+
/// Fills missing optional parameters with their default values.
246+
///
247+
/// `defaults` is the full list of default values for **all** optional
248+
/// parameters of the function. Already-provided optional arguments are
249+
/// skipped automatically.
248250
///
249251
/// `num_args` is updated to reflect the new count.
250252
///
251253
/// # Errors
252254
///
253-
/// Returns [`CallArgError`] if filling would exceed `common_num_args()`.
255+
/// Returns [`CallArgError`] if the total after filling would not match
256+
/// `common_num_args()`.
254257
pub fn materialize_missing(
255258
&mut self, defaults: impl IntoIterator<Item = ZVal>,
256259
) -> crate::Result<()> {
257260
let declared_len = self.common_num_args() as usize;
261+
let required_len = self.common_required_num_args();
258262
let passed_len = self.num_args();
263+
264+
if passed_len >= declared_len {
265+
return Ok(());
266+
}
267+
259268
let execute_data_ptr = self.as_mut_ptr();
269+
270+
let provided_optionals = passed_len.saturating_sub(required_len);
260271
let mut i = passed_len;
261272

262-
for mut default in defaults {
273+
for mut default in defaults.into_iter().skip(provided_optionals) {
263274
if i >= declared_len {
264275
return Err(CallArgError::new(i, declared_len).into());
265276
}
@@ -276,10 +287,8 @@ impl ExecuteData {
276287
if i != declared_len {
277288
return Err(CallArgError::new(i, declared_len).into());
278289
}
279-
if i > passed_len {
280-
unsafe {
281-
phper_zend_set_call_num_args(execute_data_ptr, i.try_into().unwrap());
282-
}
290+
unsafe {
291+
phper_zend_set_call_num_args(execute_data_ptr, i.try_into().unwrap());
283292
}
284293
Ok(())
285294
}

tests/integration/src/execute_data.rs

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,57 +11,48 @@
1111
use phper::{functions::Argument, modules::Module, values::ZVal};
1212

1313
pub fn integrate(module: &mut Module) {
14-
materialize_missing_fill(module);
15-
materialize_missing_noop(module);
16-
materialize_missing_partial(module);
17-
materialize_missing_exceed_error(module);
18-
materialize_missing_insufficient_error(module);
14+
register_two_optionals(module);
15+
register_no_optionals(module);
16+
register_exceed_error(module);
17+
register_insufficient_error(module);
1918
}
2019

21-
fn materialize_missing_fill(module: &mut Module) {
20+
fn register_two_optionals(module: &mut Module) {
2221
module
2322
.add_function_with_execute_data(
24-
"materialize_missing_fill",
23+
"materialize_missing_two_optionals",
2524
|execute_data, _arguments| -> phper::Result<String> {
2625
execute_data.materialize_missing([ZVal::from(42), ZVal::from("hello")])?;
2726
let a = execute_data.get_parameter(0).expect_long()?;
2827
let b = execute_data.get_parameter(1).expect_z_str()?.to_str()?;
29-
Ok(format!("{}, {}", a, b))
28+
let c = execute_data.get_parameter(2).expect_long()?;
29+
let d = execute_data.get_parameter(3).expect_z_str()?.to_str()?;
30+
Ok(format!("{}, {}, {}, {}", a, b, c, d))
3031
},
3132
)
32-
.arguments([Argument::new("a").optional(), Argument::new("b").optional()]);
33+
.arguments([
34+
Argument::new("a"),
35+
Argument::new("b"),
36+
Argument::new("c").optional(),
37+
Argument::new("d").optional(),
38+
]);
3339
}
3440

35-
fn materialize_missing_noop(module: &mut Module) {
41+
fn register_no_optionals(module: &mut Module) {
3642
module
3743
.add_function_with_execute_data(
38-
"materialize_missing_noop",
44+
"materialize_missing_no_optionals",
3945
|execute_data, _arguments| -> phper::Result<String> {
40-
let passed = execute_data.num_args();
4146
execute_data.materialize_missing([])?;
4247
let a = execute_data.get_parameter(0).expect_long()?;
4348
let b = execute_data.get_parameter(1).expect_z_str()?.to_str()?;
44-
Ok(format!("{}, {}, {}", passed, a, b))
45-
},
46-
)
47-
.arguments([Argument::new("a"), Argument::new("b")]);
48-
}
49-
50-
fn materialize_missing_partial(module: &mut Module) {
51-
module
52-
.add_function_with_execute_data(
53-
"materialize_missing_partial",
54-
|execute_data, _arguments| -> phper::Result<String> {
55-
execute_data.materialize_missing([ZVal::from(42)])?;
56-
let a = execute_data.get_parameter(0).expect_z_str()?.to_str()?;
57-
let b = execute_data.get_parameter(1).expect_long()?;
5849
Ok(format!("{}, {}", a, b))
5950
},
6051
)
61-
.arguments([Argument::new("a").optional(), Argument::new("b").optional()]);
52+
.arguments([Argument::new("a"), Argument::new("b")]);
6253
}
6354

64-
fn materialize_missing_exceed_error(module: &mut Module) {
55+
fn register_exceed_error(module: &mut Module) {
6556
module
6657
.add_function_with_execute_data(
6758
"materialize_missing_exceed_error",
@@ -73,7 +64,7 @@ fn materialize_missing_exceed_error(module: &mut Module) {
7364
.arguments([Argument::new("a").optional(), Argument::new("b").optional()]);
7465
}
7566

76-
fn materialize_missing_insufficient_error(module: &mut Module) {
67+
fn register_insufficient_error(module: &mut Module) {
7768
module
7869
.add_function_with_execute_data(
7970
"materialize_missing_insufficient_error",

tests/integration/tests/php/execute_data.php

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,27 @@
1313

1414
require_once __DIR__ . '/_common.php';
1515

16-
// Test 1: fill all missing arguments with defaults
17-
assert_eq(materialize_missing_fill(), "42, hello");
16+
// 2 required + 2 optional, 2 args provided → fill both optionals
17+
assert_eq(materialize_missing_two_optionals(1, "world"), "1, world, 42, hello");
1818

19-
// Test 2: provide all arguments, no filling needed
20-
assert_eq(materialize_missing_noop(1, "world"), "2, 1, world");
19+
// 2 required + 2 optional, 3 args provided → skip 1st default, fill 2nd
20+
assert_eq(materialize_missing_two_optionals(1, "world", 10), "1, world, 10, hello");
2121

22-
// Test 3: partial fill - only second arg is missing
23-
assert_eq(materialize_missing_partial("hello"), "hello, 42");
22+
// 2 required + 2 optional, all 4 args provided → no-op
23+
assert_eq(materialize_missing_two_optionals(1, "world", 10, "foo"), "1, world, 10, foo");
2424

25-
// Test 4: exceed declared args causes TypeError
25+
// no optional params → no-op
26+
assert_eq(materialize_missing_no_optionals(1, "world"), "1, world");
27+
28+
// defaults exceed declared param count
2629
assert_throw(
2730
function () { materialize_missing_exceed_error(); },
2831
"TypeError",
2932
0,
3033
"call arg index 2 out of bounds: must be in [0, 2) (declared_len = 2)"
3134
);
3235

33-
// Test 5: insufficient defaults causes TypeError
36+
// not enough defaults
3437
assert_throw(
3538
function () { materialize_missing_insufficient_error(); },
3639
"TypeError",

0 commit comments

Comments
 (0)