Skip to content

Commit bb2751c

Browse files
authored
Merge pull request #19 from jmjoy/0.2.0-alpha.3-dev
2 parents 8c5894a + 19b48ef commit bb2751c

File tree

26 files changed

+623
-154
lines changed

26 files changed

+623
-154
lines changed

examples/http-client/src/client.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ pub fn make_client_builder_class() -> DynamicClass<ClientBuilder> {
4949
|this, _arguments| {
5050
let state = this.as_mut_state();
5151
let client = replace_and_get(state, ClientBuilder::new(), ClientBuilder::build)?;
52-
let mut object =
53-
ClassEntry::<Option<Client>>::from_globals(HTTP_CLIENT_CLASS_NAME)?.new_object();
52+
let mut object = ClassEntry::<Option<Client>>::from_globals(HTTP_CLIENT_CLASS_NAME)?
53+
.new_object_without_construct();
5454
*object.as_mut_state() = Some(client);
5555
Ok::<_, HttpClientError>(object)
5656
},
@@ -79,7 +79,7 @@ pub fn make_client_class() -> DynamicClass<Option<Client>> {
7979
let request_builder = client.get(url);
8080
let mut object =
8181
ClassEntry::<Option<RequestBuilder>>::from_globals(REQUEST_BUILDER_CLASS_NAME)?
82-
.new_object();
82+
.new_object_without_construct();
8383
*object.as_mut_state() = Some(request_builder);
8484
Ok::<_, HttpClientError>(object)
8585
},
@@ -95,7 +95,7 @@ pub fn make_client_class() -> DynamicClass<Option<Client>> {
9595
let request_builder = client.post(url);
9696
let mut object =
9797
ClassEntry::<Option<RequestBuilder>>::from_globals(REQUEST_BUILDER_CLASS_NAME)?
98-
.new_object();
98+
.new_object_without_construct();
9999
*object.as_mut_state() = Some(request_builder);
100100
Ok::<_, HttpClientError>(object)
101101
},

examples/http-client/src/request.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ pub fn make_request_builder_class() -> DynamicClass<Option<RequestBuilder>> {
2323
|this, _arguments| {
2424
let state = this.as_mut_state();
2525
let response = replace_and_get(state, None, |builder| builder.unwrap().send())?;
26-
let mut object =
27-
ClassEntry::<Option<Response>>::from_globals(RESPONSE_CLASS_NAME)?.new_object();
26+
let mut object = ClassEntry::<Option<Response>>::from_globals(RESPONSE_CLASS_NAME)?
27+
.new_object_without_construct();
2828
*object.as_mut_state() = Some(response);
2929
Ok::<_, HttpClientError>(object)
3030
},

phper-alloc/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub trait EAllocatable {
2727

2828
/// The Box which use php `emalloc` and `efree` to manage memory.
2929
///
30-
/// TODO now feature `allocator_api` is still unstable, implement myself.
30+
/// TODO now feature `allocator_api` is still unstable, implement myself, use Box<T, Alloc> later.
3131
pub struct EBox<T: EAllocatable> {
3232
ptr: *mut T,
3333
}

phper-macros/src/derives.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,14 @@ fn parse_throwable_input(
127127
})
128128
.into())
129129
}
130-
Data::Struct(_) => Err(syn::Error::new_spanned(
131-
&input,
132-
"struct auto derive Throwable is not supported",
133-
)),
130+
Data::Struct(_) => Ok((quote! {
131+
impl #crate_ident::errors::Throwable for #input_ident {
132+
fn class_entry(&self) -> &#crate_ident::classes::StatelessClassEntry {
133+
ClassEntry::from_globals(#exception).unwrap()
134+
}
135+
}
136+
})
137+
.into()),
134138
Data::Union(_) => Err(syn::Error::new_spanned(
135139
&input,
136140
"union auto derive Throwable is not supported",

phper-macros/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub fn php_get_module(attr: TokenStream, input: TokenStream) -> TokenStream {
7373
inner::php_get_module(attr, input)
7474
}
7575

76-
/// Auto derive for [phper::errors::Throwable].
76+
/// Auto derive for `phper::errors::Throwable`.
7777
///
7878
/// # Examples
7979
///
@@ -90,7 +90,7 @@ pub fn php_get_module(attr: TokenStream, input: TokenStream) -> TokenStream {
9090
/// }
9191
/// ```
9292
///
93-
/// TODO Support struct, attbiute `throwable` with `code` and `message`.
93+
/// TODO Support attribute `throwable` with `code` and `message`, integration tests.
9494
#[proc_macro_derive(Throwable, attributes(throwable, throwable_crate))]
9595
pub fn derive_throwable(input: TokenStream) -> TokenStream {
9696
let input = parse_macro_input!(input as DeriveInput);

phper-sys/php_wrapper.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,16 @@ void *phper_zend_object_alloc(size_t obj_size, zend_class_entry *ce) {
147147
zend_object* (**phper_get_create_object(zend_class_entry *ce))(zend_class_entry *class_type) {
148148
return &ce->create_object;
149149
}
150+
151+
bool phper_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]) {
152+
function_table = function_table;
153+
return call_user_function(function_table, object, function_name, retval_ptr, param_count, params) == SUCCESS;
154+
}
155+
156+
bool phper_zend_hash_str_exists(const HashTable *ht, const char *str, size_t len) {
157+
return zend_hash_str_exists(ht, str, len) != 0;
158+
}
159+
160+
bool phper_zend_hash_index_exists(const HashTable *ht, zend_ulong h) {
161+
return zend_hash_index_exists(ht, h) != 0;
162+
}

phper-sys/php_wrapper.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef PHPER_PHP_WRAPPER_H
22
#define PHPER_PHP_WRAPPER_H
33

4+
#include <stdbool.h>
45
#include <php.h>
56
#include <php_ini.h>
67
#include <ext/standard/info.h>
@@ -50,4 +51,9 @@ void *phper_zend_object_alloc(size_t obj_size, zend_class_entry *ce);
5051

5152
zend_object* (**phper_get_create_object(zend_class_entry *ce))(zend_class_entry *class_type);
5253

54+
bool phper_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]);
55+
56+
bool phper_zend_hash_str_exists(const HashTable *ht, const char *str, size_t len);
57+
bool phper_zend_hash_index_exists(const HashTable *ht, zend_ulong h);
58+
5359
#endif //PHPER_PHP_WRAPPER_H

phper/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ clap = "3.0.0-beta.2"
1818
dashmap = "4.0.2"
1919
derive_more = "0.99.13"
2020
indexmap = "1.6.2"
21-
num-derive = "0.3"
22-
num-traits = "0.2"
21+
#num-derive = "0.3"
22+
#num-traits = "0.2"
2323
once_cell = "1.7.2"
2424
phper-alloc = { version = "0.2.0-alpha.3", path = "../phper-alloc" }
2525
phper-macros = { version = "0.2.0-alpha.3", path = "../phper-macros" }

phper/src/arrays.rs

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
33
use crate::{
44
alloc::{EAllocatable, EBox},
5+
strings::ZendString,
56
sys::*,
67
values::Val,
78
};
8-
use std::mem::zeroed;
9+
use std::{borrow::Cow, mem::zeroed};
910

1011
/// Key for [Array].
12+
#[derive(Debug, Clone, PartialEq)]
1113
pub enum Key<'a> {
1214
Index(u64),
13-
Str(&'a str),
15+
Str(Cow<'a, str>),
1416
}
1517

1618
impl From<u64> for Key<'_> {
@@ -21,7 +23,13 @@ impl From<u64> for Key<'_> {
2123

2224
impl<'a> From<&'a str> for Key<'a> {
2325
fn from(s: &'a str) -> Self {
24-
Key::Str(s)
26+
Key::Str(Cow::Borrowed(s))
27+
}
28+
}
29+
30+
impl From<String> for Key<'_> {
31+
fn from(s: String) -> Self {
32+
Key::Str(Cow::Owned(s))
2533
}
2634
}
2735

