Skip to content

Commit 85f8354

Browse files
committed
implement argument default values
1 parent c205332 commit 85f8354

File tree

5 files changed

+175
-40
lines changed

5 files changed

+175
-40
lines changed

phper-sys/php_wrapper.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,14 +511,15 @@ zend_internal_arg_info phper_zend_arg_info(bool pass_by_ref, const char *name) {
511511
zend_internal_arg_info phper_zend_arg_info_with_type(bool pass_by_ref,
512512
const char *name,
513513
uint32_t type_hint,
514-
bool allow_null) {
514+
bool allow_null,
515+
const char *default_value) {
515516
#if PHP_VERSION_ID >= 70200
516517
zend_internal_arg_info info[] = {
517-
ZEND_ARG_TYPE_INFO(pass_by_ref, , type_hint, allow_null)
518+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, , type_hint, allow_null, default_value)
518519
};
519520
#else
520521
zend_internal_arg_info info[] = {
521-
ZEND_ARG_TYPE_INFO(pass_by_ref, , type_hint, NULL, allow_null)
522+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, , type_hint, NULL, allow_null, default_value)
522523
};
523524
#endif
524525
info[0].name = name;

phper/src/functions.rs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -255,40 +255,45 @@ impl FunctionEntry {
255255

256256

257257
for arg in arguments {
258+
let default_value_ptr = arg
259+
.default_value
260+
.as_ref()
261+
.map(|s| s.as_ptr())
262+
.unwrap_or(std::ptr::null());
258263
let arg_info = if let Some(ref type_hint) = arg.type_hint {
259264
match type_hint {
260265
ArgumentTypeHint::Null => Some(
261-
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_NULL, true)
266+
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_NULL, true, default_value_ptr)
262267
),
263268
ArgumentTypeHint::Bool => Some(
264-
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), _IS_BOOL, arg.nullable)
269+
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), _IS_BOOL, arg.nullable, default_value_ptr)
265270
),
266271
ArgumentTypeHint::Int => Some(
267-
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_LONG, arg.nullable)
272+
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_LONG, arg.nullable, default_value_ptr)
268273
),
269274
ArgumentTypeHint::Float => Some(
270-
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_DOUBLE, arg.nullable)
275+
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_DOUBLE, arg.nullable, default_value_ptr)
271276
),
272277
ArgumentTypeHint::String => Some(
273-
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_STRING, arg.nullable)
278+
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_STRING, arg.nullable, default_value_ptr)
274279
),
275280
ArgumentTypeHint::Array => Some(
276-
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_ARRAY, arg.nullable)
281+
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_ARRAY, arg.nullable, default_value_ptr)
277282
),
278283
ArgumentTypeHint::Object => Some(
279-
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_OBJECT, arg.nullable)
284+
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_OBJECT, arg.nullable, std::ptr::null()) //default value not supported
280285
),
281286
ArgumentTypeHint::Callable => Some(
282-
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_CALLABLE, arg.nullable)
287+
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_CALLABLE, arg.nullable, std::ptr::null()) //default value not supported
288+
),
289+
ArgumentTypeHint::Iterable => Some(
290+
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_ITERABLE, arg.nullable, default_value_ptr)
283291
),
284-
ArgumentTypeHint::Iterable => {
285-
Some( phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_ITERABLE, arg.nullable) )
286-
},
287292
ArgumentTypeHint::Mixed => {
288293
if PHP_MAJOR_VERSION < 8 {
289294
None
290295
} else {
291-
Some(phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), 0, true))
296+
Some(phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_MIXED, true, default_value_ptr))
292297
}
293298
},
294299
ArgumentTypeHint::ClassEntry(class_name) => {
@@ -455,7 +460,7 @@ pub struct Argument {
455460
pass_by_ref: bool,
456461
required: bool,
457462
nullable: bool,
458-
//default_value: Option<zval>, //TODO
463+
default_value: Option<CString>,
459464
}
460465

461466
impl Argument {
@@ -468,6 +473,7 @@ impl Argument {
468473
pass_by_ref: false,
469474
required: true,
470475
nullable: false,
476+
default_value: None,
471477
}
472478
}
473479

@@ -480,6 +486,7 @@ impl Argument {
480486
pass_by_ref: true,
481487
required: true,
482488
nullable: false,
489+
default_value: None,
483490
}
484491
}
485492

