Skip to content

Commit 43ef456

Browse files
authored
Support class static properties (#137)
1 parent d578ce2 commit 43ef456

File tree

9 files changed

+121
-29
lines changed

9 files changed

+121
-29
lines changed

.github/workflows/publish.yml

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,61 +47,44 @@ jobs:
4747
args: --manifest-path phper-sys/Cargo.toml
4848
continue-on-error: true
4949

50-
- name: Delay
51-
run: sleep 20
52-
5350
- name: Cargo publish phper-build
5451
uses: actions-rs/cargo@v1
5552
with:
5653
command: publish
5754
args: --manifest-path phper-build/Cargo.toml
5855
continue-on-error: true
5956

60-
- name: Delay
61-
run: sleep 20
62-
6357
- name: Cargo publish phper-macros
6458
uses: actions-rs/cargo@v1
6559
with:
6660
command: publish
6761
args: --manifest-path phper-macros/Cargo.toml
6862
continue-on-error: true
6963

70-
- name: Delay
71-
run: sleep 20
72-
7364
- name: Cargo publish phper-alloc
7465
uses: actions-rs/cargo@v1
7566
with:
7667
command: publish
7768
args: --manifest-path phper-alloc/Cargo.toml
7869
continue-on-error: true
7970

80-
- name: Delay
81-
run: sleep 20
82-
8371
- name: Cargo publish phper-test
8472
uses: actions-rs/cargo@v1
8573
with:
8674
command: publish
8775
args: --manifest-path phper-test/Cargo.toml
8876
continue-on-error: true
8977

90-
- name: Delay
91-
run: sleep 20
92-
9378
- name: Cargo publish phper
9479
uses: actions-rs/cargo@v1
9580
with:
9681
command: publish
9782
args: --manifest-path phper/Cargo.toml
9883
continue-on-error: true
9984

100-
- name: Delay
101-
run: sleep 20
102-
10385
- name: Cargo publish phper-doc
10486
uses: actions-rs/cargo@v1
10587
with:
10688
command: publish
10789
args: --manifest-path phper-doc/Cargo.toml
90+
continue-on-error: true

phper/src/classes.rs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use std::{
2828
ffi::{c_void, CString},
2929
fmt::Debug,
3030
marker::PhantomData,
31-
mem::{size_of, zeroed, ManuallyDrop},
31+
mem::{replace, size_of, zeroed, ManuallyDrop},
3232
os::raw::c_int,
3333
ptr::null_mut,
3434
rc::Rc,
@@ -181,6 +181,37 @@ impl ClassEntry {
181181
pub fn is_instance_of(&self, parent: &ClassEntry) -> bool {
182182
unsafe { phper_instanceof_function(self.as_ptr(), parent.as_ptr()) }
183183
}
184+
185+
/// Get the static property by name of class.
186+
///
187+
/// Return None when static property hasn't register by
188+
/// [ClassEntity::add_static_property].
189+
pub fn get_static_property(&self, name: impl AsRef<str>) -> Option<&ZVal> {
190+
let ptr = self.as_ptr() as *mut _;
191+
let prop = Self::inner_get_static_property(ptr, name);
192+
unsafe { ZVal::try_from_ptr(prop) }
193+
}
194+
195+
/// Set the static property by name of class.
196+
///
197+
/// Return `Some(x)` where `x` is the previous value of static property, or
198+
/// return `None` when static property hasn't register by
199+
/// [ClassEntity::add_static_property].
200+
pub fn set_static_property(&self, name: impl AsRef<str>, val: impl Into<ZVal>) -> Option<ZVal> {
201+
let ptr = self.as_ptr() as *mut _;
202+
let prop = Self::inner_get_static_property(ptr, name);
203+
let prop = unsafe { ZVal::try_from_mut_ptr(prop) };
204+
prop.map(|prop| replace(prop, val.into()))
205+
}
206+
207+
fn inner_get_static_property(scope: *mut zend_class_entry, name: impl AsRef<str>) -> *mut zval {
208+
let name = name.as_ref();
209+
210+
unsafe {
211+
#[allow(clippy::useless_conversion)]
212+
zend_read_static_property(scope, name.as_ptr().cast(), name.len(), true.into())
213+
}
214+
}
184215
}
185216

186217
impl Debug for ClassEntry {
@@ -444,6 +475,19 @@ impl<T: 'static> ClassEntity<T> {
444475
.push(PropertyEntity::new(name, visibility, value));
445476
}
446477

478+
/// Declare static property.
479+
///
480+
/// The argument `value` should be `Copy` because 'zend_declare_property'
481+
/// receive only scalar zval , otherwise will report fatal error:
482+
/// "Internal zvals cannot be refcounted".
483+
pub fn add_static_property(
484+
&mut self, name: impl Into<String>, visibility: Visibility, value: impl Into<Scalar>,
485+
) {
486+
let mut entity = PropertyEntity::new(name, visibility, value);
487+
entity.set_vis_static();
488+
self.property_entities.push(entity);
489+
}
490+
447491
/// Register class to `extends` the parent class.
448492
///
449493
/// *Because in the `MINIT` phase, the class starts to register, so the*
@@ -719,24 +763,30 @@ unsafe extern "C" fn interface_init_handler(
719763
/// Builder for declare class property.
720764
struct PropertyEntity {
721765
name: String,
722-
visibility: Visibility,
766+
visibility: RawVisibility,
723767
value: Scalar,
724768
}
725769

726770
impl PropertyEntity {
727771
fn new(name: impl Into<String>, visibility: Visibility, value: impl Into<Scalar>) -> Self {
728772
Self {
729773
name: name.into(),
730-
visibility,
774+
visibility: visibility as RawVisibility,
731775
value: value.into(),
732776
}
733777
}
734778

779+
#[inline]
780+
pub(crate) fn set_vis_static(&mut self) -> &mut Self {
781+
self.visibility |= ZEND_ACC_STATIC;
782+
self
783+
}
784+
735785
#[allow(clippy::useless_conversion)]
736786
pub(crate) fn declare(&self, ce: *mut zend_class_entry) {
737787
let name = self.name.as_ptr().cast();
738788
let name_length = self.name.len().try_into().unwrap();
739-
let access_type = self.visibility as u32 as i32;
789+
let access_type = self.visibility as i32;
740790

741791
unsafe {
742792
match &self.value {

phper/src/errors.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,9 @@ pub unsafe fn throw(e: impl Throwable) {
507507
let mut val = ManuallyDrop::new(ZVal::from(obj));
508508
zend_throw_exception_object(val.as_mut_ptr());
509509
}
510+
511+
/// Equivalent to `Ok::<_, phper::Error>(value)`.
512+
#[inline]
513+
pub fn ok<T>(t: T) -> Result<T> {
514+
Ok(t)
515+
}

phper/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub mod types;
3131
mod utils;
3232
pub mod values;
3333

34-
pub use crate::errors::{Error, Result};
34+
pub use crate::errors::{ok, Error, Result};
3535
pub use phper_alloc as alloc;
3636
pub use phper_macros::*;
3737
pub use phper_sys as sys;

phper/src/objects.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
//! Apis relate to [zend_object].
1212
1313
use crate::{
14-
alloc::EBox,
1514
classes::ClassEntry,
1615
functions::{call_internal, call_raw_common, ZFunc},
1716
sys::*,
@@ -185,7 +184,7 @@ impl ZObj {
185184
#[allow(clippy::useless_conversion)]
186185
pub fn set_property(&mut self, name: impl AsRef<str>, val: impl Into<ZVal>) {
187186
let name = name.as_ref();
188-
let val = EBox::new(val.into());
187+
let mut val = val.into();
189188
unsafe {
190189
#[cfg(phper_major_version = "8")]
191190
{
@@ -194,7 +193,7 @@ impl ZObj {
194193
&mut self.inner,
195194
name.as_ptr().cast(),
196195
name.len().try_into().unwrap(),
197-
EBox::into_raw(val).cast(),
196+
val.as_mut_ptr(),
198197
)
199198
}
200199
#[cfg(phper_major_version = "7")]
@@ -206,7 +205,7 @@ impl ZObj {
206205
&mut zv,
207206
name.as_ptr().cast(),
208207
name.len().try_into().unwrap(),
209-
EBox::into_raw(val).cast(),
208+
val.as_mut_ptr(),
210209
)
211210
}
212211
}

tests/integration/src/classes.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
use phper::{
1212
alloc::RefClone,
1313
classes::{
14-
array_access_class, iterator_class, ClassEntity, InterfaceEntity, StaticInterface,
15-
StaticStateClass, Visibility,
14+
array_access_class, iterator_class, ClassEntity, ClassEntry, InterfaceEntity,
15+
StaticInterface, StaticStateClass, Visibility,
1616
},
1717
functions::Argument,
1818
modules::Module,
@@ -24,6 +24,7 @@ pub fn integrate(module: &mut Module) {
2424
integrate_a(module);
2525
integrate_foo(module);
2626
integrate_i_bar(module);
27+
integrate_static_props(module);
2728
}
2829

2930
fn integrate_a(module: &mut Module) {
@@ -154,3 +155,29 @@ fn integrate_i_bar(module: &mut Module) {
154155

155156
module.add_interface(interface);
156157
}
158+
159+
fn integrate_static_props(module: &mut Module) {
160+
let mut class = ClassEntity::new("IntegrationTest\\PropsHolder");
161+
162+
class.add_static_property("foo", Visibility::Public, "bar");
163+
164+
class.add_static_property("foo1", Visibility::Private, 12345i64);
165+
166+
class.add_static_method("getFoo1", Visibility::Public, |_| {
167+
let val = ClassEntry::from_globals("IntegrationTest\\PropsHolder")?
168+
.get_static_property("foo1")
169+
.map(ToOwned::to_owned)
170+
.unwrap_or_default();
171+
phper::ok(val)
172+
});
173+
174+
class
175+
.add_static_method("setFoo1", Visibility::Public, |params| {
176+
let foo1 = ClassEntry::from_globals("IntegrationTest\\PropsHolder")?
177+
.set_static_property("foo1", params[0].to_owned());
178+
phper::ok(foo1)
179+
})
180+
.argument(Argument::by_val("val"));
181+
182+
module.add_class(class);
183+
}

tests/integration/src/objects.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,24 @@ pub fn integrate(module: &mut Module) {
127127
)
128128
.argument(Argument::by_val("obj"));
129129

130+
module.add_function("integrate_objects_set_props", |_| {
131+
let mut o = ZObject::new_by_std_class();
132+
133+
o.set_property("foo", "bar");
134+
assert_eq!(o.get_property("foo").as_z_str().unwrap().to_bytes(), b"bar");
135+
136+
o.set_property("foo", ());
137+
assert_eq!(o.get_property("foo").as_null(), Some(()));
138+
139+
o.set_property("foo", true);
140+
assert_eq!(o.get_property("foo").as_bool(), Some(true));
141+
142+
o.set_property("foo", ZVal::from(100i64));
143+
assert_eq!(o.get_property("foo").as_long(), Some(100i64));
144+
145+
phper::ok(())
146+
});
147+
130148
let class_a =
131149
ClassEntity::new_with_state_constructor("IntegrationTest\\Objects\\A", || 123456i64);
132150
module.add_class(class_a);

tests/integration/tests/php/classes.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,11 @@
6060
$doSomethings = $interface->getMethod("doSomethings");
6161
assert_true($doSomethings->isPublic());
6262
assert_true($doSomethings->isAbstract());
63+
64+
// Test get or set static properties.
65+
assert_eq(IntegrationTest\PropsHolder::$foo, "bar");
66+
67+
assert_eq(IntegrationTest\PropsHolder::getFoo1(), 12345);
68+
$pre_foo1 = IntegrationTest\PropsHolder::setFoo1("baz");
69+
assert_eq($pre_foo1, 12345);
70+
assert_eq(IntegrationTest\PropsHolder::getFoo1(), "baz");

tests/integration/tests/php/objects.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
integrate_objects_call();
2020
integrate_objects_to_ref_owned(new stdClass());
2121
integrate_objects_to_ref_clone(new stdClass());
22+
integrate_objects_set_props();
2223

2324
$a = new IntegrationTest\Objects\A();
2425
assert_throw(function () use ($a) { $a2 = clone $a; }, "Error", 0, "Trying to clone an uncloneable object of class IntegrationTest\\Objects\\A");

0 commit comments

Comments
 (0)