Skip to content

Commit c205332

Browse files
committed
return type hints
1 parent 564566e commit c205332

File tree

6 files changed

+332
-77
lines changed

6 files changed

+332
-77
lines changed

phper-sys/php_wrapper.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,28 @@ phper_zend_begin_arg_with_return_type_info_ex(bool return_reference,
480480
#undef const
481481
}
482482

483+
zend_internal_arg_info
484+
phper_zend_begin_arg_with_return_obj_info_ex(bool return_reference,
485+
uintptr_t required_num_args,
486+
const char* class_name,
487+
bool allow_null) {
488+
#define static
489+
#define const
490+
#if PHP_VERSION_ID >= 70200
491+
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(info, return_reference,
492+
required_num_args,
493+
class_name, allow_null)
494+
#else
495+
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(
496+
info, return_reference, required_num_args,
497+
class_name, NULL, allow_null)
498+
#endif
499+
ZEND_END_ARG_INFO()
500+
return info[0];
501+
#undef static
502+
#undef const
503+
}
504+
483505
zend_internal_arg_info phper_zend_arg_info(bool pass_by_ref, const char *name) {
484506
zend_internal_arg_info info[] = {ZEND_ARG_INFO(pass_by_ref, )};
485507
info[0].name = name;

phper/src/functions.rs

Lines changed: 122 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
objects::{StateObj, ZObj, ZObject},
2020
strings::{ZStr, ZString},
2121
sys::*,
22-
types::{ArgumentTypeHint, ReturnTypeHint, TypeInfo},
22+
types::{ArgumentTypeHint, ReturnTypeHint},
2323
utils::ensure_end_with_zero,
2424
values::{ExecuteData, ZVal},
2525
};
@@ -148,34 +148,111 @@ impl FunctionEntry {
148148

149149
let require_arg_count = arguments.iter().filter(|arg| arg.required).count();
150150

151-
if let Some(return_type) = return_type {
152-
if let Some(type_hint) = &return_type.type_hint {
153-
let ret_info = match type_hint {
154-
ReturnTypeHint::String => phper_zend_begin_arg_with_return_type_info_ex(
155-
return_type.ret_by_ref,
156-
require_arg_count,
157-
IS_STRING,
158-
return_type.allow_null,
159-
),
160-
_ => phper_zend_begin_arg_with_return_type_info_ex(
161-
return_type.ret_by_ref,
162-
require_arg_count,
163-
return_type.type_info.into_raw(),
164-
return_type.allow_null,
165-
),
166-
};
167-
infos.push(ret_info);
168-
} else {
169-
infos.push(phper_zend_begin_arg_with_return_type_info_ex(
151+
let return_info = if let Some(return_type) = return_type {
152+
match &return_type.type_hint {
153+
ReturnTypeHint::Null => Some(phper_zend_begin_arg_with_return_type_info_ex(
154+
false,
155+
require_arg_count,
156+
IS_NULL,
157+
true,
158+
)),
159+
ReturnTypeHint::Bool => Some(phper_zend_begin_arg_with_return_type_info_ex(
160+
return_type.ret_by_ref,
161+
require_arg_count,
162+
_IS_BOOL,
163+
return_type.allow_null,
164+
)),
165+
ReturnTypeHint::Int => Some(phper_zend_begin_arg_with_return_type_info_ex(
170166
return_type.ret_by_ref,
171167
require_arg_count,
172-
return_type.type_info.into_raw(),
168+
IS_LONG,
173169
return_type.allow_null,
174-
));
170+
)),
171+
ReturnTypeHint::Float => Some(phper_zend_begin_arg_with_return_type_info_ex(
172+
return_type.ret_by_ref,
173+
require_arg_count,
174+
IS_DOUBLE,
175+
return_type.allow_null,
176+
)),
177+
ReturnTypeHint::String => Some(phper_zend_begin_arg_with_return_type_info_ex(
178+
return_type.ret_by_ref,
179+
require_arg_count,
180+
IS_STRING,
181+
return_type.allow_null,
182+
)),
183+
ReturnTypeHint::Array => Some(phper_zend_begin_arg_with_return_type_info_ex(
184+
return_type.ret_by_ref,
185+
require_arg_count,
186+
IS_ARRAY,
187+
return_type.allow_null,
188+
)),
189+
ReturnTypeHint::Object => Some(phper_zend_begin_arg_with_return_type_info_ex(
190+
return_type.ret_by_ref,
191+
require_arg_count,
192+
IS_OBJECT,
193+
return_type.allow_null,
194+
)),
195+
ReturnTypeHint::Callable => Some(phper_zend_begin_arg_with_return_type_info_ex(
196+
return_type.ret_by_ref,
197+
require_arg_count,
198+
IS_CALLABLE,
199+
return_type.allow_null,
200+
)),
201+
ReturnTypeHint::Iterable => Some(phper_zend_begin_arg_with_return_type_info_ex(
202+
return_type.ret_by_ref,
203+
require_arg_count,
204+
IS_ITERABLE,
205+
return_type.allow_null,
206+
)),
207+
ReturnTypeHint::Mixed => {
208+
if PHP_MAJOR_VERSION < 8 {
209+
None
210+
} else {
211+
Some(phper_zend_begin_arg_with_return_type_info_ex(
212+
return_type.ret_by_ref,
213+
require_arg_count,
214+
IS_MIXED,
215+
true,
216+
))
217+
}
218+
}
219+
ReturnTypeHint::Never => {
220+
if PHP_MAJOR_VERSION < 8 || (PHP_MAJOR_VERSION == 8 && PHP_MINOR_VERSION <= 1) {
221+
None
222+
} else {
223+
Some(phper_zend_begin_arg_with_return_type_info_ex(
224+
return_type.ret_by_ref,
225+
require_arg_count,
226+
IS_NEVER,
227+
false,
228+
))
229+
}
230+
}
231+
ReturnTypeHint::Void => Some(phper_zend_begin_arg_with_return_type_info_ex(
232+
return_type.ret_by_ref,
233+
require_arg_count,
234+
IS_VOID,
235+
false,
236+
)),
237+
ReturnTypeHint::ClassEntry(class_name) => {
238+
let class_name =
239+
CString::new(class_name.clone()).expect("CString::new failed");
240+
Some(phper_zend_begin_arg_with_return_obj_info_ex(
241+
return_type.ret_by_ref,
242+
require_arg_count,
243+
class_name.as_ptr(),
244+
return_type.allow_null,
245+
))
246+
}
175247
}
176248
} else {
177-
infos.push(phper_zend_begin_arg_info_ex(false, require_arg_count));
178-
}
249+
None
250+
};
251+
252+
infos.push(return_info.unwrap_or_else(|| {
253+
phper_zend_begin_arg_info_ex(false, require_arg_count)
254+
}));
255+
179256

180257
for arg in arguments {
181258
let arg_info = if let Some(ref type_hint) = arg.type_hint {
@@ -205,47 +282,32 @@ impl FunctionEntry {
205282
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_CALLABLE, arg.nullable)
206283
),
207284
ArgumentTypeHint::Iterable => {
208-
// For iterable typehint - this might need a special handler or could use IS_ITERABLE
209-
// if defined in your PHP version
210285
Some( phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_ITERABLE, arg.nullable) )
211286
},
212287
ArgumentTypeHint::Mixed => {
213-
// Mixed type - depends on PHP version, for PHP 8+ this might be a special constant
214-
// For PHP 8.0+, there might be a specific constant for mixed
215-
Some( phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), 0, true) ) // 0 might be replaced with appropriate constant
288+
if PHP_MAJOR_VERSION < 8 {
289+
None
290+
} else {
291+
Some(phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), 0, true))
292+
}
216293
},
217294
ArgumentTypeHint::ClassEntry(class_name) => {
218-
println!("typehint for class entry: {}", class_name);
219-
match CString::new(class_name.clone()) {
220-
Ok(c_class_name) => {
221-
// Defer actual class entry lookup
222-
// Use zend_arg_obj_info with just the class name string
223-
Some(phper_zend_arg_obj_info(
224-
arg.pass_by_ref, // Whether argument is passed by reference
225-
arg.name.as_ptr(), // Argument name
226-
c_class_name.as_ptr(), // Class name as C string
227-
arg.nullable // Whether the argument can be null
228-
))
229-
},
230-
Err(_) => {
231-
// Handle invalid class name (contains null byte)
232-
eprintln!("Invalid class name: {}", class_name);
233-
None
234-
}
235-
}
295+
let c_class_name = CString::new(class_name.clone()).expect("CString::new failed");
296+
Some(phper_zend_arg_obj_info(
297+
arg.pass_by_ref,
298+
arg.name.as_ptr(),
299+
c_class_name.as_ptr(),
300+
arg.nullable
301+
))
236302
},
237303
}
238304
} else {
239305
None
240306
};
241307