@@ -492,6 +499,7 @@ impl Argument {
492499
pass_by_ref: false,
493500
required: false,
494501
nullable: false,
502+
default_value: None,
495503
}
496504
}
497505

@@ -504,6 +512,7 @@ impl Argument {
504512
pass_by_ref: true,
505513
required: false,
506514
nullable: false,
515+
default_value: None,
507516
}
508517
}
509518

@@ -514,7 +523,7 @@ impl Argument {
514523
}
515524

516525
/// Allow type-hint to be nullable
517-
pub fn nullable(mut self) -> Self {
526+
pub fn allow_null(mut self) -> Self {
518527
self.nullable = true;
519528
self
520529
}
@@ -530,6 +539,13 @@ impl Argument {
530539
self.required = false;
531540
self
532541
}
542+
543+
/// Argument default value (always a String)
544+
pub fn with_default_value(mut self, default_value: CString) -> Self {
545+
self.default_value = Some(default_value);
546+
self.required = false; // important!
547+
self
548+
}
533549
}
534550

535551
/// Function or method return type.

phper/src/types.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010

1111
//! Apis relate to PHP types.
1212
13-
use crate::{
14-
sys::*,
15-
};
13+
use crate::sys::*;
1614
use derive_more::From;
1715
use std::{
1816
ffi::CStr,

tests/integration/src/typehints.rs

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,30 @@ use phper::{
1717
types::{ArgumentTypeHint, ReturnTypeHint},
1818
values::ZVal,
1919
};
20+
use std::ffi::CString;
2021

2122
const I_FOO: &str = r"IntegrationTest\TypeHints\IFoo";
2223

2324
pub fn integrate(module: &mut Module) {
2425
let i_foo = module.add_interface(make_i_foo_interface());
2526
let foo_class = module.add_class(make_foo_class(i_foo.clone()));
26-
let _ = module.add_class(make_b_class(foo_class.clone(), i_foo.clone()));
27-
let _ = module.add_class(make_foo_handler());
28-
let _ = module.add_class(make_arg_typehint_class());
29-
let _ = module.add_class(make_return_typehint_class());
27+
module.add_class(make_b_class(foo_class.clone(), i_foo.clone()));
28+
module.add_class(make_foo_handler());
29+
module.add_class(make_arg_typehint_class());
30+
module.add_class(make_return_typehint_class());
31+
module.add_class(make_arg_default_value_class());
32+
module.add_function("integration_function_typehints", |_| {
33+
phper::ok(())
34+
},
35+
)
36+
.argument(Argument::by_val("s").with_type_hint(ArgumentTypeHint::String).with_default_value(CString::new("'foobarbaz'").unwrap()))
37+
.argument(Argument::by_val("i").with_type_hint(ArgumentTypeHint::Int).with_default_value(CString::new("42").unwrap()))
38+
.argument(Argument::by_val("f").with_type_hint(ArgumentTypeHint::Float).with_default_value(CString::new("7.89").unwrap()))
39+
.argument(Argument::by_val("b").with_type_hint(ArgumentTypeHint::Bool).with_default_value(CString::new("true").unwrap()))
40+
.argument(Argument::by_val("a").with_type_hint(ArgumentTypeHint::Array).with_default_value(CString::new("['a'=>'b']").unwrap()))
41+
.argument(Argument::by_val("m").with_type_hint(ArgumentTypeHint::Mixed).with_default_value(CString::new("1.23").unwrap()))
42+
.return_type(ReturnType::by_val(ReturnTypeHint::Void));
43+
3044
}
3145

3246
fn make_i_foo_interface() -> InterfaceEntity {
@@ -123,7 +137,7 @@ fn make_arg_typehint_class() ->ClassEntity<()> {
123137
let _ = arguments[0].expect_z_str()?.to_str()?.to_string();
124138
phper::ok(())
125139
})
126-
.argument(Argument::by_val("string_value").with_type_hint(ArgumentTypeHint::String).nullable());
140+
.argument(Argument::by_val("string_value").with_type_hint(ArgumentTypeHint::String).allow_null());
127141

128142
// Bool tests
129143
class
@@ -138,7 +152,7 @@ fn make_arg_typehint_class() ->ClassEntity<()> {
138152
let _ = arguments[0].as_bool();
139153
phper::ok(())
140154
})
141-
.argument(Argument::by_val("bool_value").with_type_hint(ArgumentTypeHint::Bool).nullable());
155+
.argument(Argument::by_val("bool_value").with_type_hint(ArgumentTypeHint::Bool).allow_null());
142156

