@@ -17,23 +17,42 @@ def read_until(char)
1717
1818	module  SerializationState 
1919		class  Base 
20+ 			def  initialize 
21+ 				@last_processed_value_index  =  -1 
22+ 			end 
23+ 
2024			# @return [Boolean] 
2125			attr_accessor  :assoc 
26+ 
27+ 			# @return [Integer] 
28+ 			attr_accessor  :last_processed_value_index 
2229		end 
2330
2431		class  ToSerialize  < Base 
32+ 			def  initialize 
33+ 				super 
34+ 				@serialized_object_id_to_index_map  =  { } 
35+ 			end 
36+ 
37+ 			# @return [Hash{Integer => Integer}] 
38+ 			attr_accessor  :serialized_object_id_to_index_map 
2539		end 
2640
2741		class  ToUnserialize  < Base 
2842			def  initialize 
43+ 				super 
2944				@classmap  =  { } 
45+ 				@unserialized_values  =  [ ] 
3046			end 
3147
3248			# @return [Hash{String => Class}] 
3349			attr_accessor  :classmap 
3450
3551			# @return [String] 
3652			attr_accessor  :original_encoding 
53+ 
54+ 			# @return [Array<Object>] 
55+ 			attr_accessor  :unserialized_values 
3756		end 
3857	end 
3958
@@ -58,6 +77,7 @@ def PHP.serialize(var, assoc = false) # {{{
5877	end 
5978
6079	def  PHP . do_serialize ( var ,  state ) 
80+ 		this_value_index  =  ( state . last_processed_value_index  += 1 ) 
6181		s  =  String . new 
6282		case  var 
6383			when  Array 
@@ -68,7 +88,7 @@ def PHP.do_serialize(var, state)
6888					} 
6989				else 
7090					var . each_with_index  {  |v , i |
71- 						s  << "i: #{ i } ; #{ PHP . do_serialize ( v ,  state ) } " 
91+ 						s  << PHP . do_serialize ( i ,   state )  <<  PHP . do_serialize ( v ,  state ) 
7292					} 
7393				end 
7494
@@ -82,12 +102,15 @@ def PHP.do_serialize(var, state)
82102				s  << '}' 
83103
84104			when  Struct 
85- 				# encode as Object with same name 
86- 				s  << "O:#{ var . class . to_s . bytesize } \" #{ var . class . to_s . downcase } \" :#{ var . members . length }  
87- 				var . members . each  do  |member |
88- 					s  << "#{ PHP . do_serialize ( member ,  state ) } #{ PHP . do_serialize ( var [ member ] ,  state ) }  
89- 				end 
90- 				s  << '}' 
105+ 				s  = 
106+ 					handling_reference_for_recurring_object ( var ,  index : this_value_index ,  state : state )  { 
107+ 						# encode as Object with same name 
108+ 						s  << "O:#{ var . class . to_s . bytesize } \" #{ var . class . to_s . downcase } \" :#{ var . members . length }  
109+ 						var . members . each  do  |member |
110+ 							s  << "#{ PHP . do_serialize ( member ,  state ) } #{ PHP . do_serialize ( var [ member ] ,  state ) }  
111+ 						end 
112+ 						s  << '}' 
113+ 					} 
91114
92115			when  String ,  Symbol 
93116				s  << "s:#{ var . to_s . bytesize } \" #{ var . to_s } \" ;" 
@@ -106,13 +129,16 @@ def PHP.do_serialize(var, state)
106129
107130			else 
108131				if  var . respond_to? ( :to_assoc ) 
109- 					v  =  var . to_assoc 
110- 					# encode as Object with same name 
132+ 					s  = 
133+ 						handling_reference_for_recurring_object ( var ,  index : this_value_index ,  state : state )  { 
134+ 							v  =  var . to_assoc 
135+ 							# encode as Object with same name 
111136					s  << "O:#{ var . class . to_s . bytesize } \" #{ var . class . to_s . downcase } \" :#{ v . length }  
112- 					v . each  do  |k , v |
113- 						s  << "#{ PHP . do_serialize ( k . to_s ,  state ) } #{ PHP . do_serialize ( v ,  state ) }  
114- 					end 
115- 					s  << '}' 
137+ 							v . each  do  |k , v |
138+ 								s  << "#{ PHP . do_serialize ( k . to_s ,  state ) } #{ PHP . do_serialize ( v ,  state ) }  
139+ 							end 
140+ 							s  << '}' 
141+ 						} 
116142				else 
117143					raise  TypeError ,  "Unable to serialize type #{ var . class }  
118144				end 
@@ -121,6 +147,27 @@ def PHP.do_serialize(var, state)
121147		s 
122148	end  # }}} 
123149
150+ 	module  InternalMethodsForSerialize 
151+ 		private 
152+ 		# Generate an object reference ('r') for a recurring object instead of serializing it again. 
153+ 		# 
154+ 		# @param [Object] object object to be serialized 
155+ 		# @param [Integer] index index of serialized value 
156+ 		# @param [SerializationState::ToSerialize] state 
157+ 		# @param [Proc] block original procedure to serialize value 
158+ 		# @return [String] serialized value or reference 
159+ 		def  handling_reference_for_recurring_object ( object ,  index :,  state :,  &block ) 
160+ 			index_of_object_serialized_before  =  state . serialized_object_id_to_index_map [ object . __id__ ] 
161+ 			if  index_of_object_serialized_before 
162+ 				"r:#{ index_of_object_serialized_before }  
163+ 			else 
164+ 				state . serialized_object_id_to_index_map [ object . __id__ ]  =  index 
165+ 				yield 
166+ 			end 
167+ 		end 
168+ 	end 
169+ 	extend  InternalMethodsForSerialize 
170+ 
124171	# Like PHP.serialize, but only accepts a Hash or associative Array as the root 
125172	# type.  The results are returned in PHP session format. 
126173	# 
@@ -213,6 +260,7 @@ def PHP.unserialize(string, classmap = nil, assoc = false) # {{{
213260
214261	def  PHP . do_unserialize ( string ,  state ) 
215262		val  =  nil 
263+ 		this_value_index  =  ( state . last_processed_value_index  += 1 ) 
216264		# determine a type 
217265		type  =  string . read ( 2 ) [ 0 , 1 ] 
218266		case  type 
@@ -276,7 +324,7 @@ def PHP.do_unserialize(string, state)
276324
277325						val  =  val . new 
278326					rescue  NameError  # Nope; make a new Struct 
279- 						classmap [ klass ]  =  val  =  Struct . new ( klass . to_s ,  *attrs . collect  {  |v | v [ 0 ] . to_s  } ) 
327+ 						state . classmap [ klass ]  =  val  =  Struct . new ( klass . to_s ,  *attrs . collect  {  |v | v [ 0 ] . to_s  } ) 
280328						val  =  val . new 
281329					end 
282330				end 
@@ -301,10 +349,20 @@ def PHP.do_unserialize(string, state)
301349			when  'b'  # bool, b:0 or 1 
302350				val  =  string . read ( 2 ) [ 0 ]  == '1' 
303351
352+ 			when  'R' ,  'r'  # reference to value/object, R:123 or r:123 
353+ 				ref_index  =  string . read_until ( ';' ) . to_i 
354+ 
355+ 				unless  ( 0 ...( state . unserialized_values . size ) ) . cover? ( ref_index ) 
356+ 					raise  TypeError ,  "Data part of R/r(Reference) refers to invalid index: #{ ref_index . inspect }  
357+ 				end 
358+ 
359+ 				val  =  state . unserialized_values [ ref_index ] 
304360			else 
305361				raise  TypeError ,  "Unable to unserialize type '#{ type }  
306362		end 
307363
364+ 		state . unserialized_values [ this_value_index ]  =  val 
365+ 
308366		val 
309367	end  # }}} 
310368end 
0 commit comments