@@ -44,13 +44,19 @@ PHPAPI zend_class_entry     *spl_ce_MultipleIterator;
4444
4545PHPAPI  zend_object_handlers  spl_handler_SplObjectStorage ;
4646
47+ typedef  struct  _spl_SplObjectStorage_fptrs  {
48+ 	zend_function     * get_hash ;
49+ 	bool               override_read_dimension ;
50+ 	bool               override_write_dimension ;
51+ } spl_SplObjectStorage_fptrs ;
52+ 
4753typedef  struct  _spl_SplObjectStorage  { /* {{{ */ 
48- 	HashTable          storage ;
49- 	zend_long          index ;
50- 	HashPosition       pos ;
51- 	zend_long          flags ;
52- 	zend_function      * fptr_get_hash ;
53- 	zend_object        std ;
54+ 	HashTable                     storage ;
55+ 	zend_long                     index ;
56+ 	HashPosition                  pos ;
57+ 	zend_long                     flags ;
58+ 	spl_SplObjectStorage_fptrs   * fptrs ;
59+ 	zend_object                   std ;
5460} spl_SplObjectStorage ; /* }}} */ 
5561
5662/* {{{ storage is an assoc array of [zend_object*]=>[zval *obj, zval *inf] */ 
@@ -73,15 +79,19 @@ void spl_SplObjectStorage_free_storage(zend_object *object) /* {{{ */
7379	zend_object_std_dtor (& intern -> std );
7480
7581	zend_hash_destroy (& intern -> storage );
82+ 
83+ 	if  (UNEXPECTED (intern -> fptrs )) {
84+ 		efree_size (intern -> fptrs , sizeof (spl_SplObjectStorage_fptrs ));
85+ 	}
7686} /* }}} */ 
7787
7888static  int  spl_object_storage_get_hash (zend_hash_key  * key , spl_SplObjectStorage  * intern , zend_object  * obj ) {
79- 	if  (intern -> fptr_get_hash ) {
89+ 	if  (UNEXPECTED ( intern -> fptrs )  &&   intern -> fptrs -> get_hash ) {
8090		zval  param ;
8191		zval  rv ;
8292		ZVAL_OBJ (& param , obj );
8393		zend_call_method_with_1_params (
84- 			& intern -> std , intern -> std .ce , & intern -> fptr_get_hash , "getHash" , & rv , & param );
94+ 			& intern -> std , intern -> std .ce , & intern -> fptrs -> get_hash , "getHash" , & rv , & param );
8595		if  (!Z_ISUNDEF (rv )) {
8696			if  (Z_TYPE (rv ) ==  IS_STRING ) {
8797				key -> key  =  Z_STR (rv );
@@ -125,8 +135,52 @@ static spl_SplObjectStorageElement* spl_object_storage_get(spl_SplObjectStorage
125135	}
126136} /* }}} */ 
127137
138+ static  spl_SplObjectStorageElement  * spl_object_storage_create_element (zend_object  * obj , zval  * inf ) /* {{{ */ 
139+ {
140+ 	spl_SplObjectStorageElement  * pelement  =  emalloc (sizeof (spl_SplObjectStorageElement ));
141+ 	pelement -> obj  =  obj ;
142+ 	GC_ADDREF (obj );
143+ 	if  (inf ) {
144+ 		ZVAL_COPY (& pelement -> inf , inf );
145+ 	} else  {
146+ 		ZVAL_NULL (& pelement -> inf );
147+ 	}
148+ 	return  pelement ;
149+ } /* }}} */ 
150+ 
151+ static  spl_SplObjectStorageElement  * spl_object_storage_attach_handle (spl_SplObjectStorage  * intern , zend_object  * obj , zval  * inf ) /* {{{ */ 
152+ {
153+ 	uint32_t  handle  =  obj -> handle ;
154+ 	zval  * entry_zv ;
155+ 	spl_SplObjectStorageElement  * pelement ;
156+ 	entry_zv  =  zend_hash_index_lookup (& intern -> storage , handle );
157+ 	ZEND_ASSERT (intern -> fptrs  ==  NULL  ||  !intern -> fptrs -> override_write_dimension );
158+ 
159+ 	if  (Z_TYPE_P (entry_zv ) !=  IS_NULL ) {
160+ 		ZEND_ASSERT (Z_TYPE_P (entry_zv ) ==  IS_PTR );
161+ 		pelement  =  Z_PTR_P (entry_zv );
162+ 		/* FIXME unsafe if destructor of inf moves/removes this entry */ 
163+ 		zval_ptr_dtor (& pelement -> inf );
164+ 		if  (inf ) {
165+ 			ZVAL_COPY (& pelement -> inf , inf );
166+ 		} else  {
167+ 			ZVAL_NULL (& pelement -> inf );
168+ 		}
169+ 		return  pelement ;
170+ 	}
171+ 
172+ 	ZEND_ASSERT ((GC_FLAGS (& intern -> storage ) &  IS_ARRAY_PERSISTENT ) ==  0 );
173+ 	pelement  =  spl_object_storage_create_element (obj , inf );
174+ 	ZVAL_PTR (entry_zv , pelement );
175+ 	return  pelement ;
176+ } /* }}} */ 
177+ 
128178spl_SplObjectStorageElement  * spl_object_storage_attach (spl_SplObjectStorage  * intern , zend_object  * obj , zval  * inf ) /* {{{ */ 
129179{
180+ 	if  (EXPECTED (intern -> fptrs  ==  NULL  ||  !intern -> fptrs -> override_write_dimension )) {
181+ 		return  spl_object_storage_attach_handle (intern , obj , inf );
182+ 	}
183+ 
130184	spl_SplObjectStorageElement  * pelement , element ;
131185	zend_hash_key  key ;
132186	if  (spl_object_storage_get_hash (& key , intern , obj ) ==  FAILURE ) {
@@ -136,6 +190,7 @@ spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *int
136190	pelement  =  spl_object_storage_get (intern , & key );
137191
138192	if  (pelement ) {
193+ 		/* FIXME unsafe if destructor of inf moves/removes this entry */ 
139194		zval_ptr_dtor (& pelement -> inf );
140195		if  (inf ) {
141196			ZVAL_COPY (& pelement -> inf , inf );
@@ -189,6 +244,9 @@ void spl_object_storage_addall(spl_SplObjectStorage *intern, spl_SplObjectStorag
189244	intern -> index  =  0 ;
190245} /* }}} */ 
191246
247+ #define  SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE (class_type , method ) \
248+ 	(((zend_function *)zend_hash_str_find_ptr(&(class_type)->function_table, (method), sizeof((method)) - 1))->common.scope != spl_ce_SplObjectStorage)
249+ 
192250static  zend_object  * spl_object_storage_new_ex (zend_class_entry  * class_type , zend_object  * orig ) /* {{{ */ 
193251{
194252	spl_SplObjectStorage  * intern ;
@@ -207,10 +265,21 @@ static zend_object *spl_object_storage_new_ex(zend_class_entry *class_type, zend
207265
208266	while  (parent ) {
209267		if  (parent  ==  spl_ce_SplObjectStorage ) {
268+ 			/* TODO: Make these singletons with a map from class entry to IS_NULL/IS_PTR */ 
210269			if  (class_type  !=  spl_ce_SplObjectStorage ) {
211- 				intern -> fptr_get_hash  =  zend_hash_str_find_ptr (& class_type -> function_table , "gethash" , sizeof ("gethash" ) -  1 );
212- 				if  (intern -> fptr_get_hash -> common .scope  ==  spl_ce_SplObjectStorage ) {
213- 					intern -> fptr_get_hash  =  NULL ;
270+ 				spl_SplObjectStorage_fptrs  fptrs ;
271+ 				fptrs .get_hash  =  zend_hash_str_find_ptr (& class_type -> function_table , "gethash" , sizeof ("gethash" ) -  1 );
272+ 				if  (fptrs .get_hash -> common .scope  ==  spl_ce_SplObjectStorage ) {
273+ 					fptrs .get_hash  =  NULL ;
274+ 				}
275+ 				fptrs .override_read_dimension  =  fptrs .get_hash  !=  NULL  || 
276+ 					SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE (class_type , "offsetget" ) || 
277+ 					SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE (class_type , "offsetexists" );
278+ 
279+ 				fptrs .override_write_dimension  =  fptrs .get_hash  !=  NULL  ||  SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE (class_type , "offsetset" );
280+ 				if  (fptrs .get_hash  ||  fptrs .override_read_dimension  ||  fptrs .override_write_dimension ) {
281+ 					intern -> fptrs  =  emalloc (sizeof (spl_SplObjectStorage_fptrs ));
282+ 					* intern -> fptrs  =  fptrs ;
214283				}
215284			}
216285			break ;
@@ -361,6 +430,39 @@ PHP_METHOD(SplObjectStorage, attach)
361430	spl_object_storage_attach (intern , obj , inf );
362431} /* }}} */ 
363432
433+ static  zval  * spl_object_storage_read_dimension (zend_object  * object , zval  * offset , int  type , zval  * rv )
434+ {
435+ 	spl_SplObjectStorage  * intern  =  spl_object_storage_from_obj (object );
436+ 	if  (UNEXPECTED (offset  ==  NULL  ||  Z_TYPE_P (offset ) !=  IS_OBJECT  ||  (intern -> fptrs  &&  intern -> fptrs -> override_read_dimension ))) {
437+ 		/* Can't optimize it if getHash, offsetExists, or offsetGet is overridden */ 
438+ 		return  zend_std_read_dimension (object , offset , type , rv );
439+ 	}
440+ 	spl_SplObjectStorageElement  * element  =  zend_hash_index_find_ptr (& intern -> storage , Z_OBJ_HANDLE_P (offset ));
441+ 	if  (type  ==  BP_VAR_IS ) {
442+ 		if  (element  ==  NULL ) {
443+ 			return  & EG (uninitialized_zval );
444+ 		}
445+ 	}
446+ 
447+ 	if  (!element ) {
448+ 		zend_throw_exception_ex (spl_ce_UnexpectedValueException , 0 , "Object not found" );
449+ 		return  NULL ;
450+ 	} else  {
451+ 		ZVAL_COPY_DEREF (rv , & element -> inf );
452+ 		return  rv ;
453+ 	}
454+ }
455+ 
456+ static  void  spl_object_storage_write_dimension (zend_object  * object , zval  * offset , zval  * inf )
457+ {
458+ 	spl_SplObjectStorage  * intern  =  spl_object_storage_from_obj (object );
459+ 	if  (UNEXPECTED (offset  ==  NULL  ||  Z_TYPE_P (offset ) !=  IS_OBJECT  ||  (intern -> fptrs  &&  intern -> fptrs -> override_write_dimension ))) {
460+ 		zend_std_write_dimension (object , offset , inf );
461+ 		return ;
462+ 	}
463+ 	spl_object_storage_attach_handle (intern , Z_OBJ_P (offset ), inf );
464+ }
465+ 
364466/* {{{ Detaches an object from the storage */ 
365467PHP_METHOD (SplObjectStorage , detach )
366468{
@@ -1201,6 +1303,8 @@ PHP_MINIT_FUNCTION(spl_observer)
12011303	spl_handler_SplObjectStorage .clone_obj        =  spl_object_storage_clone ;
12021304	spl_handler_SplObjectStorage .get_gc           =  spl_object_storage_get_gc ;
12031305	spl_handler_SplObjectStorage .free_obj         =  spl_SplObjectStorage_free_storage ;
1306+ 	spl_handler_SplObjectStorage .read_dimension   =  spl_object_storage_read_dimension ;
1307+ 	spl_handler_SplObjectStorage .write_dimension  =  spl_object_storage_write_dimension ;
12041308
12051309	spl_ce_MultipleIterator  =  register_class_MultipleIterator (zend_ce_iterator );
12061310	spl_ce_MultipleIterator -> create_object  =  spl_SplObjectStorage_new ;
0 commit comments