Skip to content

Commit 219e9a8

Browse files
authored
Impl ToOwned for ZObj, supplement ZObj document. (#97)
1 parent 16e14aa commit 219e9a8

File tree

6 files changed

+174
-30
lines changed

6 files changed

+174
-30
lines changed

phper-doc/doc/_04_zval/index.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ Otherwise, there are also composite types that implement `From`.
4040
- `From<Option<T>>`: if Some(T), T will be converted to PHP type like `From<T>`,
4141
or `None` wll be converted to `null`.
4242

43-
### Example
44-
4543
```rust,no_run
4644
use phper::values::ZVal;
4745
@@ -59,8 +57,6 @@ Now you can use `as_*` or `expect_*` methods to convert ZVal to Rust types.
5957
[phper::errors::ExpectTypeError] will be returned, with the message:
6058
`type error: must be of type {expect_type}, {actual_type} given")`.
6159

62-
### Example
63-
6460
```rust,no_run
6561
use phper::echo;
6662
use phper::values::ZVal;
Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,46 @@
11
# Z Obj
22

3-
TODO
3+
The [`&ZObj`](phper::objects::ZObj) and [`ZObject`](phper::objects::ZObject) are
4+
the wrapper of [`zend_object`](phper::sys::zend_object).
5+
6+
You can do oop operation using `ZObj` or `ZObject`, like getting and setting properties,
7+
calling methods, etc.
8+
9+
```rust,no_run
10+
use phper::classes::ClassEntry;
11+
use phper::objects::ZObject;
12+
use phper::errors::exception_class;
13+
14+
let mut e: ZObject = exception_class().new_object([]).unwrap();
15+
e.set_property("code", 403);
16+
e.set_property("message", "oh no");
17+
let _message = e.call("getMessage", []).unwrap();
18+
```
19+
20+
`ZObj` implements `ToOwned` and `ToRefOwned` to upgrade to `ZObject`.
21+
22+
- `ToOwned`: Duplicate the object via PHP keyword `clone`, like `$cloned_object = clone $some_object();`;
23+
- `ToRefOwned`: Duplicate the object via increment refcount.
24+
25+
`ZObject` implements `Clone`, same as `ZObj::to_owned`.
26+
27+
```rust,no_run
28+
use phper::sys;
29+
use phper::objects::ZObj;
30+
use phper::alloc::ToRefOwned;
31+
32+
extern "C" {
33+
fn something() -> *mut sys::zend_object;
34+
}
35+
36+
let o = unsafe { ZObj::from_mut_ptr(something()) };
37+
38+
// By PHP `clone`.
39+
let _o = o.to_owned();
40+
41+
// By refcount increment.
42+
let _o = o.to_ref_owned();
43+
```
44+
45+
Note that neither `ZObj` nor `ZObject` implement `Send` and `Sync`, because PHP
46+
is single-threaded.

phper/src/errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ pub type Result<T> = result::Result<T, self::Error>;
166166

167167
/// Crate level Error, which also can become an exception in php.
168168
#[derive(Debug, thiserror::Error)]
169+
#[non_exhaustive]
169170
pub enum Error {
170171
/// The error type for I/O operations.
171172
#[error(transparent)]

phper/src/objects.rs

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use std::{
2525
fmt::{self, Debug},
2626
intrinsics::transmute,
2727
marker::PhantomData,
28-
mem::{forget, size_of, ManuallyDrop},
28+
mem::{forget, size_of, zeroed, ManuallyDrop},
2929
ops::{Deref, DerefMut},
3030
ptr::null_mut,
3131
};
@@ -278,6 +278,17 @@ impl ZObj {
278278
}
279279
}
280280

281+
impl ToOwned for ZObj {
282+
type Owned = ZObject;
283+
284+
/// The `to_owned` will do the copy like in PHP `$cloned_object = clone
285+
/// $some_object();`.
286+
#[inline]
287+
fn to_owned(&self) -> Self::Owned {
288+
clone_obj(self.as_ptr())
289+
}
290+
}
291+
281292
impl ToRefOwned for ZObj {
282293
type Owned = ZObject;
283294

@@ -351,30 +362,9 @@ impl ZObject {
351362
impl Clone for ZObject {
352363
/// The clone will do the copy like in PHP `$cloned_object = clone
353364
/// $some_object();`.
365+
#[inline]
354366
fn clone(&self) -> Self {
355-
unsafe {
356-
Self::from_raw({
357-
let mut zv = ManuallyDrop::new(ZVal::default());
358-
phper_zval_obj(zv.as_mut_ptr(), self.as_ptr() as *mut _);
359-
let handlers = phper_z_obj_ht_p(zv.as_ptr());
360-
361-
let ptr = {
362-
#[cfg(phper_major_version = "7")]
363-
{
364-
zv.as_mut_ptr()
365-
}
366-
#[cfg(phper_major_version = "8")]
367-
{
368-
self.as_ptr() as *mut _
369-
}
370-
};
371-
372-
match (*handlers).clone_obj {
373-
Some(clone_obj) => clone_obj(ptr),
374-
None => zend_objects_clone_obj(ptr),
375-
}
376-
})
377-
}
367+
clone_obj(self.as_ptr())
378368
}
379369
}
380370

@@ -415,6 +405,32 @@ impl Debug for ZObject {
415405
}
416406
}
417407

408+
fn clone_obj(obj: *const zend_object) -> ZObject {
409+
unsafe {
410+
ZObject::from_raw({
411+
let mut zv = zeroed::<zval>();
412+
phper_zval_obj(&mut zv, obj as *mut _);
413+
let handlers = phper_z_obj_ht_p(&zv);
414+
415+
let ptr = {
416+
#[cfg(phper_major_version = "7")]
417+
{
418+
&mut zv as *mut _
419+
}
420+
#[cfg(phper_major_version = "8")]
421+
{
422+
obj as *mut _
423+
}
424+
};
425+
426+
match (*handlers).clone_obj {
427+
Some(clone_obj) => clone_obj(ptr),
428+
None => zend_objects_clone_obj(ptr),
429+
}
430+
})
431+
}
432+
}
433+
418434
/// The object owned state, usually as the parameter of method handler.
419435
#[repr(transparent)]
420436
pub struct StateObj<T> {

tests/integration/src/objects.rs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
// See the Mulan PSL v2 for more details.
1010

1111
use phper::{
12-
classes::ClassEntry, modules::Module, objects::ZObject, types::TypeInfo, values::ZVal,
12+
alloc::ToRefOwned, classes::ClassEntry, functions::Argument, modules::Module, objects::ZObject,
13+
types::TypeInfo, values::ZVal,
1314
};
1415

1516
pub fn integrate(module: &mut Module) {
@@ -59,4 +60,88 @@ pub fn integrate(module: &mut Module) {
5960
Ok(())
6061
},
6162
);
63+
64+
module.add_function(
65+
"integrate_objects_clone",
66+
|_: &mut [ZVal]| -> phper::Result<()> {
67+
let mut o1 = ZObject::new_by_std_class();
68+
o1.set_property("foo", "bar");
69+
70+
let mut o2 = o1.clone();
71+
assert_eq!(
72+
o2.get_property("foo").as_z_str().unwrap().to_bytes(),
73+
b"bar"
74+
);
75+
76+
o2.set_property("foo", "baz");
77+
assert_eq!(
78+
o1.get_property("foo").as_z_str().unwrap().to_bytes(),
79+
b"bar"
80+
);
81+
assert_eq!(
82+
o2.get_property("foo").as_z_str().unwrap().to_bytes(),
83+
b"baz"
84+
);
85+
86+
Ok(())
87+
},
88+
);
89+
90+
module
91+
.add_function(
92+
"integrate_objects_to_owned",
93+
|arguments: &mut [ZVal]| -> phper::Result<()> {
94+
let o1 = arguments[0].expect_mut_z_obj()?;
95+
96+
o1.set_property("foo", "bar");
97+
98+
let mut o2 = o1.to_owned();
99+
assert_eq!(
100+
o2.get_property("foo").as_z_str().unwrap().to_bytes(),
101+
b"bar"
102+
);
103+
104+
o2.set_property("foo", "baz");
105+
assert_eq!(
106+
o1.get_property("foo").as_z_str().unwrap().to_bytes(),
107+
b"bar"
108+
);
109+
assert_eq!(
110+
o2.get_property("foo").as_z_str().unwrap().to_bytes(),
111+
b"baz"
112+
);
113+
114+
Ok(())
115+
},
116+
)
117+
.argument(Argument::by_val("obj"));
118+
119+
module
120+
.add_function(
121+
"integrate_objects_to_ref_owned",
122+
|arguments: &mut [ZVal]| -> phper::Result<()> {
123+
let o1 = arguments[0].expect_mut_z_obj()?;
124+
125+
o1.set_property("foo", "bar");
126+
127+
let mut o2 = o1.to_ref_owned();
128+
assert_eq!(
129+
o2.get_property("foo").as_z_str().unwrap().to_bytes(),
130+
b"bar"
131+
);
132+
133+
o2.set_property("foo", "baz");
134+
assert_eq!(
135+
o1.get_property("foo").as_z_str().unwrap().to_bytes(),
136+
b"baz"
137+
);
138+
assert_eq!(
139+
o2.get_property("foo").as_z_str().unwrap().to_bytes(),
140+
b"baz"
141+
);
142+
143+
Ok(())
144+
},
145+
)
146+
.argument(Argument::by_val("obj"));
62147
}

tests/integration/tests/php/objects.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@
1717
integrate_objects_get_set();
1818
integrate_objects_set_val();
1919
integrate_objects_call();
20+
integrate_objects_clone();
21+
integrate_objects_to_owned(new stdClass());
22+
integrate_objects_to_ref_owned(new stdClass());

0 commit comments

Comments
 (0)