Skip to content

Commit 564566e

Browse files
committed
implement class/interface typehints
1 parent 6e5a294 commit 564566e

File tree

5 files changed

+180
-116
lines changed

5 files changed

+180
-116
lines changed

phper/src/functions.rs

Lines changed: 58 additions & 54 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::{TypeHint, TypeInfo},
22+
types::{ArgumentTypeHint, ReturnTypeHint, TypeInfo},
2323
utils::ensure_end_with_zero,
2424
values::{ExecuteData, ZVal},
2525
};
@@ -149,86 +149,90 @@ impl FunctionEntry {
149149
let require_arg_count = arguments.iter().filter(|arg| arg.required).count();
150150

151151
if let Some(return_type) = return_type {
152-
infos.push(phper_zend_begin_arg_with_return_type_info_ex(
153-
return_type.ret_by_ref,
154-
require_arg_count,
155-
return_type.type_info.into_raw(),
156-
return_type.allow_null,
157-
));
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(
170+
return_type.ret_by_ref,
171+
require_arg_count,
172+
return_type.type_info.into_raw(),
173+
return_type.allow_null,
174+
));
175+
}
158176
} else {
159177
infos.push(phper_zend_begin_arg_info_ex(false, require_arg_count));
160178
}
161179

162180
for arg in arguments {
163-
//if let Some(ref type_hint) = arg.type_hint {
164-
// println!("argument has a type-hint");
165-
//}
166-
//infos.push(phper_zend_arg_info(
167-
// arg.pass_by_ref,
168-
// arg.name.as_ptr().cast(),
169-
//));
170-
171181
let arg_info = if let Some(ref type_hint) = arg.type_hint {
172182
match type_hint {
173-
TypeHint::Null => Some(
183+
ArgumentTypeHint::Null => Some(
174184
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_NULL, true)
175185
),
176-
TypeHint::Bool => Some(
186+
ArgumentTypeHint::Bool => Some(
177187
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), _IS_BOOL, arg.nullable)
178188
),
179-
TypeHint::Int => Some(
189+
ArgumentTypeHint::Int => Some(
180190
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_LONG, arg.nullable)
181191
),
182-
TypeHint::Float => Some(
192+
ArgumentTypeHint::Float => Some(
183193
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_DOUBLE, arg.nullable)
184194
),
185-
TypeHint::String => Some(
195+
ArgumentTypeHint::String => Some(
186196
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_STRING, arg.nullable)
187197
),
188-
TypeHint::Array => Some(
198+
ArgumentTypeHint::Array => Some(
189199
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_ARRAY, arg.nullable)
190200
),
191-
TypeHint::Object => Some(
201+
ArgumentTypeHint::Object => Some(
192202
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_OBJECT, arg.nullable)
193203
),
194-
TypeHint::Callable => Some(
204+
ArgumentTypeHint::Callable => Some(
195205
phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_CALLABLE, arg.nullable)
196206
),
197-
TypeHint::Iterable => {
207+
ArgumentTypeHint::Iterable => {
198208
// For iterable typehint - this might need a special handler or could use IS_ITERABLE
199209
// if defined in your PHP version
200210
Some( phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), IS_ITERABLE, arg.nullable) )
201211
},
202-
TypeHint::Mixed => {
212+
ArgumentTypeHint::Mixed => {
203213
// Mixed type - depends on PHP version, for PHP 8+ this might be a special constant
204214
// For PHP 8.0+, there might be a specific constant for mixed
205215
Some( phper_zend_arg_info_with_type(arg.pass_by_ref, arg.name.as_ptr(), 0, true) ) // 0 might be replaced with appropriate constant
206216
},
207-
TypeHint::ClassEntry(s) => {
208-
println!("typehint for class entry: {}", s);
209-
// Get the class name from the class entry
210-
/*let class_entry = f();
211-
let class_name = class_entry.get_name(); // Assuming get_name() returns a &str
212-
let temp_class_name = CString::new(class_name).unwrap();
213-
let class_name_c = Some(temp_class_name);
214-
215-
Some( phper_zend_arg_obj_info(
216-
arg.pass_by_ref,
217-
arg.name.as_ptr(),
218-
class_name_c.as_ref().unwrap().as_ptr(),
219-
arg.nullable
220-
))*/
221-
None
222-
},
223-
// everything from here is not relevant to function type-hints
224-
TypeHint::Void => {
225-
None
226-
},
227-
TypeHint::_Self => {
228-
None
229-
},
230-
TypeHint::Never => {
231-
None
217+
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+
}
232236
},
233237
}
234238
} else {
@@ -385,7 +389,7 @@ impl MethodEntity {
385389
/// Function or method argument info.
386390
pub struct Argument {
387391
name: CString,
388-
type_hint: Option<TypeHint>,
392+
type_hint: Option<ArgumentTypeHint>,
389393
pass_by_ref: bool,
390394
required: bool,
391395
nullable: bool,
@@ -442,7 +446,7 @@ impl Argument {
442446
}
443447

444448
/// Add a type-hint to the argument
445-
pub fn with_type_hint(mut self, type_hint: TypeHint) -> Self {
449+
pub fn with_type_hint(mut self, type_hint: ArgumentTypeHint) -> Self {
446450
self.type_hint = Some(type_hint);
447451
self
448452
}
@@ -469,7 +473,7 @@ impl Argument {
469473
/// Function or method return type.
470474
pub struct ReturnType {
471475
type_info: TypeInfo,
472-
type_hint: Option<TypeHint>,
476+
type_hint: Option<ReturnTypeHint>,
473477
ret_by_ref: bool,
474478
allow_null: bool,
475479
}
@@ -505,7 +509,7 @@ impl ReturnType {
505509
}
506510

507511
/// Add a type-hint to the return type
508-
pub fn with_type_hint(mut self, type_hint: TypeHint) -> Self {
512+
pub fn with_type_hint(mut self, type_hint: ReturnTypeHint) -> Self {
509513
self.type_hint = Some(type_hint);
510514
self
511515
}

phper/src/types.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,9 @@ impl From<&[u8]> for Scalar {
220220
}
221221
}
222222

223-
/// Wrapper of PHP typehints, used for arguments and return types.
223+
/// PHP argument typehints
224224
#[derive(Debug, Clone, PartialEq, Eq)]
225-
pub enum TypeHint {
225+
pub enum ArgumentTypeHint {
226226
/// null typehint
227227
Null,
228228
/// bool typehint
@@ -243,12 +243,39 @@ pub enum TypeHint {
243243
Iterable,
244244
/// mixed typehint (php 8.0+)
245245
Mixed,
246-
/// void typehint
247-
Void,
248-
/// self typehint
249-
_Self,
250246
/// ClassEntry typehint (class, interface)
251247
ClassEntry(String),
252-
/// never typehint (php 8.1+)
248+
}
249+
250+
/// PHP return typehints
251+
#[derive(Debug, Clone, PartialEq, Eq)]
252+
pub enum ReturnTypeHint {
253+
/// null typehint
254+
Null,
255+
/// bool typehint
256+
Bool,
257+
/// int typehint
258+
Int,
259+
/// float typehint
260+
Float,
261+
/// string typehint
262+
String,
263+
/// array typehint
264+
Array,
265+
/// object typehint
266+
Object,
267+
/// callable typehint
268+
Callable,
269+
/// iterable typehint
270+
Iterable,
271+
/// mixed typehint (php 8.0+)
272+
Mixed,
273+
/// ClassEntry typehint (class, interface)
274+
ClassEntry(String),
275+
/// self typehint
276+
_Self,
277+
/// never typehint (8.2+)
253278
Never,
279+
/// void typehint
280+
Void,
254281
}

tests/integration/src/classes.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use phper::{
1616
functions::Argument,
1717
modules::Module,
1818
values::ZVal,
19+
types::ReturnTypeHint,
1920
};
2021
use std::{collections::HashMap, convert::Infallible};
2122

@@ -210,6 +211,6 @@ fn integrate_stringable(module: &mut Module) {
210211
cls.add_method("__toString", Visibility::Public, |_this, _: &mut [ZVal]| {
211212
phper::ok("string")
212213
})
213-
.return_type(ReturnType::by_val(TypeInfo::STRING));
214+
.return_type(ReturnType::by_val(TypeInfo::STRING).with_type_hint(ReturnTypeHint::String));
214215
module.add_class(cls);
215216
}

0 commit comments

Comments
 (0)