@@ -17,17 +17,19 @@ use std::{
1717use crate :: {
1818 bindings:: {
1919 ext_php_rs_zend_object_alloc, ext_php_rs_zend_object_release, object_properties_init,
20- std_object_handlers, zend_is_true, zend_object, zend_object_handlers, zend_object_std_dtor,
21- zend_object_std_init, zend_objects_clone_members, zend_std_get_properties,
22- zend_std_has_property, zend_std_read_property, zend_std_write_property, zend_string,
23- HashTable , ZEND_ISEMPTY , ZEND_PROPERTY_EXISTS , ZEND_PROPERTY_ISSET ,
20+ std_object_handlers, zend_call_known_function, zend_is_true, zend_object,
21+ zend_object_handlers, zend_object_std_dtor, zend_object_std_init,
22+ zend_objects_clone_members, zend_std_get_properties, zend_std_has_property,
23+ zend_std_read_property, zend_std_write_property, zend_string, HashTable , ZEND_ISEMPTY ,
24+ ZEND_PROPERTY_EXISTS , ZEND_PROPERTY_ISSET ,
2425 } ,
2526 errors:: { Error , Result } ,
2627 php:: {
2728 class:: ClassEntry ,
2829 enums:: DataType ,
2930 exceptions:: PhpResult ,
3031 flags:: ZvalTypeFlags ,
32+ globals:: ExecutorGlobals ,
3133 types:: { array:: OwnedHashTable , string:: ZendString } ,
3234 } ,
3335} ;
@@ -166,6 +168,68 @@ impl ZendObject {
166168 fn mut_ptr ( & self ) -> * mut Self {
167169 ( self as * const Self ) as * mut Self
168170 }
171+
172+ /// Extracts some type from a Zend object.
173+ ///
174+ /// This is a wrapper function around `FromZendObject::extract()`.
175+ pub fn extract < ' a , T > ( & ' a self ) -> Result < T >
176+ where
177+ T : FromZendObject < ' a > ,
178+ {
179+ T :: from_zend_object ( self )
180+ }
181+ }
182+
183+ /// `FromZendObject` is implemented by types which can be extracted from a Zend object.
184+ ///
185+ /// Normal usage is through the helper method `ZendObject::extract`:
186+ ///
187+ /// ```rust,ignore
188+ /// let obj: ZendObject = ...;
189+ /// let repr: String = obj.extract();
190+ /// let props: HashMap = obj.extract();
191+ /// ```
192+ ///
193+ /// Should be functionally equivalent to casting an object to another compatable type.
194+ pub trait FromZendObject < ' a > : Sized {
195+ /// Extracts `Self` from the source `ZendObject`.
196+ fn from_zend_object ( obj : & ' a ZendObject ) -> Result < Self > ;
197+ }
198+
199+ impl FromZendObject < ' _ > for String {
200+ fn from_zend_object ( obj : & ZendObject ) -> Result < Self > {
201+ let mut ret = Zval :: new ( ) ;
202+ unsafe {
203+ zend_call_known_function (
204+ ( * obj. ce ) . __tostring ,
205+ obj as * const _ as * mut _ ,
206+ obj. ce ,
207+ & mut ret,
208+ 0 ,
209+ std:: ptr:: null_mut ( ) ,
210+ std:: ptr:: null_mut ( ) ,
211+ ) ;
212+ }
213+
214+ if let Some ( err) = ExecutorGlobals :: take_exception ( ) {
215+ // TODO: become an error
216+ let class_name = obj. get_class_name ( ) ;
217+ panic ! (
218+ "Uncaught exception during call to {}::__toString(): {:?}" ,
219+ class_name. expect( "unable to determine class name" ) ,
220+ err
221+ ) ;
222+ } else if let Some ( output) = ret. extract ( ) {
223+ Ok ( output)
224+ } else {
225+ // TODO: become an error
226+ let class_name = obj. get_class_name ( ) ;
227+ panic ! (
228+ "{}::__toString() must return a string" ,
229+ class_name. expect( "unable to determine class name" ) ,
230+ ) ;
231+ }
232+ }
169233}
170234
171235impl Debug for ZendObject {
0 commit comments