242-
if let Some(arg_info) = arg_info {
243-
infos.push(arg_info);
244-
} else {
245-
// No type hint, use the original function
246-
let default_arg_info = phper_zend_arg_info(arg.pass_by_ref, arg.name.as_ptr());
247-
infos.push(default_arg_info);
248-
}
308+
infos.push(arg_info.unwrap_or_else(|| {
309+
phper_zend_arg_info(arg.pass_by_ref, arg.name.as_ptr())
310+
}));
249311
}
250312

251313
infos.push(zeroed::<zend_internal_arg_info>());
@@ -472,30 +534,27 @@ impl Argument {
472534

473535
/// Function or method return type.
474536
pub struct ReturnType {
475-
type_info: TypeInfo,
476-
type_hint: Option<ReturnTypeHint>,
537+
type_hint: ReturnTypeHint,
477538
ret_by_ref: bool,
478539
allow_null: bool,
479540
}
480541

481542
impl ReturnType {
482543
/// Indicate the return type is return by value.
483544
#[inline]
484-
pub fn by_val(type_info: TypeInfo) -> Self {
545+
pub fn by_val(type_hint: ReturnTypeHint) -> Self {
485546
Self {
486-
type_info,
487-
type_hint: None,
547+
type_hint,
488548
ret_by_ref: false,
489549
allow_null: false,
490550
}
491551
}
492552

493553
/// Indicate the return type is return by reference.
494554
#[inline]
495-
pub fn by_ref(type_info: TypeInfo) -> Self {
555+
pub fn by_ref(type_hint: ReturnTypeHint) -> Self {
496556
Self {
497-
type_info,
498-
type_hint: None,
557+
type_hint,
499558
ret_by_ref: true,
500559
allow_null: false,
501560
}
@@ -510,7 +569,7 @@ impl ReturnType {
510569

511570
/// Add a type-hint to the return type
512571
pub fn with_type_hint(mut self, type_hint: ReturnTypeHint) -> Self {
513-
self.type_hint = Some(type_hint);
572+
self.type_hint = type_hint;
514573
self
515574
}
516575
}

phper/src/types.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,7 @@ pub enum ReturnTypeHint {
272272
Mixed,
273273
/// ClassEntry typehint (class, interface)
274274
ClassEntry(String),
275-
/// self typehint
276-
_Self,
277-
/// never typehint (8.2+)
275+
/// never typehint (8.1+)
278276
Never,
279277
/// void typehint
280278
Void,

tests/integration/src/classes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,13 +204,13 @@ fn integrate_static_props(module: &mut Module) {
204204

205205
#[cfg(phper_major_version = "8")]
206206
fn integrate_stringable(module: &mut Module) {
207-
use phper::{functions::ReturnType, types::TypeInfo};
207+
use phper::{functions::ReturnType};
208208

209209
let mut cls = ClassEntity::new(r"IntegrationTest\FooString");
210210
cls.implements(|| ClassEntry::from_globals("Stringable").unwrap());
211211
cls.add_method("__toString", Visibility::Public, |_this, _: &mut [ZVal]| {
212212
phper::ok("string")
213213
})
214-
.return_type(ReturnType::by_val(TypeInfo::STRING).with_type_hint(ReturnTypeHint::String));
214+
.return_type(ReturnType::by_val(ReturnTypeHint::String));
215215
module.add_class(cls);
216216
}

0 commit comments

Comments
 (0)