@@ -45,7 +53,12 @@ impl Array {
4553
}
4654
}
4755

48-
pub(crate) unsafe fn from_mut_ptr<'a>(ptr: *mut zend_array) -> &'a mut Array {
56+
pub unsafe fn from_ptr<'a>(ptr: *const zend_array) -> &'a Array {
57+
let ptr = ptr as *const Array;
58+
ptr.as_ref().expect("ptr shouldn't be null")
59+
}
60+
61+
pub unsafe fn from_mut_ptr<'a>(ptr: *mut zend_array) -> &'a mut Array {
4962
let ptr = ptr as *mut Array;
5063
ptr.as_mut().expect("ptr shouldn't be null")
5164
}
@@ -100,13 +113,34 @@ impl Array {
100113
unsafe { zend_array_count(&mut self.inner) as usize }
101114
}
102115

116+
pub fn exists<'a>(&self, key: impl Into<Key<'a>>) -> bool {
117+
let key = key.into();
118+
unsafe {
119+
match key {
120+
Key::Index(i) => phper_zend_hash_index_exists(&self.inner, i),
121+
Key::Str(s) => phper_zend_hash_str_exists(
122+
&self.inner,
123+
s.as_ref().as_ptr().cast(),
124+
s.as_ref().len(),
125+
),
126+
}
127+
}
128+
}
129+
103130
pub fn clone(&self) -> EBox<Self> {
104131
let mut other = Self::new();
105132
unsafe {
106133
zend_hash_copy(other.as_mut_ptr(), self.as_ptr() as *mut _, None);
107134
}
108135
other
109136
}
137+
138+
pub fn iter(&self) -> Iter<'_> {
139+
Iter {
140+
index: 0,
141+
array: &self,
142+
}
143+
}
110144
}
111145