143157
class
144158
.add_method("testBoolOptional", Visibility::Public, move |_, arguments| {
@@ -160,7 +174,7 @@ fn make_arg_typehint_class() ->ClassEntity<()> {
160174
let _ = arguments[0].expect_long()?;
161175
phper::ok(())
162176
})
163-
.argument(Argument::by_val("int_value").with_type_hint(ArgumentTypeHint::Int).nullable());
177+
.argument(Argument::by_val("int_value").with_type_hint(ArgumentTypeHint::Int).allow_null());
164178

165179
class
166180
.add_method("testIntOptional", Visibility::Public, move |_, arguments| {
@@ -189,7 +203,7 @@ fn make_arg_typehint_class() ->ClassEntity<()> {
189203
let _ = arguments[0].expect_double()?;
190204
phper::ok(())
191205
})
192-
.argument(Argument::by_val("float_value").with_type_hint(ArgumentTypeHint::Float).nullable());
206+
.argument(Argument::by_val("float_value").with_type_hint(ArgumentTypeHint::Float).allow_null());
193207

194208
// Array tests
195209
class
@@ -211,7 +225,7 @@ fn make_arg_typehint_class() ->ClassEntity<()> {
211225
let _ = arguments[0].expect_z_arr()?;
212226
phper::ok(())
213227
})
214-
.argument(Argument::by_val("array_value").with_type_hint(ArgumentTypeHint::Array).nullable());
228+
.argument(Argument::by_val("array_value").with_type_hint(ArgumentTypeHint::Array).allow_null());
215229

216230
// Mixed tests
217231
class
@@ -231,7 +245,7 @@ fn make_arg_typehint_class() ->ClassEntity<()> {
231245
.add_method("testCallableNullable", Visibility::Public, move |_, _| {
232246
phper::ok(())
233247
})
234-
.argument(Argument::by_val("callable_value").with_type_hint(ArgumentTypeHint::Callable).nullable());
248+
.argument(Argument::by_val("callable_value").with_type_hint(ArgumentTypeHint::Callable).allow_null());
235249

236250
class
237251
.add_method("testCallableOptional", Visibility::Public, move |_, _| {
@@ -249,7 +263,7 @@ fn make_arg_typehint_class() ->ClassEntity<()> {
249263
.add_method("testObjectNullable", Visibility::Public, move |_, _| {
250264
phper::ok(())
251265
})
252-
.argument(Argument::by_val("object_value").with_type_hint(ArgumentTypeHint::Object).nullable());
266+
.argument(Argument::by_val("object_value").with_type_hint(ArgumentTypeHint::Object).allow_null());
253267

254268
class
255269
.add_method("testObjectOptional", Visibility::Public, move |_, _| {
@@ -267,7 +281,7 @@ fn make_arg_typehint_class() ->ClassEntity<()> {
267281
.add_method("testIterableNullable", Visibility::Public, move |_, _| {
268282
phper::ok(())
269283
})
270-
.argument(Argument::by_val("iterable_value").with_type_hint(ArgumentTypeHint::Iterable).nullable());
284+
.argument(Argument::by_val("iterable_value").with_type_hint(ArgumentTypeHint::Iterable).allow_null());
271285

272286
class
273287
.add_method("testIterableOptional", Visibility::Public, move |_, _| {
@@ -291,7 +305,7 @@ fn make_arg_typehint_class() ->ClassEntity<()> {
291305
.add_method("testClassEntryNullable", Visibility::Public, move |_, _| {
292306
phper::ok(())
293307
})
294-
.argument(Argument::by_val("classentry").with_type_hint(ArgumentTypeHint::ClassEntry(String::from(I_FOO))).nullable());
308+
.argument(Argument::by_val("classentry").with_type_hint(ArgumentTypeHint::ClassEntry(String::from(I_FOO))).allow_null());
295309

296310
class
297311
.add_method("testClassEntryOptional", Visibility::Public, move |_, _| {
@@ -436,5 +450,65 @@ fn make_return_typehint_class() ->ClassEntity<()> {
436450
})
437451
.return_type(ReturnType::by_val(ReturnTypeHint::Void));
438452

453+
class
454+
}
455+
456+
fn make_arg_default_value_class() ->ClassEntity<()> {
457+
let mut class = ClassEntity::new(r"IntegrationTest\TypeHints\ArgumentDefaultValueTest");
458+
459+
class
460+
.add_method("stringDefault", Visibility::Public, move |_, _| {
461+
phper::ok(())
462+
})
463+
.argument(Argument::by_val("string_value").with_type_hint(ArgumentTypeHint::String).with_default_value(CString::new("'foobarbaz'").unwrap())); //NB single quotes!
464+
465+
class
466+
.add_method("stringConstantDefault", Visibility::Public, move |_, _| {
467+
phper::ok(())
468+
})
469+
.argument(Argument::by_val("const_value").with_type_hint(ArgumentTypeHint::String).with_default_value(CString::new("PHP_VERSION").unwrap()));
470+
471+
class
472+
.add_method("boolDefaultTrue", Visibility::Public, move |_, _| {
473+
phper::ok(())
474+
})
475+
.argument(Argument::by_val("bool_value").with_type_hint(ArgumentTypeHint::Bool).with_default_value(CString::new("true").unwrap()));
476+
477+
class
478+
.add_method("boolDefaultFalse", Visibility::Public, move |_, _| {
479+
phper::ok(())
480+
})
481+
.argument(Argument::by_val("bool_value").with_type_hint(ArgumentTypeHint::Bool).with_default_value(CString::new("false").unwrap()));
482+
483+
class
484+
.add_method("intDefault", Visibility::Public, move |_, _| {
485+
phper::ok(())
486+
})
487+
.argument(Argument::by_val("int_value").with_type_hint(ArgumentTypeHint::Int).with_default_value(CString::new("42").unwrap()));
488+
489+
class
490+
.add_method("floatDefault", Visibility::Public, move |_, _| {
491+
phper::ok(())
492+
})
493+
.argument(Argument::by_val("float_value").with_type_hint(ArgumentTypeHint::Float).with_default_value(CString::new("3.14159").unwrap()));
494+
495+
class
496+
.add_method("arrayDefault", Visibility::Public, move |_, _| {
497+
phper::ok(())
498+
})
499+
.argument(Argument::by_val("array_value").with_type_hint(ArgumentTypeHint::Array).with_default_value(CString::new("['a' => 'b']").unwrap()));
500+
501+
class
502+
.add_method("iterableDefault", Visibility::Public, move |_, _| {
503+
phper::ok(())
504+
})
505+
.argument(Argument::by_val("iterable_value").with_type_hint(ArgumentTypeHint::Iterable).with_default_value(CString::new("[0 => 1]").unwrap()));
506+
507+
class
508+
.add_method("mixedDefault", Visibility::Public, move |_, _| {
509+
phper::ok(())
510+
})
511+
.argument(Argument::by_val("mixed_value").with_type_hint(ArgumentTypeHint::Mixed).with_default_value(CString::new("999").unwrap()));
512+
439513
class
440514
}

0 commit comments

Comments
 (0)