@@ -213,54 +213,107 @@ protected function isAssocArray($arr){
213213
214214protected function build_msnode ($ objectarr ){
215215$ objecttype ='' ;
216+
217+ // While(each()) used in order to remember cursory position within array
218+ // Also sets $key and $value for current values at cursor position in array
216219while (list ($ key ,$ value ) =each ($ objectarr )) {
217- // First do some quick validation
218- if (is_array ($ value ) &&sizeof ($ value ) ==0 ) {
219- $ value ='' ;
220- }
221- if ($ key =='ClassType ' ) {
220+
221+ // First do some quick validation
222+ if (is_array ($ value ) &&sizeof ($ value ) ==0 ) {
223+ $ value ='' ;
224+ }
225+
226+ // Check for ClassType key that is always located in first element in array
227+ // If true, this is the first loop so we set ClassType XML tags and recurse back into the function with cursor positioned on second element
228+ if (strval ($ key ) ==='ClassType ' ) {
222229$ objecttype .='<mem:ClassType> ' .$ value .'</mem:ClassType><mem:Fields> ' .$ this ->build_msnode ($ objectarr ).'</mem:Fields> ' ;
223230break ;
224- }
225- else if (is_array ($ value )){
226- $ arrData ='' ;
231+ }
232+ // If false, not the first loop so start generating the XML based on the type of value at array cursor position
233+ else if (is_array ($ value )){
234+ $ arrData ='' ;
227235
228- // Fix for array of MS Objects check not being handled properly
229- reset ($ value );
236+ // Fix for array of MS Objects check not being handled properly
237+ reset ($ value );
230238
231- if (key ($ value ) ==='RecordType ' )
232- {
233- $ arrData .='i:type="mem: ' .current ($ value ).'"> ' ;
234- foreach ($ valueas $ k =>$ v ){
235- if ($ k !=='RecordType ' ){
236- $ arrData .='<mem: ' .$ k ;
237- if (!is_array ($ v ) &&strlen ($ v ) >0 )
238- $ arrData .='> ' .$ v .'</mem: ' .$ k .'> ' ;
239- else
240- $ arrData .=' i:nil="true" /> ' ;
239+ // Check for RecordType key
240+ if (key ($ value ) ==='RecordType ' )
241+ {
242+ $ arrData .='i:type="mem: ' .current ($ value ).'"> ' ;
243+ foreach ($ valueas $ k =>$ v ){
244+ if ($ k !=='RecordType ' ){
245+ $ arrData .='<mem: ' .$ k ;
246+ if (!is_array ($ v ) &&strlen ($ v ) >0 )
247+ $ arrData .='> ' .$ v .'</mem: ' .$ k .'> ' ;
248+ else
249+ $ arrData .=' i:nil="true" /> ' ;
241250}
242- }
251+ }
243252}
244- // Fix for array of MS Objects not being handled properly
245253// Original was using key(reset($value)) == 'ClassType' as condition
246254// reset(array) returns the first value of the array
247255// key(array) expects an array as a parameter, not a value
248- // Comparison Operator '===' is used due to PHP Type Juggling, string with no numeric values converts to int '0'
249- else if (key ($ value ) ==='ClassType ' ){
250- // key(reset(array)) was never "ClassType", so MS Object arrays failed always
251- $ arrData .='i:type="mem:ArrayOfMemberSuiteObject"> ' ;
252- if (key ($ value ) ==='ClassType ' ){
253- $ arrData .='<mem:MemberSuiteObject> ' .$ this ->build_msnode ($ value ).'</mem:MemberSuiteObject> ' ;
254- }else {
255- foreach ($ valueas $ k =>$ v ) {
256- if (!is_array ($ v ) && !is_object ($ v )){
257- $ arrData .='<mem:Key> ' .$ k .'</mem:Key><mem:Value i:type="a:string"> ' .$ v .'</mem:Value> ' ;
258- }else if ($ v !=null ) {
259- $ arrData .='<mem:MemberSuiteObject> ' .$ this ->build_msnode ($ v ).'</mem:MemberSuiteObject> ' ;
260- }
261- }
262- }
263- }
256+ // key(reset(array)) was never "ClassType", so MS Object arrays always returned false
257+ // Comparison Operator '===' is used due to PHP Type Juggling, string with no numeric values converts to int '0', causing false positives
258+ else if (strval (key ($ value )) ==='ClassType ' ){
259+ // This condition handles MS Objects arrays with only one element
260+ // Fields of MS Object arrays that only have one element just return the object, no array
261+ $ arrData .='i:type="mem:ArrayOfMemberSuiteObject"> ' ;
262+ if (key ($ value ) ==='ClassType ' ){
263+ $ arrData .='<mem:MemberSuiteObject> ' .$ this ->build_msnode ($ value ).'</mem:MemberSuiteObject> ' ;
264+ }else {
265+ foreach ($ valueas $ k =>$ v ) {
266+ if (!is_array ($ v ) && !is_object ($ v )){
267+ $ arrData .='<mem:Key> ' .$ k .'</mem:Key><mem:Value i:type="a:string"> ' .$ v .'</mem:Value> ' ;
268+ }else if ($ v !=null ) {
269+ $ arrData .='<mem:MemberSuiteObject> ' .$ this ->build_msnode ($ v ).'</mem:MemberSuiteObject> ' ;
270+ }
271+ }
272+ }
273+ }
274+ // Fix for formatting of multiple elements within an array having numeric keys [0=>e0, 1=>e1, 2=>e2]
275+ else if (is_numeric (key ($ value ))){
276+ // Check to see if this is an MS Object array or Simple String Array
277+ // Necessary so that arrData does not have duplicate data type tags
278+ foreach ($ valueas $ innerkey =>$ innervalue ){
279+ if (strval (key ($ innervalue )) ==='ClassType ' ){
280+ $ arrData .='i:type="mem:ArrayOfMemberSuiteObject"> ' ;
281+ // Only needs to check the first Key for ClassType, so short circuit
282+ break ;
283+ }
284+ else {
285+ $ arrData .='i:type="arr:ArrayOfstring" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays"> ' ;
286+ // Only needs to check the first Key for ClassType, so short circuit
287+ break ;
288+ }
289+ }
290+
291+ // Begin looping through the array in order to generate XML
292+ foreach ($ valueas $ innerkey =>$ innervalue ){
293+ // Check to see if it's an MS Object array
294+ if (strval (key ($ innervalue )) ==='ClassType ' ){
295+ // Condition check throws a warning if $innervalue is not an array, but it's okay
296+ // MS Object array with numeric keys
297+ if (strval (key ($ innervalue )) ==='ClassType ' ){
298+ $ arrData .='<mem:MemberSuiteObject> ' .$ this ->build_msnode ($ innervalue ).'</mem:MemberSuiteObject> ' ;
299+ }else {
300+ foreach ($ innervalueas $ k =>$ v ) {
301+ if (!is_array ($ v ) && !is_object ($ v )){
302+ $ arrData .='<mem:Key> ' .$ k .'</mem:Key><mem:Value i:type="a:string"> ' .$ v .'</mem:Value> ' ;
303+ }else if ($ v !=null ) {
304+ $ arrData .='<mem:MemberSuiteObject> ' .$ this ->build_msnode ($ v ).'</mem:MemberSuiteObject> ' ;
305+ }
306+ }
307+ }
308+ }
309+ else {
310+ // Simple string array with numeric keys
311+ if (!is_array ($ innervalue )) {
312+ $ arrData .='<arr:string> ' .$ innervalue .'</arr:string> ' ;
313+ }
314+ }
315+ }
316+ }
264317else {
265318// Simple array
266319$ arrData .='i:type="arr:ArrayOfstring" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays"> ' ;
@@ -275,10 +328,10 @@ protected function build_msnode($objectarr){
275328$ objecttype .='<mem:KeyValueOfstringanyType><mem:Key> ' .$ key .'</mem:Key><mem:Value ' .$ arrData .'</mem:Value></mem:KeyValueOfstringanyType> ' ;
276329}
277330
278- }
279- else {
280- $ objecttype .= ' <mem:KeyValueOfstringanyType>
281- <mem:Key> ' .$ key .'</mem:Key><mem:Value ' ;
331+ }
332+ //
333+ else {
334+ $ objecttype .= ' <mem:KeyValueOfstringanyType> <mem:Key> ' .$ key .'</mem:Key><mem:Value ' ;
282335if (strlen ($ value ) >0 ) {
283336if (preg_match ("/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/ " ,$ value )){
284337$ objecttype .='i:type="a:dateTime" ' ;
@@ -290,12 +343,12 @@ protected function build_msnode($objectarr){
290343else {
291344$ objecttype .='i:nil="true" ' ;
292345}
293- $ objecttype .='> ' .$ value .'</mem:Value>
294- </mem:KeyValueOfstringanyType> ' ;
295- }
296- }
297- return $ objecttype ;
298- }
346+ $ objecttype .='> ' .$ value .'</mem:Value></mem:KeyValueOfstringanyType> ' ;
347+ }
348+ }
349+ return str_replace ( " & " , " & " , $ objecttype );
350+ }
351+
299352
300353protected function str_replace_last ($ search ,$ replace ,$ str ) {
301354if ( ($ pos =strrpos ($ str ,$ search ) ) !==false ) {