Skip to content

Commit 6995d6a

Browse files
committed
adding class+interface constants
allow ClassEntity.add_constant and InterfaceEntity.add_constant, which will call zend_declare_class_constant_*
1 parent 3a7e236 commit 6995d6a

File tree

5 files changed

+151
-1
lines changed

5 files changed

+151
-1
lines changed

phper-doc/doc/_06_module/_06_register_class/index.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@ foo.add_static_method(
102102
).argument(Argument::by_val("name"));
103103
```
104104

105+
## Add constants
106+
Interfaces can have public constants. Value can be string|int|bool|float|null.
107+
108+
```rust,no_run
109+
use phper::classes::ClassEntity;
110+
111+
let mut foo = ClassEntity::new("Foo");
112+
foo.add_constant("ONE", "one");
113+
foo.add_constant("TWO", 2);
114+
foo.add_constant("THREE", 3.0);
115+
```
116+
105117
## Handle state
106118

107119
> The `ClassEntity` represents the class entry hold the state as generic type,

phper-doc/doc/_06_module/_07_register_interface/index.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,15 @@ foo.add_method("doSomethings").argument(Argument::by_val("name"));
7272
```
7373

7474
Note that abstract has no method body, so you don't need to add the handler to the method.
75+
76+
## Add constants
77+
Interfaces can have public constants. Value can be string|int|bool|float|null.
78+
79+
```rust,no_run
80+
use phper::classes::InterfaceEntity;
81+
82+
let mut foo = InterfaceEntity::new("Foo");
83+
foo.add_constant("ONE", "one");
84+
foo.add_constant("TWO", 2);
85+
foo.add_constant("THREE", 3.0);
86+
```

phper/src/classes.rs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::{
2424
};
2525
use std::{
2626
any::Any,
27-
ffi::{c_void, CString},
27+
ffi::{c_void, CString, c_char},
2828
fmt::Debug,
2929
marker::PhantomData,
3030
mem::{replace, size_of, zeroed, ManuallyDrop},
@@ -377,6 +377,7 @@ pub struct ClassEntity<T: 'static> {
377377
property_entities: Vec<PropertyEntity>,
378378
parent: Option<Box<dyn Fn() -> &'static ClassEntry>>,
379379
interfaces: Vec<Box<dyn Fn() -> &'static ClassEntry>>,
380+
constants: Vec<ConstantEntity>,
380381
bind_class: Option<&'static StaticStateClass<T>>,
381382
state_cloner: Option<Rc<StateCloner>>,
382383
_p: PhantomData<(*mut (), T)>,
@@ -414,6 +415,7 @@ impl<T: 'static> ClassEntity<T> {
414415
property_entities: Vec::new(),
415416
parent: None,
416417
interfaces: Vec::new(),
418+
constants: Vec::new(),
417419
bind_class: None,
418420
state_cloner: None,
419421
_p: PhantomData,
@@ -487,6 +489,13 @@ impl<T: 'static> ClassEntity<T> {
487489
self.property_entities.push(entity);
488490
}
489491

492+
/// Add constant to class
493+
pub fn add_constant(
494+
&mut self, name: impl Into<String>, value: impl Into<Scalar>) {
495+
let constant = ConstantEntity::new(name, value);
496+
self.constants.push(constant);
497+
}
498+
490499
/// Register class to `extends` the parent class.
491500
///
492501
/// *Because in the `MINIT` phase, the class starts to register, so the*
@@ -603,6 +612,10 @@ impl<T: 'static> ClassEntity<T> {
603612
zend_class_implements(class_ce, 1, interface_ce);
604613
}
605614

615+
for constant in &self.constants {
616+
add_class_constant(class_ce, constant);
617+
}
618+
606619
*phper_get_create_object(class_ce) = Some(create_object);
607620

