github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/unmarshal_test.go (about)

     1  package phpserialize_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/require"
     7  
     8  	"github.com/trim21/go-phpserialize"
     9  )
    10  
    11  func TestUnmarshal_as_string(t *testing.T) {
    12  	t.Parallel()
    13  
    14  	t.Run("ptr", func(t *testing.T) {
    15  		type Container struct {
    16  			V *int `php:",omitempty,string"`
    17  		}
    18  
    19  		var c Container
    20  		raw := `a:1:{s:1:"V";s:1:"1";}`
    21  		err := phpserialize.Unmarshal([]byte(raw), &c)
    22  		require.NoError(t, err)
    23  		require.Equal(t, 1, *c.V)
    24  	})
    25  }
    26  
    27  func TestUnmarshal_struct_string(t *testing.T) {
    28  	t.Parallel()
    29  
    30  	t.Run("value", func(t *testing.T) {
    31  		type Container struct {
    32  			F string `php:"f1q"`
    33  			V bool   `php:"1a9"`
    34  		}
    35  
    36  		var c Container
    37  		raw := `a:1:{s:3:"f1q";s:10:"0147852369";}`
    38  		err := phpserialize.Unmarshal([]byte(raw), &c)
    39  		require.NoError(t, err)
    40  		require.Equal(t, "0147852369", c.F)
    41  	})
    42  
    43  	t.Run("empty", func(t *testing.T) {
    44  		type Container struct {
    45  			F string `php:"f"`
    46  		}
    47  
    48  		var c Container
    49  		raw := `a:0:{}`
    50  		err := phpserialize.Unmarshal([]byte(raw), &c)
    51  		require.NoError(t, err)
    52  		require.Equal(t, "", c.F)
    53  	})
    54  }
    55  
    56  func TestUnmarshal_stdClass(t *testing.T) {
    57  	raw := `O:8:"stdClass":1:{s:1:"a";s:13:"a str value q";}`
    58  
    59  	t.Run("struct", func(t *testing.T) {
    60  		var v struct {
    61  			A string `php:"a"`
    62  		}
    63  
    64  		require.NoError(t, phpserialize.Unmarshal([]byte(raw), &v))
    65  
    66  		require.Equal(t, "a str value q", v.A)
    67  	})
    68  
    69  	t.Run("any", func(t *testing.T) {
    70  		var v any
    71  		require.NoError(t, phpserialize.Unmarshal([]byte(raw), &v))
    72  
    73  		m, ok := v.(map[string]any)
    74  		require.True(t, ok, "type cast fail")
    75  
    76  		require.Equal(t, "a str value q", m["a"])
    77  	})
    78  
    79  	t.Run("skip", func(t *testing.T) {
    80  		raw := `a:2:{s:1:"a";O:8:"stdClass":1:{s:1:"a";s:13:"a str value q";}s:5:"value";b:1;}`
    81  		var v struct {
    82  			Value bool `php:"value"`
    83  		}
    84  		require.NoError(t, phpserialize.Unmarshal([]byte(raw), &v))
    85  
    86  		require.True(t, v.Value)
    87  	})
    88  }
    89  
    90  func TestUnmarshal_struct_bytes(t *testing.T) {
    91  	t.Parallel()
    92  
    93  	t.Run("value", func(t *testing.T) {
    94  		type Container struct {
    95  			F []byte `php:"f1q"`
    96  		}
    97  
    98  		var c Container
    99  		raw := `a:1:{s:3:"f1q";s:10:"0147852369";}`
   100  		err := phpserialize.Unmarshal([]byte(raw), &c)
   101  		require.NoError(t, err)
   102  		require.Equal(t, []byte("0147852369"), c.F)
   103  	})
   104  
   105  	t.Run("empty", func(t *testing.T) {
   106  		type Container struct {
   107  			F []byte `php:"f"`
   108  		}
   109  
   110  		var c Container
   111  		raw := `a:0:{}`
   112  		err := phpserialize.Unmarshal([]byte(raw), &c)
   113  		require.NoError(t, err)
   114  		require.Nil(t, c.F)
   115  	})
   116  }
   117  
   118  func TestUnmarshal_struct_float(t *testing.T) {
   119  	t.Parallel()
   120  
   121  	t.Run("negative", func(t *testing.T) {
   122  		type Container struct {
   123  			F float64 `php:"f"`
   124  		}
   125  		var c Container
   126  		raw := `a:1:{s:1:"f";d:3.14;}`
   127  		err := phpserialize.Unmarshal([]byte(raw), &c)
   128  		require.NoError(t, err)
   129  		require.Equal(t, float64(3.14), c.F)
   130  	})
   131  
   132  	t.Run("positive", func(t *testing.T) {
   133  		type Container struct {
   134  			F float64 `php:"f"`
   135  		}
   136  		var c Container
   137  		raw := `a:1:{s:1:"f";d:1;}`
   138  		err := phpserialize.Unmarshal([]byte(raw), &c)
   139  		require.NoError(t, err)
   140  		require.Equal(t, float64(1), c.F)
   141  	})
   142  
   143  	t.Run("zero", func(t *testing.T) {
   144  		type Container struct {
   145  			F float64 `php:"f"`
   146  		}
   147  		var c Container
   148  		raw := `a:1:{s:1:"f";d:-3.14;}`
   149  		err := phpserialize.Unmarshal([]byte(raw), &c)
   150  		require.NoError(t, err)
   151  		require.Equal(t, float64(-3.14), c.F)
   152  	})
   153  
   154  	t.Run("float32", func(t *testing.T) {
   155  		type Container struct {
   156  			F float32 `php:"f1q"`
   157  		}
   158  
   159  		var c Container
   160  		raw := `a:1:{s:3:"f1q";d:147852369;}`
   161  		err := phpserialize.Unmarshal([]byte(raw), &c)
   162  		require.NoError(t, err)
   163  		require.Equal(t, float32(147852369), c.F)
   164  	})
   165  
   166  	t.Run("float64", func(t *testing.T) {
   167  		type Container struct {
   168  			F float64 `php:"f1q"`
   169  		}
   170  
   171  		var c Container
   172  		raw := `a:1:{s:3:"f1q";d:147852369;}`
   173  		err := phpserialize.Unmarshal([]byte(raw), &c)
   174  		require.NoError(t, err)
   175  		require.Equal(t, float64(147852369), c.F)
   176  	})
   177  
   178  }
   179  
   180  func TestUnmarshal_struct_uint(t *testing.T) {
   181  	t.Parallel()
   182  
   183  	t.Run("uint", func(t *testing.T) {
   184  		type Container struct {
   185  			F uint `php:"f1q"`
   186  		}
   187  
   188  		var c Container
   189  		raw := `a:1:{s:3:"f1q";i:147852369;}`
   190  		err := phpserialize.Unmarshal([]byte(raw), &c)
   191  		require.NoError(t, err)
   192  		require.Equal(t, uint(147852369), c.F)
   193  	})
   194  
   195  	t.Run("uint8", func(t *testing.T) {
   196  		type Container struct {
   197  			F uint8 `php:"f1q"`
   198  		}
   199  
   200  		var c Container
   201  		raw := `a:1:{s:3:"f1q";i:255;}`
   202  		err := phpserialize.Unmarshal([]byte(raw), &c)
   203  		require.NoError(t, err)
   204  		require.Equal(t, uint8(255), c.F)
   205  	})
   206  
   207  	t.Run("uint16", func(t *testing.T) {
   208  		type Container struct {
   209  			F uint16 `php:"f1q"`
   210  		}
   211  
   212  		var c Container
   213  		raw := `a:1:{s:3:"f1q";i:574;}`
   214  		err := phpserialize.Unmarshal([]byte(raw), &c)
   215  		require.NoError(t, err)
   216  		require.Equal(t, uint16(574), c.F)
   217  	})
   218  
   219  	t.Run("uint32", func(t *testing.T) {
   220  		type Container struct {
   221  			F uint32 `php:"f1q"`
   222  		}
   223  
   224  		var c Container
   225  		raw := `a:1:{s:3:"f1q";i:57400;}`
   226  		err := phpserialize.Unmarshal([]byte(raw), &c)
   227  		require.NoError(t, err)
   228  		require.Equal(t, uint32(57400), c.F)
   229  	})
   230  
   231  	t.Run("uint64", func(t *testing.T) {
   232  		type Container struct {
   233  			F uint64 `php:"f1q"`
   234  		}
   235  
   236  		var c Container
   237  		raw := `a:1:{s:3:"f1q";i:5740000;}`
   238  		err := phpserialize.Unmarshal([]byte(raw), &c)
   239  		require.NoError(t, err)
   240  		require.Equal(t, uint64(5740000), c.F)
   241  	})
   242  }
   243  
   244  func TestUnmarshal_struct_int(t *testing.T) {
   245  	t.Parallel()
   246  
   247  	t.Run("int", func(t *testing.T) {
   248  		type Container struct {
   249  			F int `php:"f1q"`
   250  		}
   251  
   252  		var c Container
   253  		raw := `a:1:{s:3:"f1q";i:147852369;}`
   254  		err := phpserialize.Unmarshal([]byte(raw), &c)
   255  		require.NoError(t, err)
   256  		require.Equal(t, int(147852369), c.F)
   257  	})
   258  
   259  	t.Run("int8", func(t *testing.T) {
   260  		type Container struct {
   261  			F int8 `php:"f1q"`
   262  		}
   263  
   264  		var c Container
   265  		raw := `a:1:{s:3:"f1q";i:65;}`
   266  		err := phpserialize.Unmarshal([]byte(raw), &c)
   267  		require.NoError(t, err)
   268  		require.Equal(t, int8(65), c.F)
   269  	})
   270  
   271  	t.Run("int16", func(t *testing.T) {
   272  		type Container struct {
   273  			F int16 `php:"f1q"`
   274  		}
   275  
   276  		var c Container
   277  		raw := `a:1:{s:3:"f1q";i:574;}`
   278  		err := phpserialize.Unmarshal([]byte(raw), &c)
   279  		require.NoError(t, err)
   280  		require.Equal(t, int16(574), c.F)
   281  	})
   282  
   283  	t.Run("int32", func(t *testing.T) {
   284  		type Container struct {
   285  			F int32 `php:"f1q"`
   286  		}
   287  
   288  		var c Container
   289  		raw := `a:1:{s:3:"f1q";i:57400;}`
   290  		err := phpserialize.Unmarshal([]byte(raw), &c)
   291  		require.NoError(t, err)
   292  		require.Equal(t, int32(57400), c.F)
   293  	})
   294  
   295  	t.Run("int64", func(t *testing.T) {
   296  		type Container struct {
   297  			F int64 `php:"f1q"`
   298  		}
   299  
   300  		var c Container
   301  		raw := `a:1:{s:3:"f1q";i:5740000;}`
   302  		err := phpserialize.Unmarshal([]byte(raw), &c)
   303  		require.NoError(t, err)
   304  		require.Equal(t, int64(5740000), c.F)
   305  	})
   306  }
   307  
   308  func TestUnmarshal_slice(t *testing.T) {
   309  	t.Parallel()
   310  
   311  	t.Run("empty", func(t *testing.T) {
   312  		type Container struct {
   313  			Value []string `php:"value"`
   314  		}
   315  
   316  		var c Container
   317  		raw := `a:1:{s:5:"value";a:0:{}}`
   318  		err := phpserialize.Unmarshal([]byte(raw), &c)
   319  		require.NoError(t, err)
   320  		require.Len(t, c.Value, 0)
   321  	})
   322  
   323  	t.Run("string", func(t *testing.T) {
   324  		type Container struct {
   325  			Value []string `php:"value"`
   326  		}
   327  		var c Container
   328  		raw := `a:3:{s:2:"bb";b:1;s:5:"value";a:3:{i:0;s:3:"one";i:1;s:3:"two";i:2;s:1:"q";}}`
   329  		err := phpserialize.Unmarshal([]byte(raw), &c)
   330  		require.NoError(t, err)
   331  		require.Equal(t, []string{"one", "two", "q"}, c.Value)
   332  	})
   333  
   334  	t.Run("string more length", func(t *testing.T) {
   335  		type Container struct {
   336  			Value []string `php:"value"`
   337  		}
   338  		var c Container
   339  		raw := `a:1:{s:5:"value";a:6:{i:0;s:3:"one";i:1;s:3:"two";i:2;s:1:"q";i:3;s:1:"a";i:4;s:2:"zx";i:5;s:3:"abc";}}`
   340  		err := phpserialize.Unmarshal([]byte(raw), &c)
   341  		require.NoError(t, err)
   342  		require.Equal(t, []string{"one", "two", "q", "a", "zx"}, c.Value[:5])
   343  	})
   344  }
   345  
   346  func TestUnmarshal_array(t *testing.T) {
   347  	t.Parallel()
   348  
   349  	t.Run("empty", func(t *testing.T) {
   350  		type Container struct {
   351  			Value [5]string `php:"value"`
   352  		}
   353  
   354  		var c Container
   355  		raw := `a:1:{s:5:"value";a:0:{}}`
   356  		err := phpserialize.Unmarshal([]byte(raw), &c)
   357  		require.NoError(t, err)
   358  		require.Equal(t, [5]string{}, c.Value)
   359  	})
   360  
   361  	t.Run("string less length", func(t *testing.T) {
   362  		type Container struct {
   363  			Value [5]string `php:"value"`
   364  		}
   365  		var c Container
   366  		raw := `a:1:{s:5:"value";a:3:{i:0;s:3:"one";i:1;s:3:"two";i:2;s:1:"q";}}`
   367  		err := phpserialize.Unmarshal([]byte(raw), &c)
   368  		require.NoError(t, err)
   369  		require.Equal(t, [5]string{"one", "two", "q"}, c.Value)
   370  	})
   371  
   372  	t.Run("string more length", func(t *testing.T) {
   373  		type Container struct {
   374  			Value [5]string `php:"value"`
   375  		}
   376  		var c Container
   377  		raw := `a:1:{s:5:"value";a:6:{i:0;s:3:"one";i:1;s:3:"two";i:2;s:1:"q";i:3;s:1:"a";i:4;s:2:"zx";i:5;s:3:"abc";}}`
   378  		err := phpserialize.Unmarshal([]byte(raw), &c)
   379  		require.NoError(t, err)
   380  		require.Equal(t, [5]string{"one", "two", "q", "a", "zx"}, c.Value)
   381  	})
   382  }
   383  
   384  func TestUnmarshal_skip_value(t *testing.T) {
   385  	type Container struct {
   386  		Value []string `php:"value"`
   387  	}
   388  
   389  	var c Container
   390  	raw := `a:3:{s:2:"bb";b:1;s:5:"value";a:3:{i:0;s:3:"one";i:1;s:3:"two";i:2;s:1:"q";}s:6:"value2";a:3:{i:0;s:1:"1";i:1;s:1:"2";i:2;s:1:"3";}}`
   391  	err := phpserialize.Unmarshal([]byte(raw), &c)
   392  	require.NoError(t, err)
   393  	require.Equal(t, []string{"one", "two", "q"}, c.Value)
   394  }
   395  
   396  var _ phpserialize.Unmarshaler = (*unmarshaler)(nil)
   397  
   398  type unmarshaler []byte
   399  
   400  func (u *unmarshaler) UnmarshalPHP(bytes []byte) error {
   401  	*u = append((*u)[0:0], bytes...)
   402  
   403  	return nil
   404  }
   405  
   406  func TestUnmarshal_unmarshaler(t *testing.T) {
   407  	t.Parallel()
   408  	type Container struct {
   409  		Value unmarshaler `php:"value"`
   410  	}
   411  
   412  	var c Container
   413  	raw := `a:1:{s:5:"value";a:3:{i:0;s:3:"one";i:1;s:3:"two";i:2;s:1:"q";}}`
   414  	err := phpserialize.Unmarshal([]byte(raw), &c)
   415  	require.NoError(t, err)
   416  	require.Equal(t, `a:3:{i:0;s:3:"one";i:1;s:3:"two";i:2;s:1:"q";}`, string(c.Value))
   417  }
   418  
   419  func TestUnmarshal_string_wrapper(t *testing.T) {
   420  	t.Parallel()
   421  
   422  	type Container struct {
   423  		Value int `php:"value,string"`
   424  	}
   425  
   426  	var c Container
   427  	raw := `a:1:{s:5:"value";s:3:"233";}`
   428  	err := phpserialize.Unmarshal([]byte(raw), &c)
   429  	require.NoError(t, err)
   430  	require.Equal(t, int(233), c.Value)
   431  }
   432  
   433  func TestUnmarshal_map(t *testing.T) {
   434  	t.Parallel()
   435  
   436  	t.Run("map[string]string", func(t *testing.T) {
   437  		raw := `a:1:{s:5:"value";a:5:{s:3:"one";s:1:"1";s:3:"two";s:1:"2";s:5:"three";s:1:"3";s:4:"four";s:1:"4";s:4:"five";s:1:"5";}}`
   438  		var c struct {
   439  			Value map[string]string `php:"value"`
   440  		}
   441  
   442  		err := phpserialize.Unmarshal([]byte(raw), &c)
   443  		require.NoError(t, err)
   444  		require.Equal(t, map[string]string{
   445  			"one":   "1",
   446  			"two":   "2",
   447  			"three": "3",
   448  			"four":  "4",
   449  			"five":  "5",
   450  		}, c.Value)
   451  	})
   452  
   453  	t.Run("map[any]string", func(t *testing.T) {
   454  		raw := `a:1:{s:5:"value";a:5:{i:1;s:3:"one";i:2;s:3:"two";i:3;s:5:"three";i:4;s:4:"four";i:5;s:4:"five";}}`
   455  		var c struct {
   456  			Value map[any]string `php:"value"`
   457  		}
   458  
   459  		err := phpserialize.Unmarshal([]byte(raw), &c)
   460  		require.NoError(t, err)
   461  		require.Equal(t, map[any]string{
   462  			int64(1): "one",
   463  			int64(2): "two",
   464  			int64(3): "three",
   465  			int64(4): "four",
   466  			int64(5): "five",
   467  		}, c.Value)
   468  	})
   469  
   470  	t.Run("any", func(t *testing.T) {
   471  		raw := `a:1:{s:5:"value";a:5:{i:1;s:3:"one";i:2;s:3:"two";i:3;s:5:"three";i:4;s:4:"four";i:5;s:4:"five";}}`
   472  		var c struct {
   473  			Value any `php:"value"`
   474  		}
   475  
   476  		err := phpserialize.Unmarshal([]byte(raw), &c)
   477  		require.NoError(t, err)
   478  		require.Equal(t, map[any]any{
   479  			int64(1): "one",
   480  			int64(2): "two",
   481  			int64(3): "three",
   482  			int64(4): "four",
   483  			int64(5): "five",
   484  		}, c.Value)
   485  	})
   486  }
   487  
   488  func TestUnmarshal_ptr_string(t *testing.T) {
   489  	t.Parallel()
   490  
   491  	t.Run("value", func(t *testing.T) {
   492  		var c struct {
   493  			F *string `php:"f1q"`
   494  		}
   495  
   496  		raw := `a:1:{s:3:"f1q";s:10:"0147852369";}`
   497  		err := phpserialize.Unmarshal([]byte(raw), &c)
   498  		require.NoError(t, err)
   499  		require.NotNil(t, c.F)
   500  		require.Equal(t, "0147852369", *c.F)
   501  	})
   502  
   503  	t.Run("empty", func(t *testing.T) {
   504  		var c struct {
   505  			F *string `php:"f"`
   506  		}
   507  
   508  		raw := `a:0:{}`
   509  		err := phpserialize.Unmarshal([]byte(raw), &c)
   510  		require.NoError(t, err)
   511  		require.Nil(t, c.F)
   512  	})
   513  
   514  	t.Run("nested", func(t *testing.T) {
   515  		var c struct {
   516  			F **string `php:"f"`
   517  		}
   518  
   519  		raw := `a:0:{}`
   520  		err := phpserialize.Unmarshal([]byte(raw), &c)
   521  		require.Error(t, err)
   522  	})
   523  }
   524  
   525  func TestUnmarshal_anonymous_field(t *testing.T) {
   526  	type N struct {
   527  		A int
   528  		B int
   529  	}
   530  
   531  	type M struct {
   532  		N
   533  		C int
   534  	}
   535  
   536  	var v M
   537  
   538  	require.NoError(t, phpserialize.Unmarshal([]byte(`a:4:{s:1:"A";i:3;s:1:"B";i:2;s:1:"C";i:1;}`), &v))
   539  	require.Equal(t, M{N: N{
   540  		A: 3,
   541  		B: 2,
   542  	}, C: 1}, v)
   543  }
   544  
   545  func TestUnmarshal_empty_input(t *testing.T) {
   546  	t.Run("slice", func(t *testing.T) {
   547  		var data []int
   548  		require.NoError(t, phpserialize.Unmarshal([]byte(""), &data))
   549  	})
   550  	t.Run("array", func(t *testing.T) {
   551  		var data [5]int
   552  		require.NoError(t, phpserialize.Unmarshal([]byte(""), &data))
   553  	})
   554  	t.Run("map", func(t *testing.T) {
   555  		var data map[uint]int
   556  		require.NoError(t, phpserialize.Unmarshal([]byte(""), &data))
   557  	})
   558  	t.Run("interface", func(t *testing.T) {
   559  		var data any
   560  		require.NoError(t, phpserialize.Unmarshal([]byte(""), &data))
   561  	})
   562  	t.Run("string", func(t *testing.T) {
   563  		var data string
   564  		require.NoError(t, phpserialize.Unmarshal([]byte(""), &data))
   565  	})
   566  	t.Run("int", func(t *testing.T) {
   567  		var data int
   568  		require.NoError(t, phpserialize.Unmarshal([]byte(""), &data))
   569  	})
   570  
   571  	t.Run("uint", func(t *testing.T) {
   572  		var data uint
   573  		require.NoError(t, phpserialize.Unmarshal([]byte(""), &data))
   574  	})
   575  
   576  	t.Run("bool", func(t *testing.T) {
   577  		var data bool
   578  		require.NoError(t, phpserialize.Unmarshal([]byte(""), &data))
   579  	})
   580  }
   581  
   582  func TestUnmarshal_as_string_2(t *testing.T) {
   583  	type ID uint32
   584  	type Type uint8
   585  	type Item struct {
   586  		ID   ID   `php:"eid,string"`
   587  		Type Type `php:"type"`
   588  	}
   589  	type Collection = map[ID]Item
   590  
   591  	raw := `a:7:{i:1087180;a:2:{s:3:"eid";s:7:"1087180";s:4:"type";i:2;}i:1087181;a:2:{s:3:"eid";s:7:"1087181";s:4:"type";i:2;}i:1087182;a:2:{s:3:"eid";s:7:"1087182";s:4:"type";i:2;}i:1087183;a:2:{s:3:"eid";s:7:"1087183";s:4:"type";i:2;}i:1087184;a:2:{s:3:"eid";s:7:"1087184";s:4:"type";i:2;}i:1087185;a:2:{s:3:"eid";s:7:"1087185";s:4:"type";i:2;}i:1087186;a:2:{s:3:"eid";s:7:"1087186";s:4:"type";i:2;}}`
   592  
   593  	var data Collection
   594  
   595  	err := phpserialize.Unmarshal([]byte(raw), &data)
   596  	require.NoError(t, err)
   597  
   598  }
   599  
   600  func TestUnmarshal_null_array_1(t *testing.T) {
   601  	raw := `a:0:{}`
   602  
   603  	type Tag struct {
   604  		Name  *string `php:"tag_name"`
   605  		Count int     `php:"result,string"`
   606  	}
   607  
   608  	var tags []Tag
   609  
   610  	err := phpserialize.Unmarshal([]byte(raw), &tags)
   611  	require.NoError(t, err)
   612  }
   613  
   614  func TestUnmarshal_null_array_2(t *testing.T) {
   615  	raw := `a:4:{s:1:"a";i:2;s:4:"Test";a:0:{}s:1:"b";a:0:{}s:1:"o";i:1;}`
   616  
   617  	var data any
   618  
   619  	err := phpserialize.Unmarshal([]byte(raw), &data)
   620  	require.NoError(t, err)
   621  
   622  	require.Equal(t, data, map[any]any{
   623  		"a":    int64(2),
   624  		"o":    int64(1),
   625  		"Test": map[any]any{},
   626  		"b":    map[any]any{},
   627  	})
   628  }