112146
impl EAllocatable for Array {
@@ -115,6 +149,8 @@ impl EAllocatable for Array {
115149
if (*ptr).inner.gc.refcount == 0 {
116150
zend_hash_destroy(ptr.cast());
117151
_efree(ptr.cast());
152+
} else {
153+
(*ptr).inner.gc.refcount -= 1;
118154
}
119155
}
120156
}
@@ -125,3 +161,45 @@ impl Drop for Array {
125161
unreachable!("Allocation on the stack is not allowed")
126162
}
127163
}
164+
165+
pub struct Iter<'a> {
166+
index: isize,
167+
array: &'a Array,
168+
}
169+
170+
impl<'a> Iterator for Iter<'a> {
171+
type Item = (Key<'a>, &'a Val);
172+
173+
fn next(&mut self) -> Option<Self::Item> {
174+
loop {
175+
if self.index >= self.array.inner.nNumUsed as isize {
176+
break None;
177+
}
178+
179+
unsafe {
180+
let bucket = self.array.inner.arData.offset(self.index);
181+
182+
let key = if (*bucket).key.is_null() {
183+
Key::Index((*bucket).h)
184+
} else {
185+
let s = ZendString::from_ptr((*bucket).key);
186+
let s = s.to_string().unwrap();
187+
Key::Str(Cow::Owned(s))
188+
};
189+
190+
let val = &mut (*bucket).val;
191+
let mut val = Val::from_mut_ptr(val);
192+
if val.get_type().is_indirect() {
193+
val = Val::from_mut_ptr((*val.as_mut_ptr()).value.zv);
194+
}
195+
196+
self.index += 1;
197+
198+
if val.get_type().is_undef() {
199+
continue;
200+
}
201+
break Some((key, val));
202+
}
203+
}
204+
}
205+
}

phper/src/classes.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
33
use crate::{
44
alloc::EBox,
5+
arrays::Array,
56
errors::{ClassNotFoundError, StateTypeError},
67
functions::{Argument, Function, FunctionEntity, FunctionEntry, Method},
78
objects::{ExtendObject, Object},
9+
strings::ZendString,
810
sys::*,
911
types::Scalar,
1012
values::{SetVal, Val},
@@ -171,6 +173,14 @@ impl<T: Send> Classifiable for DynamicClass<T> {
171173
pub type StatelessClassEntry = ClassEntry<()>;
172174

173175
/// Wrapper of [crate::sys::zend_class_entry].
176+
///
177+
/// # Generic
178+
///
179+
/// 1. Any `zend_class_entry` can be make into `ClassEntry<()>`, alias as [StatelessClassEntry].
180+
///
181+
/// 2. Only the `zend_class_entry` created by [crate::modules::Module::add_class] can be make into
182+
/// `ClassEntry<T>`, where `T` is the type defined by [Classifiable::state_type_id], as the inner
183+
/// state of `ClassEntry<T>` and `Object<T>`.
174184
#[repr(transparent)]
175185
pub struct ClassEntry<T: 'static> {
176186
inner: zend_class_entry,
@@ -190,7 +200,7 @@ impl<T: 'static> ClassEntry<T> {
190200
r
191201
}
192202

193-
fn check_type_id(this: *mut Self) -> Result<(), StateTypeError> {
203+
pub(crate) fn check_type_id(this: *const Self) -> Result<(), StateTypeError> {
194204
if TypeId::of::<T>() == TypeId::of::<()>() {
195205
return Ok(());
196206
}
@@ -217,12 +227,38 @@ impl<T: 'static> ClassEntry<T> {
217227
&mut self.inner
218228
}
219229

220-
pub fn new_object(&self) -> EBox<Object<T>> {
230+
/// Create the object from class and call `__construct` with arguments.
231+
pub fn new_object(&self, arguments: &mut [Val]) -> crate::Result<EBox<Object<T>>> {
221232
unsafe {
222233
let ptr = self.as_ptr() as *mut _;
223234
let f = (*phper_get_create_object(ptr)).unwrap_or(zend_objects_new);
224235
let object = f(ptr);
225-
EBox::from_raw(object.cast())
236+
let mut object: EBox<Object<T>> = EBox::from_raw(object.cast());
237+
let _ = object.call_construct(arguments)?;
238+
Ok(object)
239+
}
240+
}
241+
242+
/// Create the object from class, without calling `__construct`, be careful when `__construct`
243+
/// is necessary.
244+
pub fn new_object_without_construct(&self) -> EBox<Object<T>> {
245+
unsafe {
246+
let ptr = self.as_ptr() as *mut _;
247+
let f = (*phper_get_create_object(ptr)).unwrap_or(zend_objects_new);
248+
let object = f(ptr);
249+
let object: EBox<Object<T>> = EBox::from_raw(object.cast());
250+
object
251+
}
252+
}
253+
254+
pub fn get_name(&self) -> &ZendString {
255+
ZendString::from_ptr(self.inner.name)
256+
}
257+
258+
pub fn has_method(&self, method_name: &str) -> bool {
259+
unsafe {
260+
let function_table = Array::from_ptr(&self.inner.function_table);
261+
function_table.exists(method_name)
226262
}
227263
}
228264
}

0 commit comments

Comments
 (0)