608621
class_ce
@@ -680,6 +693,7 @@ unsafe extern "C" fn class_init_handler(
680693
pub struct InterfaceEntity {
681694
interface_name: CString,
682695
method_entities: Vec<MethodEntity>,
696+
constants: Vec<ConstantEntity>,
683697
extends: Vec<Box<dyn Fn() -> &'static ClassEntry>>,
684698
bind_interface: Option<&'static StaticInterface>,
685699
}
@@ -690,6 +704,7 @@ impl InterfaceEntity {
690704
Self {
691705
interface_name: ensure_end_with_zero(interface_name.into()),
692706
method_entities: Vec::new(),
707+
constants: Vec::new(),
693708
extends: Vec::new(),
694709
bind_interface: None,
695710
}
@@ -704,6 +719,13 @@ impl InterfaceEntity {
704719
self.method_entities.last_mut().unwrap()
705720
}
706721

722+
/// Add constant to interface
723+
pub fn add_constant(
724+
&mut self, name: impl Into<String>, value: impl Into<Scalar>) {
725+
let constant = ConstantEntity::new(name, value);
726+
self.constants.push(constant);
727+
}
728+
707729
/// Register interface to `extends` the interfaces, due to the interface can
708730
/// extends multi interface, so this method can be called multi time.
709731
///
@@ -751,6 +773,10 @@ impl InterfaceEntity {
751773
zend_class_implements(class_ce, 1, interface_ce);
752774
}
753775

776+
for constant in &self.constants {
777+
add_class_constant(class_ce, constant);
778+
}
779+
754780
class_ce
755781
}
756782

@@ -773,6 +799,21 @@ unsafe extern "C" fn interface_init_handler(
773799
zend_register_internal_interface(class_ce)
774800
}
775801

802+
/// Builder for registering class/interface constants
803+
pub struct ConstantEntity {
804+
name: String,
805+
value: Scalar,
806+
}
807+
808+
impl ConstantEntity {
809+
fn new(name: impl Into<String>, value: impl Into<Scalar>) -> Self {
810+
Self {
811+
name: name.into(),
812+
value: value.into(),
813+
}
814+
}
815+
}
816+
776817
/// Builder for declare class property.
777818
struct PropertyEntity {
778819
name: String,
@@ -973,6 +1014,54 @@ unsafe fn clone_object_common(object: *mut zend_object) -> *mut zend_object {
9731014
new_object
9741015
}
9751016

1017+
unsafe fn add_class_constant(class_ce: *mut _zend_class_entry, constant: &ConstantEntity) {
1018+
let name_ptr = constant.name.as_ptr() as *const c_char;
1019+
let name_len = constant.name.len();
1020+
unsafe {
1021+
match &constant.value {
1022+
Scalar::Null => zend_declare_class_constant_null(class_ce, name_ptr, name_len),
1023+
Scalar::Bool(b) => zend_declare_class_constant_bool(
1024+
class_ce,
1025+
name_ptr,
1026+
name_len,
1027+
*b as zend_bool,
1028+
),
1029+
Scalar::I64(i) => zend_declare_class_constant_long(
1030+
class_ce,
1031+
name_ptr,
1032+
name_len,
1033+
*i as zend_long,
1034+
),
1035+
Scalar::F64(f) => zend_declare_class_constant_double(
1036+
class_ce,
1037+
name_ptr,
1038+
name_len,
1039+
*f
1040+
),
1041+
Scalar::String(s) => {
1042+
let s_ptr = s.as_ptr() as *mut u8;
1043+
zend_declare_class_constant_stringl(
1044+
class_ce,
1045+
name_ptr,
1046+
name_len,
1047+
s_ptr.cast(),
1048+
s.len(),
1049+
)
1050+
}
1051+
Scalar::Bytes(s) => {
1052+
let s_ptr = s.as_ptr() as *mut u8;
1053+
zend_declare_class_constant_stringl(
1054+
class_ce,
1055+
name_ptr,
1056+
name_len,
1057+
s_ptr.cast(),
1058+
s.len(),
1059+
)
1060+
}
1061+
}
1062+
}
1063+
}
1064+
9761065
unsafe extern "C" fn free_object(object: *mut zend_object) {
9771066
let state_object = StateObj::<()>::from_mut_object_ptr(object);
9781067

tests/integration/src/classes.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn integrate(module: &mut Module) {
2525
integrate_foo(module);
2626
integrate_i_bar(module);
2727
integrate_static_props(module);
28+
integrate_i_constants(module);
2829
#[cfg(phper_major_version = "8")]
2930
integrate_stringable(module);
3031
}
@@ -34,6 +35,12 @@ fn integrate_a(module: &mut Module) {
3435

3536
class.add_property("name", Visibility::Private, "default");
3637
class.add_property("number", Visibility::Private, 100);
38+
class.add_constant("CST_STRING", "foo");
39+
class.add_constant("CST_NULL", ());
40+
class.add_constant("CST_TRUE", true);
41+
class.add_constant("CST_FALSE", false);
42+
class.add_constant("CST_INT", 100);
43+
class.add_constant("CST_FLOAT", 3.14159);
3744

3845
class
3946
.add_method("__construct", Visibility::Public, |this, arguments| {
@@ -158,6 +165,19 @@ fn integrate_i_bar(module: &mut Module) {
158165
module.add_interface(interface);
159166
}
160167

168+
fn integrate_i_constants(module: &mut Module) {
169+
let mut interface = InterfaceEntity::new(r"IntegrationTest\IConstants");
170+
171+
interface.add_constant("CST_STRING", "foo");
172+
interface.add_constant("CST_NULL", ());
173+
interface.add_constant("CST_TRUE", true);
174+
interface.add_constant("CST_FALSE", false);
175+
interface.add_constant("CST_INT", 100);
176+
interface.add_constant("CST_FLOAT", 3.14159);
177+
178+
module.add_interface(interface);
179+
}
180+
161181
fn integrate_static_props(module: &mut Module) {
162182
let mut class = ClassEntity::new("IntegrationTest\\PropsHolder");
163183

tests/integration/tests/php/classes.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,20 @@ class Foo2 extends IntegrationTest\Foo {}
7878
if (PHP_VERSION_ID >= 80000) {
7979
assert_eq(((string) (new IntegrationTest\FooString())), 'string');
8080
}
81+
82+
// Test class constants
83+
assert_eq('foo', IntegrationTest\A::CST_STRING);
84+
assert_eq(null, IntegrationTest\A::CST_NULL);
85+
assert_true(true, IntegrationTest\A::CST_TRUE);
86+
assert_false(false, IntegrationTest\A::CST_FALSE);
87+
assert_eq(100, IntegrationTest\A::CST_INT);
88+
assert_eq(3.14159, IntegrationTest\A::CST_FLOAT);
89+
90+
// Test interface constants
91+
assert_true(interface_exists(IntegrationTest\IConstants::class));
92+
assert_eq('foo', IntegrationTest\IConstants::CST_STRING);
93+
assert_eq(null, IntegrationTest\IConstants::CST_NULL);
94+
assert_true(IntegrationTest\IConstants::CST_TRUE);
95+
assert_false(IntegrationTest\IConstants::CST_FALSE);
96+
assert_eq(100, IntegrationTest\IConstants::CST_INT);
97+
assert_eq(3.14159, IntegrationTest\IConstants::CST_FLOAT);

0 commit comments

Comments
 (0)